The firewall must be configured properly to prevent unwanted access which could lead to data loss and exploits. UFW allows you to quickly close and open ports. The following configuration closes all incoming ports and opens 22 (SSH), 80 (HTTP) and 443 (HTTPS):
ufw default deny incoming ufw allow OpenSSH ufw allow http ufw allow https ufw enable
But Docker will update iptables when you bind a container port to the host, opening the port for public access. To prevent this, you could bind the port to an internal address (private or 127.0.0.1). Another way is telling Docker to never update iptables by setting the “iptables” option to “false” in /etc/docker/daemon.json. This file should contain a JSON string: { “iptables”: false }.
This can be automated as:
apt install -y ufw ufw default deny incoming ufw allow OpenSSH ufw allow http ufw allow https ufw --force enable # --force prevents interaction apt install -y jq touch /etc/docker/daemon.json [[ -z $(cat /etc/docker/daemon.json) ]] && echo "{}" > /etc/docker/daemon.json echo $(jq '.iptables=false' /etc/docker/daemon.json) > /etc/docker/daemon.json
But there are cases when preventing Docker from manipulating iptables can be too much and DNS won’t be resolved to some containers. The issue I met was when building a container from the NGINX Alpine image:
---> Running in 71130dd103f3 fetch http://dl-cdn.alpinelinux.org/alpine/v3.9/main/x86_64/APKINDEX.tar.gz fetch http://dl-cdn.alpinelinux.org/alpine/v3.9/community/x86_64/APKINDEX.tar.gz ERROR: http://dl-cdn.alpinelinux.org/alpine/v3.9/main: temporary error (try again later) WARNING: Ignoring APKINDEX.b89edf6e.tar.gz: No such file or directory
For containers like this one you could use –network host or manually add iptables rules, which probably is not the best idea because they could change in the future. These are the rules I’ve seen Docker add:
iptables -N DOCKER iptables -N DOCKER-ISOLATION-STAGE-1 iptables -N DOCKER-ISOLATION-STAGE-2 iptables -A FORWARD -j DOCKER-USER iptables -A FORWARD -j DOCKER-ISOLATION-STAGE-1 iptables -A FORWARD -o docker0 -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT iptables -A FORWARD -o docker0 -j DOCKER iptables -A FORWARD -i docker0 ! -o docker0 -j ACCEPT iptables -A FORWARD -i docker0 -o docker0 -j ACCEPT iptables -A DOCKER-ISOLATION-STAGE-1 -i docker0 ! -o docker0 -j DOCKER-ISOLATION-STAGE-2 iptables -A DOCKER-ISOLATION-STAGE-1 -j RETURN iptables -A DOCKER-ISOLATION-STAGE-2 -o docker0 -j DROP iptables -A DOCKER-ISOLATION-STAGE-2 -j RETURN iptables -A DOCKER-USER -j RETURN iptables -t nat -A POSTROUTING ! -o docker0 -s 172.17.0.0/16 -j MASQUERADE
I did not specifically need to stop Docker from updating iptables as I was just experimenting, so I just let it do its job. I’m publishing (-p) the ports I need for public access and exposing (- -expose) the private ones.