Server Management

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 DROP

Valid states:

  • NEW: Initiating a new connection
  • ESTABLISHED: Part of an existing connection
  • RELATED: 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 7

NAT: 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 ACCEPT

DNAT: 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 ACCEPT

ipset: 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 blacklist

Rate 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 ACCEPT

Logging

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/sec

Save 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 flush

Useful 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 443

Performance 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.

On this page