iptables is a Linux utility that is often considered difficult or intimidating to use. In this post, I will try to break down how to use it so that it is more digestible. The good thing about iptables is that it can slice and dice the network traffic in any way you want. However, tools that are highly flexible and powerful usually have a very steep learning curve. Proof of that is the existence of tools such as Uncomplicated Firewalls (UFW), that act as a more user friendly, front-end to iptables.
I actually think it’s pretty straightforward to get started with iptables, and, like any other tool, the more you use it the better you understand it and gain expertise on it. Below, I touch on some basic things you can do. In a future blog post we will examine more advanced features.
Installation
If iptables is not already installed on your Linux host (which is unlikely) you can install it with the following command:
apt-get install iptables -y
Tables, Chains, Targets
The traffic packets are filtered based on tables that are managed by iptables. Each table contains a set of rulers (or chains) that define what to do with each packet depending on its point (input, output, forward). Each chain can contain rules to match specific packets based on type of traffic (e.g. tcp/udp/icmp), destination or source IP, port, etc. Each rule contains a target which determines what to do with packets that match the rule (e.g. accept or reject).
Most Linux distributions have four tables: filter, mangle, nat, and raw. The default is filter, and that’s the one we’ll be working with today.
Each table contains a few chains, such as PREROUTING, INPUT, OUTPUT, FORWARD, and POSTROUTING (I am not trying to yell — I capitalized them, because that’s how they appear on the command line). The filter table by default contains the INPUT, FORWARD, and OUTPUT chains.
Each chain has rules to match specific packets, and each rule has a target that determines what to do with each packet that matches the rule. For example, a rule in the OUTPUT chain may match to UDP packets that go out to port 53, and the target may say that those packets should be dropped. The available targets are ACCEPT, DROP, and REJECT. The difference between DROP and REJECT, is that with REJECT, a “connection reset” for TCP and a “destination host unreachable” for UDP/ICMP is sent to the packet source, while DROP dictates that nothing is to be sent to the packet source.
List rules
With the following command you get the existing rules of the filter table:
netbeez.net$ iptables -t filter -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 DROP udp -- anywhere anywhere udp dpt:domain
If you add the “-v” option you also get the number of packets that have been processed by each rule:
netbeez.net$ iptables -t filter -L -v Chain INPUT (policy ACCEPT 34 packets, 2008 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 24 packets, 2784 bytes) pkts bytes target prot opt in out source destination 12308 874K DROP udp -- any any anywhere anywhere udp dpt:domain
As you can see there is one rule in the OUTPUT chain that DROPs all egress UDP packets. So far, it has processed 9482 packets or 673K bytes.
Since the table filter is the default, from now on, we won’t be giving it as input.
Remove all rules
To remove all rules you use the flush options as follows:
netbeez.net$ iptables -F netbeez.net$ 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
As you can see all rules have been deleted.
Block all incoming traffic from a specific IP
If you want to drop all traffic coming from CloudFlare’s DNS server 1.1.1.1 you would use
iptables -A INPUT -s 1.1.1.1 -j DROP”:
netbeez.net$ ping 1.1.1.1 -c 4 -q PING 1.1.1.1 (1.1.1.1) 56(84) bytes of data. --- 1.1.1.1 ping statistics --- 4 packets transmitted, 4 received, 0% packet loss, time 3005ms rtt min/avg/max/mdev = 13.504/14.190/14.820/0.475 ms netbeez.net$ iptables -A INPUT -s 1.1.1.1 -j DROP netbeez.net$ ping 1.1.1.1 PING 1.1.1.1 (1.1.1.1) 56(84) bytes of data. ^C --- 1.1.1.1 ping statistics --- 4 packets transmitted, 0 received, 100% packet loss, time 3159ms
As you can see I was able to ping 1.1.1.1 before applying the rule, but not after.
The “-A” option appends this rule to the INPUT chain. “-j” stands for jump and specifies the target that should be applied to packets that match this rule.
Block all incoming traffic from a specific IP to an interface
iptables -A INPUT -s 1.1.1.1 -j DROP -i wlan0
You can verify this rule by using ping and specifying the test interface with “-I”: ping 1.1.1.1 -I <interface>.
Block all incoming TCP traffic from a specific IP
iptables -A INPUT -s 1.1.1.1 -j DROP -p tcp
You can verify this command with “curl 1.1.1.1” before and after you apply it.
Block all outgoing UDP traffic to a specific port
iptables -A OUTPUT -p udp --dport 53 -j DROP
Obviously, this command will block all UDP DNS requests. You can verify it with: “dig google.com @1.1.1.1” Make sure you specify an external DNS server. Without it, dig might use cached results, and DNS might appear working even after you apply the rule.
Delete specific rule
iptables -D OUTPUT -p udp --dport 53 -j DROP
This command deletes the above rule that blocks outgoing UDP traffic to port 53. As you can see, the two commands look very similar. The rule of thumb to delete a rule is “Repeat the rule, and replace ‘-A’ with ‘-D’”
Save rules
One issue that requires special attention is whichever iptables rules you introduce are erased after a system reboot. Surprisingly enough, the iptables utility doesn’t natively support the option to restore any rules on a system reboot. To do that you have to install the following package:
apt-get install iptables-persistent -y
Then, you have to save whatever rules you want to restore with the following commands, depending on whether they are IPv4 or IPv6 rules:
iptables-save > /etc/iptables/rules.v4 Iptables-save > /etc/iptables/rules.v6
On the next boot iptables-persistent, it will read any rules that are saved in the rules.v4 and rules.v6 files and apply them on the system.
If you are looking into expanding your networking skills, Linux iptables is a tool that, sooner or later, you will need to use. In this post, I wanted to make a smooth introduction without diving into more complicated aspects. Try these simple commands out, and tailor them to your needs to see their practicality.