SSH Brute Force on an Internet-Exposed Server
A Linux server in a small office is exposed to the internet on port 22 for remote administration. /var/log/auth.log shows 2,000 failed password attempts per day from many IPs, mostly trying root and common usernames. The current /etc/ssh/sshd_config has PermitRootLogin yes, PasswordAuthentication yes, and no firewall rules. The office has a static public IP 203.0.113.10/32, and administrators connect from it. The server also runs a web app on port 443. You must reduce the attack surface while keeping admin access reliable. The office ISP sometimes changes the static IP without notice (rare but possible). There is no VPN or bastion host today, and the server provider offers a web-based serial console.
1. List the hardening steps you would take for SSH and the firewall, in priority order, and explain the trade-offs.
Read the worked answer
Priority:
- Disable root SSH login:
PermitRootLogin no; usesudofrom a normal admin account. - Move to key-based auth: install public keys, set
PasswordAuthentication no,PubkeyAuthentication yes,KbdInteractiveAuthentication no. Test in a second session before closing the first. - Restrict SSH by source IP at the firewall: allow only
203.0.113.10/32to port 22, allow 443 to the world, deny other inbound. This cuts the brute-force noise drastically. - Add rate limiting (fail2ban or
ufw limit) for defense in depth. - Consider
AllowUsersorMatch Addressin sshd config as a second layer. Trade-offs: IP restriction can lock you out if the ISP changes the address; keep console access or a secondary path. Changing the port reduces noise but is not a security control. Key-based auth requires key management and passphrases; password auth is easier but risky.
2. Give the commands to implement the firewall and SSH changes safely on a systemd-based server with UFW, including how to avoid locking yourself out.
Read the worked answer
Firewall with UFW:
sudo ufw default deny incoming
sudo ufw default allow outgoing
sudo ufw allow from 203.0.113.10 to any port 22 proto tcp
sudo ufw allow 443/tcp
sudo ufw enable
sudo ufw status verbose
SSH hardening drop-in /etc/ssh/sshd_config.d/99-hardening.conf:
PermitRootLogin no
PasswordAuthentication no
PubkeyAuthentication yes
AllowUsers [email protected]
Validate and reload:
sudo sshd -t
sudo systemctl reload ssh
Keep the current SSH session open while testing a new login. If the new login fails, revert the drop-in and reload. Do not rely only on AllowUsers; the firewall is the primary control. For the rare ISP change, have a cloud serial console or KVM, and a documented procedure to add the new IP.
3. What would you monitor to confirm the changes are working, and how would you handle the rare ISP IP change?
Read the worked answer
Monitor journalctl -u ssh -f or /var/log/auth.log for Failed password and Accepted publickey; sudo ufw status numbered; ss -tlnp | grep :22. Install fail2ban with an SSH jail to ban repeated failures, but note it is not a substitute for key auth and IP restriction. For ISP changes: keep an out-of-band console (cloud serial, IPMI, KVM), a secondary admin IP/VPN, or a provider API to update the firewall. Test the console before you need it. Document the process and consider a VPN or bastion host so SSH is not exposed to the internet at all; that removes the brute-force problem rather than filtering it.