Newer
Older
openstack-caracal-dc-dc / docs / changelog-20260715-creds-audit.md

2026-07-15 -- creds-audit: enforce per-site credential consolidation (SEC-009), so the DC0/DC1 spread cannot recur

Why

SEC-009 established the convention "ALL of a site's secrets live in ~/<site>-creds/ (0700, files 0600), no loose env files in ~." But nothing ENFORCED it, and it had two blind spots that a checklist alone would not catch:

  1. VM-minted secrets. A secret generated ON a site VM (office1-netbox's sandbox NetBox token at /root/netbox-secrets/api.token) has no forcing function to get pulled into the jumphost folder. It went un-consolidated until the C2 fidelity re-check needed it (SEC-009 addendum, 2026-07-15).
  2. Silent sprawl. A loose *.env/*.token in ~ outside a *-creds/ folder -- the ORIGINAL SEC-009 exposure -- had no standing detector.

Standing up DC0 and DC1 multiplies both (each mints MAAS API keys, per-DC NetBox tokens, node/edge SSH keys, OPNsense creds, tailscale authkeys on multiple VMs). The operator asked directly how we stop the spread recurring for the DCs. Answer: a manifest + an audit + a mint-time runbook step.

What was built

  • creds-manifests/<site>.manifest -- declares EXACTLY the secrets a site must hold: <filename> <octal-mode> <source>, where source is local or <vm>:<path> for VM-minted secrets (making the easy-to-miss class explicit and provenance-tracked). creds-manifests/vr1-office1.manifest seeded with 11 entries from the live folder; it audits CLEAN.
  • scripts/creds-audit.sh <site> -- verifies the live ~/<site>-creds/ against the manifest: folder is 0700, every entry present with its declared mode and non-empty; flags any UNDECLARED file in the folder; and scans the home root for SPRAWL (*.env/*.token/*authkey*/*appcred*/*-cred*.txt loose outside any *-creds/). Reads NO secret values -- metadata only (stat/size), verified by a harness assertion (T7: no cat/head/tail/od/xxd in the script). Env overrides CREDS_ROOT/MANIFEST_DIR make it fully offline-testable.
  • tests/creds-audit/run-tests.sh (7/7) -- synthetic folders under a tmp root: complete+correct -> CLEAN; missing (VM-sourced) entry -> fail; wrong mode -> fail; undeclared file -> fail; home-root sprawl -> fail; non-0700 folder -> fail; metadata-only. Auto-discovered by the gauntlet.
  • Runbook discipline -- runbooks/dc-dc-phase3-maas-enlist-deploy.md GATE now carries a non-negotiable close-out: consolidate + manifest each secret AT MINT TIME, and bash scripts/creds-audit.sh $DC MUST be CLEAN before a DC is declared done.
  • docs/security-ledger.md -- SEC-009 ENFORCEMENT note recording the above.

Verification

  • Harness: tests/creds-audit/run-tests.sh -> 7/7 PASS.
  • Live: bash scripts/creds-audit.sh vr1-office1 -> CLEAN (folder 0700, 11/11 entries present + correct + non-empty, no undeclared files, no sprawl). This also CONFIRMS the SEC-009 sandbox-token gap is now closed and tool-verified.
  • Gauntlet ALL GREEN; repo-lint 0 fail.

Revert

  • git rm scripts/creds-audit.sh tests/creds-audit/run-tests.sh creds-manifests/vr1-office1.manifest docs/changelog-20260715-creds-audit.md (remove creds-manifests/ if now empty).
  • git checkout HEAD -- docs/security-ledger.md runbooks/dc-dc-phase3-maas-enlist-deploy.md reverts the SEC-009 enforcement note and the runbook close-out.

Nothing here reads, moves, or mutates any secret; the audit is read-only metadata inspection and the manifest holds only filenames/modes/provenance.