caddyserver / caddy

Fast and extensible multi-platform HTTP/1-2-3 web server with automatic HTTPS
https://caddyserver.com
Apache License 2.0
58.15k stars 4.03k forks source link

using 0.0.0.0 and [::] as default_bind/bind results in double binding 0.0.0.0 and [::] in h3/UDP and crash #5692

Open Zoey2936 opened 1 year ago

Zoey2936 commented 1 year ago

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

mholt commented 1 year ago

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.

Zoey2936 commented 1 year ago

I think with update 4, the problem should now be clear

Zoey2936 commented 1 year ago

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?

francislavoie commented 1 year ago

Yeah I think that's correct, it should only bind for the same IP version as explicitly specified.

thijsvandien commented 7 months ago

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.

mholt commented 7 months ago

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.