Hardening sshd
Password off, AllowUsers, fail2ban, ports.
Learning objectives
- Apply essential
sshd_confighardening 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
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
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 yesfor 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
- Does changing SSH to port 2222 replace disabling password auth?
- What does
sshd -tdo?
Answers
- No — port change reduces log noise only; pubkey-only auth is the real control.
- Tests config syntax without applying — catches typos before reload.