caddy
caddy copied to clipboard
using 0.0.0.0 and [::] as default_bind/bind results in double binding 0.0.0.0 and [::] in h3/UDP and crash
Hello, if I run one of this Caddyfiles:
{
debug
default_bind 0.0.0.0 [::]
servers 0.0.0.0:443 {
protocols h1 h2 h2c h3
}
servers [::]:443 {
protocols h1 h2 h2c h3
}
}
example.com {
respond "Hello!"
}
{
debug
default_bind 0.0.0.0 [::]
}
example.com {
respond "Hello!"
}
example.com {
bind 0.0.0.0 [::]
respond "Hello!"
}
it results in (the first caddyfile):
root@PC-Zoey:~# caddy version
v2.7.2 h1:QqThyoyUFAv1B7A2NMeaWlz7xmgKqU49PXBX08A+6xg=
root@PC-Zoey:~# caddy run --adapter caddyfile --config t
2023/08/04 16:34:42.122 INFO using provided configuration {"config_file": "t", "config_adapter": "caddyfile"}
2023/08/04 16:34:42.124 INFO admin admin endpoint started {"address": "localhost:2019", "enforce_origin": false, "origins": ["//[::1]:2019", "//127.0.0.1:2019", "//localhost:2019"]}
2023/08/04 16:34:42.124 INFO http.auto_https server is listening only on the HTTPS port but has no TLS connection policies; adding one to enable TLS {"server_name": "srv0", "https_port": 443}
2023/08/04 16:34:42.124 INFO http.auto_https enabling automatic HTTP->HTTPS redirects {"server_name": "srv0"}
2023/08/04 16:34:42.124 DEBUG http.auto_https adjusted config {"tls": {"automation":{"policies":[{}]}}, "http": {"servers":{"remaining_auto_https_redirects":{"listen":["[::]:80"],"routes":[{},{}]},"srv0":{"listen":["0.0.0.0:443","[::]:443"],"routes":[{"handle":[{"handler":"subroute","routes":[{"handle":[{"body":"Hello!","handler":"static_response"}]}]}],"terminal":true}],"tls_connection_policies":[{}],"automatic_https":{},"protocols":["h1","h2","h2c","h3"]}}}}
2023/08/04 16:34:42.124 INFO tls.cache.maintenance started background certificate maintenance {"cache": "0xc0003c7180"}
2023/08/04 16:34:42.124 INFO http enabling HTTP/3 listener {"addr": "0.0.0.0:443"}
2023/08/04 16:34:42.124 INFO tls cleaning storage unit {"description": "FileStorage:/root/.local/share/caddy"}
2023/08/04 16:34:42.124 DEBUG http starting server loop {"address": "[::]:443", "tls": true, "http3": true}
2023/08/04 16:34:42.124 INFO http enabling HTTP/3 listener {"addr": "[::]:443"}
2023/08/04 16:34:42.124 INFO tls.cache.maintenance stopped background certificate maintenance {"cache": "0xc0003c7180"}
2023/08/04 16:34:42.124 INFO tls finished cleaning storage units
Error: loading initial config: loading new config: http app module: start: listen udp 0.0.0.0:443: bind: address already in use
root@PC-Zoey:~# caddy version
2.6.2
root@PC-Zoey:~# caddy run --adapter caddyfile --config t
2023/08/04 16:36:21.641 INFO using provided configuration {"config_file": "t", "config_adapter": "caddyfile"}
2023/08/04 16:36:21.643 INFO admin admin endpoint started {"address": "localhost:2019", "enforce_origin": false, "origins": ["//127.0.0.1:2019", "//localhost:2019", "//[::1]:2019"]}
2023/08/04 16:36:21.643 INFO http server is listening only on the HTTPS port but has no TLS connection policies; adding one to enable TLS {"server_name": "srv0", "https_port": 443}
2023/08/04 16:36:21.643 INFO http enabling automatic HTTP->HTTPS redirects {"server_name": "srv0"}
2023/08/04 16:36:21.643 INFO tls.cache.maintenance started background certificate maintenance {"cache": "0xc000734b60"}
2023/08/04 16:36:21.644 DEBUG http starting server loop {"address": "[::]:80", "tls": false, "http3": false}
2023/08/04 16:36:21.644 INFO http.log server running {"name": "remaining_auto_https_redirects", "protocols": ["h1", "h2", "h3"]}
2023/08/04 16:36:21.644 INFO tls cleaning storage unit {"description": "FileStorage:/root/.local/share/caddy"}
2023/08/04 16:36:21.644 INFO http enabling HTTP/3 listener {"addr": "0.0.0.0:443"}
2023/08/04 16:36:21.644 INFO failed to sufficiently increase receive buffer size (was: 208 kiB, wanted: 2048 kiB, got: 416 kiB). See https://github.com/lucas-clemente/quic-go/wiki/UDP-Receive-Buffer-Size for details.
2023/08/04 16:36:21.644 INFO tls finished cleaning storage units
2023/08/04 16:36:21.644 DEBUG http starting server loop {"address": "[::]:443", "tls": true, "http3": true}
2023/08/04 16:36:21.644 INFO http enabling HTTP/3 listener {"addr": "[::]:443"}
2023/08/04 16:36:21.644 INFO tls.cache.maintenance stopped background certificate maintenance {"cache": "0xc000734b60"}
Error: loading initial config: loading new config: http app module: start: listen udp 0.0.0.0:443: bind: address already in use
But if I remove h3 from the protocols directive or if I remove the default_bind directive, it works. The same errors appear if I use bind 0.0.0.0 [::] inside the example.com host instead of setting the default_bind directive as a global option. And yes, UDP on port 443 is not used, I've tested it inside docker alpine, WSL Debian and alpine. netstat output on Debian WSL:
root@PC-Zoey:~# netstat -tulpn
Active Internet connections (only servers)
Proto Recv-Q Send-Q Local Address Foreign Address State PID/Program name
tcp 0 0 127.0.0.1:49443 0.0.0.0:* LISTEN -
tcp 0 0 127.0.0.1:49000 0.0.0.0:* LISTEN -
tcp6 0 0 :::8000 :::* LISTEN -
(sorry for the edits, but I've tried to make the caddyfile smaller, so it can be clearly seen where the problem is)
Update 1: these caddyfiles also work, so it seems that the crash only appears with multiple bind IPs:
{
debug
default_bind 0.0.0.0
servers 0.0.0.0:443 {
protocols h1 h2 h2c h3
}
}
example.com {
respond "Hello!"
}
{
debug
default_bind [::]
servers [::]:443 {
protocols h1 h2 h2c h3
}
}
example.com {
respond "Hello!"
}
Update 2: these caddyfiles also don't work:
{
default_bind 127.0.0.1 [::]
}
example.com {
tls internal {
on_demand
}
respond "Hello!"
}
Error: loading initial config: loading new config: http app module: start: listen udp 0.0.0.0:443: bind: address already in use - it seems like [::] is handles by caddy on UDP as "all" IPv4 and IPv6 IPs, even if it should only handle "all" IPv6 IPs. Not sure if 0.0.0.0 is also handled as IPv6
{
default_bind 0.0.0.0 [::1]
}
example.com {
tls internal {
on_demand
}
respond "Hello!"
}
Error: loading initial config: loading new config: http app module: start: listen udp [::1]:443: bind: address already in use
Update 3: this caddy file works, so maybe 0.0.0.0 already includes [::] in UDP while [::] included 0.0.0.0 in UDP, so that if 0.0.0.0 [::] is used, it tries to bind 0.0.0.0 and [::] twice (or at least using [::] includes [::] AND 0.0.0.0)? (again, only in udp/h3 with h3 disabled, all caddyfiles I showed here work.) So Caddy handles tcp and udp binds differently?:
{
default_bind [::1] 127.0.0.1
}
example.com {
tls internal {
on_demand
}
respond "Hello!"
}
Update 4: Interesting 0.0.0.0 seems to include [::] on TCP AND UDP and [::] also seems to include 0.0.0.0 on TCP AND UDP, but only on UDP it causes double binding, which causes a crash
Thanks for the report, and for investigating -- we'll look into this soon, or anyone else is welcome to take a stab at it too.
I think with update 4, the problem should now be clear
Can I ask what the actually wanted behaivior is? Is it wanted that 0.0.0.0 included [::]/[::] inclued 0.0.0.0, since this makes no sense to me? If I say I want to bind 0.0.0.0 then I don't want to bind [::] (and the other way, wanting to bind [::] and NOT 0.0.0.0). So will this be fixed by saying 0.0.0.0 only means IPv4 and [::] only IPv6 or will it be fixed by fixing the double binding on udp?
Yeah I think that's correct, it should only bind for the same IP version as explicitly specified.
The IPv6 address space does include the IPv4 address space, e.g. ::ffff:192.168.0.1. Nevertheless, it makes sense to interpret [::] as IPv6-only. 0.0.0.0 should never be considered equivalent to [::] though. Changing that means fighting Go's standard library a bit: https://github.com/golang/go/issues/48723.
I'm in agreement; we can use tcp4 or tcp6 network strings accordingly. Even though ipv6 includes ipv4 address space, I don't think that's what users intend when they express a bind address that way.