home | O'Reilly's CD bookshelfs | FreeBSD | Linux | Cisco | Cisco Exam  


Running Linux, 4th Ed.Running Linux, 4th Ed.Search this book

17.4. Firewalls: Filtering IP Packets

While TCP wrappers can be used to restrict the set of hosts that can establish connections to certain services on a machine, in many cases it is desirable to exert finer-grained control over the packets that can enter (or leave!) a given system. It's also the case that TCP wrappers only work with services configured using inetd or xinetd; some services (such as sshd on some systems) are "standalone" and provide their own access control features. Still other services don't implement any access control themselves, so it's necessary to provide another level of protection if we wish to control the connections made to these services.

Today it is commonplace for Internet users to protect themselves against the threat of network-based attacks using a technique called IP filtering. IP filtering involves having the kernel inspect each network packet that is transmitted or received and deciding whether to allow it to pass, to throw it away, or to modify it in some way before allowing it through. IP filtering is often called "firewalling," because by carefully filtering packets entering or leaving a machine you are building a "firewall" between the system and the rest of the Internet. IP filtering won't protect you against virus and Trojan Horse attacks or application defects, but it can protect you against many forms of network-based attacks, such as certain types of DoS attacks and IP spoofing (packets that are marked as coming from a system they don't really come from). IP filtering also provides an additional layer of access control that prevents unwanted users from trying to gain access to your system.

To make IP filtering work, we need to know which packets to allow and which to deny. Usually, the decision to filter a packet is based on the packet headers, which contain information such as the source and destination IP addresses, the protocol type (TCP, UDP, and so on), and the source and destination port numbers (which identify the particular service for which the packet is destined). Different network services use different protocols and port numbers; for example, most web servers receive requests on TCP port 80. If we wanted to filter out all incoming HTTP traffic from our system, we'd set up an IP filter that rejects all TCP packets destined for port 80.

Sometimes inspecting just the header of a packet is not sufficient to accomplish a particular filtering task, so we need to inspect and interpret the actual data carried within the packet. This technique is sometimes called "stateful inspection" because a packet is considered in the context of an ongoing network connection rather than in isolation. For example, we might want to allow users inside our network to use FTP servers outside our network. FTP is a complex protocol that uses one TCP connection to send commands to the server, but another to transfer the actual data. Unfortunately the FTP specification does not mandate a particular port number for data transfers, so the client and server must negotiate port numbers using the command session. Without stateful packet inspection, allowing FTP transfers would require allowing TCP connections to arbitrary ports. Stateful inspection solves this problem by interpreting the port number negotiation between the client and server, and automatically allowing TCP packets on the negotiated port to pass through.

IP filtering is implemented by the Linux kernel, which contains code to inspect each packet that is received and transmitted, applying filtering rules that determine the fate of the packet. The rules are configured using a user-space configuration tool that accepts arguments from the command line and translates them into filter specifications that are stored and used as rules by the kernel.

There are three generations of kernel-based IP filtering in Linux, and each has had its own configuration mechanism. The first generation was called ipfw (for "IP firewall"), and provided basic filtering capability but was somewhat inflexible and inefficient for complex configurations. ipfw is rarely used now. The second generation of IP filtering, called IP chains, improved greatly on ipfw, and is still in common use. The latest generation of filtering is called netfilter/iptables. netfilter is the kernel component and iptables is the user-space configuration tool; these terms are often used interchangeably. netfilter is not only much more flexible to configure, but is extensible as well. In the following sections we'll describe netfilter and some simple configurations as examples.

17.4.1. netfilter Basics

netfilter is implemented in Linux kernels 2.4.0 and newer. The primary tool for manipulating and displaying the filtering tables is called iptables and is included in all current Linux distributions. The iptables command allows configuration of a rich and complex set of firewall rules and hence has a large number of command-line options. We'll address the most common of these here. The iptables manpage offers a complete explanation.

Just to whet your appetite, take a look at a sneak preview of where we're heading:

iptables -A INPUT -m state --state NEW -m tcp -p tcp --dport 22 -j ACCEPT

This command installs an IP filtering rule that accepts new incoming connections to TCP port 22 (the ssh service) on our local system. It also uses an extension module called state to perform connection tracking. On the following pages we'll explain how all this works.

An important concept in netfilter is the notion of a chain, which consists of a list of rules that are applied to packets as they enter, leave, or traverse through the system. The kernel defines three chains by default, but the administrator can specify new chains of rules and link them to the predefined chains. The three predefined chains are:

INPUT
This chain applies to packets that are received and are destined for the local system.

OUTPUT
This chain applies to packets that are transmitted by the local system.

FORWARD
This chain applies whenever a packet will be routed from one network interface to another through this system. It is used whenever the system is acting as a packet router or gateway, and applies to packets that are neither originating from nor destined to this system.

Each rule in a chain provides a set of criteria that specify which packets match the rule, and an action that should be taken on packets that match. Actions that can be taken on a packet include accepting the packet (allowing it to be either received or transmitted), dropping the packet (simply refusing to receive or transmit it), or passing the packet onto another chain. (The latter is useful when building user-defined chains, which allow complex packet-filtering rules to be built up hierarchically.) A packet traverses each rule in the chain until it is accepted, dropped, or reaches the end of the chain; if it reaches the end, the default action of the chain determines the fate of the packet. The default action of a chain can be configured to either accept or drop all packets.

The Linux netfilter supports a number of other interesting things you can do in filtering rules. One of the key advantages of netfilter is that it is extensible. It is possible to develop extensions that enhance the way netfilter operates. Some examples of more sophisticated packet handling actions are:

Packet logging
You can create rules that do nothing more than log a description of the matching packet so that it can be captured for analysis later. This is very useful for detecting attacks and for testing a filtering configuration.

Stateful inspection
netfilter includes a set of helper modules that can perform stateful connection inspection, such as management of FTP connections, as described earlier.

Network Address Translation
Network Address Translation (NAT), also called IP masquerading, provides a means of rewriting the IP addresses and port numbers of packets as they pass through a chain. NAT is most commonly used to allow systems on a private network to use a connection to the Internet with a single IP address. NAT is a complex subject that we don't discuss at length, but a simple example is provided later in this chapter. You can learn more about NAT in the NAT HOWTO or the Network Administrators Guide (O'Reilly).

Packet and byte accounting
netfilter provides counters that allow you to measure how the network traffic handled each rule, and several IP accounting systems are based on these statistics. These counters are visible when you use iptables to list rulesets in verbose mode; we'll demonstrate this in Example 17-3.

17.4.1.1. Using the iptables command

The iptables command is used to make changes to the netfilter chains and rulesets. You can create new chains, delete chains, list the rules in a chain, flush chains (that is, 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.

The iptables command has a large number of command-line arguments and options, but once you've used it a few times, the syntax becomes fairly obvious. In this section we are only going to cover the most common uses of iptables, so some arguments and options are left out of the following discussion. Specifically, we don't discuss user-defined chains here. Table 17-1 lists a summary of the iptables arguments that operate on chains, and Table 17-2 summarizes the iptables arguments that operate on individual rules.

Table 17-1. iptables operations on chains

Argument

Description

-L chain

List the rules in the specified chain or all chains.

-F chain

Flush (delete) the rules from the specified chain or all chains.

-Z chain

Zero the byte counters in the specified chain or all chains.

-P chain action

Set the default action on the specified chain to action.

Table 17-2. iptables operations on rules

Argument

Description

-A chain rule-specification

Append a rule to chain.

-D chain rulenum

Delete the rule with rule number rulenum from chain.

-R chain rulenum rule-specification

Replace rule number rulenum in chain with rule-specification.

-I chain rulenum rule-specification

Insert a rule into chain at slot number rulenum with specification rule-specification. If no rulenum is specified, "1" is assumed.

Each filtering rule includes parameters that describe which packets match the rule. The most common rule parameters are summarized in Table 17-3. Using an exclamation point (!) before a parameter inverts it. For example, the parameter -dport 80 means "match destination port 80," while the parameter -dport ! 80 means "match any destination port except 80."

Table 17-3. iptables rule parameters

Parameter

Matches

-p ! protocol

The packet protocol. Valid settings are tcp, udp, icmp, or all.

-s ! source/mask

Source address of the packet, specified as a hostname or IP address. mask specifies an optional netmask as either a literal netmask or a number of bits. For example, /255.255.255.0 gives the literal netmask, /24 gives the number of bits in the mask.

-d ! source/mask

Destination address of the packet. Uses the same syntax as the source address.

-- sport ! port

The source port of the packet. Specifies as a literal port number or as a service name from /etc/services.

-- dport ! port

The destination port of the packet. Uses the same syntax as the source address.

-i ! interface

The network interface on which the packet was received.

-o ! interface

The network address on which the packet will be sent.

A number of important options are used when building rulesets, summarized in Table 17-4.

Table 17-4. Other important iptables options

Option

Description

-v

Enable verbose output. Most useful when listing rules with -L.

-n

Display IP addresses in numeric form (i.e., avoid DNS lookup).

-m module

Load the iptables extension named module.

In addition to specifying matching parameters, each netfilter rule must specify some action to take for each packet matching the rule. Generally a rule specifies that a packet should be accepted or dropped, as described next. If no action is specified for a rule, the packet and byte counters for that rule will be incremented and the packet passed on to the next rule in the chain. This allows a rule to be used for accounting purposes only. To specify an action for a rule, use the syntax:

-j target

Here, -j stands for "jump," meaning that if a packet matches this rule, processing will jump to the action named by target. target can be one of:

ACCEPT
Allow this packet to be transmitted or received.

DROP
Drop the packet.

QUEUE
Pass the packet to a userspace program for processing.

RETURN
If used with a user-defined chain, causes the packet to be returned to the "calling" chain. If used with a built-in chain, causes the packet to jump to the end of the chain (where it is subject to the default action for that chain).

When using the -j option, target can also be the name of a user-specified chain, which allows the user to define a "subchain" of rules that will process this packet. As described earlier, the target RETURN is used to cause a packet to return from a user-defined chain back to the "calling" chain.

17.4.2. Developing IP Filtering Rulesets

Often the most difficult part of IP firewall implementation is deciding what you actually want it to do. Do you want to allow outgoing connections freely? Should you allow ICMP packets? What UDP services do you want? What kind of logging do you want to do?

One of the great challenges with building filtering rulesets is that most people aren't accustomed to thinking in terms of addresses, protocols, and port numbers. Instead, we more often think in terms of applications and end users. To build filtering rulesets, we must be able to translate our higher-level requirements into the low-level detail with which the filtering operates.

You can't get around the need to understand a bit of how the services that you are managing with IP filtering actually work. First and foremost, it is important to know whether a service uses TCP or UDP, and which port numbers it uses. The /etc/services file can often provide a good deal of what you need to know. For example, searching for smtp in this file yields tcp/25, which indicates that the SMTP protocol uses TCP port 25. Likewise, searching for the DNS returns two entries, one for udp/53 and another for tcp/53; this means that the service uses port 53, but uses either the TCP or UDP protocols.

Some protocols, such as FTP, have two related but different entries in /etc/services. As described earlier, FTP uses one port for the command session (tcp/21) and another for the data transfer sessions (tcp/20). Unfortunately, FTP clients and servers are free to use different ports for the data transfer session. Therefore, FTP has been somewhat of a nuisance for filtering rules. Fortunately, netfilter provides some assistance with a feature called connection tracking, along with a helper module that specifically understands the FTP service. Because of this it is necessary only to create a rule for the FTP command session, and netfilter will automatically track and allow the data transfer sessions for you. We demonstrate this later in Example 17-2.

If /etc/services doesn't provide enough information, you may need to read the relevant RFC document that specifies the protocol used by the service. Usually you don't need to know much more about a service other than what protocols and ports it uses, which is generally easy to find in the RFC.

17.4.3. IP Filter Management and Script Files

Filtering rules are stored and used by the kernel in much the same way as routing entries: when the system reboots, IP filtering rules must be reconfigured. To ensure that a firewall configuration is reinstated when a reboot occurs, you should place the appropriate iptables commands in a script file that is automatically executed at system boot time. Bundled with the iptables software package come two programs called iptables-save and iptables-restore that respectively save the current netfilter configuration to a file and restore it from that file. These tools greatly simplify the task of managing firewall configuration.

Each Linux distribution takes a slightly different approach to managing firewall configuration:

Red Hat (versions 7.0 and later)
First configure your IP filtering rules using the appropriate iptables commands. Then, execute the following command:

/sbin/service iptables save

This causes the filtering rules to be saved to /etc/sysconfig/iptables, which is automatically read at boot time.

Debian
Set up your iptables rules as follows:

  1. Edit /etc/default/iptables and set enable_iptables_initd=true.

  2. Manually configure your iptables using iptables commands.

  3. Invoke /etc/init.d/iptables save_active to save the configuration.

At system boot time the saved configuration will be restored automatically.

SuSE Linux
For a simple, albeit not as flexible, configuration, run yast2 and select the firewall configuration module Security&Users Figure Firewall. Otherwise:

  1. Edit /etc/sysconfig/SuSEfirewall2. This file is thoroughly documented.

  2. If necessary, define custom filter rules in /etc/sysconfig/scripts/SuSEfirewall2-custom. This requires deeper knowledge about how firewalls work on Linux.

  3. Start the firewall by invoking /sbin/SuSEfirewall2 start.

17.4.4. Sample netfilter Configurations

In this section we'll provide some simple but useful IP filtering configurations. The aim here is not to provide you with a set of solutions that you accept uncritically. Instead, we'll introduce you to what a useful set of IP filtering rules looks like and provide you with a skeleton on which you could base your own configurations.

17.4.4.1. Simple IP filtering example

Here we'll demonstrate the basic use of IP filtering, which is similar to our use of TCP wrappers described earlier in the chapter. Here we want to screen out packets from all hosts on the Internet, except for packets destined for the finger daemon from a small set of hosts. While TCP wrappers can be used to perform the same function, IP filtering can be used to screen many different types of packets (for example, ICMP "ping" packets), and is often necessary to protect services that aren't managed by TCP wrappers.

Unlike TCP wrappers, iptables rules cannot use hostnames to identify the origin or destination of a packet; you must use IP addresses when specifying rules. This is a good idea, anyway, since reverse hostname lookup is not a completely secure way to identify a packet (it is possible to spoof DNS, making it appear as though some IP address has a different hostname). In Example 17-1 and Example 17-2, we use IP addresses instead of hostnames, which can be obtained using a tool such as nslookup.

Example 17-1. Simple ipchains example

# Load the connection tracking modules if they're not compiled into the
# kernel.
modprobe ip_conntrack
modprobe ip_conntrack_ftp

# Set default policy on the INPUT chain to DROP.
iptables -P INPUT DROP

# ACCEPT packets belonging to an existing connection.
# '-A INPUT' is used to append to the INPUT chain.
# '-m state' uses the stateful inspection module. 
iptables -A INPUT -m state --state ESTABLISHED,RELATED -j ACCEPT

# ACCEPT all packets that have come from the loopback interface, that
# is, from the local host. '-i lo' identifies the loopback interface.
iptables -A INPUT -i lo -j ACCEPT

# ACCEPT new incoming connections, and packets belonging to existing
# connections, to port 22 (ssh).
iptables -A INPUT -m state --state NEW -m tcp -p tcp \
         --dport 22 -j ACCEPT

# ACCEPT new incoming FTP connections from 192.168.1/24.
iptables -A INPUT -m state --state NEW -m tcp -p tcp -s 192.168.1/24 \
         --dport 21 -j ACCEPT

# ACCEPT new incoming FTP connections from spaghetti.vpizza.com,
# which has IP address 10.21.2.4.
iptables -A INPUT -m state --state NEW -m tcp -p tcp -s 10.21.2.4 \
         --dport 21 -j ACCEPT

# ACCEPT new incoming FTP connections from *.vpizza.com. 
# They have two networks: 172.18.1.0 and 172.25.3.0.
iptables -A INPUT -m state --state NEW -m tcp -p tcp -s 172.18.1/24 \
         --dport 21 -j ACCEPT
iptables -A INPUT -m state --state NEW -m tcp -p tcp -s 172.25.3/24 \
         --dport 21 -j ACCEPT

The ruleset specifically accepts all packets that belong to an existing connection. This is needed in the case of FTP, in which the client and server may negotiate an alternate port for the data transfer connection. The connection tracking module (specified with -m state in the rules) ensures that the data transfer connection can be accepted.

17.4.4.2. IP filtering to protect an entire network

The previous example demonstrated IP filtering on a single host. In this section, we deal with the case where a network of machines (such as all the machines in a home or small office) are connected to the Internet through a gateway machine. We can write netfilter rules to filter the traffic between the Internet and the internal network. In this case, we place rules on both the INPUT and FORWARD chains. Recall that INPUT is used to filter incoming packets destined for this host, while FORWARD is used for packets being forwarded by the gateway (i.e., packets destined for the internal network or the Internet). Here, we assume that the gateway machine uses the ppp0 interface to communicate with the Internet.

Example 17-2. Using netfilter to protect an IP network

# Load the connection tracking modules if they're not compiled into the
# kernel.
modprobe ip_conntrack
modprobe ip_conntrack_ftp

# Set default policy on INPUT and FORWARD chains to DROP.
iptables -P INPUT DROP
iptables -P FORWARD DROP

# ACCEPT all packets from the loopback interface.
iptables -A INPUT -i lo -j ACCEPT

# Create a new user-defined chain. This chain will contain rules 
# relevant to both INPUT and FORWARD, so by grouping them together on
# a single chain we avoid stating the rules twice.
iptables -N allowfwdin

# ACCEPT packets belonging to an existing connection.
# Note that this rule (and subsequent rules) are placed
# on the user-defined chain.
iptables -A allowfwdin -m state --state ESTABLISHED,RELATED -j ACCEPT

# ACCEPT new connection requests from machines on the internal network.
# This allows machines on the internal network to establish connections
# to the Internet, but not the other way around. Note the use of
# '-i ! ppp0' to specify packets coming from interfaces other than ppp0.
iptables -A allowfwdin -m state --state NEW -i ! ppp0 -j ACCEPT

# ACCEPT new incoming connections to port 22 (ssh).
iptables -A allowfwdin -m state --state NEW -m tcp -p tcp \
         --dport 22 -j ACCEPT

# ACCEPT new incoming FTP connections from 192.168.1/24.
iptables -A allowfwdin -m state --state NEW -m tcp -p tcp -s 192.168.1/24 \
         --dport 21 -j ACCEPT

# ACCEPT new incoming FTP connections from spaghetti.vpizza.com.
iptables -A allowfwdin -m state --state NEW -m tcp -p tcp -s 10.21.2.4 \
         --dport 21 -j ACCEPT

# ACCEPT new incoming FTP connections from *.vpizza.com.
iptables -A allowfwdin -m state --state NEW -m tcp -p tcp -s 172.18.1/24 \
         --dport 21 -j ACCEPT
iptables -A allowfwdin -m state --state NEW -m tcp -p tcp -s 172.25.3/24 \
         fs

# Any packets that have passed through the user-defined chain are now
# subject to the action LOG, which causes them to be logged.
# Use the 'limit' module to prevent logging blocked packets too
# rapidly.
iptables -A allowfwdin -m limit --limit 2/sec -j LOG

# Set default action on the user-defined chain to DROP.
iptables -A allowfwdin -j DROP

# Direct all packets received for INPUT or FORWARD to our user-defined chain.
iptables -A INPUT -j allowfwdin
iptables -A FORWARD -j allowfwdin

# Enable IP routing (required by all IP routers, regardless of the use 
# of IP filtering).
echo 1 >/proc/sys/net/ipv4/ip_forward

To keep track of any attempts to breach security, we've added a rule that will log any packets that would be dropped. However, if a large number of bad packets were to arrive, this rule might fill up the disk with log entries, or slow down the gateway to a crawl (as it takes much longer to log packets than it does to forward or filter them). So, we use the limit module which controls the rate at which a rule action is taken. In the preceding example, we allowed an average rate of two bad packets per second to be logged. All other packets will pass through the rule and simply be dropped.

To view the rules that have been configured (see Example 17-3), use the iptables list option -L. Using the verbose mode (-v) displays more information than the basic output of the command.

Example 17-3. Listing iptables rulesets for Example 17-2

# iptables -L -v
Chain INPUT (policy DROP 0 packets, 0 bytes)
 pkts bytes target     prot opt in     out     source               destination
   16  1328 ACCEPT     all  --  lo     any     anywhere             anywhere
    0     0 allowfwdin all  --  any    any     anywhere             anywhere

Chain FORWARD (policy DROP 0 packets, 0 bytes)
 pkts bytes target     prot opt in     out     source               destination
    0     0 allowfwdin all  --  any    any     anywhere             anywhere

Chain OUTPUT (policy ACCEPT 9756 packets, 819K bytes)
 pkts bytes target     prot opt in     out     source               destination

Chain allowfwdin (2 references)
 pkts bytes target     prot opt in     out     source               destination
    0     0 ACCEPT     all  --  any    any     anywhere             anywhere  \
    state RELATED,ESTABLISHED
    0     0 ACCEPT     all  --  !ppp0  any     anywhere             anywhere  \
    state NEW
    0     0 ACCEPT     tcp  --  any    any     anywhere             anywhere  \
    state NEW tcp dpt:ssh
    0     0 ACCEPT     tcp  --  any    any     192.168.0.0/24       anywhere  \
    state NEW tcp dpt:ftp
    0     0 ACCEPT     tcp  --  any    any     10.21.2.4            anywhere  \
    state NEW tcp dpt:ftp
    0     0 ACCEPT     tcp  --  any    any     172.18.0.0/24        anywhere  \
    state NEW tcp dpt:ftp
    0     0 ACCEPT     tcp  --  any    any     172.25.0.0/24        anywhere  \
    state NEW tcp dpt:ftp
    0     0 LOG        all  --  any    any     anywhere             anywhere  \
    limit: avg 2/sec burst 5 LOG level warning
    0     0 DROP       all  --  any    any     anywhere             anywhere

17.4.4.3. IP masquerading example

netfilter rules can also be used to implement IP masquerading, a specific type of NAT that rewrites packets from an internal network to make them appear as though they are originating from a single IP address. This is often used in cases where one has a number of machines connected to a LAN, with a single Internet-connected machine with one IP address. This is a common situation in home networks where the ISP has allocated a single IP address; using IP masquerading, however, an entire network of machines can share the address. By having the gateway perform IP masquerading, packets from the internal LAN will appear as though they are originating from the gateway machine, and packets from the Internet will be forwarded back to the appropriate host on the internal LAN. You can accomplish all of this with a bit of clever packet rewriting using netfilter.

Configuring netfilter to support IP masquerading is much simpler than explaining how it works! More complete information about how IP masquerading and NAT are accomplished is provided in the NAT HOWTO. We'll show the most basic configuration in Example 17-4.

In this configuration we've assumed that we have a Linux system that will act as a gateway for an internal network. The gateway has a PPP connection to the Internet on interface ppp0, and a LAN connection to the internal network on interface eth0. This configuration allows outgoing connections from the internal network to the Internet, but will block incoming connections from the Internet to machines on the internal network except for the gateway. As it turns out, we don't need to provide explicit commands to achieve this, as it is the default behavior when using NAT in this fashion.

Example 17-4. Basic IP masquerade configuration

# Load the module supporting NAT, if not compiled into the kernel.
modprobe iptables_nat

# Masquerade any routed connections supported by the ppp0 device.
iptables -t nat -A POSTROUTING -p ppp0 -j MASQUERADE

# Enable IP routing.
echo 1 >/proc/sys/net/ipv4/ip_forward

There are some important details to note in this configuration. The NAT functionality is provided in a module of its own, which must be loaded unless it is built into your kernel. The NAT module uses a new chain called POSTROUTING that processes packets after the kernel performs routing operations on them (that is, decides whether the packets are destined for the Internet or for internal LAN machines). The MASQUERADE target does the hard work of the address translation and tracking.

Note that this configuration provides no filtering of outgoing connections. All hosts on the private network will be able to establish outgoing connections to any host and any port. The packet filtering HOWTO provides useful information about how to combine IP filtering with address translation.



Library Navigation Links

Copyright © 2003 O'Reilly & Associates. All rights reserved.