docs(security): prove the delivery path on hardware; close window 15

Controlled test on archi-dev-box with operator approval. The daemon was
stopped first so the reconciler could not repair the state before the
re-exposure was confirmed — without a confirmed 200, the later 401 would
be consistent with the state never having been broken at all.

  1. stale conf installed + container restarted -> POST /bitcoin-rpc/
     returned 200 with a real block height and Allow-Origin: *
  2. daemon started 20:00:36, nothing else touched
  3. 20:02:19 reconcile rendered the conf and logged the expected warn
     line naming bitcoin-ui/archy-bitcoin-ui, then restarted it
  4. POST -> 401, Allow-Origin origin-scoped
  5. conf byte-identical to the pre-test known-good, container healthy

Both halves are now proven on real hardware: a05956c4's template (the
gate works) and f6b5245b's delivery path (the gate reaches a container
the reconciler had been skipping).

Also records the operator's decision AGAINST credential rotation — no
macaroon, no Bitcoin RPC password — with the trade it accepts stated
plainly, so it is not silently re-litigated later.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
archipelago
2026-08-02 20:09:07 -04:00
co-authored by Claude Opus 5
parent 186a2c36c8
commit e205f2c34a
3 changed files with 58 additions and 14 deletions
+10 -6
View File
@@ -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.
+6 -6
View File
@@ -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"
}
]
````
+42 -2
View File
@@ -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:0020: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 46 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