diff --git a/.planning/STATE.md b/.planning/STATE.md index 0e58155e..8bcd712a 100644 --- a/.planning/STATE.md +++ b/.planning/STATE.md @@ -170,13 +170,17 @@ Stopped at: Phase 10 complete; a05956c4 verified on archi-dev-box, delivery gap Resume file: docs/security/BITCOIN-RPC-PROXY-EXPOSURE.md Open on this thread (all recorded as broken windows, none blocking): -- Window 15: f6b5245b's reconcile path is deployed but NEVER EXERCISED — an unrelated - bitcoin-ui rebuild cleared the stale state before the fix landed. Needs a node whose - bitcoin-ui conf is still stale, or a deliberately re-staled one. +- Window 15 CLOSED 2026-08-02 20:02 — f6b5245b's reconcile path proven on archi-dev-box by + a controlled test: stale conf installed + container restarted (probe 200, genuinely + re-exposed), daemon started, reconcile repaired it unaided at 20:02:19 with the expected + warn line, probe 401, conf byte-identical to the known-good. Both halves now proven on + hardware. - Windows 11/12: host-secret rotation on three fleet nodes sharing SSH host keys — detect-only so far; rotation is USER-GATED and deliberately not actioned. -- LND macaroon + Bitcoin RPC password rotation: scripts/ordering understood, NOT run on - any node per explicit user instruction 2026-08-02. A general "rotate credentials later" - path is wanted and unbuilt. +- Credential rotation DECIDED AGAINST 2026-08-02 (operator): no LND macaroon rotation, no + Bitcoin RPC password rotation — no evidence of exploitation and the vulnerability is + being closed rather than lived with. rotate-lnd-macaroon.sh stays as a tool, exercised in + detect mode only, never run against a node. Do not re-litigate; see + docs/security/BITCOIN-RPC-PROXY-EXPOSURE.md. - archy-x250-dev has been offline 2 days, so every "dev pair" verification this session reached one node, not two. diff --git a/.planning/WINDOWS.md b/.planning/WINDOWS.md index 4ec34e04..553df6af 100644 --- a/.planning/WINDOWS.md +++ b/.planning/WINDOWS.md @@ -1,10 +1,10 @@ --- schema_version: 1 -open_count: 12 +open_count: 11 waived_count: 0 -fixed_count: 3 +fixed_count: 4 total_count: 15 -last_updated: 2026-08-02T23:16:04.510Z +last_updated: 2026-08-03T00:06:03.112Z --- # Broken Windows Ledger @@ -29,7 +29,7 @@ last_updated: 2026-08-02T23:16:04.510Z | 12 | 10 | unrun-verify | scripts/security/host-secrets-audit.sh | | Rotation never exercised on real hardware: that 'systemctl reload ssh' keeps the operator's own forked session alive is proven only by design, not by observation. Needs --apply --yes on one disposable node from a session the operator is willing to lose. | open | | 2026-08-02T19:07:40.217Z | | | 13 | 10 | unrun-verify | core/archipelago/src/api/rpc/system/handlers.rs | | system.stats host_secrets never observed on a real node — proven against the file contract in unit tests only. Needs a build carrying 10-04 deployed to the dev pair, then a system.stats call. | fixed | | 2026-08-02T19:07:40.522Z | 2026-08-02T23:00:30.894Z | | 14 | 10 | unrun-verify | core/archipelago/src/container/prod_orchestrator.rs | | LIVE EXPOSURE on archi-dev-box: archy-bitcoin-ui (systemd/Quadlet-owned, user-uninstalled marker set) still serves unauthenticated POST /bitcoin-rpc/ on 0.0.0.0:8334 with Access-Control-Allow-Origin *, reaching Bitcoin Core RPC through a credential-injecting proxy. Verified live 2026-08-02 (returned a real block height with no cookies). Code fix committed f6b5245b but NOT deployed: closing it needs the new binary on the node plus an archy-bitcoin-ui restart. archy-electrs-ui is in the same uninstalled-but-running state (static UI only, no credential proxy). Operator-gated; no node touched. | fixed | | 2026-08-02T22:44:15.215Z | 2026-08-02T23:16:04.071Z | -| 15 | 10 | unrun-verify | core/archipelago/src/container/prod_orchestrator.rs | | The f6b5245b reconcile fix is DEPLOYED on archi-dev-box (binary installed 19:06, running) but NEVER EXERCISED on hardware: the state it repairs (uninstall marker + Quadlet-running + stale config) stopped existing here at 18:36, when a separate rebuild of bitcoin-ui rendered the fixed conf and restarted the container. So :8334 returning 401 proves a05956c4's template, NOT the reconcile path that is supposed to deliver it. archy-electrs-ui still carries the marker+running shape and could exercise it, but has no rendered config to rewrite. Needs a node that still has a stale bitcoin-ui conf, or a deliberately re-staled one. | open | | 2026-08-02T23:16:04.510Z | | +| 15 | 10 | unrun-verify | core/archipelago/src/container/prod_orchestrator.rs | | The f6b5245b reconcile fix is DEPLOYED on archi-dev-box (binary installed 19:06, running) but NEVER EXERCISED on hardware: the state it repairs (uninstall marker + Quadlet-running + stale config) stopped existing here at 18:36, when a separate rebuild of bitcoin-ui rendered the fixed conf and restarted the container. So :8334 returning 401 proves a05956c4's template, NOT the reconcile path that is supposed to deliver it. archy-electrs-ui still carries the marker+running shape and could exercise it, but has no rendered config to rewrite. Needs a node that still has a stale bitcoin-ui conf, or a deliberately re-staled one. | fixed | | 2026-08-02T23:16:04.510Z | 2026-08-03T00:06:03.112Z | ````json [ @@ -208,10 +208,10 @@ last_updated: 2026-08-02T23:16:04.510Z "file": "core/archipelago/src/container/prod_orchestrator.rs", "line": null, "description": "The f6b5245b reconcile fix is DEPLOYED on archi-dev-box (binary installed 19:06, running) but NEVER EXERCISED on hardware: the state it repairs (uninstall marker + Quadlet-running + stale config) stopped existing here at 18:36, when a separate rebuild of bitcoin-ui rendered the fixed conf and restarted the container. So :8334 returning 401 proves a05956c4's template, NOT the reconcile path that is supposed to deliver it. archy-electrs-ui still carries the marker+running shape and could exercise it, but has no rendered config to rewrite. Needs a node that still has a stale bitcoin-ui conf, or a deliberately re-staled one.", - "status": "open", + "status": "fixed", "reason": "", "recorded_at": "2026-08-02T23:16:04.510Z", - "resolved_at": null + "resolved_at": "2026-08-03T00:06:03.112Z" } ] ```` diff --git a/docs/security/BITCOIN-RPC-PROXY-EXPOSURE.md b/docs/security/BITCOIN-RPC-PROXY-EXPOSURE.md index 3bb071cd..f3ef0ef8 100644 --- a/docs/security/BITCOIN-RPC-PROXY-EXPOSURE.md +++ b/docs/security/BITCOIN-RPC-PROXY-EXPOSURE.md @@ -100,8 +100,48 @@ rounded off: `bitcoin-ui` is *still* in the node's `user-uninstalled` marker, me next time its config needs to change, this node depends on `f6b5245b` — untested — or on someone happening to rebuild the app again. -Tracked as broken window 15. Closing it needs a node whose `bitcoin-ui` conf is still -stale, or a deliberately re-staled one. +Tracked as broken window 15 — **since closed by the controlled test below.** + +## Proving the delivery path on real hardware + +Run on archi-dev-box, 2026-08-02 20:00–20:03 EDT, with operator approval. The point was to +prove the thing the incidental rebuild had made unprovable: that **reconcile itself** +repairs this state, unaided. + +The daemon was stopped first, so the reconciler could not repair the state before the +re-exposure had been confirmed — otherwise a passing probe would prove nothing about +which mechanism produced it. + +| Step | Action | Observed | +|---|---|---| +| 1 | Install a faithfully stale conf (no `auth_request`, credential-injecting `proxy_pass`, `Allow-Origin: *`) and restart the container | — | +| 2 | Probe with no cookies | `POST /bitcoin-rpc/` → **`200`**, `{"result":960790}`; `Allow-Origin: *`. **Genuinely re-exposed** | +| 3 | Start the daemon (20:00:36) and touch nothing further | — | +| 4 | Reconcile pass at **20:02:19** | `bitcoin_ui: nginx.conf rendered auth_hash=51f2b5af`, then `WARN prod_orchestrator: rewrote config for a user-uninstalled app whose container is still RUNNING (systemd/Quadlet keeps it alive independently of reconcile) — restarting so it picks the new config up app_id=bitcoin-ui container=archy-bitcoin-ui` | +| 5 | Probe again | `POST /bitcoin-rpc/` → **`401`**; `Allow-Origin: http://192.168.63.240:8334` | +| 6 | Compare state | Conf **byte-identical** to the pre-test known-good; container healthy | + +Step 2 is what makes steps 4–6 mean anything: without a confirmed `200`, the later `401` +would be consistent with the state never having been broken at all. + +Both halves are now proven on hardware: `a05956c4`'s template (the gate works) and +`f6b5245b`'s delivery path (the gate arrives at a container the reconciler had been +skipping). + +## Credential rotation — decided against, 2026-08-02 + +The operator's call, recorded here so it is not silently re-litigated: **no LND macaroon +rotation, and no Bitcoin RPC password rotation.** The reasoning was that there is no +evidence of exploitation and the vulnerability is being closed rather than lived with. + +`scripts/security/rotate-lnd-macaroon.sh` stays in the tree as a tool. It has been +exercised in detect mode only, and has never rotated anything on any node. Its ordering +guard (refuses to rotate on a binary lacking the fix) remains the right shape for whenever +rotation is wanted — including for the Bitcoin RPC password, which has no equivalent tool +yet. + +What this decision accepts: any macaroon or RPC password read through either hole before +it was closed stays valid. That is a deliberate, informed trade, not an oversight. ## Operator note