on Setting up a firewall using iptables

A properly configured firewall is one of the most important components in a server security. Iptables is a powerfull firewall tool installed on most Linux distributions and provides packet filtering and network address translation (NAT).

A number of front-end tools such as ufw, ferm and shorewall are available for making iptables management as easy as possible, but personally I prefer plain iptables.

To see the current iptables rules issue the following command:

sudo iptables -nvL

In order to use iptables, you need to have root privileges to make changes. This means you need to log in as root or to use sudo.

If you have Ubuntu installed and you haven't made any changes to the default iptables rules, you should see something like the following:

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

Iptables includes 3 predefined chains in the filter table by default. These default chains are:

  • INPUT - All packets destined for the host machine.
  • OUTPUT - All packets originating from the host machine.
  • FORWARD - All packets neither originating from nor destined for the host machine, but the packets that the host is routing.

Configure iptables

Iptables rules are processed in order from top to bottom, once the rule matched criteria no further processing is done. If no matching rule is found, then the default policy of that chain is applied to that packet.

As shown in the output of the sudo iptables -nvL command, on a Debian\Ubuntu machine the default chain policy for INPUT and FORWARD is set to ACCEPT, which we would set to DENY and add rules to accept traffic on loopback device and certain ports.

Adding rules

Before continuing with this guide, make sure the default chain policy is set to ACCEPT or you can easily lock yourself out of your machine. If the default policy is set DENY run the following command to set the policy to ACCEPT:

sudo iptables -P INPUT ACCEPT

First, we'll flush all existing rules so we can start with clean tables.

sudo iptables -F

Let's start adding some basic rules, we can use the -A switch to append or -I switch to prepend a rule to a specific chain.

The first and foremost we have to allow all loopback (lo0) traffic and drop all traffic to 127.0.0.0/8 that doesn't use the lo0 interface:

sudo iptables -A INPUT -i lo -j ACCEPT
sudo iptables -A INPUT ! -i lo -d 127.0.0.0/8 -j REJECT

The following rule will accept all established connection

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

The next rule will allow icmp packets also known as ping packets.

sudo iptables -A INPUT -p icmp -m icmp --icmp-type 8 -j ACCEPT

By default SSH uses port 22, and we want to allow remote logins

sudo iptables -A INPUT -p tcp -m state --state NEW --dport 22 -j ACCEPT

If you have changed the default SSH port, you need to allow traffic from that port. For example, if you have read Initial Ubuntu Server Setup and changed the SSH port to 9922 then issue the following command

sudo iptables -A INPUT -p tcp -m state --state NEW --dport 9922 -j ACCEPT

Now we need to open some specific ports, depending on what applications are running on our machine.

If we have a web server (apache or nginx) running on the default ports 80 and 443, we need to add a rule to the INPUT chain to allow HTTP packets to go through the firewall:

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

Similar to the above, depending on what applications are running, we need to open some other ports as well. Below are the most common services/applications and their ports:

| Port | Service |
|------|---------|
| 21   | FTP     |
| 22   | SSH     |
| 23   | Telnet  |
| 25   | SMTP    |
| 43   | WHOIS   |
| 53   | DNS     |
| 80   | HTTP    |
| 110  | POP3    |
| 123  | NTP     |
| 143  | IMAP    |
| 161  | SNMP    |
| 443  | HTTPS   |
| 465  | SMTPS   |
| 587  | MSA     |
| 993  | IMAPS   |
| 995  | POP3S   |

Set the default chain policy for INPUT and FORWARD to DENY

sudo iptables -P INPUT DROP
sudo iptables -P FORWARD DROP
sudo iptables -P OUTPUT ACCEPT

Deleting rules

To delete a rule, use the -D switch, execute the same commands just replace the -A with -D. For example to remove the rule for port 443 run:

sudo iptables -D INPUT -p tcp --dport 443 -j ACCEPT

Also we can delete iptables rules by line number, first list the rules by chain number:

sudo iptables -nv -L --line-numbers

And then delete the line you want. For example to remove rule number 7 run:

sudo iptables -D INPUT 7

Saving rules

All rules created with the iptables command are stored in memory and they will be lost when the system reboots. Instead of adding the same rules each time the system reboots, we can save the rules in a file and use that file to load the rules at boot time.

If you are running CentOS, you can save the current rules with the following command:

sudo service iptables save

If you are running Ubuntu, first you need to install the package called iptables-persistent and then save the rules.

sudo apt-get install iptables-persistent
sudo service iptables-persistent save

I just wanted to give a good starting point for building your own firewall rules. There is much more you can do with iptables, but in this article I only covered the basics.