Privacy Tools Guide

UFW (Uncomplicated Firewall) is a front-end for iptables that ships with Ubuntu. It gives you a readable interface for managing packet filtering without writing raw iptables rules. This guide covers practical hardening: default deny, SSH protection, web server rules, and logging.

Prerequisites

Check UFW Status

sudo ufw status verbose
# If inactive, no rules are applied yet

Step 1: Set Default Policies

The most important step: deny all incoming traffic and allow all outgoing by default.

sudo ufw default deny incoming
sudo ufw default allow outgoing
sudo ufw default deny forward

Do not enable UFW yet — add SSH rules first.

Step 2: Allow SSH Before Enabling

If you lock yourself out of SSH on a remote server, you will need console access to recover.

# Allow SSH on default port 22
sudo ufw allow ssh

# Or specify explicitly
sudo ufw allow 22/tcp

# If you run SSH on a custom port (e.g., 2222)
sudo ufw allow 2222/tcp

For extra protection, limit SSH to a specific source IP:

# Only allow SSH from your office IP
sudo ufw allow from 203.0.113.50 to any port 22 proto tcp

Step 3: Enable UFW

sudo ufw enable
# Command may disrupt existing connections. Proceed with operation (y|n)? y

sudo ufw status verbose

Output should show:

Status: active
Default: deny (incoming), allow (outgoing), deny (routed)

Step 4: Common Service Rules

Web Server

# HTTP and HTTPS
sudo ufw allow 80/tcp
sudo ufw allow 443/tcp

# Or use the application profile
sudo ufw allow 'Nginx Full'
sudo ufw allow 'Apache Full'

DNS (if running a resolver)

sudo ufw allow 53/tcp
sudo ufw allow 53/udp

Mail Server

sudo ufw allow 25/tcp    # SMTP
sudo ufw allow 587/tcp   # Submission
sudo ufw allow 993/tcp   # IMAPS
sudo ufw allow 465/tcp   # SMTPS

PostgreSQL (only from app server)

sudo ufw allow from 10.0.0.5 to any port 5432 proto tcp

Step 5: Rate Limit SSH

UFW has a built-in rate limit that blocks an IP after 6 connection attempts within 30 seconds.

# Replace the plain allow rule with a rate-limited one
sudo ufw delete allow 22/tcp
sudo ufw limit ssh

# Verify
sudo ufw status numbered

This mitigates brute-force attacks without needing fail2ban for basic SSH protection.

Step 6: Block Known Bad Ranges

Block entire CIDR ranges when you want to deny traffic from a region or known malicious ASN:

# Block a range
sudo ufw deny from 192.0.2.0/24

# Block and log
sudo ufw deny log from 198.51.100.0/24

Note: UFW processes rules in order. A deny before a more specific allow will block that traffic. List rules with numbers to check ordering:

sudo ufw status numbered

Delete a rule by number:

sudo ufw delete 3

Step 7: Application Profiles

UFW ships with application profiles in /etc/ufw/applications.d/. List available profiles:

sudo ufw app list
sudo ufw app info 'Nginx Full'

Create a custom profile for your application:

sudo tee /etc/ufw/applications.d/myapp <<EOF
[MyApp]
title=My Application
description=Custom app on port 8080
ports=8080/tcp
EOF

sudo ufw app update MyApp
sudo ufw allow MyApp

Step 8: Enable Logging

# Low logging: blocked packets only
sudo ufw logging on

# Medium: includes new connections
sudo ufw logging medium

# High: all packets (very verbose)
sudo ufw logging high

Logs appear in /var/log/ufw.log and also via syslog/journalctl:

sudo tail -f /var/log/ufw.log
sudo journalctl -k --grep="UFW" -f

Sample blocked packet log entry:

[UFW BLOCK] IN=eth0 OUT= MAC=... SRC=203.0.113.99 DST=10.0.0.1 \
  LEN=44 TTL=244 ID=54321 DF PROTO=TCP SPT=44231 DPT=3306 FLAGS=S

Step 9: IPv6 Support

Ensure IPv6 rules are also applied. Open /etc/default/ufw and confirm:

sudo grep IPV6 /etc/default/ufw
# Should show: IPV6=yes

If it is no, change it and reload:

sudo ufw disable && sudo ufw enable

UFW will then apply matching rules to both IPv4 and IPv6 interfaces automatically.

Step 10: Allow Docker Containers

Docker bypasses UFW by default, writing directly to iptables. To fix this without patching Docker:

Edit /etc/ufw/after.rules and add before COMMIT:

# Allow forwarding for Docker
-A ufw-before-forward -i docker0 -j ACCEPT
-A ufw-before-forward -o docker0 -m state --state RELATED,ESTABLISHED -j ACCEPT

A cleaner solution is to bind Docker to 127.0.0.1 in /etc/docker/daemon.json:

{
  "iptables": false
}

Then manage all exposure through UFW explicitly.

Useful Management Commands

# Show rules with line numbers
sudo ufw status numbered

# Reset all rules (dangerous — disables UFW)
sudo ufw reset

# Reload after manual edits
sudo ufw reload

# Delete a rule by specification
sudo ufw delete allow 8080/tcp

# Check before enabling (dry run)
sudo ufw --dry-run enable