status(redfix): fold A-redfix-1 into the B-redfix-8 operator remedy — one credential, two exposures

Adversary wake #45 established A-redfix-1 and B-redfix-8 are the same credential. Re-derived
every claim first-hand before writing it (the A-redfix-2 lesson), incl. value identity:
.git/config userinfo and .testenv GITEA_PASSWORD both hash to 3fcea78925015fc9; control
sha256("")=e3b0c44298fc1c14 rules out the empty-input probe artifact.

Rotation does NOT scrub /etc/cc-ci/.git/config (644 root:root, manual clone per
configuration.nix:7, not nix-generated). Remedy is now a 3-step operator procedure that
strips the userinfo rather than re-embedding the rotated password (re-embedding would
recreate the exposure). Verified the strip is safe: anonymous ls-remote succeeds rc=0.

Blast radius recorded: GITEA_PASSWORD is consumed only by bootstrap-drone-oauth.sh;
recipe-mirror-sync.sh uses an OAuth token. No reason to delay rotation.

STATUS text only. No code. ## DONE stands; M1+M2 PASS stand; no gate reopens.
This commit is contained in:
2026-07-09 09:05:26 +00:00
parent cf807321e1
commit 658f9c40b6
2 changed files with 77 additions and 5 deletions

View File

@ -20,8 +20,13 @@ there is **no standing VETO**:
Adversary findings F-redfix-1/2/3/4 are all **CLOSED**; no open blocking finding.
The four `A-redfix-*` findings are also non-blocking and are accounted for here, so nothing dangles:
- **A-redfix-1** (`/etc/cc-ci/.git/config` is 0644 and embeds the Gitea bot password) — out of redfix scope,
pre-existing undeclared server state, no DoD item. Mirrors my B-redfix-7. Not created by this phase.
- **A-redfix-1** (`/etc/cc-ci/.git/config` is 0644 and embeds the Gitea bot password) — **OPEN**, out of redfix
scope, pre-existing undeclared server state, no DoD item. Mirrors my B-redfix-7. Not created by this phase.
**It is the SAME credential as B-redfix-8, not merely a similar one** — verified first-hand 2026-07-09T09:1xZ:
the password extracted from the `.git/config` remote URL and the `GITEA_PASSWORD` in the orchestrator's
`/srv/cc-ci/.testenv` both hash to `3fcea78925015fc9` (neither is the empty-string artifact
`e3b0c44298fc1c14`). **Two exposures, one credential, still unrotated.** Folded into the B-redfix-8 remedy
below, because rotating the password does **not** scrub this file.
- **A-redfix-2** (the B-redfix-5 wedge's self-heal mechanism) — **PARTIALLY WITHDRAWN by the Adversary at
wake #44.** Its first half stands (a code path *does* redeploy keycloak — the fresh-deploy branch). Its
second half ("no re-trigger; heals on reboot") is **false** and is superseded by A-redfix-4.
@ -81,11 +86,44 @@ digest without any rotation, and must NOT be used to close this item:
Exposure confirmed at **value level** (Adversary, 2026-07-09T07:34Z): the currently-valid, push-capable
password appears **verbatim** in the publicly served body, not merely "a blob that once held a secret."
**REMEDY (operator).** Rotate the Gitea bot password, then update `/srv/cc-ci/.testenv`. The credential is a
Class-A1 external infra input (plan §4.4) — the Builder must not rotate or invent it. History rewrite is not
available to the Builder either (`--force` is forbidden by the standing rules), so rotation is the only
**REMEDY (operator).** Rotate the Gitea bot password, then update **both** places that hold it. The credential
is a Class-A1 external infra input (plan §4.4) — the Builder must not rotate or invent it. History rewrite is
not available to the Builder either (`--force` is forbidden by the standing rules), so rotation is the only
action that actually revokes the exposure.
1. Rotate the bot password in Gitea.
2. Update `GITEA_PASSWORD` in `/srv/cc-ci/.testenv` **on the orchestrator**.
3. **Rewrite `/etc/cc-ci/.git/config` on `cc-ci` (A-redfix-1) — rotation does NOT scrub it.** That file is
`644 root:root` and embeds the same password in the `origin` URL's userinfo. `/etc/cc-ci` is a **manual
`git clone`**, not nix-generated (`nix/hosts/cc-ci-hetzner/configuration.nix:7` documents the clone), so no
`nixos-rebuild` regenerates or scrubs it. Strip the userinfo from the URL rather than re-embedding the new
password — re-embedding recreates the exposure verbatim:
ssh cc-ci 'git -C /etc/cc-ci remote set-url origin \
https://git.autonomic.zone/recipe-maintainers/cc-ci.git && chmod 600 /etc/cc-ci/.git/config'
Stripping the userinfo does **not** break the clone: the sweep only ever *fetches*, and the mirror serves
this repo anonymously (that is precisely what B-redfix-8 exploits). Verified 2026-07-09T09:1xZ —
`GIT_TERMINAL_PROMPT=0 git -c credential.helper= ls-remote https://git.autonomic.zone/recipe-maintainers/cc-ci.git HEAD`
`f0372b8… HEAD`, rc=0, no credential. The sibling `cc-ci-secrets` submodule URL (`.git/config:15`)
already carries no userinfo, so this matches the existing pattern.
**Rotation blast radius is small — no operational reason to delay.** `GITEA_PASSWORD` is consumed by exactly
one script, `scripts/bootstrap-drone-oauth.sh` (`git grep -ln GITEA_PASSWORD -- .` returns that script, `docs/
install.md`, and machine-docs prose; nothing else in the runner). In particular `scripts/recipe-mirror-sync.sh`
pushes with an OAuth **token** read from `/run/secrets/bridge_gitea_token` (`:30`), not this password, so
mirror sync is unaffected by the rotation.
**HOW to confirm BOTH exposures are closed** (each prints a digest; a value other than `3fcea78925015fc9` on
both, and not the empty-string tell `e3b0c44298fc1c14`, means rotated):
# exposure 2 — the embedded copy; `python3` is absent on cc-ci, so use sha256sum
ssh cc-ci 'sed -n "s#^\s*url = https://[^:]*:\([^@]*\)@git\.autonomic\.zone/recipe-maintainers/cc-ci\.git#\1#p" \
/etc/cc-ci/.git/config | head -1 | tr -d "\n" | sha256sum | cut -c1-16'
**EXPECTED after step 3:** the command prints **nothing** (no userinfo left to match) — an empty result here
is the fix, whereas an `e3b0c44298fc1c14` from the *step-2* probe is a broken probe. Do not confuse them.
**Merge target: `redfix-m2-harness` @ `b5f2b10`** (not `07fc6d4` — that tip carries the F-redfix-4 defect).
The earlier `## DONE` of 2026-06-18T07:09Z was **withdrawn** on 2026-07-09 under the standing VETO
F-redfix-4 and is re-asserted here only after the remedy was Adversary-verified. Details of that cycle,