Configuring iptables

So you want to prevent uninvited guests from accessing your system? Iptables is a program that lets you define the access tables for the linux kernel firewall. I will show you an example of a set of filter rules that will keep your server pretty safe.

Download the sample file containing the rules to be explained.

All packets passing through the firewall transverse different tables (queues) and chains. There are three built-in tables dedicated to handling different parts of the process. There is the filter table, which we will be configuring later, handling the filtering of packets. There is the mangle table, allowing you to change different parts of the TCP headers of the packets, like ToS (Type of service). Lastly there is the nat table responsible for network address translation.

Each table or queue has a set of chains that a packet will pass through, depending on the type of packet. We will focus on the filter table and it’s three chains; INPUT, OUTPUT and FORWARD. Packets destined to the local machine will pass through the input chain, packets going from our local machine through the output chain and packets that are just passing by will pass through the forward chain.

Each rule in a chain will inspect the passing packet and either take action or pass it on to the next rule.

Lets start by adding some rules and I will explain as we go.

You add rules with the iptables(8) command and a set of flags defining the rule. You can always list the current rules by executing

iptables --list

Starting with the first rules in the sample file:

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

The -P flag indicate that we’re adding a policy. A policy defines the default action to take if there is a packet that was not affected by any other rule in our chain. [1]
-P, –policy chain target
Set the policy for the chain to the given target. See the section TARGETS for the legal targets. Only built-in (non-user-defined) chains can have policies, and neither built-in nor user-defined chains can be policy targets.

Next we tell iptables which chain the policy is to affect. We add one policy for each chain in the filter table; INPUT, FORWARD and OUTPUT.

Lastly we define what action is to be taken. We set the policy to drop all incoming and passing packets, and allowing all outgoing packets. If we were not to define any other rules the machine would be completely inaccessible over the network. You should be careful adding rules to iptables since you might shut yourself out. If you have a vps at Linode you could always access it through their control panel if you mess up.

Next we flush all existing rules. This does not affect the policies we set up earlier.[2] -F, –flush [chain]
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 INPUT
iptables -F FORWARD
iptables -F OUTPUT

Some loopback rules:

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

The -A flag tells iptables to append the rule to a chain. We append these two rules to the INPUT chain.[3] -A, –append chain rule-specification
Append one or more rules to the end of the selected chain. When the source and/or destination names resolve to more than one address, a rule will be added for each possible address combination.

The -i flag specifies an interface. The first rule affects all packets going to the loopback interface, lo. The second rule all packets NOT going to loopback.[4]
[!] -i, –in-interface name
Name of an interface via which a packet was received (only for packets entering the INPUT, FORWARD and PREROUTING chains). When the “!” argument is used before the interface name, the sense is inverted. If the interface name ends in a “+”, then any interface which begins with this name will match. If this option is omitted, any inter‐face name will match.

The -d flag is used to set a destination address.[5]
[!] -d, –destination address[/mask]
Destination specification. See the description of the -s (source) flag for a detailed description of the syntax. The flag –dst is an alias for this option.

Lastly the -j flag tells iptables what to do with the packet ( j is for jump ). What this means is that all packets going to loopback will jump to the ACCEPT action, skipping all other rules. All packets going to 127/8 that doesn’t use the loopback interface will be rejected.[6]
-j, –jump target
This specifies the target of the rule; i.e., what to do if the packet matches it. The target can be a user-defined chain (other than the one this rule is in), one of the special builtin targets which decide the fate of the packet immediately, or an extension (see EXTENSIONS below). If this option is omitted in a rule (and -g is not used), then matching the rule will have no effect on the packet’s fate, but the counters on the rule will be incremented.

Moving on:

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

This will tell iptables to use the state module, filtering the packet according to connection states. The states are defined by the –state flag in a comma separated list. Till rule accepts all established traffic. [7] [!] –state state
Where state is a comma separated list of the connection states to match. Possible states are INVALID meaning that the packet could not be identified for some reason which includes running out of memory and ICMP errors which don’t correspond to any known connection, ESTABLISHED meaning that the packet is associated with a connection which has seen packets in both directions, NEW meaning that the packet has started a new connection, or otherwise associated with a connection which has not seen packets in both directions, and RELATED meaning that the packet is starting a new connection, but is associated with an existing connection, such as an FTP data transfer, or an ICMP error.

We will also accept all outbound traffic:

iptables -A OUTPUT -j ACCEPT

This rule is redundant since we already set up an accept policy for the OUTPUT chain. Redundancy never hurts though.

Next we will accept traffic according to destination port:

iptables -A INPUT -p tcp -s 0/0 -d 0/0 --destination-port 22 --syn -j ACCEPT
iptables -A INPUT -p tcp -s 0/0 -d 0/0 --destination-port 80 --syn -j ACCEPT

The -p flag matches a specific protocol. Default is all, but we want to match only the tcp protocol.[8]
[!] -p, –protocol protocol
The protocol of the rule or of the packet to check. The specified protocol can be one of tcp, udp, udplite, icmp, esp, ah, sctp or all, or it can be a numeric value, representing one of these protocols or a different one. A protocol name from /etc/protocols is also allowed. A “!” argument before the protocol inverts the test. The number zero is equivalent to all. Protocol all will match with all protocols and is taken as default when this option is omitted.

The -s flag matches a source address or hostname. In our case this matches all sources, and as such is not needed.[9]
[!] -s, –source address[/mask]
Source specification. Address can be either a network name, a hostname (please note that specifying any name to be resolved with a remote query such as DNS is a really bad idea), a network IP address (with /mask), or a plain IP address. The mask can be either a network mask or a plain number, specifying the number of 1’s at the left side of the network mask. Thus, a mask of 24 is equivalent to 255.255.255.0. A “!” argument before the address specification inverts the sense of the address. The flag –src is an alias for this option.

The -d flag matches a destination address or hostname. In our case this matches all destinations, and as such is not needed.[10]
[!] -d, –destination address[/mask]
Destination specification. See the description of the -s (source) flag for a detailed description of the syntax. The flag –dst is an alias for this option.

The –destination-port specifies the destination port. We add one rule for SSH, port 22 and one for HTTP, port 80.[11]
[!] –destination-port,–dport port[:port]

We also add the flag –syn to match only the syn packets of the tcp protocol. This is done because all established traffic is already accepted.[12] [!] –syn
Only match TCP packets with the SYN bit set and the ACK,RST and FIN bits cleared. Such packets are used to request TCP connection initiation; for example, blocking such packets coming in an interface will prevent incoming TCP connections, but outgoing TCP connections will be unaffected. It is equivalent to –tcp-flags SYN,RST,ACK,FIN SYN. If the “!” flag precedes the “–syn”, the sense of the option is inverted.

You may add any such rules for the services you will be running on your machine.

We accept ping by allowing packets with the protocol icmp:

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

Log all packets that are denied:

iptables -A INPUT -m limit --limit 5/min -j LOG --log-prefix "iptables denied: " --log-level 7

These messages will be accessible via the dmesg(1) command.

Lastly we will drop any packets that did not match any of our rules. Once again this is redundant because of our policies.

iptables -A INPUT -j DROP
iptables -A FORWARD -j DROP

Now that you’re familiar with the rules, we need to make sure to load these rules every time our server boots up. Save the rules in a file called iptables and place it in /etc/network/if-pre-up.d/. All files placed here will be executed prior to the interfaces getting initialized.

Don’t forget to make the file executable!

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

If iptables denies an access attempt it gets logged in the syslog ( remember the line we added about logging? ). I like to have it in a custom log file for inspection, so you can add a setting to rsyslog like this.

Add this to /etc/rsyslog.d/iptables.conf. Create the file if needed.

:msg, startswith, "iptables denied: " -/var/log/iptables.log
& ~
:msg, regex, "^\[ *[0-9]*\.[0-9]*\] iptables denied: " -/var/log/iptables.log
& ~

This tells rsyslog to filter out any log message starting with or containing the words iptables denied: and store it in a log file called iptables.log located in /var/log/.

Note: if you changed the log text in the iptables config file, you need to make sure it matches the rsyslog setting.

All references in this article originates from the man page of iptables(8).

This entry was posted in System administration and tagged , , , . Bookmark the permalink.

Leave a Reply

Your email address will not be published. Required fields are marked *

*

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>