SSH is the primary way Linux servers are administered. It is also the most common attack vector — automated bots scan the internet constantly, attempting brute-force logins against SSH on port 22. A default SSH configuration is a liability. This guide covers the essential hardening steps that every production server should have in place.
These practices range from quick wins (disabling password auth) to advanced techniques (certificate-based authentication and jump hosts). Implement them in order of priority for maximum security impact.
1. Disable Password Authentication
The single most impactful change you can make. Password authentication is vulnerable to brute-force attacks, credential stuffing, and weak passwords. Key-based authentication is immune to all of these.
# /etc/ssh/sshd_config
PasswordAuthentication no
ChallengeResponseAuthentication no
UsePAM yes
# Restart sshd to apply
$ sudo systemctl restart sshd
Before disabling password auth, ensure you have at least one SSH key configured and tested for your account. Lock yourself out by disabling passwords before adding a key, and you will need physical or console access to recover.
2. Use Strong SSH Keys
Not all SSH keys are created equal. Use Ed25519 keys — they are shorter, faster, and more secure than RSA. If you must use RSA (legacy compatibility), use at least 4096 bits.
# Generate an Ed25519 key (recommended)
$ ssh-keygen -t ed25519 -C "[email protected]"
# If Ed25519 is not supported, use RSA-4096
$ ssh-keygen -t rsa -b 4096 -C "[email protected]"
# Copy key to server
$ ssh-copy-id -i ~/.ssh/id_ed25519.pub user@server
Always use a passphrase on your private key. Without one, anyone who gains access to your laptop or workstation has immediate access to all your servers. Use ssh-agentto avoid typing the passphrase repeatedly.
3. Disable Root Login
Never allow direct root login via SSH. Use a regular user account and escalate withsudo when needed. This provides an audit trail of who logged in and what they did.
# /etc/ssh/sshd_config
PermitRootLogin no
# Or allow root only with key (no password)
PermitRootLogin prohibit-password
4. Change the Default Port
Moving SSH off port 22 is not a security measure by itself — it is security through obscurity. But it dramatically reduces noise from automated scanners. Most bots only try port 22.
# /etc/ssh/sshd_config
Port 2222
# Update firewall before restarting sshd!
$ sudo ufw allow 2222/tcp
$ sudo systemctl restart sshd
# Connect on the new port
$ ssh -p 2222 user@server
Use your ~/.ssh/config file to remember custom ports per host, so you do not have to type -p 2222 every time.
5. Install fail2ban
fail2ban monitors log files for failed login attempts and automatically bans offending IP addresses using firewall rules. It is the standard defense against brute-force attacks.
$ sudo apt install fail2ban
$ sudo cp /etc/fail2ban/jail.conf /etc/fail2ban/jail.local
# /etc/fail2ban/jail.local
[sshd]
enabled = true
port = 2222 # match your SSH port
maxretry = 3
bantime = 3600 # 1 hour
findtime = 600 # within 10 minutes
Check banned IPs with sudo fail2ban-client status sshd. For cloud environments, consider using cloud-native security groups in addition to fail2ban for defense in depth.
6. Restrict Access with AllowUsers and AllowGroups
Limit which users can log in via SSH. This prevents service accounts, application users, and other non-administrative accounts from being used for SSH access.
# /etc/ssh/sshd_config — only these users can SSH in
AllowUsers admin deploy
# Or use groups (more scalable)
AllowGroups ssh-users
# Add users to the group
$ sudo usermod -aG ssh-users admin
7. Use Jump Hosts (Bastion Servers)
In production environments, internal servers should not be directly accessible from the internet. Use a hardened bastion (jump) host as the single entry point, and tunnel through it to reach internal hosts.
# Direct jump (OpenSSH 7.3+)
$ ssh -J bastion.example.com internal-server
# ~/.ssh/config — make it permanent
Host internal-*
ProxyJump bastion.example.com
User admin
# Now just: ssh internal-db
The bastion host should have the strictest hardening: no unnecessary software, key-only auth, fail2ban, and full audit logging. Restrict it to a single purpose — no other services should run on it.
8. Additional Hardening Options
# /etc/ssh/sshd_config — recommended hardening
MaxAuthTries 3 # limit login attempts per connection
LoginGraceTime 30 # seconds to authenticate
ClientAliveInterval 300 # disconnect idle sessions
ClientAliveCountMax 2 # after 2 missed keepalives
X11Forwarding no # disable X11 unless needed
AllowTcpForwarding no # disable port forwarding
AllowAgentForwarding no # disable agent forwarding
PermitEmptyPasswords no
Also consider using SSH certificates instead of authorized_keys files for large teams. With certificates, a central CA signs user keys, and servers trust the CA — no need to distribute public keys to every server. Tools like Vault, step-ca, or Teleport make this manageable.
Quick Reference: sshd_config Hardening Checklist
✓ Disable password authentication
✓ Use Ed25519 keys with passphrases
✓ Disable root login (or prohibit-password)
✓ Change default port from 22
✓ Install and configure fail2ban
✓ Restrict users with AllowUsers/AllowGroups
✓ Use jump hosts for internal servers
✓ Set MaxAuthTries, LoginGraceTime, idle timeouts
✓ Disable X11, TCP, and agent forwarding
✓ Monitor auth logs and set up alerts