review(redfix): wake #53 — cold re-verify on closed phase; DONE + no-VETO reaffirmed; A-redfix-1 'sole copy' FALSIFIED (78 world-readable cred copies, root-caused to /root/.gitconfig insteadOf; dashboard invariant HOLDS, operator-scope widened, no gate impact)
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0123qkYW7yhbqntjcjVvTUQ2
This commit is contained in:
@ -754,3 +754,47 @@ token file with `0600`, or at minimum `chmod 0600 /etc/cc-ci/.git/config`. Do no
|
||||
**Rotation blast radius is small** (checked, so this is not a reason to delay): `GITEA_PASSWORD` is consumed
|
||||
only by `scripts/bootstrap-drone-oauth.sh` (a one-off bootstrap). `scripts/recipe-mirror-sync.sh` pushes with
|
||||
an **OAuth token**, not the password, so the weekly sweep's mirror sync does **not** depend on it.
|
||||
|
||||
### A-redfix-1 — ADDENDUM (wake #53, 2026-07-09T10:15Z): the "sole copy" claim is FALSIFIED — **78** world-readable copies, root-caused to a git `insteadOf` rewrite that regenerates them on every CI run
|
||||
|
||||
The original finding asserted (see above, line ~47): "`/etc/cc-ci` is the **sole** copy whose parents are
|
||||
world-traversable." I disbelieved that universal and probed it cold on cc-ci. **It is false.** Enumerating
|
||||
every cred-bearing `.git/config` and testing each for uid-1000 readability (extract-then-hash, never echo):
|
||||
|
||||
ssh cc-ci 'find / -xdev -name config -path "*/.git/*" 2>/dev/null | while read f; do
|
||||
grep -q "autonomic-bot:" "$f" || continue
|
||||
setpriv --reuid=1000 --regid=1000 --clear-groups cat "$f" >/dev/null 2>&1 && echo "$f"; done'
|
||||
# → 78 world-readable copies: 68 under /var/lib/cc-ci-runs/*/abra/recipes/*/.git/config,
|
||||
# 8 in /nix/store/*-source/.git/config (0444, read-only fs), /tmp/v/.git/config, /etc/cc-ci/.git/config
|
||||
|
||||
All 78 carry the **same** live credential — `sha256(pw)[:16] = 3fcea78925015fc9`, identical to the B-redfix-8
|
||||
sentinel (empty-input control `e3b0c44298fc1c14` to prove the extractor fires). So the exposure is **78×**, not
|
||||
1×, and `/etc/cc-ci` is not special.
|
||||
|
||||
**Root cause (grep the callee, not the value).** `run_recipe_ci.py:360` clones a **clean, credential-less**
|
||||
URL (`https://git.autonomic.zone/{src}.git`). The userinfo is injected by a global git rewrite in
|
||||
`/root/.gitconfig`:
|
||||
|
||||
url.https://autonomic-bot:<pw>@git.autonomic.zone/.insteadOf = https://git.autonomic.zone/
|
||||
|
||||
Every per-run recipe clone the harness makes therefore bakes the cleartext password into its `.git/config` at
|
||||
mode **0644**, under `/var/lib/cc-ci-runs/<rid>/abra/recipes/<recipe>/` (world-traversable parents). This is
|
||||
the **generator**: the 68 run-dir copies accrete one-per-recipe-per-run and **regenerate autonomously on the
|
||||
next CI run** — not just on a manual re-clone as the prior addendum supposed.
|
||||
|
||||
**Consequence for the remedy.** The prior recommendation (`chmod 0600 /etc/cc-ci/.git/config`, or delete
|
||||
`/etc/cc-ci`) fixes **1 of 78** and does nothing about regeneration. A durable operator fix must remove the
|
||||
credential from git's URL layer, e.g.:
|
||||
- replace the `insteadOf`-with-userinfo rewrite in `/root/.gitconfig` with a `credential.helper` (token in a
|
||||
`0600` file), so harness clones carry **no** userinfo; and
|
||||
- scrub the existing run-dir + `/etc/cc-ci` + `/tmp/v` copies (the `/nix/store` ones are 0444 on a read-only
|
||||
store and clear on GC).
|
||||
|
||||
Otherwise the next `!testme`/nightly-sweep run re-exposes the (even freshly-rotated) value at 0644.
|
||||
|
||||
**Scope note — NOT a gate, NOT a VETO, DONE stands.** The dashboard "no secrets" invariant was independently
|
||||
**re-verified holding** this wake: `/runs/<rid>/abra/recipes/<recipe>/.git/config` → **404** on the live
|
||||
dashboard (blocked by the `len(parts)==2` guard + the `_RUN_FILES` allow-list; positive control
|
||||
`/runs/985/results.json` → 200, `has_cred=False`). This exposure is **host-local filesystem only** (any local
|
||||
uid, incl. containers sharing the host mount), which is squarely the operator-only A-redfix-1/B-redfix-8
|
||||
territory. It **widens** their remediation scope; it does not reopen any D-gate or DoD item.
|
||||
|
||||
Reference in New Issue
Block a user