Audit and Harden sshd Configuration
Disable password and root login, restrict algorithms to current best practice, verify with ssh -Q and a controlled restart, and keep an escape session open.
Task
Harden an SSH daemon without locking yourself out, verifying each change before and after the restart.
When to use this
A shared legacy host still accepts root password logins over SSH. The log shows thousands of automated attempts per day from botnets.
Prerequisites
- Root access to the host and its sshd_config
- A working key-based login for a non-root user
- A second open session as an escape hatch during restarts
Steps
1. Read the effective configuration before changing anything.
sshd -T | grep -iE "^(passwordauthentication|permitrootlogin|pubkeyauthentication|kbdinteractiveauthentication|x11forwarding|maxauthtries)"Expected output: The effective values confirm the attack surface, for example passwordauthentication yes and permitrootlogin yes.
2. Apply the hardening directives.
# /etc/ssh/sshd_config.d/10-hardening.conf
PasswordAuthentication no
KbdInteractiveAuthentication no
PermitRootLogin no
PubkeyAuthentication yes
MaxAuthTries 3
X11Forwarding no
AllowStreamLocalForwarding no
Expected output: Only key authentication for non-root users remains, and the daemon stops advertising weaker options entirely.
3. Validate the syntax before the controlled restart.
sshd -t && echo "config valid"Expected output: A syntax check passes; a failed check prints the offending line without touching the running daemon.
4. Restart while keeping an escape session open.
systemctl restart sshd && ssh -o PasswordAuthentication=yes deploy@host true 2>&1 | tail -1Expected output: The escape session survives the restart, and the new connection confirms password auth is refused while key auth still works.
5. Confirm the noise floor dropped.
journalctl -u sshd --since "1 hour ago" | grep -cE "Failed password|Invalid user" ; sshd -T | grep -c "^passwordauthentication no"Expected output: Failed password attempts disappear from the log because the daemon refuses them outright rather than evaluating them.
Troubleshooting
Problem
Locked out after restart
Solution
The escape session is the recovery path: revert the config file from it and restart; this is why the session stays open until the new connection is verified.
Problem
Legacy clients fail after algorithm restriction
Solution
Some old clients lack the required key exchange; inventory who connects, upgrade the stragglers before removing their algorithms, and stage removals.
Problem
Automated tooling depends on root login
Solution
Give the automation its own restricted account with command= forced entries, rather than re-enabling root for one script.
Limitations
- Hardening the daemon does not patch the host; key hygiene, patching, and network exposure remain part of the story.
- Restrictive algorithms break genuinely old clients; schedule removals rather than flipping them during an emergency.
Close the doors you never use
Every enabled authentication method is an attack surface, and password auth on an internet-facing sshd is a standing invitation to every botnet. Hardening is subtractive: key auth only, no root login, no password fallback, fewer rounds of guessing allowed. The discipline is procedural: read the effective config, validate the syntax, restart with an escape session open, and verify the new rules from a fresh connection before closing the old one.
# /etc/ssh/sshd_config.d/10-hardening.conf
PasswordAuthentication no
KbdInteractiveAuthentication no
PermitRootLogin no
PubkeyAuthentication yes
MaxAuthTries 3
Effective config beats file archaeology
Include files and distro overrides make the on-disk config unreliable as a description of behavior. sshd -T prints what the daemon would actually do right now. Audit from that output, change with a drop-in file rather than editing a managed file, and re-read the effective output after the restart to confirm reality matches intent.
The escape session is not optional
Every sshd restart is a chance to convert a hardening exercise into an outage. Keep one authenticated session open across the restart, and do not close it until a brand-new connection has proven that key auth works and password auth is refused. The session costs nothing and is the difference between a mistake and a drive to the datacenter.