Skip to main content

iptables

iptables is the user-space command-line tool for configuring the Linux kernel's netfilter packet filtering framework. It organises rules into tables (filter, nat, mangle) and chains (INPUT, OUTPUT, FORWARD) that evaluate every network packet passing through the system.

Understanding iptables

What Is iptables in Simple Terms

iptables is the traditional way to write firewall rules on Linux. It talks directly to the kernel's netfilter system and gives you precise control over exactly what happens to every network packet. Modern tools like ufw and firewalld are frontends that write iptables rules for you — but understanding iptables directly is essential for debugging, for working with Docker, and for production systems where the automatic tools create unexpected rules.

How It Works

◈ DIAGRAM
+------------------------------------------+
| Tables and Chains |
| |
| filter table (default -- allow/block) |
| INPUT -- packets destined for host |
| OUTPUT -- packets from host |
| FORWARD -- packets routed through host |
| |
| nat table (address translation) |
| PREROUTING -- before routing decision |
| POSTROUTING -- after routing decision |
| |
| mangle table (packet modification) |
+------------------------------------------+

Rule anatomy:

◈ DIAGRAM
iptables -A INPUT -p tcp --dport 22 -s 10.0.0.0/8 -j ACCEPT
| | | | | |
tool chain protocol dest port source IP action
Actions:
ACCEPT -- allow the packet
DROP -- silently discard
REJECT -- discard and send ICMP error back
LOG -- log to syslog and continue

Practical Commands

Bash
## View current rules
sudo iptables -L -n -v
## -L list -n numeric (no DNS lookup) -v verbose (packet counts)
## View with line numbers (needed for deletion)
sudo iptables -L INPUT -n -v --line-numbers
## Basic production ruleset -- apply in order
## 1. Allow established connections (stateful -- critical for performance)
sudo iptables -A INPUT -m state --state ESTABLISHED,RELATED -j ACCEPT
## 2. Allow loopback
sudo iptables -A INPUT -i lo -j ACCEPT
## 3. Allow SSH from internal network only
sudo iptables -A INPUT -p tcp --dport 22 -s 10.0.0.0/8 -j ACCEPT
## 4. Allow HTTPS from anywhere
sudo iptables -A INPUT -p tcp --dport 443 -j ACCEPT
## 5. Set default policy to DROP
sudo iptables -P INPUT DROP
sudo iptables -P FORWARD DROP
sudo iptables -P OUTPUT ACCEPT
## Delete a rule by line number
sudo iptables -D INPUT 3
## Flush all rules (CAREFUL -- removes all rules)
sudo iptables -F
## Save rules so they survive reboot
sudo apt install iptables-persistent
sudo iptables-save > /etc/iptables/rules.v4
## Restore rules
sudo iptables-restore < /etc/iptables/rules.v4
## See what Docker added
sudo iptables -L DOCKER -n -v
sudo iptables -L DOCKER-USER -n -v ## add your rules here to affect Docker

Troubleshooting

Symptom Command What to Check
All traffic blocked sudo iptables -L -n Default policy is DROP with no allow rules
Rules lost on reboot sudo iptables-save > /etc/iptables/rules.v4 Rules not persisted
Docker bypassing rules sudo iptables -L DOCKER-USER Add rules to DOCKER-USER chain
Connection tracking full `sudo conntrack -L wc -l`
Tip

Add custom rules that affect Docker-exposed ports to the DOCKER-USER chain, not the INPUT chain. Docker inserts its rules before INPUT, so INPUT rules do not affect Docker-managed ports. The DOCKER-USER chain is evaluated before Docker's own rules.

Remember

iptables rules are ephemeral by default — they are lost on reboot. Always save rules with iptables-save after confirming they work correctly. Use iptables-persistent on Debian/Ubuntu to automate restoration at boot.

Frequently Asked Questions

How do iptables chains and tables actually work together to process a packet?

Tables group rules by purpose — `filter` for allow/deny decisions, `nat` for address translation, `mangle` for packet header modification — and each table contains chains representing points in the packet's journey: INPUT (destined for this host), OUTPUT (originating here), and FORWARD (passing through, e.g. on a router). A packet traverses the relevant chains in order, and the first matching rule's target (ACCEPT, DROP, REJECT) decides its fate unless it's a chain with no match, which falls through to the chain's default policy.

What's a classic iptables mistake that locks admins out of a remote server?

Setting the INPUT chain's default policy to DROP before adding an explicit ACCEPT rule for SSH (port 22) — the moment that policy change applies, the remote session used to make it is cut off with no way back in except console/out-of-band access. Always add and verify the allow rules first, test connectivity from a second session, and only then set restrictive default policies — or use a tool like `ufw`/`firewalld` that applies changes more safely.