Saturday, March 26, 2011

IPTables: Fun with MARK

One thing that’s always bugged me about IPTables is the lack of a way to use groups when writing rules, which can complicate things if you’ve got a potentially large rulebase. One way round this is to use something like fwbuilder, which gives you a graphical interface not unlike Checkpoint‘s SmartDashboard GUI for their Firewall-1 devices. The downside to this, though, is that the resulting IPTables ruleset is far from legible – which, to be fair, isn’t the goal of fwbuilder – and this makes hacking about with the rules nearly impossible.
So what options are there? One way is to repeat the same rule for different sources or destinations, but this can quickly get messy, especially if there’s multiple ports involved. If there was a way we could group things together and keep them tidy, maintaining the rulebase would be a lot easier. This is where MARK comes in.
The MARK target lets us set a 32-bit value (or 0xFFFFFFFF) on a packet, which we can then look for later with the mark match. This in itself can be useful, but where it gets really handy is adding the values together.

Starting out

To start off with, here’s an example
# Create a new chain, which will in effect be our source 'group'
iptables -N S-TRUSTED
# Add our sources
iptables -A S-TRUSTED -s 192.168.1.1/32 -j MARK --set-xmark 0x8/0x0
iptables -A S-TRUSTED -s 192.168.2.0/24 -j MARK --set-xmark 0x8/0x0
iptables -A S-TRUSTED -s 192.168.3.3/29 -j MARK --set-xmark 0x8/0x0

# Create a chain for the destination group
iptables -N D-DMZ
# Add our DMZ machines
iptables -A D-DMZ -d 10.1.1.1/32 -j MARK --set-xmark 0x4/0x0
iptables -A D-DMZ -d 10.1.2.0/24 -j MARK --set-xmark 0x4/0x0

# Create a chain for the services
iptables -N D-SRV-MGMT
# Add our service
iptables -A D-SRV-MGMT -p tcp -m tcp --dport 22 -j MARK --set-xmark 0x2/0x0
iptables -A D-SRV-MGMT -p tcp -m tcp --dport 80 -j MARK --set-xmark 0x2/0x0
So what we have now are three chains, which do the following
  • If the source matches, add 0×8 to the packet mark
  • If the destination matches, add 0×4 to the packet mark
  • If the service matches, add 0×2 to the packet mark
If you know your hexadecimal, you’ll already know that if all of these are true, we’ll come out with 0xE (or 14, in decimal).

Making the rule

Hopefully you’ll see where we’re going with this with the next example, which is our actual rule
# Create a new chain for our rule and add it to our FORWARD chain
iptables -N R-ALLOW-DMZ-MGMT
iptables -A FORWARD -j R-ALLOW-DMZ-MGMT
# Zero out the packet mark to make sure no previous rules interfere
iptables -A R-ALLOW-DMZ-MGMT -j MARK --set-xmark 0x0/0x0
# Jump to our 'source group' chain
iptables -A R-ALLOW-DMZ-MGMT -j S-TRUSTED
# Jump to our 'destination group' chain
iptables -A R-ALLOW-DMZ-MGMT -j D-DMZ
# Jump to our 'service group' chain
iptables -A R-ALLOW-DMZ-MGMT -j D-SRV-MGMT
# If the packet mark matches 0xE, then ACCEPT
iptables -A R-ALLOW-DMZ-MGMT -m mark --mark 0xE -j ACCEPT
And there we have it – if the packet matches the source, destination and service, the packet mark will be 0xE. If, say, it matches everything except the destination, it’ll come out as 0xC, which won’t match and so netfilter will carry on along the rest of the rules. If you want processing to stop here, you could always add a LOG and REJECT/DROP target at the end of the R-ALLOW-DMZ-MGMT chain.

Negation

Sometimes we want to be able to say ‘everything but that particular network’, whether it be for accepting or dropping packets. We can do that with this, too
# Add a new chain for negating the source
iptables -N S-NEGATE
# XOR the current packet mark with 0x8 - our 'source match' identifier
iptables -A S-NEGATE -j MARK --xor-mark 0x8
To use it, simply drop it in the R-ALLOW-DMZ-MGMT rule above after the jump to the S-TRUSTED chain, and if S-TRUSTED matched, it won’t any more, and vice versa. To negate the destination and service matches, you’ll need to create similar chains for (for example) D-NEGATE and D-SRV-NEGATE, replacing the 0×8 with 0×4 and 0×2 respectively.

Things to note

One downside of this method is that because of the way IPTables works, if you want to use the same set of networks and hosts as a source and a destination, you’ll need to duplicate them, but match on the source or destination as appropriate. Using the example given above, if we wanted a group with the same entries as S-TRUSTED, but matching on traffic going to them, we’d need to create another group (for example, D-TRUSTED), which will be identical save for the IP matches (which will need changing to -d) and the mask (which will need setting to 0×4 instead of 0×8).
Also, be careful if you’re using packet marks to do something outside of netfilter (say, for traffic control – which I’ll cover in a future post). One way round this is the facility to save the current packet mark to the current connection mark, or vice versa – if you go down this path then having a look at the iptables manpage for the MARK and CONNMARK targets will be useful.

Conclusions

This is an effective way of grouping hosts, networks and services within IPTables. It can be quite a bit of work to start with to add all the groups, but once in place it makes writing rules a lot more logical.

Taking things further

One way which you could take this further would be to group interfaces together in a similar fashion, say by adding 0×10 to the packet, and then matching on 0x1E rather than 0xE.
Usefully, if you send the packet out to syslog with the LOG target, netfilter will print out the current packet mark at the end of the log message as MARK=0xN, which can be useful when debugging.

Courtesy : http://andys.org.uk/bits/2010/01/27/iptables-fun-with-mark/