SSH Hardening

Secure SSH configuration for Debian

This page is a practical SSH hardening reference for Debian servers. Goal: reduce attack surface, enforce strong authentication, and prevent lockouts by validating changes safely.

1) Baseline checks

Know what you are changing

  • Show SSH server version
    sshd -V 2>&1 || true
    ssh -V
  • Show effective SSH configuration
    sudo sshd -T
  • Check current listening ports
    sudo ss -lntp | grep -E ':(22|2222)\b' || true
  • Confirm active service
    systemctl status ssh --no-pager

2) Use key-based authentication

Disable passwords after verification

  • Create a key on your client (recommended: ed25519)
    ssh-keygen -t ed25519 -a 64 -f ~/.ssh/id_ed25519
  • Copy public key to server
    ssh-copy-id -i ~/.ssh/id_ed25519.pub user@server.example.com
  • Manual install (server side)
    mkdir -p ~/.ssh
    chmod 700 ~/.ssh
    cat id_ed25519.pub >> ~/.ssh/authorized_keys
    chmod 600 ~/.ssh/authorized_keys

Important: keep at least one working SSH session open when testing changes to avoid lockouts.

3) Harden sshd_config

Safe defaults (defensive)

Edit the SSH server config:

sudo vi /etc/ssh/sshd_config

Minimal hardening example (adjust the AllowUsers/AllowGroups for your environment):

# --- Authentication ---
PermitRootLogin no
PasswordAuthentication no
KbdInteractiveAuthentication no
ChallengeResponseAuthentication no
UsePAM yes

# --- Limit access ---
AllowUsers youradminuser
# Or:
# AllowGroups sshusers

# --- Reduce noise / brute-force window ---
MaxAuthTries 3
LoginGraceTime 30
ClientAliveInterval 300
ClientAliveCountMax 2

# --- Security / hygiene ---
X11Forwarding no
AllowAgentForwarding no
AllowTcpForwarding no
PermitTunnel no
PermitUserEnvironment no
PrintMotd no

Notes:

  • Disabling passwords is only safe after you verified key login works.
  • Avoid custom cipher/KEX lists unless you have a strict compliance need. Modern OpenSSH defaults are strong.
  • If you need port changes, do it together with firewall updates and keep an active session open.

4) Validate before restart

Prevent lockouts

  • Config syntax test
    sudo sshd -t
  • Show which config files are included
    sudo sshd -T | head -n 60
  • Reload safely
    sudo systemctl reload ssh
  • Test new login in a second terminal
    ssh -o PreferredAuthentications=publickey -o PasswordAuthentication=no user@server.example.com

5) Firewall notes

Only allow what you need

Example with UFW (adjust your port):

sudo ufw allow 22/tcp
sudo ufw status verbose

If you expose SSH to the internet, combine with:

  • Fail2Ban (rate-limiting and bans)
  • Allowlist by source IP when possible
  • Strong keys and no password auth

6) Quick audit checks

Confirm behavior

  • Auth logs
    sudo journalctl -u ssh --no-pager -n 200
  • Last SSH logins
    last -a | head -n 25
  • Show loaded keys in the running daemon (sanity)
    sudo sshd -T | grep -E '^(permitrootlogin|passwordauthentication|kbdinteractiveauthentication|allowusers|allowgroups)\b'