Skip to content

๐Ÿ”— Custom Chains In-Depth โ€‹

Why Use Custom Chains? โ€‹

Imagine you manage a public-facing web server with traffic from around the world. If you cram all rules into the INPUT chain, dozens of rules lined up makes troubleshooting like finding a thread in a tangled mess.

Custom chains are like organizing your rules into folders โ€” SSH rules go in one chain, HTTP in another, database in yet another. Clear, maintainable, and reusable.

๐Ÿ’ก Tip: ๐Ÿ’ก Analogy: Built-in chains (INPUT/OUTPUT/FORWARD) are the operating system's "inbox/outbox," while custom chains are your own "sorted folders." Packets don't automatically enter custom chains; you must use -j to jump to them from built-in chains.

Creating and Using Custom Chains โ€‹

bash
# Create a custom chain called WEBSERVER
iptables -N WEBSERVER

# View โ€” it will appear at the end of -L output
iptables -L -n
Chain INPUT (policy DROP)
target     prot opt source               destination
...
Chain WEBSERVER (0 references)
target     prot opt source               destination

# Add rules to the custom chain
iptables -A WEBSERVER -p tcp --dport 80 -j ACCEPT
iptables -A WEBSERVER -p tcp --dport 443 -j ACCEPT
iptables -A WEBSERVER -p tcp --dport 8080 -j ACCEPT

# Jump from INPUT chain to the custom chain (note: -j not -A)
iptables -A INPUT -p tcp -m multiport --dports 80,443,8080 -j WEBSERVER

# View statistics โ€” which rules matched how many packets
iptables -L WEBSERVER -v -n
Chain WEBSERVER (1 references)
 pkts bytes target     prot opt in     out     source               destination
  15K  960K ACCEPT     tcp  --  *      *       0.0.0.0/0            0.0.0.0/0            tcp dpt:80
  23K 1400K ACCEPT     tcp  --  *      *       0.0.0.0/0            0.0.0.0/0            tcp dpt:443
    0     0 ACCEPT     tcp  --  *      *       0.0.0.0/0            0.0.0.0/0            tcp dpt:8080

๐Ÿ’ก Tip: ๐Ÿ’ก See "1 references"? That means 1 rule references this chain. If references is 0, no rules jump to this chain โ€” it's like a folder that's never been opened.

Practice: Service-Based Firewall Organization โ€‹

This is the recommended organization for production environments โ€” each service gets its own custom chain, and the main chain only handles routing:

bash
#!/bin/bash
# ============================================
# Production server firewall โ€” organized by service
# ============================================

# Flush
iptables -F
iptables -X  # Delete all custom chains

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

# Loopback + established connections
iptables -A INPUT -i lo -j ACCEPT
iptables -A INPUT -m state --state ESTABLISHED,RELATED -j ACCEPT

# ---- Create service chains ----
iptables -N SVC_SSH
iptables -N SVC_HTTP
iptables -N SVC_MONITOR

# ---- SSH service ----
iptables -A SVC_SSH -p tcp --dport 22 -m state --state NEW -m recent \
  --set --name SSH
iptables -A SVC_SSH -p tcp --dport 22 -m state --state NEW -m recent \
  --update --seconds 60 --hitcount 5 --name SSH -j DROP
iptables -A SVC_SSH -p tcp --dport 22 -j ACCEPT

# ---- HTTP/HTTPS service ----
iptables -A SVC_HTTP -p tcp -m multiport --dports 80,443 -j ACCEPT

# ---- Monitoring service (Prometheus + Grafana) ----
iptables -A SVC_MONITOR -p tcp --dport 9090 -s 10.0.0.0/8 -j ACCEPT
iptables -A SVC_MONITOR -p tcp --dport 3000 -s 10.0.0.0/8 -j ACCEPT

# ---- Mount to INPUT chain ----
iptables -A INPUT -p tcp --dport 22 -j SVC_SSH
iptables -A INPUT -p tcp -m multiport --dports 80,443 -j SVC_HTTP
iptables -A INPUT -p tcp -m multiport --dports 9090,3000 -j SVC_MONITOR

# ---- Log and drop the rest ----
iptables -A INPUT -j LOG --log-prefix "IPT-DROP: " --log-level 4
iptables -A INPUT -j DROP

echo "Firewall rules loaded successfully"
iptables -L -v -n | head -30

โš ๏ธ Warning: โš ๏ธ Note: iptables -X deletes all custom chains. If a chain is still referenced by other rules (references โ‰  0), -X will error out. You must clear the rules that reference it first.

Renaming and Flushing Custom Chains โ€‹

bash
# Flush a custom chain's rules (keeps the chain itself)
iptables -F WEBSERVER

# Rename a custom chain
iptables -E WEBSERVER WEB_RULES
# Now -j WEBSERVER needs to be changed to -j WEB_RULES

# Delete a single custom chain (must clear references first)
iptables -D INPUT -p tcp -m multiport --dports 80,443 -j WEBSERVER
iptables -X WEBSERVER