Cyber Security

SSH Hardening for Home Servers — From Key-Based Auth to fail2ban

SSH Hardening for Home Servers — From Key-Based Auth to fail2ban

Some time ago I was idly opening the SSH log on my home server - a used laptop that I used as a server in the corner of my room. What I found was quite eye-opening: hundreds of login attempts from foreign IPs every day. Everyone tried classic username combinations like root, admin, ubuntu, then guessed the password blindly. My server doesn't even use standard port 22, but bots on the internet still find it.

This is where it hit me: installing SSH on a server is like installing a door on a house. A door alone isn't enough — you need a good lock, and if necessary a security camera too. This article summarizes the SSH hardening steps I implemented myself: key-based authentication, fail2ban, and some small configurations that had a big effect.

Why is a password alone not enough?

A password is like a lock with a combination lock. As long as the lock is complex enough, it is theoretically safe. The problem is, there are thousands of bots on the internet whose job is only one: to try all the combinations, constantly, tirelessly, without payment. This is called brute force attack.

Imagine a stranger standing in front of your fence and trying one by one the keys from a giant key chain containing millions of keys. Maybe he won't make it today. But if he stands there for a year, his chances go up steadily. Especially if you use a "fairly common" password — date of birth, girlfriend's name, or P@ssw0rd123.

The most elegant solution: replace combination locks with locks that can only be opened by one unique physical key — and that key cannot be guessed, cannot be photographed, cannot be guessed from the shape of the lock. That's key-based authentication.

Step 1: Key-Based Authentication

SSH keys consist of a pair of files: private key (the key that you store tightly on your computer) and public key (the lock that you install on the server). The server will only open the door to those holding a matching private key — and guessing the private key by brute force is practically impossible, because the space of possibilities is astronomical.

On the client computer (laptop/PC that you usually use for SSH), generate key pair:

ssh-keygen -t ed25519 -C "adam@laptop-utama"

I recommend ed25519 over RSA — shorter, faster, and more modern. When asked for a passphrase, fill it in. This passphrase is an extra layer of security: if your private key is stolen, the thief still needs the passphrase to use it.

Then send the public key to the server:

ssh-copy-id -p 6393 [email protected]

Or if ssh-copy-id is not available, do it manually:

cat ~/.ssh/id_ed25519.pub | ssh -p 6393 [email protected] \
  "mkdir -p ~/.ssh && chmod 700 ~/.ssh && cat >> ~/.ssh/authorized_keys && chmod 600 ~/.ssh/authorized_keys"

Test first to log in using the key in the new terminal. Don't close an active SSH session — this is the golden rule when opening SSH. If the configuration is wrong, you still have one open door to fix it.

Step 2: Turn off Login Password

After key-based auth runs smoothly, it's time to remove the old combination padlock. Edit the SSH server configuration in /etc/ssh/sshd_config:

PasswordAuthentication no
PubkeyAuthentication yes
ChallengeResponseAuthentication no
PermitRootLogin no
MaxAuthTries 3

Brief explanation:

  • PasswordAuthentication no — brute force password instantly becomes irrelevant. Bots can try millions of combinations, the server will never ask.
  • PermitRootLogin no — never allow direct login as root. Login as a normal user, then sudo. If someone manages to enter, at least he won't immediately become king.
  • MaxAuthTries 3 — limit attempts per connection. Slows down attackers and keeps logs cleaner.

Reload SSH service:

sudo systemctl reload ssh

Again: test login from the new terminal before closing the old session.

Step 3: fail2ban — Automated Security Guard

Key-based auth makes it impossible for brute force to succeed, but the bots still fill up logs and waste resources. This is where fail2ban comes into play — like a security guard noting the face of the person who tried the wrong key three times, then banning him from approaching for an hour.

sudo apt install fail2ban
sudo cp /etc/fail2ban/jail.conf /etc/fail2ban/jail.local

Edit /etc/fail2ban/jail.local, [sshd] section:

[sshd]
enabled = true
port = 6393
maxretry = 3
findtime = 600
bantime = 3600

This means: if an IP fails to log in 3 times in 10 minutes (findtime), block that IP for 1 hour (bantime). Don't forget to adjust port if you use a custom port like me.

sudo systemctl enable --now fail2ban
sudo fail2ban-client status sshd

The last command displays jail statistics: how many IPs are currently blocked, how many in total have been blocked. It feels quite satisfying to see that list.

Step 4: Custom Port — Security Through Invisibility

Moving SSH from port 22 to another port (I use 6393) is often debated. Critics say it's "security through obscurity" — and they're right, it's not the ultimate security mechanism. But to top it off, the effect was real: my log which was previously full of hundreds of login attempts per day dropped drastically.

The analogy is like moving the door of a house from the side of the main road to a small alley. The fences and padlocks are still there to guard — but at least not everyone passing by is trying the door handle.

# di /etc/ssh/sshd_config
Port 6393

Don't forget to open a new port on the firewall before closing port 22, and make sure your new port doesn't conflict with other services.

Step 5: Limit Who Can Enter

Two additional safeguards worth considering:

Restrict users — only allow certain users to SSH:

# di /etc/ssh/sshd_config
AllowUsers adam

IP Restriction — if you only access from a certain network (for example only from your home LAN or via VPN like WireGuard/Tailscale), limit it at the firewall level with nftables:

sudo nft add rule inet filter input ip saddr 192.168.1.0/24 tcp dport 6393 accept
sudo nft add rule inet filter input tcp dport 6393 drop

With this, the SSH port is not even visible from outside the network. But be careful: if you're away from home and your VPN has problems, you could be locked out of your own server. Always have a backup plan — I myself rely on Cloudflare tunnels as an alternative route.

Final Checklist

  • Key-based auth is active, password login is disabled
  • PermitRootLogin no
  • fail2ban running and jail sshd active
  • Custom port (optional, but clears logs)
  • AllowUsers and/or IP restrictions in firewall
  • The old SSH session remains open until all configurations are proven to work

Closing

SSH hardening is not a one-time job. It's more like tending to the house fence: occasionally check the logs (journalctl -u ssh), see who fail2ban blocked, and make sure the configuration hasn't changed after a system update. The five steps above are enough to make your small server an unattractive target — bots will give up and move on to another victim whose door still has a combination lock.

If you have other hardening tips that have proven to be effective on your home server, please share them in the comments column. Field experience is always more valuable than theory.