How to Harden SSH Server Configuration: The sshd_config Settings That Matter

Retool's new app builder is where AI-generated code ships safely
Building apps with AI is easy. Getting them to production safely is another story.
Every Linux server exposed to the internet receives automated SSH brute-force attempts within minutes of coming online. Shodan and Masscan continuously catalog internet-facing SSH servers; credential-stuffing bots run through leaked password lists against anything that responds on port 22.
A server with default SSH configuration and a weak root password is a matter of when, not if. A server with key-only authentication and root login disabled is immune to this entire attack class: there is no attack surface for password-based brute force.
Core Security Settings: The Non-Negotiables
Edit /etc/ssh/sshd_config (or drop a file in /etc/ssh/sshd_config.d/99-hardening.conf on modern distributions):
# /etc/ssh/sshd_config.d/99-hardening.conf
# AUTHENTICATION ─────────────────────────────────────────────
# Disable password authentication entirely: only SSH keys accepted
PasswordAuthentication no
KbdInteractiveAuthentication no
ChallengeResponseAuthentication no
UsePAM yes # Keep yes for PAM integration but password auth is disabled above
# Disable root login: use a regular user and sudo
PermitRootLogin no
# Do not allow empty passwords
PermitEmptyPasswords no
# Disable unused authentication methods
HostbasedAuthentication no
IgnoreRhosts yes
RhostsRSAAuthentication no # Legacy, may not apply to your version
# ACCESS CONTROL ─────────────────────────────────────────────
# Only allow specific users to log in via SSH (comma-separated)
AllowUsers deploy ansible monitoring
# Or restrict by group:
# AllowGroups ssh-users
# TIMEOUTS AND LIMITS ────────────────────────────────────────
# Disconnect unauthenticated connections after 30 seconds
LoginGraceTime 30
# Maximum authentication attempts before disconnect
MaxAuthTries 3
# Maximum simultaneous unauthenticated connections (start:rate:full)
MaxStartups 10:30:100
# Close idle sessions after 15 minutes of inactivity
ClientAliveInterval 300
ClientAliveCountMax 3
# Maximum sessions per connection
MaxSessions 4
Apply and test:
# Test the config file for syntax errors before reloading
sshd -t
# Reload (keep your current session open: test in a NEW terminal first)
systemctl reload sshd
# CRITICAL: Open a NEW terminal and test login before closing the existing session
# If you lock yourself out, the existing session stays open for recovery
ssh -i ~/.ssh/your-key user@server
Cryptographic Hardening: Ciphers, KEX, and MACs
Default OpenSSH configurations support legacy algorithms for backward compatibility. Production servers should restrict to modern, audited algorithms.
Mozilla Modern OpenSSH configuration (suitable for all clients >= OpenSSH 6.7):
# Key Exchange Algorithms: only modern elliptic curve and DH
KexAlgorithms curve25519-sha256,curve25519-sha256@libssh.org,diffie-hellman-group16-sha512,diffie-hellman-group18-sha512
# Ciphers: AES-GCM and ChaCha20-Poly1305 only
Ciphers chacha20-poly1305@openssh.com,aes256-gcm@openssh.com,aes128-gcm@openssh.com
# MACs: remove legacy HMAC algorithms
MACs hmac-sha2-512-etm@openssh.com,hmac-sha2-256-etm@openssh.com,umac-128-etm@openssh.com
# Host key algorithms (for server identity)
HostKeyAlgorithms ssh-ed25519,rsa-sha2-512,rsa-sha2-256
What to drop and why:
arcfour,blowfish,3des-cbc: old ciphers with known weaknesseshmac-sha1,hmac-md5: weak MAC algorithmsdiffie-hellman-group1-sha1,diffie-hellman-group14-sha1: 1024-bit DH, vulnerable to Logjamecdsa-sha2-nistp256: NIST curves with potential backdoor concerns; Ed25519 is preferred
Generate strong server host keys if not already present:
# Generate an Ed25519 host key (modern, fast, strong)
ssh-keygen -t ed25519 -f /etc/ssh/ssh_host_ed25519_key -N ''
# Generate a 4096-bit RSA key for compatibility
ssh-keygen -t rsa -b 4096 -f /etc/ssh/ssh_host_rsa_key -N ''
# Remove legacy host key types
rm -f /etc/ssh/ssh_host_dsa_key* /etc/ssh/ssh_host_ecdsa_key*
# Update sshd_config to only load modern host keys
HostKey /etc/ssh/ssh_host_ed25519_key
HostKey /etc/ssh/ssh_host_rsa_key
Regenerate the Diffie-Hellman moduli file (removes weak 1024-bit groups):
# Generate new moduli (takes ~10-30 minutes)
ssh-keygen -G /tmp/moduli.candidates -b 3072
ssh-keygen -T /etc/ssh/moduli -f /tmp/moduli.candidates
Briefings like this, every morning before 9am.
Threat intel, active CVEs, and campaign alerts, distilled for practitioners. 50,000+ subscribers. No noise.
Rate Limiting and Port Configuration
fail2ban: automatically ban IPs after failed attempts:
apt install fail2ban
# /etc/fail2ban/jail.local
[sshd]
enabled = true
port = ssh
filter = sshd
logpath = %(sshd_log)s
backend = %(sshd_backend)s
maxretry = 3
findtime = 300 # 5-minute window
bantime = 3600 # 1-hour ban
systemctl enable --now fail2ban
# Check ban status
fail2ban-client status sshd
iptables rate limiting (defense in depth with fail2ban):
# Limit SSH connections to 3 per minute per IP
iptables -A INPUT -p tcp --dport 22 -m state --state NEW -m recent --set --name SSH
iptables -A INPUT -p tcp --dport 22 -m state --state NEW -m recent --update --seconds 60 --hitcount 4 --name SSH -j DROP
# Persist
iptables-save > /etc/iptables/rules.v4
Changing the default port: Moving SSH from port 22 to a high-numbered port eliminates the vast majority of automated scanning noise without improving security against a targeted attacker. It reduces log noise significantly on internet-facing servers. Not a security control, but a useful noise reduction measure.
# Add in sshd_config (add a new Port line: keeps 22 active until you verify the new port works)
Port 22
Port 2222
# After testing new port works, remove Port 22 and reload
Bind SSH to specific interface (if multi-homed):
# Only accept SSH on the management interface, not the public interface
ListenAddress 10.0.1.100
# Not: 0.0.0.0 (all interfaces)
Audit your SSH configuration:
# ssh-audit: comprehensive SSH server security scanner
pip install ssh-audit
ssh-audit localhost
# Or run against a remote server
ssh-audit server.example.com
# Online: https://ssh-audit.com/ for internet-facing servers
Key Management Best Practices
Disabling password auth only helps if key management is done correctly.
Generate strong client keys:
# Ed25519 (recommended: modern, fast, compact)
ssh-keygen -t ed25519 -C "user@hostname-$(date +%Y-%m)" -f ~/.ssh/id_ed25519
# RSA 4096-bit (for compatibility with older servers)
ssh-keygen -t rsa -b 4096 -C "user@hostname-$(date +%Y-%m)" -f ~/.ssh/id_rsa_4096
# Always use a passphrase on private keys
# Use ssh-agent to cache the passphrase so you don't type it constantly
eval "$(ssh-agent -s)"
ssh-add ~/.ssh/id_ed25519
Manage authorized_keys correctly:
# Correct permissions: SSH will refuse to use authorized_keys if permissions are too open
chmod 700 ~/.ssh
chmod 600 ~/.ssh/authorized_keys
chown -R user:user ~/.ssh
# Restrict what an authorized key can do (for service accounts and automation)
# authorized_keys entry with restrictions:
# restrict,command="/usr/local/bin/backup.sh",from="10.0.1.0/24" ssh-ed25519 AAAA...
# 'restrict' disables all forwarding and PTY allocation
# 'command' forces execution of only this specific command
# 'from' restricts to specific source IPs
# Audit all authorized_keys files on a server
find /home /root -name authorized_keys -exec cat {} \; 2>/dev/null
Centralized SSH key management (for teams larger than 5-10 people):
- HashiCorp Vault SSH secrets engine: issues signed SSH certificates with short TTLs
- AWS Systems Manager Session Manager: SSH-free shell access via IAM, no port 22 needed
- Teleport: open-source bastion with certificate-based SSH, audit logging, and RBAC
The bottom line
SSH hardening takes five minutes and eliminates an entire class of attack. The three critical changes: disable PasswordAuthentication, disable PermitRootLogin, and add AllowUsers with only the accounts that legitimately need SSH access. Then restrict ciphers to modern algorithms (chacha20-poly1305, aes-gcm, Ed25519 keys), install fail2ban, and run ssh-audit to verify the configuration. For teams, evaluate replacing SSH with certificate-based access (Vault SSH engine) or agent-based access (AWS SSM, Teleport) to eliminate key sprawl.
Frequently asked questions
What are the most important SSH hardening settings?
In priority order: set PasswordAuthentication no (eliminates brute-force attack surface), set PermitRootLogin no (forces use of privileged accounts with sudo), add AllowUsers with only legitimate SSH accounts, set LoginGraceTime 30 and MaxAuthTries 3, and restrict KexAlgorithms and Ciphers to modern algorithms. Always test configuration changes by opening a new terminal to verify access before closing the current session.
How do you prevent SSH brute-force attacks?
Disable password authentication entirely (PasswordAuthentication no in sshd_config): this eliminates brute-force as an attack vector because there are no passwords to guess. Additionally: install fail2ban to ban IPs after failed attempts, use MaxStartups 10:30:100 to limit unauthenticated connections, and restrict SSH access to specific users via AllowUsers. Changing the port from 22 reduces automated scanner noise but is not a security control.
What SSH configuration settings improve security?
A minimal hardened sshd_config includes: PasswordAuthentication no (require keys), PermitRootLogin no (prohibit root login), Protocol 2 (disable SSHv1), MaxAuthTries 3 (limit guessing attempts), ClientAliveInterval 300 and ClientAliveCountMax 2 (terminate idle sessions after 10 minutes), AllowUsers or AllowGroups to restrict which accounts can use SSH, PermitEmptyPasswords no, X11Forwarding no (disable GUI forwarding if not needed), and LogLevel VERBOSE to capture fingerprints of connecting keys. Also restrict the Ciphers, MACs, and KexAlgorithms settings to modern algorithms: remove rc4, 3des-cbc, and diffie-hellman-group1-sha1.
How do I rotate SSH host keys after a server compromise?
Host keys authenticate the server to clients and prevent man-in-the-middle attacks. After a compromise: generate new host keys (ssh-keygen -t ed25519 -f /etc/ssh/ssh_host_ed25519_key; ssh-keygen -t rsa -b 4096 -f /etc/ssh/ssh_host_rsa_key), remove the old host fingerprints from all client ~/.ssh/known_hosts files (or deploy the new fingerprints via SSHFP DNS records so clients accept the new key automatically), and restart sshd. If the old host keys were accessible during the compromise period, the attacker could use them to impersonate the server in a future man-in-the-middle attack against users connecting to it, so rotation is mandatory after any confirmed server compromise.
What is SSH certificate-based authentication and how is it better than keys?
SSH certificate authentication extends public key authentication with a CA-signed certificate that includes an expiry, a list of permitted principals (usernames), and optional extensions. Unlike raw public keys, certificates expire automatically (e.g., 8-hour certificates for production access), eliminating key sprawl. Add a user: issue a signed certificate rather than copying a public key to every server's authorized_keys file. Revocation is also simpler: update the CA's revocation list rather than finding and removing a key from hundreds of servers. Implement with HashiCorp Vault's SSH secrets engine or a dedicated SSH certificate authority like Teleport.
What SSH hardening settings have the most impact for organizations with servers exposed to the internet?
For internet-exposed SSH, the highest-impact hardening settings in order of impact: disable password authentication entirely (PasswordAuthentication no in sshd_config) -- this eliminates brute force as an attack vector. Restrict AllowUsers or AllowGroups to a specific list of authorized users rather than allowing all system users. Change the default port from 22 to a non-standard port (1024-65535) to reduce automated scanner noise, though this is security through obscurity and does not prevent a targeted attacker. Enable and configure MaxAuthTries to 3 to limit authentication attempt floods before disconnection. Enable LoginGraceTime 30 to disconnect unauthenticated sessions quickly. Restrict Ciphers to remove weak algorithms: use Ciphers chacha20-poly1305@openssh.com,aes256-gcm@openssh.com,aes128-gcm@openssh.com and eliminate 3des-cbc, arcfour, and blowfish. Configure KexAlgorithms to remove diffie-hellman-group1-sha1 and diffie-hellman-group14-sha1, which are vulnerable to Logjam. Enable AllowAgentForwarding no and AllowTcpForwarding no unless specifically required -- these features have been exploited in lateral movement scenarios. For internet-exposed servers handling sensitive workloads, also consider a bastion host or SSH jump server architecture so production servers are never directly internet-accessible.
Sources & references
Free resources
Critical CVE Reference Card 2025–2026
25 actively exploited vulnerabilities with CVSS scores, exploit status, and patch availability. Print it, pin it, share it with your SOC team.
Ransomware Incident Response Playbook
Step-by-step 24-hour IR checklist covering detection, containment, eradication, and recovery. Built for SOC teams, IR leads, and CISOs.
Get threat intel before your inbox does.
50,000+ security professionals read Decryption Digest for early warnings on zero-days, ransomware, and nation-state campaigns. Free, daily, no spam.
Unsubscribe anytime. We never sell your data.

Founder & Cybersecurity Evangelist, Decryption Digest
Cybersecurity professional with expertise in threat intelligence, vulnerability research, and enterprise security. Covers zero-days, ransomware, and nation-state operations for 50,000+ security professionals every morning.
Win a $2,495 Black Hat pass.
Full-access to Black Hat USA 2026 in Las Vegas. Subscribe free to enter.
