Firewall uptime and security with iptables
Set up and maintain a Linux firewall with this effective application
Iptables is an application allowing the administration of the tables in the Linux kernel firewall. You don't need prior knowledge about the kernel, or the actual tables in it, for firewall modifications and common system administration tasks.
In some Linux distributions, iptables comes enabled by default. It is common for inexperienced users to recommend completely disabling iptables to avoid networking issues. This article will help you get started quickly and manipulate iptables to your needs.
Sometimes iptables is used to refer to the Linux kernel-level component. In this article, anything related to iptables will specifically refer to the application that controls the protocols in a Linux distribution like ipv4, ipv6, and the ARP tables.
Similar to other Linux applications, you can configure iptables in the command-line interface or in a plain text file, allowing editing with any text editor. Although it is easy to modify, it might feel awkward from the average firewall appliance where most of the interaction with settings and configurations is done in a graphical interface. There are applications using iptables to manage a firewall through graphical interfaces, but this article will cover interacting with iptables in its native environment: the Linux terminal.
Having a comfort level using a Linux terminal (also referred to as a console or terminal emulator) will help developers take advantage of the examples and configurations that follow. The command-line interface is the primary way to interact with iptables, and a Linux terminal is the application allowing access to this interface.
The rules that are applied are mostly very readable and easily ported to other servers. This feature saves significant time when dealing with unresponsive hardware.
Although iptables is the main subject of this article is, and it is probably already installed in your environment, we will also use nmap, which is another powerful application.
Verify that nmap is installed before continuing. You can install this effective network scanner in a Debian/Ubuntu Linux distribution.
Listing 1. Installing nmap on Debian/Ubuntu
sudo apt-get install nmap
Because we are going to make modifications at the kernel level, make sure you have root privileges.
Listing 2 shows the rules currently being applied to the server. Listing 2 will be repeated during the article to verify what rules are currently in use and to verify successful changes.
Listing 2. Currently applied rules
root@desktop:~# iptables -L Chain INPUT (policy ACCEPT) target prot opt source destination Chain FORWARD (policy ACCEPT) target prot opt source destination Chain OUTPUT (policy ACCEPT) target prot opt source destination
In Listing 2, we instruct iptables to list all rules currently applied to the firewall. This is accomplished by the -L flag.
The output also mentions
Chain. Think of iptable
chains as sections in the firewall allowing a certain type of traffic. For
example, to block all traffic from the private network to the internet,
that rule would be set in the OUTPUT section. Similarly, any rule
affecting incoming traffic would be listed in the INPUT chain.
The three chains are each applied to one type of activity in the firewall. At this point, there is nothing set yet. This means there are no restrictions and all network traffic is allowed to come and go.
Chain FORWARD, and
Before continuing, it is necessary to verify what ports are open on the server for comparison after it is locked down. As mentioned before, nmap is a powerful command line tool that provides network security information. Listing 3 shows the output of nmap in a remote server on the network.
Listing 3. Network scanning with nmap
~ $ nmap 10.0.0.120 Starting Nmap 5.35DC1 ( http://nmap.org ) at 2010-11-21 20:44 EST Nmap scan report for 10.0.0.120 Host is up (0.012s latency). Not shown: 991 closed ports PORT STATE SERVICE 22/tcp open ssh 25/tcp open smtp 53/tcp open domain 80/tcp open http 631/tcp open ipp 3306/tcp open mysql 4001/tcp open unknown 5900/tcp open vnc 8080/tcp open http-proxy Nmap done: 1 IP address (1 host up) scanned in 6.57 seconds
Those are a lot of open ports! In just a few steps, you will learn how the above changes after iptables is configured.
Firewall rules can be either applied and appended, or edited manually in a plain text file, and sourced. I prefer using a text file to apply changes. Most of the time syntax errors are easier to catch when written in a text file. Another problem arises with editing rules by appending rules directly, that is, these rules will not be saved when a server reboots. Before editing the file, let's tell iptables to export the current rules so that file becomes our initial template. See Listing 4.
Listing 4. Saving rules to a file
root@desktop:~# iptables-save > /etc/iptables.rules root@desktop:~# cat /etc/iptables.rules # Generated by iptables-save v1.4.4 on Sun Nov 21 14:48:48 2010 *filter :INPUT ACCEPT [732:83443] :FORWARD ACCEPT [0:0] :OUTPUT ACCEPT [656:51642] COMMIT # Completed on Sun Nov 21 14:48:48 2010
I used the
iptables-save command and redirected
the output to a text file in the "/etc" directory. I have concatenated the
file so you can see what the file looks on my machine.
One of the first requirements is to allow established connections to receive traffic. You need this when you want anything behind the firewall (in a private network) to be able to send and receive network data without restrictions. In Listing 5 we will issue a direct rule to iptables and verify the state of the firewall afterward.
Listing 5. Established sessions rule
root@desktop:~# iptables -A INPUT -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT root@desktop:~# iptables -L Chain INPUT (policy ACCEPT) target prot opt source destination ACCEPT all -- anywhere anywhere ctstate RELATED,ESTABLISHED Chain FORWARD (policy ACCEPT) target prot opt source destination Chain OUTPUT (policy ACCEPT) target prot opt source destination
To have a better idea of what was issued, let's break the command apart and explain each one:
-A INPUT: Append this rule to the
-m conntrack: Match the following connection
tracking for the current packet/connection.
-ctstate ESTABLISHED, RELATED: Connection states
that the rule should apply to. In this case, an
ESTABLISHED connection means a connection that
has seen packets in both directions and a
RELATED type means that the packet is starting
a new connection but it is associated with an existing connection.
-j ACCEPT: tells the firewall to accept the
connections described before. Another valid setting for the
-j flag would be
I'm also connecting through the SSH protocol to that server, so before locking down the firewall, a rule in Listing 6 is going to allow all incoming SSH traffic. I specify the type of network protocol (tcp) and the port that is conveniently associated with the SSH service. You can specify the port number directly if needed.
Listing 6. Accepting inbound SSH connections
root@desktop:~# iptables -A INPUT -p tcp --dport ssh -j ACCEPT root@desktop:~# iptables -L Chain INPUT (policy ACCEPT) target prot opt source destination ACCEPT all -- anywhere anywhere ctstate RELATED,ESTABLISHED ACCEPT tcp -- anywhere anywhere tcp dpt:ssh Chain FORWARD (policy ACCEPT) target prot opt source destination Chain OUTPUT (policy ACCEPT) target prot opt source destination
Finally, let's set the firewall to block everything else. Take special care when issuing the following command. If it is placed before all the other rules, it will block any and all traffic to the server. Iptables reads rules in a procedural fashion (from top to bottom) and after a rule is matched, nothing else gets evaluated.
Listing 7. Blocking all incoming traffic
root@desktop:~# iptables -A INPUT -j DROP root@desktop:~# iptables -L Chain INPUT (policy ACCEPT) target prot opt source destination ACCEPT all -- anywhere anywhere ctstate RELATED,ESTABLISHED ACCEPT tcp -- anywhere anywhere tcp dpt:ssh DROP all -- anywhere anywhere Chain FORWARD (policy ACCEPT) target prot opt source destination Chain OUTPUT (policy ACCEPT) target prot opt source destination
Listing 8. Verifying firewall configuration
root@desktop:~# iptables-save > /etc/iptables.rules root@desktop:~# cat /etc/iptables.rules # Generated by iptables-save v1.4.4 on Sun Nov 21 15:10:42 2010 *filter :INPUT ACCEPT [1234:120406] :FORWARD ACCEPT [0:0] :OUTPUT ACCEPT [1522:124750] -A INPUT -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT -A INPUT -p tcp -m tcp --dport 22 -j ACCEPT -A INPUT -j DROP COMMIT # Completed on Sun Nov 21 15:10:42 2010
iptables-=save command pushed our changes
to a plain text file. It does look a bit different from just listing the
rules in the command line, but it is exactly the same thing. Just as
before, we have three sections: INPUT, FORWARD and OUTPUT. The rules that
we initially specified concern OUTPUT connections, so this is the section
where the rules we added are placed.
At this point, the server is locked and the configuration has been saved to a file. But what will happen when we perform a network scan? Let's run nmap again against that server and check the results as shown in Listing 9.
Listing 9. Network scan with locked down server
~ $ nmap 10.0.0.120 Starting Nmap 5.35DC1 ( http://nmap.org ) at 2010-11-21 20:56 EST Note: Host seems down. If it is really up, but blocking our ping probes, try -Pn Nmap done: 1 IP address (0 hosts up) scanned in 3.04 seconds ~ $ nmap -Pn 10.0.0.120 Starting Nmap 5.35DC1 ( http://nmap.org ) at 2010-11-21 20:56 EST Nmap scan report for 10.0.0.120 Host is up (0.017s latency). Not shown: 999 filtered ports PORT STATE SERVICE 22/tcp open ssh Nmap done: 1 IP address (1 host up) scanned in 12.19 seconds
Note that a scan was attempted against the IP where the server is located, but nmap failed to list the open ports. This happens because the firewall is set in such a way that it is blocking everything except for the SSH port we have open. Since nmap uses a specific network protocol to verify if the host is up, it returned empty handed. The second try was successful and is telling us that only SSH is open and nothing else. With just three rules, we managed to get an effective lock down of our server.
Saving and restoring rules
In the previous section, we saved the rules to a text file. However, that doesn't effectively tell the server that it needs to load the rules. Also, when the server reboots, it loses all the configuration done.
If you are adding rules on the command line, you should already be familiar with saving those changes to a text file. See Listing 10 for saving the firewall rules.
Listing 10. Saving the firewall rules
iptables-save > /etc/iptables.rules
Depending on the operating system in use, there are several ways to get those rules to load on start up. An easy approach is to go to the one interface that is public facing and tell it to load those rules before bringing the interface on line. See Listing 11.
Listing 11. Public network interface loading rules
<![CDATA[ auto eth0 iface eth0 inet static address 22.214.171.124 netmask 255.255.255.0 pre-up iptables-restore < /etc/iptables.rules ]]>
Here we have the eth0 interface and are declaring a rule to load the rules before bringing the interface up. As you may have guessed, you can use these commands to manually update the firewall rules from and to the file.
Not long ago, I was in a situation where I was responsible for a firewall appliance. Although I was making sure periodic backups of the rules and configurations were made, I failed to realize that those backups were in a proprietary format and only readable by the appliance model I had. That isn't a problem, of course, as long as you have two appliances of the same brand, model, and firmware version, but, as is common in small businesses, the budget didn't allowed for anything else.
One day, that appliance decided not to run anymore and I had to implement something fast that could be as reliable (or better). I learned the hard way that having human-readable configurations and the ability to come back up quickly are very important assets.
With some luck, I found an old server in good condition with a couple of network interfaces and was able to replace the dead appliance.
Until now, we have gone through scenarios of obtaining a copy of the rules that could be easily applied to any server in case of failure. Now let's enable the firewall to be the main gateway for a small home or business network.
Iptables as a main gateway
So far everything we've covered is great if you are running iptables on a personal computer, but it doesn't make much sense if a whole office needs to share an internet connection. With a few configuration settings, we can set that up properly.
We are going to assume that the server has two physical network interfaces: eth0 (public) and eth1 (private). I need to NAT them together so network traffic flows seamlessly from one interface to the other. The private network subnet is 192.168.0.0/255.255.0.0, so let's see how a NAT rule with forwarding would look in Listing 12.
Listing 12. NAT and forwarding rules
iptables -A FORWARD -s 192.168.0.0/255.255.0.0 -i eth0 -o eth1 -m\ conntrack --ctstate NEW -j ACCEPT iptables -A FORWARD -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT iptables -A POSTROUTING -t nat -j MASQUERADE
Listing 13 shows how to modify some settings in
proc to enable forwarding in the server.
Listing 13. Enabling forwarding in the server
sh -c "echo 1 > /proc/sys/net/ipv4/ip_forward"
proc changes are volatile, so any
changes made there are lost after a reboot. There are several ways to make
sure that the modifications will stick after a reboot. In Debian/Ubuntu
distributions, add the lines to get executed in /etc/rc.local.
Finally, as shown in Listing 14, there is one more setting change that modifies kernel parameters at runtime (sysctl). These configurations are usually already in the sysctl.conf, but are commented out. Uncomment them (or add them if they are not included with your distribution).
Listing 14. Sysctl/kernel forwarding
ARP cache threshold
Running a Linux server as a gateway will cause certain issues with DNS. The kernel is designed to keep a mapping of IP addresses, but it comes with a maximum level of entries that are not suitable for heavy traffic. When this level is reached, no DNS queries get back to the host who asks. While such a threshold is rarely reached with a few clients, more than thirty clients going through this firewall will cause a problem.
The environment may need some adjusting, but the values shown in Listing 15 should provide room before seeing such issues.
Listing 15. Increasing the ARP cache size
echo 1024 > /proc/sys/net/ipv4/neigh/default/gc_thresh1 echo 2048 > /proc/sys/net/ipv4/neigh/default/gc_thresh2 echo 4096 > /proc/sys/net/ipv4/neigh/default/gc_thresh3
Be on the lookout for messages similar to those in Listing 16, which will provide a warning if it is necessary to increase the numbers just provided.
Listing 16. System log ARP cache overflow warnings
Nov 22 11:36:16 firewall kernel: [92374.325689] Neighbour table overflow. Nov 22 11:36:20 firewall kernel: [92379.089870] printk: 37 messages suppressed. Nov 22 11:36:20 firewall kernel: [92379.089876] Neighbour table overflow. Nov 22 11:36:26 firewall kernel: [92384.333161] printk: 51 messages suppressed. Nov 22 11:36:26 firewall kernel: [92384.333166] Neighbour table overflow. Nov 22 11:36:30 firewall kernel: [92389.084373] printk: 200 messages suppressed.
We have gone through some simple steps to get iptables to run properly and to safely lock down a Linux server. The rules applied should provide a good sense of what is going on in a server using iptables as a firewall. I encourage you to give iptables a try, especially if you depend on an appliance and want more control and easily replicated human-readable configurations.
While the rules used we've used here are simple, the full flexibility and complexity of iptables is beyond the scope of this article. There are many complex rules that you can combine to create a safe and controllable firewall environment.
An example of an interesting advanced feature in iptables is load balancing. Most of the time, when exploring high availability web services, you are seeking load balancing solutions. With iptables, this can be set and configured with the random or nth flags.
You can also do time-based rules. In a small office environment, it might be useful to restrict certain services from Monday through Friday, but let the firewall behave differently on Saturday and Sunday. The flags that might work in such a case are: --timestart, --timestop and days.
A problem I experienced was not having two firewalls at the same time with some kind of fail over. Creating such a thing is no easy feat, and can be approached in several ways. The easiest solution would be to have the router do the job and load balance two identical Firewall servers. I recommend looking into such an option if the network environment is a critical asset like an office or small business.
Iptables saved me once, and I hope it does the same for you!