ufw 280 , the Uncomplicated Firewall, is Ubuntu 225 's front end to the kernel packet filter. Ubuntu Server installs it disabled, so a fresh machine accepts connections on every listening port (the lean WSL 6 image omits it entirely: sudo apt install ufw). Ubuntu 26.04 carries ufw 0.36.2, still the newest upstream release. It writes iptables 40,292 rules, and iptables --version here prints v1.8.11 (nf_tables), so they land in nftables 40,292 . On a remote server, allow SSH before enabling, or enable cuts off the session you typed it in; --force skips the warning prompt, so keep it for tested scripts.
sudo ufw default deny incoming
sudo ufw default allow outgoing
sudo ufw limit 22/tcp comment 'SSH'
sudo ufw allow 80/tcp comment 'HTTP'
sudo ufw allow 443/tcp comment 'HTTPS'
sudo ufw --force enable
sudo ufw status verbose... Firewall is active and enabled on system startup Status: active Logging: on (low) Default: deny (incoming), allow (outgoing), disabled (routed) New profiles: skip To Action From -- ------ ---- 22/tcp LIMIT IN Anywhere # SSH 80/tcp ALLOW IN Anywhere # HTTP 443/tcp ALLOW IN Anywhere # HTTPS 22/tcp (v6) LIMIT IN Anywhere (v6) # SSH ...
Each rule has a v6 twin because /etc/default/ufw sets IPV6=yes. limit denies an address that opens 6 or more connections within 30 seconds, a cheap brake on password guessing. Rules persist in /etc/ufw/user.rules: when this machine restarted during testing, ufw status showed all six again. The apache2 package adds profiles, so sudo ufw allow 'Apache 129 Full' opens 80 and 443 in one rule. ufw status numbered followed by ufw delete 3 removes a rule, ufw reject refuses instead of dropping, and ufw --dry-run previews a change.
Docker 514 is the classic exception: a port published with -p 8080:80 is diverted in the nat table before it reaches the INPUT chain ufw manages, so the container is reachable although ufw status shows no rule. Publish as -p 127.0.0.1:8080:80 when only a proxy on the host needs it.