iptables isn’t just a firewall; it’s the fundamental packet-processing engine of the Linux kernel.

Imagine a single packet arriving at your Linux box. It’s not just inspected and either allowed or dropped. Instead, it embarks on a meticulously defined journey through a series of "tables," each containing "chains" of "rules." Each rule is a test, and if the packet matches, it’s handed off to a "target," which dictates the packet’s next action.

Let’s see this in action. We’ll start by setting up a simple rule to block all incoming SSH traffic from a specific IP address.

# First, list existing rules to see what we're working with
sudo iptables -L -v -n

# Now, add the rule to drop SSH from 192.168.1.100
sudo iptables -I INPUT -s 192.168.1.100 -p tcp --dport 22 -j DROP

# Verify the rule is added
sudo iptables -L INPUT -v -n

When you run iptables -L INPUT -v -n, you’ll see a line similar to this:

Chain INPUT (policy ACCEPT 0 packets, 0 bytes)
 pkts bytes target     prot opt in     out     source               destination
    0     0 DROP       tcp  --  *      *       192.168.1.100        0.0.0.0/0            tcp dpt:22

Here’s the breakdown:

  • Chain INPUT: This rule is being added to the INPUT chain, which processes packets destined for the local machine.
  • policy ACCEPT: The default action for the INPUT chain is to accept packets that don’t match any rule.
  • pkts bytes: These counters show how many packets and bytes have matched this rule. Initially zero.
  • DROP: This is the target. The packet will be silently discarded.
  • tcp: The protocol this rule applies to.
  • --dport 22: The destination port. For SSH, this is port 22.
  • 192.168.1.100: The source IP address we are blocking.

The power of iptables lies in its flexibility. You can define complex scenarios by combining these elements.

The Core Components: Tables, Chains, Rules, and Targets

  1. Tables: Think of tables as distinct processing pipelines for different types of network operations.

    • filter (default): This is the workhorse, responsible for deciding whether to ACCEPT, DROP, or REJECT packets. It has three built-in chains: INPUT, OUTPUT, and FORWARD.
    • nat: Handles Network Address Translation. Used for port forwarding, masquerading (hiding internal IPs behind a public IP), etc. It has PREROUTING, POSTROUTING, and OUTPUT chains.
    • mangle: For modifying packet headers (like TTL, TOS). It has PREROUTING, INPUT, FORWARD, OUTPUT, and POSTROUTING chains.
    • raw: Used for packet exemption from connection tracking. Has PREROUTING and OUTPUT chains.
    • security: For Mandatory Access Control (MAC) networking rules. Has INPUT, OUTPUT, FORWARD, PREROUTING, and POSTROUTING chains.
  2. Chains: Within each table, chains are ordered lists of rules. Packets traverse these chains.

    • Built-in Chains: INPUT, OUTPUT, FORWARD, PREROUTING, POSTROUTING.
    • User-defined Chains: You can create your own chains to better organize rules and improve performance by reducing the number of rules a packet has to traverse.
  3. Rules: A rule is a conditional statement. If a packet meets the criteria specified by the rule’s matching conditions, the rule’s target is executed.

    • Matching Conditions: These specify packet characteristics like source/destination IP address, protocol, port, interface, packet state (e.g., NEW, ESTABLISHED), etc.
    • Order Matters: Rules are evaluated sequentially within a chain. The first rule that a packet matches determines its fate (unless the target is a jump to another chain).
  4. Targets: This is the action taken when a packet matches a rule.

    • ACCEPT: Allow the packet to continue on its way.
    • DROP: Silently discard the packet. The sender receives no notification.
    • REJECT: Discard the packet but send an error message (e.g., "connection refused") back to the sender.
    • LOG: Log the packet’s details (useful for debugging) and then pass it to the next rule.
    • SNAT / MASQUERADE: Change the source IP address (used in nat table).
    • DNAT: Change the destination IP address (used in nat table).
    • Jumping to another chain: You can create custom chains to group related rules.

A Deeper Dive: The nat Table and Port Forwarding

Let’s say you want to forward incoming traffic on port 8080 of your external IP address to port 80 on an internal web server at 192.168.1.50. This requires the nat table.

# Add a DNAT rule to the PREROUTING chain in the nat table
# This rule intercepts traffic arriving on external_ip:8080 and changes its destination to 192.168.1.50:80
sudo iptables -t nat -A PREROUTING -p tcp --dport 8080 -j DNAT --to-destination 192.168.1.50:80

# Now, we need to allow this traffic to reach the internal server.
# If the internal server is not the local machine, we need to allow it in the FORWARD chain (filter table).
# If the internal server *is* the local machine, we'd use the INPUT chain.
# Assuming it's a different machine on the network:
sudo iptables -A FORWARD -p tcp -d 192.168.1.50 --dport 80 -m state --state NEW,ESTABLISHED,RELATED -j ACCEPT

# For traffic to be forwarded, the kernel's IP forwarding must be enabled.
# Check:
cat /proc/sys/net/ipv4/ip_forward
# If 0, enable it temporarily:
sudo sysctl -w net.ipv4.ip_forward=1
# To make it permanent, edit /etc/sysctl.conf and uncomment/add:
# net.ipv4.ip_forward = 1
# Then run: sudo sysctl -p

# We also need to allow the return traffic from the internal server back out.
# This is usually handled by the stateful tracking in the FORWARD chain,
# but sometimes you might need explicit rules if your policy is restrictive.
# A common pattern is to allow NEW, ESTABLISHED, RELATED.
# The rule above already does this.

# Finally, we might want to masquerade the traffic so the internal server
# doesn't see the router's IP as the source, but rather the router's external IP.
# This is done in the POSTROUTING chain of the nat table.
# Replace eth0 with your external network interface.
sudo iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE

The DNAT rule in PREROUTING happens before the routing decision is made for the packet. The FORWARD chain then acts on the packet after the routing decision, if it’s determined to be a packet that needs to be forwarded. MASQUERADE in POSTROUTING happens after the routing decision, just before the packet leaves the interface, and is crucial for outbound connections originating from internal IPs.

One subtlety often missed is the interplay between the filter and nat tables. A packet might be DNAT’d to a new destination IP and port, but it still has to pass through the filter table’s INPUT or FORWARD chains based on its new destination. If your filter rules are too strict, the DNAT’d packet might be dropped before it ever reaches its intended internal destination.

Understanding the order of operations across tables and chains is key to mastering iptables. The next step is usually exploring connection tracking and how to implement more sophisticated stateful firewalling.

Want structured learning?

Take the full Iptables course →