SSH Hardening
All changes below go in /etc/ssh/sshd_config (or a file in /etc/ssh/sshd_config.d/). After editing, validate and restart:
sudo sshd -t # test config for syntax errorssudo systemctl restart sshdAlways keep an existing SSH session open while testing changes — if you lock yourself out, the open session is your recovery path.
Essential Changes
Section titled “Essential Changes”Disable root login
Section titled “Disable root login”PermitRootLogin noForce use of a regular user + sudo. Eliminates the most brute-forced username.
Disable password authentication
Section titled “Disable password authentication”PasswordAuthentication noKey-only authentication. Make sure your key is in ~/.ssh/authorized_keys and working before enabling this.
Restrict to specific users
Section titled “Restrict to specific users”AllowUsers your-username deployerWhitelist approach — only listed users can SSH in. Everyone else is rejected before auth is even attempted. Remember to add new users here if they need SSH access.
Change the default port
Section titled “Change the default port”Port 2222Not real security (port scanners find it), but cuts down on automated brute-force noise significantly. Update your firewall rules to match:
sudo ufw allow 2222/tcpsudo ufw delete allow 22/tcpRecommended Settings
Section titled “Recommended Settings”| Setting | Value | Purpose |
|---|---|---|
PubkeyAuthentication | yes | Enable key-based auth (usually default) |
AuthorizedKeysFile | .ssh/authorized_keys | Default key location |
ChallengeResponseAuthentication | no | Disable keyboard-interactive auth |
KbdInteractiveAuthentication | no | Same as above (newer name) |
UsePAM | yes | Keep PAM enabled for account checks |
X11Forwarding | no | Disable unless you need remote GUI apps |
PermitEmptyPasswords | no | Never allow empty passwords |
MaxAuthTries | 3 | Lock out after N failed attempts per connection |
LoginGraceTime | 30 | Seconds before unauthenticated connection is dropped |
ClientAliveInterval | 300 | Send keepalive every N seconds |
ClientAliveCountMax | 2 | Drop connection after N missed keepalives |
Key Management
Section titled “Key Management”Generate a key pair
Section titled “Generate a key pair”ssh-keygen -t ed25519 -C "your-email@example.com"Ed25519 is the current recommendation — short keys, fast, strong. Use RSA 4096 only if you need compatibility with old systems.
Copy public key to server
Section titled “Copy public key to server”ssh-copy-id -i ~/.ssh/id_ed25519.pub -p 2222 user@example.comVerify permissions on the server
Section titled “Verify permissions on the server”SSH is strict about file permissions. If these are wrong, key auth silently fails:
~/.ssh/ → 700 (drwx------)~/.ssh/authorized_keys → 600 (-rw-------)~/ → 755 (drwxr-xr-x) or stricterCheck with:
ls -la ~/.ssh/ls -ld ~/Fail2ban Integration
Section titled “Fail2ban Integration”Fail2ban watches auth logs and bans IPs that fail too many times. Essential complement to SSH hardening.
Install and enable
Section titled “Install and enable”sudo apt install fail2bansudo systemctl enable --now fail2banSSH jail config
Section titled “SSH jail config”Create /etc/fail2ban/jail.local (don’t edit jail.conf directly — it gets overwritten on updates):
[sshd]enabled = trueport = 2222maxretry = 3findtime = 600bantime = 3600| Setting | Meaning |
|---|---|
maxretry | Ban after N failures |
findtime | Within this window (seconds) |
bantime | Duration of ban (seconds). Use -1 for permanent. |
Useful commands
Section titled “Useful commands”sudo fail2ban-client status sshd # show jail stats and banned IPssudo fail2ban-client set sshd unbanip 1.2.3.4 # unban an IPsudo fail2ban-client set sshd banip 1.2.3.4 # manually ban an IPQuick Audit Checklist
Section titled “Quick Audit Checklist”After configuring, verify the state:
# Check which auth methods are activesudo sshd -T | grep -i -E 'passwordauth|pubkeyauth|permitroot|allowusers|port '
# Check for open SSH portsudo ss -tlnp | grep sshd
# Check fail2ban is runningsudo fail2ban-client status sshdSample hardened sshd_config block
Port 2222PermitRootLogin noPasswordAuthentication noKbdInteractiveAuthentication noChallengeResponseAuthentication noPubkeyAuthentication yesAuthorizedKeysFile .ssh/authorized_keysUsePAM yesX11Forwarding noPermitEmptyPasswords noMaxAuthTries 3LoginGraceTime 30ClientAliveInterval 300ClientAliveCountMax 2AllowUsers your-username