IPTables is the most common firewall implementation for Linux. It consists of two parts:
net/netfiltersubdirectory 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.
/sbin/iptables, and you need to be logged in as root to do anything meaningful with it.
sudoto all commands in this tutorial.
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.
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.
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,
iptables-save > iptout
iptables -P INPUT ACCEPT iptables -P OUTPUT ACCEPT
and delete all user-defined chains:
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
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.
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 22.214.171.124-126.96.36.199 -j ACCEPT iptables -A OUTPUT -o eth1 -m iprange --dst-range 188.8.131.52-184.108.40.206 -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 220.127.116.11 -j ACCEPT iptables -A INPUT -i eth1 --source 18.104.22.168 -j ACCEPT iptables -A INPUT -i eth1 --source 22.214.171.124 -j ACCEPT iptables -A OUTPUT -o eth1 --destination 126.96.36.199 -j ACCEPT iptables -A OUTPUT -o eth1 --destination 188.8.131.52 -j ACCEPT iptables -A OUTPUT -o eth1 --destination 184.108.40.206 -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.
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.
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 (220.127.116.11). 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):
This is an example of creating and using a custom chain — here it's called DNS.
iptables -N DNS iptables -A DNS -s 18.104.22.168 -j ACCEPT iptables -A DNS -s 22.214.171.124 -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
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
--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.
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.
iptables-save > firewall.ipt
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.