Running a Secure System - Network Services - Running Linux, 5th Edition (2009)

Running Linux, 5th Edition (2009)

Part IV. Network Services

Chapter 26. Running a Secure System

In this chapter we discuss basic Linux system security. Security is unfortunately a topic of ever-growing importance, especially with the increasing use of permanently network-connected systems that are vulnerable to remote attacks even while unattended.

Most system security is commonsense good practice. Many of the best techniques are the simplest, yet frequently ignored practices; we cover those first. We then move on to some of the less obvious practices, and we conclude with a short discussion of the complex subject of network security. We also include some firewall recipes to protect simple installations against network attack.

A Perspective on System Security

It's sometimes difficult keeping a balanced perspective on system security. The media tends to sensationalize stories relating to security breaches, especially when they involve well-known companies or institutions. On the other hand, managing security can be a technically challenging and time-consuming task. Many Internet users take the view that their system holds no valuable data, so security isn't much of an issue. Others spend large amounts of effort nailing down their systems to protect against unauthorized use. No matter where you sit in this spectrum, you should be aware that there is always a risk that you will become the target of a security attack. There are a whole host of reasons why someone might be interested in breaching your system security. The value of the data on your system is only one of them; we discuss some others later in the chapter. You must make your own judgment as to how much effort you will expend, though we recommend that you err on the side of caution.

Traditional system security focused on systems that were accessible through either a connected hard-wired terminal or the system console. In this realm the greatest risks typically came from within the organization owning the system, and the best form of defense was physical security, in which system consoles, terminals, and hosts were in locked rooms. Even when computer systems started to become network-connected, access was still very limited. The networks in use were often expensive to gain access to, or were closed networks that did not allow connections to hosts from just anywhere.

The popularity of the Internet has given rise to a new wave of network-based security concerns. An Internet-connected computer is open to potential abuse from tens of millions of hosts around the world. With improved accessibility comes an increase in the number of antisocial individuals intent upon causing nuisance. On the Internet, a number of forms of antisocial behavior are of interest to the system administrator. Those that we address in this chapter are the following:

Denial of service (DoS)

This kind of attack degrades or disrupts a service on the system.

Intrusion

This kind of attack accesses the system by guessing passwords or compromising some service. Once an intruder has access to a system, he may then vandalize or steal data or use the target system to launch attacks on some other host.

Snooping

This kind of attack involves intercepting the data of another user and listening for passwords or other sensitive information. Sometimes this form of attack involves modification of data, too. Snooping usually involves eavesdropping on network connections, but can also be performed by compromising a system to intercept library or system calls that carry sensitive information (e.g., passwords).

Viruses, worms, and Trojan horses

These attacks each rely on compelling users of your system to execute programs supplied by the attacker. The programs could have been received in an email message, or from a web site, or even from within some other apparently harmless program retrieved from somewhere on the Internet and installed locally.

A DoS attack commonly involves generating an abnormally large number of requests to a service provided by a system. This rush of activity may cause the host system to exhaust its memory, processing power, or network bandwidth. Another way is to provide the service with non-ordinary input in order to exploit a bug in the service and cause a core dump. As a result, further requests to the system are refused, or the system's performance degrades to an unusable point. For this type of attack to work, an attacker must either exploit a poorly designed service or be able to generate a number of requests far exceeding the capacity of the service.

A more insidious form of DoS attack is the distributed denial of service (DDoS). In this form of attack, a large number of computers are used or caused to generate requests against a service. This increases the damage of a DoS attack in two ways: by overwhelming the target with a huge volume of traffic, and by hiding the perpetrator behind thousands of unwitting participants. Using a large number of hosts from which to launch an attack also makes DDoS attacks particularly difficult to control and remedy once they've occurred. Even people who have no concerns about the state of their own data should protect themselves against this form of attack so as to minimize the risk of becoming an unwitting accomplice in a DDoS attack against someone else.

The second form of attack, sometimes known as cracking , is the one that most people associate with security .[*] Companies and institutions often store sensitive data on network-accessible computer systems. A common example of concern to the average Internet user is the storage of credit card details by web sites. Where there is money involved, there is incentive for dishonest individuals to gain access and steal or misuse this kind of sensitive data.

Sometimes the methods that are used to gain unauthorized access or disrupt service are very ingenious, if not unethical. Designing an intrusion mechanism often requires a strong knowledge of the target system to uncover an exploitable flow. Often, once an intrusion mechanism has been discovered, it is packaged in the form of a so-called rootkit, a set of programs or scripts that anyone possessing only basic knowledge can use to exploit a security hole. The vast majority of intrusion attacks are launched by "script kiddies" who make use of these prepackaged intrusion kits without any real knowledge of the systems they are attacking. The good news is that it is usually straightforward for a system administrator to protect a system from these well-known attacks; we discuss various ways to secure your system in this chapter.


[*] The terms cracking and hacking are often confused in popular usage. Whereas cracking involves immoral or illegal behavior (such as compromising the security of a system), hacking is a generic word meaning to program, tinker with, or have an intense interest in something. The popular media often uses the term hacking to refer to cracking; the Linux community is trying to reassociate hacking with positive connotations.

Initial Steps in Setting Up a Secure System

There are some very simple things you can do to protect a Linux system from the most basic security risks. Of course, depending on your configuration, the ways in which you will be using your system, and so forth, they might be more involved than the simple setup described here. In this section we briefly cover the mechanisms to secure a Linux system from the most common attacks—this is the basic approach one of the authors takes whenever installing a new machine.

Shutting Down Unwanted Network Daemons

The first step in securing a Linux machine is to shut down or disable all network daemons and services that you don't need. Basically, any (external) network port that the system is listening for connections on is a risk, since there might be a security exploit against the daemon serving that port. The fast way to find out what ports are open is to use netstat -an, as shown (we've truncated some of the lines, however):

# netstat -an

Active Internet connections (servers and established)

Proto Recv-Q Send-Q Local Address Foreign Address State

tcp 0 0 0.0.0.0:7120 0.0.0.0:* LISTEN

tcp 0 0 0.0.0.0:6000 0.0.0.0:* LISTEN

tcp 0 0 0.0.0.0:22 0.0.0.0:* LISTEN

Here we see that this system is listening for connections on ports 7120, 6000, and 22. Looking at /etc/services, dropping the -n or using the -p to netstat, can often reveal what daemons are associated with these ports. In this case they are the X font server, the X Window System server, and thessh daemon.

If you see a lot of other open ports—for things such as telnetd, sendmail, and so forth ask yourself whether you really need these daemons to be running, and to be accessible from other hosts. From time to time, security exploits are announced for various daemons, and unless you are very good at keeping track of these security updates, your system might be vulnerable to attack. Also, telnetd, ftpd, and rshd all involve sending clear-text passwords across the Internet for authentication; a much better solution is to use sshd, which encrypts data over connections and uses a stronger authentication mechanism. Even if you never use telnetd, it's not a good idea to leave it running on your system, in case someone finds a way to break into it.

Shutting down services is usually a matter of de-installing the corresponding package. If you want to keep the client, but the client and daemon are packaged together (exceedingly rare these days), you need to edit the appropriate configuration files for your distribution and reboot the system (to be sure the daemon is good and dead). On Red Hat systems, for example, many daemons are started by scripts in the /etc/rc.d/init.d directory; renaming or removing these scripts can prevent the appropriate daemons from starting up. Other daemons are launched by inetd or xinetd in response to incoming network connections; modifying the configuration of these systems can limit the set of daemons running on your system.

If you absolutely need a service running on your machine (such as the X server), find ways of preventing connections to that service from unwanted hosts. For example, it might be safest to allow ssh connections only from certain trusted hosts, such as from machines in your local network. In the case of the X server and X font server, which run on many desktop Linux machines, there is usually no reason to allow connections to those daemons from anything but the local host itself. Filtering connections to these daemons can be performed by TCP wrappers or IP filtering, which are described later in this chapter.

Top 10 Things You Should Never Do

We've made the claim that security is mostly common sense, so what is this common sense? In this section we summarize the most common security mistakes. (There aren't actually 10 items in this list, but there are enough to merit the use of the common "top 10" phrase.) Consistently avoiding them all is harder work than it might first seem.

Never use simple or easily guessed passwords .

Never use a password that's the same as (or closely related to) your user ID, name, date of birth, the name of your company, or the name of your dog. If you're an amateur radio operator, don't use your callsign; if you love cars, don't use the make/model or registration number of your car—you get the idea. Always ensure that your passwords are not simple words that can be found in a dictionary. The best passwords are nonsense strings. One good practice is to use a password based on a simple rule and a phrase that you can remember. For example, you might choose a rule such as using the last letter of each word in the phrase "Mary had a little lamb, its fleece was white as snow"; hence, the password would become ydaebsesesw, certainly not something that will be easily guessed, but a password that will be easily remembered. Another common technique is to use numbers and punctuation characters in the password; indeed, some passwd programs insist upon this. A combination of the two techniques is even better. One of our collegues swears by head -c6 /dev/random | mimencode as a way to generate hard passwords. Adjust the number of random bytes to use (-c6) to taste. Six input characters give eight characters of output, the maximum some Linux distributions accept for passwords.

Don't use the root account unless you have to.

One of the reasons that many common desktop operating systems (such as Windows) are so vulnerable to attack through email viruses and the like is the lack of a comprehensive privilege system, or rather the user's convenience of running applications with administrator privileges. Mind you, some broken applications require to be run with administrator rights. In such systems, any user has permission to access any file, execute any program, or reconfigure the system in any way. Because of this it's easy to coerce a user to execute a program that can do real damage to the system. In contrast, the Linux security model limits a wide range of privileged tasks, such as installing new software or modifying configuration files, to the root user. Do not succumb to the temptation to use the root account for everything! In doing so you are throwing away one of the more powerful defenses against virus and Trojan horse attacks (not to mention accidental rm -rf * commands). Always use a normal user account, and use the su or sudo commands to temporarily obtain root access when you need to undertake privileged tasks. There is an additional benefit in this limited use of the root account: logging. The su and sudo commands write messages to the system logfile when they're invoked, mentioning the ID of the user performing the su or sudo, as well as the date and time that the command was invoked. This is very helpful for keeping track of when root privileges are being used, and by whom.

Don't share your passwords .

Don't tell anybody your passwords, ever. This also means you shouldn't write your passwords on little sticky notes attached to your monitor, or in the diary you keep in the top drawer. If you want to allow people temporary access to your system, create an account for them to use. This allows you some convenience in monitoring what they do, and you can easily clean up afterward. If you really must trust someone with your root account, use the sudo command, which allows you to give users root access to selected commands without revealing the root password.

Don't blindly trust binaries that have been given to you.

Although it is very convenient to retrieve and install binary copies of programs on your system, you should always question how much you trust the binary before running it. If you're installing software packages that you've retrieved directly from the official sites of your distribution or from a significant development site, you can be fairly confident the software is safe. If you're getting them from an unofficial mirror site, you need to consider how much you trust the administrators of the site. It is possible that someone is distributing a modified form of the software with back doors that would allow someone to gain access to your machine. Although this is a rather paranoid view, it is nevertheless one that many Linux distribution organizations are embracing. For example, the Debian organization is developing a means of validating a software package to confirm that it hasn't been modified. Other distributions are sure to adopt similar techniques to protect the integrity of their own packaged software.

If you do want to install and execute a program that has been given to you in binary form, there are some things you can do to help minimize risk. Unfortunately, none of these techniques is easy if you're new to the Linux environment. First, always run untrusted programs as a non-rootuser unless the program specifically requires root privileges to operate. This will contain any damage the program might do, affecting only files and directories owned by that user. If you want to get some idea of what the program might do before you execute it, you can run the stringscommand over the binaries. This will show you all the hardcoded strings that appear in the code. You should look for any references to important files or directories, such as /etc/passwd or /bin/login. If you see a reference to an important file, you should ask yourself whether that is in keeping with the purpose of the program in question. If not, beware. If you're more technically inclined, you might also consider first running the program and watching what it is doing using a program such as strace or ltrace, which display the system and library calls that the program is making. Look for references to unusual file system or network activity in the traces.

Don't ignore your logfiles

Your system logfiles are your friend, and they can tell you a lot about what is happening on your system. You can find information about when network connections have been made to your system, who has been using the root account, and failed login attempts. You should check your logfiles periodically and get to know what is normal and, more usefully, what is abnormal. If you see something unusual, investigate.

Don't let your system get too far out of date.

It's important to keep the software on your system fairly current. That Linux kernel 1.2 system you have running in the corner that's been reliably serving your printers for years might be a great subject at cocktail parties, but it's probably a security incident waiting to happen. Keeping the software on your system up-to-date helps ensure that all bug and security fixes are applied. Most Linux distributions provide a set of packages that are security fixes only, so you don't have to worry about issues such as configuration file and feature changes in order to keep your system secure. You should at least keep track of these updates.

Don't forget about physical security .

Most security breaches are performed by people inside the organization running the target system. The most comprehensive software security configuration in the world means nothing if someone can walk up to your machine and boot a floppy containing exploit code. If your machine uses a BIOS or system PROM that allows the device boot order to be configured, set it so that the floppy and CD-ROM drives boot after the hard drive. If your BIOS provides support for password protection of its configuration, use it. If you can padlock the machine case closed, consider doing so. If you can keep the machine in a physically secure area such as a locked room, that's even better.

TCP Wrapper Configuration

We explained earlier that connecting your system to a network significantly increases the risk of attack. With the commonsense considerations out of the way, it's time to look more closely at basic network security. Here we discuss a simple yet effective method of reducing the risk of unwanted network access, using a tool called TCP wrappers . This mechanism "wraps" an existing service (such as the mail server), screening the network connections that are made to it and refusing connections from unauthorized sites. This is a simple way of adding access control to services that weren't originally designed for it, and is most commonly used in conjunction with the inetd or xinetd daemons.

TCP wrappers are somewhat equivalent to the security guards, or bouncers, that you might find protecting the entrance to large parties or nightclubs. When you approach a venue, you first encounter the security guard, who may ask you your name and address. The guard then consults a guest list, and if you're approved, the guard moves aside and allows you entry to the party.

When a network connection is made to a service protected by TCP wrappers , the wrapper is the first thing encountered. The wrapper checks the source of the network connection using the source hostname or address and consults a list that describes who is allowed access. If the source matches an entry on the list, the wrapper moves out of the way and allows the network connection access to the actual daemon program.

There are two ways to use TCP wrappers, depending on your Linux distribution and configuration. If you are using the inetd daemon for managing services (check to see if the file /etc/inetd.conf exists), TCP wrappers are implemented using a special daemon called tcpd. If you are using thexinetd daemon instead (check for the directory /etc/xinetd.d), xinetd is usually configured to use TCP wrappers directly. We describe each case in the following sections.

Using TCP Wrappers with inetd

If your system uses the inetd daemon to launch network services, it may be necessary to edit your /etc/inetd.conf file to use TCP wrappers . Let's use the finger daemon, in.fingerd, as an example. The basic idea is that instead of running the actual in.fingerd daemon, inetd launches the tcpddaemon instead. tcpd performs the TCP wrapper operation and then runs in.fingerd in its place if the connection is accepted.

Configuring TCP wrappers requires a very simple change to /etc/inetd.conf. For the finger daemon, you might have an entry in this file such as the following:

# /etc/in.fingerd finger daemon

finger stream tcp nowait root /usr/sbin/in.fingerd in.fingerd

To protect the finger daemon using tcpd, simply modify the /etc/inetd.conf entry, as so:

# /etc/in.fingerd finger daemon

finger stream tcp nowait root /usr/sbin/tcpd /usr/sbin/in.fingerd

Here we've caused the tcpd command to be executed instead of the actual in.fingerd command. The full pathname of the finger daemon is passed to tcpd as an argument, and tcpd uses this argument to launch the real daemon after it has confirmed that access should be allowed.

You'll need to make this change for each daemon program you wish to protect. On most Linux systems, you may find that tcpd is already configured, so these changes won't be necessary.

Using TCP Wrappers with xinetd

xinetd is a replacement for inetd that some distributions (such as Red Hat) are adopting. In most cases, xinetd has built-in support for TCP wrappers, so all you'll need to do is modify the TCP wrapper configuration files (/etc/hosts.allow and /etc/hosts.deny) as described in the next section. If you are installing xinetd yourself, be sure to compile support for TCP wrappers; this is described in the xinetd documentation.

/etc/hosts.allow and /etc/hosts.deny

TCP wrappers use two configuration files, /etc/hosts.allow and /etc/hosts.deny. These files are used to specify the access rules for each network daemon protected with TCP wrappers. The files are described in detail in the hosts_access manual page, but we cover the mechanics here because common cases are fairly simple.

When a TCP wrapper is invoked, it obtains the IP address of the connecting host and attempts to find its hostname using a reverse DNS lookup. Next, it consults the /etc/hosts.allow file to see if this host is specifically allowed access to the requested service. If a match is found, access is allowed and the actual network daemon is invoked. If no match is found in the /etc/hosts.allow file, /etc/hosts.deny is consulted to see if this host has been specifically denied access. If a match is found here, the connection is closed. If no match is found in either file, access is granted. This simple technique is powerful enough to cover most access requirements.

The syntax of hosts.allow and hosts.deny is fairly simple. Each file contains a set of rules. Each rule is generally on one line but may be split across multiple lines using a backslash at the end of the line. Each rule has the following general form:

daemon_list : client_list :shell_command

The daemon list is a comma-separated list of daemons to which the rule applies. The daemons are specified using their command basename — that is, the name of the actual executable daemon program that is executed to fulfill the requested service. The client list is a comma-separated list of hostnames or IP addresses for which the rule will match. We'll demonstrate this later using an example. The shell command is optional, and specifies a command that will be executed when the rule matches. This can be used, for example, to log incoming connections.

daemon_list and client_list may contain patterns that allow you to match a number of daemons or hosts without having to explicitly name each one. In addition, you can use a number of predefined tokens to make rules simpler to read and build. The patterns are quite sophisticated, so we don't cover them in detail here; instead, we refer you to the hosts_access manual page.

Let's start with a simple hosts.deny file that looks like this:

# /etc/hosts.deny

ALL: ALL

The first line is a comment. The next line is a rule that is interpreted as follows: "Deny access requests to all services from all hosts." If our /etc/hosts.allow file is empty, this rule will have the effect of denying access to everything from all hosts on the Internet—including the local host! To get around this problem, we can make a simple change to the file:

# /etc/hosts.deny

ALL: ALL EXCEPT localhost

This is nearly always a safe rule to have in place, as it's a secure default. Remember that the /etc/hosts.allow rules are consulted before /etc/hosts.deny, so by adding rules to hosts.allow we can override this default setting in hosts.deny. For example, imagine that we want to allow every host on the Internet to access the finger daemon. To do this, we add a rule to /etc/hosts.allow that looks like the following:

# /etc/hosts.allow

in.fingerd: ALL

A more common use of TCP wrappers is to restrict the set of hosts that can access a service. Hosts can be specified using an IP address, hostname, or some pattern based on the address or hostname (e.g., to specify a group of hosts). For example, consider making the finger daemon available only to a small set of trusted hosts. In this case, our hosts.allow file would be modified as follows:

# /etc/hosts.allow

in.fingerd: spaghetti.vpasta.com, .vpizza.com, 192.168.1.

In this example we've chosen to allow finger requests from the host named spaghetti.vpasta.com, as well as from any host in the vpizza.com domain, and from any system with an IP address beginning with the pattern 192.168.1.

The host and IP address-matching rules in hosts.allow and hosts.deny are important to understand, and the presence and location of the period characters are critical. A pattern beginning with a period is assumed to be the name of a domain to which requesting systems must belong. A pattern ending with a period is assumed to specify an IP address pattern. There are other ways of specifying groups of hosts, including NIS netgroups and explicit IP address netmasks. Full details on the configuration syntax of these patterns are available in the hosts_access manual page.

Firewalls: Filtering IP Packets

Although 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 whether 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 HTTPtraffic 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 have been 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 than earlier filters, but is extensible as well. In the following sections we describe netfilter and some simple configurations as examples.

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 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 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 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 as follows:

INPUT

This chain applies to packets that are received by 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 for 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 on to 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 the following:

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 in TCP/IP Network Administration, Third Edition (O'Reilly).

Packet and byte accounting

netfilter provides counters that allow you to measure how the network traffic handled each rule. Several IP accounting systems are based on these statistics. These counters are visible when you use iptables to list rulesets in verbose mode; we demonstrate this in Example 26-3, later in this chapter.

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 only 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 26-1 summarizes the iptables arguments that operate on chains, and Table 26-2 summarizes the iptables arguments that operate on individual rules.

Table 26-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 (policy) of the specified chain to action.

Table 26-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 26-3. Using an exclamation point (!) before a parameter inverts it. For example, the parameter -dport 80 means "match destination port 80," whereas the parameter -dport ! 80 means "match any destination port except 80."

Table 26-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, and /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 packe, specified 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 26-4.

Table 26-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 will be 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 following 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 the following:

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, RETURN causes the packet to be returned to the "calling" chain. If used with a built-in chain, it 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.

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 of 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 about 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 both the TCP and 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 the chapter in Example 26-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 theRFC.

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 are 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

In order to set up iptables rules you either need to write a /etc/init.d script manually or use one of the many packages available that generate firewall rules for you.

SUSE Linux

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

§ Edit /etc/sysconfig/SUSEfirewall2. This file is thoroughly documented.

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

§ Start the firewall by invoking /sbin/SUSEfirewall2 start.

Sample netfilter Configurations

In this section we 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 introduce you to what a useful set of IP filtering rules looks like and provide you with a skeleton on which you can base your own configurations.

Simple IP filtering example

Here we 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. Although 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 an IP address has a different hostname). In Examples 26-1 and 26-2, we use IP addresses instead of hostnames, which can be obtained using a tool such as host.

Example 26-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.

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 Example 26-2, we place rules on both the INPUT and FORWARD chains. Recall that INPUT is used to filter incoming packets destined for this host, whereas 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 26-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 26-3), use the iptables list option -L. Using the verbose mode (-v) displays more information than the basic output of the command.

Example 26-3. Listing iptables rulesets for Example 26-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

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 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 26-4.

Example 26-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

In this configuration we 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.

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 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.

SELinux

SELinux is a fairly new development in the realm of secure Linux systems. It was developed by the National Security Agency (NSA) in the United States, and presumably fits into its mission of securing U.S. computers and communications. But it's curious that a government agency whose raison d'être includes making it possible to break into people's computers and wiretap their communication would develop a Linux system that is supposed to be more secure against these kinds of attacks. See the book SELinux (O'Reilly) for an in-depth guide.

SELinux contains a changed Linux kernel that includes mandatory access controls, as well as a number of utilities for controlling the new kernel features. With SELinux, user programs (and daemons) only get just as much access to resources as they need. A security hole such as a buffer overflow in a web server can therefore not compromise the whole computer anymore. In SELinux, there is no root user that has access to everything.

It would be beyond the scope of this book to describe the installation and day-to-day operation of SELinux, but if you are interested in hardened Linux systems, you should have a look at http://www.nsa.gov/selinux. Information about how to install an SELinux kernel on a number of distributions can be found on http://selinux.sf.net.