--- title: Give IPv6 connectivity to its Docker containers using an IPv6 block from its ISP date: !!timestamp '2023-05-04 15:10:00' tags: - network - ipv6 - docker --- It may seem surprising that a modern service like Docker does not offer IPv6 in containers by default, especially when in a network with IPv6. In fact, for the same reason we saw in [the introductory article]({{< relref "use-additional-ipv6-blocks-from-isp.md" >}}), since the containers are in a virtual network, they cannot be reached by the box/router distributing the IPv6 subnet. The same phenomenon can be observed with IPv4: each container has an IPv4 in a subnet separate from the one in which our host machine is located. ![Illustration of a classic IPv4 home network](common-network-with-docker.png) In order for the containers to have access to the Internet under these conditions, in IPv4 NAT is implemented: ``` 42sh$ iptables -t nat -vnL POSTROUTING Chain POSTROUTING (policy ACCEPT 3 packets, 228 bytes) pkts bytes target prot opt in out source destination 14713 978K MASQUERADE all -- * !docker0 172.17.0.0/16 0.0.0.0/0 ``` Since NAT is generally not done on IPv6, nothing similar is done by Docker in this sense. # Docker as IPv6 router Without IPv6 in a container, it is impossible for containers to address other services listening exclusively in IPv6 on the Internet. In order for containerized programs to be able to connect to other services in IPv6, the *Enable IPv6* option must be activated and the prefix to be used must be defined through the *IPv6 Prefix* option. Be careful, it is not enough to define these options, it is also necessary that the box correctly routes the packets to the destinations of the containers to your machine. This is why we need to take advantage of the other IPv6 blocks provided by our operator. By telling the box the address of the machine hosting our containers, it will route all packets to the containers without question. So not everything can be done exclusively on the machine, the network must also be configured. Let's start with that. # Set up IPv6 prefix delegation on the Freebox The router will ask us for the address (IPv6) to which it should route the packets. We usually indicate a [local link IP](https://en.wikipedia.org/wiki/Link-local_address). So we start by looking at our local IPv6 on the outgoing link to the router. ⚠️ Be careful, all interfaces have a local address, they all start with `fe80:`, they are only valid on the network card considered. If you get the wrong address, nothing will happen (it won't break your network though). In my case, it is the `eth0` interface that is connected to the router: ``` 42sh$ ip address show eth0 2: eth0: mtu 1500 qdisc mq state UP group default qlen 1000 link/ether fd:54:01:98:cd:ba brd ff:ff:ff:ff:ff:ff inet 192.168.0.42/24 brd 192.168.0.255 scope global dynamic noprefixroute eth0 valid_lft 35141sec preferred_lft 35141sec inet6 2a01:...:2420:24ac:f101:c280:50c2/64 scope global noprefixroute valid_lft forever preferred_lft forever inet6 fe80::5a43:3580:173c:395e/64 scope link noprefixroute valid_lft forever preferred_lft forever ``` My local IP is therefore `fe80::5a43:3580:173c:395e`. It is this IP that I will indicate in the configuration of the router. On the Freebox, the window for setting additional prefixes is in "Paramètres de la Freebox", "Configuration IPv6", under the "Général" tab. It is the "Délégation de préfixe" box that will interest us. It looks like this: ![Freebox IPv6 prefix delegation settings window](freebox-ipv6-prefix-delegation.png) Always leave the first field empty, otherwise the box will not offer you IPv6 on the main network. Indicate in the next empty field (normally the second one!) the local address retrieved earlier. That's all! The hardest part is over. Now let's see the Docker configuration. # Setting up Docker for IPv6 We will not use the range to which our machine is connected. We are going to use a whole /64 range, the one for which we have given the local IP of our machine to the box. ![Our prefix delegation correctly set up on the Freebox](freebox-ipv6-delegation-filled.png) According to the previous screenshot, our configuration file `/etc/docker/daemon.json` should look like: ``` { "ipv6": true, "fixed-cidr-v6": "2a01:1234:abcd:2421::/64" } ``` We restart Docker and we can test: ``` 42sh$ docker run -it alpine / # ip address show eth0 58: eth0@if59: mtu 1500 qdisc noqueue state UP link/ether 02:42:ac:11:00:09 brd ff:ff:ff:ff:ff:ff inet 172.17.0.9/16 brd 172.17.255.255 scope global eth0 valid_lft forever preferred_lft forever inet6 2a01:1234:abcd:2421:0:242:ac11:9/64 scope global flags 02 valid_lft forever preferred_lft forever inet6 fe80::42:acff:fe11:9/64 scope link valid_lft forever preferred_lft forever ``` If you have an IPv6 in addition to the usual IPv4, Docker is correctly configured. To find out if the configuration on the box side was successful, let's do a `ping` in the container: ``` / # ping ping6.online.net PING ping6.online.net (2001:bc8:1::40): 56 data bytes 64 bytes from 2001:bc8:1::40: seq=0 ttl=52 time=11.008 ms 64 bytes from 2001:bc8:1::40: seq=1 ttl=52 time=8.822 ms ^C --- ping6.online.net ping statistics --- 2 packets transmitted, 2 packets received, 0% packet loss round-trip min/avg/max = 8.822/9.915/11.008 ms ``` If the ping responds, it's all good: your containers will now have access to and be accessible in IPv6. # Others use cases This post is part of a series of posts on the use of additional IPv6 ranges: - [Introduction: Use the additional IPv6 blocks of the Free and Orange network]({{< relref "use-additional-ipv6-blocks-from-isp.md" >}}) - Use a /64 block to give IPv6 to its virtual machines - Use a /64 block to give IPv6 to your Docker containers - Use a /64 block to have IPv6 in several isolated subnets - Use a /64 block to have public IPv6 in your Wireguard tunnel