Chapter 4

Hardening sshd

Password off, AllowUsers, fail2ban, ports.

Learning objectives

  • Apply essential sshd_config hardening settings
  • Restrict users, groups, and authentication methods
  • Validate config before restart and avoid lockout

Defense in depth for SSH

Workshop Co.'s bastion is the only Linux host with a public IP accepting SSH. Marcus hardens every guest's sshd too — defense if someone breaches the internal network.

Recommended settings

Edit /etc/ssh/sshd_config (Debian/Ubuntu) or drop-in files in /etc/ssh/sshd_config.d/:

# /etc/ssh/sshd_config.d/99-workshopco-hardening.conf

PermitRootLogin no
PasswordAuthentication no
KbdInteractiveAuthentication no
ChallengeResponseAuthentication no
PubkeyAuthentication yes
AuthenticationMethods publickey

AllowUsers deploy marcus
# Or: AllowGroups ssh-users

MaxAuthTries 3
LoginGraceTime 30
ClientAliveInterval 300
ClientAliveCountMax 2

X11Forwarding no
AllowTcpForwarding yes   # needed on bastion; restrict on db VM
PermitTunnel no

# Optional — custom port on bastion only (security through obscurity + less bot noise)
# Port 2222
Test before lockout

Always keep one existing SSH session open. In a second terminal:

sudo sshd -t && sudo systemctl reload sshd

Verify new login works before closing the old session.

Worked example — database VM stricter policy

VM 120 (PostgreSQL) allows SSH only from bastion IP on internal bridge:

# VM 120 — /etc/ssh/sshd_config.d/99-db.conf
AllowUsers deploy
PasswordAuthentication no
AllowTcpForwarding no
PermitOpen none

Plus ufw or nftables on VM 120:

ufw default deny incoming
ufw allow from 10.20.0.2 to any port 22 proto tcp  # bastion IP only
ufw enable

fail2ban (optional layer)

On the bastion, Marcus installs fail2ban to ban IPs after repeated failed attempts — useful if password auth were accidentally re-enabled:

apt install fail2ban
# /etc/fail2ban/jail.local
[sshd]
enabled = true
maxretry = 3
bantime = 3600

Canadian compliance note

PIPEDA and access logs

Log SSH auth success and failure. Workshop Co. retains /var/log/auth.log (or journal) for 90 days — supports incident response if customer booking data is on the same infrastructure.

Try it yourself

List three settings Marcus must apply on the bastion vs the database VM. Which differ and why?

Sample answers
  • Bastion: public-facing, may allow AllowTcpForwarding yes for jump access, consider non-standard port + fail2ban
  • Database VM: no forwarding, firewall allow bastion IP only, no public route
  • Both: no root login, no passwords, pubkey only

Check your understanding

  1. Does changing SSH to port 2222 replace disabling password auth?
  2. What does sshd -t do?
Answers
  1. No — port change reduces log noise only; pubkey-only auth is the real control.
  2. Tests config syntax without applying — catches typos before reload.