Iptables, blast from the past!

IPtables is a firewall utility which has been there for over two decades. The idea to write this blog came while working on network solutions for the production system, and to brush-up the syntax and present some use cases with real-world usage. We discuss the following topics:

Why?

If you are an SRE or a DevOps practitioner, iptables is a handy “arrow in the quiver” utility. Even though there are other alternatives(discussed later) to it, but now and then, you deal with networks implementing iptables. A lot of tools and frameworks like OpenStack, Docker and Kubernetes still manages rules using iptables (Do a sudo iptables -L).

Introduction

Linux kernels have the in-built feature of providing IP Firewalls, which provides the ability to configure rules for stream and datagram processing. It has been part of the Linux kernel module for packet filtering, and iptables is the userspace utility based on top of the Netfilter framework.

We go through iptables options and try to understand the semantics, though the syntax looks to be complicated, let us break down and explain.

Syntax:

iptables -t <table_name> -<COMMAND> <CHAIN> <matches> -j <TARGET>

Some of the options that can be used are:

| Tables  | Command     | Chain       | Matches                  | Target     |
|---------|-------------|-------------|--------------------------|------------|
| Filter  | A (append)  | INPUT       | -s source_ip             | ACCEPT     |
| NAT     | I (insert)  | OUTPUT      | -d destination_ip        | DROP       |
| Mangle  | D (delete)  | FORWARD     | -p protcol               | REJECT     |
| RAW     | R (replace) | PREROUTING  | --sport source_port      | MASQUERADE |
|         | F (flush)   | POSTROUTING | --dport destination_port | DNAT       |
|         | Z (zero)    |             | -i incoming_interface    | SNAT       |
|         | S (show)    |             | -o outgoing_interface    |            |
|---------|-------------|-------------|--------------------------|------------|

The command is the operation performed on the table. Options like (A, I) are to append and insert rules in the chain, respectively.

Each rule has a matching and action part to it, and it defines the criteria a packet must match for an action to execute.

The target specifies what action to take on matching the criteria.

Tables:

Different tables use a different type of rules. We have discussed common used tables like Filter, Mangle and NAT, besides there is Raw and Security tables.

Filter (Default):

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

NAT:

Mangle:

Netfilter Chains:

Each table further classifies into Chains. Some of the default chains are:

INPUT:

iptables -t filter -A INPUT -p ICMP -j DROP

OUTPUT:

iptables -t filter -A OUTPUT -d google.com  -j DROP

FORWARD:

PREROUTING:

POSTROUTING:

We can create our custom chains. For example, Docker daemon creates a custom chain.

Target

When a chain meets the criteria, an action is triggered. Some of the actions are:

ACCEPT:

DROP:

REJECT:

DNAT:

SNAT:

MASQUERADE:

ACCEPT and DROP are terminating targets, which means the action is taken on the packet when a rule matches and will not further traverse the chain.

LOG and TEE are non-terminating targets, on matching the target the evaluation continues. Let us see general syntax with some of the options and examples.

iptables -t filter -A OUTPUT -p tcp --dport 443 -d google.com -j DROP
iptables -A INPUT -p tcp --dport 22 -s 192.168.12.0/24 -j ACCEPT
iptables -t filter -A INPUT -p ICMP -j DROP
iptables -A INPUT -p tcp --dport 22 -s 192.168.12.3 -j REJECT --reject-with tcp-reset
iptables -t nat -A POSTROUTING -s 192.168.0.0/16 -j MASQUERADE
iptables -t nat -A POSTROUTING -s 192.168.0.0/16 -j SNAT --to-source 123.14.78.9

All the above command uses -A, which means the rule appends at the end. The rules traverse in order from top to bottom. So, if the rule is to DROP packets for SSH traffic from all the CIDRs and allow only packets from specific CIDR to ACCEPT, then apply to accept rule first.

Use Cases:

a.) Make a machine as a NAT device for sharing internet connections.

I have a NAS device, which does not come with a WiFi adapter and only has an ethernet card. Unfortunately for some reason, the device could not be kept near the router, and could not do cloud sync because of no internet connectivity. The option is to either buy a CAT 5 cable and connect, but it is an overhead to manage such a long cable. Another alternative was, to use a spare Raspberry Pi 4 and make a NAT device to share internet connections to other devices(Storage Server) with the use of iptables.

There are two machines A(Nas device) and B(Raspberry Pi 4). A’s network device eth0 has IP address as 172.25.0.101, machine B has two interfaces eth0 and wlan0, with IP 172.25.0.102 and 192.162.1.0/24 with 192.162.1.8 respectively. B’s wlan0 device’s default gateway is to a router 192.162.1.1.

192.162.1.0/24 machines can connect to the Internet, so machine B has connectivity but not for machine A.

On machine A, change the default gateway to 172.25.0.102 (machine B).

route add default gw 172.25.0.102 eth0

Enable ipv4 forwarding for machine B like:

Set "net.ipv4.ip_forward=1" in /etc/sysctl.conf

Append an iptable rule in POSTROUTING chain of NAT Table

iptables -t nat -A POSTROUTING -s 172.25.0.0/16 -o wlan0 -j MASQUERADE

Now all the egress traffic is routed through Machine B coming from Machine A and could connect to the Internet.

b.) Using as IP resolver between NAT IP and internal IP across VLANs.

We had a use case, of connecting Nomad servers in VLAN A to nomad client in VLAN B. The following are the internal IPs and NAT IPs :

VLAN A:

VLAN B:

In the nomad client configuration, pass the list of server address to connect over port 4647.

["10.34.214.3:4647","10.34.214.4:4647","10.34.214.5:4647"]

When the client connects to the list of servers, in response, the server nodes return their broadcasted address, i.e. their internal IP to the client, which won’t work. Use iptables to solve the problem.

On client machines, we append a rule to OUTPUT chain for a NAT table; whenever a packet destined to local IP of server nodes on port 4647, it should go to the NAT IPs of the server nodes.

iptables -t nat -A OUTPUT -d 192.168.103.5 -p tcp --dport 4647 -j DNAT --to-destination 10.34.214.5

Important: To persist the rule use something “iptables-save” or as daemon “service iptables save”

Disadvantages

There are certain disadvantages like:

Discussion and feedback