Iptables traversal and the chain policies, manual editing

firewalliptables

I like editing the iptables file manually, but there's something that confuses me a bit.

From how I have understood iptables from the manual, it traverses the rules from top to bottom, and if no match is possible it will be logged and denied by the default rules in the bottom.

All good.

But what does these mean:

*filter
:INPUT ACCEPT [0:0]
:FORWARD ACCEPT [0:0]
:OUTPUT ACCEPT [0:0]
<accept ssh, ssl, etc>
<log all, deny all>

The chains in the top has default policy "accept" – is this only a way of saying that we accept the packets into the filter table, for later matching? For me, after reading the manual, it feels as if it would accept everything and then not read the rules, because that's how rules work by default – if anything matches it will stop reading rules and then do what the policy sais. Any documentation of this behaviour? I cannot find anything specific enough.

I have also seen this, after the *filter table:

*filter
:RH-Firewall-1-INPUT - [0:0]

Which gives the ability to add your own chains and route other chains to this chain:

-A INPUT -j RH-Firewall-1-INPUT
-A FORWARD -j RH-Firewall-1-INPUT

But here the "ACCEPT" or "DROP" is not specified at all for this chain, but instead there's a dash (-).

Which makes me even more confused – how does it work?

Best Answer

  1. The policy on a built-in chain determines what happens to packets that make it through all the rules in the chain without matching a single one with a dispositive target. In your example above, packets that make it all the way through the FORWARD chain without matching any rule with a dispositive target will be ACCEPTed, because the chain's policy is ACCEPT.

  2. User-defined chains cannot have a policy. Packets that pass all the way through a user-defined chain without matching a rule with a dispositive target return to the chain from which they were sent to the user-defined chain (the "calling chain") at the rule after the one that sent them there.

A dispositive target is one which disposes of a packet; examples include DROP, ACCEPT, and MASQUERADE; non-dispositive targets include LOG, and the null target (it's quite legal to have a rule that has no target). When a packet matches a rule with a dispositive target, the packet is considered disposed of, and no further rule processing occurs. When a packet matches a rule without a dispositive target, packet processing continues at the next rule.

An important corollary of this logic on target-matching and disposition is that first-dispositive-match wins. Too often we see questions on SF where people have a chain that says, eg,

Chain INPUT (policy ACCEPT 210 packets, 22621 bytes)
 pkts bytes target     prot opt in     out     source        destination         
3224K 1330M DROP       tcp  --  *      *       0.0.0.0/0     0.0.0.0/0     tcp dpt:80

which contains a single rule to block access to a web server (tcp dpt:80). They wish to allow a single external IP address, say 1.2.3.4, to access this web server, so they add a rule with

iptables -A INPUT -p tcp --dport 80 -s 1.2.3.4 -j ACCEPT

and they end up with a chain like this

Chain INPUT (policy ACCEPT 210 packets, 22621 bytes)
 pkts bytes target     prot opt in     out     source        destination         
3224K 1330M DROP       tcp  --  *      *       0.0.0.0/0     0.0.0.0/0     tcp dpt:80
    0     0 ACCEPT     tcp  --  *      *       1.2.3.4       0.0.0.0/0     tcp dpt:80

As you can see, the packet counts on that last rule are zero - and they will always be zero. No packet can match that last rule without having matched the one before it, and the one before it is dispositive (DROP), so no packet will ever reach that last rule.

The way to deal with this correctly is to ensure that your exceptions come before your rules, like this:

Chain INPUT (policy ACCEPT 210 packets, 22621 bytes)
 pkts bytes target     prot opt in     out     source        destination         
   20   875 ACCEPT     tcp  --  *      *       1.2.3.4       0.0.0.0/0     tcp dpt:80
3224K 1330M DROP       tcp  --  *      *       0.0.0.0/0     0.0.0.0/0     tcp dpt:80

Now, the exceptional acceptances are handled first, then other web server packets that don't come from the exceptional server are handled and dropped, and finally the chain policy takes care of all other packets and accepts them.

Related Topic