Linux Firewall Configuration: UFW and iptables Practice

The firewall is the first line of defense for any server exposed to the internet. By default Linux does not restrict inbound traffic, so a freshly installed system that opens business ports directly exposes management services such as MySQL and Redis to the public network. The right approach is "deny inbound by default, allow on demand", plus rate limiting and source restrictions for management entry points like SSH.

If you have not yet done basic security setup, start with the Server Initialization Security Script article, then refine the rules with this guide.

UFW: the easiest default configuration

UFW is a front-end for iptables/nftables with intuitive syntax, well suited to most single-server scenarios.

ufw default deny incoming
ufw default allow outgoing
ufw allow 22/tcp
ufw allow 80/tcp
ufw allow 443/tcp
ufw limit ssh
ufw enable
  • ufw allow 51312 opens TCP+UDP, ufw allow 51312/udp opens UDP only, and port ranges use 51312:51314.
  • ufw limit ssh automatically rate-limits any IP that attempts 6 or more connections within 30 seconds, ideal for management entry points.
  • Use ufw status verbose to review rules; user rules are stored in /etc/ufw/user.rules.

Note: Docker bypasses UFW by writing its own iptables rules, so you need a solution like ufw-docker to keep container traffic under control.

iptables: rule chains and persistence

Use iptables directly when you need fine-grained control. A basic inbound rule set:

iptables -A INPUT -m state --state ESTABLISHED,RELATED -j ACCEPT
iptables -A INPUT -p tcp --dport 22 -j ACCEPT
iptables -A INPUT -p tcp --dport 80 -j ACCEPT
iptables -A INPUT -p tcp --dport 443 -j ACCEPT
iptables -A INPUT -j DROP

iptables rules are not persistent by default and are lost on reboot. Save them with iptables-save > /etc/iptables/rules.v4 and install iptables-persistent.

Restricting to specific source IPs is also common — for example, allowing only your office's egress range to reach a management port:

iptables -A INPUT -p tcp --dport 22 -s 203.0.113.0/24 -j ACCEPT
iptables -A INPUT -p tcp --dport 22 -j DROP

For entry points like SSH that get brute-forced constantly, hashlimit gives connection-level rate limiting that is more controllable than ufw limit:

iptables -A INPUT -p tcp --dport 22 -m state --state NEW \
  -m hashlimit --hashlimit-above 3/min --hashlimit-burst 5 \
  --hashlimit-name ssh -j DROP

nftables: the next-generation framework

nftables replaces iptables as the kernel's default framework with more unified syntax. A single inet table can handle IPv4 and IPv6 together:

table inet filter {
  chain input {
    type filter hook input priority filter; policy drop;
    ct state established,related accept
    tcp dport { 22, 80, 443 } accept
  }
}

Save rules with nft list ruleset > /etc/nftables.conf; nftables.service reloads them automatically after reboot. For port forwarding (say, forwarding public 8443 to an internal host's 443), use a nat table:

table ip nat {
  chain prerouting {
    type nat hook prerouting priority dstnat; policy accept;
    tcp dport 8443 dnat to 192.168.1.10:443
  }
}

Common scenario checklist

  • Web servers: open 80/443, and 22 when needed; close everything else.
  • Databases: allow only internal network ranges to reach 3306/5432; never expose them publicly.
  • Management entry points: restrict SSH source IPs and rate-limit connections, combined with SSH Hardening.
  • Periodic audits: export and review rules regularly with nft list ruleset or iptables -S.

A worked example: hardening a WordPress box

For a typical WordPress server, a full hardening pass usually has four steps: first, inventory what must be exposed — 80/443 for the web tier and certificate renewal, 22 only for operations, and database and Redis bound strictly to the internal network; second, allow 22/80/443 with UFW and deny everything else by default; third, restrict SSH to your office egress range and enable ufw limit ssh; finally, add another source restriction to high-value entry points such as a control panel or phpMyAdmin. When done, export the rule list with ufw status numbered and archive it for later comparison.

This allow-list covers the common ports; anything not listed stays closed by default:

Service Port Recommendation
SSH 22 Restrict source + rate limit
HTTP/HTTPS 80/443 Open to the world
MySQL/PostgreSQL 3306/5432 Internal network only
Redis 6379 Internal only, no 0.0.0.0 binding
Control panel Custom Source-restrict or VPN

Common Questions

Can UFW and iptables coexist? UFW is just a front-end for iptables/nftables — do not hand-maintain both at once, or rules will overwrite each other. Pick one backend: nftables or iptables.

Why does Docker break the firewall after publishing ports? Docker writes directly to iptables' FORWARD chain, bypassing UFW's INPUT policy. Enable ufw-docker or use NetworkPolicy at the orchestration layer to contain container traffic.

Do cloud security groups replace a server firewall? No. Security groups filter at the virtual network layer; the local firewall is the last line of defense in depth. Configure both.

What if I forget about IPv6? Many hosts allow IPv6 by default while only configuring IPv4 rules — a backdoor. UFW manages both together; with iptables, configure ip6tables separately; with nftables, an inet table covers both stacks at once.

Verify and enable

Do not rely on intuition after writing rules — verify with commands: ufw status verbose shows the active policy, ufw show added lists pending rules, and nft list ruleset (nftables backend) or iptables -S (iptables backend) shows the actual chains. After rollout, keep firewall logs enabled (ufw logging on) for a while so you can spot legitimate traffic being dropped, then decide whether to tighten further.

A common gotcha: running ufw enable in a remote session applies rules immediately, and if you forgot to allow SSH, the session dies on the spot. The safe sequence is to allow SSH first, then run ufw enable --force, keeping the current session open to verify. If you do get locked out, recover through your cloud provider's VNC or serial console.

16IDC Observation

For most sites, UFW's default-deny policy plus a few allow rules is enough; move up to iptables/nftables only when you need port forwarding, multiple NICs, or container networking. Beyond the firewall, layer application-level protections such as Security Headers and Web Application Firewalls on top of network rules to build defense in depth. Browse the full list back in the Security Hardening category.

Source: https://wiki.archlinux.org/title/Uncomplicated_Firewall

Reference: nftables wiki https://wiki.nftables.org/; UFW man page https://manpages.debian.org/ufw