September 10, 2004
November 12, 2004 (revisions) May 8, 2005 (more revisions)

Two-Faced: Setting up a Simple Linux Firewall

(Debian GNU/Linux, Shorewall, and you)

This is an overview of the things I think you need to know if you're going to try and set up a simple firewall using linux on an old PC (really it's an excuse for me to whine about the hassles I went through doing this, but I've got to get something out of it).
But why?    First of all, ask yourself why you're doing this. You can buy a simple off-the-shelf firewall/router gadget pretty cheap these days, and I hear at least some of them have fairly easy-to-use administration features (e.g. via a web browser). Saving money by using that old PC you've got kicking around is unlikely. Acceptable answers include "learning experience" and "insanity".
ABCs of Hardware:   For a PC to be used as a firewall, it needs two ethernet cards (aka NICs or "Network Interface Cards"). If you need to buy a second card to do this with, see the above point. Ditto if you need to buy a 10baseT hub.
When you plug ethernet gadgets together you should see pretty green lights come on. This is a hardware level check, and should be looked at before you touch the software.
Some caveats:   Don't assume that a firewall is the ultimate in security. It is true that if the security of some of the boxes on your internal network is poor, then the firewall might help cover for this, but it's not a substitute for trying to improve your internal security. Also, the kind of "two-interface firewall" I'm discussing here (see Diagram, below) may be a too simple: arguably the webserver should be in a "DMZ", hung off the firewall on a third ethernet card (see the quickstart for Three-Interface Firewall and possibly the Shorewall FAQ).
UPDATE:   The approach I settled on here is somewhat, shall we say, "controversial". I've since been told that netfilter and Shorewall both do in fact work with the Debian stable branch (aka "woody"). It was a mystery to me for some time as to why I couldn't get them to work. My central problem was that I thought that Debian releases had specific associated kernel versions. I thought that Woody came with the 2.2 kernel and if you needed the 2.4 kernel for something, you had to switch to Sarge. As it turns out, Woody just defaults to a 2.2. kernel, but also comes with an option to use a 2.4 kernel. You just need to know the magic incantation "bf24" to use in response to the "boot:" prompt.

Also, one of my premises behind switching to the Debian testing branch (aka sarge) was that sarge was just about ready to become the new stable -- then I heard about the delays and only now (six months later) are we hearing about an imminent release. So as it happens, using sarge was a bigger gamble than I thought; abandoning Debian stable for a security related use is not to be done lightly (the security team releases updates only for the stable branch).
Think "iptables", aka "netfilter":   The kernel developers can't make their minds up about how to do ip routing with linux, so the documentation is always out of date. Over the years, the Right Way to do it has progressed through:
  1. ipfwadm
  2. ipchains
  3. iptables (i.e. netfilter).
If you're reading something that talks about the first two items on that list, you should stop. The iptables/netfilter mechanism has been stable for a while now, and it seems to work, so maybe they won't throw it away in six months (*knock* *knock*).
(Some pointers to things that are worth reading are collected below in references).
Debian:   I strongly suggest using the Debian GNU/Linux distribution (see the Why Debian? section below).

If you don't know what the following things mean, you should read up on them before messing with Debian:
  • /etc/sources.list
  • apt-get install <package name>
  • dpkg -l 'foo*'
  • apt-cache search '<some key phrase>'
  • apt-get clean
By the way. The Debian trick called "Pinning" is pretty dubious, though I'm using it anyway. See: Apt-Pinning for Beginners. Me, I've got a two-level set up pinning sarge and sid together, where sarge takes precedence. The problem with this, of course, is that you can get incompatible versions of things installed on the same box. However, my expectation is that nearly any package I apt-get will come out of sarge, but that on occasion there may be some small utility (perl modules? emacs lisp code?) that will only be in sid. If these don't work, it's not going to be a big deal. Of course, doing pinning on a firewall is probably at least mildly dumb, so I may strip this "feature" out later: the key reason I included it is that the shorewall "quickstart" docs presumed the presence of the "iproute" package, and that was sid-only back in September of 2004.
Automating iptables set-up: Shorewall:   Don't try and set up iptables manually. I realize that it sounds pretty simple to do it when you read about how they work, but I guarantee it'll be a worse problem than you think. Use some software to set up netfilter for you.

I started learning to use gshield once, and I liked it quite a bit. But I couldn't find a Debian package for it, though web searches revealed announcements that it had been accepted by Debian a few years ago. I went around on this for a while, before concluding that it had been dumped from Debian without much fanfare. I guess the maintainer has abandoned it, and no one has picked it up, which means people are voting with their feet for using something else...
I'd say the winner is Shorewall. Shorewall requires a 2.4 kernel compiled with the "netfilter" options, which rules out Debian stable, aka woody -- (or so I thought... see the above "Caveats").
   Shorewall comes with a daunting quantity of documentation. The one you want to read for the purpose at hand is the quickstart guide: Basic Two-Interface Firewall (or as I like to think of it, "2face"). The extensive comments in the config files may be all you need for further reference. Of course, there's also a daunting quantity of config files, but you don't need to bother with many of them. Most likely you'll just work with these (found in /etc/shorewall):
  • zones
  • interfaces
  • shorewall.conf
  • masq
  • policy
  • rules
By the way, for those of you who swing that way, there is a GUI interface available to work with Shorewall: it's supported by Webmin. I don't know what the big deal is about this myself... Would you want someone setting up your firewall who needed a GUI to do it? Anyway, I'm inclined to agree with this fellow on the subject.
The Debian upgrade process:   Surprisingly enough, despite it's reputation for easy upgradability, I ran into some rough spots in the Debian upgrade process. I started off my installation with some Debian 3.0 CDs, which means I ended up with woody, and needed to switch to sarge (or so I thought... etc.). My understanding was that the way to go on this was just these steps:
edit /etc/sources.list
apt-get update
apt-get dist-upgrade
... and then reboot.
But I had continual troubles with the puny disk resources on the old Compaq 2000 I was trying to do this on. Throughout this entire project I kept having to do various things to clean up space (okay, first I'll blow away /usr/share/doc, that'll give me some wiggle room to run dselect and get rid of anything related to X, gnome, KDE... and I was excited to discover apt-get clean).

Many mysterious (to me) things kept happening: dist-upgrade or dselect would give me more stuff than I expected, I'd run out of disk again (once right at the end of an hours long kernel build!), and I'd have to run around again cleaning up.

Originally, I had some naive hopes that "dist-upgrade" would do everything for me, showing the superiority of the Debian Way to the classic Redhat kernel RPM gotcha. But once again, I found myself stuck rebuilding a kernel from source, though a little reflection shows why this is probably necessary: there are hundreds of kernel build option settings, so getting a single generic kernel that would keep most people happy is probably a near impossibility, kernel modules or no.

Oh, by the way: don't forget to reboot after you do a "dist-upgrade", or you may find yourself wondering what happened to your network connection (yeah, yeah, I know this is linux, maybe you don't really need to reboot -- you could carefully restart all processes that need to be restarted and skip the ones that don't -- but come on, life is too short.) Actually, in view of the need to build your own kernel next, I was impressed that you can just reboot. A complete dist-upgrade does not hork the system, even if you're running with a stale kernel.
Kernel builds under debian:  
Start with this to find out what source packages are available:
apt-cache search kernel-source
Pick one out of the list, and get it in the usual way:
apt-get install kernel-source-2.4.27
But you also need to do this:
apt-get install libc6-dev
And while you're at it, you should probably do something like this (to make sure your tools are up to date):
apt-get install binutils e2fsprogs gcc make
apt-get install modul-init-tools procps util-linux
apt-get install kernel-package expectk
(I like this better than checking the versions yourself, as recommended in some places. Doing another "install" does nothing if you've already got the latest, so why not?)
Then the build process:
First we unpack
cd /usr/src
tar xvfj kernel-source-*
Update the symlink
rm linux
ln -s /usr/src/kernel-source-2.4.27 /usr/src/linux
Edit the file
Updating the EXTRAVERSION line with the current date in the form -YYYMMDD, in my case:
EXTRAVERSION = -20040906
And then it's the old familiar:
make config
(Though, maybe I should've tried "menuconfig". Note: "xconfig" wasn't an option: no X.) Anyway, don't forget to say "yes" to CONFIG_NETFILTER. A (possibly) relevant excerpt from my .config is included below

After that the Debian Way begins to help out again:
make-kpkg buildpackage -rev Custom.1 kernel-image
dpkg -i kernel_image-2.4.27_Custom.1-i386.deb
And then it's time to reboot.
No libc6 dependencie for kernel source:   What was really shocking here was that the Debian package system doesn't cover a really important dependency: just installing a kernel-source package doesn't give you the libc6 package that you need to build it, so the build fails with some misleading error messages (it says it can't find various *.h files, but if you dig around it looks like it's all there). This is evidentially a known problem... too bad it hasn't been fixed yet. Anyway, you need something like that explicit apt-get install libc6-dev instruction up there to get it to work.
Another problem: the handling of configuration files. I don't remember the order of events at this point, but somehow I started hacking on Shorewall version 1.2, but ended up getting a version upgrade to 2.0. That's great, except that there were some big changes in the format of the configuration files, and the Debian installation process is (quite properly) paranoid about over-writing the configuration files that you've already edited. I wasted some time there trying to get Shorewall to work with the wrong vintage files.

Presumably Debian warned me about this during the upgrade, but I don't remember anything like that. And oddly enough, shorewall itself doesn't have a mechanism to check for this version difference and warn you about it.
Joys of Linux Network Admin:  
A Linux system admin gripe:
route -n displays your routing table.
If you want to change it, you use commands like this:
route add -net netmask dev eth0
But when you reboot, you'll lose any changes you make this way. To make permanent changes you need to work on some config files in /etc.

So you need to understand three different representations of the same information.

And just to make it fun, exactly which config files and what changes you need to make vary among the different linux distros.

(In an ideal world, I'd like to see something like an "edit-route" tool that would let you make changes directly to something that looks like the output of the route -n command. Doing a "save" should make the necessary changes to your /etc files.)

But thankfully, with Debian, the changes you need to make to your /etc files are pretty minimal. Mostly you need to work on this file: /etc/network/interfaces
Joys of Linux Admin, part II: ethernet card driver name change:   To get this machine in shape as a firewall, I needed to slap another ethernet card into it. It wasn't particularly difficult to get it working really, I just needed to add an alias line for it to /etc/modutils/aliases and append a few lines for it with the relevant ip address to /etc/network/interfaces (See the section below for the details).

There was one really bad piece of trouble which hopefully you will never run into: they had changed the name of the driver I needed to use. Originally working with woody, I did some research, and figured out that I probably needed to use the rtl8139 driver. Except that that didn't work, and I started wondering what the rtl8139-scyld driver might do, so I tried that on speculation and it did work. (I infer that the "-scyld" suffix implies that it's the latest driver straight from Donald Becker's machine). So then I did my upgrade from woody to sarge, and had a hard time finding the right driver... some web searches turned up the interesting piece of information that it had been re-named as 8139too.

I'm sure there are some fascinating sociological reasons for this sort of name dance, but I really wish the programmers would not inflict this kind of stuff on the outside world.

Anyway, if you need to debug problems like this some day, it's not too difficult to just keep trying different drivers. Stick a name in your alias eth1 <name> line, do a modprobe eth1 and see if it complains.

(Imagine what this job would've been like in the days before kernel modules. Ugh.)
Forget not thy ip_forwarding:   The last problem I had before getting everything working was solved when I was just poking around and noticed in /etc/network/options I had a suspicious looking line: ip_forward=no. I changed that to a "yes", restarted everything, and that was pretty much it. I was somewhat shocked that shorewall didn't make this change for me, and as it happens it's quite willing to do so, except that in shorewall.conf, I still had: IP_FORWARDING=Keep, which evidentially does just what the comments say it does:
  # If you set this variable to "Keep" or "keep", Shorewall will neither
  # enable nor disable packet forwarding.
And I see that the 2face doc tells you about this, I just missed it somehow.

So: don't get distracted by any other documentation (including this web page), read that quickstart.
Don't forget what you don't need:   What's the first thing they tell you about how to secure a linux box? That's right, "Shut off unneeded services." Then your next question is "How do I know what services I need?" and then they lose interest in talking to you.

I took a look inside the /etc/inetd.conf on my brand new Debian Firewall, and the one thing that leaped out at me as being (currently) useless was the smtp / exim line. I don't have a reason to run a mail server on my firewall (and if I did, I'd go with postfix, though exim probably isn't a bad choice either). So should I stick a "#" in front of that line?

But actually, doing a ps ax doesn't turn up anything like "exim"... and in fact it doesn't turn up anything that looks to my eye like I want to get rid of it.

Conclusion: the Debian defaults are not bad, security wise, and my understanding of the Linux boot process needs some fine-tuning. (Reading this might be a good start.)

Appendix: a "2face" example

This is the kind of set-up I'm talking about:

                 |         ADSL, Cable, etc.
                 |         For me this is the
                 |         static ip:
            _____|_____    (which has the domain name
            |          |
            |  Bridge  |
                 |            eth0 is assigned the IP address
          _______|__________  provided by your provider,
          |                |  in my case it's static:
          |     eth0       |  (there're ways to do this with DHCP also).
          |                |
          |   Firewall PC  |
          |                |
          |     eth1       |   eth1 is assigned an internal
          |________________|   IP address, selected from one of
                 |             the ranges used for that purpose.
            _____|_________    I use:
           |               |
           |  10baseT hub  |
              |    |   |
              |    |   |
              |    |   |________________________
              |    |                           |
           ___|    |________                   |
           |                |                  |
           |                |                  |
   ________|_______   ______|___________    ___|____________
   |              |   |                |    |              |
   |   PC         |   |  PC            |    |   PC         |
   |   (Work      |   | (living room   |    |   (Web-      |
   |   station)   |   |  web-terminal) |    |   server)    |
   |              |   |                |    |              |
   | |   |   |    | |
   |______________|   |________________|    |______________|

An example solution: summary of the active lines from my config files

Presented for your edification, these are the significant (i.e. non-comment) lines from every file in my /etc tree that I modified to get my firewall working. Note that my external IP address is, and that much at least would be different for you in your version of the following.

Essentially, I just ran the following command to get this listing. This works because I'm an emacs user, thus any file I change has a backup file with a tilde appended to the name. (Note: this is a "one-liner", with line breaks added for clarity):

  find /etc -type f -name '*~' |
    perl -ne '
       print "\n$_:";
       open IN,"<$_";
       print grep !/^\s*$/, grep /^[^#]/, <IN>
Here's the output, with some uninteresting chunks elided ([...]):

alias eth1 8139too


auto lo
iface lo inet loopback
auto eth0
iface eth0 inet static
auto eth1
iface eth1 inet static



deb sarge main
deb sarge main contrib non-free
deb sid main
deb sid main contrib non-free
deb-src sarge/updates main contrib non-free

APT::Cache-Limit "141943904";

eth0			eth1


eth1		-

DNS_1_IP=  # really, 192.2?
INTERNAL_INTERIM_WEB_SERVER=  # until 3, aka latveria is ready




# Networking options
# CONFIG_IP_ROUTE_TOS is not set
# CONFIG_IP_PNP is not set
# CONFIG_NET_IPGRE is not set
# CONFIG_IP_PIMSM_V1 is not set
# CONFIG_IP_PIMSM_V2 is not set
# CONFIG_INET_ECN is not set
# CONFIG_INET_AH is not set
# CONFIG_INET_ESP is not set

#   IP: Netfilter Configuration
# CONFIG_IP_NF_AMANDA is not set
# CONFIG_IP_NF_TFTP is not set

#   IP: Virtual Server Configuration
# CONFIG_IP_VS is not set
# CONFIG_XFRM_USER is not set
# CONFIG_VLAN_8021Q is not set

# CONFIG_IPX is not set
# CONFIG_ATALK is not set


Distribution Wars

Why Debian?    This is why I used the Debian GNU/Linux distribution (and perhaps it's why you should too):
  1. The Debian package management system (*.deb files, handled by the "apt" suite of tools, apt-get, apt-cache, etc.) works really well to automatically deal with the problem of cascading dependencies. (It could be that the Redhat world is lurching toward a way to get you out of Rpm Hell, but Debian has had one in place for years.)
  2. Because Debian is not a commercial entity, there is no financial pressure on them to add "features". They maintain three flavors of sub-distributions, "stable", "testing" and "unstable", and they remain very conservative with what happens to stable. They back port security fixes to stable long after many a commercial distro would have abandoned the code base.
  3. Because of all of the above, it's fairly simple to make security upgrades automatic. You can make it a near certainty that you won't get nailed on a known exploit (which is the way most machines get cracked). And also, you can be reasonably certain that a security upgrade won't break something else.
Why not Redhat? There are lots of reasons I'm getting away from the Redhat distro. These are inflammatory enough to move from here to "The Whinery": The Whinery: Farewell Redhat
Honorable mentions: freebsd, openbsd, knoppix But maybe you should think about using openbsd or freebsd. Kernel upgrades in Debian are only slightly less painful than in other distros. Maybe the Freebsd ports system would work better? And Openbsd is supposedly designed with security in mind, no code is included without undergoing a heavy security audit. (So why didn't I use Openbsd? To limit the amount of stuff I needed to learn to get the job done. I insist that this is valid, despite the academic attitude you get from many quarters: "Learning is good! You should try and learn everything! What, are you lazy or something?" Realistically, you need to call your shots.)
(Why didn't I use the Debian-based Knopix with it's excellent hardware detection? There's not enough disk on the ancient Compaq Deskpro 2000 I'm using as a firewall. And it's a little light on memory to run X -- a mere 65Mb -- so all that KDE stuff would be doubly useless.)


Linux Networking
More than you will (hopefully) need to know to turn on those pretty green lights:
A really good networking reference. Note the section: "25.7.2 Debian networking scripts" (Read this.):
Introduction to IP
And here's probably more detail than you want (at the moment) on internet routing:
Debian folks are quite proud of their packaging system.
If you need to read up on apt-get and so on, you will find no shortage of information.
This one seems okay:
Installing Debian Software with the Advanced Package Tool
An excellent introduction to the highly dubious Debian "pinning" trick :
Apt-Pinning for Beginners
Not my favorite reference on the subject, but this isn't too bad:
The Very Verbose Guide to Updating and Compiling Your Debian Kernel
An obvious thing to read, though it didn't strike me as being tremendously illuminating:
Debian Reference - Building a gateway with a Debian system
This is the "Quickstart" document for the subject at hand (Read this.):
Basic Two-Interface Firewall - Tom Eastep
Worth a look:
Shorewall FAQ
The main site:
Shoreline Firewall
A slightly more advanced style of firewall:
Three-Interface Firewall
Other stuff
This looks kind of interesting also... didn't find it until I didn't need it any longer:
Ditto for this one, I'm not sure how I missed it (but it's probably just as well... this is the kind of "howto" that if you understand it, maybe you don't need it):
Securing Debian HOWTO
Here's one I skipped on purpose (ipfwadm? ipchains? Could be this needs an update):
Firewall HOWTO
Some books I used (or tried to):
"Learning Debian Gnu/Linux" by Bill McCarty (O'Reilly, 1st ed, 1999)
This has way too much stuff in it I didn't need to hear about again (an Introduction to Bash!), but there's an appendix in the back about dpkg and apt-get that I found useful. This doesn't talk about "apt-cache search" though, which is close to essential, but that's what I get for using ancient books.

"Running Linux" by Matt Welsh and Lar Kaufman (O'Reilly, 2nd ed, 1996)
"TCP/IP Network Administration" by Craig Hunt (O'Reilly, 2nd ed, 1998)
I flipped through these books off and on to try and convince myself I understood routing tables. These books generally gave me the feeling I didn't really understand them, but as it turns out none of my problems were in the "route -n" department.

It may be my imagination, but that basic piece of information about which IP address ranges are reserved for internal network use seems to be missing from "TCP/IP Network Administration". I needed to look it up again in Net-HOWTO, though it's also in: Introduction to IP


This page has received some comments/suggestions/criticisms, etc. I'm going to try and summarize some of them here...
stable has iptables/netfilter and shorewall I thought that iptables couldn't be made to work with the 2.2 kernel and the Debian stable branch (woody). I'm not sure where I got this idea. A contributing factor might be the history of netfilter/iptables:
Finally, the fourth-generation tool, `iptables', and another kernel rewrite occurred in mid-1999 for Linux 2.4. It is this iptables which this HOWTO concentrates on.
And the site leads off with this remark:
netfilter and iptables are building blocks of a framework inside the Linux 2.4.x and 2.6.x kernel.
But hopefully this isn't all that lead me astray, because while stable/woody is based on the 2.2 kernel, one of the significant things about Debian stable is that the security team backports security related improvements. You might not assume that netfilter was backported, but you'd certainly expect that that was at least considered.

Also, stable/woody does come equipped with Shorewall, though it is an older version (1.2 vs. 2.0).

You might wonder if the stock kernel that comes with 3.0 Debian was compiled with netfilter, but evidentally it is:
The standard Debian 2.2 kernel (also 2.2) provides the packet filter ipchains firewall, Debian 3.0 standard kernel (kernel 2.4) provides the stateful packet filter iptables (netfilter) firewall.
And there's a trick that I hadn't even occurred to me to wonder about: you can upgrade to the 2.4 kernel and still run stable:
Note that Debian 3.0 woody allows users to install 2.4 kernels (selecting flavors), however the default kernel is 2.2 (save for some architectures for which kernel 2.2 was not ported). If you consider this a bug consider Bug 145244 before sending it.

Joseph Brenner, 10 Sep 2004