iptables
Cheatsheet
A comprehensive reference for iptables — the low-level Linux firewall tool — covering tables, chains, rules, targets, NAT, stateful packet inspection, logging, and rule persistence.
What is iptables?
iptables is the low-level Linux kernel firewall that directly controls the Netfilter packet filtering framework. Unlike UFW (a simplified frontend), iptables gives you granular, explicit control over every aspect of packet filtering, NAT, and packet mangling.
What it does: iptables inspects every network packet passing through the kernel and applies rules to accept, drop, reject, log, or redirect them. Rules are organised into tables (filter, nat, mangle) and chains (INPUT, OUTPUT, FORWARD).
Real-world use: Network security on Linux servers, building routers and firewalls, NAT/masquerading for internet sharing, port forwarding, and security hardening. Required knowledge for RHCSA, LFCS, and network security certifications. Note: modern systems may use nftables as the backend, but iptables syntax remains widely used.
⚠️ Remote Server Warning: Misconfigured iptables rules can lock you out of a remote server. Always ensure SSH (port 22) is allowed before adding DROP policies. Consider using UFW for simpler firewall management.
1. Tables & Chains
iptables has three main tables, each with specific chains.
| Table | Chains | Purpose |
|---|---|---|
| filter | INPUT, OUTPUT, FORWARD | Default table — packet filtering (allow/drop). Used for firewall rules. |
| nat | PREROUTING, OUTPUT, POSTROUTING | Network Address Translation — port forwarding, masquerading. |
| mangle | All 5 chains | Packet modification — TTL, TOS, marks. Advanced use. |
| raw | PREROUTING, OUTPUT | Connection tracking bypass. Low-level, rarely needed. |
| Chain | Traffic Type |
|---|---|
| INPUT | Packets destined for the local system |
| OUTPUT | Packets originating from the local system |
| FORWARD | Packets routed through the system (not local origin/dest) |
| PREROUTING | Packets before routing decision (NAT table) |
| POSTROUTING | Packets after routing decision (NAT/masquerade) |
2. Basic Commands
# List rules sudo iptables -L # list all rules sudo iptables -L -n -v # verbose with numeric addresses sudo iptables -L -n -v --line-numbers # show line numbers sudo iptables -L INPUT -n -v # specific chain sudo iptables -t nat -L -n -v # list NAT table # Flush (clear) rules sudo iptables -F # flush all rules (filter table) sudo iptables -F INPUT # flush specific chain sudo iptables -t nat -F # flush NAT table sudo iptables -X # delete user-defined chains sudo iptables -Z # zero packet/byte counters # Set default policies sudo iptables -P INPUT DROP # drop all incoming by default sudo iptables -P OUTPUT ACCEPT # allow all outgoing by default sudo iptables -P FORWARD DROP # drop forwarded by default
3. Allow Rules (ACCEPT)
# Allow loopback (always required) sudo iptables -A INPUT -i lo -j ACCEPT sudo iptables -A OUTPUT -o lo -j ACCEPT # Allow established / related connections sudo iptables -A INPUT -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT # Allow SSH sudo iptables -A INPUT -p tcp --dport 22 -j ACCEPT # Allow HTTP and HTTPS sudo iptables -A INPUT -p tcp --dport 80 -j ACCEPT sudo iptables -A INPUT -p tcp --dport 443 -j ACCEPT # Allow DNS (UDP) sudo iptables -A INPUT -p udp --dport 53 -j ACCEPT sudo iptables -A OUTPUT -p udp --dport 53 -j ACCEPT # Allow ICMP (ping) sudo iptables -A INPUT -p icmp --icmp-type echo-request -j ACCEPT # Allow from specific IP sudo iptables -A INPUT -s 203.0.113.10 -j ACCEPT # Allow from subnet sudo iptables -A INPUT -s 192.168.1.0/24 -j ACCEPT # Allow specific port from specific IP sudo iptables -A INPUT -s 192.168.1.0/24 -p tcp --dport 3306 -j ACCEPT
4. Drop & Reject Rules
# DROP — silently discard packet sudo iptables -A INPUT -p tcp --dport 23 -j DROP # block Telnet sudo iptables -A INPUT -s 198.51.100.5 -j DROP # block IP sudo iptables -A INPUT -s 10.0.0.0/8 -j DROP # block subnet # REJECT — drop and send error back to sender sudo iptables -A INPUT -p tcp --dport 23 -j REJECT sudo iptables -A INPUT -p tcp --dport 23 -j REJECT --reject-with tcp-reset # Block outgoing (OUTPUT chain) sudo iptables -A OUTPUT -p tcp --dport 25 -j DROP # block outbound SMTP sudo iptables -A OUTPUT -d 198.51.100.0/24 -j DROP # block traffic to subnet # Rate limiting (protect against brute-force) sudo iptables -A INPUT -p tcp --dport 22 -m limit \ --limit 5/min --limit-burst 10 -j ACCEPT sudo iptables -A INPUT -p tcp --dport 22 -j DROP # Block invalid packets sudo iptables -A INPUT -m conntrack --ctstate INVALID -j DROP
5. Targets Reference
| Target | Description |
|---|---|
| ACCEPT | Allow the packet through |
| DROP | Silently discard packet — sender gets no response |
| REJECT | Discard packet and send ICMP/TCP error to sender |
| LOG | Log packet to kernel log (syslog/journald) and continue processing |
| RETURN | Stop traversing current chain, return to calling chain |
| DNAT | Destination NAT — change destination address (port forwarding) |
| SNAT | Source NAT — change source address (outbound NAT) |
| MASQUERADE | Dynamic SNAT — for dynamic IP addresses (home routers) |
| REDIRECT | Redirect packet to a different local port |
6. NAT & Port Forwarding
# Enable IP forwarding (required for routing) echo 1 | sudo tee /proc/sys/net/ipv4/ip_forward # Persist in /etc/sysctl.conf: net.ipv4.ip_forward = 1 # MASQUERADE — share internet via eth0 (home router / VPN) sudo iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE # Port forwarding — forward external port 8080 → internal 80 sudo iptables -t nat -A PREROUTING -p tcp --dport 8080 \ -j REDIRECT --to-port 80 # Forward port to another host (DNAT) sudo iptables -t nat -A PREROUTING -p tcp --dport 80 \ -j DNAT --to-destination 192.168.1.10:80 sudo iptables -A FORWARD -p tcp -d 192.168.1.10 \ --dport 80 -j ACCEPT # SNAT — change source IP for outbound traffic sudo iptables -t nat -A POSTROUTING -s 192.168.1.0/24 \ -o eth0 -j SNAT --to-source 203.0.113.10
7. Rule Management
# Append rule to end of chain (-A) sudo iptables -A INPUT -p tcp --dport 80 -j ACCEPT # Insert rule at specific position (-I) sudo iptables -I INPUT 1 -p tcp --dport 22 -j ACCEPT # insert at top (pos 1) sudo iptables -I INPUT 3 -s 10.0.0.5 -j DROP # insert at position 3 # Delete rule by specification (-D) sudo iptables -D INPUT -p tcp --dport 80 -j ACCEPT # Delete rule by line number sudo iptables -L INPUT --line-numbers # find line number sudo iptables -D INPUT 3 # delete line 3 # Replace rule (-R) sudo iptables -R INPUT 2 -p tcp --dport 8080 -j ACCEPT # Create custom chain sudo iptables -N MY_CHAIN sudo iptables -A INPUT -j MY_CHAIN # jump to custom chain # Delete custom chain sudo iptables -F MY_CHAIN # flush first sudo iptables -X MY_CHAIN # then delete
8. Logging
# LOG target — write to kernel log (non-terminating) sudo iptables -A INPUT -p tcp --dport 22 \ -j LOG --log-prefix "SSH-ACCESS: " --log-level 4 # Log then drop (LOG must come before DROP) sudo iptables -A INPUT -s 198.51.100.0/24 \ -j LOG --log-prefix "BLOCKED-IP: " sudo iptables -A INPUT -s 198.51.100.0/24 -j DROP # View iptables log entries sudo dmesg | grep "BLOCKED-IP" sudo journalctl -k | grep "SSH-ACCESS" sudo tail -f /var/log/syslog | grep iptables # Log levels: 0=emerg 1=alert 2=crit 3=err 4=warn 5=notice 6=info 7=debug
9. Saving & Persistence
iptables rules are not persistent by default — they are lost on reboot. Use these methods to persist them.
# Method 1: iptables-save / iptables-restore sudo iptables-save > /etc/iptables/rules.v4 # save IPv4 rules sudo ip6tables-save > /etc/iptables/rules.v6 # save IPv6 rules sudo iptables-restore < /etc/iptables/rules.v4 # restore rules # Method 2: iptables-persistent package (Debian/Ubuntu) sudo apt install iptables-persistent sudo netfilter-persistent save # save current rules sudo netfilter-persistent reload # reload from saved # Rules saved to: /etc/iptables/rules.v4 and rules.v6 # View saved rules cat /etc/iptables/rules.v4 # Complete server ruleset example sudo iptables -F # flush all sudo iptables -P INPUT DROP # default deny in sudo iptables -P OUTPUT ACCEPT # default allow out sudo iptables -P FORWARD DROP # default deny forward sudo iptables -A INPUT -i lo -j ACCEPT # allow loopback sudo iptables -A INPUT -m conntrack \ --ctstate ESTABLISHED,RELATED -j ACCEPT # allow established sudo iptables -A INPUT -p tcp --dport 22 -j ACCEPT # allow SSH sudo iptables -A INPUT -p tcp --dport 80 -j ACCEPT # allow HTTP sudo iptables -A INPUT -p tcp --dport 443 -j ACCEPT # allow HTTPS sudo iptables-save > /etc/iptables/rules.v4 # persist