Plan user-engine runtime secret custody

This commit is contained in:
tegwick 2026-07-30 00:22:35 +02:00
parent 26cb011141
commit 8578a0074d

View file

@ -0,0 +1,99 @@
---
id: user-engine-openbao-runtime-custody
demand_source: net-kingdom/workplans/NK-WP-0023-T05
consumer_repo: net-kingdom
credential_type: openbao-approle-kv
status: proposed
created: "2026-07-30"
updated: "2026-07-30"
---
# Construction plan: user-engine OpenBao runtime custody
## 1. Demand
The live portal edge marker and identity-provisioner service token are
bootstrap Kubernetes Secrets without an authoritative rotation source.
`user-engine` and `identity-provisioner` need the same runtime behavior with
OpenBao as authority and External Secrets Operator as delivery mechanism.
## 2. Existing-structure survey
- `Secret/user-engine-runtime` in namespace `user-engine` contains only
`proxy-secret`.
- `Secret/identity-provisioner-client` in namespace `user-engine` and
`Secret/identity-provisioner-token` in namespace `sso` carry the two copies
of the provisioner service token.
- The applications already consume only Secret references; no code or
Deployment interface change is required.
- The established `rapp-qonto` bridge proves that railiance01 External Secrets
must currently use AppRole against the OpenBao instance on CoulombCore;
railiance01 service-account tokens cannot use that instance's Kubernetes
auth mount.
- Existing activity-core stores and policies are unrelated and must not be
widened or reused across this identity boundary.
## 3. Proposed changes
| # | Action | Object | Rationale |
|---|---|---|---|
| 1 | create | KV path `platform/workloads/user-engine/runtime` | One authoritative workload path with fields `PROXY_SECRET` and `PROVISIONER_TOKEN` |
| 2 | create | policy `workload-kv-read-user-engine-runtime` | Read and metadata access only to the exact KV v2 path |
| 3 | create | AppRoles `external-secrets-user-engine` and `external-secrets-sso-user-engine` | Separate revocable bootstrap identities for the two consuming namespaces |
| 4 | deliver | AppRole bootstrap Secrets in `external-secrets` | Write-only Mason delivery; 15-minute tokens, 30-minute maximum, eight uses |
| 5 | create | namespace-restricted ClusterSecretStores for `user-engine` and `sso` | Prevent either identity from becoming a general cross-namespace store |
| 6 | create | three ExternalSecrets targeting the existing Secret names | Preserve application interfaces while changing authority |
| 7 | migrate | existing values into the KV path through a wrapped platform-admin process | Read source and write destination in one process; never return either value |
| 8 | verify | metadata, rollout, positive/negative authentication, and rotation rehearsal | Record field names, readiness, denial, and rollout only |
| 9 | retire | manual Secret-copy/bootstrap procedure | ExternalSecret becomes the sole writer and rotation path |
The migration does not mint a new application capability. It moves two
existing values into governed custody. Same-cluster Kubernetes auth remains
the target after OpenBao moves to reef-railiance.
## 4. Review and optimization
- Separate AppRoles avoid widening one namespace identity across `sso` and
`user-engine`.
- Both policies resolve to one exact path, avoiding duplicate values and
divergent rotation.
- Existing Secret names and keys remain stable, limiting rollout risk.
- A wrapped migration process is required because printing, downloading, or
staging either value would create a new disclosure path.
- Reversal deletes the ExternalSecrets/stores/AppRoles and restores the
protected pre-migration Secret snapshots during the bounded rollback window.
- Rotation changes the OpenBao fields, waits for all three ExternalSecrets,
and rolls the provisioner and portal together; failed synchronization stops
before either Deployment changes.
## 5. Executive summary
**One-line ask:** approve moving the existing user-engine edge marker and
provisioner token into one exact OpenBao workload path, delivered by two
namespace-specific, revocable External Secrets AppRoles.
**Who gets access:** only External Secrets through
`external-secrets-user-engine` and `external-secrets-sso-user-engine`.
Application pods continue to receive only their existing namespaced Secrets.
**To what:** read-only access to exactly
`platform/workloads/user-engine/runtime`; no sibling workload or tenant path.
**For how long:** each AppRole login receives a 15-minute token with a
30-minute maximum and eight uses. AppRole secret IDs persist for ESO continuity
until rotated or same-cluster Kubernetes auth replaces the bridge.
**Blast radius:** compromise permits impersonating the internal provisioner
client and/or the portal trusted marker, but grants no direct OpenBao sibling
read, directory administrator password, user password, MFA seed, or platform
role. Revoke both AppRole secret IDs, rotate both runtime values, and roll the
two workloads.
**Cost to reverse:** delete three ExternalSecrets, two stores, two AppRoles,
their bootstrap Secrets, and one exact policy/path; restore the protected
pre-migration Kubernetes Secret snapshots. No database or identity record
changes.
**Decision needed:** approve as proposed, reject, or return with changes.
Approval authorizes custody migration and bounded rotation rehearsal, never
printing or otherwise disclosing either value.