secrets-and-pki: add real evidence for the asymmetric-trust and cert-renewal claims

Step 5 re-audit. Caddyfile contrast (skip-verify default vs Vault's
real cert verification block) shown as text, IP/domain genericized.
The cert-rebuild hook script's real 19-day-expiry story is shown as a
screenshot instead of a text excerpt - the script has a hardcoded P12
password in its body, so a screenshot with that one line blurred shows
the actual guard logic in full without a text-copy risk of the
password ever landing in a file.
This commit is contained in:
thesabear committed 2026-08-28 21:48:09 +03:00
1 parent 0186966476
commit f4ef56d229
2 files changed
+26 -1

No files matched your search

Binary file not shown.

After

Width:  |  Height:  |  Size: 164 KiB

+26 -1
View File
@@ -57,7 +57,28 @@ None of these ever call the Vault API themselves — every one of them goes thro
| Bootstrap auth | Rare, human-initiated | Operator uses a root token to mint new AppRole credentials or update policies — never part of any workload's runtime path | | Bootstrap auth | Rare, human-initiated | Operator uses a root token to mint new AppRole credentials or update policies — never part of any workload's runtime path |
> [!IMPORTANT] > [!IMPORTANT]
> **The one deliberately-asymmetric trust decision**: every other backend connection inside the internal zone described in [`docs/dns-naming.md`](dns-naming.md) skips certificate verification on the reverse-proxy hop (still encrypted, just doesn't check identity) — a pragmatic tradeoff for low-stakes internal traffic between containers that already share a trusted network. The connection to Vault is the single exception: the proxy verifies Vault's certificate for real, against a dedicated internal CA cert. Given that Vault is the root of trust for every other secret in the fleet, treating it identically to a media-server backend would have been the actual mistake — the asymmetry here is the correct call, not an inconsistency. > **The one deliberately-asymmetric trust decision**: every other backend connection inside the internal zone described in [`docs/dns-naming.md`](dns-naming.md) skips certificate verification on the reverse-proxy hop (still encrypted, just doesn't check identity) — a pragmatic tradeoff for low-stakes internal traffic between containers that already share a trusted network. The connection to Vault is the single exception: the proxy verifies Vault's certificate for real, against a dedicated internal CA cert. Given that Vault is the root of trust for every other secret in the fleet, treating it identically to a media-server backend would have been the actual mistake — the asymmetry here is the correct call, not an inconsistency. This is the real config contrast, not a description of it — every other backend uses this:
>
> ```
> handle @trusted {
> reverse_proxy https://{args.0} {
> transport http { tls_insecure_skip_verify }
> }
> }
> ```
>
> Vault's own block, specifically, does not:
>
> ```
> handle @trusted {
> reverse_proxy https://[Vault's IP]:8200 {
> transport http {
> tls_trust_pool file { pem_file /etc/caddy/certs/pki_int_ca.crt }
> tls_server_name vault.[app-to-app zone]
> }
> }
> }
> ```
## What's protected ## What's protected
@@ -69,6 +90,10 @@ 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 | | 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. | | 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. |
This is the real hook script, not a description of it — the full guard logic, the actual PKCS12 rebuild, and the service restart it triggers (password redacted, everything else genuine):
![The real technitium-rebuild-p12.sh script: the 19-day-expired-cert incident documented in the script's own comments, the 7-day expiry guard, and the actual PKCS12 rebuild + dns.service restart](../assets/secrets-and-pki-cert-rebuild-hook.png)
## Access model ## 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. 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.