💾 Archived View for bgp.rocks › ipv6-and-docker-containers.gmi captured on 2022-01-08 at 13:36:54. Gemini links have been rewritten to link to archived content
⬅️ Previous capture (2021-11-30)
-=-=-=-=-=-=-
Published on 2021-07-17.
I use Docker to run some services, which is working just fine. I however want to enable IPv6 in my containers so that these services can be reached over IPv6 connections. In this post I will show two ways of doing it.
For reference I am running Docker version 20.10.7 on Alpine Linux version 3.14.
To test the IPv6 setup I will be using an nginx container, and for making my life easier I created two DNS records pointing to the servers IP addresses:
- docker-ipv4.bgp.rocks - 192.0.2.10
- docker-ipv6.bgp.rocks - 2001:db8:1234::7357
This is the nginx configuration:
user nginx;
worker_processes auto;
error_log /var/log/nginx/error.log notice;
pid /var/run/nginx.pid;
events {
worker_connections 1024;
}
http {
include /etc/nginx/mime.types;
default_type application/octet-stream;
log_format main '$remote_addr - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent" "$http_x_forwarded_for"';
access_log /var/log/nginx/access.log main;
sendfile on;
keepalive_timeout 65;
server {
listen 80;
listen [::]:80;
server_name localhost;
location / {
default_type text/plain;
return 200 "Server addr: $server_addr\nRemote addr: $remote_addr\n";
}
}
}
It will return the container IP address and the clients IP address. Save the file and start the container:
docker run --rm --name nginx -v $PWD/nginx.conf:/etc/nginx/nginx.conf:ro -p 192.0.2.10:8080:80 -p [2001:db8:1234::7357]:8080:80 nginx:alpine
If you are not familiar with Docker, this will happen:
- We will start a [nginx:alpine](https://hub.docker.com/_/nginx) container, name it "nginx" and it will be removed once it's stopped with ctrl+c
- We map the file nginx.conf (the file with the config from above) to /etc/nginx/nginx.conf inside the container. nginx will read the config from this file
- We bind to port 8080 on two IP addresses, one IPv4 and one IPv6
- Port 8080 is exposed to the world, and forwarded to port 80 inside the container which nginx is listening on
The IPv6 address is surrounded by [] because that is the standard way of marking something as an IPv6 address and not for instance ports.
Once the container has started we can try and reach it via both IPv4 and IPv6:
~ ❯ curl http://docker-ipv4.bgp.rocks:8080
Server addr: 172.17.0.3
Remote addr: 198.51.100.97
~ ❯ curl http://docker-ipv6.bgp.rocks:8080
Server addr: 172.17.0.3
Remote addr: 172.17.0.1
This is surprising and a bit unexpected.
We, as a user, can reach the container via both IPv4 and IPv6 but internally all traffic gets translated into IPv4 by Docker. This means that the container has no idea what traffic type it is receiving. The IP addresses shown above are standard addresses Docker uses for it's internal network.
This may or may not be a passable solution, it is indeed a fast way of getting IPv6 connectivity to a container but I want to expose the application inside to actual IPv6 traffic.
Another issue not shown above is that Docker doesn't update ip6tables rules. By default iptables rules are added when a new container is created, so that it can be reached from the outside world (if necessary). This does not happen with ip6tables which means that new rules has to be added manually when a new container is created.
To enable IPv6 traffic in a container we need to enable IPv6 and specify a network from which the container will get an IP address, and to enable Docker to modify ip6tables rules we must enable experimental features and tell Docker to update the rules:
{
"ipv6": true,
"fixed-cidr-v6": "2001:db8:1::/64",
"experimental": true,
"ip6tables": true
}
Add that configuration in /etc/docker/daemon.json, restart the Docker daemon and create the nginx container once again. Now we will receive the following response from the container:
~ ❯ curl http://docker-ipv4.bgp.rocks:8080
Server addr: 172.17.0.3
Remote addr: 198.51.100.97
~ ❯ curl http://docker-ipv6.bgp.rocks:8080
Server addr: 2001:db8:1::832:be22:9
Remote addr: 2001:db8:2327:77ff:b298:2e5f:1911:2ef5
Now the container is receiving IPv6 traffic and ip6tables is automatically updated whenever a container is created or destroyed.