Hi,
Docker IPv6 support is messed up. Instead of sharing a non-routed IPv6 prefix between host and containers, just like it does for IPv4, docker team decided to implement a full IPv6 support, with routing and everything.
The point is: most of people just want basic IPv6 inside the container (in my case I’m doing monitoring checks) and would love to have it working out of the box. Honestly, who wants a dedicated routed prefix for its IPv6 containers ? Certainly not me.
So here’s the trick: we’re going to use a chosen prefix between docker daemon and its host and we’ll use shorewall6 to NAT it when going outside. Basically it’ll mimic IPv4 behavior.
So first, make sure IPv6 is working correctly on host (actually while writing this doc, it was not on my sample setup :D):
1 2 3 4 |
root@server:~# ping6 -n google.lu PING google.lu(2a00:1450:4001:816::2003) 56 data bytes 64 bytes from 2a00:1450:4001:816::2003: icmp_seq=1 ttl=57 time=10.3 ms 64 bytes from 2a00:1450:4001:816::2003: icmp_seq=2 ttl=57 time=10.3 ms |
Then, define a IPv6 prefix to be use inside docker by editing /etc/docker/daemon.json
1 2 3 4 |
{ "ipv6": true, "fixed-cidr-v6": "2a00:1450::/64" } |
Create the file if not existing yet.
Restart docker daemon:
1 |
systemctl restart docker |
Now your container should have an IP range in the range (and a default gateway):
1 2 3 4 5 6 7 8 9 |
root@server:~# docker <container_name> -ti centreon ip -6 a 1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 state UNKNOWN qlen 1 inet6 ::1/128 scope host valid_lft forever preferred_lft forever 228: eth0@if229: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 state UP inet6 2a00:1450::242:ac11:2/64 scope global nodad valid_lft forever preferred_lft forever inet6 fe80::42:acff:fe11:2/64 scope link valid_lft forever preferred_lft forever |
1 2 3 4 |
root@server:~# docker exec -ti <container_name> ip -6 r 2a00:1450::/64 dev eth0 proto kernel metric 256 fe80::/64 dev eth0 proto kernel metric 256 default via 2a00:1450::1 dev eth0 metric 1024 |
You host should be able to reach the container too:
1 2 3 4 |
root@server:~# ping6 2a00:1450::242:ac11:2 PING 2a00:1450::242:ac11:2(2a00:1450::242:ac11:2) 56 data bytes 64 bytes from 2a00:1450::242:ac11:2: icmp_seq=1 ttl=64 time=0.076 ms 64 bytes from 2a00:1450::242:ac11:2: icmp_seq=2 ttl=64 time=0.099 ms |
Here we go… Now all we need to do is make a source NAT of 2a00:1450::/64 when going out of eth0 on the host server.
For this, we’ll use shorewall:
1 |
apt -y install shorewall6 |
And we’ll create a default configuration based on provided examples:
1 |
cp /usr/share/doc/shorewall6/examples/one-interface/{policy,interfaces,rules,zones} /etc/shorewall6/ |
Default configuration can work but I’ll tweak it a bit.
In /etc/shorewall6/interfaces I renamed the interface to loc instead of net and disable routeur advertisment (IPv6 gateway statically defined in /etc/network/interfaces) and I need to define the docker bridge interface:
1 2 |
loc eth0 tcpflags,accept_ra=0,sourceroute=0 dockr docker0 tcpflags,accept_ra=0,sourceroute=0 |
In /etc/shorewall6/zones there must be one ipv6 zone for each interface:
1 2 3 |
fw firewall loc ipv6 dockr ipv6 |
In /etc/shorewall6/policy I give full access to host server to any network, docker has access to local network (but not to its host) and I block everything else
1 2 3 |
fw all ACCEPT dockr loc ACCEPT all all DROP info |
Then, I create a few overrides in /etc/shorewall6/rules to authorize Ping and SSH from local network:
1 2 3 4 5 6 7 8 9 10 11 12 |
# From docker network Ping(ACCEPT) dockr fw MySQL(ACCEPT) dockr fw SNMP(ACCEPT) dockr fw ACCEPT dockr fw tcp 5666 # From local netowork Ping(ACCEPT) loc fw SSH(ACCEPT) loc fw SNMP(ACCEPT) loc fw ACCEPT loc fw tcp 5666 HTTP(ACCEPT) loc fw |
As you can see, there’s a few more rules: at least one containers uses a MariaDB server on the host, so I permit that. Also I permit Nagios NRPE and SNMP from local network to host (fw) for monitoring purpose and I also accept the same from container to host because there’s a monitoring container that will actually monitor its own host.
Now we need to NAT the private IPv6 subnet used by docker in /etc/shorewall6/snat
1 |
MASQUERADE 2a00:1450::/64 eth0 |
Finally we’ll enable IP_FORWARDING in */etc/shorewall6/shorewall.conf* by setting it to yes and restart shorewall:
1 2 3 |
sed -i 's!IP_FORWARDING=.*!IP_FORWARDING=Yes!' /etc/shorewall6/shorewall6.conf systemctl enable shorewall6 systemctl start shorewall6 |
And that’s it:
1 2 3 4 |
root@server:~# docker exec -ti <container_name> ping6 -n google.lu PING google.lu(2a00:1450:4001:81b::2003) 56 data bytes 64 bytes from 2a00:1450:4001:81b::2003: icmp_seq=1 ttl=56 time=10.6 ms 64 bytes from 2a00:1450:4001:81b::2003: icmp_seq=2 ttl=56 time=10.5 ms |