Iptables tracks the progress of connections through the connection lifecycle, so yu can inspect and restrict connections to services based on their connection state.
Although the underlying TCP connection state model is more complicated, the connection tracking logc assigns one of the states in below to each connection at any point of time.
NEW — A packet requesting a new connection, such as an HTTP request.
ESTABLISHED — A packet that is part of an existing connection.
RELATED — A packet that is requesting a new connection but is part of an existing connection. For example, FTP uses port 21 to establish a connection, but data is transferred on a different port (typically port 20).
INVALID — A packet that is not part of any connections in the connection tracking table.
State and Conntrack module
iptables uses a method called connection tracking to store information about incoming connections. You can allow or deny access based on the following connection states:
iptables -A FORWARD -m state --state ESTABLISHED,RELATED -j ACCEPT
iptables -A FORWARD -m conntrack -ctstate ESTABLISHED,RELATED -j ACCEPT
All of the connection tracking is done by special framework within the kernel called conntrack, state module allows access to the connection tracking state(listed above) for this packet. While conntrack module provides more extension for packet/connection tracking.
For example, in conntrack match extension, Statuses for --ctstatus allows you to track status information of each connection(maintained in 3 bits of status)
Statuses for --ctstatus:
NONE None of the below.
This is an expected connection (i.e. a conntrack helper set it up)
Conntrack has seen packets in both directions.
Conntrack entry should never be early-expired.
Connection is confirmed: originating packet has left box.
In addition to that, in conntrack module, it supports 2 more states
SNAT A virtual state, matching if the original source address differs from the reply destination.
DNAT A virtual state, matching if the original destination differs from the reply source.
Note: other states are same as listed with state module(NEW,ESTABLISHED,INVALID,RELATED)
How does iptables connection tracking work
All connection tracking is handled in the PREROUTING chain, except locally generated packets which are handled in the OUTPUT chain. What this means is that iptables will do all recalculation of states and so on within the PREROUTING chain. If we send the initial packet in a stream, the state gets set to NEW within the OUTPUT chain, and when we receive a return packet, the state gets changed in the PREROUTING chain to ESTABLISHED, and so on. If the first packet is not originated by ourself, the NEW state is set within the PREROUTING chain of course. So, all state changes and calculations are done within the PREROUTING and OUTPUT chains of the nat table.
Connection tracking is done to let the Netfilter framework know the state of a specific connection. Firewalls that implement this are generally called stateful firewalls. A stateful firewall is generally much more secure than non-stateful firewalls since it allows us to write much tighter rule-sets.
This is also to say, the tracking state is availale throught a packet lifecycle, we can setup rules to maniupate the Linux firewall.
iptables -A INPUT -m state --state NEW -m tcp -p tcp --dport 22 -j ACCEPT
iptables -A INPUT -s 192.168.1.0/24 -m state --state NEW -p tcp --dport 22 -j ACCEPT
The state table for udp and tcp connections is maintained in /proc/net/nf_conntrack. And the maximum number of connections the state table can contain is stored in /proc/sys/net/ip_conntrack_max. This value is determined initially by how much physical memory you have (on my 16GB machine, ip_conntrack_max = 8184 by default), surely you can set it in /etc/sysctl.conf
net.nf_conntrack_max = 65536
Connection tracking state in different protocols
Let's take a look three protocols, udp, tcp and icmp in turn.
UDP is known as a "stateless" protocol, mainly because they don't contain any connection establishment or connection closing; most of all they lack sequencing. Receiving two UDP datagrams in a specific order does not say anything about the order in which they were sent . However, this does not mean we can't track udp connections. It's still possible to set states on the connections within the kernel.
How, let's take a look a UDP connection state table entry, which is recorded in /proc/net/nf_conntrack
udp 17 19 src=192.168.1.2 dst=192.168.1.50 sport=1032 dport=53 [UNREPLIED] src=192.168.1.50 dst=192.168.1.2 sport=53 dport=1032 use=1
As you can see from the first and second values, this is an UDP packet. The first is the protocol name, and the second is protocol number.
The third value marks how many seconds this state entry has to live.
As you can see 'stateless' UDP in iptables conntrack module is short lived TCP, isn't it?
A tcp connection is initiated via a three-way handshake involving a synchronization request from the client, a synchronization and an acknowledgement from the server, and finally an acknowledgement from the client. Subsequent traffic flowing between server and client is acknowledged in all cases.
Surely tcp connection has all conntrack states. However, the important point here is that the conntrack states are not equivalent to tcp states. We a connection doesn't achive the tcp connection status of ESTABLISHED until the ACK after the SYN+ACK has been received.
To make sure that NEW tcp connections are packets with SYN set, use the following rule:
iptables -A INPUT -p tcp ! --syn -m state --state NEW -j DROP
Note that doing this will prevent idle sessions from continuing once they have expired from the conntrack table. In the normal "relaxed" view such connections initiated from the correct direction (i.e. the direction you allow NEW packets through) can normally continue even if expired from conntrack, provided that the first data/ack packet that resumes the connection comes from the correct direction.
CMP packets are far from a stateful stream, since they are only used for controlling and should never establish any connections. There are four ICMP types that will generate return packets however, and these have 2 different states. These ICMP messages can take the NEW and ESTABLISHED states.
The request in each case is classified as NEW and the reply as ESTABLISHED.
However, mostly ICMP is used to tell the hosts what happened to UDP and TCP connections or connection attempts, what about the host unreachable situation?
In that scenario, a reply from the last router tells the host the connection attempts not successful, is a RELATED packet.
Here is an examples:
iptables -A OUTPUT -p icmp -m state --state NEW,ESTABLISHED,RELATED -j ACCEPT
iptables -A INPUT -p icmp -m state --state ESTABLISHED,RELATED -j ACCEPT
The iptables connection tracking logic allows plugin modules to help identify new connections that are related to existing connections. You need to use these plugins if you want to make multiconnection protocols work right across your firewall.
Most common modules are ip_conntrack_amanda and ip_conntrack_ftp. Let's take one of them in discussion
Connection tracking and ftp
Lload the ip_conntrack_ftp module first
description: ftp connection tracking helper
vermagic: 2.6.32-504.3.3.el6.x86_64 SMP mod_unload modversions
parm: ports:array of ushort
Assuming you have a single-homed box, a simple ruleset to allow an ftp connection would be:
iptables -A INPUT -p tcp --sport 21 -m state --state ESTABLISHED -j ACCEPT
iptables -A OUTPUT -p tcp --dport 21 -m state --state NEW,ESTABLISHED -j ACCEPT
(Please note, I am assuming here you have a separate ruleset to allow any icmp RELATED to the conection. Please see my example ruleset for this).
This is not the whole story. An ftp connection also needs a data-channel, which can be provided in one of two ways:
1) Active ftp
The ftp client sends a port number over the ftp channel via a PORT command to the ftp server. The ftp server then connects from port 20 to this port to send data, such as a file, or the output from an ls command. The ftp-data connection is in the opposite sense from the original ftp connection.
To allow active ftp without knowing the port number that has been passed we need a general rule which allows connections from port 20 on remote ftp servers to high ports (port numbers > 1023) on ftp clients. This is simply too general to ever be secure.
Enter the ip_conntrack_ftp module. This module is able to recognize the PORT command and pick-out the port number. As such, the ftp-data connection can be classified as RELATED to the original outgoing connection to port 21 so we don't need NEW as a state match for the connection in the INPUT chain. The following rules will serve our purposes grandly:
iptables -A INPUT -p tcp --sport 20 -m state --state ESTABLISHED,RELATED -j ACCEPT
iptables -A OUTPUT -p tcp --dport 20 -m state --state ESTABLISHED -j ACCEPT
2) Passive ftp
A PORT command is again issued, but this time it is from the server to the client. The client connects to the server for data transfer. Since the connection is in the same sense as the original ftp connection, passive ftp is inherently more secure than active ftp, but note that this time we know even less about the port numbers. Now we have a connection between almost arbitrary port numbers.
Enter the ip_conntrack_ftp module once more. Again, this module is able to recognize the PORT command and pick-out the port number. Instead of NEW in the state match for the OUTPUT chain, we can use RELATED. The following rules will suffice:
iptables -A INPUT -p tcp --sport 1024: --dport 1024: -m state --state ESTABLISHED -j ACCEPT
iptables -A OUTPUT -p tcp --sport 1024: --dport 1024: -m state --state ESTABLISHED,RELATED -j ACCEPT
For more detail about connect state, see Iptables Connection State