[Bug Report] daemon doesn't listen on IPv6
Describe the bug The daemon only listens on IPv4, even if the Docker container has IPv6 enabled & working.
To Reproduce Steps to reproduce the behavior:
- Configure Docker to use IPv6
- Modify the
docker-compose.ymlfile to enable IPv6 for the network (see below) - Run
docker compose up - Run
docker compose inspect …to determine IPv4 & IPv6 addresses - Try to connect to the IPv4 & IPv6 addresses determined in step 4, port 8000 each time, & observer that you can connect via IPv4 but not IPv6:
[0 root@durnik /srv/docker-compose/yacht] docker container inspect yacht | rg 'IPAddress|GlobalIPv6Address' | tail -n 2
"IPAddress": "172.18.2.2",
"GlobalIPv6Address": "2a01:4f8:262:xxxx:a:b:c:d",
[0 root@durnik /srv/docker-compose/yacht] telnet 172.18.2.2 8000
Trying 172.18.2.2...
Connected to 172.18.2.2.
Escape character is '^]'.
HTTP/1.1 400 Bad Request
Server: nginx/1.18.0
Date: Sun, 20 Nov 2022 00:38:37 GMT
Content-Type: text/html
Content-Length: 157
Connection: close
<html>
<head><title>400 Bad Request</title></head>
<body>
<center><h1>400 Bad Request</h1></center>
<hr><center>nginx/1.18.0</center>
</body>
</html>
Connection closed by foreign host.
[1 root@durnik /srv/docker-compose/yacht] telnet 2a01:4f8:262:xxxx:a:b:c:d 8000
Trying 2a01:4f8:262:xxxx:a:b:c:d...
telnet: Unable to connect to remote host: Connection refused
Actual IPv6 obscured.
Expected behavior The daemon should listen on both IPv4 & IPv6.
Desktop (please complete the following information):
- Yacht Docker image ID: 27aae3676a3a
Additional context
Contents of docker-compose.yml:
---
version: "3"
services:
yacht:
container_name: yacht
restart: unless-stopped
ports:
- 8200:8000
volumes:
- /srv/docker-volumes/yacht-config:/config
- /var/run/docker.sock:/var/run/docker.sock
image: selfhostedpro/yacht
networks:
- net
networks:
net:
enable_ipv6: true
ipam:
config:
- subnet: "172.18.2.0/24"
- subnet: "2a01:4f8:262:xxxx:a::/80"
Again, actual IPv6 subnet obscured.
** Logs **
[0 root@durnik /srv/docker-compose/yacht] docker compose logs
yacht | [s6-init] making user provided files available at /var/run/s6/etc...exited 0.
yacht | [s6-init] ensuring user provided files have correct perms...exited 0.
yacht | [fix-attrs.d] applying ownership & permissions fixes...
yacht | [fix-attrs.d] done.
yacht | [cont-init.d] executing container initialization scripts...
yacht | [cont-init.d] 01-envfile: executing...
yacht | [cont-init.d] 01-envfile: exited 0.
yacht | [cont-init.d] 02-tamper-check: executing...
yacht | [cont-init.d] 02-tamper-check: exited 0.
yacht | [cont-init.d] 10-adduser: executing...
yacht | usermod: no changes
yacht |
yacht | -------------------------------------
yacht | _ ()
yacht | | | ___ _ __
yacht | | | / __| | | / \
yacht | | | \__ \ | | | () |
yacht | |_| |___/ |_| \__/
yacht |
yacht |
yacht | Brought to you by linuxserver.io
yacht | -------------------------------------
yacht |
yacht | To support LSIO projects visit:
yacht | https://www.linuxserver.io/donate/
yacht | -------------------------------------
yacht | GID/UID
yacht | -------------------------------------
yacht |
yacht | User uid: 911
yacht | User gid: 911
yacht | -------------------------------------
yacht |
yacht | [cont-init.d] 10-adduser: exited 0.
yacht | [cont-init.d] 30-config: executing...
yacht | [cont-init.d] 30-config: exited 0.
yacht | [cont-init.d] 31-migrate: executing...
yacht | INFO [alembic.runtime.migration] Context impl SQLiteImpl.
yacht | INFO [alembic.runtime.migration] Will assume non-transactional DDL.
yacht | INFO [alembic.autogenerate.compare] Detected removed index 'ix_secret_key_key' on 'secret_key'
yacht | INFO [alembic.autogenerate.compare] Detected removed table 'secret_key'
yacht | INFO [alembic.autogenerate.compare] Detected removed index 'ix_jwt_token_blacklist_jti' on 'jwt_token_blacklist'
yacht | INFO [alembic.autogenerate.compare] Detected removed table 'jwt_token_blacklist'
yacht | --- MODELS ---
yacht | Generating /alembic/versions/9afeebe2e0e4_automated_db_upgrade.py ... done
yacht | INFO [alembic.runtime.migration] Context impl SQLiteImpl.
yacht | INFO [alembic.runtime.migration] Will assume non-transactional DDL.
yacht | INFO [alembic.runtime.migration] Running upgrade -> 9afeebe2e0e4, automated db upgrade
yacht | --- MODELS ---
yacht | [cont-init.d] 31-migrate: exited 0.
yacht | [cont-init.d] 32-env: executing...
yacht | Replacing env constants in JS
yacht | Processing /app/js/app.37804ef8.js ...
yacht | Processing /app/js/app.37804ef8.js.map ...
yacht | Processing /app/index.html ...
yacht | [cont-init.d] 32-env: exited 0.
yacht | [cont-init.d] 90-custom-folders: executing...
yacht | [cont-init.d] 90-custom-folders: exited 0.
yacht | [cont-init.d] 99-custom-files: executing...
yacht | [custom-init] no custom files found exiting...
yacht | [cont-init.d] 99-custom-files: exited 0.
yacht | [cont-init.d] done.
yacht | [services.d] starting services
yacht | [services.d] done.
yacht | INFO: Started server process [356]
yacht | INFO: Waiting for application startup.
yacht | INFO: Application startup complete.
yacht | INFO: Uvicorn running on unix socket /tmp/gunicorn.sock (Press CTRL+C to quit)
yacht | Secret key generated
yacht | Secret key exists
yacht | DISABLE_AUTH = False (<class 'bool'>)
yacht | Users Exist
yacht | Template Variables Exist
I believe the container is setup to support ipv4 only, in general docker does not work well with ip6.
The container has no problem with IPv6, or rather, with outgoing IPv6:
[0 root@durnik /srv/docker-compose/yacht] docker exec -ti yacht /bin/bash
root@23d4d0143f51:/api# ping -6 www.google.com
PING www.google.com (2a00:1450:4001:802::2004): 56 data bytes
64 bytes from 2a00:1450:4001:802::2004: seq=0 ttl=116 time=5.407 ms
64 bytes from 2a00:1450:4001:802::2004: seq=1 ttl=116 time=5.852 ms
^C
--- www.google.com ping statistics ---
2 packets transmitted, 2 packets received, 0% packet loss
round-trip min/avg/max = 5.407/5.629/5.852 ms
root@23d4d0143f51:/api#
I'm well aware of Docker's sub-par IPv6 support. Having images support IPv6 would be a great step in improving the situation overall, especially given that there are other orchestrators out there that use the same images. For example Kubernetes has proper IPv6 support now (or is pretty close to having it). And yes, I'm also aware that Kubernetes isn't a great argument in the context of Yacht :grin:
Anyway, in the case of Yacht "proper IPv6" support would probably mean nothing more than making the daemon listen on IPv6, too. In terms of sockets this means listening on :: instead of 0.0.0.0, or if the port number is part of the listen spec, on [::]:8000 instead of 0.0.0.0:8000. The reason is that at least on Linux listening on the IPv6 wildcard address will automatically listen on IPv4, too.
If you're worried about that, you can also try listening on both [::]:8000 and 0.0.0.0:8000, in that order, and ignore failures if exactly one of those fails:
- if listening on
[::]:8000succeeds & turns on listening on IPv4 as well, then0.0.0.0:8000will fail; the daemon will now listen on both IPv6 & IPv4 - if listening on
[::]:8000succeeds & but doesn't turn on listening on IPv4 as well, then0.0.0.0:8000will also succeed, and the daemon will now listen on both IPv6 & IPv4 - if listening on
[::]:8000fails due to IPv6 being completely turned off on the host but listening on0.0.0.0:8000succeeds, then the daemon will only listen on IPv4, and that'd be OK for those hosts
As a last resort one might make the address/port number to listen on configurable via environment variables. Other images go that route.
As an example for Docker images that support listening on IPv6 out of the box (or where I could configure it via an environment variable): Authentik; Wekan; PowerDNS Admin; Docker Registry; Portainer.