SSH Server Hardening: From Passwords to Key-Based Auth + Fail2Ban
Overview
After setting up your droplet and getting key-based login working, don't stop there. Leaving password authentication and root login enabled is the equivalent of locking your front door but leaving the windows open.
This guide covers the essential hardening steps to apply after you confirm your SSH key works, before logging out of your existing session.
Step 1: Verify Key-Based Login Works
Before touching anything, confirm you can log in with your key in a new terminal window:
ssh tony@your-domain-or-ip
If this succeeds without asking for a password, you're good. If it asks for a password, stop here — your key isn't set up correctly yet.
Step 2: Hardening sshd_config
sudo nano /etc/ssh/sshd_config
Disable password authentication — the single most important step. If a password isn't accepted, brute-force attacks become useless.
# Find and change these lines:
PasswordAuthentication no
PermitRootLogin no
Why disable root login? Attackers know
rootexists on every Linux system. Even with a strong password, it's a predictable target. Usesudofrom your regular user account instead.
Restart the SSH daemon to apply changes:
sudo systemctl restart sshd
Step 3: Install and Configure Fail2Ban
Fail2Ban automatically blocks IPs that fail too many login attempts. Zero configuration required — the defaults work well out of the box.
# Update package lists and install
sudo apt update && sudo apt install fail2ban -y
# Enable and start the service
sudo systemctl enable fail2ban
sudo systemctl start fail2ban
# Verify it's running
sudo systemctl status fail2ban
What it does by default:
- Monitors
/var/log/auth.logfor failed SSH attempts - Bans an IP after 5 failed attempts (configurable)
- Bans last 10 minutes (configurable)
- Unbans automatically after the timeout
Tip: You can check current bans with:
sudo fail2ban-client status sshd
Step 4: Configure the Firewall (UFW)
If ufw (Uncomplicated Firewall) is available, enable it:
# Allow SSH through the firewall (if using default port 22)
sudo ufw allow ssh
# Enable UFW (will ask for confirmation)
sudo ufw enable
# Check status
sudo ufw status verbose
Warning: If you're connecting via SSH and haven't allowed it first,
ufw enablewill lock you out. The command above allows SSH first, which is safe.
Step 5: Change the SSH Port (Optional but Recommended)
Port 22 is constantly scanned by bots and attackers. Changing it to something random like 2222 or 49152 drastically reduces automated attack traffic.
sudo nano /etc/ssh/sshd_config
Find the line #Port 22, uncomment it, and change the number:
Port 2222
Choose a port in the range 49152-65535 for unprivileged ports (less likely to conflict with other services). Avoid ports below 1024 unless you have a specific reason — those require root and are reserved for well-known services.
Restart SSH daemon:
sudo systemctl restart sshd
Update UFW if enabled:
# Remove the old rule (ufw allow ssh opens port 22/tcp specifically, not your new port)
sudo ufw delete allow ssh
# Open the new SSH port
sudo ufw allow 2222/tcp
# Verify
sudo ufw status verbose
Important:
ufw allow sshalways resolves to port 22 via/etc/services— it does not auto-detect whatever port sshd is actually listening on. You must explicitly allow the new port number.
Test the new port before closing your session:
# In a new terminal — verify new port works
ssh -p 2222 tony@your-domain-or-ip
If it works, your old session is still alive as a fallback. Once the new port is confirmed working, the old session can be closed.
Step 6: Verify Everything Works (Critical)
Open a second terminal — do not close your current session yet.
In the new terminal, test each of these:
# 1. Can you still SSH in with your key?
ssh -p 2222 tony@your-domain-or-ip
# 2. Is password authentication rejected?
# (Try connecting from a friend's machine or phone to be sure)
# 3. Can you reach your server?
ping your-domain-or-ip
If anything fails, go back to your original session — it's still alive, so you can fix sshd_config and try again.
Quick Reference
| Action | Command |
|---|---|
| SSH with custom port | ssh -p 2222 tony@your-domain-or-ip |
| Restart SSH after config change | sudo systemctl restart sshd |
| Check SSH config syntax | sudo sshd -t |
| View active SSH connections | who |
| Check fail2ban status | sudo fail2ban-client status sshd |
| Unban a specific IP | sudo fail2ban-client set sshd unbanip <IP> |
| View UFW rules | sudo ufw status verbose |
| Disable UFW (emergency) | sudo ufw disable |
Summary Checklist
- Key-based SSH login confirmed working
-
PasswordAuthentication noset in sshd_config -
PermitRootLogin noset in sshd_config - SSH daemon restarted
- Fail2Ban installed and running
- UFW enabled with SSH allowed
- SSH port changed to non-standard (optional)
- New session opened in separate terminal — login works
- Old session kept open until new session confirmed
What's Next
- Set up 2FA for SSH using Google Authenticator or TOTP
- Regularly check
auth.log(sudo tail -f /var/log/auth.log) for suspicious activity - Keep your droplet updated:
sudo apt update && sudo apt upgrade -y
Client-Side: Windows (PuTTY / WinSCP)
If you connect from Windows, you'll need to convert your private key to PuTTY's format (.ppk) since OpenSSH keys and PuTTY don't natively understand each other.
Option 1: PuTTYgen (Convert .pem to .ppk)
- Download PuTTYgen if you don't have it
- Open PuTTYgen — click Load — select your
dropletprivate key file - PuTTY will prompt you to enter your passphrase (if set) — click OK
- Go to File — Save Private Key — save as
droplet.ppk - Never save the key without a passphrase if you can avoid it
Option 2: WinSCP (Import .pem Directly)
WinSCP can import OpenSSH keys directly without conversion:
- Open WinSCP — Login dialog
- Click Tools — Run PuTTYgen (or open PuTTYgen manually)
- Load your
dropletprivate key - Save as
.ppkfile - Back in WinSCP: select the
.ppkfile in SSH — Authentication settings - Login as
tony@your-domain-or-ip
Connecting with PuTTY (Custom Port)
- Open PuTTY
- Session: Host Name =
tony@your-domain-or-ip, Port =2222(your custom port), Connection type =SSH - Connection — SSH — Auth — Private key file: browse to
droplet.ppk - Connection — Data: Auto-login username =
tony - Go back to Session, enter a name under Saved Sessions, click Save
- Click Open to connect
Tip: First time connecting, you'll see a host key fingerprint prompt — click Yes to cache it. This verifies you're connecting to the right server.
Client-Side: macOS / Linux (ssh-agent)
On macOS or Linux, use the OpenSSH agent to cache your passphrase so you don't type it every time.
Add Key to Agent
# Start the SSH agent (macOS may already have it running)
eval "$(ssh-agent -s)"
# Add your private key (will ask for passphrase)
ssh-add ~/.ssh/droplet
Verify Key Is Loaded
ssh-add -l
You should see your key's fingerprint. If it says The agent has no identities, the key wasn't added.
Persist Across Reboots
macOS — add to Keychain:
ssh-add --apple-use-keychain ~/.ssh/droplet
Then add this to ~/.ssh/config:
Host *
AddKeysToAgent yes
UseKeychain yes
IdentityFile ~/.ssh/droplet
Linux — most desktop environments auto-start ssh-agent. You can also add to ~/.bashrc:
eval "$(ssh-agent -s)"
ssh-add ~/.ssh/droplet 2>/dev/null
~/.ssh/config for Custom Port
If you changed your SSH port, store it in your config so you don't type -p 2222 every time:
Host my-droplet
HostName your-domain-or-ip
Port 2222
User tony
IdentityFile ~/.ssh/droplet
Then just run ssh my-droplet — the config handles everything.
Copy Public Key from Linux/macOS Client
On your local machine (not the server):
# Display your public key
cat ~/.ssh/droplet.pub
# Copy to clipboard (macOS)
cat ~/.ssh/droplet.pub | pbcopy
Then paste it into the server's ~/.ssh/authorized_keys file as described in Section 4 of the original guide.
Troubleshooting
| Symptom | Likely Cause | Fix |
|---|---|---|
Permission denied (publickey) |
Key not in authorized_keys |
cat ~/.ssh/droplet.pub on client, paste into server's ~/.ssh/authorized_keys |
Connection refused |
SSH service not running | sudo systemctl start sshd |
Connection timed out |
Firewall blocking port | sudo ufw allow 2222/tcp |
WARNING: UNPROTECTED PRIVATE KEY FILE |
Wrong file permissions | chmod 600 ~/.ssh/droplet |
PuTTY: No supported authentication methods available |
Server disabled password auth but key not configured | Re-enable password auth temporarily, add key, disable again |
Agent admitted failure to sign using the key |
Key not in ssh-agent | ssh-add ~/.ssh/droplet |
| Can't connect after port change | Forgot to specify new port | ssh -p 2222 tony@your-domain-or-ip |