Advanced iptables Firewall Configuration
Master iptables tables, chains, stateful rules, NAT, rate limiting, and logging for production firewalls.
Overview
iptables is a userspace utility for managing Linux kernel netfilter rules. It controls how packets are filtered, translated, and routed through your network stack.
Modern Alternative: nftables is the official successor to iptables and is recommended for new setups on modern kernels. It offers a unified syntax, better performance, and easier rule composition. Consider migrating if you're not locked into legacy systems.
Tables: Data Processing Pipeline
iptables organizes rules into tables, each handling different aspects of packet processing:
filter Table
Default table for packet filtering. Contains chains for accepting/rejecting traffic.
nat Table
Network Address Translation. Translates source or destination IPs/ports.
mangle Table
Modifies packet headers (TTL, TOS, MARK). Advanced use cases like QoS tagging.
raw Table
Bypasses connection tracking for performance. Used before the conntrack system.
Chains: Rule Evaluation Points
Each table contains chains representing decision points:
- INPUT: Packets destined for the local machine
- OUTPUT: Packets originating from the local machine
- FORWARD: Packets passing through (router scenarios)
- PREROUTING: Packets arriving, before routing decision (NAT destination)
- POSTROUTING: Packets leaving, after routing decision (NAT source)
Stateful Firewall: Connection Tracking
The modern approach uses conntrack to track connection states instead of the deprecated --state option.
# Enable connection tracking with -m conntrack
iptables -A INPUT -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT
# Explicitly drop invalid packets
iptables -A INPUT -m conntrack --ctstate INVALID -j DROPValid states:
NEW: Initiating a new connectionESTABLISHED: Part of an existing connectionRELATED: Related to an established connection (e.g., FTP passive mode)INVALID: Malformed or unknown packets
Complete Stateful Firewall Script
This production-ready script sets up a secure default-deny firewall:
#!/bin/bash
# Flush all rules and chains
iptables -F
iptables -X
iptables -t nat -F
iptables -t nat -X
# Set default policies to DROP
iptables -P INPUT DROP
iptables -P OUTPUT DROP
iptables -P FORWARD DROP
# Allow loopback traffic (critical for local services)
iptables -A INPUT -i lo -j ACCEPT
iptables -A OUTPUT -o lo -j ACCEPT
# Allow established and related connections
iptables -A INPUT -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT
iptables -A OUTPUT -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT
iptables -A FORWARD -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT
# Drop invalid packets
iptables -A INPUT -m conntrack --ctstate INVALID -j DROP
# Allow SSH (restrict to specific IP if possible)
iptables -A INPUT -p tcp --dport 22 -j ACCEPT
# Allow HTTP/HTTPS
iptables -A INPUT -p tcp --dport 80 -j ACCEPT
iptables -A INPUT -p tcp --dport 443 -j ACCEPT
# Allow ICMP (ping, traceroute)
iptables -A INPUT -p icmp --icmp-type echo-request -j ACCEPT
# Explicit OUTPUT rules (adjust as needed)
iptables -A OUTPUT -p tcp --dport 22 -j ACCEPT # SSH outgoing
iptables -A OUTPUT -p tcp --dport 80 -j ACCEPT # HTTP outgoing
iptables -A OUTPUT -p tcp --dport 443 -j ACCEPT # HTTPS outgoing
iptables -A OUTPUT -p udp --dport 53 -j ACCEPT # DNS
iptables -A OUTPUT -p icmp --icmp-type echo-request -j ACCEPT
# Log dropped packets (optional, creates noise)
# iptables -A INPUT -j LOG --log-prefix "DROP-INPUT: " --log-level 7
# iptables -A FORWARD -j LOG --log-prefix "DROP-FWD: " --log-level 7NAT: Masquerading for Outgoing Traffic
Allow internal machines to share a single external IP for outgoing connections:
# Enable IP forwarding
echo 1 > /proc/sys/net/ipv4/ip_forward
# POSTROUTING MASQUERADE (internal → external)
iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE
# INPUT rule to allow forwarding
iptables -A FORWARD -i eth1 -o eth0 -j ACCEPT
iptables -A FORWARD -i eth0 -o eth1 -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPTDNAT: Port Forwarding
Redirect external traffic to internal services:
# Forward external port 8080 to internal 192.168.1.10:80
iptables -t nat -A PREROUTING -p tcp --dport 8080 -j DNAT --to-destination 192.168.1.10:80
# Allow the actual traffic to pass through
iptables -A FORWARD -p tcp -d 192.168.1.10 --dport 80 -j ACCEPTipset: Bulk IP Management
Manage large IP lists efficiently (blocklists, whitelists) without creating hundreds of rules:
# Create a set
ipset create blacklist hash:ip
# Add IPs
ipset add blacklist 192.168.1.50
ipset add blacklist 203.0.113.5
# Use in iptables rule
iptables -A INPUT -m set --match-set blacklist src -j DROP
# List set contents
ipset list blacklist
# Delete set
ipset destroy blacklistRate Limiting
Protect against brute-force attacks and DoS:
# Limit SSH connection attempts: max 5 new connections per minute
iptables -A INPUT -p tcp --dport 22 -m limit --limit 5/minute --limit-burst 5 -j ACCEPT
iptables -A INPUT -p tcp --dport 22 -j DROP
# Block IP after 10 connection attempts in 60 seconds
iptables -N syn_flood
iptables -A INPUT -p tcp --dport 80 -j syn_flood
iptables -A syn_flood -m limit --limit 10/sec --limit-burst 20 -j RETURN
iptables -A syn_flood -j DROP
# Alternative: use recent module
iptables -A INPUT -p tcp --dport 22 -m recent --name ssh_brute --update --seconds 60 --hitcount 5 -j DROP
iptables -A INPUT -p tcp --dport 22 -m recent --name ssh_brute --set -j ACCEPTLogging
Debug and monitor dropped packets:
# Log before dropping
iptables -A INPUT -j LOG --log-prefix "DROP-INPUT: " --log-level 7
iptables -A INPUT -j DROP
# View logs (kernel messages)
tail -f /var/log/syslog | grep DROP-INPUT
# Or use dmesg
dmesg | grep DROP-INPUT
# Limit log spam (1 packet per second)
iptables -A INPUT -j LOG --log-prefix "DROP: " --log-level 7 -m limit --limit 1/secSave and Restore Rules
Persist rules across reboots:
# Save current rules
iptables-save > /etc/iptables/rules.v4
# Restore rules on startup
iptables-restore < /etc/iptables/rules.v4
# Using netfilter-persistent (Debian/Ubuntu)
apt-get install iptables-persistent netfilter-persistent
# Rules auto-saved to /etc/iptables/rules.v4 and rules.v6
netfilter-persistent save
netfilter-persistent reload
netfilter-persistent flushUseful Diagnostics
# List all rules with line numbers
iptables -L -n -v --line-numbers
# List specific table
iptables -t nat -L -n -v
# Count rules
iptables -L | tail -n +3 | wc -l
# Show rule bytes/packets
iptables -L -n -v
# Monitor rule hits in real-time
watch -n 1 'iptables -L -n -v'
# Check default policies
iptables -P
# Test connectivity
ping -c 1 8.8.8.8
nc -zv example.com 443Performance Tip: Order rules from most to least specific. High-traffic ports should appear early. Use ipset for large IP lists instead of many individual rules.