diff --git a/docs/security-ledger.md b/docs/security-ledger.md index 13d8ccd..9d39877 100644 --- a/docs/security-ledger.md +++ b/docs/security-ledger.md @@ -41,7 +41,7 @@ | SEC-023 | 2026-07-25 | **Sprawl detection has systematic blind spots, and one exposure is already PREDICTED for Stage 5.** `scripts/creds-audit.sh:72-73` sprawl-globs exactly `*.env`, `.*.env`, `*appcred*`, `*-cred*.txt`, `*authkey*`, `*.token`. That set misses whole file classes this repo actually uses: `admin.pass`, `*.apikey`, `*.key`, `*.pem`, `*_ed25519`, and `maas-api-key.txt` (which is manifest-declared in TWO sites yet would not be caught loose). PREDICTED EXPOSURE: `scripts/phase-03-admin-openrc.sh:32` writes `RC="${RC:-$HOME/admin-openrc}"` and `:72` writes `export OS_PASSWORD='$ADMIN_PASS'` into it -- a credential-bearing file in the HOME ROOT, outside any `*-creds/` folder, whose filename matches NONE of the sprawl globs. It does not exist yet (Stage 5 has not run), so no discovery sweep can flag it today; it is recorded here in advance so the mint is not missed when Stage 5 executes. NOTE the keystone admin password is CHARM-generated but OPERATOR-materialized (`:52` extracts it via `juju run keystone/leader get-admin-password`) -- the operator-ruled "materialization test" puts it firmly in scope. | measured read-only this session; `scripts/creds-audit.sh:72-73`; `scripts/phase-03-admin-openrc.sh:32,52,72`; D-137 research finding F4 | operator | **OPEN -- latent, becomes live at Stage 5.** Two remediations, both UNRULED and NOT built: widen the sprawl globs (cheap, independent of D-137), and add an expected-state row so the openrc credential is asserted rather than discovered. Until then, whoever runs Stage 5 phase-03 must consolidate `~/admin-openrc` into the site creds folder BY HAND at mint time. Count note: SEC-021/-022/-023 take open SEC rows 16 -> 19. Custody off-repo per D-069. | -| SEC-024 | 2026-07-26 | **`opentofu/terraform.tfstate.backup` is mode 0664 (group- and world-readable) and carries the Office1 MAAS API key in PLAINTEXT.** DOCFIX-175 records that `maas_api_key` reaches the OpenTofu state file in clear; the live `terraform.tfstate` is correctly 0600, but its automatically-written `.backup` sibling is 0664. MEASURED `stat -c '%n %a'` this session on the jumphost clone. FOUND BY the D-137 credential matrix on its first run after the committee fixes: the world-readable check had been gated to `custody=consolidated` rows (43 of 77 rows unchecked), and the tfstate siblings were not declared locations at all -- so neither the mode nor the file was visible to any control. The two `terraform.tfstate.pre-*` snapshots are the same class and carry the same key. | measured 2026-07-26; `creds-matrix.py --tier2` E2 WORLD-READABLE; DOCFIX-175; capture `docs/audit/d137-tier2-postcommittee-20260726.txt` | operator | **OPEN -- logged, NOT actioned (hard rule 1).** Remediation is a `chmod` on the jumphost plus a decision about whether the historical snapshots should be retained at all; both are mutations and neither is in this step's scope. Rotation of the underlying key remains the SEC-018/-019 obligation. Custody off-repo per D-069. | +| SEC-024 | 2026-07-26 | **`opentofu/terraform.tfstate.backup` carries the Office1 MAAS API key in PLAINTEXT at file mode 0664.** SEVERITY CORRECTED 2026-07-26 (measured, same day the row was opened): the original wording called it "group- and world-readable", which overstated it. The FILE BITS are 0664, but `opentofu/` is **0700** and `~` is 0750, so no other account can traverse to it and it is NOT effectively readable today. This is a DEFENCE-IN-DEPTH defect -- one `chmod` on the directory, a `cp -p` elsewhere, or an archive/extract would expose it -- not a live exposure. DOCFIX-175 records that `maas_api_key` reaches the OpenTofu state file in clear; the live `terraform.tfstate` is correctly 0600, but its automatically-written `.backup` sibling is 0664. MEASURED `stat -c '%n %a'` this session on the jumphost clone. FOUND BY the D-137 credential matrix on its first run after the committee fixes: the world-readable check had been gated to `custody=consolidated` rows (43 of 77 rows unchecked), and the tfstate siblings were not declared locations at all -- so neither the mode nor the file was visible to any control. Of the two `terraform.tfstate.pre-*` snapshots, `pre-G16-20260721` is also 0664 and `pre-G6-20260719` is 0600 -- an inconsistency consistent with an accidental umask at write time rather than a deliberate mode. Neither snapshot nor the backup is git-TRACKED (both match `.gitignore:72-73`), so SEC-004's public-repo exposure is NOT implicated. Other accounts exist on the host (`logxen` uid 1000, `libvirt-qemu`), which is why the directory mode is the only thing standing between them and the key. | measured 2026-07-26; `creds-matrix.py --tier2` E2 WORLD-READABLE; DOCFIX-175; capture `docs/audit/d137-tier2-postcommittee-20260726.txt` | operator | **OPEN -- logged, NOT actioned (hard rule 1).** Remediation is a `chmod` on the jumphost plus a decision about whether the historical snapshots should be retained at all; both are mutations and neither is in this step's scope. Rotation of the underlying key remains the SEC-018/-019 obligation. Custody off-repo per D-069. | **CREDENTIAL-LIFECYCLE POLICY LIVES IN D-137 -- this register does not carry the rule.** DEMOTED 2026-07-26 per D-137 sub-ruling 4 ("D-137 is the authority"): the standing