IPv6 Access to Published Ports
The previous section described the role of userland docker-proxy supporting containers connecting to published ports, or local processes connecting to loopback interface. We've also seen that when a published port is not bound to a specific IPv4 address, the proxy process listens on an IPv6 socket, enabling IPv6 access to services offered in IPv4-only containers.
Docker proxy is a simple TCP or UDP proxy, not a NAT64 implementation.
It's easy to check whether the expected IPv6 functionality works: connect to a published port on ::1 from a local process:
$ docker run --rm -d --name web_1 -p 8080:80 webapp 3398eae2649a55d2b9aa11c4979a50e025c035e34227537f4f4bd91c3ba44c9f $ curl http://[::1]:8080/ <b>Hostname:</b> 3398eae2649a<br/> <b>Remote IP:</b> 172.17.0.1
As expected, the remote IP address seen by the web server running in a Docker container (our Flask application) is the source IPv4 address of the outgoing docker-proxy session.
If you want to see IPv6 and IPv4 TCP sessions going through the docker-proxy process, use telnet to connect to a published port and netstat to display the established sessions:
$ netstat -nt Active Internet connections (w/o servers) Proto Recv-Q Send-Q Local Address Foreign Address State tcp 0 0 172.17.0.1:52880 172.17.0.2:80 ESTABLISHED tcp6 0 0 ::1:8080 ::1:41534 ESTABLISHED tcp6 0 0 ::1:41534 ::1:8080 ESTABLISHED
IPv6 access to published ports is obviously not limited to the local processes; IPv6 clients can reach a container-based service as soon as you have an IPv6 address configured on an external interface.
$ ip addr show dev eth1 3: eth1: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc... link/ether 08:00:27:9b:8a:66 brd ff:ff:ff:ff:ff:ff inet 192.168.33.2/24 brd 192.168.33.255 scope global eth1 valid_lft forever preferred_lft forever inet6 2001:db8::2/64 scope global valid_lft forever preferred_lft forever inet6 fe80::a00:27ff:fe9b:8a66/64 scope link valid_lft forever preferred_lft forever
worker-1$ curl http://[2001:db8::2]:8080/ <b>Hostname:</b> 3398eae2649a<br/> <b>Remote IP:</b> 172.17.0.1
While the Docker proxy enables convenient IPv6 connectivity to published container ports, it does not provide the true client identity information - it's a simple TCP proxy and thus does not add HTTP headers or any other indication what the actual client IPv6 address might be.
You can easily check that claim with our Flask application, which returns original HTTP headers when invoked with the
/headers path. You'll see the headers generated by the web client, but no extra headers like
X-Forwarded-For a typical web proxy might insert.
$ curl http://[2001:db8::2]:8080/headers User-Agent: curl/7.58.0 Host: [2001:db8::2]:8080 Accept: */*