iptables

Article by on May 15, 2014, last modified on May 18, 2014

Knowing how to use iptables is awesome. ufw is even more awesome, but I like the idea of having very simple firewall rules. If you use ufw your rules look awful, but it is a simpler interface.

Now, supposedly nftables is replacing iptables, so look out for that.

Starting Guide

A Few Notes

First, if you are wanting to completely overhaul your iptables consider deleting all iptables that are there (see Delete Rules section).

Second, know that more than likely you will lock yourself out of a server if you try editing iptables remotely. So, be prepared in case you do this by making sure you have access to a direct console to the computer. If you are using a hosting service like Rackspace, Linode, or Amazon you will be able to do it through their website.

Third, working with iptables is easiest when you are logged in as root. Or, run "sudo su -".

Fourth, if there are pre-existing firewall rules and you are modifying them be VERY VERY VERY careful. Like, write out on paper what you're going to do then type a line and read it three or four or fifteen times before hitting enter. Treat it with the same care you would "rm -rf".

Last, if you're looking for a quick and dirty "I don't care what my iptables are I want to wipe them clean and do something simple and secure" then see the Simple iptables Setup Example.

How to Read iptables

I think arkascha says it well enough in a StackOverflow response:

They are tested from top to bottom. If a rule matches and accepts or declines, then the process is stopped.

So you need to code exceptions first, then more general rules.

Source vs Destination

It's important to know the difference between the source port and destination port, because you have to specify which for every rule you make. However, to understand source vs destination you have to know how a TCP packet is constructed. Every TCP packet has a source IP, source port, destination IP, and destination port. So, you on your computer at 192.168.0.150 requesting a web page from your web server at 192.168.0.10 would send a packet with source IP of 192.168.0.150, source port of 80, destination IP of 192.168.0.10, and destination port of 80. Now, on the trip back the server would send a packet with source IP 192.168.0.10, source port 80, destination IP 192.168.0.150, and destination port 80.

Now, imagine you are ssh'd into a server and your local port is 55980 and the remote port is 22. The packet sent from your computer would have source IP 192.168.0.150, source port 55980, destination IP 192.168.0.10, and destination port 22. The packet coming back from the server would have source IP 192.168.0.10, source port 22, destination IP 192.168.0.150, and destination port 55980.

So, when you are writing rules you generally write INPUT rules with source IP and destination port and OUTPUT rules with destination IP and source port. Now, if you think it about it this makes sense. An INPUT tcp packet's source IP will be the IP of the remote machine and the destination port will be the port on  your local machine. Most of the INPUT rules you write are targeting a remote IP or your local port. An OUTPUT tcp packet's destination IP will be the IP of the remote machine and the source port will be the port of your local machine. Most of the OUTPUT rules you write are targeting a remote IP or your local port. Thus, the general format of most of your rules will look like:

$ iptables -A INPUT -p tcp -s 192.168.0.150 --dport 22 -j ACCEPT

or

$ iptables -A OUTPUT -p tcp -d 192.168.0.150 --sport 22 -j ACCEPT

Again, this is a general rule not a requirement. Here are some further reading links on source vs destination:

Simple iptables Setup Example

If you want a simple example here is what I use for very simple stupid security:

# completely clear iptables (removes anything that pre-existed)
iptables -F
iptables -X

# accept everything no matter port on localhost
iptables -A INPUT -i lo -j ACCEPT

# allow input on the following ports
iptables -A INPUT -p tcp --dport 22 -j ACCEPT

# allow output on the following ports
iptables -A OUTPUT -p tcp --sport 22 -j ACCEPT
iptables -A OUTPUT -p tcp --sport 80 -j ACCEPT
iptables -A OUTPUT -p tcp --sport 443 -j ACCEPT

# set default policy to drop
iptables -P INPUT DROP
iptables -P OUTPUT DROP
iptables -P FORWARD DROP

This will deny everything incoming except port 22 and allow all outgoing on port 22, 80, and 443. You will probably want outgoing 80 and 443 for running system updates and such. The result looks like:

Chain INPUT (policy DROP)
target     prot opt source        destination         
ACCEPT     all  --  0.0.0.0/0     0.0.0.0/0           
ACCEPT     tcp  --  0.0.0.0/0     0.0.0.0/0     tcp dpt:22

Chain FORWARD (policy DROP)
target     prot opt source        destination         

Chain OUTPUT (policy DROP)
target     prot opt source        destination         
ACCEPT     tcp  --  0.0.0.0/0     0.0.0.0/0     tcp spt:22
ACCEPT     tcp  --  0.0.0.0/0     0.0.0.0/0     tcp spt:80
ACCEPT     tcp  --  0.0.0.0/0     0.0.0.0/0     tcp spt:443

Now, if you want something that's more usable I have written a setup script gist iptables-setup.sh.

Testing Ports

This section I will talk about how to test for open ports. The easiest way is:

$ nc -l 80

Run the above on your server then make a request to it from outside your server to see if you get a response, for example:

$ echo "this port is open" | nc 192.168.0.150 80

Then, on your server 192.168.0.150 you should see "this port is open" in your console. If it's not open you won't see anything in your console on the server and netcat (nc) on your client will hang / timeout.

Listing Rules

To list all rules, run:

$ iptables --list
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

My favorite way to list rules is the following:

$ iptables --list --numeric --line-numbers --verbose

This will list them as all IP's (--numeric) and show line numbers for each rule (--line-numbers) and show what network interface is used (--verbose). The reason I like this is because I'm always working with IP's, not hostnames, and if you know the line number it makes it easier to delete.

Setting Default Policies

Show Your Default Policy

First and foremost, know your default policy. Your default policy will show up just to the right of your chain, for example:

$ iptables --list
Chain INPUT (policy ACCEPT)
...

That shows a default policy of ACCEPT. So, any rule not matching the rules listed will be accepted.

$ iptables --list
Chain INPUT (policy DROP)
...

That shows a default policy of DROP. So, any rule not matching the rules listed will be dropped.

I find the easiest way to manage security is to make your default policy DROP. This ensures that only the traffic you specify will get through.

Set Your Default Policy

To set your default policy, run:

$ iptables -P INPUT ACCEPT

where "INPUT" can be whatever your chain is (e.g. "FORWARD" or "OUTPUT") and ACCEPT can be "DROP" or "RETURN".

Adding and Removing Rules

Before you begin blocking or opening ports you need to know how to add and remove rules. A rule will state whether a port is open or closed.

Add A Rule

The format for adding a rule is:

$ iptables -A INPUT -i eth0 -j ACCEPT

Where "INPUT" can be changed to "FORWARD" or "OUTPUT", "eth0" can be changed to whatever your ethernet device is, and "ACCEPT" can be changed to "DROP" or "RETURN".

Add A Rule At a Line

Say you are OCD and you want to insert a rule at line two given the following rules:

Chain OUTPUT (policy DROP 0 packets, 0 bytes)
num   target     prot opt source         destination         
1     ACCEPT     tcp  --  0.0.0.0/0      0.0.0.0/0      tcp spt:80
2     ACCEPT     tcp  --  0.0.0.0/0      0.0.0.0/0      tcp spt:6667

To do this, you would run:

$ iptables -I OUTPUT 2 -p tcp --sport 443 -j ACCEPT

Now, you have inserted a rule at line two and your ports are listed in ascending order :)

Chain OUTPUT (policy DROP 0 packets, 0 bytes)
num   target     prot opt source         destination         
1     ACCEPT     tcp  --  0.0.0.0/0      0.0.0.0/0      tcp spt:80
2     ACCEPT     tcp  --  0.0.0.0/0      0.0.0.0/0      tcp spt:443
3     ACCEPT     tcp  --  0.0.0.0/0      0.0.0.0/0      tcp spt:6667

Drop A Rule

The format for dropping a rule is:

 $ iptables -D INPUT 1

Where "INPUT" is whatever chain you are editing, usually "INPUT", "FORWARD", or "OUTPUT" unless you create your own. And, "1" is the line number given from iptables --list --line-numbers.

Delete All Rules

To delete all rules, run:

# Flush the selected chain (all the chains in the table if none is given). This is equivalent to deleting all the rules one by one.
$ iptables -F

To delete the entire chain:

# Delete the optional user-defined chain specified. There must be no references to the chain. If there are, you must delete or replace the referring rules before the chain can be deleted. The chain must be empty, i.e. not contain any rules. If no argument is given, it will attempt to delete every non-builtin chain in the table.
$ iptables -X

Running the above two commands will completely clear your iptables for you if you are wanting to start from scratch. A clean rule set will look like:

$ 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

Blocking Ports

Before you open any ports you will most likely want to close the ones that you don't want through. So, here is how you block a port:

$ iptables -A INPUT -p tcp --dport 22 -j DROP

This will drop all incoming traffic to port 22.

Opening Ports

The following will allow all incoming traffic to port 22:

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

Backing Up, Saving, Resetting, and Restoring iptables

Finally, on to backing up, saving, and restoring.

Backing Up iptables

To back up iptables, run:

$ iptables-save > /etc/iptables.bup

Where "/etc/iptables.bup" is where you want to save them. One time I saved them in /etc/sysconfig/iptables.bup, fwiw.

Saving iptables

By default, iptables do not get saved to disk. This means that on restart your firewall rules will be destroyed. To save iptables, run:

$ /etc/init.d/iptables save

To check if they saved you can run /etc/init.d/iptables restart. However, if you are on Ubuntu you have to install iptables-persistent and then save them to:

$ iptables-save > /etc/iptables/rules.v4

Resetting iptables

Say you changed your iptables but don't like what you did. To reset your iptables to the last saved state, run:

 $ /etc/init.d/iptables restart

You can also run this to check if your iptables were saved properly.

Restoring iptables

To restore iptables from a file, run:

$ iptables-restore < /etc/iptables.bup

A Note on UFW

Before you go digging around, check to see if your system is using ufw. If it is, commands are as simple as:

$ sudo ufw allow ftp

or

$ sudo ufw allow 222

An Odd Note

If you don't have an iptables, you may need to set them up by adding the following file, /etc/network/if-pre-up.d/iptablesload, with the following contents:

#!/bin/sh
iptables-restore < /etc/iptables.rules
exit 0

This tutorial explains in more depth:

https://help.ubuntu.com/community/IptablesHowTo

Topics Not Covered

  • Logging
  • Chains
  • NAT

Further Reading

Older Articles »