iptables by example

Introduction

Iptables is the default firewall on Linux. It's usually installed by default, and available on most distributions.
This page is meant to show a couple of basics, that you might need when securing a system that's available on the internet.

Listing your current ruleset

To list your current ruleset, run 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

What you see here are three default "Chains":

  • INPUT: Inbound packets that have the current machine as target
  • FORWARD: Packets that transfer through this machine: for example when acting as a router/firewall
  • OUTPUT: Packets that are sent outbound from the current machine

It's possible to make more chains with iptables -N if you need them.

Setting the default policy for a chain

In the iptables -L output, you see what the default policy is for a chain. (Standard, it's to accept all packets).
Use iptables -P to set the policy on a chain.

iptables -P INPUT DROP

This drops(discards) the packets that are coming in on the network.

iptables -P OUTPUT ACCEPT

Allows outbound packets.

Note: If you want to drop inbound connections, but allow outgoing ones, you'll need something more than just the two above rules. More on that later.

Appending new rules to a chain

In the above output, you can see that there are no active firewall rules. What you do, is append specific rules to a chain with "iptables -A ":

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

This rule allows inbound packets that are using the TCP protocol, and have port 22 (ssh) as destination port.

iptables -A OUTPUT -p udp --dport 53 -j ACCEPT

This rule allows outbound UDP packets on port 53 (dns lookups).

Allowing "return packets"

To have a working connection, you'll also need to allow "return packets". It's not good enough that a package coming in on port 80 is being accepted, the response should be allowed to go back out as well! What you could do, is add rules like this:

iptables -A INPUT -p tcp --dport 80 -j ACCEPT
iptables -A OUTPUT -p tcp --sport 80 -j ACCEPT

However, this means that you'll need duplicate rules for pretty much everything that you want in your firewall. Also, if you have a rule to allow all outbound traffic like this:

iptables -P OUTPUT ACCEPT

And are blocking inbound traffic:

iptables -P INPUT DROP

Then you would have to add a rule somehow to allow packets for those connections to come back in.

Luckily, there is the "connection tracking module". This module is able to identify packets as being a part of an existing "connection". (Note that this works for all protocols, and not just TCP).
To allow all outgoing connections, block all incoming ones, but allow inbound packets that are part of an existing connections:

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

Flushing a chain

To empty an existing chain, use the -F option. However, be careful when flushing chains. It might remove a rule that you need for accessing a server remotely:

iptables -F INPUT

Rules that you probably want

Allow packets that are already member of an existing connection:

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

Allow ping:

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

Inserting rules in a chain

If you want to add a rule on a specific spot, and not just at the end of what is already there, use iptables -I:

This will insert a line at the top of the chain:

iptables -I INPUT -p tcp --dport 80 -j ACCEPT

This will insert a rule at spot number 5 in the chain: (use iptables -L --line-numbers to see rulenumbers)

iptables -I INPUT 5 -p tcp --dport 80 -j ACCEPT

Removing rules from a chain

Use iptables -D to remove rules:

This will remove rule number 3: (use iptables -L --line-numbers to see rulenumbers)

iptables -D INPUT 3

Get more output from iptables -L

I like to use:

iptables -L -n -v

This will not resolve port names or look for reverse dns entries for listed ip address.

Have the firewall come up when booting

Debian
Use iptables-save to store the firewall configuration in a file. Create an "if-up" script that will be executed every time an interface is brought up to use iptables-restore, that will restore the firewall from the saved file:

iptables-save > /etc/iptables.up.rules

Edit /etc/network/if-up.d/iptables and insert the following:

#!/bin/bash
/sbin/iptables-restore 

Then make it executable:

chmod +x /etc/network/if-up.d/iptables

Examples

# Drop inbound connections, allow everything out:
iptables -P INPUT DROP
iptables -A INPUT -m state --state RELATED,ESTABLISHED -j ACCEPT

# Allow outbound connections, allow inbound ping, http, https, dns lookups and secure shell
iptables -P INPUT DROP
iptables -A INPUT -m state --state RELATED,ESTABLISHED -j ACCEPT
iptables -A INPUT -p icmp --icmp-type 8 -j ACCEPT
iptables -A INPUT -p tcp --dport 80 -j ACCEPT
iptables -A INPUT -p tcp --dport 443 -j ACCEPT
iptables -A INPUT -p tcp --dport 22 -j ACCEPT
iptables -A INPUT -p udp --dport 50 -j ACCEPT