Contact us

Managed servers · 7 min read

Locking down SSH on a server that faces the internet

regreSSHion and Terrapin were reminders that SSH needs looking after like anything else. The baseline we put on every server: patches first, keys only, no root login, a short list of who may log in, and an audit we can repeat.

Patches before anything else

No setting in sshd_config fixes a vulnerable daemon. Two recent examples:

  • regreSSHion (CVE-2024-6387). A race condition in how sshd handles signals let anyone run code as root on glibc-based Linux, without logging in. OpenSSH 8.5p1 up to 9.7p1 were affected.
  • Terrapin (CVE-2023-48795). An attacker in the network path could cut messages out of the start of an SSH session when ChaCha20-Poly1305 or Encrypt-then-MAC modes were used. OpenSSH 9.6 added "strict key exchange" to stop it, and both ends need it.

Distributions often backport fixes without changing the version number. So check the package changelog or the distribution's security tracker. ssh -V alone can mislead you.

The baseline

We put our settings in a drop-in file, so a package update doesn't overwrite them:

# /etc/ssh/sshd_config.d/10-baseline.conf
PermitRootLogin no
PasswordAuthentication no
KbdInteractiveAuthentication no
PubkeyAuthentication yes
AuthenticationMethods publickey
AllowGroups ssh-users
MaxAuthTries 3
LoginGraceTime 30
ClientAliveInterval 300
ClientAliveCountMax 2
X11Forwarding no
AllowAgentForwarding no

Test the configuration before reloading, and keep your current session open until a second login works:

sshd -t && systemctl reload sshd

On distributions that use drop-in files, check that the main sshd_config includes them near the top. For most options sshd keeps the first value it reads, so an earlier line can quietly win.

Keys

  • Ed25519 keys for people. One key per person per device, with a passphrase.
  • No shared accounts. Admin work goes through personal accounts and sudo, so the logs show who did what.
  • A list of authorised keys per server, gone through whenever someone leaves. An old key in authorized_keys is a way in that nobody remembers.
  • Keys for deployments and automation restricted in authorized_keys with from=, command= and restrict where possible.

Algorithms

Current OpenSSH defaults are fine. We only remove old algorithms where some old client once forced them back in. ssh-audit shows what the server offers and flags the weak ones:

ssh-audit server.example.com

Who can reach port 22

Moving SSH to another port cuts down the noise in the logs, and that's about all it does. What helps is limiting who can connect at all:

  • Allow port 22 only from known addresses at the firewall, or put it behind a VPN or a bastion host.
  • Rate-limit or ban repeated failures (fail2ban, or limits in nftables).
  • On hosting platforms where shell access is optional, leave it off for accounts that don't need it.

The same checks every time

We check the same things on every server and write the results into the runbook or the audit report:

What How
The configuration actually in force sshd -T \| grep -Ei 'permitroot\|passwordauth\|allowgroups'
Package and patch level the distribution's security tracker
Authorised keys find /home /root -name authorized_keys -exec wc -l {} +
Algorithms offered ssh-audit
Recent logins last -a, journalctl -u sshd

sshd -T prints the configuration after every include has been read. That's the one sshd actually uses.

Got something that needs looking after?

Tell us what you have and what's bothering you. You'll hear back within one working day, usually with a few questions before any price.