Linux firewall reference: iptables (chains, tables, stateful filtering, NAT, logging, persistence) and nftables (modern syntax). Covers LPIC-2 topic 212.1.
iptables โ structure
Tables & chains
| Table | Chains | Purpose |
|---|---|---|
| filter | INPUT ยท OUTPUT ยท FORWARD | Default table โ allow/drop/reject |
| nat | PREROUTING ยท OUTPUT ยท POSTROUTING | Address translation |
| mangle | All five | Packet modification (TTL, TOS, marks) |
| raw | PREROUTING ยท OUTPUT | Conntrack bypass |
Chain flow
| Traffic | Path |
|---|---|
| Incoming to host | PREROUTING โ INPUT โ local process |
| Outgoing from host | local process โ OUTPUT โ POSTROUTING |
| Forwarded (router) | PREROUTING โ FORWARD โ POSTROUTING |
Listing & reset
Viewing rules
| Command | Description |
|---|---|
| iptables -L -n -v --line-numbers | All filter rules with counters and line numbers |
| iptables -L INPUT -n -v --line-numbers | INPUT chain only |
| iptables -t nat -L -n -v | NAT table |
| iptables -t mangle -L -n -v | Mangle table |
Reset to clean state
| Command | Description |
|---|---|
| iptables -F | Flush all filter rules |
| iptables -F INPUT | Flush INPUT chain only |
| iptables -X | Delete user-defined chains |
| iptables -Z | Zero counters |
| iptables -Z INPUT | Zero INPUT counters only |
| iptables -t nat -F | Flush NAT table |
| iptables -t mangle -F | Flush mangle table |
| iptables -P INPUT ACCEPT | Reset default INPUT policy |
Policies & rule management
Adding, inserting, deleting rules
| Command | Description |
|---|---|
| iptables -P INPUT DROP | Set default policy to DROP |
| iptables -A INPUT -p tcp --dport 22 -j ACCEPT | Append rule to end of chain |
| iptables -I INPUT 1 -s 10.0.0.0/8 -j ACCEPT | Insert rule at position 1 |
| iptables -D INPUT 3 | Delete rule by line number |
| iptables -D INPUT -p tcp --dport 80 -j ACCEPT | Delete rule by specification |
| iptables -R INPUT 2 -p tcp --dport 443 -j ACCEPT | Replace rule at position 2 |
Common filter rules
Essential INPUT rules
| Command | Description |
|---|---|
| iptables -A INPUT -i lo -j ACCEPT | Allow loopback (always required) |
| iptables -A INPUT -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT | Allow established/related traffic |
| iptables -A INPUT -m conntrack --ctstate INVALID -j DROP | Drop invalid packets |
| iptables -A INPUT -p tcp --dport 22 -j ACCEPT | Allow SSH |
| iptables -A INPUT -p tcp -m multiport --dports 80,443 -j ACCEPT | Allow HTTP/HTTPS (multiport) |
| iptables -A INPUT -p icmp --icmp-type echo-request -j ACCEPT | Allow ping |
| iptables -A INPUT -j DROP | Drop everything else |
Source/destination filtering
| Command | Description |
|---|---|
| iptables -A INPUT -s 192.168.1.0/24 -j ACCEPT | Allow from subnet |
| iptables -A INPUT -s 10.0.0.55 -j DROP | Block a specific IP |
| iptables -A INPUT -i eth0 -p tcp --dport 22 -j ACCEPT | SSH on specific interface only |
| iptables -A INPUT -m iprange --src-range 10.0.0.10-10.0.0.11 -j ACCEPT | Allow IP range |
| iptables -A INPUT -m mac --mac-source aa:bb:cc:dd:ee:ff -j DROP | Block by MAC (L2 only) |
| iptables -A OUTPUT -m owner --uid-owner nobody -j DROP | Drop traffic from user (OUTPUT only) |
Stateful filtering (conntrack)
Connection states
| State | Meaning |
|---|---|
| NEW | First packet of a new connection |
| ESTABLISHED | Connection is established, packets flowing both ways |
| RELATED | Related to an existing connection (FTP data, ICMP error) |
| INVALID | Does not match any known connection, should be dropped |
conntrack commands
| Command | Description |
|---|---|
| iptables -A INPUT -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT | Core stateful rule |
| iptables -A INPUT -m conntrack --ctstate INVALID -j DROP | Drop invalid |
| iptables -A INPUT -p tcp --dport 22 --syn -m conntrack --ctstate NEW -j ACCEPT | New SSH connections only |
| conntrack -L | Show connection tracking table |
| conntrack -S | Conntrack statistics |
NAT
SNAT & MASQUERADE
| Command | Description |
|---|---|
| iptables -t nat -A POSTROUTING -s 192.168.10.0/24 -o eth0 -j MASQUERADE | MASQUERADE โ dynamic source IP (DHCP, PPPoE) |
| iptables -t nat -A POSTROUTING -s 192.168.10.0/24 -o eth0 -j SNAT --to-source 203.0.113.10 | SNAT โ fixed source IP (faster than MASQUERADE) |
| echo 1 | sudo tee /proc/sys/net/ipv4/ip_forward | Enable IP forwarding (required for NAT router) |
| net.ipv4.ip_forward = 1 | Persist in /etc/sysctl.conf, apply with sysctl -p |
DNAT & port forwarding
| Command | Description |
|---|---|
| iptables -t nat -A PREROUTING -i eth0 -p tcp --dport 8080 -j DNAT --to-destination 10.0.0.50:80 | Forward port 8080 โ internal host:80 |
| iptables -t nat -A PREROUTING -i eth0 -p tcp --dport 10000:10010 -j DNAT --to-destination 10.0.0.50 | Forward port range (mapped 1:1) |
| iptables -t nat -A PREROUTING -p tcp --dport 80 -j REDIRECT --to-ports 8080 | Redirect to local port (PREROUTING/OUTPUT only) |
| iptables -A FORWARD -i eth0 -o eth1 -p tcp --dport 80 -d 10.0.0.50 -m conntrack --ctstate NEW -j ACCEPT | FORWARD rule required alongside DNAT |
| conntrack -L -j | Show active NAT translations |
Logging & custom chains
Logging
| Command | Description |
|---|---|
| iptables -A INPUT -j LOG --log-prefix "[DROP] " --log-level 4 | Log (does NOT stop processing โ packet continues) |
| iptables -A INPUT -m limit --limit 5/min -j LOG --log-prefix "[INPUT-DROP] " | Log with rate limit (last rule before default policy) |
| journalctl -k | grep DROP | Read kernel log (iptables LOG target) |
Custom chains
| Command | Description |
|---|---|
| iptables -N LOGDROP | Create custom chain |
| iptables -A LOGDROP -j LOG --log-prefix "[DROP] " | Add LOG to custom chain |
| iptables -A LOGDROP -j DROP | Add DROP to custom chain |
| iptables -A INPUT -s 10.0.0.55 -j LOGDROP | Send traffic to custom chain |
| iptables -A chain -j RETURN | Return to parent chain (continue processing there) |
Rate limiting
limit & recent modules
| Command | Description |
|---|---|
| iptables -A INPUT -p icmp --icmp-type echo-request -m limit --limit 5/second --limit-burst 10 -j ACCEPT | Limit ping to 5/s (burst 10) |
| iptables -A INPUT -p tcp --dport 22 --syn -m conntrack --ctstate NEW -m limit --limit 1/second --limit-burst 5 -j ACCEPT | SSH brute-force protection |
| iptables -A INPUT -p tcp --dport 80 -m conntrack --ctstate NEW -m recent --set --name HTTP | Track new HTTP connections per IP |
| iptables -A INPUT -p tcp --dport 80 -m conntrack --ctstate NEW -m recent --update --seconds 1 --hitcount 20 --name HTTP -j DROP | Drop if >20 new connections/s from same IP |
FORWARD & routing
Routing / forwarding rules
| Command | Description |
|---|---|
| iptables -A FORWARD -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT | Allow established forwarded traffic |
| iptables -A FORWARD -s 192.168.10.0/24 -i eth1 -o eth0 -m conntrack --ctstate NEW -j ACCEPT | Allow internal LAN to initiate connections outward |
| iptables -I FORWARD 1 -s 192.168.10.50 -j DROP | Block a single internal host from forwarding |
Persistence
Save & restore
| Command | Description |
|---|---|
| iptables-save > /etc/iptables/rules.v4 | Save rules to file |
| iptables-restore < /etc/iptables/rules.v4 | Restore rules atomically |
| sudo apt install iptables-persistent | Auto-load rules on boot |
| sudo netfilter-persistent save | Save current rules via persistent service |
| sudo netfilter-persistent reload | Reload rules from /etc/iptables/ |
Minimal server template
# Safe order: allow SSH first, then set DROP policy
iptables -F; iptables -X
iptables -A INPUT -p tcp --dport 22 -j ACCEPT # SSH before DROP policy
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 -m conntrack --ctstate INVALID -j DROP
iptables -A INPUT -p tcp -m multiport --dports 80,443 -j ACCEPT
iptables -A INPUT -p icmp --icmp-type echo-request -m limit --limit 5/s -j ACCEPT
iptables -A INPUT -m limit --limit 5/min -j LOG --log-prefix "[DROP] "
netfilter-persistent save
nftables
Key differences from iptables
| Feature | iptables | nftables |
|---|---|---|
| CLI | iptables / ip6tables / arptables | nft (single tool) |
| Atomic update | No (rule-by-rule) | Yes (transactions) |
| Sets | ipset (external) | Built-in named sets |
| Tables | Built-in (filter/nat/โฆ) | User-defined |
nft syntax
| Command | Description |
|---|---|
| nft list ruleset | Show all tables, chains, rules |
| nft add table inet filter | Create table (inet = IPv4+IPv6) |
| nft add chain inet filter input '{ type filter hook input priority 0; policy drop; }' | Create input chain with DROP policy |
| nft add rule inet filter input ct state established,related accept | Allow established |
| nft add rule inet filter input tcp dport { 22, 80, 443 } accept | Allow ports (set literal) |
| nft add rule inet filter input iif lo accept | Allow loopback |
| nft delete rule inet filter input handle 5 | Delete rule by handle number |
| nft -f /etc/nftables.conf | Load ruleset from file |
| nft list ruleset > /etc/nftables.conf | Save ruleset to file |
iptables โ nftables equivalents
| iptables | nftables equivalent |
|---|---|
| -p tcp --dport 22 -j ACCEPT | tcp dport 22 accept |
| -s 192.168.1.0/24 -j DROP | ip saddr 192.168.1.0/24 drop |
| -m conntrack --ctstate ESTABLISHED -j ACCEPT | ct state established accept |
| -t nat -A POSTROUTING -j MASQUERADE | ip saddr ... masquerade (in postrouting chain) |
| -m multiport --dports 80,443 | tcp dport { 80, 443 } |
| -m limit --limit 5/s -j ACCEPT | limit rate 5/second accept |
| -j LOG --log-prefix "DROP: " | log prefix "DROP: " |