When designing a network, it’s often desirable to define policies governing how and where certain vital network services can be accessed. The firewall is a key technology that is instrumental in enforcing these policies and can allow network administrators to delineate trust relationships between networks and hosts with a fine grain of detail.
By instituting a firewall, you can prevent unauthorized access to services at the network level before an attacker is given the chance to attempt to exploit them. You can use a firewall not only to limit what information flows into a network, but also to prevent the egress of information. Doing so aids in preventing worm propagation and helps stop important confidential information from leaving an enterprise. Additionally, firewall logs can be excellent tools to help you understand where the threats to your network originate.
A variety of firewalls are available today. In addition to the many firewall appliances that are available, Linux, BSD, and Windows all include some form of firewalling support. This chapter shows how to set up firewalls with Linux, FreeBSD, OpenBSD, and Windows, as well as how to test your firewall rulesets. You’ll also see how to perform MAC address filtering and how to create a gateway that will authenticate machines based on login credentials. Finally, you’ll learn a few additional tricks to keep certain types of traffic from exiting your network.
Protect your network with Linux’s powerful firewalling features.
Linux
has long had the capability for filtering packets, and it has come a long way since the early days in terms of both power and flexibility. The first generation of packet-filtering code, called ipfw (for “IP firewall”), provided basic filtering capability. Since it was somewhat inflexible and inefficient for complex configurations, ipfw is rarely used now. The second generation of IP filtering was called IP chains. It improved greatly on ipfw and is still in common use. The latest generation of filtering, called Netfilter, is manipulated with the iptables
command and used exclusively with the 2.4.x and later series of kernels. Although Netfilter is the kernel component and iptables is the user-space configuration tool, these terms are often used interchangeably.
An important concept in Netfilter is the chain
, which consists of a list of rules that are applied to packets as they enter, leave, or traverse the system. The kernel defines three chains by default, but new chains of rules can be specified and linked to the predefined chains. The INPUT
chain applies to packets that are received by and destined for the local system, and the OUTPUT
chain applies to packets that are transmitted by the local system. Finally, the FORWARD
chain applies whenever a packet will be routed from one network interface to another through the system. It is used whenever the system is acting as a router or gateway, and it applies to packets that are neither originating from nor destined for the local system.
The iptables
command makes changes to the Netfilter chains and rulesets. Using iptables
, you can create new chains, delete chains, list the rules in a chain, flush chains (i.e., remove all rules from a chain), and set the default action for a chain. iptables
also allows you to insert, append, delete, and replace rules in a chain.
Before
we get started with some example rules, it’s important to set a default behavior for all the chains. To do this, use the -P
(which stands for “policy”) command-line switch:
#iptables -P INPUT DROP
#iptables -P FORWARD DROP
This ensures that only those packets covered by subsequent rules that you specify will make it past your firewall. After all, with the relatively small number of services that your network will likely provide, it is far easier to explicitly specify all the types of traffic that you want to allow than it is to specify all the traffic that you don’t.
Note that you don’t specify a default policy for the OUTPUT
chain. This is because you’ll want to allow traffic to proceed out of the firewall itself in a normal manner.
With the default policy set to DROP
, you’ll specify what is actually allowed. Here’s where you’ll need to figure out what services will have to be accessible to the outside world. For the rest of these examples, assume that eth0
is the external interface on the firewall and eth1
is the internal one. The sample network will contain a web server (192.168.1.20), a mail server (192.168.1.21), and a DNS server (192.168.1.18)—a fairly minimal setup for a self-managed Internet presence.
We’ll begin specifying rules momentarily, but first, remove filtering from the loopback interface:
#iptables -A INPUT -i lo -j ACCEPT
#iptables -A OUTPUT -o lo -j ACCEPT
Now, let’s construct some rules to allow this traffic through. First, make a rule to allow traffic on TCP port 80—the standard port for web servers—to pass to the web server unfettered by the firewall:
#iptables -A FORWARD -m state --state NEW -p tcp \
-d 192.168.1.20 --dport 80 -j ACCEPT
And now for the mail server, which uses TCP port 25 for SMTP:
#iptables -A FORWARD -m state --state NEW -p tcp \
-d 192.168.1.21 --dport 25 -j ACCEPT
You might also want to allow remote POP3, IMAP, and IMAP+SSL access:
Finally, allow DNS access via port 53:
#iptables -A FORWARD -m state --state NEW -p tcp \
-d 192.168.1.21 --dport 53 -j ACCEPT
Unlike the other services, DNS can use both TCP and UDP port 53. Using a default deny policy makes it slightly more difficult to use UDP for DNS. This is because the policy relies on the use of state-tracking rules, and since UDP is a stateless protocol, there is no way to track it. In this case, you can configure the DNS server either to use only TCP, or to use a UDP source port of 53 for any response that it sends back to clients that were using UDP to query the name server.
If the DNS server is configured to respond to clients using UDP port 53, you can allow this traffic through with the following two rules:
#iptables -A FORWARD -p udp -d 192.168.1.18 --dport 53 -j ACCEPT
#iptables -A FORWARD -p udp -s 192.168.1.18 --sport 53 -j ACCEPT
The first rule allows traffic destined for the DNS server into your network, and the second rule allows responses from the DNS server to leave the network.
You might be wondering what the -m state
and --state
arguments are about. These two options allow us to use Netfilter’s stateful packet-inspection engine. Using these options tells Netfilter that you want to allow only new connections to the destination IP and port pairs that you have specified. When these rules are in place, the triggering packet is accepted and its information is entered into a state table.
Now, you can specify that you want to allow any outbound traffic that is associated with these connections by adding a rule like this:
# iptables -A FORWARD -m state --state ESTABLISHED,RELATED -j ACCEPT
The only thing left now is to allow traffic from machines behind the firewall to reach the outside world. To do this, use a rule like the following:
# iptables -A FORWARD -m state --state NEW -i eth1 -j ACCEPT
This rule enters any outbound connections from the internal network into the state table. It works by matching packets coming into the internal interface of the firewall that are creating new connections. If you were setting up a firewall that had multiple internal interfaces, you could have used a Boolean NOT
operator on the external interface (e.g., -i ! eth0
). Now, any traffic that comes into the firewall through the external interface that corresponds to an outbound connection will be accepted by the preceding rule, because this rule will have put the corresponding connection into the state table.
In
these examples, the order in which the rules were entered does not really matter. Since you’re operating with a default DENY
policy, all your rules have an ACCEPT
target. However, if you had specified targets of DROP
or REJECT
as arguments to the -j
option, you would have had to take a little extra care to ensure that the order of those rules would result in the desired effect. Remember that the first rule that matches a packet is always triggered as the rule chains are traversed, so rule order can sometimes be critically important.
It should also be noted that rule order can have a performance impact in some circumstances. For example, the rule shown earlier that matches ESTABLISHED
and RELATED
states should be specified before any of the other rules, since that particular rule will be matched far more often than any of the rules that will match only on new connections. Putting that rule first will prevent any packets that are already associated with a connection from having to traverse the rest of the rule chain before finding a match.
To complete the firewall configuration, you’ll want to enable packet forwarding. Run this command:
# echo 1 > /proc/sys/net/ipv4/ip_forward
This tells the kernel to forward packets between interfaces whenever appropriate. To have this done automatically at boot time, add the following line to /etc/sysctl.conf :
net.ipv4.ip_forward=1
If your system doesn’t support /etc/sysctl.conf, you can put the preceding echo
command in one of your startup rc scripts, such as /etc/rc.local.
Another useful kernel parameter is rp_filter
, which helps prevent IP spoofing. Running the following command enables source address verification by checking that the IP address for any given packet has arrived on the expected network interface:
# echo 1 > /proc/sys/net/ipv4/conf/default/rp_filter
You can also enable source address verification by editing /etc/sysctl.conf on systems that support it, or else putting the changes in your rc.local. To enable rp_filter
in your sysctl.conf, add the following line:
net.ipv4.conf.all.rp_filter=1
To save all of the rules, either write them to a shell script or use your Linux distribution’s particular way of saving them. Do this in Red Hat by running the following command:
# /sbin/service iptables save
This saves all currently active filter rules to /etc/sysconfig/iptables. To achieve the same effect under Debian, edit /etc/default/iptables and set enable_iptables_initd=true
.
After doing this, run the following command:
# /etc/init.d/iptables save_active
When the machine reboots, your iptables configuration will automatically be restored.