IPTables Architecture

IPTables is the most common firewall implementation for Linux. It consists of two parts:

netfilter
The piece of code that sits in the operating system and checks all packets against the list of rules. If you want to look at the code, it's in the net/netfilter subdirectory of the kernel source code, but that's not necessary for this tutorial; you don't have to know the details of the netfilter implementation to understand IPTables.
iptables
The program that allows the system administrator to alter the list of rules that netfilter uses. It's usually installed as /sbin/iptables, and you need to be logged in as root to do anything meaningful with it.On Ubuntu, this means you'll have to prefix sudo to all commands in this tutorial.

The IPTables Tables (and Chains)

An overview of the packet filtering in the Linux kernel

As the name suggests, IPTables organizes its rules into tables based on each rule's purpose and what packets it should apply to. When creating a firewall, the only table you actually need to use is the filter table; the other tables are used for specific kinds of packet alterations.

Within each table, the rules are organized into chains, which are the structures you'll need to be familiar with in order to use IPTables effectively. Each chain is just a named, ordered list of rules. There are three predefined chains named INPUT, FORWARD, and OUTPUT, which are the starting points for, respectively, packets coming into the computer, packets "just passing through" the computer, and packets heading out of the computer. Other than those three names, you can use any name you want for any new chains you create.

Adding your own chains is useful because when you create a rule, you can specify the name of any custom chain as the target of the rule. This means that instead of, say, accepting or rejecting the packet on the spot, IPTables will jump to the chain you named and start checking the packet against the rules in that chain. In a way, chains are like the IPTables equivalent of subroutines or functions.

Configuring a Linux Firewall

Let's say you've got yourself a brand new Linux system and you want to set up its firewall. In order to do this, you're going to need to run some iptables commands. If you look around the internet for information on how to set up IPTables, you'll find a bunch of sites that can give you a script with long lists of iptables commands to run, but they often don't explain how to come up with those commands in the first place. That's what this section is about.

The sections on Shorewall and Firewall Builder describe alternatives to this procedure — that is, you can go through the steps below, or you can use Shorewall or Firewall Builder to configure the firewall for you, but there's no need to do both.

Examining the Existing Rules

The first thing to do is see whether there are existing firewall rules. The command to list all the existing rules (in the filter table) is

iptables -L -n --line-numbers -v

On a brand-new Linux system with no existing rules, you'll get output that looks like this:

Chain INPUT (policy ACCEPT 1546 packets, 550K bytes)
num   pkts bytes target     prot opt in     out     source               destination

Chain FORWARD (policy ACCEPT 0 packets, 0 bytes)
num   pkts bytes target     prot opt in     out     source               destination

Chain OUTPUT (policy ACCEPT 1598 packets, 392K bytes)
num   pkts bytes target     prot opt in     out     source               destination

Note the policy ACCEPT on each of the three chains: this shows that the firewall is set up not to block any packets.

If your computer already has some firewall rules, they'll be listed in the output of this command, and in that case you have a choice. You can adjust the existing ruleset by adding, deleting, and replacing rules, although you'd need to be somewhat familiar with IPTables syntax before doing so. (You can look at the syntax summary and the IPTables man page for help.) The other alternative is to reset the firewall completely and start from scratch. To do that,

  1. First save the existing rules (just in case) using
    iptables-save > iptout
  2. Then, if you're configuring your system over an SSH connection, reset the default policies for the default chains so that you don't lock yourself out:
    iptables -P INPUT ACCEPT
    iptables -P OUTPUT ACCEPT
  3. Finally, clear out the rules:
    iptables -F
    and delete all user-defined chains:
    iptables -X

Setting the Essential Rules and Default Policies

Since any secure firewall is based on whitelisting rather than blacklisting, once you've established a clean slate, you need to make sure that all packets which you don't explicitly approve will get dropped. However, if you're configuring your firewall over SSH, you first need to set up enough rules to keep your SSH connection from being blocked by the firewall. To that end, we'll add the rule for established and related packets that should be present in pretty much every firewall:

iptables -A INPUT -m state --state RELATED,ESTABLISHED -j ACCEPT
iptables -A OUTPUT -m state --state RELATED,ESTABLISHED -j ACCEPT

This basically tells IPTables that any incoming or outgoing packets which are related to an established connection should be let through. Since you presumably already have an established SSH connection, this should be good enough for now, but just in case your connection goes down in the next 10 seconds, it'd be a good idea to add an explicit rule to allow packets to the port SSH is listening on, which by default is port 22. Many people change their SSH daemons to listen on a different port, though (it makes hackers' jobs harder); if you've done that, replace 22 with your chosen SSH port number in the following command.

iptables -A INPUT -p tcp --dport 22 -j ACCEPT

Now that SSH packets can pass safely, we can change the default policies to DROP:

iptables -P INPUT DROP
iptables -P FORWARD DROP
iptables -P OUTPUT DROP

Loopback and Loopback Spoofing

From this point on, the order of the rules generally isn't that important. I'm going to handle the loopback interface next since it's a good thing to have near the top of the ruleset.

The loopback interface is an internal network interface used to send messages from the computer to itself. It's a virtual circuit inside the computer which cannot be accessed except by programs running locally, so anything sent over the loopback interface should be safe:

iptables -A INPUT -i lo -j ACCEPT
iptables -A OUTPUT -o lo -j ACCEPT

However, we do have to be careful about the loopback address, which is 127.0.0.1 (or more generally, anything beginning with 127). People sometimes assume that any packet which is addressed to or from 127.0.0.1 is automatically on the loopback interface; after all, if it came from another computer, wouldn't it have been delivered to that computer's loopback interface? Usually yes, but hackers can be tricky and we don't want to take any chances, so let's put in rules to make sure that only the loopback interface handles the loopback address:

iptables -A INPUT -s 127.0.0.0/8 -i ! lo -j DROP
iptables -A INPUT -d 127.0.0.0/8 -i ! lo -j DROP
iptables -A OUTPUT -s 127.0.0.0/8 -o ! lo -j DROP
iptables -A OUTPUT -d 127.0.0.0/8 -o ! lo -j DROP

This particular set of rules covers not only 127.0.0.1, but all addresses whose first 8 bits match 127.0.0.0. You should never be receiving a valid packet from an address of this form if it's not on the loopback interface.

Local Network Interface

Say your computer has a network interface, let's call it eth1, which connects to a private network that you want to use to communicate with other computers you control. Obviously, anything that comes in on this interface that's not from one of your other computers should be discarded, and similarly for anything going out on the interface that's not destined for one of your other computers. These rules enforce those restrictions:

iptables -A INPUT -i eth1 -m iprange --src-range 172.168.110.101-172.168.110.103 -j ACCEPT
iptables -A OUTPUT -o eth1 -m iprange --dst-range 172.168.110.101-172.168.110.103 -j ACCEPT

Obviously, change the IP address range to match your own private network. If your IP addresses don't fall in a contiguous range, you'll need separate rules instead, something like this:

iptables -A INPUT -i eth1 --source 172.168.110.101 -j ACCEPT
iptables -A INPUT -i eth1 --source 172.168.110.102 -j ACCEPT
iptables -A INPUT -i eth1 --source 172.168.110.103 -j ACCEPT
iptables -A OUTPUT -o eth1 --destination 172.168.110.101 -j ACCEPT
iptables -A OUTPUT -o eth1 --destination 172.168.110.102 -j ACCEPT
iptables -A OUTPUT -o eth1 --destination 172.168.110.103 -j ACCEPT

This ability to restrict packets by source on a specific interface can be especially useful when the network includes some computers you control and some that you don't: you can make sure that only packets going to or coming from one of your computers get through the firewall.

Useful ICMP Packets

ICMP packets are used for many different kinds of status notifications over the internet, but some can give away information that might be useful to hackers. Some of the ones that are useful to allow include ICMP 0 echo reply (used to check if your server is running with ping), ICMP 3 destination unreachable (sent by firewalls that want to explicitly show that a port is closed), ICMP 8 echo request (what prompts a server to send an ICMP 0 echo reply), and ICMP 11 time exceeded (sent when a packet gets lost in the internet, necessary for traceroute). These rules allow those kinds of packets through:

iptables -A INPUT -p icmp -m icmp --icmp-type 0/0 -j ACCEPT
iptables -A INPUT -p icmp -m icmp --icmp-type 3 -j ACCEPT
iptables -A INPUT -p icmp -m icmp --icmp-type 8/0 -j ACCEPT
iptables -A INPUT -p icmp -m icmp --icmp-type 11 -j ACCEPT
iptables -A OUTPUT -p icmp -m icmp --icmp-type 0/0 -j ACCEPT
iptables -A OUTPUT -p icmp -m icmp --icmp-type 3 -j ACCEPT
iptables -A OUTPUT -p icmp -m icmp --icmp-type 8/0 -j ACCEPT
iptables -A OUTPUT -p icmp -m icmp --icmp-type 11 -j ACCEPT

If you want to read up on the other types of ICMP packets and decide whether to let any of them through as well, have a look at IANA's official list of ICMP packet numbers.

Allowing DNS Resolution

DNS, the domain name system protocol, is a fundamental part of how the internet works; it's how computers translate hostnames (like www.ellipsix.net) to IP addresses (173.45.227.179). Unless you're running a DNS server on your computer, you can probably get away without allowing DNS packets through, but it's a good idea to do so anyway because you never know what will try to use them. A lot of programs have a legitimate need to resolve hostnames to IP addresses, even ones you sometimes wouldn't expect, and it doesn't do much harm to let them through. These rules allow DNS queries via TCP and UDP (the latter is the usual method):

iptables -N DNS
iptables -A DNS -s 172.168.95.1 -j ACCEPT
iptables -A DNS -s 172.168.95.2 -j ACCEPT
iptables -A DNS -j DROP
iptables -A INPUT -p tcp -m tcp --dport 53 -j DNS
iptables -A INPUT -p udp -m udp --dport 53 -j DNS
iptables -A OUTPUT -p tcp -m tcp --dport 53 -j ACCEPT
iptables -A OUTPUT -p udp -m udp --dport 53 -j ACCEPT
This is an example of creating and using a custom chain — here it's called DNS.

Logging Dropped Packets

Last but not least — in fact, nearly the most important rules — are the ones that implement logging of dropped packets. You should always enable logging so that if you make a mistake configuring the firewall or forget to account for some program that needs to use the internet, there will be some evidence of the dropped packets that you can use to figure out what's going on. Additionally, tools like Fail2Ban or DenyHosts use these log records to block computers that are trying to hack your server (and I highly recommend installing one of them, but that's beyond the scope of this page). These rules enable logging of all dropped packets on the INPUT or OUTPUT chains:

iptables -A INPUT -m limit --limit 100/minute -j LOG --log-prefix "iptables denied (in): " --log-level 7
iptables -A OUTPUT -m limit --limit 100/minute -j LOG --log-prefix "iptables denied (out): " --log-level 7

The --limit 100/minute option limits the rate of packet logging to 100 packets per minute; that is, only the first 100 packets dropped by the firewall in any given minute will be logged. The rest will be discarded without any trace. This is important because it keeps your server responsive while it's under attack: if the server does get flooded with packets it needs to reject, the time it takes to create all those log records adds up quickly, and the more time iptables spends writing to the log, the less time the computer has to do legitimate work like serving web pages.

In practice, you'll rarely hit 100 dropped packets per minute unless your server is an especially popular target. You can probably drop the limit as low as, say, 5 or 10 per minute without having problems.

Saving the Configuration

Okay, I lied, actually this is the last section. But it's often unnecessary, since many Linux distributions (including Gentoo) will automatically save the firewall state when you shut down the computer and restore it when you restart.Note that you do not need to restart the computer after applying firewall rules; the commands in this page take effect immediately. If you do need to save the configuration, just run

iptables-save > firewall.ipt

where firewall.ipt is the name of the file you want to save the configuration to. To load the saved configuration back into the firewall, run

iptables-restore < firewall.ipt

This is actually what Linux does behind the scenes to preserve the firewall state.