moby / vpnkit

A toolkit for embedding VPN capabilities in your application
Apache License 2.0
1.09k stars 182 forks source link

Setting up forwarding and port spec round trip do not appear to support UDP under IPv6 #547

Open plm opened 2 years ago

plm commented 2 years ago

As of commit 0b631022, vpnkit/forward/udp.go's makeUDP method and vpnkit/port.go's spec method do not appear to fully support IPv6 addresses.

Under Docker Desktop 3.6.0 (67351) on macOS, I had been trying to publish a UDP port to an IPv6 address but received the error too many colons in address:

$ docker run --rm --publish [::1]:8080:80/udp nginx:latest
docker: Error response from daemon: Ports are not available: unable to resolve frontend address for port docker-proxy-port-approver/approver-7121331846359289064 udp forward from ::1:8080 to 0.0.0.0:0: address ::1:8080: too many colons in address.
ERRO[0000] error waiting for container: context canceled 

In triaging this, I found that IPv6 doesn't appear to be supported by the Docker daemon on macOS and (unsurprisingly) is not enabled on my Docker install:

$ jq -r ".ipv6 // false" < ~/.docker/daemon.json
false

However, testing port creation with TCP suggests that despite what is documented, IPv6 TCP publish doesn't appear to fail, and, if it does, it doesn't do so in the same way that IPv6 UDP does above:

$ docker run --rm --publish [::1]:8080:80/tcp nginx:latest
/docker-entrypoint.sh: /docker-entrypoint.d/ is not empty, will attempt to perform configuration
/docker-entrypoint.sh: Looking for shell scripts in /docker-entrypoint.d/
/docker-entrypoint.sh: Launching /docker-entrypoint.d/10-listen-on-ipv6-by-default.sh
10-listen-on-ipv6-by-default.sh: info: Getting the checksum of /etc/nginx/conf.d/default.conf
10-listen-on-ipv6-by-default.sh: info: Enabled listen on IPv6 in /etc/nginx/conf.d/default.conf
/docker-entrypoint.sh: Launching /docker-entrypoint.d/20-envsubst-on-templates.sh
/docker-entrypoint.sh: Launching /docker-entrypoint.d/30-tune-worker-processes.sh
/docker-entrypoint.sh: Configuration complete; ready for start up
2021/08/20 05:06:12 [notice] 1#1: using the "epoll" event method
2021/08/20 05:06:12 [notice] 1#1: nginx/1.21.1
2021/08/20 05:06:12 [notice] 1#1: built by gcc 8.3.0 (Debian 8.3.0-6) 
2021/08/20 05:06:12 [notice] 1#1: OS: Linux 5.10.47-linuxkit
2021/08/20 05:06:12 [notice] 1#1: getrlimit(RLIMIT_NOFILE): 1048576:1048576
2021/08/20 05:06:12 [notice] 1#1: start worker processes
2021/08/20 05:06:12 [notice] 1#1: start worker process 31
2021/08/20 05:06:12 [notice] 1#1: start worker process 32
2021/08/20 05:06:12 [notice] 1#1: start worker process 33
2021/08/20 05:06:12 [notice] 1#1: start worker process 34
2021/08/20 05:06:12 [notice] 1#1: start worker process 35
2021/08/20 05:06:12 [notice] 1#1: start worker process 36

Looking through the code, I believe the UDP-specific error is assembled from code in two files:

Looking at the tests associated with these two files, I applied the patch (udp-ipv6-tests.txt) to demonstrate that:

Is the IPv6 UDP behavior observed here expected?

My Docker information is included for completeness:

$ docker version
Client:
 Cloud integration: 1.0.17
 Version:           20.10.8
 API version:       1.41
 Go version:        go1.16.6
 Git commit:        3967b7d
 Built:             Fri Jul 30 19:55:20 2021
 OS/Arch:           darwin/amd64
 Context:           default
 Experimental:      true

Server: Docker Engine - Community
 Engine:
  Version:          20.10.8
  API version:      1.41 (minimum version 1.12)
  Go version:       go1.16.6
  Git commit:       75249d8
  Built:            Fri Jul 30 19:52:10 2021
  OS/Arch:          linux/amd64
  Experimental:     false
 containerd:
  Version:          1.4.9
  GitCommit:        e25210fe30a0a703442421b0f60afac609f950a3
 runc:
  Version:          1.0.1
  GitCommit:        v1.0.1-0-g4144b63
 docker-init:
  Version:          0.19.0
  GitCommit:        de40ad0
$ docker info | sed "s/ ID: .*/ ID: REDACTED/"
Client:
 Context:    default
 Debug Mode: false
 Plugins:
  buildx: Build with BuildKit (Docker Inc., v0.6.1-docker)
  compose: Docker Compose (Docker Inc., v2.0.0-rc.1)
  scan: Docker Scan (Docker Inc., v0.8.0)

Server:
 Containers: 10
  Running: 1
  Paused: 0
  Stopped: 9
 Images: 15
 Server Version: 20.10.8
 Storage Driver: overlay2
  Backing Filesystem: extfs
  Supports d_type: true
  Native Overlay Diff: true
  userxattr: false
 Logging Driver: json-file
 Cgroup Driver: cgroupfs
 Cgroup Version: 1
 Plugins:
  Volume: local
  Network: bridge host ipvlan macvlan null overlay
  Log: awslogs fluentd gcplogs gelf journald json-file local logentries splunk syslog
 Swarm: inactive
 Runtimes: io.containerd.runc.v2 io.containerd.runtime.v1.linux runc
 Default Runtime: runc
 Init Binary: docker-init
 containerd version: e25210fe30a0a703442421b0f60afac609f950a3
 runc version: v1.0.1-0-g4144b63
 init version: de40ad0
 Security Options:
  seccomp
   Profile: default
 Kernel Version: 5.10.47-linuxkit
 Operating System: Docker Desktop
 OSType: linux
 Architecture: x86_64
 CPUs: 6
 Total Memory: 7.773GiB
 Name: docker-desktop
 ID: REDACTED
 Docker Root Dir: /var/lib/docker
 Debug Mode: false
 HTTP Proxy: http.docker.internal:3128
 HTTPS Proxy: http.docker.internal:3128
 Registry: https://index.docker.io/v1/
 Labels:
 Experimental: false
 Insecure Registries:
  127.0.0.0/8
 Live Restore Enabled: false

$ ifconfig lo0 inet6
lo0: flags=8049<UP,LOOPBACK,RUNNING,MULTICAST> mtu 16384
    options=1203<RXCSUM,TXCSUM,TXSTATUS,SW_TIMESTAMP>
    inet6 ::1 prefixlen 128 
    inet6 fe80::1%lo0 prefixlen 64 scopeid 0x1 
    nd6 options=201<PERFORMNUD,DAD>