A huge pain point with Docker since day one has always been the ability to do iptables filtering of incoming traffic. Exposed ports were not filtered, rules could not persist a Docker daemon restart and no easy way to do filtering based on IP etc. The actual problem is better described at the first Google search result for docker iptables.
--iptables=false never worked quite well, at least not for me with multiple Docker networks.
This problem is no more since the release of Docker 17.06.
This new release introduces a new iptables chain that Docker adds to the
FORWARD chain. This is added as the first rule in the
FORWARD chain and it's named
Chain FORWARD (policy DROP 0 packets, 0 bytes) num pkts bytes target prot opt in out source destination 1 167K 68M DOCKER-USER all -- any any anywhere anywhere 2 155K 61M DOCKER-ISOLATION all -- any any anywhere anywhere
DOCKER-USER chain is NOT cleared when the Docker daemon restarts. This allows you as a user to add your own iptables rules which survives a restart of the Docker engine. Before Docker 17.06 you had to inject your own chain at the beginning of
FORWARD after each Docker restart to get the same ability to do filtering. For example if the Docker package were upgraded and the daemon restarted Docker would add it's own rules before yours and the filtering rules were no longer working. Dangerous and error prone.
I'm using the
iptables-persistent package to auto load my rules on boot. I'm in no way an iptables expert but here is an example setup that seems to work good for me. The ipv4 rule file is located at
*filter :INPUT ACCEPT [0:0] :FORWARD DROP [0:0] :OUTPUT ACCEPT [0:0] :DOCKER-USER - [0:0] ## # INPUT ## # Allow localhost -A INPUT -i lo -j ACCEPT # Allow established connections -A INPUT -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT # Allow ICMP ping -A INPUT -p icmp -m icmp --icmp-type 8 -j ACCEPT # SSH -A INPUT -p tcp -m tcp --dport 22 -j ACCEPT # INPUT default DROP -A INPUT -j DROP ## # DOCKER-USER rules ## # Allow established connections -A DOCKER-USER -i eth0 -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT # SMTP -A DOCKER-USER -i eth0 -p tcp -m tcp --dport 25 -j ACCEPT # http -A DOCKER-USER -i eth0 -p tcp -m tcp --dport 80 -j ACCEPT # https -A DOCKER-USER -i eth0 -p tcp -m tcp --dport 443 -j ACCEPT # DOCKER-USER default DROP -A DOCKER-USER -i eth0 -j DROP COMMIT
This setup uses the new
DOCKER-USER chain to do filtering. It allows SSH and ICMP ping to the base system and defaults to drop the rest of the incoming traffic. The Docker traffic rules allows SMTP, HTTP and HTTPs but denies all other traffic. This way no external traffic can reach Docker containers with exposed ports without you adding explicit iptables rules to allow it.
This also allows you to do host based filtering. For example only allow HTTPs from source IP 184.108.40.206.
-A DOCKER-USER -i eth0 -p tcp -s 220.127.116.11 -m tcp --dport 443 -j ACCEPT
One thing to note here is that I'm using
-i eth0 for all
DOCKER-USER rules. This tells iptables only to apply the rules on my external network interface
eth0. If this weren't included iptables denied traffic that wasn't supposed to be denied. Since I'm only interested in filtering external traffic I guess this is fine.
In order to completely clear and reload my rules I used a small shell script. This is just included as an example if you need to do the same. Note that this flushes iptables completely so make sure you got a way to reach your machine if anything goes wrong.
#!/usr/bin/env bash echo "* Flushing iptables" && sudo iptables -F -t nat && sudo iptables -F && echo "* Reloading iptables" && sudo /etc/init.d/iptables-persistent start && echo "* Restarting docker" && sudo service docker restart
When the rules are added to iptables verify that everything looks fine with
iptables --list --line-number -v. The chain
DOCKER-USER should now contain your own rules and it should be the first chain called in the
It should now be safe to restart Docker and your iptables rules should persist and still work when the Docker daemon reloads and recreates the iptables rules.