Linux
SSH hardening without locking yourself out
Applies to Linux This applies to OpenSSH on Linux; the drop-in directory and systemctl steps assume a modern Debian, Ubuntu or RHEL-family server.
Hardening SSH is straightforward. Doing it remotely without losing access requires a specific order, and the order is the part people skip.
The safe sequence
Install and test the key before disabling passwords. Open a second session and keep it open while you change anything. Test the new configuration from a third session before closing either.
Never close your working session until a new one has succeeded with the new settings.
ssh-copy-id -i ~/.ssh/id_ed25519.pub user@host
ssh -i ~/.ssh/id_ed25519 user@host # prove it works first
sshd -t # validate config before reload
systemctl reload sshd # reload, not restart
# now open a NEW session and confirm before closing the old one
reload rather than restart keeps existing sessions alive, so a broken configuration does not evict you.
The settings worth having
PermitRootLogin prohibit-password
PasswordAuthentication no
KbdInteractiveAuthentication no
MaxAuthTries 3
LoginGraceTime 20
AllowUsers alice bob
The drop-in ordering trap
Modern distributions use /etc/ssh/sshd_config.d/. sshd takes the first value it reads for any setting, and files are read in lexical order. A cloud-init file named 50-cloud-init.conf re-enabling password authentication will win against your 99-hardening.conf, because 50 sorts before 99.
Name your file so it sorts first — 00-hardening.conf — and verify the result rather than the file:
sshd -T | grep -E 'passwordauthentication|permitrootlogin'
Testing per-source policy
If you use Match Address blocks, test what applies from each vantage point:
sshd -T -C addr=203.0.113.5 | grep passwordauthentication
sshd -T -C addr=10.0.0.2 | grep passwordauthentication
And always follow a Match block with Match all, or everything parsed afterwards falls inside the match without you intending it.
Related reading