OpenSSH Server Hardening: sshd_config Guide - 夜莺博客

OpenSSH Server Hardening: sshd_config Guide

Hardening SSH is one of the few security tasks with a clean failure mode: if you get it wrong, you lock yourself out of the box you were trying to protect. That makes procedure as important as configuration. The approach below keeps every change in a single drop-in file, validates the syntax before reloading, and never closes the working session until a fresh one has been proven.

The non-negotiable procedure

  1. Confirm key-based login works before disabling passwords: ssh -i ~/.ssh/id_ed25519 user@host.
  2. Write the change into /etc/ssh/sshd_config.d/99-hardening.conf, leaving the vendor file untouched.
  3. Validate: sshd -t.
  4. Reload: systemctl reload sshd (reload, not restart — existing sessions survive).
  5. Open a new session from a different terminal and confirm it works before closing the old one.
sudo install -m 600 -o root -g root /dev/null /etc/ssh/sshd_config.d/99-hardening.conf
sudo nano /etc/ssh/sshd_config.d/99-hardening.conf

The policy block

# Authentication policy
PermitRootLogin no
PubkeyAuthentication yes
PasswordAuthentication no
KbdInteractiveAuthentication no
AuthenticationMethods publickey

# Who may connect
AllowUsers deploy alice
# or, preferably, a group:
# AllowGroups sshusers

# Limit the attack surface
MaxAuthTries 3
LoginGraceTime 30
MaxStartups 10:30:60

# Idle session handling
ClientAliveInterval 300
ClientAliveCountMax 2

# Modern cryptography only
KexAlgorithms curve25519-sha256,curve25519-sha256@libssh.org,ecdh-sha2-nistp521,ecdh-sha2-nistp384
Ciphers chacha20-poly1305@openssh.com,aes256-gcm@openssh.com,aes128-gcm@openssh.com
MACs hmac-sha2-512-etm@openssh.com,hmac-sha2-256-etm@openssh.com

# Keep configuration tidy
PermitEmptyPasswords no
X11Forwarding no
AllowAgentForwarding no
LogLevel VERBOSE

AuthenticationMethods publickey is worth including even though PasswordAuthentication no already implies it: it is an explicit statement of intent, and it blocks any method not listed if a future edit re-enables password auth by accident.

Notes on the individual directives

  • PermitRootLogin no refuses root logins regardless of the authentication method used. If you genuinely need root access, use PermitRootLogin prohibit-password plus a key, and prefer sudo from a named account instead.
  • AllowUsers vs AllowGroups — use groups. A group survives staff changes without a config edit, and a user must satisfy every allow list present if you configure both.
  • MaxStartups 10:30:60 means ten unauthenticated connections, then a 30 % random drop rate, then hard-refuse at sixty. This is a cheap defence against connection floods — but pair it with a firewall rule, because nothing in SSH stops a SYN flood.
  • LogLevel VERBOSE logs the key fingerprint used for a successful login, which is what makes key-based access auditable.

Verifying what is actually in effect

# effective configuration after all includes are merged
sudo sshd -T | grep -E "permitrootlogin|passwordauthentication|allowusers|kexalgorithms"
sudo sshd -t                       # syntax only
systemctl status sshd --no-pager

# prove the login path before trusting it
ssh -v -i ~/.ssh/id_ed25519 deploy@host   # look for "Authentications that can continue: publickey"
ssh -o PreferredAuthentications=password deploy@host   # must FAIL

Auditing from outside removes all doubt: ssh-audit host enumerates the offered algorithms and flags deprecated ones, which is faster than reading a config file and comparing it mentally against a baseline.

Permissions, always

chmod 700 ~/.ssh
chmod 600 ~/.ssh/authorized_keys
chmod 600 ~/.ssh/id_ed25519
# authorized_keys must not be writable by group or other
sudo chown -R deploy:deploy ~deploy/.ssh

Wrong permissions on authorized_keys produce the most confusing symptom in the whole system: a key that is present and correct, silently ignored. If the client says "publickey" was offered and refused, check permissions before anything else.

What people get wrong

  • Forgetting AuthorizedKeysFile context in Match blocks — a Match block that sets a different key path can make one user behave completely differently from the rest.
  • Changing the port and calling it security — it reduces log noise, not risk. The allowlist and key-only policy are what actually matter.
  • Disabling password auth without a tested key — the classic lockout. Always test first.
  • Forgetting IPv6 — an AllowUsers list that assumes IPv4 filtering can still be reached over IPv6 if the firewall only covers v4.

Related: reverse proxy and TLS termination for the services you will expose instead of SSH, and self-hosted Tailscale control with Headscale if you would rather not expose SSH to the internet at all.

原文链接:https://stackharbor.com/en/knowledge-base/sshd-hardening-checklist