Never Ending Security

It starts all here

Iptables Firewall Cheatsheet

How To: IPTables Firewall

Iptables is a standard firewall included in most Linux distributions by default. It is actually a front end to the kernel-level netfilter hooks that can manipulate the Linux network stack. It works by matching each packet that crosses the networking interface against a set of rules to decide what to do.


First, you should be aware that iptables commands must be run with root privileges. Let’s start head-first. This is an example output from the command sudo iptables -L -v:

Note that these are INPUT (in-bound traffic) examples.
target   prot   opt in  out source destination
ACCEPT   all    --  lo  any anywhere anywhere
ACCEPT   all    --  any any anywhere anywhere ctstate RELATED,ESTABLISHED
ACCEPT   tcp    --  any any anywhere anywhere tcp dpt:ssh
ACCEPT   tcp    --  any any anywhere anywhere tcp dpt:http
ACCEPT   tcp    --  any any anywhere anywhere tcp dpt:https
DROP     all    --  any any anywhere anywhere
iptables rules are followed in order. The first found to handle the traffic will determine what happens to the data being checked.

Let’s quickly go over this list of these rules so you can get the feel for it:

  1. Accept all traffic on “lo”, the “loopback” interface. This is essentially saying “Allow all internal traffic to pass through”
  2. Accept all traffic from currently established connections and related (Typically set so you don’t accidentaly block yourself from the server when editing firewall rules)
  3. Accept TCP traffic over port 22 (which iptables labels “ssh” by default).
  4. Accept TCP traffic over port 80 (which iptables labels “http” by default)
  5. Accept TCP traffic over port 443 (which iptables labels “https” by default)
  6. Drop anything and everything else

See how the last rule says to DROP all from/to anywhere? If a packet has passed all other rules without matching, it will reach this rule, which says to DROP the data. This means that we’ve only allowed current connections, SSH (tcp port 22), http (tcp port 80) and https (tcp port 443) traffic into our server! Everything else is blocked.

Again ( I can’t stress this enough since it is a source of so many problems ) The first rule that matches the traffic type (protocol, interface, source/destination and other types) will decide how to handle the traffic. Rules below a match are not applied. If more than one rule match the traffic type, then the 2nd rule will never be reached.

Now let’s go to the real world example On a fresh system ( Ubuntu in this case ) issuing iptables -L will produce the following output:

sudo 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

As you can see, we have our three default chains (INPUT,OUTPUT, and FORWARD). We can see each chain’s default policy (each chain has ACCEPT as its default policy) and we can also see some column headers – but we don’t see any actual rules. This is because Ubuntu doesn’t ship with a default rule set.

We can see the output in a format that reflects the commands necessary to enable each rule and policy by instead using the -S flag:

sudo iptables -S

You should ensure that the default policy on your INPUT and OUTPUT chains are set to ACCEPT.  You can do this by typing:

sudo iptables -P INPUT ACCEPT
sudo iptables -P OUTPUT ACCEPT
sudo iptables -F

If you have rules in place and wish to scrap them and start over, you can flush the current rules by typing:

sudo iptables -F

Make A Rule

We are going to start with the rule that explicitly accepts SSH connection.

sudo iptables -A INPUT -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT

This may look incredibly complicated, but most of it will make sense when we go over the components:

  • -A INPUT: The -A flag appends a rule to the end of a chain. This is the portion of the command that tells iptables that we wish to add a new rule, that we want that rule added to the end of the chain, and that the chain we want to operate on is the INPUT chain.
  • -m conntrack: iptables has a set of core functionality, but also has a set of extensions or modules that provide extra capabilities. In this portion of the command, we’re stating that we wish to have access to the functionality provided by the conntrack module. This module gives access to commands that can be used to make decisions based on the packet’s relationship to previous connections.
  • –ctstate: This is one of the commands made available by calling the conntrack module. This command allows us to match packets based on how they are related to packets we’ve seen before.
  • We pass it the value of ESTABLISHED to allow packets that are part of an existing connection. We pass it the value of RELATED to allow packets that are associated with an established connection. This is the portion of the rule that matches our current SSH session.
  • * -j ACCEPT: This specifies the target of matching packets. Here, we tell iptables that packets that match the preceding criteria should be accepted and allowed through.

We put this rule at the beginning because we want to make sure the connections we are already using are matched, accepted, and pulled out of the chain before reaching any DROP rules. We can see the changes if we list the rules:

sudo 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

Now that you know the general syntax, let’s continue by adding some more cases where we want to accept the connection.

We have told iptables to keep open any connections that are already open and to allow new connections related to those connections. Now we want to keep two specific ports open. We want to keep our SSH port and also our web server port (default 80) open.

The two lines we’re going to use to add these rules are:

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

As you can see, these are very similar to our first rule, but more simple. The new options are:

  • -p tcp: This option matches packets if the protocol being used is TCP.
  • –dport: This option is available if the -p tcp flag is given. It gives a further requirement of matching the destination port for the matching packet. The first rule matches for TCP packets destined for port 22, while the second rule matches TCP traffic pointed towards port 80.

There is one more accept rule that we need to ensure that our server can function correctly. Often, services on the computer communicate with each other by sending network packets to each other. They do this by utilizing a pseudo network interface called the loopback device, which directs traffic back to itself rather than to other computers.

The rule we need to add is this:

sudo iptables -I INPUT 1 -i lo -j ACCEPT

This looks a bit different than our other commands. Let’s go over what it is doing:

  • -I INPUT 1: The -I flag tells iptables to insert a rule. This is different than the -A flag which appends a rule to the end. The -I flag takes a chain and the rule position where you want to insert the new rule. In this case, we’re adding this rule as the very first rule of the INPUT chain. This will bump the rest of the rules down. We want this at the top because it is fundamental and should not be affected by subsequent rules.
  • * -i lo: This component of the rule matches if the interface that the packet is using is the “lo” interface. The “lo” interface is another name for the loopback device. This means that any packet using that interface to communicate (packets generated on our server, for our server) should be accepted.

To see our current rules, we should use the -S flag. This is because the -L flag doesn’t include some information, like the interface that a rule is tied to, which is an important part of the rule we just added:

sudo iptables -S
-A INPUT -i lo -j ACCEPT
-A INPUT -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT
-A INPUT -p tcp -m tcp --dport 22 -j ACCEPT
-A INPUT -p tcp -m tcp --dport 80 -j ACCEPT

Drop Rules

If a packet enters the INPUT chain and doesn’t match one of the four rules that we made, it is being passed to our default policy, which is to accept the packet anyways. We need to change this. There are two different ways that we can do this, with some pretty important differences. The first way we could do this is to modify the default policy of our INPUT chain. We can do this by typing:

sudo iptables -P INPUT DROP

This will catch any packets that fall through our INPUT chain, and drop them. This is what we call a default drop policy. One of the implications of this type of a design is that it falls back on dropping packets if the rules are flushed. This may be more secure, but also can have serious consequences if you don’t have another way of accessing your server. The alternative approach is to keep the default policy for the chain as accept and add a rule that drops every remaining packet to the bottom of the chain itself. If you changed the default policy for the INPUT chain above, you can set it back to follow along by typing:

sudo iptables -P INPUT ACCEPT

Now, you can add a rule to the bottom of the chain that will drop any remaining packets:

sudo iptables -A INPUT -j DROP

The result under normal operating conditions is exactly the same as a default drop policy. This rule works by matching every remaining packet that reaches it. This prevents a packet from ever dropping all of the way through the chain to reach the default policy. Of course, this also means that any additional rule that you wish to add to the end of the chain will have to be added before the drop rule. You can do this either by temporarily removing the drop rule:

sudo iptables -D INPUT -j DROP
sudo iptables -A INPUT new_rule
sudo iptables -A INPUT -j DROP

Or, you can insert rules that you need at the end of the chain (but prior to the drop) by specifying the line number. To insert a rule at line number 4, you could type:

sudo iptables -I INPUT 4 new_rule

If you are having trouble finding a line number, you can tell iptables to number the rules:

sudo iptables -L --line-numbers
Chain INPUT (policy DROP)
num  target     prot opt source               destination         
1    ACCEPT     all  --  anywhere             anywhere            
2    ACCEPT     all  --  anywhere             anywhere             ctstate RELATED,ESTABLISHED
3    ACCEPT     tcp  --  anywhere             anywhere             tcp dpt:ssh
4    ACCEPT     tcp  --  anywhere             anywhere             tcp dpt:http

Chain FORWARD (policy ACCEPT)
num  target     prot opt source               destination         

Chain OUTPUT (policy ACCEPT)
num  target     prot opt source               destination

Deleting Rules

Let’s say we want to change our SSH port to a non-standard port.  We can delete the SSH rule in two ways:

# Delete at position 3
sudo iptables -D INPUT 3  # Or delete by the rule:
sudo iptables -D INPUT -p tcp --dport 22-j ACCEPT

Then we can insert our new SSH rule at port 1234:

sudo iptables -I INPUT 3-p tcp --dport 1234-j ACCEPT

Save iptables

By default, the rules that you add to iptables are ephemeral. This means that when you restart your server, your iptables rules will be gone. This is actually a feature. However, most users will want a way to automatically save the rules you have created and to load them when the server starts. There are a few ways to do this, but the easiest way is with the iptables-persistent package. You can download this from Ubuntu’s default repositories:

sudo apt-get update
sudo apt-get install iptables-persistent

During the installation, you will be asked if you would like to save your current rules to be automatically loaded. If you are happy with your current configuration you can select to save your current rules. It will also ask you if you want to save the IPv6 rules that you have configured. These are configured through a separate utility called ip6tables which controls the flow of IPv6 packets in almost the same way. Once the installation is complete, you will have a new service called iptables-persistent that is configured to run at boot. This service will load in your rules and apply them when the server is started.

Troubleshooting ?

This is an optional step but it can be very useful in detecting network traffic problems.

Create a new chain called LOG:

iptables -N LOG

Route INPUT/OUTPUT/FORWARD to LOG chain (substitute CHAIN with the chain you want to monitor, such as “INPUT”):

iptables -A INPUT -j LOG

Now log the packets with this command:

iptables -A LOG -m limit --limit 60/min -j LOG --log-prefix "IPTables DROP: " --log-level 7

Now you can monitor your system messages to see which packets are being dropped. In Ubuntu, the messages can be read in real-time with the following command:

sudo tail -f /var/log/syslog

In CentOS, the messages can be read in real-time with the following command:

sudo tail -f /var/log/messages

The logs will list the interface, source port, destination port, and some other information about each packet dropped. This should help you figure out any issues that you may have.

Leave a Reply

Please log in using one of these methods to post your comment: Logo

You are commenting using your account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s