SSH Cheatsheet

Hardening the Server

Use this SSH reference while you build software engineering projects, review code for technical interview prep, or polish examples for a software engineer resume.

Configuration File

All settings go in /etc/ssh/sshd_config. After every change:

# Test config syntax before reloading
sudo sshd -t

# Reload (keep existing sessions alive)
sudo systemctl reload sshd

# Full restart (drops existing sessions)
sudo systemctl restart sshd

Essential Hardening Settings

# /etc/ssh/sshd_config

# Disable password auth — key-only
PasswordAuthentication no
ChallengeResponseAuthentication no
KbdInteractiveAuthentication no   # OpenSSH 8.7+

# Disable root login
PermitRootLogin no
# (or: PermitRootLogin prohibit-password  — allows root with key only)

# Disable empty passwords
PermitEmptyPasswords no

# Move off port 22 (reduces noise, not real security)
Port 2222

# Restrict to specific users or groups
AllowUsers alice bob
AllowGroups sshusers

# Limit authentication attempts
MaxAuthTries 3
MaxSessions 5
MaxStartups 10:30:60    # start:rate:max half-open connections

# Set an auth timeout
LoginGraceTime 30       # seconds before unauthenticated connection dropped

# Disable X11 and agent forwarding if not needed
X11Forwarding no
AllowAgentForwarding no
AllowTcpForwarding no

# Disable tunneled plaintext passwords in PAM
UsePAM yes              # keep yes; the above PasswordAuthentication no is what locks it

# Banner (shown before auth — legal notice)
Banner /etc/ssh/banner.txt

# Log level
LogLevel VERBOSE        # or INFO; avoid QUIET in prod

Restrict to Modern Algorithms

# /etc/ssh/sshd_config — strong crypto only (OpenSSH 8.x+)
KexAlgorithms curve25519-sha256,curve25519-sha256@libssh.org,diffie-hellman-group16-sha512,diffie-hellman-group18-sha512
Ciphers chacha20-poly1305@openssh.com,aes256-gcm@openssh.com,aes128-gcm@openssh.com
MACs hmac-sha2-512-etm@openssh.com,hmac-sha2-256-etm@openssh.com
HostKeyAlgorithms ssh-ed25519,rsa-sha2-512,rsa-sha2-256

Run ssh -Q kex, ssh -Q cipher, ssh -Q mac to see what your client supports.

Host-Based Access Control

/etc/ssh/sshd_config Match blocks

# Allow password auth only from the internal network
Match Address 10.0.0.0/8
    PasswordAuthentication yes

# Lock down a specific user further
Match User deploy
    ForceCommand /usr/local/bin/deploy.sh
    PermitTTY no
    AllowTcpForwarding no

/etc/hosts.allow and /etc/hosts.deny (TCP Wrappers)

# /etc/hosts.allow
sshd: 192.168.1.0/24

# /etc/hosts.deny
sshd: ALL

Fail2ban (Rate-Limit Brute Force)

apt install fail2ban    # Debian/Ubuntu

# /etc/fail2ban/jail.local
[sshd]
enabled  = true
port     = 2222         # match your Port directive
maxretry = 5
bantime  = 3600
findtime = 600
sudo systemctl enable --now fail2ban
sudo fail2ban-client status sshd     # check bans
sudo fail2ban-client set sshd unbanip 1.2.3.4

Firewall (ufw / iptables)

# ufw — allow SSH on custom port, deny others
ufw allow 2222/tcp
ufw enable

# iptables — rate-limit SSH connections
iptables -A INPUT -p tcp --dport 22 -m state --state NEW -m recent --set
iptables -A INPUT -p tcp --dport 22 -m state --state NEW -m recent --update --seconds 60 --hitcount 4 -j DROP

Two-Factor Authentication (TOTP)

# Install
apt install libpam-google-authenticator

# Run per user
google-authenticator

# /etc/pam.d/sshd — add at the top
auth required pam_google_authenticator.so

# /etc/ssh/sshd_config
KbdInteractiveAuthentication yes
AuthenticationMethods publickey,keyboard-interactive

Key Rotation Checklist

  1. Generate a new key pair (ssh-keygen -t ed25519)
  2. Add new public key to authorized_keys
  3. Test login with the new key
  4. Remove the old key from authorized_keys
  5. Delete or archive the old private key

Audit: Who Can Log In

# Check authorized keys for all users
find /home -name authorized_keys -exec echo "=== {} ===" \; -exec cat {} \;
cat /root/.ssh/authorized_keys 2>/dev/null

# Check active SSH sessions
who
ss -tnp | grep :22
last | head -20

# Recent auth failures
grep "Failed password\|Invalid user" /var/log/auth.log | tail -20
journalctl -u sshd --since "1 hour ago" | grep -i fail