How to audit stale SSH keys with ssh-keygen‑l and fail2ban to stop brute‑force leaks on production servers

Auditing Stale SSH Keys with ssh-keygen -l and Protecting with fail2ban

SSH keys that sit unused for months can become a silent backdoor if a private key ever gets compromised. On a production server the safest way to keep the key list lean is to audit it regularly and block any brute‑force attempts that surface while you’re cleaning up.

1. List all authorized keys with fingerprints

The authorized_keys file lives in each user’s ~/.ssh/. To see which keys are actually present, run:

# Show fingerprints for all keys in /root/.ssh/authorized_keys
ssh-keygen -l -f /root/.ssh/authorized_keys

The -l flag prints a short fingerprint and the key type. If you want a more detailed view (including the comment field), add -E md5 or -E sha256:

ssh-keygen -l -E sha256 -f /root/.ssh/authorized_keys

Output looks like:

2048 SHA256:3Zx... [email protected] (RSA)
4096 SHA256:8Yz... alice@workstation (ECDSA)

Take note of the comment field; it usually tells you who owns the key. If you see an entry that no longer matches an active user or device, flag it for removal.

2. Identify stale keys by age

ssh-keygen itself can’t tell you when a key was added, but you can infer age by checking the modification time of the authorized_keys file and the timestamps of the individual key files if you store them separately. A common practice is to keep each key in its own file and use a symlink in authorized_keys:

# Example layout
/root/.ssh/keys/
├── [email protected]
├── [email protected]

Then run:

find /root/.ssh/keys/ -type f -mtime +90 -print

This lists keys older than 90 days. Adjust the threshold to your policy.

3. Remove or rotate stale keys

Once you’ve identified a key, remove it from authorized_keys and delete the file:

sed -i '/[email protected]/d' /root/.ssh/authorized_keys
rm /root/.ssh/keys/[email protected]

If you prefer to keep a backup, move it to a secure archive instead of deleting outright.

4. Block brute‑force attempts with fail2ban

Even with a clean key list, attackers may try to guess passwords or use stolen keys. fail2ban watches logs and bans IPs that trigger a pattern. For SSH, the default jail is usually already enabled. Verify it:

sudo fail2ban-client status sshd

If it’s not active, enable it:

sudo systemctl enable --now fail2ban

Customizing the SSH jail

The default sshd.conf in /etc/fail2ban/jail.d/ can be tweaked. For example, increase the ban time and limit the number of retries:

[sshd]
enabled = true
port    = ssh
filter  = sshd
logpath = /var/log/auth.log
maxretry = 3
bantime  = 86400   # 24 hours

After editing, reload:

sudo fail2ban-client reload

5. Verify the setup

Attempt a failed login to trigger a ban:

ssh baduser@localhost

Then check the status:

sudo fail2ban-client status sshd

You should see the offending IP in the Ban list. If you’re using a firewall that blocks the IP, the ban is effective.

6. Automate the audit

Add a cron job that runs the key audit script nightly and emails you a report:

0 2 * * * /usr/local/bin/ssh-key-audit.sh | mail -s "SSH Key Audit" [email protected]

ssh-key-audit.sh can wrap the ssh-keygen -l command and compare against a whitelist.

7. Caveats and troubleshooting

Issue Fix
ssh-keygen -l fails on a key file Ensure the file is in OpenSSH format; convert with ssh-keygen -i -f key.pub
fail2ban not banning Check /var/log/fail2ban.log for errors; confirm sshd log path matches logpath
Keys removed accidentally Keep a backup directory; use git to version the authorized_keys file

8. Resources

By combining a regular key audit with fail2ban’s real‑time protection, you keep your production servers free from orphaned credentials and shielded against brute‑force attacks.

See also