diff --git a/creds-matrix-notes.md b/creds-matrix-notes.md index c20c8f5..12d09a8 100644 --- a/creds-matrix-notes.md +++ b/creds-matrix-notes.md @@ -145,7 +145,8 @@ ## n-juju-user-nopassword `createadmin` is invoked with NO `--password`, so the value is stored NOWHERE. Kept random -and unstored BY RULING (2026-07-25): the API key is what is load-bearing, and the +and unstored by operator decision recorded in SEC-018/SEC-019 (2026-07-24): the API key +is what is load-bearing, and the `operator` identity removes any need to log in as this user. `filename` is `-` because there is no host artifact -- existence checks must skip such rows rather than fail them. diff --git a/docs/CURRENT-STATE.md b/docs/CURRENT-STATE.md index 2713af5..5f40a34 100644 --- a/docs/CURRENT-STATE.md +++ b/docs/CURRENT-STATE.md @@ -884,25 +884,54 @@ `sudo` and the command was `ssh 'sudo ...'` -- the quote meant NO rule matched, so it fell through to the classifier. Fixed by adding the quoted variants (`ssh *'sudo *`, `ssh *"sudo *`, `ssh *sudo -n *`) plus a targeted ask rule for the - privileged invocation; all are `ask`, never `allow`. **`Bash(ssh * virsh *)` carries the - IDENTICAL latent gap and is NOT fixed** (logged, not executed -- hard rule 1). - **MEASURED RESULTS.** Region secrets dir contains exactly `admin.apikey`, `admin.pass`, - `db.pass`, `lxd-trust.pass` (all 0600) -- precisely the SEC-020(i) carve-out list, with - nothing undeclared. Netbox dir: `admin.pass`, `api.token`, `secret_key` (all 0600). - SEC-022 shadow stores CONFIRMED and now audited: the dc0 store holds - `maas-virsh_ed25519` + `vr1-dc0_svc_ed25519{,.pub}`, the dc1 store only - `vr1-dc1_svc_ed25519`. **ZERO undeclared files remain at any declared location**, so the - SEC-020 and SEC-022 surfaces are fully accounted for. **The sweep also caught SIX - INFERRED FILENAMES in the first matrix draft** (`maas-cli-profile`, - `maas-enroll-secret`, `netbox-secret-key`, `netbox-admin-password`, `juju-credentials`, - `maas-snap-power-key`) -- authored as plausible names rather than measured, a hard-rule-2 - miss by the authoring session, caught by the checker's own undeclared-detection and - corrected to measured values (`.maas.cli`, `secret`, `secret_key`, `admin.pass`, - `credentials.yaml`, `id_ed25519`/`id_dc1_power`), with their real locations added to the - declared list. Matrix now 77 rows. **9 findings remain, all TRUE:** SEC-021 (3x S2 - declaration + 2x E1 on-disk), the ruling-5 conflation, and 3x S5 power-key naming - asymmetry -- dc0's snap key is the generic `id_ed25519` while dc1's is the dedicated - `id_dc1_power`, which is the SEC-012/SEC-016 per-DC-isolation divergence made visible. + privileged invocation; all are `ask`, never `allow`. `Bash(ssh * virsh *)` carried the + IDENTICAL latent gap; it was flagged-not-fixed at discovery (hard rule 1) and then + **FIXED 2026-07-26 under operator direction** (commit `6d43619`, quoted variants added). + **MEASURED RESULTS** -- capture `docs/audit/d137-location-listing-20260726.txt` (a + `stat` listing; the tier-2 capture cited here previously is a checker VERDICT file and + contains none of these values -- a GA-R1 rule 2 defect found by the committee and + corrected in this commit). Region secrets dir: `admin.apikey`, `admin.pass`, `db.pass`, + `lxd-trust.pass` (0600) -- precisely the SEC-020(i) carve-out list. Netbox dir: + `admin.pass`, `api.token`, `secret_key` (0600). SEC-022 shadow stores confirmed. The + claim that **"ZERO undeclared files remain, so SEC-020/SEC-022 are fully accounted + for" is WITHDRAWN** -- it rested on a site-blind check (see the committee block below). + **INFERRED-FILENAME MISS (hard rule 2), corrected count:** the session first reported + SIX; the committee measured **~21**, because only the rows the sweep physically touched + were re-measured. `.maas.cli` was itself never measured and is WRONG -- the MAAS snap + CLI stores its profile at `~/snap/maas/current/.maascli.db` (measured this session). + Matrix 77 rows at that point. The 9 findings then reported were true so far as they + went, but the run that produced them is superseded below. + **COMMITTEE AUDIT 2026-07-26 (6 independent read-only lenses: correctness, coverage, + claim-verification, ruling-fidelity, record-integrity, Roosevelt-transfer). VERDICT: + the register's DESIGN holds, but TIER 2's VERDICTS ARE NOT TRUSTWORTHY as delivered and + this document overstated what was verified.** Every defect below was REPRODUCED, and + FOUR lenses converged independently on the first one. + *Superseding figures (these are current; earlier figures in this entry are history):* + matrix **77 rows**, creds-matrix harness **35/35**, creds-audit **13/13**, gauntlet + **ALL GREEN (80)**. The earlier "14 rows NOT JUDGED" was from the unprivileged run; the + privileged run reports **2**. The widened sprawl glob is `*.pass`, not `admin.pass`. + *Confirmed false greens (a real missing or misplaced credential passes a green sweep):* + (1) **tier 2 is SITE-BLIND** -- `observed`/`declared` are keyed by host-role only, so a + file in the wrong site's folder satisfies the row; this masked SEC-021's `opnsense-api.txt` + on-disk absence, so **"SEC-021's on-disk half REPRODUCED" is corrected to the power-key + artifacts ONLY**; (2) `probe_remote` reports UNREACHABLE for a location it successfully + read (empty-but-readable dir, or absent literal-file path), which gates the whole role + and converts every absence FAIL at that role into an `[ok]`; (3) literal-file locations + get no absent/unreadable detection at all -- the T33 fix covered only `dir/*` patterns; + (4) an empty locations file bypasses ruling 3's refusal and affirms existence over zero + locations; (5) no non-empty floor -- a 0-row matrix passes every check; (6) a mint-ref + pointing at a directory crashes with exit 1, indistinguishable from findings, and S5/S6/S7 + never run; (7) `E2 MODE` is custody-gated so 43 of 77 rows are never mode-checked; + (8) S5's dc1->dc0 direction is untested -- deleting it leaves the harness green (the + in-repo both-bounds precedent, in the symmetry check itself). + *Also outstanding:* ruling 4's SEC-009 demotion is NOT done and its stated trigger + (tier 2 built) has passed; the 12-column amendment is recorded in no D-numbered surface; + `sec-ref` mis-attribution recurs (both `juju-maas-user` rows cite SEC-020; correct is + SEC-018/-019); the libvirt SSH power password (standing rotation obligation, + `reenroll-hosts.sh:22-25`) has no row; and T24 asserts literal finding strings, so + remediating the ruling-5 conflation would turn the GAUNTLET red -- the test punishes the + fix it exists to protect. Remediation is IN PROGRESS under operator direction + ("Do as many as you can autonomously"); this block is the authority on what is fixed. ## 9. Additional defect found while authoring (FIXED in sweep Batch 0.3, 2026-07-19 -- wrap-aware exclusion, GA-F15; history below) diff --git a/docs/D-137-implementation-plan.md b/docs/D-137-implementation-plan.md index 8a84118..125385a 100644 --- a/docs/D-137-implementation-plan.md +++ b/docs/D-137-implementation-plan.md @@ -147,7 +147,7 @@ ## Hard constraints inherited -- **Metadata only, enforced by source-grep.** `tests/creds-audit/run-tests.sh:71-73` fails +- **Metadata only, enforced by source-grep.** `tests/creds-audit/run-tests.sh:90-92` fails on any `cat|head|tail|less|more|od|xxd|hexdump` in the audit source. Remote provenance verification MUST compare `sha256sum` digests over ssh -- never transfer content. A new checker should carry the same guard test. @@ -204,7 +204,8 @@ - `admin` serves a `human` GUI row AND a `service` api row -> ruling-5 invariant FAIL. Remediation is the `operator`/`admin` split already begun 2026-07-25, i.e. migrate the 19 `maas admin` call sites to a service identity. -- 12 rows carry `mint-ref=operator-terminal` -> reproducibility debt to convert. +- 27 rows carry `mint-ref=operator-terminal` (the plan's original figure of 12 counted + only FINDING 1's declared secrets; the delivered matrix admits every undocumented mint) -> reproducibility debt to convert. - SEC-021 (dc0 `opnsense-api.txt` ABSENT; power-key name/host divergence), SEC-022 (headend shadow stores), SEC-023 (sprawl blind spots + predicted `~/admin-openrc`). diff --git a/docs/audit/d137-location-listing-20260726.txt b/docs/audit/d137-location-listing-20260726.txt new file mode 100644 index 0000000..b00def2 --- /dev/null +++ b/docs/audit/d137-location-listing-20260726.txt @@ -0,0 +1,74 @@ +# D-137 declared-location STAT LISTING -- measured 2026-07-26 +# +# WHY THIS FILE EXISTS: docs/CURRENT-STATE.md previously asserted specific remote +# filenames and modes citing d137-tier2-privileged-20260726.txt, which is a checker +# VERDICT file and contains none of them (committee lens 3/5 finding). GA-R1 rule 2 +# requires measurements be quoted from captured command output. This is that output. +# +# Metadata only: stat -c '%n %a'. No file content read. sudo -n used ONLY for the +# two root-owned directories. + +## jumphost (vcloud) -- SEC-009 convention folders +# /home/jessea123/vr1-office1-creds + /home/jessea123/vr1-office1-creds/maas-admin-password 600 + /home/jessea123/vr1-office1-creds/maas-operator-password 600 + /home/jessea123/vr1-office1-creds/office1_svc_ed25519 600 + /home/jessea123/vr1-office1-creds/office1_svc_ed25519.pub 644 + /home/jessea123/vr1-office1-creds/opnsense-api.txt 600 + /home/jessea123/vr1-office1-creds/opnsense-root-hash 600 + /home/jessea123/vr1-office1-creds/opnsense-root-password 600 + /home/jessea123/vr1-office1-creds/README 600 + /home/jessea123/vr1-office1-creds/tailscale-authkey.txt 600 + /home/jessea123/vr1-office1-creds/vr1-netbox.env 600 + /home/jessea123/vr1-office1-creds/vr1-netbox-sandbox.env 600 + /home/jessea123/vr1-office1-creds/vr1-office1.env 600 + /home/jessea123/vr1-office1-creds/vr1-stage1.env 600 +# /home/jessea123/vr1-dc0-creds + /home/jessea123/vr1-dc0-creds/maas-api-key.txt 600 + /home/jessea123/vr1-dc0-creds/vr1-dc0-edge_ed25519 600 + /home/jessea123/vr1-dc0-creds/vr1-dc0-edge_ed25519.pub 644 + /home/jessea123/vr1-dc0-creds/vr1-dc0_svc_ed25519 600 + /home/jessea123/vr1-dc0-creds/vr1-dc0_svc_ed25519.pub 644 +# /home/jessea123/vr1-dc1-creds + /home/jessea123/vr1-dc1-creds/maas-api-key.txt 600 + /home/jessea123/vr1-dc1-creds/opnsense-api.txt 600 + /home/jessea123/vr1-dc1-creds/vr1-dc1-edge_ed25519 600 + /home/jessea123/vr1-dc1-creds/vr1-dc1-edge_ed25519.pub 644 + /home/jessea123/vr1-dc1-creds/vr1-dc1-maas-power_ed25519 600 + /home/jessea123/vr1-dc1-creds/vr1-dc1-maas-power_ed25519.pub 644 + /home/jessea123/vr1-dc1-creds/vr1-dc1_svc_ed25519 600 + /home/jessea123/vr1-dc1-creds/vr1-dc1_svc_ed25519.pub 644 + +## headend (voffice1) -- SEC-022 shadow stores + /home/jessea123/vr1-dc0-creds/maas-virsh_ed25519 600 + /home/jessea123/vr1-dc0-creds/vr1-dc0_svc_ed25519 600 + /home/jessea123/vr1-dc0-creds/vr1-dc0_svc_ed25519.pub 644 + /home/jessea123/vr1-dc1-creds/vr1-dc1_svc_ed25519 600 +## headend -- CLI profile, juju client store +# CORRECTION (measured this run): ~/.maas.cli does NOT exist. The MAAS snap CLI stores its +# profile + API key in ~/snap/maas/current/.maascli.db (a SQLite DB), NOT the path the +# matrix inferred. Note the `current` symlink -- same per-revision snap fragility class as +# the power keys. + stat: cannot statx '/home/jessea123/.maas.cli': No such file or directory + /home/jessea123/.local/share/juju/clouds.yaml 600 + /home/jessea123/.local/share/juju/credentials.yaml 600 + /home/jessea123/.local/share/juju/public-clouds.yaml 600 +## headend -- region secrets dir (SEC-020) + snap ssh + enroll secret [PRIVILEGED] + /root/maas-secrets/admin.apikey 600 + /root/maas-secrets/admin.pass 600 + /root/maas-secrets/db.pass 600 + /root/maas-secrets/lxd-trust.pass 600 + /var/snap/maas/current/root/.ssh/config 644 + /var/snap/maas/current/root/.ssh/id_dc1_power 600 + /var/snap/maas/current/root/.ssh/id_ed25519 600 + /var/snap/maas/current/root/.ssh/known_hosts 600 + /var/snap/maas/current/root/.ssh/known_hosts.old 644 + /var/snap/maas/common/maas/secret 640 + +## netbox VM (office1-netbox) -- founding SEC-009 miss [PRIVILEGED] + /root/netbox-secrets/admin.pass 600 + /root/netbox-secrets/api.token 600 + /root/netbox-secrets/secret_key 600 + +## headend -- REAL MAAS CLI profile store (measured after the ~/.maas.cli miss) + /home/jessea123/snap/maas/current/.maascli.db 600 diff --git a/docs/changelog-20260726-d137-tier1.md b/docs/changelog-20260726-d137-tier1.md index a056dc5..32f5ee8 100644 --- a/docs/changelog-20260726-d137-tier1.md +++ b/docs/changelog-20260726-d137-tier1.md @@ -103,7 +103,7 @@ One case per invariant, each named for the real defect it encodes, asserting rc AND an output regex. Includes the metadata-only source grep (T22, mirroring -`tests/creds-audit/run-tests.sh:71-73`) and **T24, which asserts the real tree stays RED** +`tests/creds-audit/run-tests.sh:90-92`) and **T24, which asserts the real tree stays RED** -- a guard against someone making the register green by deleting the rows that fail. The harness found three genuine bugs in the checker before it shipped: the Row-vs-Row sort @@ -151,7 +151,7 @@ ## Item 6 -- `creds-manifests/vm-secret-locations` (ruling 3) The absolute bound on discovery. Includes every class the ruling's faithful-implementation -note names, plus two the note did not: the in-clone PKI overlay and the DOCFIX-175 +note names, plus ONE the note did not: the DOCFIX-175 plaintext `terraform.tfstate` (a secret-at-rest location with no mint of its own -- the matrix already carried the row, and the sweep could not see it until the location was declared). @@ -324,3 +324,39 @@ the widening did not become a catch-all. **Revert:** drop the two rules from `.claude/settings.json`. + +## Item 13 -- COMMITTEE AUDIT (6 lenses) + phase 1: record corrections + +Operator-directed six-lens read-only review (correctness, coverage, claim-verification, +ruling-fidelity, record-integrity, Roosevelt-transfer). Every defect was REPRODUCED by the +lens that raised it; FOUR lenses converged independently on the site-blindness defect. +Full defect list and remediation status: `docs/CURRENT-STATE.md` (GA-R1). + +**Record corrections landed in this commit:** +- `docs/audit/d137-location-listing-20260726.txt` NEW -- the actual `stat` listing for every + declared location. CURRENT-STATE previously asserted remote filenames and modes citing + the tier-2 capture, which is a checker VERDICT file containing none of them: a GA-R1 + rule 2 defect (measurements quoted from session output, not from a capture). +- **`.maas.cli` was never measured** and is WRONG. Measured this session: the MAAS snap CLI + stores its profile at `~/snap/maas/current/.maascli.db` (0600). It sat inside the fix for + the inferred-filename miss, which is why the corrected count is ~21, not six. +- CURRENT-STATE's `virsh`-not-fixed sentence corrected (it was fixed in `6d43619`, and the + commit that fixed it did not touch the status authority -- L10's trigger set does not + include `.claude/settings.json`, so lint passed clean). +- The **"ZERO undeclared files ... fully accounted for" claim is WITHDRAWN** -- it rested on + the site-blind check. +- **"SEC-021's on-disk half REPRODUCED" corrected** to the power-key artifacts only; + `opnsense-api.txt`'s on-disk absence was masked. +- Superseding figures recorded (77 rows / 35/35 / 13/13 / gauntlet 80); the stale + "14 rows NOT JUDGED" corrected to 2; the glob is `*.pass`, not `admin.pass`. +- **12-column amendment recorded on D-137** in `design-decisions.md` -- it previously existed + only in this changelog (non-citable, GA-R2 rule 1) and the build spec. +- Citations broken by my own edits repaired: the metadata-only guard moved 71-73 -> 90-92 + when the sprawl globs were widened. +- Plan's "12 operator-terminal rows" reconciled to the delivered 27. +- Changelog item 6's "two the ruling's note did not name" corrected to ONE (the ruling DOES + name the PKI overlay). +- `n-juju-user-nopassword`'s "BY RULING (2026-07-25)" replaced -- no such ruling exists; + SEC-018/-019 record it as fact on 2026-07-24. + +**Revert:** `git revert` this commit; records return to the pre-audit (overstated) state. diff --git a/docs/design-decisions.md b/docs/design-decisions.md index 3366e1c..812a0ea 100644 --- a/docs/design-decisions.md +++ b/docs/design-decisions.md @@ -5198,3 +5198,32 @@ the `maas admin` profile across 19 call sites in 4 scripts). That is "who holds accounts"; this D is "how secret material is stored". Same lifecycle family, different decisions; folding them would batch two rulings and violate GA-R5. + +**IMPLEMENTATION AMENDMENT 2026-07-26 (12-column matrix schema) [OPS].** Recorded here +because the amendment previously existed only in a session changelog and the build-spec +file; GA-R2 rule 1 makes a changelog non-citable and rule 5 consolidates it away, so the +only durable home for an operational choice that outlives the stage is this entry. +The matrix schema went from 10 to 12 columns and a row was re-keyed from one credential to +one (credential, location) PAIR. Added: `custody` +(`consolidated` | `source-of-record` | `off-manifest-known` | `not-consolidated-ruled`) and +`notes-ref` (an id-keyed pointer into `creds-matrix-notes.md`). Question as presented: +"reply with a go on the schema amendment"; operator answer, exact utterance: +"go with the 12-column amendment", 2026-07-26. +WHY: a read-first round-trip check of the three checked-in manifests proved the 10-column +form could not carry what sub-ruling 2 forbids dropping, and sub-ruling 3 places four +deliberate, reasoned credential copies INSIDE `--remote`'s declared locations, where +`creds-audit.sh`'s undeclared-file check would report every one as a defect. This +IMPLEMENTS sub-ruling 2 ("this prose must become MATRIX FIELDS") rather than changing what +D-137 decides; the five sub-rulings are untouched. OPS under GA-R3 (mechanism detail, +doubt resolves DOWN); no new D-number. +NOTE the `not-consolidated-ruled` token encodes standing SEC-020(i) operator rulings as +machine-readable data -- that is the reason this belongs in a decision surface and not +only in session scratch. + +**COMMITTEE AUDIT 2026-07-26 -- implementation defects found post-adoption.** A six-lens +read-only review of the tier-1 + tier-2 build reproduced eight false-greens, the most +consequential being that tier 2 keys existence on host-role ALONE and never consults +`site-key`, so a credential present at one site satisfies the row for every site. Status, +the full defect list, and remediation progress live in `docs/CURRENT-STATE.md` (GA-R1); +this note exists so a reader of D-137 is not left believing the adopted design shipped +sound. The DESIGN is unchanged and was not faulted; the defects are implementation.