iptables
Legacy packet filter — chains, tables, rules, and NAT configuration.
Rule Inspection
List all rules with packet counts, numeric output — the standard view
sudo iptables -L -n -v
List rules with line numbers — need these for insert/delete by position
sudo iptables -L -n -v --line-numbers
List a specific chain only
sudo iptables -L INPUT -n -v --line-numbers
Show rules in command format — what you would type to recreate them
sudo iptables -S
Show rules for a specific chain in command format
sudo iptables -S INPUT
Show NAT table rules — SNAT, DNAT, MASQUERADE
sudo iptables -t nat -L -n -v
Show mangle table rules — TOS, TTL, MARK
sudo iptables -t mangle -L -n -v
Core Rule Operations
Append a rule to the end of INPUT chain
sudo iptables -A INPUT -p tcp --dport 22 -j ACCEPT
Insert a rule at position 1 — processed before existing rules
sudo iptables -I INPUT 1 -p tcp --dport 22 -j ACCEPT
Delete a rule by specification — must match exactly
sudo iptables -D INPUT -p tcp --dport 22 -j ACCEPT
Delete a rule by line number — faster when you know the position
sudo iptables -D INPUT 3
Flush all rules in a chain — wipes everything, use with caution
sudo iptables -F INPUT
Flush all chains in all tables
sudo iptables -F
sudo iptables -t nat -F
sudo iptables -t mangle -F
Set default policy — what happens when no rule matches
sudo iptables -P INPUT DROP
sudo iptables -P FORWARD DROP
sudo iptables -P OUTPUT ACCEPT
Common Rule Patterns
Allow established and related connections — stateful baseline, always first
sudo iptables -A INPUT -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT
Allow loopback — breaking lo breaks everything
sudo iptables -A INPUT -i lo -j ACCEPT
Allow SSH from a specific subnet
sudo iptables -A INPUT -p tcp -s 10.50.0.0/16 --dport 22 -j ACCEPT
Allow ICMP ping — useful for monitoring, drop in hardened environments
sudo iptables -A INPUT -p icmp --icmp-type echo-request -j ACCEPT
Allow DNS queries
sudo iptables -A INPUT -p udp --dport 53 -j ACCEPT
sudo iptables -A INPUT -p tcp --dport 53 -j ACCEPT
Allow HTTPS
sudo iptables -A INPUT -p tcp --dport 443 -j ACCEPT
Drop invalid packets — malformed, out-of-state
sudo iptables -A INPUT -m conntrack --ctstate INVALID -j DROP
Rate limit SSH — 3 new connections per minute per source
sudo iptables -A INPUT -p tcp --dport 22 -m conntrack --ctstate NEW -m recent --set
sudo iptables -A INPUT -p tcp --dport 22 -m conntrack --ctstate NEW -m recent --update --seconds 60 --hitcount 4 -j DROP
Complete Stateful Firewall
Minimal stateful firewall — the baseline every host should start with
# Flush existing
sudo iptables -F
sudo iptables -X
# Default policies
sudo iptables -P INPUT DROP
sudo iptables -P FORWARD DROP
sudo iptables -P OUTPUT ACCEPT
# Loopback
sudo iptables -A INPUT -i lo -j ACCEPT
# Established/related
sudo iptables -A INPUT -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT
# Drop invalid
sudo iptables -A INPUT -m conntrack --ctstate INVALID -j DROP
# SSH from trusted subnet
sudo iptables -A INPUT -p tcp -s 10.50.0.0/16 --dport 22 -j ACCEPT
# ICMP
sudo iptables -A INPUT -p icmp --icmp-type echo-request -j ACCEPT
NAT Rules
MASQUERADE — dynamic SNAT for outbound traffic (typical for routers)
sudo iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE
SNAT — static source NAT with a known IP
sudo iptables -t nat -A POSTROUTING -o eth0 -j SNAT --to-source 192.168.1.100
DNAT — forward incoming port 8443 to an internal host on 443
sudo iptables -t nat -A PREROUTING -p tcp --dport 8443 -j DNAT --to-destination 10.50.1.20:443
Port forwarding requires FORWARD chain to allow the traffic too
sudo iptables -A FORWARD -p tcp -d 10.50.1.20 --dport 443 -j ACCEPT
Enable IP forwarding — required for any NAT/routing
sudo sysctl -w net.ipv4.ip_forward=1
Logging
Log dropped packets — essential for debugging firewall issues
sudo iptables -A INPUT -j LOG --log-prefix "iptables-dropped: " --log-level 4
Log with rate limiting — prevent log flooding
sudo iptables -A INPUT -m limit --limit 5/min -j LOG --log-prefix "iptables-dropped: "
Watch firewall logs in real time
journalctl -k -f | grep "iptables-dropped"
Connection Tracking
Show connection tracking table — all tracked connections
sudo conntrack -L
Count tracked connections by state
sudo conntrack -L 2>/dev/null | awk '{for(i=1;i<=NF;i++) if($i ~ /^[A-Z_]+$/ && length($i)>3) print $i}' | sort | uniq -c | sort -rn
Show max tracked connections — hitting this limit drops new connections
cat /proc/sys/net/netfilter/nf_conntrack_max
Show current connection count
cat /proc/sys/net/netfilter/nf_conntrack_count
Save and Restore
Save current rules — persist across reboots
sudo iptables-save > /etc/iptables/iptables.rules
Restore saved rules
sudo iptables-restore < /etc/iptables/iptables.rules
Enable iptables service to load rules at boot (systemd)
sudo systemctl enable iptables
Test rules safely — auto-revert after 60 seconds if you lock yourself out
sudo iptables-restore < /tmp/new-rules.v4 && sleep 60 && sudo iptables-restore < /etc/iptables/iptables.rules