Tuesday, April 14, 2015

Basic filter rules with Netfilter (iptables)

If you understood how Netfilter works after reading my previous article, you are able to configure some basic rules to filter traffic between two or more networks.

Let's suppose the following scenario: You've got a system with two network interfaces connected to an Intranet (eth0) and to the outside (eth1) and you want only traffic from the former to the later to be allowed, but only through a local proxy.

I'll show you how to do that with some iptables commands stored in a shell script that will use variables so you can easily modify it for your own network.

The file with this shell script must have execution permission for the root and shouldn't be readable for any one else. Save it in the directory /etc/network/if-pre-up.d/ if you're working with Debian or Ubuntu, as it's my case.


# Variables
# Group 1

# Delete any previous ruleset
# Group 2
iptables -F
iptables -X
iptables -Z

# Set the default policy
# Group 3
iptables -P INPUT DROP
iptables -P FORWARD DROP

# Allow traffic for loopback interface
# Group 4
iptables -A INPUT -i lo -j ACCEPT

# Allow HTTP and HTTPS for the local proxy
# Group 5
iptables -A FORWARD -s $LOCALPROXY -p tcp --dport 80 -j ACCEPT
iptables -A FORWARD -d $LOCALPROXY -p tcp --sport 80 -j ACCEPT
iptables -A FORWARD -s $LOCALPROXY -p tcp --dport 443 -j ACCEPT
iptables -A FORWARD -d $LOCALPROXY -p tcp --sport 443 -j ACCEPT

# Allow DNS traffic (consider changing LOCALNET for LOCALPROXY)
# Group 6
iptables -A FORWARD -s $LOCALNET -p tcp --dport 53 -j ACCEPT
iptables -A FORWARD -d $LOCALNET -p tcp --sport 53 -j ACCEPT
iptables -A FORWARD -s $LOCALNET -p udp --dport 53 -j ACCEPT
iptables -A FORWARD -d $LOCALNET -p udp --sport 53 -j ACCEPT

# Masquerade local addresses
# Group 7

# Allow routing through network interfaces
# Group 8
echo 1 > /proc/sys/net/ipv4/ip_forward

The lines in group 1 have to be changed with your own settings. Next, group 2 makes the ruleset to be completely deleted. In group 3, the default policy is setup for each chain in the filter table (iptables works with the filter table if no other table is specified). Users trying to reach the outside network won't be able to get a connection and no message will be sent back due to the DROP policy in the FORWARD chain.

After that, group 4 allows any loopback interface traffic (i.e. connections to localhost). Next, in group 5, web traffic is allowed for the local proxy, so users will have to use it in order to navigate. Last filter rules, in group 6, allow dns queries for all the Intranet, but you can consider allowing it for only the local proxy depending on your security policy.

The rules in groups 6 and 7 are coupled, for one allows the traffic from the Intranet to the outside network while the other allows the traffic back to the local network.

The rule in group 7 is the only one in the nat table in this example. It makes the source IP address to be changed for the outside network interface IP address, so the local addresses are masqueraded. This kind of Network Address Translation is named source NAT or SNAT.

Finally, in group 8, the traffic is allowed to be routed between the network interfaces.

Once the shell script is executed, you should get the following output from the command iptables -nL:

Chain INPUT (policy DROP)
target     prot opt source               destination
ACCEPT     all  --  

Chain FORWARD (policy DROP)
target     prot opt source               destination
ACCEPT     tcp  --            tcp dpt:80
ACCEPT     tcp  --          tcp spt:80
ACCEPT     tcp  --            tcp dpt:443
ACCEPT     tcp  --          tcp spt:443
ACCEPT     tcp  --            tcp dpt:53
ACCEPT     tcp  --       tcp spt:53
ACCEPT     udp  --            udp dpt:53
ACCEPT     udp  --       udp spt:53

Chain OUTPUT (policy ACCEPT)
target     prot opt source               destination

Now you can try to establish some connections. Execute telnet www.google.com 80 from the local proxy:

And watch for the incoming and outgoing connexions through the firewall with the command:
watch -n 1 'sudo iptables -nvL | grep 80'

The first column is a counter of packets and the second is a counter of bytes that matched that rule.

Tuesday, April 7, 2015

Understanding how Netfilter (iptables) works

As I've read a lot of articles about Netfilter, aka iptables, and most of them are confusing and hard to read, I better explain in this article the basics of this powerful wide available firewall.

Before you continue reading, be sure you understand how communications work in TCP/IP networks such as Internet or, probably, your own Intranet.

I'm not going to bore you with the history of Netfilter and where it comes from. However, it's important to notice that this firewall is integrated in the Linux kernel, so it's available broadway with no need of extra packages to be installed. In addition, it's quick and strong enough to be used in a wide variety of scenarios.

Network packets can come from, go to or pass through your firewall. Depending on the path it follows, different rules can be applied. Rulesets are named tables and tables are associated to chains. You can create your own chains, but that's not the goal of this article.

In a packet's lifetime, it passes through different chains containing rules, which may change, accept or deny the packet somehow. A policy determines what happens to a packet. There is a default policy for each chain.

The following image shows some of the basic chains and flows:

Basic chains and flows

For a full description of filtering tables, please refer to this page.

Here are some examples of paths a packet could follow:

  • Packets from another system to the firewall: PREROUTING and INPUT.
  • Packets from the firewall to another system: OUTPUT and POSTROUTING.
  • Packets from one system to another through the firewall: PREROUTING, FORWARD and POSTROUTING.
The order of the chains is always the same. For instance, PREROUTING comes always before INPUT and not the other way round.

To interact with Netfilter you can use the command line tool iptables, though it's not the only way.

Please keep in mind that any changes made will be losed after reboot. To avoid that, you can:

  1. Save the tables state to a file with the command iptables-save and restore the rules from the file at startup with iptables-restore, or
  2. Create a shell script with direct iptables commands to be executed at startup.
I prefer the later, for it allows you to use shell variables and to add comments to make the it more readable.

Anyway, the file created one way or the other should have reading permissions for the root only and placed in a secure directory.

When reading or executing the file at startup, it has to be done before the network interfaces are configured, for security reasons. To do so, you can place the rules or a shell script that reads them in the directory /etc/init.d/if-pre-up.d if you're working with Debian.