SSH is the most-scanned service on the internet. If you're not hardening it, you're leaving your front door open.
I've seen production boxes compromised because someone left password auth on and root login enabled. It's not a question of if attackers will find your SSH port—they will. The question is whether they can get in.
This post covers the specific changes I make to every Linux server I touch, in order of impact. No fluff, no "best practices" that don't matter. Just the config lines that actually stop intrusions.
Disable password authentication entirely
Password auth is the weakest link. Brute-force attacks work because humans choose weak passwords and reuse them across services.
First, ensure your key-based auth works:
ssh -i ~/.ssh/id_ed25519 user@server
Then edit /etc/ssh/sshd_config and set:
PasswordAuthentication no
PubkeyAuthentication yes
Restart SSH:
sudo systemctl restart sshd
Gotcha: Don't disconnect until you've tested login from another terminal. Lock yourself out and you'll need console access to fix it.
Disable root login
Root is the prize. Every scanner on the internet tries ssh root@target. Don't let them.
Set in /etc/ssh/sshd_config:
PermitRootLogin no
If you need root access, use sudo from your unprivileged user. This also creates an audit trail.
Change the default SSH port
Port 22 gets 99% of the noise. Moving to a non-standard port (8022, 2222, anything above 1024) won't stop a determined attacker who knows your IP, but it stops the automated scanners.
Port 2222
Then open that port in your firewall:
sudo ufw allow 2222/tcp
sudo ufw delete allow 22/tcp
Update your SSH config locally so you don't forget:
cat >> ~/.ssh/config << 'EOF'
Host myserver
HostName 192.0.2.1
Port 2222
User deploy
EOF
Now ssh myserver just works.
Disable weak authentication methods
Turn off anything you're not using. Each one is a potential weakness.
PubkeyAuthentication yes
PasswordAuthentication no
PermitEmptyPasswords no
ChallengeResponseAuthentication no
UsePAM no
GSSAPIAuthentication no
KerberosAuthentication no
If you need to support legacy clients, you'll make exceptions. But start restrictive and only add what you need.
Limit login attempts with fail2ban
Even with strong auth, you want to catch brute-force attempts. fail2ban watches SSH logs and blocks IPs after N failed attempts.
Install it:
sudo apt install fail2ban
sudo systemctl enable fail2ban
Create /etc/fail2ban/jail.local:
[sshd]
enabled = true
port = 2222
maxretry = 3
bantime = 3600
findtime = 600
This bans any IP with 3 failed logins in 10 minutes for 1 hour.
Restart fail2ban:
sudo systemctl restart fail2ban
Check active bans:
sudo fail2ban-client status sshd
Use SSH keys with passphrases
Key-based auth is stronger than passwords, but a compromised key is still a problem. Protect your private key with a passphrase.
Generate a new key:
ssh-keygen -t ed25519 -C "user@hostname" -f ~/.ssh/id_ed25519
You'll be prompted for a passphrase. Use one.
Add your key to the server's ~/.ssh/authorized_keys:
ssh-copy-id -i ~/.ssh/id_ed25519.pub user@server
Ed25519 keys are faster and more secure than RSA. If you must use RSA, go 4096-bit minimum.
Monitor SSH logs for anomalies
Hardening is half the battle. The other half is knowing when something's wrong. Attackers who get past your SSH defenses often rely on the same social-engineering tricks covered in this guide on phishing from digitalwarga.id, so understanding the full threat landscape helps.
Watch for repeated failed attempts:
sudo grep "Failed password" /var/log/auth.log | tail -20
Or watch for successful logins from unusual IPs:
sudo grep "Accepted publickey" /var/log/auth.log | tail -20
For production boxes, ship these logs to a central system (syslog, ELK, Datadog, whatever you use). Set up alerts for:
- More than 10 failed attempts per minute
- Successful login from a new IP
- Root login attempts
I use a simple cron job that emails me on suspicious activity:
0 * * * * grep "Failed password" /var/log/auth.log | wc -l | awk '{if ($1 > 20) print "SSH brute-force detected"}' | mail -s "Alert" admin@example.com
This is crude, but it works. For anything bigger than a handful of servers, use proper monitoring.
Apply these changes to your config
Here's a minimal /etc/ssh/sshd_config that passes a security audit:
Port 2222
AddressFamily any
ListenAddress 0.0.0.0
ListenAddress ::
Protocol 2
HostKey /etc/ssh/ssh_host_ed25519_key
HostKey /etc/ssh/ssh_host_rsa_key
PermitRootLogin no
StrictModes yes
MaxAuthTries 3
MaxSessions 10
PubkeyAuthentication yes
AuthorizedKeysFile .ssh/authorized_keys
PasswordAuthentication no
PermitEmptyPasswords no
ChallengeResponseAuthentication no
UsePAM no
X11Forwarding no
PrintMotd no
AcceptEnv LANG LC_*
Subsystem sftp /usr/lib/openssh/sftp-server
Validate the config before restarting:
sudo sshd -t
If it says nothing, you're good. Then restart:
sudo systemctl restart sshd
What to do tomorrow
Start with the easiest wins: disable password auth and root login. Those two changes alone eliminate 90% of automated attacks.
Then move to fail2ban and log monitoring. You can't prevent every attack, but you can catch the ones that slip through and respond quickly.
If you're managing more than a few servers, automate this. Use Ansible, Puppet, or Salt to push the same hardened config everywhere. Consistency beats perfection.
Finally, rotate your SSH keys every 90 days and keep your authorized_keys file clean. Remove old team members' keys immediately.
Hardening SSH isn't a one-time task. It's part of your operational baseline. Do it right, and you'll sleep better at night.