Using a custom chain to define a list of trusted hosts in iptables 1

Posted by James Wilford Sun, 10 Aug 2008 13:03:00 GMT

A common problem with firewalls is that you want to allow certain ports from a list of trusted hosts. For example, you have 3 locations that you commonly log into your machine from, and you want to allow ssh and mysql from these locations, but not from elsewhere. If you just added rules to the INPUT chain, you would need 3 rules for ssh, and another 3 rules for mysql, and each source address would be specified twice, once for each of these services. This is not very efficient, as adding another source would involve inserting 2 rules. Furthermore, the number of rules multiplies rapidly as additional ports or source addresses are needed.

Here's how to avoid this problem by using custom chains in iptables to allow connections from a range of different sources without having lots of similar rules in the INPUT chain. I'm doing this on Ubuntu, but this can obviously be applied to any distro.

Add the standard rules

First of all lets set up the standard incoming rules. I'm going to allow everything on the loopback interface, any packets with state RELATED or ESTABLISHED, port 80 for my web server, and icmp so people can ping my server:

sudo iptables -I INPUT 1 -i lo -j ACCEPT
sudo iptables -A INPUT -m state --state ESTABLISHED,RELATED -j ACCEPT
sudo iptables -A INPUT -p tcp --dport 80 -j ACCEPT
sudo iptables -A INPUT -p icmp -m icmp --icmp-type any -j ACCEPT

Add a custom chain

Now lets say we want to allow ssh from 3 different trusted subnets. I'll use private RFC1918 addresses for this example, but in reality these would probably be the public addresses of the locations you want to log in from.

First add a "Trusted" chain:

sudo iptables -N Trusted

Then add rules to this chain that just allow anything from your trusted sources:

sudo iptables -A Trusted -s 192.168.0.0/255.255.255.0 -j ACCEPT
sudo iptables -A Trusted -s 10.20.0.0/255.255.255.0 -j ACCEPT
sudo iptables -A Trusted -s 172.16.0.0/255.255.0.0 -j ACCEPT

Now add a rule to your INPUT chain that hands off ssh connections to your custom chain:

sudo iptables -A INPUT -p tcp --dport 22 -j Trusted

This will check incoming ssh connections against your Trusted chain, which will allow them.

Drop all other connections

At the moment we are still allowing all incoming connections anyway. So finally, add a rule at the end of the INPUT chain to drop everything else:

sudo iptables -A INPUT -j DROP

Now your iptables should look something like this:

$ sudo iptables -L -v
Chain INPUT (policy ACCEPT 778 packets, 85228 bytes)
 pkts bytes target     prot opt in     out     source               destination
56151   57M ACCEPT     0    --  lo     any     anywhere             anywhere
 446K   70M ACCEPT     0    --  any    any     anywhere             anywhere            state RELATED,ESTABLISHED
19045 1065K ACCEPT     tcp  --  any    any     anywhere             anywhere            tcp dpt:www
   94  8056 ACCEPT     icmp --  any    any     anywhere             anywhere            icmp any
 4943  468K Trusted    tcp  --  any    any     anywhere             anywhere            tcp dpt:ssh
 1695  331K DROP       0    --  any    any     anywhere             anywhere

Chain FORWARD (policy ACCEPT 0 packets, 0 bytes)
 pkts bytes target     prot opt in     out     source               destination

Chain OUTPUT (policy ACCEPT 441K packets, 701M bytes)
 pkts bytes target     prot opt in     out     source               destination

Chain Trusted (1 references)
 pkts bytes target     prot opt in     out     source               destination
    0     0 ACCEPT     0    --  any    any     192.168.0.0/24       anywhere
    0     0 ACCEPT     0    --  any    any     10.20.0.0/24         anywhere
    0     0 ACCEPT     0    --  any    any     172.16.0.0/16        anywhere

And now, you can allow extra ports from your trusted sources without having to repeat your sources in the INPUT chain. For example, we can insert a rule to allow MySQL connections. This needs to be inserted before the DROP rule:

sudo iptables -I INPUT 6 -p tcp --dport 3306 -j Trusted

Saving your iptables rules

Save your rules to a file:

sudo iptables-save > /etc/iptables.rules

Now you can edit your /etc/network/interfaces file to bring up iptables automatically, and save the rules when the interface is brought down:

auto eth0
iface eth0 inet dhcp
  pre-up iptables-restore < /etc/iptables.rules
  post-down iptables-save -c > /etc/iptables.rules

For more information, see the Ubuntu iptables howto:

help.ubuntu.com/community/IptablesHowTo