fix(deploy): the tunnel's door binds loopback only (P9a)

Review finding: `--http` parsed whatever address it was given and bound
it. The door is the plaintext MCP service with a static bearer token;
plan 3.9 and the P9a model put a TLS proxy or tunnel in front of a
loopback listener, and no decision authorises exposing the books
unencrypted. `--http 0.0.0.0:7411` would have done exactly that.

`local::tunnel_address` now accepts only a loopback address
(127.0.0.0/8 or ::1; `127.0.0.1:7411` stays the default) and refuses
anything else with a message naming loopback and why. `server` refuses
while parsing, before it takes the lock or binds a socket; `serve`
refuses before it starts a host, so nothing is left half-open. The
bind-failure behaviour for a loopback address is unchanged (said, not
fatal, 3.9), as is the unit's hardening.

The test `the_tunnel_door_binds_loopback_only` refuses 0.0.0.0, a public
sample address and [::] through `server`, checks no socket or host was
left behind, refuses through `serve`, then starts a loopback listener and
reaches `books.start` with the bearer.

No accounting code, report or figure changes.
This commit is contained in:
swaits committed 2026-10-27 09:35:36 -06:00
1 parent 47951e5734
commit c8541ea9fc
7 files changed
+156 -29

No files matched your search

+16 -8
View File
@@ -2993,6 +2993,11 @@ loopback door (`--http`) for Caddy or a tunnel.
already running keeps the flags it was started with (documented in
`deploy/README.md` and the opener's comment).
- **`crates/ledger-cli/tests/two_user.rs`:**
`the_tunnel_door_binds_loopback_only` (a network address — `0.0.0.0`,
a public sample address, `[::]` — is refused by `server` with a message
naming loopback, before any socket or lock exists, and by `serve` before
a host starts; a loopback address still starts and answers with the
bearer);
`isolation_label_is_enforced_under_the_unit` (a server with no flag says
`same_user (advisory)`; with `--isolation enforced --http` it says
`service_user (enforced)` over the Unix socket and over the loopback
@@ -3034,11 +3039,14 @@ loopback door (`--http`) for Caddy or a tunnel.
would report the advisory truth on the enforced machine);
`Isolation::label`'s two strings; `start_out`'s `self.isolation.label()`
(it replaced a literal, and a mutant that restores a literal must fail the
two-door test); the `--http` arm's `args.peek()` guard and the default
`127.0.0.1:7411` (binding the wrong interface, or consuming the next flag,
must fail); `tunnel.is_some()` and the bind failure path (ready must still
come after the try, and a failed bind must not be fatal); `open_with`'s
`isolation` into `Served` and `BooksServer::new`'s copy (a dropped field
must fail `isolation_label_is_enforced_under_the_unit`); the host's
`server_flags` extension and `parse_args`' passthrough of `--isolation`
and its optional `--http` value.
two-door test); `local::tunnel_address`'s `is_loopback` test (a mutant
that accepts any address must fail `the_tunnel_door_binds_loopback_only`,
and one that rejects loopback must fail its second half); the `--http`
arm's `args.peek()` guard and the default `127.0.0.1:7411` (binding the
wrong interface, or consuming the next flag, must fail); `tunnel.is_some()`
and the bind failure path (ready must still come after the try, and a
failed bind must not be fatal); `open_with`'s `isolation` into `Served`
and `BooksServer::new`'s copy (a dropped field must fail
`isolation_label_is_enforced_under_the_unit`); the host's `server_flags`
extension and `parse_args`' passthrough of `--isolation` and its optional
`--http` value.