Newer
Older
openstack-caracal-dc-dc / docs / changelog-20260725-maas-admin-recovery.md

Session changelog 2026-07-25 -- MAAS admin-account recovery + creds consolidation

Branch dc-dc-stage4-phase3-maas-deploy. Operator task: "complete the recovery of the MAAS admin accounts ... complete password resets, and save the creds in their proper folders so I can retrieve them." Status lives ONLY in docs/CURRENT-STATE.md (GA-R1); this changelog is the review surface -- each item states what / why / revert.

Measured evidence for every claim below: docs/audit/maas-admin-recovery-20260725.txt (42 lines, no secret values).

The task premise was FALSIFIED by measurement before any mutation. The session-ledger's 2026-07-25 "IMMEDIATE NEXT" asserted MAAS web-GUI login "does NOT exist ... never minted; no prior task did it". It did exist: the admin superuser's password was minted 2026-07-13 by scripts/site-headend-install.sh:452 and is MEASURED working (login POST 204 with the stored value, 400 with a wrong-password control; has_usable_password=True). It was merely root-only on voffice1 at /root/maas-secrets/admin.pass and never consolidated to ~/vr1-office1-creds/ -- the SEC-009 miss class, not a missing account. Only the ledger's juju-* clause was correct. That finding was reported and the scope RE-RULED by the operator before execution: "New account + consolidate only" -- no existing password rotated.

Item 1 -- NEW MAAS superuser operator (live region mutation, gated)

What: minted MAAS superuser operator / jesse.austin@neumatrix.com on the Office1 region for HUMAN GUI login. Password 32-char base64, generated ON vcloud straight into ~/vr1-office1-creds/maas-operator-password (0600), then fed to maas createadmin over stdin -- never in argv, never printed, never in context. Verified: account present + superuser=True; stored password authenticates (204) against a wrong-password control (400). Why: operator-ruled scope. Gives a human identity distinct from the service identity -- until now the only usable GUI login was admin, which is simultaneously the automation identity (item 6). Note maas changepassword has NO --password flag and createadmin --password would place the secret in argv (the shape site-headend-install.sh:452 uses); the stdin path satisfies the SEC-018 "never printed/echoed/in argv" discipline and is the transferable technique. Revert: ssh voffice1 'maas admin user delete operator' then rm ~/vr1-office1-creds/maas-operator-password and drop its manifest row (item 3).

Item 2 -- admin password CONSOLIDATED to the jumphost (no rotation)

What: copied voffice1:/root/maas-secrets/admin.pass -> ~/vr1-office1-creds/maas-admin-password (0600, 28 bytes). Byte-identical, sha256-verified both ends. Value moved over ssh into the file; never printed. Verified the CONSOLIDATED copy authenticates as admin (204). Why: closes the SEC-009 miss for this secret -- it is now retrievable from the operator's own folder instead of being root-only on a VM. Deliberately NOT rotated (operator ruling), so the VM copy REMAINS source-of-record and the vcloud file is a working copy; a future admin rotation must update BOTH in one operation or the VM copy becomes a stale trap. Recorded in SEC-020. Revert: rm ~/vr1-office1-creds/maas-admin-password + drop its manifest row. No loss -- the source-of-record copy on voffice1 was never modified.

Item 3 -- creds-manifests/vr1-office1.manifest: two new declared secrets

What: declared maas-admin-password (provenance voffice1:/root/maas-secrets/admin.pass -- the <vm>:<path> class the manifest format exists for) and maas-operator-password (local), with a header comment recording the mint mechanism, the byte format, and the still-un-consolidated residue (item 7). Why: the manifest is the forcing function -- an undeclared secret is exactly how the NetBox token went missing. Declaring makes creds-audit enforce presence+mode. Revert: git checkout HEAD -- creds-manifests/vr1-office1.manifest.

Item 4 -- dc0/dc1 manifest BACKFILL -> creds-audit CLEAN on all three sites

What: declared the four files that were failing creds-audit as UNDECLARED: dc0 maas-api-key.txt + vr1-dc0-edge_ed25519{,.pub}, dc1 maas-api-key.txt. Provenance local for all four (SEC-018/019 state the API keys were generated FROM the jumphost so they never persisted on the region; the edge keypair likewise). Why: creds-audit was RED on two of three sites at session open (dc0 3 fails, dc1 1 fail) -- the same convention the operator's task is about, so leaving the gate red would have been incoherent. The dc0 edge-keypair rows were a KNOWN queued finding, named in creds-manifests/vr1-dc1.manifest:18-20 ("dc0 precedent, whose manifest rows are a queued backfill finding") -- this closes it. Result: creds-audit CLEAN x3 (13/5/8 entries), harness 7/7. Revert: git checkout HEAD -- creds-manifests/vr1-dc0.manifest creds-manifests/vr1-dc1.manifest (returns both sites to FAIL).

Item 5 -- SEC-020 opened (register of record)

What: new row in docs/security-ledger.md covering (a) the un-consolidated VM-minted admin password + the falsified "no GUI login" claim, (b) the human/service identity conflation that CAUSED the miss, (c) the new operator standing credential. Status: OPEN -- rotation obligation on TWO region superusers, plus three logged-not- actioned residues. Open SEC rows 15 -> 16. Why: ledger mandate -- every credential exposure/obligation gets a row, never only a comment. A new standing credential is a new rotation obligation. Revert: delete the SEC-020 row.

Item 6 -- session-ledger: falsified claim corrected + machine block re-seeded

What: (a) struck the 2026-07-25 "IMMEDIATE NEXT" block and annotated it FALSIFIED + RESOLVED with what was actually true and what was done. (b) Re-seeded the machine-derived block from a fresh ledger-scan.sh: it was stale on EVERY line -- open SEC 12 -> 16, next-free D 133 -> 137 / DOCFIX 200 -> 204 / BUNDLEFIX 052 -> 053, and D-136 was missing from the open-decision list while D-071/D-129 were still listed despite being ADOPTED/RESOLVED. Why: (a) GA-R1 -- a falsified claim left standing misdirects the next session, and this one would have sent it to create an account that already existed. (b) my SEC-020 changes the count that block carries, so re-seeding is required by this change, not added scope; the block's own rule is "regenerate, never hand-edit". Revert: git checkout HEAD -- docs/session-ledger.md.

Item 7 -- Findings LOGGED, not executed (hard rule 1)

What: recorded in SEC-020, no action taken:

  • (i) voffice1:/root/maas-secrets/{admin.apikey,db.pass,lxd-trust.pass} remain un-consolidated. admin.apikey deliberately NOT copied -- trivially regenerable (maas apikey --username=admin) so a second copy is pure added surface; db.pass/lxd-trust.pass are region infrastructure, outside this task's scope.
  • (ii) the DURABLE fix for the miss class is a site-headend-install.sh amendment to consolidate at mint time (the script currently leaves all four secrets on the VM). Queued, unruled -- a script change needs its harness and a ruling.
  • (iii) the human/service conflation is proposable as a D-number, [ARCH] -- still UNASSIGNED after item 9 took D-137 for the credential pipeline; the two are kept separate deliberately (see D-137's "Related, deliberately NOT folded in"): admin is both the GUI login and the automation identity (its API key drives the maas admin CLI profile across 19 call sites in 4 scripts -- site-headend-install.sh 10, phase-04-network-verify.sh 5, phase-04-network-create.sh 3, phase-00-teardown-release.sh 1). Roosevelt-delta: per-identity MAAS separation / audit attribution on a multi-tenant cloud. NOT assigned -- GA-R3 doubt-resolves-DOWN, operator's call. Why: findings are logged, never executed mid-task. Revert: n/a (records only).

Item 8 -- evidence capture

What: docs/audit/maas-admin-recovery-20260725.txt -- generated, not hand-written: user list, Django password-state booleans, both login probes with wrong-password controls, the sha256 fidelity compare, file modes/sizes, creds-audit x3 + harness, and an automation-unaffected check. Machine-scanned for password bytes (0 hits) before commit. Why: GA-R1 rule 2 -- claims are quoted from captured command output, never prose. Revert: git rm docs/audit/maas-admin-recovery-20260725.txt.

Not done / still open

  • admin, juju-vr1-dc0, juju-vr1-dc1 passwords UNCHANGED by ruling. The two juju-* passwords remain random + unstored; their API keys are load-bearing for the blocked Stage-5 bootstrap and operator removes any need to log in as them.
  • MAAS and maas-init-node are MAAS-internal accounts -- deliberately untouched (maas-init-node is also is_active=False).
  • Items 7(i)-(iii) above.
  • Pre-existing, NOT touched by this session: CURRENT-STATE G14 SEC count (reads 12, actual 16) and section 4 ("RULED-BUT-NOT-BUILT", flagged comprehensively stale 2026-07-25) -- both already operator-deferred to Stage 4 close.

Item 9 -- D-137 PROPOSED: credential mint-and-consolidate pipeline [ARCH]

What: new docs/design-decisions.md entry D-137, PROPOSED (not ruled, not implemented). Answers the operator's follow-up ask -- a better minting/saving method plus "a durable rule to make sure when accounts are created there is a consolidation that happens every time". Records the measured diagnosis, a three-part proposal (the invariant; creds-mint.sh + creds-audit --all/--remote + gate wiring + site-headend-install.sh amendment; the Roosevelt delta), and THREE open forks. Section 8 of CURRENT-STATE gains item 9; the ledger's machine block goes 4 -> 5 open decisions. Why: the diagnosis is that the existing SEC-009 control cannot detect the failure class it was built for -- the audit is DECLARATION-based, so an undeclared secret is structurally invisible. creds-audit vr1-office1 reported CLEAN on 2026-07-15 while four secrets minted on the region VM on 2026-07-13 sat undeclared; admin.pass surfaced only 2026-07-25, twelve days later, and only because a task went looking (SEC-020). Compounding it: no --all (creds-audit.sh:25 takes one mandatory site), and the only enforcement is PROSE at runbooks/dc-dc-phase3-maas-enlist-deploy.md:498 -- absent from preflight/cloud-assert/repo-lint/gauntlet -- which demonstrably did not fire at either DC standup (dc0 3 + dc1 1 undeclared at this session's open). Admitted as a D under GA-R3 on the A1 test: Roosevelt mints per-DC IPMI/BMC and Vault material on real hardware, so a Roosevelt build session should grep this before creating any credential; today the rule lives only in docs/security-ledger.md, a register of exposures, which no gate can cite as authority. Deliberately NOT done: no tooling built, no enforcement wired, no SEC-009 demotion -- PROPOSED means present options, never implement (hard rule 1 + GA-R5). The SEC-020 identity-conflation question is kept SEPARATE to avoid batching two rulings. Revert: delete the D-137 block from docs/design-decisions.md, CURRENT-STATE section 8 item 9, and the ledger machine-block D-137 clause (restore count to 4).

Item 10 -- SEC-021/-022/-023 opened: live credential defects found during D-137 research

What: three new docs/security-ledger.md rows recording defects MEASURED read-only while researching the credential matrix. None actioned (hard rule 1). Open SEC rows 16 -> 19; CURRENT-STATE G14's evidence cell reconciled to 19 in this commit (its title text keeps the 07-23 figure and is marked superseded); ledger machine block re-seeded.

  • SEC-021 -- dc0 custody defects. (a) changelog-20260719-dc0-deploy-stepB.md:263 records the dc0 edge REST API credential written to ~/vr1-dc0-creds/opnsense-api.txt; a find over vcloud $HOME returns that filename for office1 and dc1 ONLY -- no dc0 copy -- and creds-audit vr1-dc0 reports CLEAN because it was never manifested. Severity is a custody/record defect + API-access gap, NOT a lockout: the dc0 edge SSH key is present and D-112(c) makes key-only SSH the primary management path. (b) dc0's power key is maas-virsh_ed25519 on voffice1 vs dc1's declared vr1-dc1-maas-power_ed25519{,.pub} on vcloud -- same purpose, different name, host and custody, dc0 with no .pub.
  • SEC-022 -- unaudited shadow stores. voffice1 holds ~/vr1-dc0-creds/ AND ~/vr1-dc1-creds/ with real key material. creds-audit.sh:29-31 resolves the folder under $HOME on whatever host it runs on (vcloud) and contains no ssh at all, so these are structurally invisible. A SCOPE gap in the control, not a one-off -- D-128 puts Plane-2 execution on voffice1, so headend-resident credentials will keep appearing.
  • SEC-023 -- sprawl blind spots + a PREDICTED Stage-5 exposure. The globs at creds-audit.sh:72-73 miss admin.pass, *.apikey, *.key, *.pem, *_ed25519 and even maas-api-key.txt. phase-03-admin-openrc.sh:32,72 will write OS_PASSWORD into $HOME/admin-openrc at Stage 5 -- home root, outside any creds folder, matching no glob. Recorded BEFORE it exists so the mint is not missed. Why: the ledger mandate is a row at discovery, never only a note. These are live defects independent of any D-137 ruling, and the approved plan lists recording them as the one action not gated behind the five open forks. They also constitute the empirical case for the matrix: SEC-021/-023 are absence-and-drift (invisible to discovery) while SEC-022 is an unknown-unknown (invisible to declaration) -- the two halves are complementary, which could not be argued from the discovery-only D-137. NOT done: no remediation, no tooling, no glob widening -- all coupled to unruled forks. Interim obligations are stated per row (notably: whoever runs Stage 5 phase-03 must consolidate ~/admin-openrc BY HAND at mint time). Revert: delete the three rows, restore the G14 evidence cell, re-seed the ledger block to 16.

Item 11 -- D-137 ADOPTED: all five sub-rulings ruled (GA-R5), implementation unblocked

What: the operator's follow-up ask ("better best practices method ... and a durable rule so credentials aren't misplaced or lost") was designed, committee-reviewed, and ruled. D-137 moves PROPOSED -> ADOPTED 2026-07-26. Five sub-rulings, each its own operator exchange, each committed+pushed BEFORE the next was asked (GA-R5), verbatim selections quoted in the Status block: (1) enforcement = "Blocking in preflight"; (2) derivation = "Derive manifests from matrix"; (3) remote scope = "Declared locations only"; (4) policy home = "D-137 is the authority"; (5) identity = "Fold in as a D-137 invariant". Commits a3a2d46, df8fa21, aa67634, db85531, and this one. Why the design changed: the operator identified the flaw in D-137-as-first-proposed -- a discovery sweep can never detect a credential that was NEVER MINTED, because absence is invisible to discovery. The known creation points support a forward EXPECTED-state register, which is the only instrument that catches a missed mint. Research (3 read-only agents) then produced four measured findings that validated it and one that qualified the premise: ssh-keygen returns ZERO hits repo-wide, so 12 declared secrets have no mint command anywhere -- recorded in docs/audit/creds-creation-points-20260725.md, and the reason mint-ref must admit an operator-terminal provenance kind. Committee (5 lenses) produced three amendments now folded into the ruled design: cardinality + host-role as first-class columns; manifests DERIVED not dual-maintained (the GA-F06 mitigation); and a VALIDITY tier distinct from existence -- prompted by finding that creds-audit parses provenance and NEVER verifies it, so the SEC-020 stale trap is unchecked today. NOT done: nothing built. No matrix file, no checker, no --render, no preflight Pn, no SEC-009 demotion. Implementation is unblocked but unstarted. Note the first run of the identity invariant is expected RED by design (admin serves both a human and a service row -- that IS the defect). Revert: flip the D-137 Status line back to PROPOSED and drop the five sub-ruling blocks; restore CURRENT-STATE item 9 and the ledger open-decision count to 5.

Item 12 -- D-137 build spec made repo-durable (handoff to a fresh session)

What: new docs/D-137-implementation-plan.md -- the executable build detail for the ADOPTED D-137: the five ruled decisions and their consequences, the matrix schema (logical keys only per the SEC-004 ruling), the checker model and precedents with file:line, the three tiers, the inherited hard constraints, the file list, the acceptance test, and an explicit "EXPECTED RED BY DESIGN" section. CURRENT-STATE item 9 and the ledger addendum now point at it. Also updated the creds-folder-convention auto-memory pointer: policy authority is now D-137 (ruling 4), with a verify-first caveat that creds-audit CLEAN is not evidence of completeness and the matrix is not built. Why: the design and committee review originated in a plan file under ~/.claude/plans/, which is OUTSIDE the repo -- so a fresh session bootstrapping via the GA-R4 path (CURRENT-STATE -> session-ledger -> ledger-scan) would never surface it, and the repo-is-authoritative rule would leave the build spec undiscoverable. Precedent for a D-NNN plan doc in-repo: docs/D-068-vault-migration-plan-draft.md. Operator directed the build to a fresh session, which makes this durability gap load-bearing rather than tidy. Note: the spec deliberately tells the next session NOT to make the first run green -- the acceptance test is that the checker REPRODUCES SEC-021/-022/-023 and the openrc case as named failures. A checker that passes on today's tree is wrong. Revert: git rm docs/D-137-implementation-plan.md and drop the two pointers.