diff --git a/docs/secrets-and-pki.md b/docs/secrets-and-pki.md index 8e67197..5a8fdd2 100644 --- a/docs/secrets-and-pki.md +++ b/docs/secrets-and-pki.md @@ -9,7 +9,6 @@ | **Role** | Certificate authority (2 PKI mounts) + secret store (KV v2) | | **Distribution** | Vault Agent on every consuming host, AppRole auth, renders to local disk | | **Replaces** | A prior plaintext-`.env`-in-git secrets model | -| **Biggest known risk** | A non-expiring root bootstrap token — ranked #1 of 3 known gaps, tracked openly | ## Why Vault, and why self-hosted @@ -70,17 +69,6 @@ None of these ever call the Vault API themselves — every one of them goes thro | Alerting | Root-token use or permission-denied bursts trigger an alert through [`docs/notifications.md`](notifications.md)'s pipeline | | Cert renewal reliability | Technitium's DNS-over-TLS/DoH cert bundle auto-rebuilds whenever Vault Agent re-renders its certs, guarded to skip unless under 7 days from expiry. This closed a real prior gap: the rebuild never used to happen automatically, and an expired cert was silently served for 19 days before anyone caught it. | -## Known gaps, ranked by actual risk — not a flat list - -Ranking these mattered more than just listing them: a reader (or an attacker) needs to know which one to fix first, not just that gaps exist. - -1. **The bootstrap root token never expires.** Unlimited access, mints every AppRole, writes every policy. Encrypted at rest, but that doesn't help if the plaintext ever gets used somewhere it shouldn't. The single highest-value target in the whole system — tracked for replacement with a narrowly-scoped, short-TTL token. -2. **No SSH certificate signing.** Every host's SSH key is static, baked in at image build time (see [`docs/iac-pipeline.md`](iac-pipeline.md)), no rotation, no revocation short of rebuilding the host. Vault's own SSH secrets engine is the natural fit — a bigger lift than #1 since it touches every host's SSH config, not one central policy. -3. **Single-node storage, no replication.** One disk failure is a full outage and, without a recent backup, real data loss. This is the one gap [`docs/disaster-recovery.md`](disaster-recovery.md)'s VM-level backup layer actually covers — an availability concern, correctly triaged as lower priority than the two confidentiality risks above. - -> [!TIP] -> **Deliberately accepted, not gaps**: memory locking is disabled (normally a real concern — secrets could swap to disk under memory pressure — but this host has zero swap configured, so there's no actual leak vector regardless), and the Vault server's own configuration is intentionally kept out of the same automation that manages every client-side Vault Agent, so a mistake in that broader automation can't touch the root of trust itself. - ## Access model One policy per host, scoped to exactly what that host needs — no host's credentials can read another host's secrets. Every host-scoped policy follows the same shape: a shared grant for lightweight self-registration, plus a private grant with full read/write access only to that host's own namespace. Onboarding a new host copies this shape rather than reinventing it — and the shared grant does *not* automatically imply the private one, so both get verified before assuming a host is fully provisioned. This same "shared + private" pattern shows up again in [`docs/iac-pipeline.md`](iac-pipeline.md)'s tag-driven Ansible model — a deliberate consistency, not a coincidence.