How to Harden SSH on Linux: 7 Steps

by Liam Foster

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.