Stop Writing iptables Rules by Hand

Firewall iptables nftables

Everyone who manages Linux firewalls has done it: spent 20 minutes writing an iptables rule, typo in the chain name, locked yourself out of SSH, had to walk to the data center or pray the console works.

iptables syntax is powerful but unforgiving. One mistake in rule ordering and your carefully crafted firewall does the opposite of what you intended.

The 5 mistakes everyone makes

1. Forgetting conntrack on the first rule

# Wrong — blocks return traffic for established connections
iptables -P INPUT DROP
iptables -A INPUT -p tcp --dport 22 -j ACCEPT
iptables -A INPUT -p tcp --dport 80 -j ACCEPT
# Right — allow established connections first
iptables -P INPUT DROP
iptables -A INPUT -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT
iptables -A INPUT -p tcp --dport 22 -j ACCEPT
iptables -A INPUT -p tcp --dport 80 -j ACCEPT

Without conntrack, every TCP ACK packet gets dropped. Your SSH session dies the moment you apply the rules.

2. Wrong rule order

iptables processes rules top-to-bottom, first match wins:

# Wrong — the DROP never fires because ACCEPT matches first
iptables -A INPUT -p tcp --dport 22 -j ACCEPT
iptables -A INPUT -s 10.0.0.0/8 -p tcp --dport 22 -j DROP
# Right — restrict first, then allow
iptables -A INPUT -s 10.0.0.0/8 -p tcp --dport 22 -j DROP
iptables -A INPUT -p tcp --dport 22 -j ACCEPT

3. Not saving rules

# Rules vanish after reboot
iptables -A INPUT -p tcp --dport 443 -j ACCEPT
# Persist with iptables-persistent
apt install iptables-persistent
netfilter-persistent save

4. Ignoring IPv6

You locked down IPv4 but left IPv6 wide open:

# Check — if this shows only default rules, you have a problem
ip6tables -L -n

Every iptables rule needs an ip6tables counterpart, or set the default policy to DROP.

5. Logging everything (and filling the disk)

# This fills /var/log in hours on a busy server
iptables -A INPUT -j LOG --log-prefix "DROPPED: "
# Better — rate-limited logging
iptables -A INPUT -m limit --limit 5/min --limit-burst 10 \
  -j LOG --log-prefix "DROPPED: "
iptables -A INPUT -j DROP

Common rule sets nobody memorizes

Basic web server

iptables -P INPUT DROP
iptables -P FORWARD DROP
iptables -P OUTPUT ACCEPT
iptables -A INPUT -i lo -j ACCEPT
iptables -A INPUT -m conntrack --ctstate 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 -p icmp --icmp-type echo-request -j ACCEPT

NAT gateway

echo 1 > /proc/sys/net/ipv4/ip_forward
iptables -t nat -A POSTROUTING -o eth0 -s 192.168.1.0/24 -j MASQUERADE
iptables -A FORWARD -i eth1 -o eth0 -j ACCEPT
iptables -A FORWARD -i eth0 -o eth1 -m conntrack \
  --ctstate ESTABLISHED,RELATED -j ACCEPT

Rate-limit SSH

iptables -A INPUT -p tcp --dport 22 -m conntrack --ctstate NEW \
  -m recent --set --name SSH
iptables -A INPUT -p tcp --dport 22 -m conntrack --ctstate NEW \
  -m recent --update --seconds 60 --hitcount 4 --name SSH -j DROP

The visual way: NetForge Firewall Builder

Instead of remembering syntax, use NetForge's visual rule builder:

  1. Select chain (INPUT, OUTPUT, FORWARD)
  2. Pick protocol, port, source/destination
  3. Choose action (ACCEPT, DROP, REJECT, LOG)
  4. Click "Add Rule"
  5. Export as iptables or nftables script

Features:

iptables vs nftables — which one?

If you're on Debian 11+ or Ubuntu 22.04+, nftables is the default backend. iptables commands still work via the compatibility layer, but native nftables syntax is cleaner:

iptablesnftables
-A INPUT -p tcp --dport 22 -j ACCEPTtcp dport 22 accept
-t nat -A POSTROUTING -j MASQUERADEmasquerade
Separate v4/v6 commandsSingle inet family for both

NetForge generates both formats from the same visual rules.

Try NetForge

Visual firewall builder with iptables/nftables export. No cloud, runs on your machine.

Live Demo Get NetForge