I started this document asking for help and while writing this I ended up finding a solution so i figured I’d share anyways.
I followed this this guide to setup ipv6 for docker.
In my case I have this in my daemon.json:
{
"ipv6": true,
"fixed-cidr-v6": "fd00::/80"
}
AND!!! needs custom iptables rules.
ip6tables -t nat -A POSTROUTING -s fd00::/80 ! -o docker0 -j MASQUERADE
It took some mucking around but eventually i got working and this line seems to work.
#!/usr/bin/env bash
docker run --rm -t busybox ping6 -c 4 google.com
output:
PING google.com (2607:f8b0:400a:808::200e): 56 data bytes
64 bytes from 2607:f8b0:400a:808::200e: seq=0 ttl=119 time=17.133 ms
64 bytes from 2607:f8b0:400a:808::200e: seq=1 ttl=119 time=17.119 ms
64 bytes from 2607:f8b0:400a:808::200e: seq=2 ttl=119 time=17.281 ms
64 bytes from 2607:f8b0:400a:808::200e: seq=3 ttl=119 time=17.430 ms
--- google.com ping statistics ---
4 packets transmitted, 4 packets received, 0% packet loss
round-trip min/avg/max = 17.119/17.240/17.430 ms
Okay great, So at least docker knows how to speak ipv6. Now docker-compose.
I ran into multiple references this and this one that point out that the latest compose format of compose doesn’t support IPv6 and you have to use the 2.1 format.
~/ $ docker -v
Docker version 19.03.12, build 48a66213fe
~/ $ docker-compose -v
docker-compose version 1.26.2, build eefe0d31
So I believe that each time docker-compose runs unless i’m explicitly specifying an external network, it’s creating a new network for my stack. So even though docker itself works fine, compose needs more work.
The first attempt is this:
version: "2.1"
services:
busy:
image: busybox
command: ping6 -c 4 google.com
out:
busy_1 | PING google.com (2607:f8b0:400a:808::200e): 56 data bytes
busy_1 | ping6: sendto: Network is unreachable
So, trying to define everything I did for docker in compose:
version: "2.1"
services:
busy:
image: busybox
command: ping6 -c 4 google.com
networks:
- app_net
networks:
app_net:
enable_ipv6: true
driver: bridge
driver_opts:
com.docker.network.enable_ipv6: "true"
ipam:
driver: default
config:
- subnet: 172.16.238.0/24
gateway: 172.16.238.1
- subnet: 2001:3984:3989::/64
gateway: 2001:3984:3989::1
out:
busy_1 | PING google.com (2607:f8b0:400a:808::200e): 56 data bytes
busy_1 |
busy_1 | --- google.com ping statistics ---
busy_1 | 4 packets transmitted, 0 packets received, 100% packet loss
and last iteration explicitly choosing an IP
version: "2.1"
services:
busy:
image: busybox
command: ping6 -c 4 google.com
networks:
app_net:
ipv4_address: 172.16.238.10
ipv6_address: 2001:3984:3989::10
networks:
app_net:
enable_ipv6: true
driver: bridge
driver_opts:
com.docker.network.enable_ipv6: "true"
ipam:
driver: default
config:
- subnet: 172.16.238.0/24
gateway: 172.16.238.1
- subnet: 2001:3984:3989::/64
gateway: 2001:3984:3989::1
out:
busy_1 | PING google.com (2607:f8b0:400a:808::200e): 56 data bytes
busy_1 |
busy_1 | --- google.com ping statistics ---
busy_1 | 4 packets transmitted, 0 packets received, 100% packet loss
Finally, the last thing I forgot is that since i’m defining a new network, I need to also create an iptables rule.
sudo ip6tables -t nat -A POSTROUTING -s 2001:3984:3989::/64 ! -o docker0 -j MASQUERADE
it works!!
busy_1 | PING google.com (2607:f8b0:400a:808::200e): 56 data bytes
busy_1 | 64 bytes from 2607:f8b0:400a:808::200e: seq=0 ttl=119 time=17.087 ms
busy_1 | 64 bytes from 2607:f8b0:400a:808::200e: seq=1 ttl=119 time=17.041 ms
busy_1 | 64 bytes from 2607:f8b0:400a:808::200e: seq=2 ttl=119 time=17.042 ms
busy_1 | 64 bytes from 2607:f8b0:400a:808::200e: seq=3 ttl=119 time=17.100 ms
So basically what I got out of this is this.
Summary
- Docker IPv6 docs is mostly complete, requires an iptables rule to be in place.
- Docker-compose works, but requires file format to be set to 2.1
- For both docker and compose you will need to maintain your own routing rules via iptables in order for any traffic to be allowed to flow through.
Also for reference to remove the rule you’d replace the -A with a -D, example:
ip6tables -t nat -D POSTROUTING -s 2001:3984:3989::/64 ! -o docker0 -j MASQUERADE
Have I missed something? This seems incredibly over complicated. The whole appeal of docker is that it hides much of complexities. You define the ports, the app create an image and then use compose and all the routing and such just works. It creates/destroys networks as needed.
I’m glad i finally figured out how to get IPv6 to work, but this is Not exactly trivial, not to mention since we’re changing firewall rules, it requires root access for each compose definition being deployed.
Any thoughts?