7.4. Firewall or Packet Filtering
A firewall is a piece of computer equipment with hardware, software, or both that parses the incoming or outgoing network packets (coming to or leaving from a local network) and only lets through those matching certain predefined conditions.
A filtering network gateway is a type of firewall that protects an entire network. It is usually installed on a dedicated machine configured as a gateway for the network so that it can parse all packets that pass in and out of the network. Alternatively, a local firewall is a software service that runs on one particular machine in order to filter or limit access to some services on that machine, or possibly to prevent outgoing connections by rogue software that a user could, willingly or not, have installed.
The Linux kernel embeds the netfilter firewall. There is no turn-key solution for configuring any firewall since network and user requirements differ. However, you can control netfilter from user space with the iptables and ip6tables commands. The difference between these two commands is that the former works for IPv4 networks, whereas the latter works on IPv6. Since both network protocol stacks will probably be around for many years, both tools will need to be used in parallel. You can also use the excellent GUI-based fwbuilder tool, which provides a graphical representation of the filtering rules.
However you decide to configure it, netfilter is Linux's firewall implementation, so let's take a closer look at how it works.
Netfilter uses four distinct tables, which store rules regulating three kinds of operations on packets:
- filter concerns filtering rules (accepting, refusing, or ignoring a packet);
- nat (Network Address Translation) concerns translation of source or destination addresses and ports of packets;
- mangle concerns other changes to the IP packets (including the ToS—Type of Service—field and options);
- raw allows other manual modifications on packets before they reach the connection tracking system.
Each table contains lists of rules called chains. The firewall uses standard chains to handle packets based on predefined circumstances. The administrator can create other chains, which will only be used when referred by one of the standard chains (either directly or indirectly).
The filter table has three standard chains:
- INPUT: concerns packets whose destination is the firewall itself;
- OUTPUT: concerns packets emitted by the firewall;
- FORWARD: concerns packets passing through the firewall (which is neither their source nor their destination).
The nat table also has three standard chains:
- PREROUTING: to modify packets as soon as they arrive;
- POSTROUTING: to modify packets when they are ready to go on their way;
- OUTPUT: to modify packets generated by the firewall itself.
These chains are illustrated in Figure 7.1, "How Netfilter Chains are Called".
Figure 7.1. How Netfilter Chains are Called
Each chain is a list of rules; each rule is a set of conditions and an action to perform when the conditions are met. When processing a packet, the firewall scans the appropriate chain, one rule after another, and when the conditions for one rule are met, it jumps (hence the -j option in the commands with Section 18.104.22.168, “Rules”) to the specified action to continue processing. The most common behaviors are standardized and dedicated actions exist for them. Taking one of these standard actions interrupts the processing of the chain, since the packets fate is already sealed (barring an exception mentioned below). Listed below are the Netfilter actions.
- ACCEPT: allow the packet to go on its way.
- REJECT: reject the packet with an Internet control message protocol (ICMP) error packet (the --reject-with type option of iptables determines the type of error to send).
- DROP: delete (ignore) the packet.
- LOG: log (via syslogd) a message with a description of the packet. Note that this action does not interrupt processing, and the execution of the chain continues at the next rule, which is why logging refused packets requires both a LOG and a REJECT/DROP rule. Common parameters associated with logging include:
- --log-level, with default value warning, indicates the syslog severity level.
- --log-prefix allows specifying a text prefix to differentiate between logged messages.
- --log-tcp-sequence, --log-tcp-options, and --log-ip-options indicate extra data to be integrated into the message: respectively, the TCP sequence number, TCP options, and IP options.
- ULOG: log a message via ulogd, which can be better adapted and more efficient than syslogd for handling large numbers of messages; note that this action, like LOG, also returns processing to the next rule in the calling chain.
- chain_name: jump to the given chain and evaluate its rules.
- RETURN: interrupt processing of the current chain and return to the calling chain; in case the current chain is a standard one, there's no calling chain, so the default action (defined with the -P option to iptables) is executed instead.
- SNAT (only in the nat table): apply Source Network Address Translation (SNAT). Extra options describe the exact changes to apply, including the --to-source address:port option, which defines the new source IP address and/or port.
- DNAT (only in the nat table): apply Destination Network Address Translation (DNAT). Extra options describe the exact changes to apply, including the --to-destination address:port option, which defines the new destination IP address and/or port.
- MASQUERADE (only in the nat table): apply masquerading (a special case of Source NAT).
- REDIRECT (only in the nat table): transparently redirect a packet to a given port of the firewall itself; this can be used to set up a transparent web proxy that works with no configuration on the client side, since the client thinks it connects to the recipient whereas the communications actually go through the proxy. The --to-ports port(s) option indicates the port, or port range, where the packets should be redirected.
Other actions, particularly those concerning the mangle table, are outside the scope of this text. The iptables(8) and ip6tables(8) manual pages have a comprehensive list.
The iptables and ip6tables commands are used to manipulate tables, chains, and rules. Their -t table option indicates which table to operate on (by default, filter).
The major options for interacting with chains are listed below:
- -L chain lists the rules in the chain. This is commonly used with the -n option to disable name resolution (for example, iptables -n -L INPUT will display the rules related to incoming packets).
- -N chain creates a new chain. You can create new chains for a number of purposes, including testing a new network service or fending off a network attack.
- -X chain deletes an empty and unused chain (for example, iptables -X ddos-attack).
- -A chain rule adds a rule at the end of the given chain. Remember that rules are processed from top to bottom so be sure to keep this in mind when adding rules.
- -I chain rule_num rule inserts a rule before the rule number rule_num. As with the -A option, keep the processing order in mind when inserting new rules into a chain.
- -D chain rule_num (or -D chain rule) deletes a rule in a chain; the first syntax identifies the rule to be deleted by its number (iptables -L --line-numbers will display these numbers), while the latter identifies it by its contents.
- -F chain flushes a chain (deletes all its rules). For example, to delete all of the rules related to outgoing packets, you would run iptables -F OUTPUT. If no chain is mentioned, all the rules in the table are deleted.
- -P chain action defines the default action, or "policy" for a given chain; note that only standard chains can have such a policy. To drop all incoming traffic by default, you would run iptables -P INPUT DROP.
Each rule is expressed as conditions -j action action_options. If several conditions are described in the same rule, then the criterion is the conjunction (logical AND) of the conditions, which is at least as restrictive as each individual condition.
The -p protocol condition matches the protocol field of the IP packet. The most common values are tcp, udp, icmp, and icmpv6. This condition can be complemented with conditions on the TCP ports, with clauses such as --source-port port and --destination-port port.
The -s address or -s network/mask condition matches the source address of the packet. Correspondingly, -d address or -d network/mask matches the destination address.
The -i interface condition selects packets coming from the given network interface. -o interface selects packets going out on a specific interface.
The --state state condition matches the state of a packet in a connection (this requires the ipt_conntrack kernel module, for connection tracking). The NEW state describes a packet starting a new connection, ESTABLISHED matches packets belonging to an already existing connection, and RELATED matches packets initiating a new connection related to an existing one (which is useful for the ftp-data connections in the “active” mode of the FTP protocol).
There are many available options for iptables and ip6tables and mastering them all requires a great deal of study and experience. However, one of the options you will use most often is the one to block malicious network traffic from a host or range of hosts. For example, to silently block incoming traffic from the IP address 10.0.1.5 and the 22.214.171.124/24 class C subnet:
# iptables -A INPUT -s 10.0.1.5 -j DROP # iptables -A INPUT -s 126.96.36.199/24 -j DROP # iptables -n -L INPUT Chain INPUT (policy ACCEPT) target prot opt source destination DROP all -- 10.0.1.5 0.0.0.0/0 DROP all -- 188.8.131.52/24 0.0.0.0/0
Another commonly-used iptables command is to permit network traffic for a specific service or port. To allow users to connect to SSH, HTTP, and IMAP, you could run the following commands:
# iptables -A INPUT -m state --state NEW -p tcp --dport 22 -j ACCEPT # iptables -A INPUT -m state --state NEW -p tcp --dport 80 -j ACCEPT # iptables -A INPUT -m state --state NEW -p tcp --dport 143 -j ACCEPT # iptables -n -L INPUT Chain INPUT (policy ACCEPT) target prot opt source destination DROP all -- 10.0.1.5 0.0.0.0/0 DROP all -- 184.108.40.206/24 0.0.0.0/0 ACCEPT tcp -- 0.0.0.0/0 0.0.0.0/0 state NEW tcp dpt:22 ACCEPT tcp -- 0.0.0.0/0 0.0.0.0/0 state NEW tcp dpt:80 ACCEPT tcp -- 0.0.0.0/0 0.0.0.0/0 state NEW tcp dpt:143
It is considered to be good computer hygiene to clean up old and unnecessary rules. The easiest way to delete iptables rules is to reference the rules by line number, which you can retrieve with the --line-numbers option. Be wary though: dropping a rule will renumber all the rules appearing further down in the chain.
# iptables -n -L INPUT --line-numbers Chain INPUT (policy ACCEPT) num target prot opt source destination 1 DROP all -- 10.0.1.5 0.0.0.0/0 2 DROP all -- 220.127.116.11/24 0.0.0.0/0 3 ACCEPT tcp -- 0.0.0.0/0 0.0.0.0/0 state NEW tcp dpt:22 4 ACCEPT tcp -- 0.0.0.0/0 0.0.0.0/0 state NEW tcp dpt:80 5 ACCEPT tcp -- 0.0.0.0/0 0.0.0.0/0 state NEW tcp dpt:143 # iptables -D INPUT 2 # iptables -D INPUT 1 # iptables -n -L INPUT --line-numbers Chain INPUT (policy ACCEPT) num target prot opt source destination 1 ACCEPT tcp -- 0.0.0.0/0 0.0.0.0/0 state NEW tcp dpt:22 2 ACCEPT tcp -- 0.0.0.0/0 0.0.0.0/0 state NEW tcp dpt:80 3 ACCEPT tcp -- 0.0.0.0/0 0.0.0.0/0 state NEW tcp dpt:143
There are more specific conditions, depending on the generic conditions described above. For more information refer to manual pages for iptables(8) and ip6tables(8)
Each rule creation requires one invocation of iptables or ip6tables. Typing these commands manually can be tedious, so the calls are usually stored in a script so that the system is automatically configured the same way every time the machine boots. This script can be written by hand but it can also be interesting to prepare it with a high-level tool such as fwbuilder.
# apt install fwbuilder
The principle is simple. In the first step, describe all the elements that will be involved in the actual rules:
- The firewall itself, with its network interfaces
- The networks, with their corresponding IP ranges
- The servers
- The ports belonging to the services hosted on the servers
Next, create the rules with simple drag-and-drop actions on the objects as shown in Figure 7.2, "Fwbuilder's Main Window". A few contextual menus can change the condition (negating it, for instance). Then the action needs to be chosen and configured.
As far as IPv6 is concerned, you can either create two distinct rulesets for IPv4 and IPv6, or create only one and let fwbuilder translate the rules according to the addresses assigned to the objects.
Figure 7.2. Fwbuilder's Main Window
fwbuilder will generate a script configuring the firewall according to the rules that you have defined. Its modular architecture gives it the ability to generate scripts targeting different systems including iptables for Linux, ipf for FreeBSD, and pf for OpenBSD.
In order to implement the firewall rules each time the machine is booted, you will need to register the configuration script in an up directive of the /etc/network/interfaces file. In the following example, the script is stored under /usr/local/etc/arrakis.fw (arrakis being the hostname of the machine).
auto eth0 iface eth0 inet static address 192.168.0.1 network 192.168.0.0 netmask 255.255.255.0 broadcast 192.168.0.255 up /usr/local/etc/arrakis.fw
This example assumes that you are using ifupdown to configure the network interfaces. If you are using something else (like NetworkManager or systemd-networkd), then refer to their respective documentation to find out ways to execute a script after the interface has been brought up.