fix(api,auth)!: bind loopback by default, and let a container ask for more

Both servers bound `0.0.0.0`. That was harmless while the only deployment was
docker-compose.yml, which publishes these ports on 127.0.0.1 and makes the
container's own bind irrelevant. As ordinary processes on a rented machine
there is no such wrapper, and `0.0.0.0` is a listener on the internet — in
front of an issuer that sets cookies without `Secure` and mints sign-in codes,
because it expects something else to be terminating TLS.

So the default is `127.0.0.1` and `HOST` is there for the deployment that
genuinely needs every interface. compose now sets `HOST: 0.0.0.0` explicitly,
which is not a workaround: inside a container, binding loopback is what would
make the published port unreachable. The right answer differs by deployment,
which is why it is a variable rather than a constant.

Both now log the address they bound, not the one they hoped for.

BREAKING CHANGE: api and auth no longer listen on every interface by default.
A deployment that relied on that must set HOST=0.0.0.0.
This commit is contained in:
Wanjohi
2026-09-16 23:10:22 +03:00
parent e3c9416170
commit b00f1064ae
3 changed files with 46 additions and 4 deletions

View File

@@ -74,6 +74,12 @@ services:
# unconfigured and this unset, the issuer refuses to send rather than
# logging codes, which is the failure a self-hoster should get.
EMAIL_DEV_LOG: ${EMAIL_DEV_LOG:-}
# Every interface *inside the container*, which is what makes the
# loopback publication below reachable. The processes default to
# 127.0.0.1 because a bare process on a host has no such wrapper and a
# public bind there is a listener on the internet; a container's own
# loopback is not, and binding it would make the port unpublishable.
HOST: 0.0.0.0
# Loopback. This listener speaks plain HTTP and sets no `Secure` on the
# cookies it issues, because it expects to be behind something that
# terminates TLS. Published on every interface it would be a way to reach
@@ -101,6 +107,12 @@ services:
# A shared secret that turns any request carrying it into an operator.
# Required, with no default, for that reason.
ADMIN_SHARED_SECRET: ${ADMIN_SHARED_SECRET:?set ADMIN_SHARED_SECRET in .env to a value you generated}
# Every interface *inside the container*, which is what makes the
# loopback publication below reachable. The processes default to
# 127.0.0.1 because a bare process on a host has no such wrapper and a
# public bind there is a listener on the internet; a container's own
# loopback is not, and binding it would make the port unpublishable.
HOST: 0.0.0.0
# Loopback, for the same reason as the issuer above.
ports:
- '127.0.0.1:3000:3000'