Skip to content

🧰 05-05: iptables Fundamentals


πŸ“Œ What Is iptables?

iptables is the traditional command-line firewall configuration tool on Linux. It's important to be precise about what iptables actually is, because the terminology trips people up:

  • Netfilter is the actual packet-filtering framework built into the Linux kernel. It defines a series of "hook points" inside the kernel's networking stack where code can inspect, modify, or drop packets as they pass through.
  • iptables is the user-space program administrators use to write rules into Netfilter. It does not do the filtering itself β€” it just configures the kernel's tables.
  • In casual conversation, "iptables" is often used to refer to the whole system (kernel + tool), but technically the enforcement engine in the kernel is Netfilter/Xtables, and iptables is just the control interface to it.

πŸ’‘ Think of it like a light switch and the electrical wiring behind the wall: iptables is the switch, Netfilter is the wiring that actually does the work. Modern distros are transitioning to a newer tool called nftables, but iptables remains extremely widely used and is the best starting point for learning the underlying concepts.


πŸ—„οΈ Tables

iptables organizes rules into tables, where each table serves a distinct purpose. This is the top level of the hierarchy β€” you almost always specify a table before you specify anything else.

Table Purpose
filter The default table β€” decides whether to ACCEPT, DROP, or REJECT packets. This is "the firewall" in the everyday sense.
nat Handles Network Address Translation β€” rewriting source or destination addresses/ports (used for port forwarding, masquerading internal hosts, etc.)
mangle Used for specialized packet modification β€” altering TTL, marking packets for later processing (QoS), changing TOS/DSCP fields
raw Used to mark packets that should bypass connection tracking entirely (an advanced/rare use case)

πŸ’‘ If you don't specify -t <table> in an iptables command, it defaults to the filter table.


πŸ”— Chains

Within each table, rules are grouped into chains. A chain is simply an ordered list of rules that gets checked at a specific point in a packet's journey through the kernel.

Chain When It's Checked
PREROUTING As soon as a packet arrives, before the kernel decides where to route it
INPUT For packets destined for this local machine
FORWARD For packets passing through this machine to somewhere else (e.g., this machine is acting as a router)
OUTPUT For packets originating from this local machine
POSTROUTING Just before a packet leaves the network interface, after routing has been decided

Not every chain exists in every table β€” a table only has the chains relevant to its purpose:

Table Available Chains
filter INPUT, FORWARD, OUTPUT
nat PREROUTING, OUTPUT, POSTROUTING
mangle PREROUTING, INPUT, FORWARD, OUTPUT, POSTROUTING
raw PREROUTING, OUTPUT

🚦 How a Packet Traverses the Chains

This is the single most important mental model for using iptables correctly. Depending on where a packet is going, it passes through a different sequence of chains.

                                    β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
   Packet arrives  ───────────────▢│ PREROUTING  β”‚  (nat: DNAT happens here)
   on a network                    β””β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”˜
   interface                              β”‚
                                           β–Ό
                                  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
                                  β”‚ Routing Decision β”‚
                                  β”‚  (is this packet β”‚
                                  β”‚  for ME, or to    β”‚
                                  β”‚  be FORWARDED?)   β”‚
                                  β””β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                        β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
                        β–Ό                                     β–Ό
                β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”                     β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
                β”‚     INPUT      β”‚                     β”‚    FORWARD     β”‚
                β”‚ (destined for  β”‚                     β”‚ (passing       β”‚
                β”‚  this host)    β”‚                     β”‚  through)      β”‚
                β””β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”˜                     β””β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                        β–Ό                                      β”‚
              β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”                             β”‚
              β”‚ Local Process /   β”‚                             β”‚
              β”‚ Application       β”‚                             β”‚
              β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜                             β”‚
                        β–Ό                                      β”‚
                β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”                              β”‚
                β”‚    OUTPUT      β”‚                              β”‚
                β”‚ (generated by  β”‚                              β”‚
                β”‚  this host)    β”‚                              β”‚
                β””β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”˜                              β”‚
                        β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                                           β–Ό
                                  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
                                  β”‚ POSTROUTING   β”‚  (nat: SNAT/MASQUERADE here)
                                  β””β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”˜
                                          β–Ό
                                Packet leaves the
                                network interface

Reading the Diagram

  • Traffic destined for a service running on this machine (e.g., someone SSHing into this server): PREROUTING β†’ INPUT
  • Traffic this machine generates itself (e.g., this machine making an outbound HTTP request): OUTPUT β†’ POSTROUTING
  • Traffic this machine is only routing/forwarding for others (e.g., this machine acting as a home router passing traffic from your laptop to the internet): PREROUTING β†’ FORWARD β†’ POSTROUTING β€” it never touches INPUT or OUTPUT at all
  • DNAT (destination NAT / port forwarding) rules live in PREROUTING, because the destination address needs to be rewritten before the routing decision is made
  • SNAT/MASQUERADE (source NAT) rules live in POSTROUTING, because the source address should be rewritten after routing, right before the packet leaves the interface

πŸ’‘ A very common beginner mistake is writing a rule in the INPUT chain expecting it to affect traffic that's actually being forwarded through the machine (e.g., traffic from a home network out to the internet via a Linux router). That traffic never touches INPUT/OUTPUT β€” you need FORWARD.


🎯 Default Policies

Every chain in the filter table has a default policy β€” what to do with a packet that reaches the end of the chain without matching any rule. This is the practical implementation of "default deny vs. default allow" from 05-01: Firewall Concepts & History.

# View current default policies (shown in the "Chain ... (policy ...)" header)
sudo iptables -L

# Set the INPUT chain's default policy to DROP (default-deny)
sudo iptables -P INPUT DROP

# Set the FORWARD chain's default policy to DROP
sudo iptables -P FORWARD DROP

# OUTPUT is often left as ACCEPT for a typical server (be cautious β€” see 05-08)
sudo iptables -P OUTPUT ACCEPT

⚠️ Important safety note: if you're configuring a remote server over SSH, setting the INPUT policy to DROP before you've added a rule that explicitly allows your SSH connection will instantly lock you out. Always add your allow rules first, then tighten the default policy last.


πŸ‘€ Listing and Viewing Rules

The single most useful iptables command for a beginner is:

sudo iptables -L -v -n

Breaking down the flags:

Flag Meaning
-L List the rules in a chain (or all chains, if none specified)
-v Verbose β€” show packet/byte counters and interface names
-n Numeric β€” show IP addresses and ports as numbers, not resolved hostnames/service names (much faster, and avoids DNS lookups leaking information)

Other Useful Viewing Commands

# List rules in the nat table specifically
sudo iptables -t nat -L -v -n

# List rules with line numbers (needed to delete a specific rule by position)
sudo iptables -L --line-numbers

# Delete rule number 2 from the INPUT chain
sudo iptables -D INPUT 2

# Flush (delete) all rules in the filter table's chains
sudo iptables -F

# Flush all rules in the nat table
sudo iptables -t nat -F

πŸ’‘ iptables rules are not persistent by default β€” a reboot wipes them unless you save them with a distro-specific tool (e.g., iptables-save / iptables-persistent on Debian/Ubuntu, or a systemd unit that reapplies a saved rule file at boot).


πŸ“Œ Key Takeaways

  • iptables is the user-space tool that configures Netfilter, the actual packet-filtering engine inside the Linux kernel.
  • Rules are organized into tables (filter, nat, mangle, raw), each with a distinct purpose; filter is the default and most commonly used.
  • Within each table, rules are grouped into chains (PREROUTING, INPUT, FORWARD, OUTPUT, POSTROUTING) tied to specific points in a packet's journey.
  • Which chains a packet passes through depends on whether it's destined for the local machine (INPUT), generated by it (OUTPUT), or merely passing through (FORWARD) β€” this is the core mental model for writing correct rules.
  • DNAT (destination NAT) happens in PREROUTING; SNAT/MASQUERADE (source NAT) happens in POSTROUTING.
  • Every chain has a default policy (ACCEPT or DROP) that applies when no rule matches β€” set your allow rules before tightening the default policy to DROP, especially on a remote machine.
  • iptables -L -v -n is the essential command for viewing current rules with packet counters and numeric addresses.
  • iptables rules do not survive a reboot unless explicitly saved β€” this trips up many beginners in labs and real deployments alike.