Using IPChains to Make a Packet-Filtering Firewall

Author: Mark W. Krentel, May 2000.

1. Introduction

This tutorial describes how to use ipchains to make a simple packet-filtering firewall in Linux, mainly for the home user. We start with some background and theory on what packet filters can and cannot do, but the main focus is on selecting the ipchains rules. And although these notes use ipchains in Linux, most of what we say applies to any packet filter, just with a different syntax.

You certainly should read the ipchains(8) man page to see what the options are and what they do. Beyond that, the Howtos on IPChains, Firewalling and Masquerading are worth a look. For even more references, these sites contain links to other documentation and firewall programs.

   http://www.linuxfirewall.org
   http://www.linuxfirewall.com
   http://www.redhat.com/support/resources/networking/firewall.html

For a good introduction to networking in Unix, I recommend TCP/IP Network Administration (O'Reilly) by Craig Hunt. The definitive book on packet filters is Building Internet Firewalls (O'Reilly) by Chapman and Zwicky. Both of these books are fairly advanced, but they tell you the real story.

2. Background and Theory

It's important to understand what packet filters can and cannot do. IPChains is a simple (static) packet filter and thus it only examines a packet's header. You can accept or deny packets based on their source and destination IP address and port number, but you cannot examine the data inside a packet. And each packet must be acted upon entirely on its own, without any coordination with any other packet. Finally, the rules are static, they do not adapt to earlier events.

Proxy servers are another type of firewall tool. Proxies work on a service by service basis and they understand the underlying protocol. The proxy sits between the client and the real server, evaluates the client's requests, forwards them to the real server and relays the answers. Packet filters are simpler, but proxies are more powerful because they examine the packet's data. Packet filters are a good first step, but they should not be confused with a full-blown firewall.

2.1 Ports and Servers

Packet filtering is based on port numbers. A port is a number from 1 to 65535 (unsigned 16-bit) and provides an access point for network programs. Programs don't communicate across a network through standard input and output, they use ports. Ports 1 to 1023 are special (privileged) and may only be used by root, mainly for system daemons for incoming servers. The file /etc/services contains the well-known port numbers. Ports 1024 to 65535 are available for general use by any program that wants network access.

For example, suppose you use telnet to connect to a remote machine. The client program (telnet) asks the kernel for an unused port. You could get any port above 1024, but let's say that you get port 3000. The server program (in.telnetd) always uses port 23 on the remote machine. And that's how the client finds the server, it expects port 23 to be a telnet server. The packets for your telnet connection will then be between port 3000 on your machine and port 23 on the remote machine. And this is how a well-behaved protocol normally works. The client uses a high port (1024-65535), the server uses a well-known, low port (1-1023), and the client initiates the connection. Theoretically, any protocol could use any port, but it's much more friendly to firewalls if the client opens the connection and the server uses a fixed port.

So, if you want to use outgoing telnet, your filter must allow packets (in both directions) between the high ports (1024-65535) on your machine and port 23 on the server. But more importantly, suppose you want to block incoming telnet. The best way, if you can, is to not run the daemon. In the case of telnet, just comment out its entry in /etc/inetd.conf. But suppose you have to run the daemon because you want to allow telnet within your local network, you just want to block telnet from outside. In that case, you can block outside access to port 23 on your machine. And even if you don't run the daemon, it doesn't hurt to have two lines of defense.

2.2 What Packet Filters Can and Cannot Do

Packet filters have two main uses. First, as discussed above, you can control access to servers and ports. Theoretically, if you have good passwords on all of your accounts, then you could leave the telnet server exposed, and this is how Unix is normally configured. But if you have no need for incoming telnet, then it's far better to block access to that port and thus not allow someone to even attempt to login. And you can control access by both ports and IP addresses. That is, you can allow telnet but not FTP, or you can allow telnet from some addresses but not others.

And this is the key to packet filtering. It's knowing what network services your machine runs and what ports they use. Other than some denial of service attacks, simply throwing packets at you doesn't really infiltrate your machine. What really matters is what program receives those packets and what it does with them. So, by knowing what programs are behind what ports, you can understand the risks and open your system as little as possible to allow only the services that you need.

The second thing packet filters can do is prevent an outside machine from spoofing the addresses on your local network. A hacker may fake the source address of his packets, hoping that you'll believe they originated from inside your network. But suppose your Linux machine is the gateway between your local network and your ISP. That is, you have one connection to your ISP and a separate interface to your network, and packets must go through the Linux machine to go between the two. In that case, packets claiming to be from your network that arrive via your ISP are bogus and should be blocked. And even if you have just one machine and no network, then you should still block packets from the Internet that claim to be from localhost (127.0.0.1).

The main limitation of packet filters is that they don't examine the data inside a packet. For example, you can limit FTP to outgoing connections, but you can't control what files are copied. Similarly, packet filters won't protect you from receiving spam or dangerous attachments in email, or from downloading a virus off the web. You still need to know what programs you run and what they do.

3. Preliminaries

Before running ipchains, there are a few other security issues that you should look at first because they are related. First, check the password files (/etc/passwd and /etc/shadow) that every account has a password, and try to pick good, random passwords. Second, check /etc/inetd.conf and comment out the services that you don't need. And third, some network daemons (sendmail, lpd, nfsd, etc.) are started in the rc scripts, not through inetd. Again, identify the ones that you don't need and don't run them.

IPChains requires some kernel support, including Networking and Firewalling. Plus, if you want to use your Linux machine as the gateway for a local network, then you'll also need Forwarding and Masquerading. These options are usually included in the generic kernel, but it doesn't hurt to check. Also, some of these options have to be explicitly turned on. This varies somewhat by distribution, but on my RedHat system, it's controlled by variables in /etc/sysconfig/network.

3.1 IPChains Options

Here is a summary of the ipchains options that we use. Be sure to run ``man ipchains'' for a more complete explanation of the options.

   -p  protocol type (tcp, udp, icmp)        -f  packet fragment
   -s  source IP address and port num.       -y  syn packet (tcp only)
   -d  destn. IP address and port num.       --sport  source port
   -i  interface name (lo, ppp0, eth0)       --dport  destn. port
   -j  target (ACCEPT, DENY, REJECT, MASQ)   --icmp-type
   -l  log this packet

A rule applies to a packet if the packet matches all of the options (the options are and-ed together). For example, this rule says to accept packets that enter from the first ethernet device (eth0) from source address 192.168.1.*.

   ipchains -A input -i eth0 -s 192.168.1.0/24 -j ACCEPT

For each packet, the kernel applies the rules in the order that they are appended. The first rule that matches the packet and includes a target (ACCEPT, DENY) determines the action for that packet and ends the search. The kernel applies rules for both incoming and outgoing packets and keeps separate lists for the input and output chains. So, you could write all the input rules followed by all the output rules, but my preference is to group them by section.

I recommend putting these rules in a shell script that first flushes any old rules (ipchains -F) and then loads the new rules in the order given. During testing, edit the script offline and when you change a rule, rerun the entire script. When you're happy with the rules, invoke the script from /etc/rc.d/rc.local so the rules are automatically loaded at boot time. Also, as part of good script programming, you should either set the PATH variable (PATH="/bin:/sbin" should be enough) or else use the full pathname (/sbin/ipchains) for each command.

4. A Simple Firewall

Now we're ready to design a simple packet filter. It's important to understand that no one firewall can meet everyone's needs. You may find these rules too restrictive or too permissive, or maybe your network is arranged differently and you'll have to make adjustments. So don't blindly copy the ipchains rules, try to understand what they do and how they fit into your system.

These notes are targeted at the home user. By that I mean you trust your users and that you're not running any servers for the outside world. Later, we'll see how to loosen the rules to allow running some servers. It's also helpful to not have too many users. If you have thousands of users, then chances are that for every possible protocol, there are some users with legitimate need for that service and you'll end up allowing everything through the firewall. But with only a few users, you can use a tighter set of rules for only the services that you use.

We also assume that if you have a local network, then your Linux machine is the gateway between your network and your Internet provider. That is, your Internet connection is on your Linux machine and packets must go through the Linux machine to get from your network to your ISP. It's important to filter the packets on the gateway machine and to not allow packets to bypass it.

We build the firewall in five sections, working from the inside of the machine outward. The first two sections cover the inside world that we trust: the loopback interface and the local network. The last three sections cover TCP, UDP and ICMP with the outside world: the Big, Bad Internet. These last sections provide the main filtering that determines what protocols we do and do not allow. The first two sections prevent an outside machine from masquerading as an inside machine and free the local network from the restrictions of the later rules.

4.1 Loopback Rules

The loopback interface (lo) is a virtual network interface that connects a machine to itself. This allows network programs to communicate through a single, uniform interface, rather than making a special case for the local machine. All Unix machines have a loopback interface and it always uses address 127.0.0.1 (localhost).

The packet-filtering rules are simple. We allow all traffic over the loopback interface and block any other machine from pretending that it is this machine. The 127.0.0.1 address is valid over the loopback interface, but not through any other interface.

   ipchains -A input  -i lo -j ACCEPT
   ipchains -A output -i lo -j ACCEPT
   ipchains -A input  -s 127.0.0.0/8 -j DENY -l

The syntax 127.0.0.0/8 matches any IP address that agrees with 127.0.0.0 in the high-order 8 bits. In Unix wild-card notation, that means 127.*.*.*, but the *-notation is not legal syntax for ipchains, so we use /8.

4.2 Local Network Rules

Section two covers a local network. If you have just one machine, then skip the rules in this section. Also, these rules are for the gateway machine on your network. That is, we assume your Linux machine has one connection to your ISP and a separate interface for your local network as in the picture below. And let's assume that your Internet connection is via a modem (ppp0), that your local network is an ethernet (eth0) and that your network address is 192.168.1. Your numbers may be different.

                     +---+ lo0
                     v   |
             ppp0  +-------+  eth0        192.168.1.*
   ISP ------------| Linux |-------------------------------
                   +-------+         |         |         |
                                     O         O         O

As in the loopback section, these rules do two things. First, they allow all traffic within the local network, thus exempting it from the restrictions of the later rules. And second, they prevent an outside machine from spoofing your address and pretending to be inside your network. You can do this only on the gateway machine and only for addresses for which you have physical access. Packets with a 192.168.1.* source address are legitimate if they arrive over eth0 and are bogus if they arrive over ppp0. And by using the interface option (-i), we can tell the difference.

   ipchains -A input  -i eth0 -j ACCEPT
   ipchains -A output -i eth0 -j ACCEPT
   ipchains -A input  -s 192.168.0.0/16 -j DENY -l
   ipchains -A output -s 192.168.0.0/16 -j DENY -l

This last rule prevents your local network addresses from leaking out to the Internet. The 192.168.*.* addresses are for private networks only and should not be used on the Internet. Thus, we use ipchains not only to protect us from the Internet, but also to protect the Internet from us!

But these rules are only half the story. They allow communication within your network and between your Linux gateway and the Internet, but not from inside your network out to the Internet. The problem is the internal network addresses. You either need real IP addresses that your ISP knows about (not 192.168.1.*), or else you need to masquerade your internal addresses. IP Masquerading, also called Network Address Translation, really deserves a section all to itself. But the rules are so short that we can give them here.

   ipchains -A forward -s 192.168.1.0/24 -i ppp0 -j MASQ
   ipchains -A forward -j DENY -l
The first rule masquerades packets originating from 192.168.1.* that leave over the modem. The -i flag on the forward chain refers to the outgoing interface. The last rule, blocking all other masquerading, is very important. Without it, ipchains would masquerade incoming packets, and that would be a serious security hole.

4.3 Outside TCP Rules

The last three sections for TCP, UDP and ICMP form the actual filter between you and the Internet. We cover Transmission Control Protocol first, although you could put them in any order. Most common network services such as Telnet, FTP, HTTP, POP mail and Sendmail use TCP, and by far the majority of your packets are TCP. That makes this section the most important part of the filter. It's also the section that requires the most fine tuning for your own needs.

TCP uses an explicit connection between the client and server. Before any real data is sent, the client sends a setup packet (SYN) to the server requesting the connection. In ipchains, the -y flag matches the SYN packet. Subsequent packets are called established and don't match the -y flag. Now, the normal way to filter TCP is to allow all established packets and to block the SYN packets of the connections that you don't want. It's not ideal allowing all established packets because they can lie. But a packet claiming to belong to a connection that doesn't exist will normally just get dropped before it can do any harm.

Although the TCP rules require some attention to individual needs, a good place to start is to allow only outgoing connections. This assumes that you don't run any servers outside your local network.

   ipchains -A input  -p tcp --dport 1:1023 -j DENY -l
   ipchains -A input  -p tcp --dport 6000   -j DENY -l
   ipchains -A output -p tcp --sport 1:1023 -j DENY -l
   ipchains -A output -p tcp --sport 6000   -j DENY -l

   ipchains -A input  -p tcp ! -y -j ACCEPT
   ipchains -A input  -p tcp -f -j ACCEPT
   ipchains -A input  -p tcp -j DENY -l
   ipchains -A output -p tcp -j ACCEPT

The first four rules block outside access to the privileged ports (1-1023) and to X windows (6000). If you don't run any servers, then you may as well deny access to those ports. The X server can use up to 64 ports (6000-6063), but if you use only one video card, one monitor and one server (and DISPLAY is :0.0), then the port is 6000. X windows is a network windowing system, and it is dangerous to allow untrusted parties to talk to your X server.

The last four rules allow all outgoing packets and established incoming packets, but not incoming setup packets. The -f flag matches IP fragments. Sometimes packets must be broken into pieces, and the second, third and subsequent pieces are called fragments. The problem with fragments is that only the first piece contains the port numbers and SYN bit. The remaining pieces will never match any rule that specifies a port, thus bypassing the main filtering rules. Again, it's not ideal to allow all fragments, but you don't have a lot of choice.

Note that these rules are intended for packets between you and the Internet, but we don't specify an interface (-i) or source address (-s). We can do this because the previous sections cover the inside network and the only interface left is to your ISP. If your network is arranged differently or if this is not your gateway machine, then you should explicitly restrict the TCP, UDP and ICMP rules to outside traffic.

Also, these rules interfere with FTP because FTP opens its data connection (port 20) from the server side, a firewall unfriendly arrangement. But there is a simple workaround: use passive mode. Passive mode tells FTP to open all TCP connections from the client side. Or, you can use Netscape for FTP because Netscape automatically uses passive mode.

4.4 Outside UDP Rules

The other type of data packet is the User Datagram Protocol. UDP packets are small, independent and self-contained. They don't have a setup packet and don't use an explicit connection like TCP does. Name Service (DNS), the Network File System (NFS), the Network Time Protocol (NTP), talk and traceroute use UDP.

You certainly need Domain Name Service (DNS) with your Internet Provider, but you can probably get by without any other UDP protocols. DNS servers use port 53, so assuming your name server is 10.1.2.3 (replace this with the actual address), these rules allow DNS with your ISP and block everything else. Also, you probably have two or three name servers, and you should add rules for each of them.

   ipchains -A input  -p udp -s 10.1.2.3 53 -j ACCEPT
   ipchains -A output -p udp -d 10.1.2.3 53 -j ACCEPT
   ipchains -A input  -p udp -j DENY -l
   ipchains -A output -p udp -j DENY -l

The UDP rules are very tight, which is good. But they do block NFS version 2, NTP, talk and traceroute. NFS is for sharing files and you really should not allow it across your firewall. Talk is a poorly structured combination of UDP and TCP and cannot be filtered safely. NTP servers use port 123, so if you want to synchronize your clock with NTP, then add rules (similar to the above) that allow UDP packets with port 123 on your NTP server. Traceroute uses outgoing UDP packets with destination ports 33434 through 33523 and incoming ICMP types 3 and 11.

Technically, these rules also block UDP fragments because fragments do not have port numbers. But DNS packets are small and there are few hops between you and your name server, so it's unlikely that DNS packets will ever be fragmented. But if you find that legitimate fragments are being blocked, then you will have to allow them like we did with TCP.

4.5 Outside ICMP Rules

And finally, the last section covers the Internet Control Message Protocol. ICMP handles flow control and reports on misdirected or malformed packets and other error conditions. It doesn't carry actual data. Here is a partial list of common ICMP types.

    0  echo reply (pong)              5  redirect
    3  destination unreachable        8  echo request (ping)
    4  source quench                 11  time-to-live exceeded

As a home user, you should certainly block redirect (5) messages. A redirect tells your computer to change its routing tables. But if you have only one connection to the Internet through your ISP, then you should never receive a legitimate redirect message. You probably should allow destination unreachable (3) messages. These are error messages reporting that data packets failed to reach their destination. But as a home user, nothing too terrible would break even if you blocked all ICMP packets.

A reasonable design is to allow destination unreachable (3), source quench (4), incoming time-to-live exceeded (11) and outgoing pings (8 and 0). We can do that with the following rules. Unfortunately, the syntax does not allow multiple types per rule.

   ipchains -A input  -p icmp --icmp-type  0 -j ACCEPT
   ipchains -A input  -p icmp --icmp-type  3 -j ACCEPT
   ipchains -A input  -p icmp --icmp-type  4 -j ACCEPT
   ipchains -A input  -p icmp --icmp-type 11 -j ACCEPT
   ipchains -A input  -p icmp -j DENY -l

   ipchains -A output -p icmp --icmp-type  3 -j ACCEPT
   ipchains -A output -p icmp --icmp-type  4 -j ACCEPT
   ipchains -A output -p icmp --icmp-type  8 -j ACCEPT
   ipchains -A output -p icmp -j DENY -l

And finally, since this is the last set of rules, we block everything else.

   ipchains -A input  -j DENY -l
   ipchains -A output -j DENY -l

5. Firewall Maintenance

After installing your firewall rules, you still need to monitor them. First, you want to detect break-in attempts as early as possible. Even if a hacker finds a way to break in, he will probably have to probe your system several times first and this will show up in the log files. Second, depending on the programs you run, you may find these rules too restrictive and they may block legitimate packets. In this case, you will need to track down what ports and connections your program needs and add rules to allow them.

Check the file /var/log/messages for logged packets (-l flag). You can even use xbiff (-file option) to continually look for changes in this file. Also, ``ipchains -L -v'' gives verbose reporting on how many packets and bytes have matched each rule. This is very useful in identifying your network's traffic patterns. And that's another virtue of packet filters. If you regularly use these commands to monitor your system, you will definitely learn a lot more about ports, services and networking.


Copyright © Mark W. Krentel, May 2000. These notes are provided in the hope that they will be useful, but WITH ABSOLUTELY NO WARRANTY OF ANY KIND. I am not a professional network security engineer, I just read the documentation and summarize it as best I can.

$Id: ipchains.html,v 1.2 2003/08/13 05:33:02 krentel Exp $