SSH Server Hardening
Introduction¶
SSH is the front door to most servers. Hardening it is high-value and mostly a
matter of a dozen sshd_config lines. The one rule: keep a second session
open while you test, so a mistake doesn't lock you out.
The priority order¶
1. Key-only authentication¶
PubkeyAuthentication yes
PasswordAuthentication no
KbdInteractiveAuthentication no # was ChallengeResponseAuthentication
AuthenticationMethods publickey
This alone eliminates password brute-forcing. Make sure your key works before disabling passwords.
2. No direct root login¶
PermitRootLogin no
# or, if automation needs it, key-only + forced command:
# PermitRootLogin prohibit-password
Log in as a normal user, sudo up.
3. Limit who can log in¶
AllowUsers alice bob deploy
# or
AllowGroups sshusers
DenyUsers *
Explicit allow-lists beat "everyone except".
4. Modern crypto¶
Recent OpenSSH defaults are already good; be explicit if you have a compliance need:
KexAlgorithms [email protected],curve25519-sha256,[email protected]
Ciphers [email protected],[email protected],[email protected]
MACs [email protected],[email protected]
HostKeyAlgorithms ssh-ed25519,rsa-sha2-512
Use an ed25519 host key and user keys; drop DSA and small RSA.
5. Reduce exposure¶
LoginGraceTime 20
MaxAuthTries 3
MaxSessions 4
MaxStartups 10:30:60 # start dropping new unauthed conns under flood
ClientAliveInterval 300
ClientAliveCountMax 2
X11Forwarding no
AllowAgentForwarding no
AllowTcpForwarding no # unless you need it
PermitTunnel no
6. Rate-limit at the firewall / fail2ban¶
# nftables
nft add rule inet filter input tcp dport 22 ct state new \
meter ssh { ip saddr limit rate 6/minute } accept
# or fail2ban's sshd jail (see the fail2ban post)
Changing the port to 2222 reduces log noise but isn't real security — do it if you like, keep everything above.
7. Match blocks for exceptions¶
Match User backup
ForceCommand /usr/local/bin/rrsync -ro /srv/backups
PermitTTY no
AllowTcpForwarding no
Match Group sftponly
ChrootDirectory /srv/sftp/%u
ForceCommand internal-sftp
AllowTcpForwarding no
8. SSH certificates (at scale)¶
Instead of distributing authorized_keys everywhere, run a small CA and sign
user keys with short lifetimes:
TrustedUserCAKeys /etc/ssh/ca_user_key.pub
ssh-keygen -s ca_user_key -I [email protected] -n alice,deploy -V +8h alice_key.pub
Now alice logs in anywhere the CA is trusted, and access auto-expires in 8h.
Revocation via RevokedKeys.
Applying safely¶
sudo sshd -t # syntax check
sudo sshd -T | grep -Ei 'permitroot|password|pubkey' # effective config
# Keep your current session. Restart in a way you can undo:
sudo systemctl restart ssh # (or reload)
# In a NEW terminal, confirm you can still log in:
ssh -v you@server
If the new session fails, fix it from the still-open one. Some people add a
sleep 300 && systemctl restart ssh.bak safety timer, or use systemd-run with
a timeout.
Verification and troubleshooting¶
ssh -vvv you@server # client-side: which auth methods, key offered
sudo journalctl -u ssh -f # server-side: why an auth failed
sudo sshd -T # the actual running config values
- Locked out after
PasswordAuthentication no— key wasn't actually working. Recover via console/cloud serial console/rescue; check~/.ssh/authorized_keysperms (dir 700, file 600, owned by the user) andsshd -T | grep pubkey. Permission denied (publickey)— key not inauthorized_keys, wrong key offered (IdentityFile/ssh-add -l), home dir group-writable (sshd refuses), or SELinux context on.ssh(restorecon -Rv ~/.ssh).Matchblock not applying —Matchmust come after the global settings; everything after aMatchuntil the nextMatch/EOF is conditional.- Forwarding disabled broke a workflow — re-enable per-user with a
Matchblock rather than globally. - fail2ban banned you —
fail2ban-client set sshd unbanip <ip>; whitelist your admin ranges inignoreip. - Slow login —
UseDNS no(reverse-DNS lookup on connect),GSSAPIAuthentication noif not using Kerberos.
Related tools and reading¶
- On-site: Port Checker (22).
- Related posts: sudoers done safely, FTP active vs passive, and why SFTP won.
Stuck on something this site can't fix?Reach out to Prabath directly on LinkedIn.More in Linux & Sysadmin
auditd Basics
auditd records who did what at the syscall level — file access, config changes, privilege use. Here's how to write useful rules without drowning the disk.
September 16, 2026Finding What Is Using a Port on Linux
The three commands that answer 'address already in use', including the container and namespace cases that trip people up.
September 16, 2026