Docker – Security implications of setting UFW default_forward_policy to accept

dockerufw

The docker manual (http://docs.docker.com/installation/ubuntulinux/#docker-and-ufw) states that it's nescessary to set UFWs DEFAULT_FORWARD_POLICY to "ACCEPT" so docker containers can reach each other.

  1. What's the security implication of doing so on a server with publicly accessible network interfaces?

  2. What should be done to secure such a docker host?

Best Answer

They seem to have solved this part of the problem, at least in the latest version 1.4.1. My FORWARD policy is to drop packets and inter-container communication works without problems.

Those are the standard rules in the FORWARD chain created by docker:

$ iptables -vnL FORWARD
Chain FORWARD (policy DROP 0 packets, 0 bytes)
 pkts bytes target     prot opt in      out      source               destination         
 6902   96M ACCEPT     all  --  *       docker0  0.0.0.0/0            0.0.0.0/0            ctstate RELATED,ESTABLISHED
 6151  482K ACCEPT     all  --  docker0 !docker0 0.0.0.0/0            0.0.0.0/0           
    3   180 ACCEPT     all  --  docker0 docker0  0.0.0.0/0            0.0.0.0/0           

From bottom up:

  • The third rule is docker->docker communication, which is accepted without restrictions.
  • The second rule is docker->anywhere(but not docker), which is also accepted without restrictions.
  • The first rule is anywhere->docker, only "answer" packets get accepted.

No problems here. (Unless you want outgoing filters)

You can happily set FORWARD + INPUT policy to DROP/REJECT.

Now, you may want to provide a service on the internet. E.g. a simple webserver:

$ docker run -d -p 80:80 dockerfile/nginx

OK, now go to yourserver.com. You will see the default nginx page. Why? Docker added some special rules in iptables for you.

$ iptables -vn -t nat -L # NAT table, truncated for clarity
Chain PREROUTING (policy ACCEPT 0 packets, 0 bytes)
 pkts bytes target     prot opt in     out     source               destination         
  189 11900 DOCKER     all  --  *      *       0.0.0.0/0            0.0.0.0/0            ADDRTYPE match dst-type LOCAL

Chain DOCKER (2 references)
 pkts bytes target     prot opt in     out     source               destination         
    0     0 DNAT       tcp  --  !docker0 *       0.0.0.0/0            0.0.0.0/0            tcp dpt:80 to:172.17.0.15:80

$ iptables -vnL FORWARD
Chain FORWARD (policy DROP 0 packets, 0 bytes)
 pkts bytes target     prot opt in     out     source               destination         
    0     0 ACCEPT     tcp  --  !docker0 docker0  0.0.0.0/0            172.17.0.15          tcp dpt:80
 6903   96M ACCEPT     all  --  *      docker0  0.0.0.0/0            0.0.0.0/0            ctstate RELATED,ESTABLISHED
 6159  483K ACCEPT     all  --  docker0 !docker0  0.0.0.0/0            0.0.0.0/0           
    3   180 ACCEPT     all  --  docker0 docker0  0.0.0.0/0            0.0.0.0/0           

Those rules circumvent all of the ufw rules if there's a container listening. ufw does nothing in the nat table and Docker sets it's rules at the first place in the FORWARD chain. So it's not possible for you to block an IP address or do rate limiting of any kind.

Possible solutions:

  • Start Docker with --iptables=false and do everything manually. Painstaking solution, because everytime you restart a container, it gets a new IP (for now, they plan to change that).
  • Bind all ports on localhost with -p 127.0.0.1:30080:80 and create your own iptables rules to get there.
  • Modify ufw somehow?
  • Don't use any firewall framework. I do this for now. All my rules are saved in a separate script as iptables commands. This script is executed after reboots and every service docker start. Altough a bit hacky it works...