Post

iptables: building rules that actually make sense

A practical walkthrough of iptables rule logic, chain structure, and building a sensible default policy from scratch.

Project Description

iptables has a reputation for being arcane. The reputation is partly deserved; the syntax is terse and the documentation assumes you already understand what chains, tables, and targets are. But the underlying model is actually quite logical once you internalize it, and the visibility it gives you over network traffic is worth the learning curve.

This is a walkthrough of how I approach iptables and the ruleset I use as a baseline on my own systems.


The Mental Model

iptables filters packets at the kernel level by passing them through a series of chains in specific tables. For most firewall purposes, only the filter table matters, and within it three chains:

  • INPUT: traffic destined for the local machine
  • OUTPUT: traffic originating from the local machine
  • FORWARD: traffic passing through (router/gateway use)

Each chain has a default policy (what happens to a packet that doesn’t match any rule). The two meaningful policies are ACCEPT and DROP.

A sane starting point: default DROP on INPUT, default ACCEPT on OUTPUT.


A Working Baseline

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
# Flush existing rules
iptables -F
iptables -X

# Default policies
iptables -P INPUT DROP
iptables -P FORWARD DROP
iptables -P OUTPUT ACCEPT

# Allow established and related connections (return traffic)
iptables -A INPUT -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT

# Allow loopback
iptables -A INPUT -i lo -j ACCEPT

# Allow SSH (adjust port if you've changed it)
iptables -A INPUT -p tcp --dport 22 -j ACCEPT

# Allow HTTP and HTTPS if hosting anything
iptables -A INPUT -p tcp --dport 80 -j ACCEPT
iptables -A INPUT -p tcp --dport 443 -j ACCEPT

# Allow ICMP (ping), optional but useful
iptables -A INPUT -p icmp -j ACCEPT

# Drop everything else (covered by default policy, but explicit is clear)
iptables -A INPUT -j DROP

The ESTABLISHED,RELATED rule is critical. Without it, default DROP on INPUT would block all return traffic including responses to outbound connections you initiated, which would break nearly everything.


Logging Before Dropping

Before the final DROP, adding a LOG rule lets you see what is being blocked:

1
2
iptables -A INPUT -m limit --limit 5/min -j LOG --log-prefix "iptables-drop: " --log-level 4
iptables -A INPUT -j DROP

The --limit prevents log flooding from port scans. The logged output appears in /var/log/kern.log or wherever your system routes kernel messages. Watching this for a while gives you a clear picture of what’s actually hitting your machine.


Rate Limiting SSH

SSH brute force is a constant reality even on non-standard ports. iptables can rate-limit connection attempts:

1
2
iptables -A INPUT -p tcp --dport 22 -m state --state NEW -m recent --set
iptables -A INPUT -p tcp --dport 22 -m state --state NEW -m recent --update --seconds 60 --hitcount 4 -j DROP

This drops any source IP that makes more than 3 new SSH connection attempts within 60 seconds. Crude but effective. Fail2Ban is more sophisticated, but this works at the kernel level with no daemon required.


Persistence

iptables rules do not survive a reboot by default. To make them persistent:

1
2
3
4
5
6
# Debian/Ubuntu
sudo apt install iptables-persistent
sudo netfilter-persistent save

# Fedora/RHEL (firewalld wraps iptables, but raw rules can be saved):
sudo iptables-save > /etc/sysconfig/iptables

Alternatively, write your rules into a shell script and call it from a systemd unit on boot. This approach makes the ruleset self-documenting and easy to version-control.


Security Relevance

Domain 1: Threats and Vulnerabilities Network-layer filtering is a direct control against unauthorized access and reduces exposure to scanning and brute-force attempts.

Domain 3: Implementation Firewall rule design is a core implementation competency. Understanding why rules are ordered the way they are, and what the default policy means, informs better network architecture decisions.


Summary

The thing about iptables is that writing a bad ruleset that blocks everything important is just as easy as writing a good one. The key habits are: start with an explicit default policy, always allow established connections before anything else, test changes carefully (especially on remote systems), and log what you drop.

Once the mental model clicks, iptables becomes one of the more satisfying tools to work with. The kernel is doing exactly what you told it to, and you can see precisely which packets are being accepted or rejected and why.

Tools used: iptables, iptables-save, iptables-restore, conntrack, netfilter-persistent.

This post is licensed under CC BY 4.0 by the author.