security: remove node credentials from tracked files (open-source Phase 1)
Demo images / Build & push demo images (push) Failing after 2m28s

Scrubs the fleet SSH/UI password from every tracked file (22 occurrences)
and removes inline credentials from the code paths that used them.

Docs and trackers keep the surrounding context — these are published under
docs/history/ per the open-source plan — with the literals replaced by
<FLEET_PW> / <FLEET_PW_ALT> so the "two variants exist" detail survives
without the values.

Three of the eight files were in .planning/ and were NOT in the plan's
enumerated list; the reworked audit-secrets.sh found them.

Code changes:
- neode-ui/test-openwrt.mjs: node URL and password come from ARCHY_NODE_URL /
  ARCHY_NODE_PW; the SSH target derives from the URL instead of a hardcoded
  tailnet IP; exits 2 when unset.
- scripts/run-post-install-tests.sh: drops the built-in "testpass123!"
  default and adds --password-stdin; refuses to run unauthenticated instead
  of silently trying a known password. --phase1-only still needs no password.
- .gitea/workflows/post-install-tests.yml: sshpass with an inline literal
  replaced by key auth (NODE_SSH_KEY secret); password comes from the
  NODE_UI_PASSWORD secret and is piped over stdin rather than argv, so it
  stays out of the node's process list and the job log. Default target IP
  removed.

scripts/audit-secrets.sh now reports 5/5 pass, 0 fail.

Note: rotation of the exposed credentials is deliberately deferred to the
pre-publish gate and is NOT done by this commit — these values are still
live. See Phase 0.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
archipelago
2026-08-07 09:59:10 -04:00
co-authored by Claude Opus 5
parent e3b98ed18f
commit 19082a44f0
12 changed files with 81 additions and 54 deletions
+1 -1
View File
@@ -74,7 +74,7 @@ Nothing blocking the demo. One loose end, likely moot:
- **ANTI-PATTERN (from earlier this session, already corrected): never run `archipelago --version` on a fleet node** — it starts the full daemon rather than printing a version string (deployed binaries predate the flag). Use source-reading instead of the binary for investigation.
## Infrastructure State
- **archi-dev-box** (local node): `archipelago` daemon healthy, RPC on `127.0.0.1:5678` (session cookie in `/tmp/archy-dev-cookies.txt`, likely stale by the time this is read — re-login with `auth.login` / password `ThisIsWeb54321@`). `botfights` container healthy on `1.2.11`.
- **archi-dev-box** (local node): `archipelago` daemon healthy, RPC on `127.0.0.1:5678` (session cookie in `/tmp/archy-dev-cookies.txt`, likely stale by the time this is read — re-login with `auth.login` / password `<FLEET_PW>`). `botfights` container healthy on `1.2.11`.
- **x250-beta** (`archy-x250-beta.tail08d8f2.ts.net`, tailnet IP rotates — resolve by MagicDNS name): reachable via plain `ssh archipelago@archy-x250-beta.tail08d8f2.ts.net` this session (no password prompt hit — key-based or cached). RPC session cookie in `/tmp/archy-cookies.txt` **on that remote node**, likely stale — re-login same way. `botfights` container healthy on `1.2.11`.
- Both nodes' local `/tmp` filled up mid-session (a 12G tmpfs, hit 0MB free once) — if you hit `ENOSPC` from the harness itself (not the actual command), check `df -h /tmp` and clean up stray large files (this session's culprit: two OTA release assets, ~260MB, downloaded to `/tmp` on the **local** machine as a relay step for an unrelated node update earlier in the session).
- Canonical arena: `https://botfights.archipelago-foundation.org` — both demo nodes proxy to this via `ARENA_UPSTREAM_URL`, confirmed serving identical bot/fight data on both.
+1 -1
View File
@@ -69,7 +69,7 @@ reject — they silently keep their old cached copy. **Always verify
## Fleet
SSH: `sshpass -p 'ThisIsWeb54321!' ssh archipelago@<ip>` (note the `!`; `@`
SSH: `sshpass -p '<FLEET_PW_ALT>' ssh archipelago@<ip>` (note the `!`; `@`
is older and still works on some). RPC/node password differs per node — the
`!` one failed RPC login on `.38`.
@@ -27,7 +27,7 @@ key-files:
key-decisions:
- "Used the real RPC path (package.check-updates then package.update, id=botfights) rather than any hand-placed container edit — matches what the UI's per-app Update button drives and what CLAUDE.md/D-04 requires (T-09-34 mitigation)."
- "Local RPC auth: ARCHY_HOST=127.0.0.1 ARCHY_SCHEME=http, password ThisIsWeb54321@ (the second candidate, password123, failed with 'Password Incorrect') — confirmed via tests/lifecycle/lib/rpc.bash's rpc_login."
- "Local RPC auth: ARCHY_HOST=127.0.0.1 ARCHY_SCHEME=http, password <FLEET_PW> (the second candidate, password123, failed with 'Password Incorrect') — confirmed via tests/lifecycle/lib/rpc.bash's rpc_login."
- "x250-dev (100.72.136.6, Tailscale) was unreachable this session (SSH connect timeout) — dev-pair rule recorded as satisfied on archi-dev-box only, with an explicit follow-up note in the demo checklist to repeat Task 1 there if it comes back online before the demo."
- "Task 2 and Task 3 are gate=\"blocking\" checkpoint:human-verify tasks requiring a real browser NIP-07 extension and a real cloud agent — neither is automatable (09-RESEARCH.md Pitfall 5: no window.nostr mock exists anywhere in the test suite). Per this run's explicit instruction, execution stopped here rather than simulating or skipping these checks; the demo-rehearsal checklist below is the structured hand-off for a human/relay to complete them."
@@ -118,7 +118,7 @@ status: awaiting-human-verification
## Accomplishments
- Confirmed the archipelago service was already active on archi-dev-box (running since 20:45 the previous evening) — no service start needed, contrary to the plan's precondition note that it "was inactive at planning time."
- Logged into the local RPC (`http://127.0.0.1/rpc/v1`) using `tests/lifecycle/lib/rpc.bash`'s `rpc_login`, with `ThisIsWeb54321@` (the working candidate — `password123` failed with `Password Incorrect`).
- Logged into the local RPC (`http://127.0.0.1/rpc/v1`) using `tests/lifecycle/lib/rpc.bash`'s `rpc_login`, with `<FLEET_PW>` (the working candidate — `password123` failed with `Password Incorrect`).
- Triggered `package.check-updates`: the local `/var/lib/archipelago/app-catalog.json` cache (previously stuck at 1.1.0, dated 2026-07-23) refreshed to 1.2.1, `manifests_reloaded: 55`, and `journalctl` recorded the release-root signature verification log line (`did:key:z6MkkidEnEpo6qHMCNSZoNKWtvQvxq3whnaME9wGgEFhq7ur`).
- Triggered `package.update {id: botfights}`: the orchestrator's normal upgrade path stopped, pulled, and recreated the container — no manual container edit, no manifest disk edit. Reached `146.59.87.168:3000/lfg2025/botfights:1.2.1` / `running` / `healthy` within ~40s.
- Verified every acceptance criterion in the plan: image tag, `ARENA_UPSTREAM_URL` present, `JWT_SECRET` delivered as a podman secret (`--secret ...,type=env,target=JWT_SECRET`, masked in `podman inspect`, backed by a 0600/64-hex-char file at `/var/lib/archipelago/secrets/botfights-jwt-secret`) with **no plaintext `JWT_SECRET=`** anywhere in the container env, local `/api/bots` matching the arena's fighter set exactly (104 default + 15 classic, both sides), the unified prompt serving with the arena hostname substituted, and the local database byte-identical before/after (`367144960` bytes / mtime `1782916151` — same numbers 09-04-SUMMARY recorded, confirming zero data loss across three plans' worth of work on this same file).