Cooking with Linux: A Few Recipes for Easier Firewalls
François! You call that a security notification? The message must contain more information than “This is a test of the emergency security broadcast system”, if any self-respecting system administrator is going to be able to act on it. Qu'est-ce que je vais faire avec toi? François, what are you looking at? What? Oh, mes amis, forgive me. I did not notice your arrival. We were setting up some new security policies on our Linux servers, and François had written a script to detect my port scans and I was completely distracted.
François! Wine for our friends. Vite! Vite! Come, mes amis. Let me show you to your tables. As you get comfortable, I should tell you about this message my waiter had the system send out. I did a thorough port scan on the system, and all he sends as notification is that old “This is a test of the emergency et cetera” type of message. Not much of a description, non? You know, when I was younger and they had those messages on television, I always suspected that if there had been a real emergency, we would not have gotten the message because those who knew of the problem would already have been in hiding. A little joke, mes amis. Ah, François. Yes. An excellent choice of wine. You will like this one, mes amis, a superb 1995 Montrachet. Please pour, François.
Security, as you know, is very serious business indeed. Every day, we hear of damage caused by viruses, of new exploits through which crackers compromise systems. For those of us in the information technology restaurant business, these are challenging times. We must be ever vigilant. A good firewall, then, is an excellent beginning. But how to do it simply is the question, non?
The simplest way to secure your machine (short of locking it up in the wine cellar with no Internet connection) is to disable all nonessential services from your /etc/inetd.conf file and let your TCP wrapper program control who gets in and who stays out. The three lines in Listing 1 are samples from an /etc/inetd.conf file.
Notice that the third line is commented out. That service (rlogin) is simply not available on this system, while telnet and FTP are. Now, the /usr/sbin/tcpd you see is actually the path to the TCP wrapper program. It decides whether those services that are uncommented will be allowed as incoming traffic. If someone tries to access services that are denied, you'll get a log of that in your /var/log/secure file with a nice “refused” message to let you know someone was being bad. Here's how it is done.
Add the following line to your /etc/hosts.deny file:
You start by denying all access by anybody. The first ALL refers to all services. The second ALL simply means everyone. You probably expected there would be a hosts.allow to counter the hosts.deny file, and you are correct. That file contains the hosts you want excluded from this global refusal of service. Let's assume your internal network has a base address of 192.168.1.0. The hosts we want to be able to telnet or ftp to are the ones in that network. In the /etc/hosts.allow file, you would put these entries:
ALL: 127.0.0.1< > ALL: 192.168.1.The dot after the 1 means that 192.168.1.anything is acceptable. The last thing to do is restart the master network process, inetd. On most Linux systems, it can be done like this:
/etc/rc.d/init.d/inet restartAs I mentioned, this is pretty simple, but also covers some pretty basic ground. Perhaps too basic. For instance, your TCP wrappers cover only those services listed in /etc/inetd.conf. A better way to build a firewall is through the use of ipchains, a packet filtering system for Linux machines running kernels starting just before version 2.2. The ipchains program is the successor to the older ipfwadm program. Most newer distributions will require ipchains for packet filtering.
If you have read the ipchains documentation, you've probably also felt a twinge of panic as you started to realize how complex this can all be. I'll spend the next few paragraphs with some (I hope) simple explanations of the process, then I will show you a few recipes from some great open-source chefs that will make the process less frightening and more fun.
Ipchains is a command-line utility that lets you create packet filtering rule sets called “chains”. These chains come in a few different flavors: input (packets coming in from the outside), output (packets bound for the outside world) and forward (packets being routed through your system, as in the case of ip forwarding and masquerading). There is actually a fourth, which can be user-defined and named. The format of the command is similar to the following:
/sbin/ipchains -A forward -j MASQ< > -s 192.168.1.0/24 -d 0.0.0.0/0
The -A means to add a rule to the chain. Other options are -D to delete, -R to replace, -I to insert, -N to create a new user-defined chain, and a handful of others. The above command creates a rule set that will forward packets from any host on my internal network and masquerade them to appear as though all traffic was originating from one machine only. The 0.0.0.0 means the packet destinations can be anywhere. The -j flag defines the action for this rule. Other than the above, which will do masquerading for your site, you can also specify ACCEPT (let the packet through), REJECT (reject the packet, but let the other side know it is being rejected) and DENY (don't allow the packet through and don't offer any explanation).
What else can we do with this? For one thing, I could create a pretty safe system by adding this single rule:
/sbin/ipchains -A input -j DENY -s 0.0.0.0/0 -d< > 0.0.0.0/0 No one will get into your system, and no one will ever know why. It might be an idea, then, to insert this rule into the chain before you do the above. Then at least your local network will have access to your server.
/sbin/ipchains -I input -j ACCEPT -s< > 192.168.22.0/24 -d 0.0.0.0/0
Let's try something a bit more complex. Starting from scratch, I might set up the following input chain. Note that there should be no broken lines here. Each command in the list begins with /sbin/ipchains. You will probably also notice that my Internet address is an imaginary one. No IP address starts with 259.
#< > # Input Rules # /sbin/ipchains -F input # /sbin/ipchains -P input ACCEPT # /sbin/ipchains -A input -j ACCEPT -s 192.168.1.0/24 -d< > 0.0.0.0/0 # /sbin/ipchains -A input -j ACCEPT -s 0.0.0.0/0 -d 0.0.0.0/0 # /sbin/ipchains -A input -j DENY -p tcp -s 0.0.0.0/0 -d 2184.108.40.206< > 137:139 # /sbin/ipchains -A input -j DENY -p udp -s 0.0.0.0/0 -d< > 2220.127.116.11 137:139 # /sbin/ipchains -A input -j ACCEPT -p tcp -s< > 0.0.0.0/0 -d 218.104.22.168 80 # /sbin/ipchains -A -d 0.0.0.0/0We start by “flushing” our input chain. That's the -F option at work. We then assign a default policy of ACCEPT with the -P option. The next two rules allow all traffic from our local network and the outside world to enter. Next, we DENY all traffic bound for the netbios services (Windows or SAMBA file sharing) at ports 137 through 139. A range of TCP ports is expressed by a starting number, a colon and an ending number. Since we do want to allow web server access on this mythical system, we then open port 80. Finally, we catch anything that is still open and does not fit the above rules by closing off access as earlier with an all-inclusive DENY.
Mais oui, the security-conscious gentleman at table twelve questions my reasons behind starting out with a policy of ACCEPT instead of DENY. You are wondering, has Marcel been sipping too much of his own wine? Well, mes amis, when contructing this example, I considered the creation of a rules set, added line by line remotely, whether from your own internal network, or from outside. If you DENY everything immediately, you deny yourself as well. Now, when it comes time to script all these commands, you may want to consider setting up a default DENY policy rather than ACCEPT. Even so, when the rules are executed in a script at boot time, with the final result being that you DENY anything that has not been specifically ACCEPTed, the window for a cracker to exploit this policy is very small.
At any point, you can list all the rules by typing /sbin/ipchains -L. You can also flush the table of all chains, rule sets, etc. by typing /sbin/ipchains -F. This is by no means a complete firewall setup, nor is it entirely realistic. In a real setup on a real server, there are other ports I would like open (such as e-mail or DNS services). I would also like to explicitly close access to my X window sockets (ports 6000, 6001, etc.).
If the prospect of creating a firewall from scratch seems a bit daunting and you would prefer a very quick-and-dirty approach to a rules-based IP filtering system, look no further than the “ipchains-firewall” script distributed by Ian Hall-Beyer. To create a basic firewall, you need only call the script with your outside interface and inside interface as the parameters. Here's how I did it on my system with an external PPP connection:
./firewall.sh ppp0 eth0
As the script runs, it will set up masquerading, block out remote access to things like netbios (SAMBA and Windows file sharing) and X sessions. You can see the script in action in Figure 1. Using /sbin/ipchains -L, you can list the rules created by the script. Ian's script is the beginnings of a firewall system, taking into consideration the “usual”. Customization can be done just by editing the script, which you will no doubt want to do. The defaults are fairly restrictive. For instance, vital services like SMTP are, by default, denied. The following example comes from the script itself, showing where I have uncommented port 25 (among other things):
# telnet (23)< > # $IPCHAINS -A input -p tcp -s 0/0 -d $1 23 -j ACCEPT # echo -n "." # smtp (25) $IPCHAINS -A input -p tcp -s 0/0 -d $1 25 -j ACCEPT echo -n "." # DNS (53) $IPCHAINS -A input -p tcp -s 0/0 -d $1 53 -j ACCEPT $IPCHAINS -A input -p udp -s 0/0 -d $1 53 -j ACCEPT echo -n ".."
What? Ah, you are asking if we have anything with a bit more spice on the menu. Mais oui. For those who prefer a graphical approach, I would like to recommend the following two items, Easy Firewall and GTK+ Firewall Control Center.
The brainchild of Daniel Roche, Easy Firewall is a Tcl/Tk (version 8) application that provides a nice simple interface for firewall administration. For those out there running older Linuxes, this application also works with ipfwadm. The program is available in a tarred and gzipped bundle or a Red Hat RPM. Since it is a Tcl/Tk application, it's also very easy to work through the program in case you happen to be curious. Installation is easy in either case, since there is no compiling to do with the Tcl/Tk script.
Easy Firewall comes with a few pre-defined firewall configurations that are easily modified. Start the program with the command easyfw. The big friendly screen lets you choose your internal and external interfaces at the click of a button. It will then scan your system for any ipchains rules that may already be in effect, and display them in the interface. Modify, add or delete rules as you see fit. When you have a satisfactory configuration, you can choose to Apply that configuration to your running network (now or at boot time) as well as save to a file. The file option is great for a glimpse into the structure of the firewall rules. Furthermore, since that file is a shell script, you can call it from your /etc/rc.d/rc.local at boot time.
Our next offering comes from Koo Kyoseon, a little something called the GTK+ Firewall Control Center. In the style of our previous chef, Koo has built a clean, friendly interface to ipchains administration. This program is available in either RPM or source code format. If you will be building it from source, note that you will also need the libipfwc library. Luckily, this is included with the source for gfcc. At the time of this writing, I picked up version 0.7.4 of the package, gfcc-0.7.4.tar.gz. Here are the steps for building the package and the library:
tar -xzvf gfcc-0.7.4.tar.gz< > cd gfcc-0.7.4
Once inside this directory, we can build libipfwc:
tar -xzvf libipfwc.tar.gz< > cd libipfwc makeNow, change back to the gfcc directory and build it:
cd ..< > ./configure --with-ipfwc=./libipfwc make make installStart the program by typing gfcc. This program actually has two different ways of saving information. One is a list of rules very much like what you will see if you look at the existing rules in the /proc file system. You can do this by typing this command:
cat /proc/net/ip_fwchainsIf you want a nice shell script like the one I mentioned earlier, you can use the export function to create the script. For instance, if I export my rules to /usr/local/.Admin/fwrules.sh, I can load it the next time I reboot by including that path as a single-line command at the end of my /etc/rc.d/rc.local script.
Now that we have all these ideas on the table, let me make an alternative serving suggestion. If you are really new to this and you feel uncomfortable embarking on building your own firewall, use Ian's ipchains-firewall script to generate the initial set of rules. Then, use either EasyFw or gfcc to modify the rules. It's an easy way to get started on your way to a more secure system.
Alas, the clock, she is telling us that closing time is near,non? Before you go, François will refill your glasses a final time. Security is a vitally important subject. Linux's powerful, network-ready architecture opens many doors, including some you may prefer leaving closed. With a little experimentation in the Linux kitchen, locking those doors need not be a frightening experience. Until next time, I invite you to network safely, and please, lock the door before you leave. We'll see you next time at Chez Marcel. Your table will be waiting.
A votre santé! Bon appétit!