ssh.rocks

sshd_config linter

The server side. Paste /etc/ssh/sshd_config or a drop-in from sshd_config.d/.

What no linter can tell you

This cannot see file order, and file order is what usually bites. sshd reads /etc/ssh/sshd_config.d/*.conf alphabetically and takes the first value it finds for each keyword. Cloud images ship 50-cloud-init.conf containing PasswordAuthentication yes — so a hardening file named 60-hardening.conf is read, parsed, and silently ignored.

The config looks right. The setting is still wrong. There is no error anywhere. Always confirm against what sshd actually resolved:

sshd -T | grep -E 'passwordauthentication|permitrootlogin|kbdinteractive|loglevel'

Never by reading the file. Not even this page's opinion of the file.

The one that catches everyone

Setting PasswordAuthentication no and leaving KbdInteractiveAuthentication yes often changes nothing at all: keyboard-interactive goes through PAM, and PAM asks for a password. People disable password auth, watch a password login still succeed, and conclude the setting does not work.

Turn off both.

A baseline to start from

Named 00- deliberately — see above. Conservative rather than maximal, and worth revisiting on each OpenSSH upgrade: pinning algorithm lists means you stop negotiating anything newer, including post-quantum key exchange.

# /etc/ssh/sshd_config.d/00-hardening.conf
#
# Named 00- ON PURPOSE. sshd reads this directory alphabetically and takes the
# FIRST value it sees for each keyword — and cloud images ship
# 50-cloud-init.conf containing "PasswordAuthentication yes". A file sorting
# after that one is silently ignored, which is the failure mode where the
# config looks right and the setting is still wrong.
#
# Verify with:  sshd -T | grep -E 'passwordauth|permitroot|loglevel'
# Never by reading the file.

# Authentication
PermitRootLogin                 no
PasswordAuthentication          no
KbdInteractiveAuthentication    no      # otherwise PAM hands passwords back
PermitEmptyPasswords            no
PubkeyAuthentication            yes
MaxAuthTries                    3

# Who may log in at all. Without this, every account on the host can.
AllowGroups                     ssh-users

# Log the key fingerprint used for each login. This is what lets you answer
# "which key was that?" months later.
LogLevel                        VERBOSE

# Reduce what a session can do to the host and the network
X11Forwarding                   no
PermitUserEnvironment           no
GatewayPorts                    no
PermitTunnel                    no
StrictModes                     yes

# Modern algorithms only. Revisit on each OpenSSH upgrade: pinning means you
# stop negotiating anything new, including post-quantum key exchange.
KexAlgorithms                   sntrup761x25519-sha512@openssh.com,curve25519-sha256,curve25519-sha256@libssh.org
Ciphers                         chacha20-poly1305@openssh.com,aes256-gcm@openssh.com,aes256-ctr
MACs                            hmac-sha2-512-etm@openssh.com,hmac-sha2-256-etm@openssh.com
HostKeyAlgorithms               ssh-ed25519,ssh-ed25519-cert-v01@openssh.com,rsa-sha2-512

# Drop idle and half-open sessions
ClientAliveInterval             300
ClientAliveCountMax             2
LoginGraceTime                  30