Tutorials

SSH: Key-Only Login and Sensible Hardening

Turn off password authentication, lock down root, and stop the log noise — the twenty minutes that closes the most-attacked door on any server.

beginner ⏱ 25 min Groundwork for Chapter 9 sshlinuxhardening

Every server with port 22 reachable gets scanned. Not targeted — scanned, constantly, by bots working through credential lists. Password authentication is the only thing that makes those attempts worth making. Take it away and the entire category of attack stops mattering.

This assumes you already have a server you can SSH into.

Get a key onto the server first

Do this before touching any config. Locking out password login while your key doesn’t work means a keyboard, a monitor and a trip to wherever the machine lives.

On your client machine:

ssh-keygen -t ed25519 -C "laptop -> homelab"

Ed25519 over RSA: shorter keys, faster, and no parameter choices to get wrong. Use a passphrase — it encrypts the private key at rest, so a stolen laptop isn’t a stolen server. Your agent will cache it, so you type it once per session, not once per connection.

Copy it across:

ssh-copy-id [email protected]

Now prove the key works before you change anything:

ssh -o PasswordAuthentication=no [email protected]

If that drops you at a prompt, you’re safe to continue. If it asks for a password, the key isn’t in place — fix that first.

Keep a second session open

The rule that saves people: open a second SSH session and leave it connected while you edit the config. An existing session survives an sshd restart. If you break the config, that window is how you fix it instead of driving to the machine.

The config

sudo nano /etc/ssh/sshd_config.d/99-hardening.conf

Use a drop-in file rather than editing sshd_config directly — package upgrades leave it alone, and everything you changed is in one readable place.

# Key-only. The reason we're here.
PasswordAuthentication no
KbdInteractiveAuthentication no
PubkeyAuthentication yes

# No direct root login, by key or otherwise. Log in as yourself, then sudo —
# so the logs record which human did what.
PermitRootLogin no

# Don't let a misconfigured client fall back to something weaker.
PermitEmptyPasswords no
AuthenticationMethods publickey

# Limit who may log in at all.
AllowUsers yourname

# Drop unauthenticated connections quickly; don't let them pile up.
LoginGraceTime 20
MaxAuthTries 3
MaxSessions 4

# Turn off features a home server doesn't use.
X11Forwarding no
AllowAgentForwarding no
AllowTcpForwarding yes   # keep: needed for `ssh -L` tunnels to admin UIs

That last one is worth a moment. Local port forwarding is how you reach a service bound to 127.0.0.1 on the server without exposing it to the network:

ssh -L 8080:localhost:8080 [email protected]

Chapter 9 makes the point that Pi-hole has no reason to be externally accessible. An SSH tunnel is how you administer something that isn’t listening anywhere but loopback.

Check, then apply

sshd validates its own config. Always use this before restarting:

sudo sshd -t && echo "config OK"

Only if that prints config OK:

sudo systemctl restart ssh

Now, from a new terminal and without closing the one you already have open:

ssh [email protected]

Works? Done. You can close the safety session.

Quiet the logs with fail2ban

With passwords off, brute-force attempts can’t succeed — but they still fill your logs and waste cycles. fail2ban watches for repeated failures and firewall-bans the source.

sudo apt install -y fail2ban
sudo nano /etc/fail2ban/jail.local
[DEFAULT]
bantime  = 1h
findtime = 10m
maxretry = 3
backend  = systemd
# Never lock yourself out from the LAN
ignoreip = 127.0.0.1/8 ::1 192.168.1.0/24

[sshd]
enabled = true
sudo systemctl enable --now fail2ban
sudo fail2ban-client status sshd

On changing the port

Moving SSH to a high port is often recommended. Be clear about what it buys: it removes almost all undirected scan noise, and it provides no security against anyone who runs nmap against you. It’s log hygiene, not a control. If you already restricted port 22 to your LAN in ufw, you have the real protection and the noise is already gone.

If you want it anyway, add Port 2222 to the drop-in, open it in ufw before restarting, and update your ~/.ssh/config.

Make the client side pleasant

On your laptop, ~/.ssh/config:

Host homelab
    HostName 192.168.1.50
    User yourname
    IdentityFile ~/.ssh/id_ed25519
    IdentitiesOnly yes
    ServerAliveInterval 60

Now it’s just ssh homelab. IdentitiesOnly yes matters once you have several keys — without it your client offers each one in turn and can trip MaxAuthTries before reaching the right one.

Where this leaves you

Password authentication is off, root can’t log in directly, only your user is permitted, and repeat offenders get banned. Next: Docker and Compose.