iptables: Linux Firewall Configuration and Management — Rules That Don't Burn You at 3 AM
Master iptables Linux firewall configuration and management with production-tested rules, debugging techniques, and real incident stories from 15+ years in the trenches..
20+ years shipping production infrastructure and CI/CD at scale. Written from production experience, not tutorials.
- ✓Basic Linux command-line proficiency
- ✓Understanding of TCP/IP and network layers
- ✓Familiarity with netfilter kernel subsystem
Use iptables -L -n -v to list current rules with packet/byte counts. To block all incoming traffic except SSH on port 22: iptables -P INPUT DROP; iptables -A INPUT -p tcp --dport 22 -j ACCEPT; iptables -A INPUT -m state --state ESTABLISHED,RELATED -j ACCEPT. Always test with a backup SSH session open.
Imagine your server is a nightclub. iptables is the bouncer at the door. You give the bouncer a list of rules: 'Let in anyone on the VIP list (port 22), but block everyone else unless they're already inside (established connections).' The bouncer checks each person (packet) against the list, top to bottom, and the first match decides their fate. If no rule matches, the bouncer follows the default policy — either let everyone in (ACCEPT) or turn everyone away (DROP).
I've seen a single misplaced iptables rule take down a production payment gateway at 2 AM on a Saturday. The rule was meant to block a malicious IP, but a missing -s flag made it block all traffic. The on-call engineer spent 45 minutes SSH'd into the DR site, sweating, because the primary's iptables had locked out even the monitoring system. That's the power of iptables: one line can save your bacon or deep-fry it. This isn't a tutorial for hobbyists — it's a survival guide for engineers who manage firewalls in production, where a typo means a P0 incident.
Netfilter Architecture: The Kernel's Packet Processing Pipeline
Before you touch iptables, you need to understand netfilter — the kernel framework that iptables talks to. Netfilter hooks into five key points in the network stack: PREROUTING, INPUT, FORWARD, OUTPUT, and POSTROUTING. Each hook corresponds to a chain in iptables. When a packet arrives, it traverses these hooks in order. The filter table's INPUT chain runs at the INPUT hook, deciding whether to deliver the packet locally. The nat table's PREROUTING chain runs at the PREROUTING hook, before routing decisions. Understanding this flow is critical: a rule in the wrong table or chain won't work as expected. For example, DNAT (destination NAT) must be in the PREROUTING chain of the nat table, not in the filter table. I've seen engineers waste hours trying to DNAT in the FORWARD chain — it doesn't work because FORWARD runs after routing.
iptables -t raw -A PREROUTING -j TRACE to trace packet flow through all chains. Then watch dmesg -w | grep TRACE. This is invaluable for debugging why a rule isn't matching.Tables and Chains: Where Rules Live and Why It Matters
iptables has five tables: filter (default), nat, mangle, raw, and security. Each table has built-in chains. The filter table has INPUT, FORWARD, and OUTPUT chains. The nat table has PREROUTING, OUTPUT, and POSTROUTING. Mangle has all five. Raw has PREROUTING and OUTPUT. Security is for SELinux. Most people only use filter, but ignoring the others leads to brittle setups. For example, rate-limiting SYN packets should be done in the mangle table's PREROUTING chain, not filter's INPUT, because mangle runs before the first routing decision and can drop packets before they consume connection tracking resources. I once saw a DDoS knock over a server because the rate limit was in filter INPUT — the connection tracking table filled up before the rule was evaluated. Move it to mangle PREROUTING and the server survived.
-j NOTRACK to bypass conntrack for high-traffic ports. Otherwise, conntrack table exhaustion will drop valid packets silently.Rule Syntax: The Devil in the Flags
An iptables rule is a chain of matches and one target. The basic structure: iptables -A <chain> <matches> -j <target>. Matches include -s (source), -d (destination), -p (protocol), --dport (destination port), -i (input interface), -o (output interface), -m state (connection state), -m limit (rate limit), and many more. The target can be ACCEPT, DROP, REJECT, LOG, RETURN, or a user-defined chain. The order of matches matters: put the most specific matches first to short-circuit evaluation. For example, -m state --state ESTABLISHED,RELATED -j ACCEPT should be near the top to allow return traffic. A common mistake is putting -j DROP before allowing established connections — that kills all return traffic. Also, remember that -s and -d can be IP addresses or CIDR ranges. Never use hostnames; iptables resolves them at rule insertion time, and if DNS fails, the rule won't apply.
iptables -P INPUT DROP first, then add rules, there's a window where you're locked out. Always add rules first, then change policy.Connection Tracking: The Hidden State Machine
Connection tracking (conntrack) is what makes stateful firewalling possible. The -m state module uses conntrack to classify packets as NEW, ESTABLISHED, RELATED, or INVALID. Without it, you'd have to open ephemeral ports for return traffic. Conntrack tracks all connections in a table in kernel memory. The table size is configurable via net.netfilter.nf_conntrack_max. Default is often 65536, which is too low for busy servers. When the table fills, new connections are dropped silently. I've seen this happen on a web server handling 10k concurrent connections — the symptom was intermittent timeouts. The fix: increase nf_conntrack_max and reduce nf_conntrack_tcp_timeout_established from the default 5 days to something sane like 1 hour. Also, use the raw table to bypass conntrack for high-traffic UDP services (like DNS) with -j NOTRACK.
-m state --state ESTABLISHED,RELATED -j ACCEPT is what permits it.NAT: Masquerading, DNAT, and the Routing Trap
Network Address Translation in iptables is done in the nat table. SNAT (source NAT) is used for masquerading — hiding internal IPs behind a single public IP. DNAT (destination NAT) is used for port forwarding. The classic setup: iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE for SNAT. For DNAT: iptables -t nat -A PREROUTING -i eth0 -p tcp --dport 80 -j DNAT --to-destination 192.168.1.100:8080. A common mistake is forgetting to enable IP forwarding: sysctl net.ipv4.ip_forward=1. Without it, DNAT won't work. Also, DNAT only changes the destination — the return traffic must be un-DNATed automatically by conntrack. If you're doing DNAT to a different port, the return traffic's source port is the original port, not the translated one. This usually works, but can break with some protocols. For hairpin NAT (accessing a DNATed service from inside the same network), you need additional rules.
Rate Limiting and DDoS Mitigation with iptables
iptables can do basic rate limiting using the limit and connlimit modules. The limit module matches at a certain rate (e.g., 10/second) and can be used to throttle ICMP or SYN packets. The connlimit module limits the number of concurrent connections per IP. For SYN flood mitigation, use -m limit --limit 10/s --limit-burst 20 in the mangle table's PREROUTING chain. This drops excess SYN packets before they reach conntrack. For per-IP connection limits: -m connlimit --connlimit-above 100 --connlimit-mask 32 -j DROP. This is useful for preventing a single client from exhausting server resources. However, iptables is not a full DDoS mitigation solution — it's a first line of defense. For large attacks, you need a dedicated appliance or cloud-based protection. Also, be careful with connlimit on NATed clients; they all share the same public IP, so you might block legitimate users.
iptables -A INPUT -p tcp --syn -m limit --limit 1/s -j LOG --log-prefix "SYN: " to log SYN packets at a low rate without flooding logs. Then analyze with grep SYN: /var/log/kern.log.Logging and Debugging: Seeing What the Firewall Sees
The LOG target sends packet info to the kernel log (viewable via dmesg or /var/log/kern.log). Use --log-prefix to tag entries for easy grepping. The ULOG target sends logs to userspace via netlink, which is more efficient. For debugging, use the TRACE target in the raw table to trace packet traversal through all chains. This is invaluable when a rule isn't matching as expected. Also, iptables -L -n -v shows packet and byte counters per rule. If a rule's counter isn't incrementing, the packet isn't reaching that rule — check previous rules or chain order. Another trick: insert a temporary LOG rule at the top of a chain to see if packets arrive. Remember to remove it afterwards.
Persistence: Making Rules Survive Reboot
iptables rules are ephemeral — they vanish on reboot. To make them persistent, save them to a file and restore on boot. On Debian/Ubuntu, use iptables-save > /etc/iptables/rules.v4 and install iptables-persistent. On RHEL/CentOS, use service iptables save which writes to /etc/sysconfig/iptables. The modern way is to use iptables-restore with a systemd service. Never rely on startup scripts that re-run iptables commands — they're fragile. Instead, maintain a single rules file versioned in your config management. Also, always test a new ruleset by saving the current one first: iptables-save > /root/iptables.backup.$(date +%s). If something breaks, restore with iptables-restore < /root/iptables.backup....
iptables-apply (from iptables-persistent) to safely apply new rules with a timeout. If you lose connectivity, the old rules are restored automatically after the timeout. Example: iptables-apply -t 60 newrules.v4.Performance: How Rules Affect Throughput
iptables rules are evaluated in linear order. The more rules, the slower the packet processing. For high-throughput environments (10Gbps+), minimize the number of rules and use the most specific matches first. Use iptables -L -n -v to identify rules with high packet counts — those are your hot paths. Consider moving frequently matched rules to the top. Also, use the -m set module with ipset for large IP blocklists — it uses hash tables and is O(1) instead of O(n). For connection tracking, ensure nf_conntrack_max is adequate and timeouts are tuned. On multi-core systems, iptables uses a single lock, which can become a bottleneck. Newer nftables (the successor to iptables) has better concurrency. If you're pushing serious traffic, consider migrating to nftables or using XDP/BPF for packet filtering.
When Not to Use iptables: Modern Alternatives
iptables is legacy. The modern replacement is nftables, which offers better performance, atomic rule replacement, and a more consistent syntax. nftables also supports sets, maps, and concatenations natively. For container environments, consider using CNI plugins like Calico or Cilium that use eBPF for high-performance networking. For host-based firewalls, firewalld (which uses nftables under the hood) provides a dynamic zone-based interface. If you're starting a new project, use nftables. If you're maintaining legacy systems, iptables is still fine, but plan to migrate. The migration is straightforward: iptables-translate can convert rules. However, for simple setups, iptables is still widely used and understood. Don't rewrite a working system just because it's old — but don't start new projects with it.
iptables-translate to convert individual rules: iptables-translate -A INPUT -p tcp --dport 22 -j ACCEPT outputs nft add rule ip filter INPUT tcp dport 22 accept.Production Checklist: Deploying iptables Safely
Before you deploy any iptables change in production, follow this checklist. First, ensure you have out-of-band access (IPMI, iDRAC, serial console) in case you lock yourself out. Second, save the current rules: iptables-save > /root/iptables.backup. Third, write your new rules in a file and test with iptables-restore --test < newrules.v4 to check for syntax errors. Fourth, apply with a timeout using iptables-apply -t 60 newrules.v4. Fifth, verify connectivity from another host: nc -zv <server> 22. Sixth, monitor logs for dropped packets: dmesg -w | grep iptables. Seventh, if everything works, make the rules persistent. Eighth, document the change in your runbook. Never make changes directly on a production server without a rollback plan. I've seen too many engineers panic when SSH drops — having a backup plan is non-negotiable.
iptables -F (flush) without having a backup session or a fallback plan. Flushing all rules with a default DROP policy will instantly lock you out. Always flush specific chains or use -P to set policy to ACCEPT first.The Rule That Blocked the CEO's VPN
iptables -A INPUT -j DROP. Without -s flag, it matched ALL incoming packets, dropping them before the VPN allow rule (which was later in the chain). The rule order was wrong.iptables -D INPUT -j DROP. Then added the correct rule with source IP: iptables -A INPUT -s 5.5.5.5 -j DROP. Also moved the DROP rule to the end of the chain.- Always specify source/destination when blocking.
- And always insert DROP rules at the end of the chain, not the beginning.
iptables -L -n -v to see current rules. 3. If default policy is DROP and no SSH allow rule, add one: iptables -I INPUT 1 -p tcp --dport 22 -j ACCEPT. 4. If you have no console, ask a colleague to reboot the server (rules may revert if not persistent). 5. After regaining access, save working rules.iptables -L INPUT -n -v | grep lo. 2. If missing, add iptables -A INPUT -i lo -j ACCEPT. 3. Also check OUTPUT chain: iptables -L OUTPUT -n -v. 4. If OUTPUT policy is DROP, add allow rule for the database port.iptables -L -n -v --line-numbers. 2. Check source IPs: iptables -L -n -v | grep DROP. 3. If legitimate traffic is being dropped, adjust the rule (e.g., increase rate limit, add source whitelist). 4. If malicious, consider adding to ipset blocklist.iptables -L INPUT -n -v --line-numbers | grep -E 'dpt:22|DROP'iptables -I INPUT 1 -p tcp --dport 22 -j ACCEPT| File | Command / Code | Purpose |
|---|---|---|
| list_tables.devops | for table in filter nat mangle raw security; do | Tables and Chains |
| basic_rules.devops | iptables -A INPUT -i lo -j ACCEPT | Rule Syntax |
| conntrack_tuning.devops | cat /proc/net/nf_conntrack | wc -l | Connection Tracking |
| nat_rules.devops | sysctl -w net.ipv4.ip_forward=1 | NAT |
| rate_limiting.devops | iptables -A INPUT -p tcp --dport 22 -m state --state NEW -m limit --limit 3/minu... | Rate Limiting and DDoS Mitigation with iptables |
| debugging.devops | iptables -I INPUT 1 -j LOG --log-prefix "INPUT-PACKET: " --log-level 4 | Logging and Debugging |
| persistence.devops | iptables-save > /etc/iptables/rules.v4 | Persistence |
| performance_check.devops | iptables -L -n -v --line-numbers | head -20 | Performance |
| migrate_to_nftables.devops | iptables-save > /tmp/rules.v4 | When Not to Use iptables |
| safe_deploy.devops | iptables-save > /root/iptables.backup.$(date +%s) | Production Checklist |
Key takeaways
Interview Questions on This Topic
How does iptables handle return traffic for a connection initiated from inside the network? Explain the role of conntrack.
-m state --state ESTABLISHED,RELATED -j ACCEPT is what permits it. Without conntrack, you'd need to open ephemeral ports.Frequently Asked Questions
20+ years shipping production infrastructure and CI/CD at scale. Written from production experience, not tutorials.
That's Linux. Mark it forged?
6 min read · try the examples if you haven't