Open Source

Lock IT Down: ipchains are painless way to ensure networking security

Secure Linux networks with ipchains


Linux does a lot of things as an OS, and it does them well. Linux develops well. Linux plays well with other OSs. Linux does incredible GUIs. Linux does office suites well. Linux networks well. And above all, Linux is king of security. In this Daily Drill Down, I’ll take a look at ipchains, an incredibly handy tool for cheap, efficient network security.

Finding out whether your system supports ipchains
For the longest time, the Linux kernel dealt with security through ipfwadm and the Linux IPv4 firewalling code (stolen from BSD). The older method was a bit clunky. It didn't deal with fragments; had 32-bit counters (on Intel); didn't allow for the specifications of any protocol other than TCP, UDP, or ICMP; couldn't make large changes automatically; didn't deal with inverse rules; and was prone to user error. Starting with the Linux 2.1.102 kernel, the switch was made to ipchains, and making a network secure couldn't be easier. With any of the latest Linux distributions, the kernel is already compiled with ipchains, so it's simply a matter of writing your own ipchain rules (or downloading any of a number of GUI front ends—more on that later). If you aren’t sure whether your particular distribution works with ipchains, run the command
ls /proc/net

and you should see something similar to:
arp dev_stat ip_fwnames netlink route sockstat udp
dev igmp ip_masq netstat rt_cache tcp unix
dev_mcast ip_fwchains ip_masquerade raw snmp tr_rif wireless


The entry you’re looking for is ip_fwchains. If this entry is listed, you are in luck, and ipchains will be your friend. If ip_fwchains doesn’t appear, you’ll have to remake your kernel and add in support (or upgrade to a newer distribution).

Now that you know whether your system supports ipchains, it’s time to begin looking into the why and how of the system.

Security, logging, and portability
Like most Linux users, you’ll probably want to know whyipchains are better. The primary (and obvious) reason is security. Most machines sitting on a network pipe are open and vulnerable to exploitation, hacks, cracks, and prowling, so it's best that they find their way to some semblance of security. Using a Linux box on a network is much different than using a Windows box, because the security of the system is under the complete control of the administrator. When I say complete, I mean just that: The admin can make the system as open or closed as desired and can do so without third-party software or a certification course. With ipchains, the admin configures each rule by hand (unless a GUI is used), so each rule can be customized and specified to that particular system's needs.

Logging is essential to any good security model, and with ipchains,logging is as simple as adding the proper flag (-l) to the command line. It’s very important that you watch your syntax when you're running commands with ipchains. Although you probably won’t do any damage, you might assume you have a secure system when in fact you don’t.

What more could you ask for than a security tool that’s usable on all your Linux boxes? You write a set of rules that are tight, and you can use them at work and at home, then distribute them to your friends and family. (I'll present a very good downloadable ipchains ruleset at the end of this Daily Drill Down to show you how easy it is to install a working set of rules onto a different machine.)

The needs of the few...
The first thing to remember is that ipchains can be done two different ways:
  • You can enter the rules at a console, one at a time.
  • You can run an existing ipchains file that contains all the necessary rules for your system.

Both methods have advantages and disadvantages. The former allows better means of testing, debugging, and customizing with a one-rule-at-a-time approach. The latter allows simplicity, efficiency, and portability. (Note: When I refer to portability, I don’t mean cross-platform portability.) In this Daily Drill Down, I’ll introduce the idea of ipchains with the one-rule-at-a-time approach and eventually move to a much stronger script approach.

You’ll also want to take into consideration just how the ipchains function deals with an incoming/outgoing packet. As a packet comes in (or goes out), the header is compared to the first rule in the input chain. If the header matches (it’s an incoming FTP request to a port that has FTP blocked by ipchains), the packet will be dropped and no further action will be taken. If the packet doesn’t match, it will move on to the next rule. If the packet makes it through all the rules (or matches a rule that allows it through), the packet will make it into/out of the system. As a result, you should design your ruleset carefully. Make sure your more specific rules are above the general rules. For example, keep your catchall rules toward the bottom, which will catch any packets missed by the previous rules.

The structure of a rule
Let's take a look at the parts of an ipchain rule. The basic structures of ipchains are:
  • ipchains -[ADC] chain rule-specification [options]
  • ipchains -[RI] chain rulenum rule-specification [options]
  • ipchains -D chain rulenum [options]
  • ipchains -[LFZNX] [chain] [options]
  • ipchains -P chain target [options]
  • ipchains -M [ -L | -S ] [options]

This list doesn’t include all the options. I’ll focus on the most widely used structures.

The parts of the rule are:
  • ipchains: The actual command itself.
  • -A: Tells ipchains you’re going to append a new rule to the bottom of the chain.
  • input: Tells ipchains that the rule is for the input chain.
  • output: Tells ipchains that the rule is for the output chain.
  • -s: Specifies where the packets are coming from.
  • -d: Specifies where the packets are going.
  • -p: Tells ipchains what protocol of the rule or the packet to check.
  • -j: Tells ipchains to jump to DENY or do something else if the packet matches the rule.
  • -N: Creates a new chain.
  • -X: Deletes an empty chain.
  • -P: Changes the policy for a built-in chain.
  • -L: Lists the rules in a chain.
  • -F: Flushes the rules out of a chain.
  • -Z: Sets the packet and byte counters on all rules of a chain to 0.
  • -I: Inserts a new rule into the top of a chain.
  • -R: Replaces a rule in a chain.
  • -D: Deletes a rule in a chain.
  • -I: Specifies an interface to be used (that is, eth0).
  • -l: Logs all instances when a packet matches a rule.
  • -v: Provides verbose output.

The first rule I’ll look at is a very basic rule that will deny a ping to the local machine. I’ll call this local machine willow (and it will have an entry in the /etc/hosts file) with an IP address of 172.22.1.4. The actual rule looks like
ipchains -A input -s willow -p icmp -j DENY

and is run from the command line as root. When you run this command, it places the rule into the input chain and will remain present until the chain is flushed (by entering the command ipchains -F or by reentering the same chain and replacing -A with -D) or the machine is rebooted.
In order for aliases to be used, you must have them in your /etc/hosts file.
A normal ping attempt to willow will look like:
> ping willow
PING willow.tech (172.22.1.4) from 172.22.1.4 : 56(84) bytes of data.
64 bytes from 172.22.1.4: icmp_seq=0 ttl=255 time=0.1 ms
—- willow.tech ping statistics —-
1 packets transmitted, 1 packets received, 0% packet loss
round-trip min/avg/max = 0.1/0.1/0.1 ms


which is successful. Once the above rule is entered, the ping will not succeed and, when you press [Ctrl]D, it will return:
PING willow.tech (172.22.1.4) from 172.22.1.4 : 56(84) bytes of data.
—- willow.tech ping statistics —-
2 packets transmitted, 0 packets received, 100% packet loss


As you can see, the ping failed this time. A very simple test!

Now to get your ping back, you can do one of two things: You can flush the ruleset or you can delete the ruleset. To flush the ruleset, you simply run (again as root):
ipchains -F

Now rerun ping and it should be successful. However, if you created more than one ruleset and you want to delete only one rule (instead of all of them), you simply mirror the original rule and replace the Append flag with the Delete flag. So the new command will look like:
ipchains -D input -s willow -p icmp -j DENY

and the rule is gone. Simple.

A bit more practical
Let's face it—denying ping is not horribly impressive or effective (in fact, Linux is now immune to the ping of death), so we'll have to look deeper into ipchains and write some more useful rulesets. The following rules are pretty basic, but they are fairly inclusive (we're using our test machine willow for these rules to simplify the writing) and list some of the most commonly exploited ports.

Set default output policy to ACCEPT (this will allow all packets to leave willow):
ipchains -P output ACCEPT

Set default input policy to DENY (this will deny all packets coming into willow):
ipchains -P input DENY
Once the above are set, new entries can be added to either input or output chains to allow traffic.
Deny all access of type tcp into willow:
ipchains -A input -p tcp -s ! 172.22.1.4 -j DENY
By using the ! symbol, you are, in effect, saying deny everything that is not 172.22.1.4.
Deny FTP access into willow:
ipchains -A input -p tcp -d willow 20 -j DENY -l

Deny telnet access into willow:
ipchains -A input -p tcp -d willow 23 -j DENY -l

Enable loopback (interface = lo) traffic (this lets you run any local networking service you or your system might need):
ipchains -v -A input -i lo -s 0/0 -d 0/0 -j ACCEPT
ipchains -v -A output -i lo -s 0/0 -d 0/0 -j ACCEPT


The next few rules are used to deny access ports that shouldn't be exposed to the network. Many of these particular ports are frequently scanned and easily abused.

Port 0, a reserved port with no legitimate use:
ipchains -A input -p tcp -d willow 0 -j DENY -l

Ports 0-5, used for sscan signature (ports 0 and 5 will have two entries):
ipchains -A input -p tcp -d willow 0/5 -j DENY –l
ipchains -A input -p icmp -d willow 5 -j DENY –l
ipchains -A input -p udp -d willow 0 -j DENY -l


Port 7, echo, used for UDP attack (here you’ll have two entries, one for TCP and one for UDP):
ipchains -A input -p tcp -d willow 7 -j DENY –l
ipchains -A input -p udp -d willow 7 -j DENY -l


Port 11, systat, used for User process information (ps):
ipchains -A input -p tcp -d willow 11 -j DENY -l

Port 15, netstat, used for network status, open connections, routing tables, etc.:
ipchains -A input -p tcp -d willow 15 -j DENY -l

Port 19, chargen, UDP attack (again you’ll have two entries):
ipchains -A input -p tcp -d willow 19 -j DENY –l
ipchains -A input -p udp -d willow 19 -j DENY -l


Port 21, 20, FTP service:
ipchains -A input -p tcp -d willow 20/21 -j DENY -l

Port 22, ssh, secure shell service (again two entries):
ipchains -A input -p tcp -d willow 22 -j DENY –l
ipchains -A input -p udp -d willow 22 -j DENY \-l


Port 23, telnet:
ipchains -A input -p tcp -d willow 23 -j DENY -l

Port 25, smtp, SPAM relay and older vulnerabilities:
ipchains -A input -p tcp -d willow 25 -j DENY -l

Port 53, domain, TCP zone transfers and DNS spoofing:
ipchains -A input -p tcp -d willow 53 -j DENY -l

Port 69, tftpd, Insecure FTP alternative:
ipchains -A input -p udp -d willow 69 -j DENY -l

Port 79, finger, user information:
ipchains -A input -p tcp -d willow 79 -j DENY -l

Port 87, link, tty link, which is very commonly used:
ipchains -A input -p tcp -d willow 87 -j DENY -l

Ports 109, 110, pop-3, one of the most exploited ports:
ipchains -A input -p tcp -d willow 109/110 -j DENY -l

Port 111, sunrpc, the most exploited port (two entries):
ipchains -A input -p tcp -d willow 111 -j DENY –l
ipchains -A input -p udp -d willow 111 -j DENY -l


Port 119, nntp, public news feed for SPAM relay:
ipchains -A input -p tcp -d willow 119 -j DENY -l

Port 143, imap, one of the three most exploited ports:
ipchains -A input -p tcp -d willow 143 -j DENY -l

Port 144, NeWS, window management system:
ipchains -A input -p tcp -d willow 144 -j DENY -l

Port 161, 162, snmp, remote network administration and queries:
ipchains -A input -p udp -d willow 161/162 -j DENY -l

Port 177, xdmcp, X Display Login Manager:
ipchains -A input -p udp -d willow 177 -j DENY -l

Port 512-520, various, intranet only (two entries):
ipchains -A input -p tcp -d willow 512/520 -j DENY –l
ipchains -A input -p udp -d willow 512/520 -j DENY -l


Port 540, uucp:
ipchains -A input -p tcp -d willow 540 -j DENY -l

Port 635, mount, mountd exploit:
ipchains -A input -p udp –d willow 635 -j DENY -l

Port 1080, socks, SPAM relay, proxy server exploit:
ipchains -A input -p tcp -d willow 1080 DENY -l

Port 1114, sql, sscan signature:
ipchains -A input -p tcp -d willow 1114 -j DENY -l

Port 2000, openwin, Open Windows:
ipchains -A input -p tcp -d willow 2000 -j DENY -l

Port 2049, nfs, remote file access (two entries):
ipchains -A input -p tcp -d willow 2049 -j DENY –l
ipchains -A input -p udp -d willow 2049 -j DENY -l


Ports 6000-6003, X11, X Windows System:
ipchains -A input -p tcp -d willow 6000/6003 -j DENY -l

Flush and set the byte/packet containers to 0:
ipchains -F input
ipchains -F output
ipchains -Z input
ipchains -Z output


Enable all ICMP:
ipchains -v -A input -p ICMP -s 0/0 -d willow -j ACCEPT

If you’re doing any IP masquerading:
echo 1 > /proc/sys/ipv4/ip_forward
ipchains -A forward -s 172.22.1.0/255.255.0.0 -j MASQ


This is a fairly exhaustive list of rules and may be a bit on the side of overkill for a single ipchain ruleset. However, better safe than sorry. You may also notice that one of the final lines in the downloadable ipchains script will cover many of the above rules:
ipchains -v -A input TCP -i $OUTWARD_IF -d LOCAL_IP 0:1024 -l -j DENY

However, with this rule you’ll block services that may be needed for certain hosts. Therefore, the script includes loops that open certain ports and services for all hosts listed in $TRUSTED_LOCS. This approach keeps down the size of the script (imagine having to hardcode all the lines for each trusted location!).

The ipchains script
Now that you understand the individual pieces, you can probably see that running each of them separately would be quite a chore. Granted, using all the above rules probably won’t be necessary. However, as the saying goes, an ounce of prevention is worth a pound of cure. So, let's focus on using all the rules for our ipchain.

To properly put this chain together to run in a script, you need to think, for the moment, like a programmer. As you can see, there’s a lot of room for IP number entry, and the above rules, as written, apply to a specific machine. To change the entire script, you’d have to change each line (such as each instance of -s willow). You can do this easily by using global variables. Global variables let you apply a variable in one location, within a script or code, and that variable will be applied to all locations where the variable name is found.

For our ipchains script, use the following global variables:
  • LOCAL_IP
  • OUTWARD_IF
  • INWARD_IF
  • DNS1,2
  • ANYWHERE
  • SERVE_TCP
  • SERVE_UDP
  • TRUSTED_SERVE_TCP
  • TRUSTED_SERVE_UDP
  • TRUSTED_LOCS
  • TRUSTED_NTP

We’ll also include DNS numbers within the declaration of the above global headers (even though they won’t technically be global).

The script will be in sections, which will include the following:
  • header
  • variable declaration
  • flush and zero section
  • set policy
  • enable loopback
  • deny ports
  • enable special services

Using this outline, you can put together a fairly tight ipchains script. I've written the script, with comments, and left the variable declaration up to you. Download the script here and then I'll describe its use.

The script
To use the script, first log on as root (with su) and create a /root/bin directory. Then, move the ip_chains_script file into that directory (mv ip_chains_script /root/bin). With the file properly in place, go through and fill in the global directories. Here's a brief description of each variable:
  • LOCAL_IP—External IP address of the machine being used.
  • OUTWARD_IF—Interface used for external network traffic.
  • INWARD_IF—Interface used for internal network traffic (for example, if IP masquerading is to be used).
  • DNS 1, 2—Primary and secondary DSN numbers for your network.
  • ANYWHERE—A simple way to keep from having to type 0/0 or 0.0.0.0 all the time. It means all locations.
  • SERVE_TCP—TCP services offered to all.
  • SERVE_UDP—UDP services offered to all.
  • TRUSTED_SERVE_TCP—TCP services offered to trusted locations.
  • TRUSTED_SERVE_UDP—UDP services offered to trusted locations.

With the global variables all filled in, you’ll need to take care of one little detail before you run this script. Some distributions of Linux do not have /sbin in root’s $PATH. In order for this script to run as is, you’ll have to add /sbin to root’s $PATH. The simplest way to do this is with the export command (run as root):
export PATH=$PATH:/sbin

and you should be good to go. Log off your machine and log back in (so the new $PATH will take effect). Now all you have to do is make sure the script has executable permissions (with chmod u+x ip_chains_script) and you're ready to run.

In the /root/bin directory, type the following:
./ip_chains_script

and you will see a lot of text scroll by. If you want to see what this text is, you can run:
./ip_chains_script > testing

which will cat the output to the file testing.

Now that you've run your first ipchains script, you can test your network connection to make sure you're still getting out. A good way to do this is to go to Gibson Research Corporation’s Shields Up site and test your machine's vulnerabilities. Or you could grab a copy of nmap to get a better feel of how well your script stands up to port scanning. The output of an nmap portscan (without using the -P0 switch, which says Do not try and ping hosts at all before scanning them. This allows the scanning of networks that don't allow ICMP echo requests (or responses) through their firewall.) on a machine running our script should look something like this:
[jwallen@giles nmap-2.30BETA18]$ nmap willow
Starting nmap V. 2.30BETA18 by fyodor@insecure.org ( www.insecure.org/nmap/ )
WARNING! The following files exist and are readable: /usr/local/lib/nmap/nmap-services and ./nmap-services. I am choosing /usr/local/lib/nmap/nmap-services for security reasons. set NMAPDIR=. to give priority to files in your local directory
Note: Host seems down. If it is really up, but blocking our ping probes, try -P0
Nmap run completed — 1 IP address (0 hosts up) scanned in 54 seconds
[jwallen@giles nmap-2.30BETA18]$


Making it a bit more permanent
As it stands, you now have your ipchain rules running on your machine. However, when you reboot that machine, you’ll lose those rules and, as a result, find yourself wide open again. How do you make the rules more permanent? There are many, many ways to keep your ruleset running. I‘ll show you one very simple method.

Cron
Let’s create a cron job that will always rerun your ipchain file. With this method, you can rest assured that at a certain time your ruleset will be run fresh and your machine will be secure.

To create this cron job first (as root) cd to /etc/cron.daily and open your favorite editor. The file we’ll create will be called ipchains_daily and will contain the following text:
#! /bin/sh
/root/bin/ip_chains_script


Once you've entered the text and saved the script, change the permissions to the following:
-rwx———

with the following command:
chmod 700 ipchains-daily

Once you’ve finished, your ipchains will run every morning at 4:00. This time is fairly safe, since little work will be done that early in the morning.

What about startup?
What if you restart your machine before 4 A.M.? You’ll certainly want your long- awaited security measures in place—but how?

The simplest method of ensuring your new security will be in place upon boot is to place an entry in your /etc/rc.d/rc.local script. The entry
/root/bin/ip_chains_script

will cause the ipchains script to run at boot.

Another method, and a more solid one, is to add an entry with chckconfig. To do this, first download this packetfilter script and then run the following steps:
  • su to root.
  • mv the file to /etc/rc.d/init.d.
  • Assign executable permission to the script with chmod u+x packetfilter.
  • Run the command chkconfig —add packetfilter.
  • Run the command find ..|grep pack.

The final command is simply a means to check to see whether the setup was successful. You should see something like this:
../init.d/packetfilter
../rc0.d/K90packetfilter
../rc1.d/K90packetfilter
../rc2.d/S09packetfilter
../rc3.d/S09packetfilter
../rc4.d/S09packetfilter
../rc5.d/S09packetfilter
../rc6.d/K90packetfilter


which will inform you that the packetfilter script has been entered in your startup routines.

Other aspects I didn't cover
There are many other aspects of the ipchains utility I didn’t cover in this Daily Drill Down. For instance, you can generate and use additional ipchains (beyond input and output). With the command
ipchains -N test

you create a new ipchain called test. (Note: ipchain names cannot exceed eight characters.) This is primarily useful for organizational purposes and makes debugging a much less painful task.

Another helpful tool to use with ipchains is ipchains-save. The ipchains-save utility prints the firewall chains to stdout, which will allow the administrator to see a list of all the rules (without comments or confusing script) for debugging or archiving purposes.

Conclusion
Although I didn’t cover every aspect of the ipchains utility, I've tried to bring you a simple means of locking down your network without having to earn an engineering degree in the process.

One thing you’ll want to consider: This is certainly not the most advanced ipchain ruleset. There are better, more detailed, and more inclusive scripts that would require days of class time to explain and understand.

Regardless of depth and breadth, what you've learned from this Daily Drill Down (and the script included) should enable you to breathe a bit easier knowing that your network has more security than it ever did before.
As stated in the comments of the ipchains script, our script is based on one written by splanky .

Jack Wallen, Jr. is very pleased to have joined the TechRepublic staff as editor in chief of Linux content. Jack was thrown out of the "Window" back in 1995, when he grew tired of the "blue screen of death" and realized that "computing does not equal rebooting." Prior to Jack's headfirst dive into the computer industry, he was a professional actor with film, TV, and Broadway credits. Now, Jack is content with his new position of Linux Evangelist. Ladies and gentlemen—the poster boy for the Linux Generation!

The authors and editors have taken care in preparation of the content contained herein, but make no expressed or implied warranty of any kind and assume no responsibility for errors or omissions. No liability is assumed for any damages. Always have a verified backup before making any changes.

About Jack Wallen

Jack Wallen is an award-winning writer for TechRepublic and Linux.com. He’s an avid promoter of open source and the voice of The Android Expert. For more news about Jack Wallen, visit his website jackwallen.com.

Editor's Picks