SSH Hardening on Linux
SSH is the door to every server. Most attacks against Linux infrastructure are unsophisticated โ they're login brute-force, credential stuffing, exposed root accounts. A correctly configured SSH daemon defeats most of them with a config file change.
The minimum config
# /etc/ssh/sshd_config (or a drop-in in /etc/ssh/sshd_config.d/)
# Authentication
PermitRootLogin no
PasswordAuthentication no
ChallengeResponseAuthentication no
KbdInteractiveAuthentication no
UsePAM yes
PubkeyAuthentication yes
# Session
MaxAuthTries 3
MaxSessions 5
LoginGraceTime 30
ClientAliveInterval 300
ClientAliveCountMax 2
# Restrict who can log in at all
AllowUsers alice bob deploy
# Or by group:
# AllowGroups ssh-users
# Modern crypto
KexAlgorithms curve25519-sha256,curve25519-sha256@libssh.org
Ciphers chacha20-poly1305@openssh.com,aes256-gcm@openssh.com
MACs hmac-sha2-512-etm@openssh.com,hmac-sha2-256-etm@openssh.com
After editing: sshd -t to test, then systemctl reload ssh. Keep your current session open while you test.
Keys, not passwords
# Generate (ed25519 โ small, fast, modern)
ssh-keygen -t ed25519 -C "alice@laptop-2026"
# Copy to a server
ssh-copy-id alice@server
# Or paste manually
cat ~/.ssh/id_ed25519.pub | ssh server 'cat >> ~/.ssh/authorized_keys'
ssh server 'chmod 700 ~/.ssh && chmod 600 ~/.ssh/authorized_keys'
Port choice
Moving SSH off port 22 is security-through-obscurity, but it does reduce log noise from random scans dramatically. Pick a high port and document it in your ~/.ssh/config. The real protection is still pubkey-only auth.
fail2ban
For the inevitable brute-force noise. fail2ban watches the auth log and adds firewall rules to ban offending IPs after N failures.
# /etc/fail2ban/jail.local
[sshd]
enabled = true
port = ssh
filter = sshd
backend = systemd
maxretry = 3
findtime = 10m
bantime = 1h
Per-key restrictions
An entry in authorized_keys can prefix the key with restrictions. Useful for deploy keys and rsync-only accounts:
from="10.0.0.0/8",no-pty,no-agent-forwarding,no-X11-forwarding,command="/usr/local/bin/rsync-only" ssh-ed25519 AAAA... deploy@ci
Audit
journalctl -u ssh -f # live auth attempts
last -F # successful logins
lastb # failed logins (root only)
ss -tnp state established '( dport = :ssh or sport = :ssh )' # current sessions
2FA
For human accounts on production boxes, add a second factor. Google Authenticator's PAM module is the lowest-friction option โ see TOTP on Linux.