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
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
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.
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
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@serverStep 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 sessionStep 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 activeBest 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.Common Mistakes
Disabling password auth before confirming key auth works
Ifssh-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 passessystemctl 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.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.
Interview Questions
Cheat Sheet
Networking
ip aShow all network interfaces and their IP addressesss -tulnpShow listening TCP/UDP ports and owning processescurl -I https://example.comFetch only the HTTP response headersping -c 4 hostSend 4 ICMP echo requests to a hosttraceroute hostTrace the network path to a hostscp file user@host:/pathCopy a file to a remote host over SSHFrequently Asked Questions
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.