Newer
Older
openstack-caracal-dc-dc / docs / audit / creds-creation-points-20260725.md

Credential creation-point inventory -- 2026-07-25

Read-only research capture for the D-137 credential-matrix design. No credential file was opened; every row below is a file:line reference to CODE or RUNBOOK PROSE that mints, materializes, or uploads credential material.

Classification used: MINT (new secret material comes into existence), MATERIALIZE (an existing secret is fetched/copied/written somewhere new), UPLOAD (public key registered -- no secret created). Only MINT rows are creation points; the other two classes were checked and excluded deliberately.

Operator-ruled scope for the matrix (this session): the materialization test -- in-matrix if a credential is ever written to a file, presented by a human, or consumed out-of-band, regardless of who generated it.

Counts

Class Count Where
Scripted MINT (repo code generates the secret) 25 rows scripts/
Runbook/prose MINT (operator runs by hand, no script owns it) 30 rows runbooks/, clientdocs/
Declared secrets with NO creation point anywhere in the repo 12 see below
Charm/platform-minted at bundle apply summarised bundle.yaml deploy
MATERIALIZE / UPLOAD (excluded, verified non-creating) 20+ see source agent record

FINDING 1 -- twelve declared secrets have NO mint command anywhere

This is the most consequential result and it qualifies the "creation points are already known" premise: for twelve standing credentials, they are not.

grep -rnI "ssh-keygen" . returns ZERO hits repo-wide (searched everything except .git/.terraform/tfstate/tfplan, including tests/, docs/archive/, clientdocs/, .claude/). There is likewise no mkpasswd, htpasswd, password_hash, pwgen, uuidgen, nor any Terraform random_password / tls_private_key. Yet the manifests declare:

Credential Declared at Only provenance record
office1_svc_ed25519{,.pub} vr1-office1.manifest:30-31 vr1-office1-as-built.md:126 (SEC-007)
vr1-dc0_svc_ed25519{,.pub} vr1-dc0.manifest:16-17 a manifest COMMENT (:10-12)
vr1-dc1_svc_ed25519{,.pub} vr1-dc1.manifest:16-17 a manifest COMMENT (:10-12)
vr1-dc0-edge_ed25519{,.pub} vr1-dc0.manifest:22-23 manifest comment (backfilled 2026-07-25)
vr1-dc1-edge_ed25519{,.pub} vr1-dc1.manifest:21-22 manifest comment (:18-20)
vr1-dc1-maas-power_ed25519{,.pub} vr1-dc1.manifest:29-30 manifest comment (:26-28, SEC-016)
opnsense-root-password vr1-office1.manifest:34 vr1-office1-as-built.md:128, SEC-007 -- HUMAN GUI login
opnsense-root-hash vr1-office1.manifest:33 same (bcrypt of the above, for config.xml)
tailscale-authkey.txt vr1-office1.manifest:36 SEC-008 -- operator-supplied, third-party control server (correctly has no repo mint)
vr1-netbox.env vr1-office1.manifest:37 minted on the upstream apex; SEC-006 (BURNED)
vr1-office1.env / vr1-stage1.env .manifest:39-40 vr1-office1-as-built.md:132-133

Six SSH keypairs and the OPNsense root password/hash are pure operator-terminal mints whose only record is a manifest comment. Consequence: they are not reproducible. If the jumphost is rebuilt, no repo artifact states how to recreate them -- which is a Roosevelt-transfer defect, not merely a hygiene one. The tailscale key is genuinely third-party and correctly has no repo mint.

FINDING 2 -- credential directories OUTSIDE the *-creds/ convention

SEC-009 says all site secrets live in ~/<site>-creds/. The inventory shows at least three other jumphost directories holding credential material, none of which creds-audit covers (it checks ~/<site>-creds/ folders plus a loose-FILE sprawl glob in $HOME -- it never inspects these DIRECTORIES):

  • ~/vault-init/init.txt -- Vault 5 unseal shares + root token, the cloud's entire root of trust (runbooks/phase-02-vault-bringup.md:71, irreversible one-shot).
  • ~/octavia-pki/ -- 8 artifacts: 2 CA passphrases, 2 CA private keys, 2 CA certs, the controller key, the signed bundle (runbooks/phase-01-bundle-deploy.md:299-410).
  • ~/tenant-<client>/ -- per-tenant domain-admin / cluster / svc passwords, an application credential, and a Nova private key (scripts/tenant-onboard.sh:70-71, 94-95, 141, 167).

All three are named in CLAUDE.md as secret locations, so they are KNOWN -- they are simply outside the control. This materially extends SEC-023.

Related: runbooks/phase-01-bundle-deploy.md:391-410 writes overlays/octavia-pki.yaml containing base64 CA key blobs plus the issuing-CA passphrase in plaintext, inside the repo clone (gitignored). Given SEC-004 (repo currently PUBLIC), that gitignore is the only thing standing between a CA private key and publication.

FINDING 3 -- highest-concentration mint sites (matrix seeding order)

  1. scripts/site-headend-install.sh -- 7 mints (:418 primitive, :430 DB pass, :445 rack-enrollment secret, :450/:452 admin password, :453 API key, :485 LXD trust). Every Office1/DC-region MAAS + LXD secret originates here.
  2. scripts/tenant-onboard.sh -- 7 mints, the entire tenant identity set.
  3. runbooks/phase-01-bundle-deploy.md:299-410 -- 8 Octavia PKI artifacts.
  4. runbooks/tenant-onboarding-v2-DRAFT.md:162-320 -- 6 mints DUPLICATING #2 (two creation locations, one credential set -- do not double-count).
  5. runbooks/phase-02-vault-bringup.md:71-144 -- 4 mints incl. the root of trust.
  6. runbooks/dc-dc-phase4-juju-bundle-per-dc.md:119-151 -- 4 mints PER DC.

FINDING 4 -- corroborations of already-recorded defects

  • dc-dc-phase4-juju-bundle-per-dc.md:128 mints juju-<dc> via maas createadmin with no --password, so MAAS prompts and the value is stored nowhere -- independently confirming the SEC-020 finding about those two superusers.
  • site-headend-install.sh:452 uses --password in argv, the exposure SEC-020 flagged and which this session's operator mint avoided via stdin.
  • scripts/phase-03-admin-openrc.sh:46-52 is classified MATERIALIZE (charm-generated, operator-materialized) -- exactly the case the ruled materialization test captures and a generation-based test would have missed (SEC-023).

What this means for the matrix

The matrix must carry a mint-ref column that can hold three provenance kinds, not one: script:line, runbook:step, and operator-terminal (undocumented). Finding 1 means a meaningful fraction of today's estate is the third kind, so a checker that requires every row to name executable code would fail 12 rows on day one. Those rows are the backlog the matrix exists to surface -- they should be admitted as operator-terminal and tracked as a debt to convert, not treated as parse errors.

Source: three read-only Explore agents, 2026-07-25. Full per-row tables (55 MINT rows with file:line, host, destination, stage, and human/service classification) are in the session transcript; the structural findings and all counts are reproduced above.