Docker and Iptables: You may do it wrong!

Mission

If you’re running Docker on a host that is exposed to the Internet (network bridge), you will probably want to restrict external access.

Docker network

Let’s start with a fact that Docker manipulates iptables rules to provide network isolation, on Linux. Docker installs custom iptables chains named DOCKER, DOCKER-USER and DOCKER-ISOLATION-STAGE-*, and it ensures that incoming packets are always checked by these chains first.

iptables -L -n

Re.Docker network, I won’t describe here because it’s a lot of knowledge. You may want to check Docker network note. I will only show iptables packet flow: For example, we have a host with ip 10.0.10.26, then start a container that is exposed port 8000 to internet.

This is a basic packet flow from outside:

You may do it wrong - common mistakes

Alright, before we make it right, there are some common mistakes.

Modify Docker generated rules manually

Docker generates iptables rules, then adds to DOCKER chains. Some users may manipulate this chain manually in order to block connections.

Please don’t do it. Yes, you are able to do it, there is nothing prevent you to perform this kind of action. But every time Docker daemon is reloaded, iptables rules are re-generated and your changes is gone.

Right: Do not manipulate Docker rules manually.

Insert you rules in the wrong chain

iptables basic: iptables is divied into three levels: tables, chains and rules. We only use the filter tables, which contains:

Commonly, to block connection from external, put reject rules in INPUT chain. But in Docker, it doesn’t work. Look at the above packet flow, the packet doesn’t pass through INPUT chain, it goes in FORWARD chain. Since Docker connects the bridge (default docker0) to the default gateway (external interface ens33 for example) via Network Address Translation (NAT) by default, setting INPUT is useless and the FORWARD chain should be set. And since the FORWARD chain defaults to DROP, all forwarding is blocked by DOCKER-USER.

Right: Add rules which load before Docker’s rules, add them to DOCKER-USER.

Modify and persistent iptables wrong

You modify and persistent iptables rules like this:

This implementation has a drawback, every time Docker container changes, you need to save the current iptables configuration, otherwise when executing iptables-restore, it will load the old rules, which will lead to confusing iptables rules.

Right: Do not save, flush then restore all rules. Check the following solution.

Do it right!

Overview

I have create a repository for this, which is highly inspired by systemd-service-iptables: https://github.com/ntk148v/systemd-iptables

Getting started

git clone https://github.com/ntk148v/systemd-iptables
cd systemd-iptables
# Edit the rules in etc/iptables/base.rules as needed.
# and install the service
cp -Rv etc/. /etc/
systemctl daemon-reload
systemctl enable iptables.service
systemctl enable iptables@base.service
systemctl start iptables@base.service
# Check status
systemctl status iptables@base.service
systemctl restart iptables@base.service

References

  1. https://docs.docker.com/network/iptables/
  2. https://github.com/boTux-fr/systemd-service-iptables