Skip to content

Lesson 2 of 7 · 20m

ufw and fail2ban: The Perimeter in Two Commands

Deny everything, allow what you serve — plus the brute-force tarpit that quiets your auth logs.

Perimeter security on a single host is boring by design: close everything except what you serve, and slow down the bots that pound what's open.

ufw: default deny, explicit allow

sudo ufw default deny incoming
sudo ufw default allow outgoing
sudo ufw allow 22/tcp    # SSH — before enabling, or you lock yourself out
sudo ufw allow 80/tcp
sudo ufw allow 443/tcp
sudo ufw enable && sudo ufw status verbose

That's the whole firewall. Every service you add is one ufw allow — and a deliberate decision instead of an accident.

fail2ban: the tarpit

sudo apt install fail2ban
sudo systemctl enable --now fail2ban

The default sshd jail bans IPs after a few bad password attempts. With password auth off it's mostly cosmetic — but it keeps your auth log readable, and on services that do take credentials (webmail, admin panels) it genuinely reduces pressure. Check fail2ban-client status sshd when tuning.

What the firewall can't do

ufw governs what's reachable, not what's safe: an open 443 serving a container with a 2019 CMS is a hole. The firewall's job is shrinking the surface; the service's job is being worth exposing.

::exercise(allow-ssh)

::lab(lock-the-door)

Field notes

  • Docker bypasses ufw rules by default (it programs iptables directly) — published ports are reachable even with ufw deny. Bind to 127.0.0.1 or accept the bypass knowingly.
  • IPv6 exists: ufw handles both, but a rule you think closes a service may leave it open on :: — test both.
  • ss -tulpn is the audit: what's actually listening vs what you think is listening.