OpenSSH Server Hardening¶
Directives live in /etc/ssh/sshd_config. Always validate before reloading:
sudo sshd -t # syntax check
sudo systemctl reload sshd
For the full current baseline (crypto algorithms, Match blocks, certificate trust, logging) see openssh-comprehensive-guide.md §6 and §10 — this doc is a working notes/rationale version. A ready-to-use hardened sshd_config is included there.
1. Restrict access by user/group¶
AllowUsers user_name1 user_name2
# or scoped by source IP:
AllowUsers user_name1@192.168.1.3 user_name2@127.0.0.1
DenyUsers service_account_that_should_never_ssh_in
AllowGroups sshaccess
DenyGroups developers qa
Allow* and Deny* directives are evaluated together; a user must pass both to connect. Prefer AllowGroups over per-user AllowUsers once you have more than a handful of accounts — group membership is easier to audit and rotate.
2. Change the listening port¶
Port 2222
Treat this as noise reduction against opportunistic scanners, not a security control — it does nothing against a targeted attacker doing a full port scan. Don't let it substitute for the auth/crypto hardening below.
3. Disable root login and password auth¶
PermitRootLogin no
PasswordAuthentication no
ChallengeResponseAuthentication no
KbdInteractiveAuthentication no
Confirm key-based login works for your account before disabling password auth — test in a second session, don't disconnect your only working session first.
4. Shorten the login grace period¶
LoginGraceTime 20
Limits how long an unauthenticated connection can sit open, reducing exposure to slow-auth DoS patterns.
5. Bind to a specific address¶
ListenAddress 192.168.0.1
Useful when a host has both a management NIC and a public-facing NIC and SSH should only be reachable on the former.
6. Version banner¶
VersionAddendum "Unauthorized access prohibited"
Cosmetic/legal-notice value only — doesn't hide the actual OpenSSH version, which is still visible in the protocol banner itself.
7. Two-factor authentication (TOTP via PAM)¶
Adds a second factor on top of (not instead of) public key auth. The common approach on Linux is libpam-google-authenticator:
sudo apt install libpam-google-authenticator # Debian/Ubuntu
google-authenticator # run as the target user; generates a QR code + backup codes
# /etc/pam.d/sshd — add near the top
auth required pam_google_authenticator.so
# sshd_config
KbdInteractiveAuthentication yes
AuthenticationMethods publickey,keyboard-interactive
UsePAM yes
This requires both a valid key and a valid TOTP code — pubkey alone is no longer sufficient once AuthenticationMethods lists both. Test thoroughly in a second session before rolling out broadly; a misconfigured PAM stack can lock out all SSH access.
8. Fail2ban¶
Rate-limits/bans repeat authentication failures at the network level — complements, doesn't replace, the above. See openssh-comprehensive-guide.md §11 for a working jail config.
Deprecated / no longer recommended¶
The following were once-common hardening steps that are now either meaningless or actively wrong on current OpenSSH. Listed here because they show up in a lot of older guides (including earlier versions of these notes):
| Old advice | Why it's obsolete |
|---|---|
Protocol 2 |
SSH-1 support was removed from OpenSSH entirely (long ago) — there's no Protocol 1 to disable anymore, and the directive is a no-op / may error on modern versions. |
ServerKeyBits 4096 |
This directive is a leftover from SSH-1 server key regeneration and doesn't apply to SSH-2 host keys. Host key strength is set by key type (ssh-keygen -t ed25519), not this directive. |
Cipher blowfish / Ciphers blowfish-cbc,arcfour,aes256-cbc,... |
Blowfish, arcfour (RC4), and all -cbc ciphers are weak or deprecated. Use AEAD ciphers only — see the algorithm tables in the comprehensive guide §13. |
TCP Wrappers (/etc/hosts.allow, /etc/hosts.deny, libwrap) |
glibc dropped TCP Wrappers support years ago; most current OpenSSH builds aren't even linked against libwrap. Use AllowUsers/AllowGroups in sshd_config, or a real firewall (nftables/firewalld/security groups), instead. |
Switching sshd to socket activation for "TCP wrapper" style access control |
Same issue as above — this was working around TCP Wrappers limitations that no longer apply. Not worth the added complexity now; use firewall rules. |