iptables — a comprehensive guide
iptables is the command-line interface to the packet filtering functionality of the Linux kernel firewall — netfilter. It is still widely in use despite being replaced by nftables. For the sake of simplicity we could say that iptables allows us to administer the packet management system in Linux, rather than just a firewall. In this article, I will first explain how iptables itself works, and then some practical situations it is used in.
The core of iptables is simply rules, which is organized hierarchically in tables, chains and a list of rules in that order.
When a packet arrives (or leaves, depending on the chain), iptables matches it against rules in these chains one-by-one. When it finds a match, it jumps onto the target and performs the action associated with it. If it doesn’t find a match with any of the rules, it simply does what the default policy of the chain tells it to. Don’t worry just yet if you didn't get this the first try, all of these terms are explained individually below.
This is the entire concept of iptables, all packets are run through this ‘filtration system’ of rules, and it also provides more functionality than just filtering, it also allows you to perform all sorts of different actions when a packet matches any rules.
Tables
Tables reside at the top of the hierarchy, their function and distinguishing factor being the roles the rules they contain provide.
A table also contains multiple chains which are essentially a list of rules, grouped by the point in the flow of the packet. (e.g. just as they arrive on the NIC, just as they leave etc.) More on this in the next section.
The tables and their roles are as follows;
The filter table
This is the default and perhaps the most widely used table. This table contains the rules that provides the actual firewall functionality, it lets you decide whether a packet should be allowed or denied to reach its destination.
The nat table
This table contains rules that allows you to route packets to different hosts on NAT networks by changing the source and destination addresses of packets. It is often used to allow access to services that can’t be accessed directly, because they’re on a NAT network.
The mangle table
This table contain the rules that allows you to alter packet headers and other forms of packet alteration.
The raw table
The raw table allows you to work with packets before the kernel starts tracking its state. (For example, a packet could be part of a new connection, or it could be part of an existing connection)
In addition, you can also exempt certain packets from the state-tracking machinery. This basically means its used to let certain packets bypass the firewall.
The security table
The security table is consulted after the filter table to implement mandatory access control network rules. i.e. — it is used by SELinux to set SELinux context markers on packets.
Chains
Composition
A chain is a string of rules. When a packet is received, it triggers a netfilter hook, which corresponds to a default chain. iptables finds the appropriate table, then runs it through the chain and traverses a list of rules until it finds a match.
A rule is a statement that tells the system what to do with a packet. Rules can block one type of packet, or forward another type of packet.
The outcome of a rule or where a packet is sent, is called a target.
The default policy of a chain is also a target. The default policy of a chain is what happens to a packet that matches none of the rules. By default, all chains have a default policy of allowing packets.
Targets
A target is a decision of what to do with a packet. Typically, this is to accept it, drop it, or reject it (which sends an error back to the sender).
- ACCEPT: This causes iptables to accept the packet.
- DROP: iptables drops the packet. To anyone trying to connect to your system, it would appear like the system didn’t even exist.
- REJECT: iptables “rejects” the packet. It sends a “connection reset” packet in case of TCP, or a “destination host unreachable” packet in case of UDP or ICMP.
Default Chains
As mentioned earlier, chains represent the point in flow of a data packet. There are several default chains in iptables and tables contain a specific set of these chains (but not all of them). It is also possible to have your own custom chains.
Following is a list of the default chains and what point in flow of a data packet they represent;
- The PREROUTING chain
Rules in this chain apply to packets as they just arrive on the network interface. - The INPUT chain
Rules in this chain apply to packets just before they’re given to a local process. - The OUTPUT chain
The rules here apply to packets just after they’ve been produced by a process. - The FORWARD chain
The rules here apply to any packets that are routed through the current host. - The POSTROUTING chain
The rules in this chain apply to packets as they just leave the network interface.
skip this section for now
It is important to remember which chains are present in which tables and also the traversal order of chains. However this section is a bit overwhelming at first and also better understood after understanding the flow of a data packet, so I recommend skipping this for now and coming back to it later.
Table-chain mapping
- filter — INPUT OUTPUT FORWARD
- security — INPUT OUTPUT FORWARD
- raw — PREROUTING OUTPUT
- NAT — PREROUTING OUTPUT POSTROUTING
- mangle — PREROUTING OUTPUT INPUT FORWARD POSTROUTING
Packet Flow in iptables
To properly understand the iptables functionality, it can be divided into 3 ideate sections which we will use on a scenario basis;
(Take a quick peek at the complete overview of process section below for a illustrated view)
- Routing —(Referred in this article as the ‘Routing Section’)
Every Inbound packet first arrives at this section. This section is where its decided whether the packet is to be routed to the Host Inbound Firewall or out into another address on the network. - Host Inbound Firewall — Packets that are to be sent to a local socket.
- Host Outbound Firewall — Packets that are being sent out of the host.
Packet Entry — Routing Section I
When a packet first enters, it is run through the PREROUTING chains of the raw, mangle and nat tables, in that order.
- The raw table rules check if the packet is to be bypass the firewall or not.
- The mangle table rules check if the packet headers needs to be modified in any way.
- The nat table rules are consulted when a packet that creates a new connection is encountered.
- After this PREROUTING processing, a decision is made whether the packet is intended for the host system or not.
- If it is, it is then sent to the Host Firewall section, otherwise it is sent to the FORWARD processing area of the Routing section.
Host Inbound Firewall Section
After a packet enters this section, it is then sent to this INPUT processing section where it is run through the mangle, filter and security tables, in that order.
- The mangle table rules check if any headers or packet alteration is to be made.
- The filter table INPUT chain is where most of the commonly used firewall functionality happens. The rules in this table are what decides whether a packet is dropped or accepted before it is allowed to reach a host application.
- The security table INPUT chain implements any mandatory access control, mostly checking for SeLinux Context in data packets.
- The packet is finally received by the Host Application.
Host Outbound Firewall Section
You can also implement an outbound firewall to control what data is leaving your computer. This is done by OUTPUT chain processing where it is run through the raw, mangle, nat, filter and security tables in that order.
- The raw table OUTPUT chain rules decides whether the packet has privileges to bypass the firewall.
- The mangle table OUTPUT chain rules decides whether the packet headers needs to be modified.
- The nat table rules are consulted when a packet that creates a new connection is encountered.
- The filter table OUTPUT chain rules is where we decide whether we want to allow or deny the packet.
- The security table OUTPUT chain rules implements access control and SeLinux Control of data packets.
- It is then sent to Routing Section II where it is decided whether the packet is routable or not.
Packet Exit — Routing Section II
This is the area of the routing section where a packet is redirected outside the host. It is sent through the FORWARD chains of the mangle, filter and security tables, in that order.
- The mangle tables rules decide if any packet headers needs to modified before routing it out.
- The filter table rules allows us to decide whether or not we want to allow this packet to be routed.
- The security table rules decides whether the selinux or access control wants to allow this packet to be routed.
- Then it reaches section II, which is the same as step 6 in the outbound firewall, here it is first checked if this packet is routable or not.
- Its dropped if it is not routable.
- If it is routable then it is sent through POSTROUTING chains of the mangle and nat tables in that order.
- The mangle table rules decide if any packet headers needed to be modified after finding out it is routable.
- The nat table rules are consulted when a packet that creates a new connection is encountered.
- The packet is finally sent to the outbound interface.
Complete Overview of Process
Following is a holistic view of the entire process and can be used as a cheat-sheet to understand the whole process in one shot. I have also uploaded a higher resolution image of the above here.
Using iptables and practical uses
Now that we learnt how iptables actually works, lets see how to actually use it.
Note;
The different internet protocols are handled differently in the kernel, so there’s the command iptables for IPv4 and ip6tables for IPv6, however it works and is used in a very similar way.
Command Syntax
Lets take a look at the command syntax specified in iptables itself;
sudo iptables [option] CHAIN_rule [-j target]
An example command;
sudo iptables –A INPUT –s 192.168.0.27 –j ACCEPT
The same command in long format;
sudo iptables --append INPUT –-source 192.168.0.27 –-jump DROP
For the sake of easier comprehension we will only be using the long format in this article, you can switch to the short format in your real use.
Now you know how the commands look like, lets understand them. The best way to do this is by explaining real scenarios that we might run into.
Scenario 1: Blocking IPs
One of the most common use cases of a firewall, this is how we do it.
iptables \
--table filter \ # Use the filter table
--append INPUT \ # Append to the INPUT chain
--source 59.45.175.62 \ # This source address
--jump REJECT # Use the target Reject
If you want to delete this exact rule;
iptables \
--table filter \ # Use the filter table
--delete INPUT \ # Delete from the INPUT chain
--source 59.45.175.62 \ # This source address
--jump REJECT # Use the target Reject
or if you just want to change the IP;
iptables \
--table filter \ # Use the filter table
--replace INPUT \ # Replace from the INPUT chain
--source 59.45.175.62 \ # This source address
--jump REJECT # Use the target Reject
Note that you don't have to specify the filter table, since it is the default table, it will automatically be used if no table is specified.
Scenario 2: Adding a rule to a specific position in a chain
To do this, we will first print out an ordered list of the current rules, this can be done by;
iptables --list --line-numbers
which will give an output similar to;
Chain INPUT (policy ACCEPT)
num target prot opt source destination
1 DROP all -- 59.45.175.0/24 anywhere
2 DROP all -- 221.194.47.0/24 anywhere
3 DROP all -- 91.197.232.104/29 anywhere
Chain FORWARD (policy ACCEPT)
num target prot opt source destination
Chain OUTPUT (policy ACCEPT)
num target prot opt source destination
1 DROP all -- anywhere 31.13.78.0/24
In this scenario assume you want to block the entire IP Block 59.45.175.0/24, all except one address from it, 59.45.175.10. We have used the same process as earlier already to block the IP block. Now we want to whitelist the exception address, Since iptables evaluates rules in the chains one-by-one, you simply need to add a rule to “accept” traffic from this IP above the rule blocking 59.45.175.0/24. So in this case you just need to run the command;
iptables \
--table filter \
--insert INPUT 1 \
--source 59.45.175.10 \
--jump ACCEPT
If you list again now, you will see;
Chain INPUT (policy ACCEPT)
num target prot opt source destination
1 ACCEPT all -- 59.45.175.10 0.0.0.0/0
2 DROP all -- 59.45.175.0/24 0.0.0.0/0
3 DROP all -- 221.192.0.0/20 0.0.0.0/0
4 DROP all -- 91.197.232.104/29 0.0.0.0/0
You have now inserted a rule into a chain.
Scenario 3: Modifying Default Policy of a chain
We have discussed default policies in the introduction, it is simply what is done to a packet if none of the rules in a chain match, the default chains have an accept policy by default. Perhaps you’re configuring iptables for your home computer, and you don’t want any local programs to receive inbound connections. To change this use;
iptables \
--policy INPUT DROP
Scenario 4: Blocking access to ports
To block access to SSH port for a range;
iptables \
--append INPUT \
--protocol tcp \ # Specify TCP protocol
--match tcp \ # Load the TCP module
--dport 22 \ # Destination port
--source 59.45.175.0/24 \
--jump DROP
To block access to multiple ports, i.e. SSH and VNC access for a range;
iptables \
--append INPUT \
--protocol tcp \
--match multiport \ # Load multiport module
--dports 22,5901 \
--source 59.45.175.0/24 \
--jump DROP
To block access to all ports except the ones specified, i.e. all except HTTP, HTTPS and SSH (ideal for web server);
iptables \
--append INPUT \
--protocol tcp \
--match multiport \
! --dports 22,80,443 \
--jump DROP
To block Ping/ICMP requests;
iptables \
--append INPUT \
--jump REJECT \
--protocol icmp \
--icmp-type echo-request
Block with no error message;
iptables \
--append INPUT \
--protocol icmp \
--jump DROP \
--icmp-type echo-requestANDiptables \
--append OUTPUT \
--protocol icmp \
--jump DROP \
--icmp-type echo-reply
Scenario 4: Using Connection Tracking — Block incoming traffic but still access services
If you block IP on INPUT chain, you will find that you cannot access any services on that IP too, While that IP cannot access your host, you should still be able to access that IP. The reason why this is not working is because while your requests are reaching that server, iptables is dropping the responses coming to your hosts.
We can still achieve this, because iptables is a stateful firewall and has a connection tracking module for this purpose. The state tracked by this module can be either one of;
- NEW: This state represents the very first packet of a connection.
- ESTABLISHED: This state is used for packets that are part of an existing connection. For a connection to be in this state, it should have received a reply from the other host.
- RELATED: This state is used for connections that are related to another ESTABLISHED connection. An example of this is a FTP data connection — they’re “related” to the already “established” control connection.
- INVALID: This state means the packet doesn’t have a proper state. This may be due to several reasons, such as the system running out of memory or due to some types of ICMP traffic.
- UNTRACKED: Any packets exempted from connection tracking in the raw table with the NOTRACK target end up in this state.
- DNAT: This is a virtual state used to represent packets whose destination address was changed by rules in the nat table.
- SNAT: Like DNAT, this state represents packets whose source address was changed.
Therefore we can use the RELATED and ESTABLISHED states for connections that are initiated by us. To do this we can use the following command;
iptables \
--append INPUT
--match conntrack
--ctstate RELATED,ESTABLISHED \
--jump ACCEPT # Accept packets in above connection states
Scenario 5: Security Rules in iptables
To block Christmas tree packets aka kamikaze packets, which have all headers set so we need to simply drop packets with all headers. We can use command;
iptables \
--append INPUT \
--protocol tcp \
--match tcp \
--tcp-flags ALL FIN,PSH,URG \
--jump DROP
To block common invalid packets such as packets with SYN and FIN set, we can simply look for packets with both set. To do this use;
iptables \
--append INPUT \
--protocol tcp \
--match tcp \
--tcp-flags SYN,FIN SYN,FIN \
--jump DROP
And also packets that are a ‘new connection’ that doesn't begin with a SYN. To do this you simply need to check the FIN, RST, ACK and SYN flags; however only SYN should be set. Then, you can negate this condition. Finally, you can use conntrack to verify if the connection is new. Thus, the rule is:
iptables \
--append INPUT \
--protocol tcp \
--match conntrack \
--ctstate NEW
--match tcp ! \
--tcp-flags FIN,SYN,RST,ACK SYN \
--jump DROP
Scenario 6: Rate Limiting using iptables
This is done by using the limit module, it has two main parameters; ‘limit-burst’ acts as a buffer and is the buffer size, if this buffer size is exceeded all packets are dropped. The rate you can refill this buffer is the ‘limit’.
This can be done with 2 commands, first you must change the default policy of the INPUT chain to drop and then use; (i.e. rate limiting ICMP packets)
iptables \
--append INPUT \
--protocol icmp \
--match limit \
--limit 1/sec \
--limit-burst 1 \
--jump ACCEPT
You can also perform rate limiting dynamically for specific IP addresses, however this can be only performed by the recent module. It takes two commands and is done as follows;(i.e. Rate Limiting SSH per IP)
iptables \
--append INPUT \
--protocol tcp \
--match tcp \
--dport 22 \
--match conntrack \
--ctstate NEW \
--match recent \
--set \
--name SSHLIMIT
--rsourceiptables \
--append INPUT \
--protocol tcp \
--match tcp \
--dport 22 \
--match conntrack \
--ctstate NEW \
--match recent \
--set \
--name SSHLIMIT \
--update \
--seconds 180 \
--hitcount 5 \
--name SSH \
--rsource \
--jump DROP
Scenario 7: Logging packets using iptables
This is fairly simple and done using;
iptables \
--append INPUT \
--protocol tcp \
--match tcp \
--tcp-flags FIN,SYN FIN,SYN \
--jump LOG
--log-prefix=iptables:
The ‘ — log-prefix=’ is optional and is added so you can easily search the log later. The log is usually stored at /var/log/syslog
or /var/log/messages
.
Scenario 8: Port redirection In same machine
To redirect a port in your machine to another port, i.e. HTTP port to 8080
iptables \
--table nat \
--append PREROUTING \
--protocol tcp \
— dport 80 \
--jump REDIRECT \
--to 8080
Scenario 9: Turning iptables into a proxy!
Watch this amazing video by the great Hussein Nasser, he will explain it better than I ever could. Below are the iptables rules;
iptables \
--in-interface wlan0 \
--append PREROUTING \
--table nat \
--protocol tcp \
--destination 192.168.254.47 \
--dport 81 \
--jump DNAT \
--to-destination 192.168.254.10:8080iptables \
--append POSTROUTING \
--table nat \
--protocol tcp \
--destination 192.168.254.10 \
--dport 8080 \
--jump SNAT \
--to-source 192.168.254.47iptables \
--table nat \
--append POSTROUTING \
--protocol tcp \
--dport 81 \
--jump MASQUERADE
Bonus Scenarios:
- iptables as a load-balancer? — also by Hussein check it out here!
- Custom chains in iptables
- The owner module for rules only for internal users
Conclusion
As you can see the uses of iptables are limitless. There are many frontend firewalls now that use iptables so chances are you wont have to directly interact with iptables, and with nftables the chances are less. It is still important as an SRE, developer or security professional to understand how iptables work.
This article is a compilation of all the knowledge I gathered from many sources while on my journey to understand iptables, I hope to make that journey easier for someone else thanks to this article.
For an even more comprehensive guide check out — Linux iptables Pocket Reference [Book] (oreilly.com)
A great many thanks to all my sources mentioned below, all rights reserved to owners.
Sources: Dr. Murphy, Supriyo Biswas, Phoenix Nap, XPSTECH, David Adams
Thanks for reading!