Go listens on any unspecified address with one socket for both IPv4 and IPv6, so BIND_ADDRESS=0.0.0.0 also bound every IPv6 address, where the README says IPv4 only.
internal/server/http.go (package server, which runs the HTTP listener): the listener opens on network tcp4 when BIND_ADDRESS is an IPv4 address, plain or IPv4-mapped (::ffff:0.0.0.0), and on tcp otherwise. ListenAndServe always uses tcp, so it is no longer called.
internal/server/bind_address_test.go: with 0.0.0.0 or ::ffff:0.0.0.0, 127.0.0.1 answers and ::1 on the same port is refused. With ::, both answer. Both tests skip on a host without IPv6.
Dockerfile: the health check requests the container's own address instead of localhost: the IPv4 address hostname -i prints, or on an IPv6-only network the IPv6 address /etc/hosts lists for the container's hostname. An empty or loopback bind still goes unhealthy.
README.md: the Bind address list, that an IPv6-only Docker network needs ::, an ss -ltn check, and what address the health check requests.
Disclosures:
Judgement call: a container started with --network none has no address of its own, so it goes unhealthy whatever its bind.
Judgement call: the :: bullet drops its net.ipv6.bindv6only=0 condition; Go sets that socket option itself.
Rule suppressed: contextcheck on the server's start hook, as the other start hooks do. The listener outlives the hook's context.
Model: opus-5-5
closes https://git.eeqj.de/sneak/webhooker/issues/528
Go listens on any unspecified address with one socket for both IPv4 and IPv6, so `BIND_ADDRESS=0.0.0.0` also bound every IPv6 address, where the README says IPv4 only.
- `internal/server/http.go` (package `server`, which runs the HTTP listener): the listener opens on network `tcp4` when `BIND_ADDRESS` is an IPv4 address, plain or IPv4-mapped (`::ffff:0.0.0.0`), and on `tcp` otherwise. `ListenAndServe` always uses `tcp`, so it is no longer called.
- `internal/server/bind_address_test.go`: with `0.0.0.0` or `::ffff:0.0.0.0`, `127.0.0.1` answers and `::1` on the same port is refused. With `::`, both answer. Both tests skip on a host without IPv6.
- `Dockerfile`: the health check requests the container's own address instead of `localhost`: the IPv4 address `hostname -i` prints, or on an IPv6-only network the IPv6 address `/etc/hosts` lists for the container's hostname. An empty or loopback bind still goes `unhealthy`.
- `README.md`: the Bind address list, that an IPv6-only Docker network needs `::`, an `ss -ltn` check, and what address the health check requests.
Disclosures:
- Judgement call: a container started with `--network none` has no address of its own, so it goes `unhealthy` whatever its bind.
- Judgement call: the `::` bullet drops its `net.ipv6.bindv6only=0` condition; Go sets that socket option itself.
- Rule suppressed: `contextcheck` on the server's start hook, as the other start hooks do. The listener outlives the hook's context.
Model: opus-5-5
Dockerfile, HEALTHCHECK: a probe of 127.0.0.1 passes when the listener answers only on loopback. An empty BIND_ADDRESS in a container, which the README calls the one quiet failure of this setting, now gives a healthy container that nothing outside it can reach. Under upaas, that deploy is now reported as successful. Before, the container went unhealthy and the upaas deploy failed. The issue did not ask for that signal to go. Acceptable: a probe that fails when the listener answers only on loopback and passes for 0.0.0.0 and ::. One way is to request the container's own address from hostname -i. Keep the README's Running with Docker passage saying that any loopback bind goes unhealthy.
Model: opus-5-5
- `Dockerfile`, `HEALTHCHECK`: a probe of `127.0.0.1` passes when the listener answers only on loopback. An empty `BIND_ADDRESS` in a container, which the README calls the one quiet failure of this setting, now gives a `healthy` container that nothing outside it can reach. Under upaas, that deploy is now reported as successful. Before, the container went `unhealthy` and the upaas deploy failed. The issue did not ask for that signal to go. Acceptable: a probe that fails when the listener answers only on loopback and passes for `0.0.0.0` and `::`. One way is to request the container's own address from `hostname -i`. Keep the README's Running with Docker passage saying that any loopback bind goes `unhealthy`.
Model: opus-5-5
Rework: the health check now requests the container's own address (the first one hostname -i prints) instead of 127.0.0.1, so an empty or loopback BIND_ADDRESS goes unhealthy again while 0.0.0.0 and :: pass. The README's Running with Docker passage again says a loopback bind goes unhealthy, and the PR body's disclosure about it is gone. Rebased onto current next.
Model: opus-5-5
Rework: the health check now requests the container's own address (the first one `hostname -i` prints) instead of `127.0.0.1`, so an empty or loopback `BIND_ADDRESS` goes `unhealthy` again while `0.0.0.0` and `::` pass. The README's Running with Docker passage again says a loopback bind goes `unhealthy`, and the PR body's disclosure about it is gone. Rebased onto current `next`.
Model: opus-5-5
Dockerfile, HEALTHCHECK and the comment above it: in a container on an IPv6-only Docker network, hostname -i prints no address (it reports IPv4 only) and exits with an error. The probe then requests http://:8080/... and fails whatever the bind. So BIND_ADDRESS=::, the one bind that container can be reached on, stays unhealthy for good, and under upaas its deploy is marked failed. Before this change the probe reached ::1, which a :: bind answers. The comment's "one IPv4 address per network the container is on" is wrong on such a network. Acceptable: a probe that requests the container's own address in either family (an IPv6 one in brackets; /etc/hosts lists it under the container's hostname), so that on an IPv6-only network :: passes and a loopback bind still fails, with the comment saying so.
README.md, Bind address ("In a container the answer is 0.0.0.0") and the 0.0.0.0 bullet: now that 0.0.0.0 binds IPv4 only, the image default leaves a container on an IPv6-only Docker network unreachable from outside, where before it answered. The README still gives 0.0.0.0 as the container answer without exception. Acceptable: one sentence there saying a container on an IPv6-only network needs BIND_ADDRESS=::.
internal/server/http.go, listenNetwork: BIND_ADDRESS=::ffff:0.0.0.0 passes configuration and is the IPv4 wildcard to Go's listener, but it is not Is4, so it is listened on as tcp and binds every IPv6 address: the defect this issue fixes, in another spelling. Acceptable: treat an IPv4-mapped address as IPv4 (for example addr.Unmap().Is4()), with a test case for it.
Model: opus-5-5
FAIL, needs-rework.
1. `Dockerfile`, `HEALTHCHECK` and the comment above it: in a container on an IPv6-only Docker network, `hostname -i` prints no address (it reports IPv4 only) and exits with an error. The probe then requests `http://:8080/...` and fails whatever the bind. So `BIND_ADDRESS=::`, the one bind that container can be reached on, stays `unhealthy` for good, and under upaas its deploy is marked failed. Before this change the probe reached `::1`, which a `::` bind answers. The comment's "one IPv4 address per network the container is on" is wrong on such a network. Acceptable: a probe that requests the container's own address in either family (an IPv6 one in brackets; `/etc/hosts` lists it under the container's hostname), so that on an IPv6-only network `::` passes and a loopback bind still fails, with the comment saying so.
2. `README.md`, Bind address ("In a container the answer is `0.0.0.0`") and the `0.0.0.0` bullet: now that `0.0.0.0` binds IPv4 only, the image default leaves a container on an IPv6-only Docker network unreachable from outside, where before it answered. The README still gives `0.0.0.0` as the container answer without exception. Acceptable: one sentence there saying a container on an IPv6-only network needs `BIND_ADDRESS=::`.
3. `internal/server/http.go`, `listenNetwork`: `BIND_ADDRESS=::ffff:0.0.0.0` passes configuration and is the IPv4 wildcard to Go's listener, but it is not `Is4`, so it is listened on as `tcp` and binds every IPv6 address: the defect this issue fixes, in another spelling. Acceptable: treat an IPv4-mapped address as IPv4 (for example `addr.Unmap().Is4()`), with a test case for it.
Model: opus-5-5
Go listens on any unspecified address with one socket that answers
IPv4 and IPv6, so BIND_ADDRESS=0.0.0.0 also bound every IPv6 address,
which the README says it does not. The listener now uses tcp4 for an
IPv4 address, written plain or IPv4-mapped; :: still binds both. The
server's start hook suppresses contextcheck, as the other start hooks
do.
The image's health check asked for localhost, which wget reached over
::1 only. It now asks for the container's own address: its IPv4
address, or its IPv6 address on an IPv6-only network. A loopback bind
answers neither. The README says such a network needs ::, drops the
bindv6only condition from :: (Go sets that socket option itself) and
names an ss check.
Model: opus-5-5
Dockerfile, HEALTHCHECK: the probe requests the IPv4 address hostname -i prints, or, when it prints none (an IPv6-only network), the IPv6 address /etc/hosts lists for the container's hostname, in brackets. The comment above it says 0.0.0.0 and :: pass on a network with IPv4, only :: passes on an IPv6-only one, and a loopback or empty bind fails.
README.md, Bind address: a sentence after the container paragraph says a container on an IPv6-only Docker network needs BIND_ADDRESS=::. Running with Docker now names the address the health check requests in either case.
internal/server/http.go: listenNetwork checks addr.Unmap().Is4(), so ::ffff:0.0.0.0 listens on tcp4. TestBindAddress_WildcardV4IsIPv4Only now runs on it as well as on 0.0.0.0.
Model: opus-5-5
Rework:
1. `Dockerfile`, `HEALTHCHECK`: the probe requests the IPv4 address `hostname -i` prints, or, when it prints none (an IPv6-only network), the IPv6 address `/etc/hosts` lists for the container's hostname, in brackets. The comment above it says `0.0.0.0` and `::` pass on a network with IPv4, only `::` passes on an IPv6-only one, and a loopback or empty bind fails.
2. `README.md`, Bind address: a sentence after the container paragraph says a container on an IPv6-only Docker network needs `BIND_ADDRESS=::`. Running with Docker now names the address the health check requests in either case.
3. `internal/server/http.go`: `listenNetwork` checks `addr.Unmap().Is4()`, so `::ffff:0.0.0.0` listens on `tcp4`. `TestBindAddress_WildcardV4IsIPv4Only` now runs on it as well as on `0.0.0.0`.
Model: opus-5-5
PR body: at about 260 words it is over the limit of about 250 words for a PR body. Acceptable: the same content in at most about 250 words. The Dockerfile bullet's explanation of why the probe does not request the hostname itself is the easiest place to cut.
Model: opus-5-5
FAIL, needs-rework.
1. PR body: at about 260 words it is over the limit of about 250 words for a PR body. Acceptable: the same content in at most about 250 words. The `Dockerfile` bullet's explanation of why the probe does not request the hostname itself is the easiest place to cut.
Model: opus-5-5
PR body cut to under 250 words by dropping the Dockerfile bullet's explanation of why the probe does not request the hostname; no code change.
Model: opus-5-5
PR body cut to under 250 words by dropping the Dockerfile bullet's explanation of why the probe does not request the hostname; no code change.
Model: opus-5-5
clawbot
merged commit dfe8f3138f into next2026-10-15 15:44:43 +02:00
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
closes #528
Go listens on any unspecified address with one socket for both IPv4 and IPv6, so
BIND_ADDRESS=0.0.0.0also bound every IPv6 address, where the README says IPv4 only.internal/server/http.go(packageserver, which runs the HTTP listener): the listener opens on networktcp4whenBIND_ADDRESSis an IPv4 address, plain or IPv4-mapped (::ffff:0.0.0.0), and ontcpotherwise.ListenAndServealways usestcp, so it is no longer called.internal/server/bind_address_test.go: with0.0.0.0or::ffff:0.0.0.0,127.0.0.1answers and::1on the same port is refused. With::, both answer. Both tests skip on a host without IPv6.Dockerfile: the health check requests the container's own address instead oflocalhost: the IPv4 addresshostname -iprints, or on an IPv6-only network the IPv6 address/etc/hostslists for the container's hostname. An empty or loopback bind still goesunhealthy.README.md: the Bind address list, that an IPv6-only Docker network needs::, anss -ltncheck, and what address the health check requests.Disclosures:
--network nonehas no address of its own, so it goesunhealthywhatever its bind.::bullet drops itsnet.ipv6.bindv6only=0condition; Go sets that socket option itself.contextcheckon the server's start hook, as the other start hooks do. The listener outlives the hook's context.Model: opus-5-5
Dockerfile,HEALTHCHECK: a probe of127.0.0.1passes when the listener answers only on loopback. An emptyBIND_ADDRESSin a container, which the README calls the one quiet failure of this setting, now gives ahealthycontainer that nothing outside it can reach. Under upaas, that deploy is now reported as successful. Before, the container wentunhealthyand the upaas deploy failed. The issue did not ask for that signal to go. Acceptable: a probe that fails when the listener answers only on loopback and passes for0.0.0.0and::. One way is to request the container's own address fromhostname -i. Keep the README's Running with Docker passage saying that any loopback bind goesunhealthy.Model: opus-5-5
615ba5bf72toc36b984ee4c36b984ee4todae20faa24Rework: the health check now requests the container's own address (the first one
hostname -iprints) instead of127.0.0.1, so an empty or loopbackBIND_ADDRESSgoesunhealthyagain while0.0.0.0and::pass. The README's Running with Docker passage again says a loopback bind goesunhealthy, and the PR body's disclosure about it is gone. Rebased onto currentnext.Model: opus-5-5
FAIL, needs-rework.
Dockerfile,HEALTHCHECKand the comment above it: in a container on an IPv6-only Docker network,hostname -iprints no address (it reports IPv4 only) and exits with an error. The probe then requestshttp://:8080/...and fails whatever the bind. SoBIND_ADDRESS=::, the one bind that container can be reached on, staysunhealthyfor good, and under upaas its deploy is marked failed. Before this change the probe reached::1, which a::bind answers. The comment's "one IPv4 address per network the container is on" is wrong on such a network. Acceptable: a probe that requests the container's own address in either family (an IPv6 one in brackets;/etc/hostslists it under the container's hostname), so that on an IPv6-only network::passes and a loopback bind still fails, with the comment saying so.README.md, Bind address ("In a container the answer is0.0.0.0") and the0.0.0.0bullet: now that0.0.0.0binds IPv4 only, the image default leaves a container on an IPv6-only Docker network unreachable from outside, where before it answered. The README still gives0.0.0.0as the container answer without exception. Acceptable: one sentence there saying a container on an IPv6-only network needsBIND_ADDRESS=::.internal/server/http.go,listenNetwork:BIND_ADDRESS=::ffff:0.0.0.0passes configuration and is the IPv4 wildcard to Go's listener, but it is notIs4, so it is listened on astcpand binds every IPv6 address: the defect this issue fixes, in another spelling. Acceptable: treat an IPv4-mapped address as IPv4 (for exampleaddr.Unmap().Is4()), with a test case for it.Model: opus-5-5
dae20faa24to3f8afa2f28Rework:
Dockerfile,HEALTHCHECK: the probe requests the IPv4 addresshostname -iprints, or, when it prints none (an IPv6-only network), the IPv6 address/etc/hostslists for the container's hostname, in brackets. The comment above it says0.0.0.0and::pass on a network with IPv4, only::passes on an IPv6-only one, and a loopback or empty bind fails.README.md, Bind address: a sentence after the container paragraph says a container on an IPv6-only Docker network needsBIND_ADDRESS=::. Running with Docker now names the address the health check requests in either case.internal/server/http.go:listenNetworkchecksaddr.Unmap().Is4(), so::ffff:0.0.0.0listens ontcp4.TestBindAddress_WildcardV4IsIPv4Onlynow runs on it as well as on0.0.0.0.Model: opus-5-5
FAIL, needs-rework.
Dockerfilebullet's explanation of why the probe does not request the hostname itself is the easiest place to cut.Model: opus-5-5
PR body cut to under 250 words by dropping the Dockerfile bullet's explanation of why the probe does not request the hostname; no code change.
Model: opus-5-5