SSH is the gateway to your infrastructure. A compromised SSH key or misconfigured daemon can lead to complete server takeover. Hardening SSH is one of the highest-impact security improvements you can make.
Disable Password Authentication
Password authentication is susceptible to brute-force attacks. Disable it and use key-based authentication only:
# /etc/ssh/sshd_config
PasswordAuthentication no
ChallengeResponseAuthentication no
UsePAM no
PubkeyAuthentication yes
After making changes, restart the SSH daemon:
sudo systemctl restart sshd
Always keep an active SSH session open while testing changes. If something breaks, you can debug before the session closes.
Use Ed25519 Keys
Ed25519 keys offer better security and performance than RSA:
ssh-keygen -t ed25519 -a 100 -f ~/.ssh/id_ed25519
The `-a 100` option increases the KDF rounds for the private key file, making it harder to crack if the file is stolen. For legacy systems that require RSA, use at least 4096-bit keys:
ssh-keygen -t rsa -b 4096 -a 100 -f ~/.ssh/id_rsa
Restrict Key Usage
Limit what individual keys can do using the `authorized_keys` file:
# ~/.ssh/authorized_keys
restrict,command="/usr/bin/git-shell",from="192.168.1.0/24" ssh-ed25519 AAA...
The `restrict` keyword denies all forwarding and agent access. `command=` limits the key to a specific command. `from=` restricts which IP addresses can use this key.
Disable Root Login
Never allow direct root SSH access:
PermitRootLogin no
Use a regular user account with `sudo` access instead. This creates an audit trail -- every privileged command is logged with the user who ran it.
Change the Default Port
Changing the default port (22) reduces automated attack noise:
Port 2222
This is not real security (a determined attacker will find your SSH port), but it dramatically reduces log noise from automated scanners.
Use a Strong Cipher Configuration
Modern SSH supports strong ciphers. Enforce them:
Ciphers chacha20-poly1305@openssh.com,aes256-gcm@openssh.com
MACs hmac-sha2-256-etm@openssh.com,hmac-sha2-512-etm@openssh.com
KexAlgorithms curve25519-sha256,diffie-hellman-group16-sha512
HostKeyAlgorithms ssh-ed25519,rsa-sha2-512
This configuration only allows algorithms with proven security. Remove legacy algorithms like `diffie-hellman-group14-sha1` and `hmac-sha1`.
SSH Timeout and Session Limits
Prevent idle sessions from accumulating:
ClientAliveInterval 300
ClientAliveCountMax 2
TCPKeepAlive no
MaxSessions 10
MaxStartups 10:30:60
`ClientAliveInterval=300` with `ClientAliveCountMax=2` means the server checks every 5 minutes and disconnects after 10 minutes of inactivity. `MaxStartups` limits concurrent unauthenticated connections to prevent DoS attacks.
Fail2Ban Integration
Install and configure Fail2Ban to block brute-force attempts:
sudo apt install fail2ban
# /etc/fail2ban/jail.local
[sshd]
enabled = true
port = ssh
filter = sshd
logpath = /var/log/auth.log
maxretry = 3
bantime = 3600
findtime = 600
This bans IPs for one hour after three failed attempts within ten minutes. For internet-facing SSH, consider longer ban times (24 hours) to be more aggressive.
Two-Factor Authentication
Add a second factor with `libpam-google-authenticator`:
sudo apt install libpam-google-authenticator
google-authenticator
# /etc/pam.d/sshd
auth required pam_google_authenticator.so
# /etc/ssh/sshd_config
ChallengeResponseAuthentication yes
AuthenticationMethods publickey,keyboard-interactive
This requires both an SSH key and a TOTP code to authenticate. Use this for high-value jump boxes and production servers.
SSH Agent Forwarding
Be careful with agent forwarding. Use `-J` (jump host) instead when possible:
# Instead of forwarding your agent through a chain:
ssh -J bastion.example.com target.internal
# Or use ProxyJump in ~/.ssh/config:
Host internal-*
ProxyJump bastion.example.com
If you must use agent forwarding, use `ssh -A` with the `-t` flag for a single session rather than enabling forwarding globally in your config.
Key Rotation
Regularly rotate SSH keys and audit authorized keys:
#!/bin/bash
# audit-ssh-keys.sh
for user in $(getent passwd | cut -d: -f1); do
home=$(getent passwd "$user" | cut -d: -f6)
if [[ -f "$home/.ssh/authorized_keys" ]]; then
echo "User: $user"
cat "$home/.ssh/authorized_keys"
fi
done
Remove keys belonging to departed team members and replace keys that are over a year old. Integrate key management with your identity provider (Okta, Azure AD) using tools like `ssh-ca` for certificate-based auth.
Monitor SSH Access
Monitor SSH access in real-time:
# Watch auth log for SSH activity
tail -f /var/log/auth.log | grep sshd
# Audit currently logged-in users
w
# Last login times for all users
lastlog
Set up alerts for SSH logins from unexpected IP ranges or at unusual hours using log ingestion tools.
Summary
SSH hardening follows defense in depth: disable passwords, use Ed25519 keys, lock down the daemon configuration, and add Fail2Ban. For production systems, add two-factor authentication and eliminate agent forwarding in favor of jump hosts. Regularly audit authorized keys and rotate them. Most SSH compromises come from weak configurations, not zero-days -- hardening your SSH setup prevents the majority of attack vectors.