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.