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 know about iptables?
  • Syntax explanation with examples.
  • Use cases:
    • Make a machine as a NAT device for sharing internet connections.
    • Use as IP resolver between NAT IP and internal IP across VLANs.

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          | ASQUERADE   |
|              | 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):

  • Used for traffic input and output through the device.
  • Built-in Chains for INPUT, OUTPUT and FORWARD.
iptables -A INPUT -p tcp --dport 22 -j DROP

NAT:

  • SNAT and DNAT (Port Forwarding).
  • Built-In Chains: PREROUTING used for DNAT, POSTROUTING for SNAT and OUTPUT (for locally-generated packets)

Mangle:

  • Used for packet alteration.
  • Built-in Chains: PREROUTING, INPUT, FORWARD, OUTPUT, POSTROUTING

Netfilter Chains:

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

INPUT:

  • Filtering incoming packets to the host machines.
  • For example: To drop incoming ICMP traffic to a host machine, use the INPUT chain. It blocks all ping requests.
iptables -t filter -A INPUT -p ICMP -j DROP

OUTPUT:

  • Filter outgoing packets out of the host machines.
iptables -t filter -A OUTPUT -d google.com  -j DROP

FORWARD:

  • Filter routed packets, just used by routers.

PREROUTING:

  • used for Destination NAT(DNAT), for port forwarding.

POSTROUTING:

  • used for Source NAT(SNAT)/ MASQUERADE

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:

  • It is for accepting a packet from the source.

DROP:

  • Dropping a packet to the destination.

REJECT:

  • A packet rejects with a reply. Drop target drops a packet on a connection which keeps on retrying until timeout for retransmission reaches.
  • We can pass valid reject types with REJECT. Some of them are – tcp-reset, host-unreachable, port-unreachable.

DNAT:

  • Expose specific services on an internal network to a public network. For example – Forwarding traffic to a gateway’s port 443 to an internal web server running on 8000.

SNAT:

  • Source NAT Replaces the private IP address from the packet with the public IP address of the external interface. Generally used for sharing a single internet connection for a private network.

MASQUERADE:

  • A particular case of SNAT used when the public IP address of the NAT Router is dynamic.

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.

  • Drop outbound packets from a host to google.com on port 443.
iptables -t filter -A OUTPUT -p tcp --dport 443 -d google.com -j DROP
  • Accept SSH connection on port 22 only from source originating 192.168.12.0/24
iptables -A INPUT -p tcp --dport 22 -s 192.168.12.0/24 -j ACCEPT
  • Drop all the incoming ICMP traffic.
iptables -t filter -A INPUT -p ICMP -j DROP
  • Reject all the SSH traffic from a source
iptables -A INPUT -p tcp --dport 22 -s 192.168.12.3 -j REJECT --reject-with tcp-reset
  • Add a rule that is NATed to a specific external interface.
iptables -t nat -A POSTROUTING -s 192.168.0.0/16 -j MASQUERADE
  • In the case of static public IP address, we can use SNAT
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:

  • Server Node 1 – 192.168.103.3 – 10.34.214.3
  • Server Node 2 – 192.168.103.4 – 10.34.214.4
  • Server Node 3 – 192.168.103.5 – 10.34.214.5

VLAN B:

  • Client Node 1 – 10.34.208.212 – 10.27.212.3
  • Client Node 2 – 10.34.208.213 – 10.27.212.4

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:

  • Considered to have complex syntax and hence many frontends came out.
  • Does not give flexibility like other alternatives such as NFTables that allows combining rules and multiple actions.
  • Troubleshooting and maintenance become a challenge with multiple rules; it also impacts performance.
Jaideep Khandelwal
Jaideep Khandelwal