Julia Evans

Iptables basics

Yesterday I tweeted “hey, I learned some stuff about iptables today”! A few people replied “oh no, I’m sorry”. iptables has kind of a reputation for being hard to understand (and I’ve also found it intimidating) so I wanted to write down a few things I learned about iptables in the last few days. I don’t like being scared of things and understanding a few of the basics of iptables seems like it shouldn’t be scary!

I have been looking at Kubernetes things, and Kubernetes creates 5 bajillion iptables rules, so it has been time to learn a little bit about iptables.

The best references I’ve found for understanding iptables so far have been:

how to view what iptables stuff you have set up

iptables has a bunch of er, “tables” in it. These are places you can put iptables rules. They’re used at different times during packet processing. There’s a diagram here, from this “traversing of tables and chains” page.

The first surprising thing I learned about iptables is that to look at all the iptables rules you have to run 4 commands

sudo iptables -L # there's an implicit `-t filter` here,
                 # this just lists the filter table
sudo iptables -L -t nat
sudo iptables -L -t mangle
sudo iptables -L -t raw

This isn’t super fun, I find it annoying to have to run 4 commands to see all the iptables rules on my computer. I started running sudo iptables-save which generates a dump of all the iptables rules I have. I like being able to see everything by running one command!

$ sudo iptables-save
*nat
:PREROUTING ACCEPT [1664:324261]
:INPUT ACCEPT [1629:320166]
:OUTPUT ACCEPT [36545:10406977]
:POSTROUTING ACCEPT [34390:10034797]
:DOCKER - [0:0]
-A PREROUTING -m addrtype --dst-type LOCAL -j DOCKER
-A OUTPUT ! -d 127.0.0.0/8 -m addrtype --dst-type LOCAL -j DOCKER
-A POSTROUTING -s 172.17.0.0/16 ! -o docker0 -j MASQUERADE
-A DOCKER -i docker0 -j RETURN
COMMIT
# Completed on Wed Jun  7 21:25:14 2017
# Generated by iptables-save v1.6.0 on Wed Jun  7 21:25:14 2017
*filter
:INPUT ACCEPT [2078627:2180604942]
:FORWARD ACCEPT [0:0]
:OUTPUT ACCEPT [1617291:254682158]
:DOCKER - [0:0]
:DOCKER-ISOLATION - [0:0]
-A FORWARD -j DOCKER-ISOLATION
-A FORWARD -o docker0 -j DOCKER
-A FORWARD -o docker0 -m conntrack --ctstate RELATED,ESTABLISHED -j
ACCEPT
-A FORWARD -i docker0 ! -o docker0 -j ACCEPT
-A FORWARD -i docker0 -o docker0 -j ACCEPT
-A DOCKER-ISOLATION -j RETURN
COMMIT

You can see on my laptop right now that I have a bunch of stuff in the filter table and some more stuff in the nat table. And that it’s all generated by Docker.

Okay, now that we’re able to look at iptables rules, how do we read them? And how do we write our own?!

the nat & filter tables

The only tables I’ve seen Kubernetes use so far are the nat and filter tables. The filter table mostly makes sense to me – you can set it up as a firewall to drop some packets, and read the man page to understand the syntax. In the example above there are a bunch of things in the FORWARD chain that apply to forwarded packets. I’m not sure how the idea of a “forwarded packet” applies to Docker yet (I think this documentation page “Understand container communication” is relevant), but I’ll leave it there for now.

But the nat table!! I have learned a few things about that! I’m going to break down a specific nat table rule that I found in the Kubernetes source code because it took me a while to understand what it was doing. Here it is:

/usr/sbin/iptables -w -t nat -A POSTROUTING -o eth0 -j MASQUERADE ! -d ${CONTAINER_SUBNET}

The first time (and possibly second & third times) I saw this my eyes kind of glazed over. I googled iptables masquerade like 5 times and was like “why doesn’t the iptables man page even say the word masquerade at all?!?“.

But it actually turned out to be important to know what this meant, so, now I know!! Let’s break down the easy options first

  • -w: this takes out an exclusive lock so that it can’t run concurrently with other iptables executions. Makes sense.
  • -A: just means “add a rule”
  • POSTROUTING is the name of a chain, it’s basically the last step in iptables processing
  • -o eth0: means “the output interface is eth0” – so the packet is going out of the computer (and not for example being bridged to a Docker network interface)

Those aren’t so bad! Now let’s deal with the complicated part – -j MASQUERADE ! -d $CONTAINER_SUBNET

-j MASQUERADE means “execute the MASQUERADE rule on this packet”. But what is the MASQUERADE rule?

SNAT & MASQUERADE

The problem this is trying to solve is – packets in Kubernetes that are sent from pods have pod IP addresses on them (which are different from the “real” IP address on the host). This is fine, but if you send a packet to a computer outside of your cluster, they won’t know what that IP address means or how to route traffic back to it.

This is a lot like the problem you have when you’re on a home network (your IP is 192.168.x.x), and you want to talk to hosts in the outside world. They don’t know what 192.168.x.x means!

The iptables.info documentation for SNAT says:

This is what we want, for example, when several hosts have to share an Internet connection. We can then turn on ip forwarding in the kernel, and write an SNAT rule which will translate all packets going out from our local network to the source IP of our own Internet connection. Without doing this, the outside world would not know where to send reply packets, since our local networks mostly use the IANA specified IP addresses which are allocated for LAN networks. If we forwarded these packets as is, no one on the Internet would know that they were actually from us. The SNAT target does all the translation needed to do this kind of work, letting all packets leaving our LAN look as if they came from a single host, which would be our firewall.

This sounds perfect! We need to set up a SNAT iptables rule! Except with SNAT you need to specify which IP address you want to rewrite the source IP address to. So MASQUERADE lets you just rewrite packets to the host’s IP address.

iptables.info actually explains what MASQUERADE means, unlike the iptables man page. With this reference, it’s much easier to understand iptables incantations! Yay!

having the right iptables rules is important

iptables is kind of frustrating because if you don’t have the right rules you can end up in situations like “oh well, no networking works at all, oops”. And I don’t know any iptables debugging tools (though if you do, I’d like to know!) so so far I just stare at the rules until I understand them.

that’s all

I am a little less intimidated by iptables than I was last week so that’s good!

A few things I've learned about Kubernetes Log-structured storage