CRITICAL. Two app-UI ports handed unauthenticated callers full control of
the node's money. Both verified live on archi-dev-box 2026-08-02 over the
fips0 mesh ULA with no cookies.
GET /lnd-connect-info returned 200 with the LND ADMIN MACAROON, the TLS
cert, the gRPC/REST ports and the node's onion address — a complete remote
wallet-drain package, and the onion means an attacker keeps that ability
after losing network access. POST /bitcoin-rpc/ reached Bitcoin Core RPC
with credentials the proxy injected on the caller's behalf, with a wallet
loaded, so wallet methods were reachable too.
Both were reachable because ports 18083 (lnd-ui) and 8334 (bitcoin-ui)
bind 0.0.0.0 AND sit on the fips0 mesh allowlist in fips/app_ports.rs. Any
mesh peer, LAN host or Tailscale peer could take either path.
The root cause is one mistaken idea in two places: that a check performed
by a reverse proxy is an auth check. It is not — it only holds for traffic
that arrived through that proxy. /lnd-connect-info's comment said "nginx
validates session cookie (presence check), backend is bound to 127.0.0.1
so only nginx can reach it". Both clauses were false in production: the
lnd-ui container runs its OWN nginx on :18083 that proxies straight to the
backend forwarding whatever cookies arrived, including none, and that
second front door never performed the check the premise named.
So authorisation moves to the resource:
- /lnd-connect-info now requires a session, like /proxy/lnd/ beside it.
The 401 carries CORS headers so the wallet UI shows a readable error
rather than an opaque CORS failure.
- New GET /auth/session-check returns 204/401 and nothing else, giving
container nginx an auth_request gate it can actually use.
- bitcoin-ui's /bitcoin-rpc/ is gated by that auth_request. Its
`Access-Control-Allow-Origin *` is also gone: on a proxy that injects
credentials, it let any page a user visited drive the node's RPC.
Preflight is answered before the gate, since OPTIONS carries no cookies.
The nginx template is include_str!'d and re-rendered on every reconcile
pass, so this ships atomically with the binary.
Operators must treat the LND admin macaroon and the Bitcoin RPC password
on every affected node as compromised and rotate them AFTER this is
deployed — rotating first just re-leaks through the same hole.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>