Skip to content
Security

SSH hardening

An sshd configuration that resists everything short of a targeted attacker: Ed25519 keys, modern ciphers only, fail2ban, TOTP and hardware tokens.

10 min readUpdated

SSH is the single most attacked service on any internet-facing server. A default install with password authentication sees thousands of login attempts a day. Everything below is cheap and none of it breaks normal use.

Keys only, and use Ed25519

# On your workstation, not the server
ssh-keygen -t ed25519 -a 100 -C "deploy@$(hostname)"
ssh-copy-id -i ~/.ssh/id_ed25519.pub deploy@YOUR_IP

Ed25519 is faster and shorter than RSA at equivalent strength. -a 100 increases the KDF rounds on the private key, making an offline attack against a stolen key file considerably more expensive.

A sane sshd_config

# /etc/ssh/sshd_config
Port 22
PermitRootLogin no
PasswordAuthentication no
KbdInteractiveAuthentication no
PubkeyAuthentication yes
AuthenticationMethods publickey
MaxAuthTries 3
MaxSessions 5
LoginGraceTime 20
AllowUsers deploy
X11Forwarding no
AllowAgentForwarding no
AllowTcpForwarding yes
ClientAliveInterval 300
ClientAliveCountMax 2

# Modern algorithms only, hybrid post-quantum key agreement first
KexAlgorithms mlkem768x25519-sha256,[email protected],curve25519-sha256,[email protected]
Ciphers [email protected],[email protected]
MACs [email protected],[email protected]
sshd -t && systemctl reload sshd

Always sshd -t first. Always keep a second session open until you have confirmed the new config works.

Two things about that KexAlgorithms line. It names algorithms your sshd may not know — mlkem768x25519-sha256 needs OpenSSH 9.9 or newer and [email protected] needs 8.5 or newer, and an unrecognised name makes sshd refuse to start, which is what sshd -t is for. And it is a list with an expiry date: the hybrid post-quantum schemes here became upstream defaults in 9.0 and again in 10.0, and the next one will arrive the same way. Put a reminder in your calendar to re-read this line once a year, or delete it and take whatever your OpenSSH ships with.

On changing the port

Moving SSH off 22 eliminates essentially all automated noise in your logs. It does not stop a targeted attacker, who will portscan you in seconds. It is worth doing for log hygiene and nothing else — do not treat it as a security control.

fail2ban

apt -y install fail2ban
cat > /etc/fail2ban/jail.local <<'EOF'
[sshd]
enabled = true
maxretry = 3
findtime = 600
bantime = 86400
EOF
systemctl enable --now fail2ban

With password authentication disabled fail2ban is mostly about log volume, but it also drops the cost of a key-guessing flood.

Two-factor for the paranoid

apt -y install libpam-google-authenticator
google-authenticator -t -d -f -r 3 -R 30 -W

# /etc/ssh/sshd_config
AuthenticationMethods publickey,keyboard-interactive
KbdInteractiveAuthentication yes

# /etc/pam.d/sshd — add at the top
auth required pam_google_authenticator.so nullok

This requires a key and a TOTP code. Store the emergency scratch codes somewhere that is not the server.

Hardware keys

OpenSSH 8.2+ supports FIDO2 tokens natively:

ssh-keygen -t ed25519-sk -O resident -O verify-required

The private key cannot be exfiltrated from the token, which defeats the most common realistic compromise: a stolen laptop or a compromised workstation.

Verify your work

ssh -o PasswordAuthentication=yes -o PubkeyAuthentication=no deploy@YOUR_IP
# Expected: Permission denied (publickey)

ssh root@YOUR_IP
# Expected: Permission denied (publickey)

Deploy in the next five minutes.

Pick a location, size the box, pay in crypto. No account signup wall, no ID, no waiting on a human.

7-day money-back guarantee · No KYC · Cancel any time from the panel