SSH Hardening Checklist for 2026 (Ubuntu/Debian)
A practical SSH hardening playbook: key-only auth, Fail2ban, rate limiting, MFA options, and safe rollback steps.
SSH remains a high-value attack surface on Linux. The goal is practical risk reduction without lockouts.
Pre-flight safety
Keep one admin session open, confirm console access, back up config, and validate syntax with sshd -t before restart.
# Back up current config
sudo cp /etc/ssh/sshd_config /etc/ssh/sshd_config.bak
# Always validate before restarting
sudo sshd -tWarning: Never close your current SSH session until you have confirmed a fresh login works in a separate terminal. Locking yourself out of a remote server is the number-one hardening mistake.
Core hardening checklist
1. Enable key-only authentication
Password authentication is the single largest SSH attack vector. Disable it once all users have deployed their public keys.
# /etc/ssh/sshd_config
PasswordAuthentication no
KbdInteractiveAuthentication no
PubkeyAuthentication yes2. Restrict login users/groups
Limit SSH access to an explicit allow-list of users or groups. This prevents any new system account from automatically having SSH access.
AllowGroups ssh-users
# Or restrict to specific users:
# AllowUsers deploy admin3. Disable direct root login
Force operators to authenticate as a named user and then escalate with sudo. This provides an audit trail for every privileged action.
PermitRootLogin no4. Reduce brute-force window
Limit the number of authentication attempts per connection and shorten the login grace period to reduce the window for brute-force attacks.
MaxAuthTries 3
LoginGraceTime 205. Prefer modern crypto defaults
Restrict key exchange, ciphers, and MACs to current-generation algorithms. This eliminates legacy cryptographic weaknesses.
KexAlgorithms curve25519-sha256,[email protected]
Ciphers [email protected],[email protected],[email protected]
MACs [email protected],[email protected]Common pitfall: Overly restrictive algorithm lists can lock out older clients (e.g. legacy CI runners or embedded devices). Test connectivity from all known client types before applying in production.
6. Enable Fail2ban for sshd
Fail2ban watches auth logs and temporarily bans IPs that exceed a threshold of failed login attempts.
sudo apt install fail2ban
sudo systemctl enable fail2banCreate a local jail override:
# /etc/fail2ban/jail.local
[sshd]
enabled = true
port = ssh
maxretry = 5
bantime = 3600
findtime = 6007. Add firewall allow-listing where practical
If your SSH access originates from known IP ranges, restrict inbound connections at the firewall level.
# Allow SSH only from trusted subnet
sudo ufw allow from 10.0.0.0/24 to any port 22
sudo ufw deny 22
sudo ufw enable8. Monitor auth logs and unusual login patterns
Regularly review authentication logs for signs of brute-force attempts, logins from unexpected IPs, or off-hours access.
# Recent auth failures
journalctl -u ssh --since "1 hour ago" | grep "Failed"
# Successful logins
journalctl -u ssh --since "today" | grep "Accepted"9. Add MFA for high-risk systems
For servers that require an extra layer of assurance, add TOTP-based multi-factor authentication with libpam-google-authenticator or a similar PAM module.
sudo apt install libpam-google-authenticatorNote: MFA configuration requires changes to both /etc/pam.d/sshd and sshd_config. Test thoroughly in a second session before closing your current one.
Post-change verification
After applying changes, confirm each of the following before closing your admin session:
- Fresh login works from a new terminal session
sudostill works for your admin user- Fail2ban is active:
sudo fail2ban-client status sshd - Firewall rules are persistent across reboot:
sudo ufw status - Auth logs are being ingested:
journalctl -u ssh --since "5 min ago"
# Quick post-change validation script
sudo sshd -t && echo "Config OK" || echo "Config ERROR"
sudo systemctl status ssh
sudo fail2ban-client status sshd
sudo ufw status verboseRollback plan
Use active session/console, restore backup config, validate with sshd -t, restart SSH.
# Restore original config
sudo cp /etc/ssh/sshd_config.bak /etc/ssh/sshd_config
# Validate syntax
sudo sshd -t
# Restart SSH
sudo systemctl restart sshTip: If you are locked out remotely, use your hosting provider's console or out-of-band access to restore the backup config. This is why backing up before changes and confirming console access is step one.
Final takeaway
A small verified baseline (keys + least privilege + ban rules + logging) blocks most commodity attacks. Apply these changes incrementally, verify each step, and always keep a rollback path open.