By default, docker daemon appends iptables rules for forwarding. For this, it uses a filter chain named DOCKER.
Chain FORWARD (policy DROP) target prot opt source destination DOCKER all -- 0.0.0.0/0 0.0.0.0/0 ... Chain DOCKER (1 references) target prot opt source destination |
Moreover, when you tell docker to expose a port of a container, it exposes it to the entire world, breaking your possibly existing iptables rules.
So.. if you are running docker on a host that already have an iptables based firewall, you should probably set —iptables=false.
What are you talking about?
Let’s take an example. You want to start nginx and bind containerPort 80 to hostPort 9090:
docker run --name some-nginx -d -p 9090:80 nginx |
What it does behind the scene is adding an iptables rule to the DOCKER filter chain:
Chain FORWARD (policy DROP) target prot opt source destination DOCKER all -- 0.0.0.0/0 0.0.0.0/0 ... Chain DOCKER (1 references) target prot opt source destination ACCEPT tcp -- 0.0.0.0/0 172.17.0.2 tcp dpt:9090 <-- this was added when running the container |
Now port 9090 is available from the entire world. Why? Because we’re listening 9090 on any IP addresses (*) and because of the forwarding rules that are dynamically added in the DOCKER filter chain. Note that docker’s forward rules permit all external source IPs by default.
You probably don’t want that.
Exposing ports locally
You might want to publish ports just locally and not to *, for internal use. Let’s read the documentation of docker run:
-p=[] : Publish a container's port or a range of ports to the host format: ip:hostPort:containerPort | ip::containerPort | hostPort:containerPort | containerPort Both hostPort and containerPort can be specified as a range of ports. When specifying ranges for both, the number of container ports in the range must match the number of host ports in the range. (e.g., `-p 1234-1236:1234-1236/tcp`) (use 'docker port' to see the actual mapping) |
docker run --name some-nginx -d -p 127.0.0.1:9090:80 nginx # BEFORE netstat -an | grep 9090 tcp6 0 0 :::9090 :::* LISTEN # AFTER netstat -an | grep 9090 tcp 0 0 127.0.0.1:9090 0.0.0.0:* LISTEN Better. |
Docker, stop messing with my iptables rules!
Let’s say you are using docker on a server available on the Internet. You already have an iptables based firewall configured. Personally, I’m using uif which is a very powerful perl script available in debian. Have a look at a config example.
To tell docker to never make changes to your system iptables rules, you have to set —iptables=false when the daemon starts.
For sysvinit and upstart based systems, you can edit /etc/default/docker. For systemd, you can do that:
mkdir /etc/systemd/system/docker.service.d cat << EOF > /etc/systemd/system/docker.service.d/noiptables.conf [Service] ExecStart= ExecStart=/usr/bin/dockerd -H fd:// --iptables=false EOF systemctl daemon-reload |
Now reload your firewall and restart docker daemon. You can see that the chain named DOCKER and the references to it in chain FORWARD (policy DROP) disappeared.
Configure iptables to work with docker
If you’re still using the Ethernet bridge created by docker and named docker0, you can set the following rules for forwarding:
# just an example. It implies that your host Ethernet NIC is eth0 -A FORWARD -i docker0 -o eth0 -j ACCEPT -A FORWARD -i eth0 -o docker0 -j ACCEPT |
Now if you want to expose TCP port 10000 of a running container to the world, this container must expose port to any IP (*) on host side:
docker run --name some-nginx -d -p 10000:80 nginx netstat -an | grep 10000 tcp6 0 0 :::10000 :::* LISTEN |
Then you can add this firewall rule to allow the world to access your container through the forwarding rules:
-A INPUT -p tcp -m tcp --dport 10000 -s 0.0.0.0/0 -j ACCEPT |