This page is a try to show what is going on when you do filtering and network address translation.
It’s a classical example, we’ve got a Local Area Network and a DeMilitarized Zone. A single computer (Admin, 192.168.1.18) in the LAN can directly go outside internet in http. We’ve got a single smtp server (SMTP, 184.108.40.206) in our DMZ.
But, there’s a really unwanted things, we want Admin to be accessible from an outside counterpart named Maint with address 220.127.116.11 on port 22 for ssh maintenance .
Netfilter was designed with the idea to write firewalling rules as easy as write a network shema on a papersheet or speaking. By speaking, I mean sentences such as :
- “I want to authorize the access of people to my webserver through my firewall.”
- “I want to authorize the users of the LAN to connect on the web through my firewall.”
- “I accept that nice people connect to the ssh server on my firewall.”
- “I want my firewall to be able to ping all the internet.”
So a simple way to classify the packets managed by a firewall is to be split the flow in three parts :
- packets addressed to the firewall (third case)
- packets going through the firewall (first and second cases)
- packets emitted by the firewall (fourth case)
Assuming that, the simpliest way to manage authorization is to use this three categories which we name :
When a packet comes to an interface, the kernel has first to take a routing decision. The incoming packet can be addressed to the firewall (the packet is next directed through the INPUT chain) or the packet can only be routed by the firewall (packets directed through the FORWARD chain)
For the outgoing packets we’ve got two chains :
- packets coming from the firewall: the OUTPUT chain
- packets going through the firewall: the FORWARD chain
With Destination Network Address Translation
Now, we want to redirect the packet going to the port 80 of the IP of the firewall to a webserver in the DMZ. That’s called destination address translation because the firewall as to change the IP to wihc the packets are going. With such a scheme we’ve got packet going to INPUT (port 80 of the IP of the firewall), but redirect to FORWARD (they are routed to a webserver in the DMZ). So if the kernel decide the routing before the destination IP is changed, he will do a mistake because the destination chain is changed by the DNAT. So it’s necessary to modify the packet before the decision is taken at the kernel level, so we must have this scheme :
The destinations related headers of the packets have to be modified in the PREROUTING chain before the packets are routed by the kernel.
The complete scheme, adding Source Network Address Translation
At contrary, suppose we want to permit to the user in the LAN to go on webserver on the internet. The packet will have their source IP changed by the firewall. So, we will have packet coming from the FORWARD chain modified in packet coming from the firewall, thus packet that should have come from the OUTPUT chain. If the addressed is changed before these two chains, we’re going into problems because, classification will be wrong. So it’s necessary to act after this two chains. As we want to do everything we want and as we don’t want to confused the routing algorithm, we have to change the packet header after the routing occurs. Thus we have the following scheme :
So the Netfilter code act in the POSTROUTING chain to change source related headers (doing SNAT), or to do other funny things. A more complete schema is available, including quality of service stuff is available here.
Stateful filtering, the way to simplicity
Things go both way
As we just saw, Netfilter filtering wants to be as easy as speaking. When we say “I want to authorize the access of people to my webserver through my firewall”, what does really happen and what do we really want ?
First of all, when someone ask a page to the web server, we really often like that an answer reach our customer. To do so, it’s necessary to authorize the packet that our webserver send back. So the correct sentence, should be “I want to authorize the access of people to my webserver through my firewall and I want my firewall to let the response comes back”. So for every authorisations access, we’ve have to take in account the response. Things beginning to become boring ! But, what !, “for every authorisations access, we’ve have to take in account the response”, that’s the response !
The sentence “I authorize the packet coming in response of something I have authorized” does the stuff really well. Every answers enter in that criteria. But here’s the question : “Can Netfilter do this ?”. I’m sure you already know the response 😉
How does Netfilter do to know that a packet is a response to a previously authorized one ?
The kernel maintains a table that record every session . You can see all the records in the virtual file /proc/net/ip_conntrack.
When a packet comes to an interface, the Netfilter code looks at the ip header to see if it indicates that the packet is part of a known session. Depending of the case, it fix the state of the packet which can be :
- NEW if the packet is not related to any session
- ESTABLISHED if the packet is related to a session which exists at the protocol level (for example a tcp session)
- RELATED if the Netfilter code can guess that the packet is relative to something that first go through it (for example, the pong which comes after a ping).
Using that, the sentence “I authorize the packet coming in response of something I have authorized” is translated to “I authorize packet with state NEW and or ESTABLISHED to go through the firewall”.
But what is really a Netfiler rule
Let’s use on of our sentence : “I want to authorize the access of people to my webserver through my firewall.” A netfilter rules is just saying it to the kernel. So, it just a decision about packets. A generic decision is just “I want all packets which match this criteria to have the destiny I choose here”.
As we saw earlier we have many tables (or chain) we can act on (mainly INPUT, OUTPUT, FORWARD). Thus a rule has to be specific to a table. As we want to be able to use more than one rules, the kernel maintains for each table the list of rules we have added to the table. So, when a packet comes to a chain, it’s faith is decided by looking if it match the criteria of one of the rules of the chain. This is done using the simplest way possible, the kernel take the rules sequentially in the order they have been given to it. So Order does matter. If a packet match a rules is faith is done. If it is not the case, we have to decide what has to be done. This is done by following the policy of the chain which can be to :
- ACCEPT the packet is accepted.
- DROP the packet is ignored and get to the trash.
Doing the things
Rules are injected in the kernel using the iptables command.
One of the simplest things to do with iptables is to fix the policy of a chain. This is done by :
iptables -P FORWARD DROP
iptables -P INPUT DROP
iptables -P OUTPUT DROP
Using the conntrack
iptables -A FORWARD -m state --state RELATED,ESTABLISHED -j ACCEPT
iptables -A INPUT -m state --state RELATED,ESTABLISHED -j ACCEPT
iptables -A OUTPUT -m state --state RELATED,ESTABLISHED -j ACCEPT
Adding the rules
Let’s authorize access to our smtp server from everywhere :
iptables -A FORWARD -m state --state NEW -d 18.104.22.168 -p tcp --dport smtp -j ACCEPT
Let’s authorize acces to our client to the web :
iptables -A FORWARD -m state --state NEW -s 192.168.1.18 -p tcp --dport http -j ACCEPT
And the last rules access of maint to admin :
iptables -A FORWARD -m state --state NEW -s 22.214.171.124 -d 192.168.1.18 -p tcp --dport 22 -j ACCEPT
Managing address translation
It’s necessary to translate packet from our admin to the external address of the firewall (noted PUBLIC_IP).
iptables -A POSTROUTING -m state --state NEW -s 192.168.1.18 -p tcp --dport http -j SNAT --to $PUBLIC_IP
And we have to do the same job with incoming connection from Maint :
iptables -A FORWARD -m state --state NEW -s 126.96.36.199 -d $PUBLIC_IP -p tcp --dport 22 -j DNAT --to 192.168.1.18