Files
MajorWiki/02-selfhosting/security/ufw-firewall-management.md
MajorLinux c66d3a6fd0 Update UFW article: add web server ports lesson from tttpod outage
Adds a section documenting how missing HTTP/HTTPS rules caused a
site outage on tttpod, and updates the fleet reference table.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-03 03:57:27 -04:00

193 lines
5.2 KiB
Markdown

---
title: "UFW Firewall Management"
domain: selfhosting
category: security
tags: [security, firewall, ufw, ubuntu, networking]
status: published
created: 2026-04-02
updated: 2026-04-03
---
# UFW Firewall Management
UFW (Uncomplicated Firewall) is the standard firewall tool on Ubuntu. It wraps iptables/nftables into something you can actually manage without losing your mind. This covers the syntax and patterns I use across the MajorsHouse fleet.
## The Short Answer
```bash
# Enable UFW
sudo ufw enable
# Allow a port
sudo ufw allow 80
# Block a specific IP
sudo ufw insert 1 deny from 203.0.113.50
# Check status
sudo ufw status numbered
```
## Basic Rules
### Allow by Port
```bash
# Allow HTTP and HTTPS
sudo ufw allow 80
sudo ufw allow 443
# Allow a port range
sudo ufw allow 6000:6010/tcp
# Allow a named application profile
sudo ufw allow 'Apache Full'
```
### Allow by Interface
Useful when you only want traffic on a specific network interface — this is how SSH is restricted to Tailscale across the fleet:
```bash
# Allow SSH only on the Tailscale interface
sudo ufw allow in on tailscale0 to any port 22
# Then deny SSH globally (evaluated after the allow above)
sudo ufw deny 22
```
Rule order matters. UFW evaluates rules top to bottom and stops at the first match.
### Allow by Source IP
```bash
# Allow a specific IP to access SSH
sudo ufw allow from 100.86.14.126 to any port 22
# Allow a subnet
sudo ufw allow from 192.168.50.0/24 to any port 22
```
## Blocking IPs
### Insert Rules at the Top
When blocking IPs, use `insert 1` to place the deny rule at the top of the chain. Otherwise it may never be evaluated because an earlier ALLOW rule matches first.
```bash
# Block a single IP
sudo ufw insert 1 deny from 203.0.113.50
# Block a subnet
sudo ufw insert 1 deny from 203.0.113.0/24
# Block an IP from a specific port only
sudo ufw insert 1 deny from 203.0.113.50 to any port 443
```
### Don't Accumulate Manual Blocks
Manual `ufw deny` rules pile up fast. On one of my servers, I found **30,142 manual DENY rules** — a 3 MB rules file that every packet had to traverse. Use Fail2ban for automated blocking instead. It manages bans with expiry and doesn't pollute your UFW rules.
If you inherit a server with thousands of manual blocks:
```bash
# Nuclear option — reset and re-add only the rules you need
sudo ufw --force reset
sudo ufw allow 'Apache Full'
sudo ufw allow in on tailscale0 to any port 22
sudo ufw deny 22
sudo ufw enable
```
## Managing Rules
### View Rules
```bash
# Simple view
sudo ufw status
# Numbered (needed for deletion and insert position)
sudo ufw status numbered
# Verbose (shows default policies and logging)
sudo ufw status verbose
```
### Delete Rules
```bash
# Delete by rule number
sudo ufw delete 3
# Delete by rule specification
sudo ufw delete allow 8080
```
### Default Policies
```bash
# Deny all incoming, allow all outgoing (recommended baseline)
sudo ufw default deny incoming
sudo ufw default allow outgoing
```
## Don't Forget Web Server Ports
If you're running a web server behind UFW, make sure ports 80 and 443 are explicitly allowed. This sounds obvious, but it's easy to miss — especially on servers where UFW was enabled after the web server was already running, or where a firewall reset dropped rules that were never persisted.
```bash
# Allow HTTP and HTTPS
sudo ufw allow 80
sudo ufw allow 443
# Or use an application profile
sudo ufw allow 'Apache Full'
```
If your site suddenly stops responding after enabling UFW or resetting rules, check `sudo ufw status numbered` first. Missing web ports is the most common cause.
## UFW with Fail2ban
On Ubuntu servers, Fail2ban and UFW operate at different layers. Fail2ban typically creates its own nftables table (`inet f2b-table`) at a higher priority than UFW's chains. This means:
- Fail2ban bans take effect **before** UFW rules are evaluated
- A banned IP is rejected even if UFW has an ALLOW rule for that port
- Add trusted IPs (your own, monitoring, etc.) to `ignoreip` in `/etc/fail2ban/jail.local` to prevent self-lockout
```ini
# /etc/fail2ban/jail.local
[DEFAULT]
ignoreip = 127.0.0.1/8 ::1 100.0.0.0/8
```
The `100.0.0.0/8` range covers all Tailscale IPs, which prevents banning fleet traffic.
## UFW Logging
```bash
# Enable logging (low/medium/high/full)
sudo ufw logging medium
```
Logs go to `/var/log/ufw.log`. Useful for seeing what's getting blocked, but `medium` or `low` is usually enough — `high` and `full` can be noisy.
## Fleet Reference
UFW is used on these MajorsHouse servers:
| Host | Key UFW Rules |
|---|---|
| majortoot | SSH on tailscale0, deny 22 globally |
| majorlinux | SSH on tailscale0, deny 22 globally |
| tttpod | SSH on tailscale0, deny 22 globally, Apache Full (added 2026-04-03) |
| teelia | SSH on tailscale0, deny 22 globally, Apache Full |
The Fedora servers (majorlab, majorhome, majormail, majordiscord) use iptables or firewalld instead.
## See Also
- [Linux Server Hardening Checklist](linux-server-hardening-checklist.md) — initial firewall setup as part of server provisioning
- [Fail2ban & UFW Rule Bloat Cleanup](../../05-troubleshooting/networking/fail2ban-ufw-rule-bloat-cleanup.md) — what happens when manual blocks get out of hand