SSH Hardening: Key Authentication and Disabling Passwords

SSH is the only remote management channel for most servers, VPS, and cloud instances, and it is the entry point most frequently scanned and brute-forced. According to Mozilla's security guidelines, SSH hardening does not require complex tooling. The core tasks are a handful: switch to key-based authentication, disable password login, block direct root login, tighten the algorithm set, and add Fail2ban for brute-force protection. These changes are completed once and add almost no daily overhead, yet they block the most common weak-password and dictionary attacks at the door.

If you just received a new server, complete the basics in the Server Initialization Security Script article first, then harden SSH step by step.

Why brute force is so common

Most VPS instances are discovered by scanning tools within minutes of boot. /var/log/auth.log often shows records like these:

Feb  3 02:14:31 host sshd[1234]: Failed password for root from 185.220.101.34 port 51234 ssh2
Feb  3 02:14:33 host sshd[1236]: Failed password for root from 185.220.101.34 port 51236 ssh2
Feb  3 02:14:35 host sshd[1238]: Failed password for root from 45.155.205.12 port 40211 ssh2

Most of these come from botnet pools that rotate IPs and keep trying. Pure brute force rarely succeeds against a long, random password — but attackers also combine it with credential stuffing, trying passwords leaked from other platforms. Disabling password authentication kills this attack outright, which is why it offers the best return on effort.

Step 1: Generate and deploy a key pair

Generate a key pair on your workstation, preferring ed25519; use RSA 4096 only when you must support legacy systems.

ssh-keygen -t ed25519 -a 100 -f ~/.ssh/id_ed25519 -C "your-comment"

Then copy the public key to the server (still using a password for the first login):

ssh-copy-id -i ~/.ssh/id_ed25519.pub user@server-ip

Usage notes:

  • Protect the private key with a strong passphrase and never copy it to other machines.
  • Set 600 on the public key file and 700 on the ~/.ssh directory.
  • Use separate keys for separate purposes instead of one key everywhere, and audit existing keys regularly with ssh-keygen -lf.

Step 2: Disable password and direct root login

Edit /etc/ssh/sshd_config and confirm the following settings:

PermitRootLogin no
PasswordAuthentication no
AuthenticationMethods publickey
PubkeyAuthentication yes
LogLevel VERBOSE

Note that PasswordAuthentication no alone does not fully prevent password authentication through PAM. Check /etc/pam.d/sshd as well to make sure password auth is actually disabled. Validate the syntax with sshd -t and restart with systemctl restart sshd. Before confirming the key works, keep an active login session so you do not lock yourself out.

A common practice is to set PasswordAuthentication no first while keeping root login, wait a day or two for key login to stabilize, then add PermitRootLogin no to finish the job with minimal risk.

Step 3: Tighten algorithms and key strength

Mozilla's Modern configuration keeps only ed25519/rsa/ecdsa host keys, uses curve25519 for key exchange, and prefers chacha20-poly1305 and AES-GCM ciphers. It also removes short Diffie-Hellman parameters: all moduli should be at least 3072 bits.

awk '$5 >= 3071' /etc/ssh/moduli > /etc/ssh/moduli.tmp && mv /etc/ssh/moduli.tmp /etc/ssh/moduli

The narrower the algorithm set, the lower the compatibility with older clients. Validate in a test environment before rolling out. The open-source ssh-audit tool quickly checks whether your current sshd algorithm suite matches expectations:

pip install ssh-audit
ssh-audit your-server-ip

Step 4: Fail2ban and access control

Even with key authentication enabled, deploy Fail2ban to block IPs that repeatedly probe the service, and use Match blocks to restrict automation accounts to trusted networks. You can also limit port 22 to specific IPs in your cloud security group or in the firewall guides within the Security Hardening category.

A minimal Fail2ban configuration (/etc/fail2ban/jail.local):

[sshd]
enabled = true
port = ssh
maxretry = 3
bantime = 3600
findtime = 600
ignoreip = 127.0.0.1/8 your-static-ip

Common configuration mistakes and a go-live checklist

These are the most common pitfalls:

  • Changing sshd_config without checking PAM, leaving password auth active.
  • Disconnecting the session before verifying the key, locking yourself out.
  • Overly permissive key file permissions, readable by other users.
  • Changing to a non-default port but forgetting to update firewall and cloud security group rules.

Before going live, verify with the checklist: sshd -T to inspect effective settings, ssh -v to confirm key login, ufw status to confirm which sources can reach port 22, and journalctl -u ssh for login logs. Fold the configuration into the Server Initialization Security Script so it is applied once, and periodically audit authorized_keys with ssh-keygen -lf, removing keys no longer in use.

FAQ

  • What if I travel without my private key after disabling passwords? Keep a spare key, or have a colleague temporarily allowlist your IP and switch back to keys afterward.
  • How do I manage keys across many servers? Organize aliases with SSH config and manage private keys centrally through Secrets Management with HashiCorp Vault instead of letting them scatter.
  • Can I disable passwords for some accounts only? Yes — use Match User or Match Group blocks to apply per-user settings.
  • Should I add a second factor on top? For stricter setups, layer TOTP or a hardware key onto key login (via PAM MFA); it is especially worthwhile on shared or SSO entry points.

16IDC Observation

For site owners and small teams, SSH hardening is the highest-ROI security step: a few lines of configuration remove the most common brute-force path. The real pitfalls are "losing your session after the change" and "keys scattered everywhere". Consider folding key management into a Secrets Management with HashiCorp Vault approach so access and credentials are consolidated end to end.

Source: https://infosec.mozilla.org/guidelines/openssh
Reference: OpenSSH manual https://www.openssh.com/manual.html
Reference: ssh-audit https://github.com/jtesta/ssh-audit