Skip to content

SSH Hardening: Key-Based Auth, Fail2ban & Bastion Hosts

SSH is the door to every server you manage — lock it down with the exact config a production host should ship with.

OpenSSHfail2bansystemd
3 min readIntermediate
Lab time: 25-35 min
RR

Ranjith R

Linux SysAdmin & Cloud Engineer

Published July 1, 2026Updated July 6, 2026
Share
Section 01

Prerequisites

  • Root or sudo access on the Linux server you're hardening
  • A local SSH client (OpenSSH — built into macOS/Linux, available via PowerShell on Windows)
  • An existing working SSH connection you won't lock yourself out of mid-change
Section 02

What You'll Learn

  • Generate and deploy an ed25519 key pair for passwordless login
  • Harden sshd_config: disable root login and password auth
  • Install and configure fail2ban to block brute-force attempts
  • Explain bastion-host architecture and why it reduces attack surface
Section 03

Theory

SSH authenticates a connection two separate ways that are often confused: host authentication(the client verifying it's really talking to the intended server, via the server's host key fingerprint) and user authentication(the server verifying who's connecting — by password or by public key). Hardening focuses almost entirely on the second: removing password auth as an option removes the entire class of brute-force and credential-stuffing attacks against SSH.

Why key-based auth is fundamentally stronger

A password is a shared secret that must be transmitted (even over an encrypted channel) and can be guessed, brute-forced, phished, or reused across sites. Public-key auth never transmits the private key at all — the server issues a challenge that only the holder of the matching private key can answer, and a 256-bit ed25519 key is computationally infeasible to brute-force.

fail2ban

fail2ban watches log files (like /var/log/auth.logor the systemd journal) for patterns matching failed login attempts, and after a configurable threshold, adds a firewall rule to block that source IP for a set duration. It doesn't replace key-based auth — it reduces log noise and slows down automated scanners probing every server on the internet.

Section 04

Architecture

A bastion (jump) host is the single, hardened, heavily monitored entry point into a private network — every other server has no direct route from the internet at all, so compromising one exposed service doesn't hand an attacker a path to everything else.

Only the bastion is internet-reachable; app/DB servers accept SSH only from the bastion's security group

Section 05

Hands-On Lab

Step 1 — generate and deploy a key:

ssh-keygen -t ed25519 -C "you@yourlaptop"
# Accept the default path, set a real passphrase when prompted

ssh-copy-id -i ~/.ssh/id_ed25519.pub user@server
# Test it BEFORE touching sshd_config:
ssh -i ~/.ssh/id_ed25519 user@server

Step 2 — harden sshd_config (keep your current session open in a second terminal while testing):

sudo cp /etc/ssh/sshd_config /etc/ssh/sshd_config.bak

sudo tee -a /etc/ssh/sshd_config <<'EOF'
PermitRootLogin no
PasswordAuthentication no
KbdInteractiveAuthentication no
X11Forwarding no
MaxAuthTries 3
EOF

sudo sshd -t                        # validate syntax before restarting — critical
sudo systemctl restart sshd
# From a SEPARATE terminal, confirm key-based login still works before closing your original session

Step 3 — install fail2ban:

sudo apt install fail2ban -y        # Debian/Ubuntu
# sudo dnf install fail2ban -y      # RHEL/Rocky

sudo tee /etc/fail2ban/jail.local <<'EOF'
[sshd]
enabled = true
port = 22
maxretry = 4
bantime = 1h
findtime = 10m
EOF

sudo systemctl enable --now fail2ban
sudo fail2ban-client status sshd    # confirm the jail is active
Section 07

Best Practices

Never test hardening changes in the only open session

Always keep your current SSH session open and validate the new config (sshd -t) and a fresh connection in a second terminal before closing the original — this is the single most effective way to avoid a lockout.

Change the port only as a noise reducer, not a security control

Moving SSH off port 22 reduces automated scanner noise in your logs but provides no real security against a targeted attacker doing a full port scan. Treat it as optional log hygiene, not hardening.
Section 08

Common Mistakes

Disabling password auth before confirming key auth works

If ssh-copy-id silently fails (wrong path, wrong user) and you disable PasswordAuthentication anyway, you lock yourself out entirely unless you have console/out-of-band access. Always complete a successful key-based login first.

Restarting sshd without validating syntax first

A typo in sshd_config that passes systemctl restart sshd silently but fails the actual bind can leave you with no SSH access at all. sshd -t catches syntax errors before you ever restart the service.
Section 09

Troubleshooting

"Permission denied (publickey)" after setup: check that ~/.ssh is 700 and ~/.ssh/authorized_keys is 600 on the server — sshd refuses to use a key file with overly permissive modes, silently, and logs the reason to journalctl -u sshd or /var/log/auth.log. Also confirm you're connecting as the user whose authorized_keys you actually updated.

Locked out after disabling password auth: if you have console access (cloud provider serial console, hypervisor console), log in there, restore /etc/ssh/sshd_config.bak, and restart sshd. This is exactly why the backup copy in the lab above matters.

Section 10

Interview Questions

Generate a key pair (`ssh-keygen -t ed25519`), copy the public key to the target's `~/.ssh/authorized_keys` (via `ssh-copy-id`), then disable password auth in sshd_config once key auth is confirmed working. The risk: an unencrypted private key on a workstation that gets compromised grants immediate access to every server trusting it — always protect the private key with a passphrase and an ssh-agent, and never copy private keys between machines.
See all 21 Linux interview questions
Section 11

Cheat Sheet

Networking

ip aShow all network interfaces and their IP addresses
ss -tulnpShow listening TCP/UDP ports and owning processes
curl -I https://example.comFetch only the HTTP response headers
ping -c 4 hostSend 4 ICMP echo requests to a host
traceroute hostTrace the network path to a host
scp file user@host:/pathCopy a file to a remote host over SSH
Get the full cheat sheet (with PDF download)
FAQ

Frequently Asked Questions

ed25519 keys are shorter (faster to compute), consistently strong at their single supported size, and immune to a class of RSA implementation weaknesses tied to poor random number generation. RSA-4096 is still secure and widely supported for compatibility with very old systems, but ed25519 is the modern default.
Section 12

Summary

Key-based authentication removes the entire brute-force attack class against SSH; sshd_config hardening (no root login, no password auth, low MaxAuthTries) closes the remaining gaps; fail2ban reduces noise from automated scanners; and a bastion-host architecture ensures a single compromised service doesn't expose your entire private network.