Network Hardening

SSH Hardening

Key-only auth, disabling root login, and other sshd_config essentials.

SSH is almost always the one service you cannot close, which makes port 22 the single most probed target on the internet. Automated bots hammer it around the clock trying common usernames and passwords. The good news is that a handful of well-understood settings turn SSH from a soft target into a hard one: authenticate with keys instead of passwords, forbid direct root login, restrict which accounts may connect at all, and rate-limit the attempts that get through. This page assumes you have already trimmed listeners (see Reducing Exposed Services) and put a default-deny firewall in front (see Firewalls); SSH hardening locks the door you deliberately left open.

Why password auth is the weak point

A password is a low-entropy secret a human can remember, which is exactly why brute force works against it. An SSH key pair is a high-entropy secret the attacker does not have and cannot guess. Moving to key-only authentication eliminates the entire category of password-guessing attacks in one change. The tradeoff is operational: you must distribute public keys and not lose your private keys, so set up key access and confirm it works before you disable passwords.

Setting up key-based auth

On your client, generate a key pair (if you do not already have one) and copy the public half to the server.

ssh-keygen -t ed25519 -C "adi@lab"
ssh-copy-id ubuntu@lab-vm
ssh ubuntu@lab-vm 'echo key login works'

Expected output:

Number of key(s) added: 1
key login works

The public key lands in ~/.ssh/authorized_keys on the server. Confirm you can log in with the key before changing any server config, or you risk locking yourself out.

The essential sshd_config settings

Edit /etc/ssh/sshd_config (or better, drop a file in /etc/ssh/sshd_config.d/ so upgrades do not clobber it). These are the settings that matter most.

SettingValueWhy
PasswordAuthenticationnoEliminates brute-force password guessing
PermitRootLoginnoForces attackers to compromise a normal account first, and preserves an audit trail via sudo
PubkeyAuthenticationyesEnables the key-based path you just set up
AllowUsersubuntu deployExplicit allow-list; only named accounts may connect
KbdInteractiveAuthenticationnoCloses the other interactive password path
MaxAuthTries3Cuts off a connection after a few failed attempts
X11ForwardingnoRemoves an unneeded feature and its attack surface
sudo tee /etc/ssh/sshd_config.d/99-hardening.conf >/dev/null <<'EOF'
PasswordAuthentication no
KbdInteractiveAuthentication no
PermitRootLogin no
PubkeyAuthentication yes
AllowUsers ubuntu deploy
MaxAuthTries 3
X11Forwarding no
EOF
# Validate the config BEFORE restarting (catches typos that would break sshd)
sudo sshd -t && echo "config OK"
sudo systemctl restart ssh

Expected output:

config OK

PermitRootLogin no matters even with key auth: root is the highest-value account and a shared target, so requiring login as a normal user plus sudo both raises the bar and produces per-user accountability. AllowUsers is a strong control because it fails closed: any account not listed is refused regardless of credentials.

Rate limiting and fail2ban

Even with password auth off, bots will keep connecting and burning resources. Two complementary defences help. First, throttle at the firewall (sudo ufw limit 22/tcp, covered in Firewalls), which slows any single source. Second, fail2ban watches the auth log and temporarily bans addresses that trip a threshold of failures.

sudo apt-get install -y fail2ban
sudo tee /etc/fail2ban/jail.d/sshd.local >/dev/null <<'EOF'
[sshd]
enabled = true
maxretry = 4
bantime = 1h
EOF
sudo systemctl restart fail2ban
sudo fail2ban-client status sshd

Expected output:

Status for the jail: sshd
|- Filter
|  |- Currently failed: 0
|  `- Total failed:     12
`- Actions
   |- Currently banned: 1
   `- Total banned:     3

Note that with key-only authentication the volume of successful-looking attacks drops to near zero; fail2ban is then mostly about cutting noise and load rather than being your primary defence. Changing the SSH port away from 22 reduces log noise from untargeted scans but is not real security on its own, so treat it as cosmetic, not a control.

Practical Guidance

  1. Set up and test key-based login first, then disable PasswordAuthentication; never disable passwords before confirming a key works, or you can lock yourself out.
  2. Set PermitRootLogin no and log in as a normal user with sudo, both to raise the bar and to get per-user accountability.
  3. Use AllowUsers as an explicit allow-list, since it fails closed and refuses any account you did not name.
  4. Always run sudo sshd -t to validate the config before restarting, and keep a second session open in case the new config is wrong.
  5. Layer rate limiting: ufw limit 22/tcp at the firewall plus fail2ban on the auth log to blunt automated scanning.
  6. Do not rely on changing the SSH port as a security measure; it reduces log noise but does not stop a targeted attacker.