From e706d446e76900fd9a33ae7008963a9f043a696a Mon Sep 17 00:00:00 2001 From: archipelago Date: Wed, 8 Jul 2026 19:36:27 -0400 Subject: [PATCH] =?UTF-8?q?docs(hardening):=20tick=20=C2=A7B/=C2=A7C/?= =?UTF-8?q?=C2=A7H=20items=20landed=202026-07-08=20+=20two=20new=20finding?= =?UTF-8?q?s?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Secrets→podman-secrets verified on .228; release-path guards e77ccff0; test creds/timeouts 380f4f19; mempool umbrella lifecycle fix 161a6e4d. New open items: manifests hardcoding secrets in plain environment, orphan umbrella container cleanup follow-up. Co-Authored-By: Claude Fable 5 --- docs/1.8.0-RELEASE-HARDENING-PLAN.md | 55 +++++++++++++++++++--------- 1 file changed, 38 insertions(+), 17 deletions(-) diff --git a/docs/1.8.0-RELEASE-HARDENING-PLAN.md b/docs/1.8.0-RELEASE-HARDENING-PLAN.md index 13674a18..2b13a819 100644 --- a/docs/1.8.0-RELEASE-HARDENING-PLAN.md +++ b/docs/1.8.0-RELEASE-HARDENING-PLAN.md @@ -107,13 +107,14 @@ end-to-end on real hardware. No test installs the previous version, points it at a staged 1.8.0 manifest, applies, and asserts health + rollback. This is the top release risk for an OTA release. A two-VM (or two-node) harness is enough. -- [ ] 🟡 **Guard the frontend-build-no-op in the *actual* release path.** The - `ui-dist-version` grep guard (`tests/release/run.sh:82`) is behind `--with-build`, which - `scripts/create-release.sh:90` never passes → a stale frontend can ship with a valid - sha256. Call `run.sh --with-build --manifest` from create-release (or fold the grep in). -- [ ] 🟢 **publish-release-assets verifies size, not sha256** (`publish-release-assets.sh:97`). - Add a HEAD/GET sha256 compare so a size-correct/content-wrong mirror asset fails the - publish gate. +- [x] 🟡 **Guard the frontend-build-no-op in the *actual* release path.** DONE 2026-07-08 + (`e77ccff0`): the grep guard is folded directly into `create-release.sh` right after its + own `npm run build` (post-version-bump, pre-packaging — calling `run.sh --with-build` at + stage 0 would have checked the pre-bump version). Verified both ways against the real + dist: passes on the current version, trips on a missing one. +- [x] 🟢 **publish-release-assets verifies size, not sha256.** DONE 2026-07-08 (`e77ccff0`): + the verify loop now downloads each published asset and compares sha256 (and size) + against the manifest. Verified live against the v1.7.99-alpha assets on the vps2 mirror. --- @@ -149,8 +150,11 @@ modules; production request/boot paths are essentially panic-free. The real risk public; zmq 28332/28333 should get the same look — unauthenticated). ⚠ May break external wallets pointed straight at nodeIP:8332 — needs a user call + on-node gate re-run, so NOT changed drive-by from the dev box. -- [x] 🟡 **Move secret env out of plaintext channels → podman secrets.** DONE 2026-07-05 - (code + unit tests; needs the on-node gate re-run before it counts as verified): +- [x] 🟡 **Move secret env out of plaintext channels → podman secrets.** DONE 2026-07-05, + **VERIFIED ON .228 2026-07-08**: recreate wave settled (only fedimint-gateway lagged — + restart-sensitive, converged via a controlled quadlet restart, healthy, 0 plaintext + password vars in inspect); btcpay inspect carries no secret values; 17 quadlet unit + files use `Secret=` lines. Original notes: secret env no longer merges into `environment` — it would land in `podman inspect` AND as plaintext `Environment=` lines in Quadlet unit files on disk (the worse leak). New pipeline: `expand_and_partition_env` taints plain entries that interpolate @@ -185,6 +189,19 @@ modules; production request/boot paths are essentially panic-free. The real risk runtime config. - [ ] 🟢 **Apply the seccomp/apparmor profile** — `security/src/container_policies.rs:71` is a TODO; the profile is defined but never applied to podman. +- [ ] 🟡 **Manifests that hardcode secrets in plain `environment:` bypass the whole secret + pipeline** (found during the 2026-07-08 .228 leak check): indeedhub-api/-ffmpeg ship + `AES_MASTER_SECRET=0123456789abcdef…` and photoprism `PHOTOPRISM_ADMIN_PASSWORD=archipelago` + as literal plaintext in quadlet unit files; grafana's `GF_SECURITY_ADMIN_PASSWORD=$${…}` + reference never expands. Fix = declare them as `generated_secrets`/`secret_env` in the + manifests — which means catalog regen + re-sign + republish (catalog overlay supremacy). +- [x] 🟠 **Legacy `mempool` umbrella id destroys the split stack on stop→start (quadlet).** + FOUND by the first quadlet-mode gate run on .228 (2026-07-08), FIXED `161a6e4d`: + orchestrator start/stop/restart now alias `mempool` → the split members whenever the + umbrella manifest was dropped (same alias install already used); podman-5 + `no such object` phrasing added to all 3 `is_missing_container_error` classifiers. + Follow-up (open): reconciler-side cleanup of an orphan umbrella `mempool` container + when the split stack owns the frontend, so the stale-tile state can't arise at all. --- @@ -312,10 +329,13 @@ media (latest artifact only one minor behind). - [ ] 🟡 **Make the release gate run the full Rust suite** (or hard-require a green CI sha). `tests/release/run.sh:101` runs only a 6-module slice because the full 1000-test suite hangs PTYs on the dev box → 994 tests unverified at release time if CI is stale. -- [ ] 🟡 **Add `--max-time` to `node_rpc()`** (`tests/multinode/lib/multinode.bash`) — a slow - server-side RPC hangs the whole multinode suite with no feedback. -- [ ] 🟢 **De-hardcode creds/IPs in tests** (`tests/multinode/smoke.sh:32`, - `remote-lifecycle.sh:136`); snapshot/restore node baseline between destructive iterations +- [x] 🟡 **Add `--max-time` to `node_rpc()`.** DONE 2026-07-08 (`380f4f19`): login + rpc get + `--connect-timeout 10 --max-time 120` (override `MULTINODE_RPC_TIMEOUT`). Verified live: + .116 login/rpc OK; an unroutable node fails in 10s instead of hanging. +- [x] 🟢 **De-hardcode creds in tests.** DONE 2026-07-08 (`380f4f19`): multinode suites no + longer commit node passwords — `*_PW` env required, auto-loaded from git-ignored + `tests/multinode/.env` (`.env.example` documents the shape). Still open from this + bullet: snapshot/restore node baseline between destructive iterations (teardown currently only clears `/tmp` session files). --- @@ -354,7 +374,8 @@ media (latest artifact only one minor behind). 6. **Before public GA only (NOT alpha/beta):** remove + rotate the Anthropic key (§F) — intentionally left in for frictionless AI during alpha/beta. -*Last updated: 2026-07-02 PM (hardening session 1: §A anchor+catalog `1977bdef`, §A -manifest signature `51647b21`, §D origin checks `206d5fe8`, §C swallowed writes + -§I tombstone fix — this commit). Update this line + tick boxes with commit shas as -items land.* +*Last updated: 2026-07-08 PM (hardening session 3: §C secrets verified on .228, +§B release-path guards `e77ccff0`, §H test creds/timeouts `380f4f19`, mempool +umbrella lifecycle fix `161a6e4d` from the first quadlet-mode gate run; new +finding: manifests hardcoding secrets in plain `environment:`). Update this +line + tick boxes with commit shas as items land.*