This page content was cloned from http://ornellas.apanela.com/dokuwiki/pub:firewall_and_adv_routing for safe keeping. — Nathan Martin 2011/10/04 14:57
To present different tools and functionalities present on Linux systems to implement firewall and routers. Specific usage of each tool will not be covered by this article and it is up to the reader to learn more about them by reading the manuals.
It is assumed that the author has basic knowledge about GNU / Linux systems and TCP / IP networking (including firewall and routing).
It is recommended that the reader follow the article together with each tool´s manual in hand for reference.
The current tool for firewall under Linux is iptables. IP Tables is composed of a series of userland tools that interact with netfilter, which is Linux firewall implementation in kernel space.
IP Tables behavior is based on pairs of rules and actions. Rules define in which packets (eg. packets from a certain network) a certain action (eg. to drop the packets) will be executed. Netfilter will process all rules sequentially for each package. When it finds one that matches a certain rule, it will act by calling the defined action for that rule. Actions taken can be either terminative or not. For example, an action which asks netfilter to ignore a certain packet will be called and no other rules will be applied. This is a terminative action. However, if the action specifies to only log the existence of the package, it will do its job and let netfilter process the following rules. This is a non terminative action.
The main tool is the command iptables
, which manual can be accessed via:
# man iptables
The next sections will explain how the explained behavior works inside iptables and how to configure it.
The name iptables
comes from the fact that internally iptables works based on tables, each one specialized on a certain type of packets treatment. These are the tables existent on a Linux 2.6.8 (different kernel versions can have different tables):
Table | Meaning |
---|---|
raw | Low level alteration of packets. |
nat | Changes on packets headers (where NAT takes place). |
mangle | Used to make specialized packet alterations. |
filter | Packet filtering. |
Thus, depending on what you wish to do with a certain packet, there is an adequate table for that.
In IP Tables, there are several chains glued to each table and each one associated a certain kind of traffic. Here they are:
Chain | Meaning |
---|---|
PREROUTING | Incoming traffic to the machine (including local generated traffic destined to the machine itself) before routing takes place (can be either local or passing traffic). |
INPUT | Traffic destined to the machine itself. |
FORWARD | Matches passing traffic (remote generated, remote destination). |
OUTPUT | Locally generated traffic (either local or remote destination). |
POSTROUTING | Outgoing traffic (including locally generating traffic, destined to the machine itself). |
It is also possible to create personalized chains, topic that will not be covered on this article.
The FORWARD
chain has a special treatment inside Linux kernel. It comes with a lock that blocks traffic through this chain by default. To allow such traffic, it is needed to change the following kernel parameter:
net.ipv4.ip_forward=1
to allow IPv4 traffic (there is a similar switch to IPv6 traffic). This parameter can be changed using sysctl(8)
command:
# sysctl -w net.ipv4.ip_forward=1
This can be automated by adding the following line to sysctl.conf(5)
(usually /etc/sysctl.conf
) and should be set up at boot time:
net.ipv4.ip_forward=1
Note: Debian versions previous (and including) Sarge have a file named /etc/network/options
that can also be used to set up this parameter. This is wrong and is not present any more in future releases.
The data flow inside each table / chain inside the Linux kernel is described by the graph below. In each box, it is labeled the acting chain and the names of the valid tables for each chain. Traffic flows through each one of the tables in series for each chain. For example, at PREROUTING
chain, there are the tables raw
, mangle
e nat
. Traffic flowing through this chain will pass by each one of the three tables sequentially.
Incoming Traffic | | V +----------+ |PREROUTING| +----------+ | raw | <--------------+ | mangle | | | nat | | +----------+ | | | | | Routing | +- Decision -+ | | | | | | | V V | Local Remote | Destination Destination | | | | | | | V V | +--------+ +---------+ | | INPUT | | FORWARD | | +--------+ +---------+ | | mangle | | mangle | | | filter | | filter | | +--------+ +---------+ | | | | | | | V | | Local | | Machine | | | | | | | | V | | Routing | | Decision | | | | | | | | V | | +--------+ | | | OUTPUT | | | +--------+ | | | raw | | | | mangle | | | | nat | | | | filter | | | +--------+ | | | | | | +-------------+ | | | POSTROUTING | Local +----> +-------------+ --> Traffic | mangle | | nat | +-------------+ | | V Outgoing Traffic
Let´s take as an example an access from the machine to itself. Traffic will flow sequentially by each one of the following chains / tables:
OUTPUT
: raw
OUTPUT
: mangle
OUTPUT
: nat
OUTPUT
: filter
POSTROUTING
: mangle
POSTROUTING
: nat
PREROUTING
: raw
PREROUTING
: mangle
PREROUTING
: nat
INPUT
: mangle
INPUT
: filter
Thus, if one wish to allow such traffic, there must have no rule / action pair prohibiting the traffic in any of the above cases.
Given a certain chain / table, it is necessary to use rules to select in which packets a certain action will take place. Not all rules applies to all chains. For example, a rule specifying the outgoing interface of a packet does not apply to the chain PREROUTING
, since routing decision has not been taken at that point.
There are general rules (referred as PARAMETERS
on the manual iptables(8)
) and extensions (see MATCH EXTENSIONS
on the same manual). General rules are usually the same across different kernel versions. Match extensions depends on the running kernel and IP Tables version (remember that IP Tables is only a userland tool to interface with netfilter). It is possible to have the case where a certain rule exists in IP Tables but it's corresponding implementation is missing in the running kernel. In this case, trying to use such rule will fail.
General rules are:
Rule | Description |
---|---|
-p PROTOCOL | Specify an IP protocol (eg. TCP or UDP). |
-s ADDRESS | Specify a source address. |
-d ADDRESS | Specify a destination address. |
-i INTERFACE | Specify a local interface of incoming traffic. |
-o INTERFACE | Specify a local interface of outgoing traffic. |
Match extensions will be treated to follow.
A target specify the action to be taken when a certain packet match a certain rule. This target can be one of the default (should be available on all kernels) or one target extension (refer to TARGET EXTENSIONS
at iptables(8)
).
The default targets are:
Target | Meaning |
---|---|
ACCEPT | Allow the packet through. |
DROP | Instruct netfilter to ignore the packet. |
QUEUE | Pass the packet to userspace. |
RETURN | From the manual: “stop traversing this chain and resume at the next rule in the previous (calling) chain. If the end of a built-in chain is reached or a rule in a built-in chain with target RETURN is matched, the target specified by the chain policy determines the fate of the packet.” |
In practice, most times you will be using either ACCEPT
or DROP
.
According to iptables(8)
, there are many ways to add / remove rules. All add rule actions, are discrete operation. This suggests the creation of a shell script containing all iptables(8)
calls that compose the whole firewall. Remember to clean all pre-existing rules at the beginning of the script.
To add a single rule (at the end of the existing ones), follow this general form:
# iptables -t [TABLE] -A [CHAIN] [RULES] -j [TARGET]
Where:
Variable | Meaning |
---|---|
TABLE | Specify the table. |
CHAIN | Specify the chain. |
RULES | Packet selection rules. |
TARGET | Action to take place. |
For example, to allow the network 192.168.0.0/24 connected through the network interface named eth0 to send traffic to the network 192.168.1.0/24, connected to the network interface eth1, one can write the following rule:
# iptables -t filter -A FORWARD -s 192.168.0.0/24 -d 192.168.1.0/24 -i eth0 -o eth1 -j ACCEPT
Follow the data flow on the above data flow graph to note that this rule would not be enough, since the packet flows through many other table / chains.
In the case that no rule matches a certain traffic, there is a way to set up a default policy to a certain table / chain. This default policy configure a target to be executed in the case no other terminative rule on the same target / table match the traffic. To define a default policy try:
# iptables -t [TABLE] -P [CHAIN] [TARGET]
For example:
# iptables -t raw -P OUTPUT ACCEPT
This rule will allow free traffic by default on the table raw, chain OUTPUT
Up to here, resources to control traffic are limited. The neTABELAxt topic will cover some extensions that give much more control / power over the firewall.
As said previously, there are match extensions that allow to better select packets. The general form is:
... -m [NAME] [OPTIONS]
where
Variable | Meaning |
---|---|
NAME | Name of the match extension in use. |
OPTIONS | Match extension options. |
Please reffer to the topic MATCH EXTENSIONS
of iptables(8)
to get more information on available match extensions and its options.
Here are presented some common match extensions, that will be used most of the times.
Allow to specify a certain source / destination TCP port:
... -m tcp --dport www
This will match TCP connections which destination port is www (number 80, please reffer to /etc/services
).
Allow to specify a certain source / destination UDP port:
... -m udp --dport www
IP Tables is a so called stateful firewall. This means it is possible to match all packets related to a certain connection, having no need to make a rule to allow packets going and another to allow packets coming from the same connection. This is direct for TCP connections, but there are extensions to allow UDP traffic windows, thus defining an “UDP connection”. Example:
# iptables -t filter -A FORWARD -d [WEB_SERVER] -i [EXTERNAL_INTERFACE] -o [INTERNAL_INTERFACE] -j ACCEPT # iptables -t filter -A FORWARD -m state --state RELATED,ESTABLISHED -j ACCEPT
The first rule allows external traffic coming via EXTERNAL_INTERFACE
to reach WEB_SERVER
on INTERNAL_INTERFACE
. The second rule says that any forwarding traffic related to an existing connection, will be allowed (eg. traffic coming from WEB_SERVER
to EXTERNAL_INTERFACE
). The match extension state
tracks the connections, and matches the respective traffic. Note that in this case, all forwarding traffic related to a pre-existing connection will be allowed, not only the traffic from the first rule.
The connection tracking system from netfilter has many different modules, being able to track specific protocols, such as FTP, to allow related (data) connections to work with no additional rule or the need of a proxy server. In the case where no connection is defined (eg. UDP or ICMP) after the traffic goes, a window is opened to allow its return. Thus, everything works like if there was actually an “UDP connection”.
It is recommended to let a rule similar to the above second one. It is better to have a generic rule “catch all” than writing an specific connection rule for each traffic. Since this rule will not allow traffic if it was not allowed before, it is not security problem to do this. It is also recommended to allow ICMP traffic to flow freely.
Please refer to iptables manual to find out more about connection tracking and more uses of this tool.
Target extensions allow some specialized actions and packet treatment to be done, such as NAT or load balancing. Please refer to iptables manual to know more about available target extensions. Here are some important ones:
This one allow to log (via kernel logging, you can see using the command dmesg
) any message you wish when a certain packet matches. A good rule is to include a logging rule right before any rejected traffic. This way, finding out problems with your firewall is easy, as you can see what and when it is dropping something. This target accepts an option –log-prefix
, that allow to prefix a text to each log message.
Those are three possibilities of implementing NAT (RFC1631). This way, it is possible to integrate a private network (RFC1918) to the internet with no problems. Please refer to the manual on the use of each target.
Linux has a different approach for routing than other UNIX. The way things are implemented on Linux is more flexible and powerful than traditional ways. Legacy utilities such as ifconfig
and route
are still valid, but incomplete. This is because they do not give access to the advanced routing layer present on Linux. The utility ip
(part of iproute2
) is the current tool for networking related stuff under Linux. This tool will be the focus of this section.
Linux advanced routing is implemented in two parts:
It is possible to have multiple routing tables under Linux and the rules indicate when to use each one of them.
To list existing routing rules, one can try:
# ip rule list 0: from all lookup local 32766: from all lookup main 32767: from all lookup default
Rules are processed in increasing priority (1st) column). In the above case, priority 0 rule instruct the kernel to look for a routing decision inside table named local
for traffic comming from any origin. The content of this table can be accessed via:
# ip route list table local local 192.168.1.1 dev eth3 proto kernel scope host src 192.168.1.1 local 200.170.111.237 dev eth0 proto kernel scope host src 200.170.111.233 broadcast 192.168.1.0 dev eth3 proto kernel scope link src 192.168.1.1 broadcast 192.168.2.255 dev eth1 proto kernel scope link src 192.168.2.1 local 200.170.111.236 dev eth0 proto kernel scope host src 200.170.111.233 broadcast 127.255.255.255 dev lo proto kernel scope link src 127.0.0.1 broadcast 200.170.111.239 dev eth0 proto kernel scope link src 200.170.111.233 local 200.170.111.239 dev eth0 proto kernel scope host src 200.170.111.233 local 200.170.111.238 dev eth0 proto kernel scope host src 200.170.111.233 local 200.170.111.233 dev eth0 proto kernel scope host src 200.170.111.233 broadcast 200.170.111.232 dev eth0 proto kernel scope link src 200.170.111.233 local 200.170.111.235 dev eth0 proto kernel scope host src 200.170.111.233 local 201.20.202.11 dev eth0 proto kernel scope host src 201.20.202.11 broadcast 201.6.149.0 dev eth2 proto kernel scope link src 201.6.149.41 local 200.170.111.234 dev eth0 proto kernel scope host src 200.170.111.233 local 192.168.2.1 dev eth1 proto kernel scope host src 192.168.2.1 broadcast 192.168.1.255 dev eth3 proto kernel scope link src 192.168.1.1 broadcast 192.168.2.0 dev eth1 proto kernel scope link src 192.168.2.1 broadcast 201.20.207.255 dev eth0 proto kernel scope link src 201.20.202.11 broadcast 201.20.200.0 dev eth0 proto kernel scope link src 201.20.202.11 broadcast 127.0.0.0 dev lo proto kernel scope link src 127.0.0.1 broadcast 201.6.149.255 dev eth2 proto kernel scope link src 201.6.149.41 local 201.6.149.41 dev eth2 proto kernel scope host src 201.6.149.41 local 127.0.0.1 dev lo proto kernel scope host src 127.0.0.1 local 127.0.0.0/8 dev lo proto kernel scope host src 127.0.0.1
This table is long, but is managed by the kernel itself, thus, do not worry about it. This table contains information regarding local addresses (the ones associated with network interfaces), informing that they are resolved locally.
In the case the table does not solve a certain destination address (eg. traffic to another machine in this case), Linux kernel look for the next rule with lower priority (higher priority number) to find out a routing solution. In this case, the next rule is the one with priority 32766, which says to go to table main
:
# ip route list table main 200.170.111.232/29 dev eth0 proto kernel scope link src 200.170.111.233 192.168.2.0/24 dev eth1 proto kernel scope link src 192.168.2.1 192.168.1.0/24 dev eth3 proto kernel scope link src 192.168.1.1 201.6.149.0/24 dev eth2 proto kernel scope link src 201.6.149.41 201.20.200.0/21 dev eth0 proto kernel scope link src 201.20.202.11 default via 201.6.149.1 dev eth2
This table contain exactly the same information displayed by the route
command:
# route -n Kernel IP routing table Destination Gateway Genmask Flags Metric Ref Use Iface 200.170.111.232 0.0.0.0 255.255.255.248 U 0 0 0 eth0 192.168.2.0 0.0.0.0 255.255.255.0 U 0 0 0 eth1 192.168.1.0 0.0.0.0 255.255.255.0 U 0 0 0 eth3 201.6.149.0 0.0.0.0 255.255.255.0 U 0 0 0 eth2 201.20.200.0 0.0.0.0 255.255.248.0 U 0 0 0 eth0 0.0.0.0 201.6.149.1 0.0.0.0 UG 0 0 0 eth2
So, this table contains the traditional routing information. The last table named default
comes empty (try it).
The rules and tables described here contain all the traditional routing information. It is possible however to insert new rules and new tables to fit demands of a more complex environment.
One can for example, add a rule specifying that traffic from a certain source must be routed using a specific routing table:
# ip rule add type unicast from 192.168.4.0/24 priority 55 table 55
This will instruct the kernel to when it finds traffic coming from 192.168.4.0/24 network, look for a routing solution inside table number 55, with priority 55. The table 55 must be created containing routing information regarding only to the source network in question. In this case:
# ip rule list 0: from all lookup local 55: from 192.168.4.0/24 lookup 55 32766: from all lookup main 32767: from all lookup default
Note that priority number 55 and table number 55 are absolutely arbitrary and have no need to be the same number. These numbers must only make sense to fit your routing needs.
Please look at ip
manual to obtain more information on how to add or remove rules and all possible parameters.
Routing tables are identified by numbers from 0 to 255. Tables local
and main
have the numbers 255 and 254 associated with it. There is a configuration file that one can associate a name to each table number (as for local
and main
) that will not be covered by this article.
The existence of a table containing routing rules inside does not imply that it is in use. A table will only be used if there is a rule pointing to it.
Please consult ip
manual on how to add routing rules inside tables. It should not be a problem, since its operation is similar to classic routing utility route
.
Linux kernel comes with an anti-spoof protection apart from firewall. This protection protects an internal network against external attacks. Depending on the routing situation you need (specially when connected to multiple ISPs) this setting must be disable in order for your routing rules to work.
This option is accessible vis sysctl
net.ipv4.conf.all.rp_filter
which changes configuration for all network interfaces. There is also the possibility to enable / disable by interface (substituting .all
by .eth0
for example, to change only interface eth0).
The line:
net.ipv4.conf.all.rp_filter=0
at /etc/sysctl.conf
, should disable this feature at boot time. Please refer to distribution specific settings.
One possible routing rule matching filter is fwmark
. This option makes packets marked by the firewall to be matched and fall into desired table.
# ip rule list 0: from all lookup local 33: from all fwmark 0x1 lookup 33 32766: from all lookup main 32767: from all lookup default
In this case, packets marked as 0x1 by the firewall (0x1 is 1 noted in hexa-decimal) to be solved by the table numbered 33. The number 0x1 is absolutely arbitrary.
It is important to know that this mark is local and internal to the Linux kernel and in any way alters the packets.
To mark a packet with iptables, use the target MARK
:
# iptables -t mangle -A PREROUTING -i eth0 -j MARK --set-mark 1
In this example, all traffic coming through interface eth0 will be marked as 1 (an arbitrary number). After this, it is possible to include a routing rule matching these packets, as explained on the previous topic. Thus, this iptables rule and the routing rule will make specific routing for packets coming by interface eth0.
Note by the above iptables fluxogram that the mark is being done before routing table. In this case, traffic is entering the machine, thus the PREROUTING
. In the case you wish to mark traffic generated locally, you should use the OUTPUT
chain.
It is also possible to mark all packets related to a connection. The target CONNMARK
allows marking of a connection (referred as connection mark, internal to the firewall), that is not the same marking seen by routing (referred as netfilter mark, accessible by routing rules), do not confuse them. Thus, one can make the following to mark packets of a connection to the routing rules:
# iptables -t mangle -A OUTPUT -d SERVER -j CONNMARK --set-mark 1 # iptables -t mangle -A OUTPUT -m connmark --mark 1 -j MARK --set-mark 1
The first rule mark all desired connections. The second rule uses the match extension -m connmark
to identify those connection marks and create netfilter marks by using the target MARK
(to be saved by routing rules).