Securing network access to IPv6 docker containers

Posted on 2019-12-14 by ungleich virtualisation team

Like in any situation, we should secure our systems. In the legacy IPv4 world, things are often not clear due to the use of NAT (network address translation). Things are much more transparent and also easier with IPv6.

In this article we give easy to follow instructions on how to secure your IPv6 based docker containers. If you don't know yet how to enable IPv6 on your docker containers, you can follow how to enable IPv6 in docker.

Docker containers with IPv6

IPv6 was made to restore direct connectivity, like the Internet was designed to be in the first place. So instead of needing to "expose ports" or to add "port forwarding", IPv6 addresses are generally directly reachable.

This is nice and general, but if we run containers that are not fully secured, this is a security risk.

For this reason we should limit access to our docker containers.

Network firewall with nftables

You might have seen noticed that in the Linux world we are moving from iptables to nftables. In case you need a refresher on the differences, checkout the article about iptables vs. nftables.

What to allow, what to filter?

So what should be the general general rules for accessing our docker containers? We have made a short list on what we think is a good way to expose your docker containers:

First, allow ping6 and various helper packets from icmp6. This way our containers can react on debugging messages from the network and are working in settings with different MTUs. The rule for this in nftables looks as follows:

icmpv6 type { destination-unreachable, packet-too-big,
    time-exceeded, parameter-problem, echo-request,
    nd-router-advert, nd-neighbor-solicit, nd-neighbor-advert } accept

Secondly, allow the tcp ports 22 (ssh), 80 (http) and 443 (https). While http is unencrypted, we need to open it so that letsencrypt certificate verifications can work. And we want to be able to get letsencrypt certificates to secure communication with https. In nftables, this reads as follows:

tcp dport { 22, 80, 443 } accept

And the rest? We will drop the rest. This is as simple as saying drop in nftables.

Putting it all together

So how does this look like in a complete picture? You can use the below configuration directly on your machine, just replace 2001:db8::/64 with your IPv6 docker network.

table ip6 filter {
        chain forward {
                type filter hook forward priority filter; policy accept;
                ct state established,related accept
                ip6 daddr 2001:db8::/64 jump to_docker_container
        }

        chain to_docker_container {
                icmpv6 type { destination-unreachable, packet-too-big, time-exceeded, parameter-problem, echo-request, nd-router-advert, nd-neighbor-solicit, nd-neighbor-advert } accept
                tcp dport { 22, 80, 443 } accept
                drop
        }
}

You can save this snippet as nftables.conf and run nft -f nftables.conf to apply it. Use nftables list ruleset to see your active rules.

Proxying insecure applications

With the above firewall you can run your insecure applications on any port that is not 22, 80 or 443. And then you can use a side car or proxy to expose it securely. If you use above firewall, we recommend to run your insecure (http) containers on port 8080. This indicates it is http (alike) and is also automatically blocked from the outside world.

Learning more about this

You can give above a direct try with a VM from IPv6onlyhosting, where you also get a /64 for free per VM. Or you can talk to others about it on the IPv6.Chat.