By Chad Perrin
Basics of firewalls
Every new Linux user will at some point start wondering about installing a firewall application on this strange new operating system. Eventually everyone also runs across the concept of iptables. Early on, one might not know what iptables means, or even have heard the term. This is quite a disappointing state of affairs, considering how important good iptables management is to tight security in a Linux networking environment.
I'm going to assume you've heard of a firewall and have some vague notion of what that means in relation to computers and networking. As a quick summation, though, a firewall basically provides and enforces rules for allowing or denying network access on specific ports, from or to specific networked computers, and so on. Most Windows users, when they think of a firewall, think of Windows Firewall, ZoneAlarm, Norton Firewall, or what they tend to refer to as a "hardware firewall", such as the many rinky-dink router appliances that can be purchased from Circuit City, Best Buy, and so on.
Windows Firewall and ZoneAlarm (along with a slew of others), sometimes called "software firewalls", are in fact little different in concept from the hardware firewalls. In point of fact, the biggest difference between the concepts is the security that each provides. Because the software firewall is on the local system, it provides a reduced security potential: by the time unauthorized traffic touches the software firewall, it has already touched the system you're trying to protect. That doesn't mean you shouldn't use such a thing, however: it's just an extra layer of security, and used properly it can enhance the overall security of your network. You should never, ever consider it a substitute for a separate (hardware) firewall, however.
Applications such as the Windows Firewall and ZoneAlarm are really pretty low quality, as firewalls go. Even ZoneAlarm Pro (much better than the free version, and staggeringly, incredibly, frighteningly better than the deeply flawed Windows Firewall on Windows XP systems) is not great as a firewall. Norton Firewall is better in some ways than the above, in that it is capable of providing better security, and worse in others, in that it is difficult to configure, hides much of what it's doing even worse than ZoneAlarm (and on a par with Windows Firewall), and in general has the potential to seriously screw things up without ever giving a hint to the user aside from "Oh, it's probably Norton again."
Ultimately, the major problem with all of these popular software firewalls on Windows systems is that they do not operate at a low enough level to provide really significant security. There are a couple of firewall applications for Windows that do provide a more fundamental firewalling capability, making use of Windows kernel socket APIs, but the Windows OS design and driver APIs provide for potential "leakage" that even these Windows socket-layer firewalls (such as the iSafer Winsock Firewall) can be worked around by a clever security cracker, depending on the sort of hardware you're using for network connectivity, what drivers you're using, and so on.
Ultimately, the problem with these Windows-based firewalls is that they're software that sits on top of the OS trying to get the OS to relinquish control of network packet control earlier than it really wants to so that the traffic can be filtered effectively.
Free UNIX firewalls
Free versions of UNIX tend to have a much better packet filtering model. Linux, for instance, has the netfilter project, which works on kernel-integrated network traffic filtering. The management system for that, which handles filtering rules for netfilter to apply and enforce, is called iptables. The OpenBSD analog to iptables, meanwhile, is called pf, and there are a number of cited advantages and disadvantages to each in comparison to the other.
In any case, it happens that iptables and pf both work extremely well as firewalling systems. While I haven't done an exhaustive survey, I'd say that probably at least half of the little hardware firewalls you run across in retail electronics outlets are in fact running a stripped-down embedded Linux kernel with netfilter, some running iptables and some running some wacky hybrid thing that replaces iptables just to make everything work differently somehow—probably to frustrate the efforts of people who would like to have more hands-on control of how the router/firewall appliance is working behind the scenes. Regardless, if you've used a store-bought router/firewall appliance, there's a reasonable chance you've used something running iptables for firewalling, even if you've never installed Linux on anything.
Because of the open, modular design of Linux (and other free versions of UNIX, for that matter), kernel-integrated network packet filtering can be easily implemented and has improved over the years. This allows for a very close marriage of the firewalling capability of such OSes with the network interface itself, providing a basically impenetrable security model, in theory.
In theory, of course, theory and practice are the same; in practice, they are not. The security you can get from this security model, in practice, depends on your ability to effectively define firewall rules and the flexibility and functionality of the filtering rules management system—in this case, iptables.
There was a predecessor to iptables called ipchains. From what I've seen thus far, it looks like ipchains differed from iptables mostly in that it was a little more difficult to configure and manage, and in that it was stateless, whereas iptables is stateful. That means is that iptables can actually apply firewall rules based on the current state of network traffic: rules can exist that depend upon the amount of traffic you're receiving on a specific port, for instance, rather than simply blocking or opening that port across the board. This makes iptables much, much more useful for ensuring system security than ipchains. Interestingly enough, ZoneAlarm is also stateful in a very limited fashion, but its statefulness is largely unconfigurable and the benefits of its stateful operation can be circumvented by automated scripting, if the person writing the scripts knows what he or she is doing.
Basics of iptables
It's always a good idea to have a decent iptables configuration in place on a given machine, regardless of outside firewalls you may have. Each individual machine, depending on its uses, might have different packet filtering needs. As a result of this, the external firewall device should have a configuration that permits everything the most permissive of your local systems is going to need to do: each individual machine, then, can deny as much of that as it can get away with, without sacrificing its critical functionality.
When you first set up a Linux machine, it should have an iptables configuration of some sort already in place. That might consist, in some distributions of Linux, of a pages-long set of complex rules designed to allow you to make use of hundreds of applications and services that you probably won't ever touch: this is the approach that "kitchen sink" distributions like Mandrake have taken over the years. Minimal systems have a tendency to just set everything to "allow", giving an extremely simple, but essentially pointless, configuration with the assumption that the user will do something to change that. This is the equivalent of installing the security software without configuring it to do anything at all. In practice, the kitchen sink approach tends to be no more effective a security measure than the unconfigured approach.
There are user interface tools that can be used to manage your system's security profile in a higher-level, more abstracted, more "user friendly" manner than hacking iptables configuration yourself from the shell with individual table definition and rules management commands. For a comprehensive treatment of how iptables can be configured directly, you can have a read through the manpage for it simply by entering "man iptables" at the command line.
These user interface firewall management tools, on the other hand, include both CLI-capable tools like Bastille, and GUI tools with pretty colors and clicky buttons like KDE's Guarddog. The browser-interface service management system known as Webmin is capable of iptables management as well. There are even Linux distributions whose whole purpose is to provide a GUI front-end to iptables with a fair amount of configurability, reasonably sane default configurations, and integration of the configuration interface with that for routing services and other functionality commonly implemented on a network firewall box.
I find that it tends to provide a greater understanding of, and thus better ability to manage, network and system security to work with iptables directly instead. It is to the task of making CLI iptables management simple and easy that I devote this document.
Simple iptables management
When you're managing a single computer for personal use, and you're about to do something you've never done before, it helps to have a good step-by-step system for getting things going easily and simply. It's good to have a system that makes it easy to improve upon the initial setup as you learn more, too. Perhaps most importantly, it's good to know how to undo the damage if you screw something up.
For those reasons, among others, this brief tutorial will focus on iptables management by way of three very convenient and useful commands and a text editor of your choosing. Those commands are iptables, iptables-save, and iptables-restore. As for the text editor, my favorite is vim: your mileage may vary.
When you're managing a network of many users (and by "many" I mean pretty much anything more than five, really), you start needing to be able to reduce workload significantly by automation and reducing repetitive actions across multiple computers. A standard iptables configuration that can be deployed as-is across all workstations on your network becomes an important part of the process of network administration, to reduce the amount of time you have to spend on getting every system on the network within operating requirement specifications for security and manageability.
It is also for reasons related to easy deployment of a standard configuration, easy enforcement and definition of company security policy, and centralized firewall configuration testing, among other reasons, that this brief tutorial will focus on iptables management by use of iptables, iptables-save, and iptables-restore commands—and, of course, a text editor of your choosing.
The first thing to do, of course, is to save your original iptables configuration so that you can revert to it easily at a later time if you run into trouble. Doing this is simple and pretty brainless, but it does require you to make some decisions. You can get a look at your current iptables configuration by simply entering "iptables -L" at the command line, but that doesn't really offer much other than a glimpse into the current configuration. To actually save the configuration in a way that can be reused later, you need to use the iptables-save command. Entering "iptables-save", with no arguments, produces output that defines the iptables rules that are implemented when netfilter is run on your system. This is exactly the data we want saved.
The first decision you'll have to make is where to save your iptables configuration files. One option, which is recommended in a number of howtos, is to use (or create) a directory at /var/lib/iptables and store your configuration files there. Another is, since you're the root user when configuring iptables, to save them in the /root directory or some subdirectory of that.
In either case, you'll have to make sure that the directory path and the filename you choose will not be so obscure that you'll later forget them, or forget how to find them. If it ends up in a directory called iptables, it might be sufficient to save your previous iptables configuration file as saved.cfg. If not, you might want to save it as iptables.saved or, if you're really attached to three letter filename extensions, perhaps iptables.bak.
Assuming for the moment that you're saving the file at /var/lib/iptables/saved.cfg, the way you'd save the initial configuration is this: first, navigate to that directory (using a command such as cd /var/lib/iptables), then enter the command iptables-save > saved.cfg.
In case you're not familiar with it, the > character is an extremely useful and widely used shell operator known as a "redirect". It essentially takes the output of the command on the left side and sends it to the file named on the right side of the redirect operator. There is also a left-facing redirect, <, which I will use later. Essentially, it does the opposite (as one might guess): it takes the contents of the file named on the right-hand side of the redirect, and sends it to the command on the left-hand side as its input. By running iptables-save > saved.cfg, you are basically just making a file that contains nothing but the output of the iptables-save command.
Later, if you feel you need to undo everything you've done to your iptables setup, and get it back to the distribution's default (assuming that's the state it was in before you started mucking with it), you can simply enter iptables-restore < saved.cfg, or iptables-restore < /var/lib/iptables/saved.cfg if your current working directory is not /var/lib/iptables.
Armed with this knowledge, you actually already have most of the information necessary to implement the iptables management system I'm describing, though I won't be so cruel as to leave you to figure it out on your own. The point of this, after all, is to make the initial implementation, and later self education, as easy as possible.
The next thing I do, after saving a backup of my original iptables configuration on the new system, is flush my iptables setup with:iptables -F -t filter
iptables -F -t nat, and
iptables -F -t mangle
The first of these three command strings does not technically require the use of the "-t filter" argument, since the filter table is the default target of the iptables command, but it doesn't hurt to be explicit. To find out more about the various tables in an iptables configuration, you can refer to the manual pages.
You likely won't have to actually perform this task yourself, though doing so won't hurt anything. The reason I do so at times is that, now and then, I essentially need to start building an iptables configuration from scratch and find it easier to do this than start with a template configuration like the one I'm going to provide later on.
This, of course, means that my next step would be to use all the iptables rules manipulation knowledge I've gleaned from the manpages and other references over the time I've been working with Linux to produce a worthwhile, secure iptables configuration. You'll benefit from that by receiving, free of charge and effort, such a reasonably secure configuration template within the next few paragraphs.
After testing that configuration out for a little while, I output it to a file using iptables-save. I use a redirect to save the output to a filename such as saved.std or iptables.std, with "std" being an abbreviation of standard. Thus, I have my standard, generic iptables workstation configuration template clearly marked.
Once I have that file saved, I can edit it to contain the iptables rules I desire for that specific workstation at my leisure, and use the iptables-restore command to load them. By default, iptables-restore will flush your iptables before rebuilding them, so you don't have to worry about flushing the tables before using that command to ensure that everything works the way you want it to.
Now, if that save.std file of mine is something I will want to reuse for a lot of other computers, I should save a copy of it somewhere convenient. Then, to apply it to a new computer, all I have to do is copy it to the new system and, on that system, run iptables-restore < saved.std just as I would to restore my backup of the default configuration on the first system. This completely replaces the iptables configuration in use with the new configuration, and even restarting the computer doesn't undo the change. It stays put until you change it again.
Here's an example template (Listing A) of a saved.std file for you, with some explanation:
:PREROUTING ACCEPT [48436:11233990]
:INPUT ACCEPT [48436:11233990]
:FORWARD ACCEPT [0:0]
:OUTPUT ACCEPT [29730:6162034]
:POSTROUTING ACCEPT [29730:6162034]
:PREROUTING ACCEPT [391:49336]
:POSTROUTING ACCEPT [1793:110951]
:OUTPUT ACCEPT [1793:110951]
:INPUT DROP [0:0]
:FORWARD DROP [0:0]
:OUTPUT ACCEPT [1418:147349]
-A INPUT -i lo -j ACCEPT
-A INPUT -m state —state ESTABLISHED -j ACCEPT
-A INPUT -p tcp -m tcp —dport 22 -j ACCEPT
-A INPUT -p tcp -m tcp —dport 631 -j ACCEPT
-A INPUT -p all -s 127.0.0.1 -d 127.0.0.1 -j ACCEPT
-A INPUT -j DROP
-A OUTPUT -o lo -j ACCEPT
-A OUTPUT -p tcp -m tcp —sport 22 -j ACCEPT
-A OUTPUT -p tcp -m tcp —sport 631 -j ACCEPT
This iptables configuration essentially causes your computer to pretend, first off, that any incoming connections that have not been solicited by you just don't exist. It also does the same for forwarding packets. This is a generally good policy, using the :INPUT DROP and :FORWARD DROP defaults. Exceptions can be created later for specific ports, addresses, and so on.
Meanwhile, for purposes of ensuring you don't forget to allow something that your user can do, you should probably use an :OUTPUT ACCEPT default, operating under the assumption that connections initiated by the user are less likely to be a security threat than those initiated from outside the system. A more secure way to configure it is to use :OUTPUT DROP with exceptions defined for behaviors you want to allow, but that can get prohibitively difficult with end-user client systems that must perform a wide variety of networking functions.
As such, though it is possible to create this more secure configuration and to manage it well, such is a task too varied in its requirements from one case to another to be included within the scope of this article. Consider this configuration that I have provided to be the "good enough" solution until you've learned enough more about iptables to change the configuration to suit your specific, individual needs. It will provide drastically increased security over most default configurations without interfering with your ability to get work done.
-A INPUT -i lo -j ACCEPT allows your system to accept all incoming requests that originate at your own network adapter. This is useful for things like testing your network configuration by pinging yourself, getting local system mail delivered (like when your computer wants to tell you something broke), and so on.
-A INPUT -m state —state ESTABLISHED -j ACCEPT takes advantage of the stateful packet filtering capabilities of iptables to allow you to function quite flexibly with default DROP policies for incoming packets. This line basically states that any connection that is initiated by you will be allowed to continue, circumventing the DROP policies for incoming packets related to already established connections. Otherwise, you might be able to send data to another server, but never know whether it got there because when the server tried to reply your firewall would just drop the packets without comment.
-A INPUT -p tcp -m tcp —dport 22 -j ACCEPT allows incoming SSH connections. You might want to change that 22 to another, nonstandard port number, and ensure that anyone that is supposed to have remote SSH access to the computer in question knows to use a different port when making such connections.
For a system to which nobody should ever have remote access, you should remove this line from this file before using it, but it's my experience that remote SSH access is one of the tools that makes secure management of a distributed network possible, and thus most people who aren't on stand-alone systems like a home desktop on a network of one computer will probably want to have some kind of remote SSH access to the system possible. When you're more well versed in iptables in the future, you might consider replacing this line with several lines that define what sources are valid for attempts to connect to the computer remotely, so that even on a nonstandard port no system cracker is going to be able to use SSH to get in from the wrong source (such as an incorrect IP address, et cetera), but that's well beyond the scope of this article.
-A INPUT -p tcp -m tcp —dport 631 -j ACCEPT allows for connectivity with network printers using the Common Unix Printing System (CUPS). If that's not something you have to worry about, delete this line. More complex, more secure iptables rules than this can be used to do the same thing, but this is a decent starting point.
-A INPUT -p all -s 127.0.0.1 -d 127.0.0.1 -j ACCEPT is another "allow me to talk to myself" line.
Based on the INPUT explanations I've just given, the OUTPUT lines should be fairly self-evident, specifically allowing certain network activities involving outbound connections to occur. A lot more can be done with iptables to ensure system security, but I'm only aiming to provide a starting point for managing iptables configuration. This very short, simple configuration is superior, security-wise, to every single distro-default iptables configuration I've ever laid eyes on, despite the fact they're typically a hundred lines long, give or take—-except when they're empty or simply nonexistent.
Summary of implementation
To make it simple, here's what you do with this information:
- Save your current iptables configuration by entering the command iptables-save > /var/lib/iptables/saved.bak (or something similar for the filename and path) so that you can undo changes later if you wish.
- Save the iptables configuration file I provided above as /var/lib/iptables/saved.std (or something similar).
- Configure your current iptables configuration using saved.std by entering a command like iptables-restore < /var/lib/iptables/saved.std.
Then, if you later decide you want to make changes to one system, but not others, you might copy saved.std to saved.loc, for instance. You can then edit the iptables configuration arguments in the new file to reflect the new configuration requirements, and then use iptables-restore to implement the changes on the local system.
For large networks, push scripts or network node cron job pull scripts can be used to place any new versions of the saved.std file on those machines, they can even be implemented automatically—though I recommend not allowing local scripts on workstations to alter iptables configuration, as this might introduce security vulnerabilities. Rather, a push script run from a central location on your network that places the new configuration file and runs iptables-restore might be a better option for cutting down on the amount of administrative overhead.
This is all just the beginning of a serious company-wide security policy relating to firewalls on local systems. More can be done, and in larger networks more really must be done to reduce the amount of work involved. From here, however, you can definitely make a good start on the next best thing to an impenetrable network.
Have at it, and good luck.