Let's Secure Me

Best practices and tips to secure your infrastructure.

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 -t

Warning: 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 yes

2. 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 admin

3. 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 no

4. 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 20

5. 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 fail2ban

Create a local jail override:

# /etc/fail2ban/jail.local
[sshd]
enabled = true
port = ssh
maxretry = 5
bantime = 3600
findtime = 600

7. 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 enable

8. 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-authenticator

Note: 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
  • sudo still 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 verbose

Rollback 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 ssh

Tip: 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.