core: a login at the gateway, and container ports bound to one address

For services with no authentication of their own -- a trace viewer, an admin
UI, an OTLP receiver -- which must not be open to the LAN just because they run
here.

- `expose.http.basic_auth: {user, password_hash}` has the gateway ask for HTTP
  Basic auth (Caddy's `basic_auth`, bcrypt from `caddy hash-password`) before
  proxying, on the internal name and any public one. It is recorded in the
  registry too, so a Caddyfile generated from the registry alone still asks
  rather than serving the route open. Checked against caddy 2.11: 401 without
  credentials, through with them.
- A container's `ports` may give the host side an address, `4318:
  127.0.0.1:4318` -> `-p 127.0.0.1:4318:4318`. A bare port still publishes on
  every interface, as before, and the docs now say that this means the LAN.
This commit is contained in:
2026-10-22 10:13:44 -07:00
parent 517471605a
commit 01819b2984
6 changed files with 280 additions and 20 deletions

View File

@@ -294,6 +294,19 @@ run:
program: my-service # name in [project.scripts]
```
A `container`'s `ports` map container port → host port, published with docker's
`-p`. A bare host port publishes on **every** interface — so it is on the LAN,
whatever `reach:` says. To keep one to this machine, give the host side an
address:
```yaml
run:
launcher: container
image: litellm:main
ports:
4000: 127.0.0.1:4000 # loopback only; `4000: 4000` would be LAN-wide
```
A `compose` launcher supervises a **whole multi-container stack as one systemd
unit** — `ExecStart` runs `docker compose … up` attached (`Type=simple`) and a
generated `ExecStop` runs `… down` so networks/anonymous volumes are reclaimed on
@@ -339,6 +352,20 @@ expose:
health_path: /health # Used by health polling
```
A service with no login of its own can have the gateway ask for one. HTTP Basic
auth, applied wherever the gateway routes it (internal and public names); the
hash is bcrypt, from `caddy hash-password`, so the manifest never holds the
password itself:
```yaml
expose:
http:
internal: { port: 16686 }
basic_auth:
user: paul
password_hash: $2a$14$... # caddy hash-password
```
### `proxy` — Expose the service at a subdomain
`proxy` is a **checkbox** (a bool): `true` means the gateway routes