--- id: rapp-qonto-openbao-kubernetes-lane demand_source: reef-railiance/workplans/REEF-RAILIANCE-WP-0003-T01 consumer_repo: rapp-qonto credential_type: openbao-kubernetes-kv status: reviewed approved_by: null approved_at: null created: "2026-07-27" updated: "2026-07-27" --- # Construction plan: rapp-qonto OpenBao Kubernetes lane ## 1. Demand `rapp-qonto` needs non-interactive, read-only delivery of the existing Binky Qonto API credential from `tenants/binky/qonto-api` into its dedicated Kubernetes namespace. External Secrets Operator should authenticate to OpenBao with Kubernetes workload identity; neither the application nor a ClusterSecretStore should hold a long-lived OpenBao token. ## 2. Existing-structure survey Live OpenBao state was checked on 2026-07-27: - auth mounts include `kubernetes/`; its configured API endpoint is the cluster-local `https://10.43.0.1:443`; - existing Kubernetes roles are `external-secrets-activity-core`, `external-secrets-issue-core`, and `external-secrets-reuse-surface`; - policy `workload-kv-read-binky-qonto-api` already exists and is scoped to the existing tenant credential; - the `tenants/` KV v2 mount and `tenants/binky/qonto-api` value already exist; this plan does not create or read the value; - existing cluster stores use an interim static-token pattern, despite the Kubernetes roles existing; - `CCR-2026-0009` proposed a duplicate `workload-kv-read-qonto-assistant` policy and the transitional `qonto-assistant` namespace/name. The existing read policy satisfies the requested data scope exactly. Reuse it; do not create the duplicate policy from CCR-2026-0009. A new authentication role is still required because no existing role should gain Qonto access and no existing Qonto lane is workload-authenticated. ## 3. Proposed changes | # | Action | Object | Reuse-vs-new rationale | |---|---|---|---| | 1 | reuse | policy `workload-kv-read-binky-qonto-api` | Already grants the exact required read scope; a second equivalent policy adds drift without isolation | | 2 | create | Kubernetes auth role `external-secrets-rapp-qonto` | New identity binding only; bound to service account `external-secrets` in namespace `external-secrets`, policy #1, token TTL 15 minutes | | 3 | create | `ClusterSecretStore/openbao-rapp-qonto` | Uses Vault Kubernetes auth role #2 and the `tenants` KV v2 mount; namespace condition permits only `rapp-qonto` | | 4 | modify | `rapp-qonto` ExternalSecret | Map remote fields `API_USER` -> `QONTO_LOGIN` and `API_KEY` -> `QONTO_SECRET_KEY`; no values enter Git or logs | | 5 | retire | proposed `openbao-qonto-assistant` static-token store | Superseded before deployment; avoids a long-lived OpenBao token in a Kubernetes Secret | | 6 | propose | ops-warden catalog entry `rapp-qonto-workload-kv` | Pointer to the built Kubernetes-auth lane, `warden_executes: false`, initially `draft` | No underlying Qonto secret is created, changed, printed, or rotated. ## 4. Review notes - **Naming:** steady-state `rapp-qonto` names replace the migration-only `qonto-assistant` names. - **TTL/scoping:** 15-minute tokens match existing External Secrets roles. The role binds only the ESO service account; the ClusterSecretStore condition restricts consumers to one namespace. - **Redundancy:** reuses the exact existing policy. This removes the duplicate policy proposed by CCR-2026-0009. - **Compaction:** the draft static-token Qonto store is retired before it is deployed. Existing unrelated stores are untouched. - **Consumer usability:** External Secrets performs authentication and delivery. The application consumes only a namespaced Kubernetes Secret. - **Posture:** the lane accesses a high-risk bank credential, but it is read-only and the runtime exposes no financial write operation. Production still requires egress, audit, revocation, and failure-domain gates. - **Build executor extension:** ops-mason currently builds AppRole lanes only. Phase 4 must add a narrowly tested Kubernetes-role operation with the same approval refusal and metadata-only audit guarantees before applying this plan. ## 5. Executive summary **One-line ask:** allow External Secrets on railiance01 to deliver the existing Binky Qonto read credential to `rapp-qonto` without storing a long-lived OpenBao token in Kubernetes. **Who/what gets access:** a new OpenBao Kubernetes role named `external-secrets-rapp-qonto`, usable only by the existing `external-secrets` service account in the `external-secrets` namespace. No application pod, human role, or unrelated service account receives OpenBao login rights. **To what:** read-only access through the existing `workload-kv-read-binky-qonto-api` policy to exactly `tenants/binky/qonto-api`. Store use is limited to ExternalSecret resources in the `rapp-qonto` namespace. **For how long:** each Kubernetes-auth login receives a 15-minute OpenBao token. There is no delivered AppRole secret ID and no long-lived OpenBao token stored in Kubernetes. The Kubernetes service-account identity remains valid while the ESO deployment and role binding exist. **Blast radius if the identity is abused:** an attacker able to act as the ESO service account and use this role could read the Binky Qonto API credential. The OpenBao policy cannot read any sibling tenant secret. The downstream Qonto key remains high-risk and must be rotated if exposed. **Cost to reverse:** delete one Kubernetes auth role, one ClusterSecretStore, and the `rapp-qonto` ExternalSecret/derived Secret. The existing human Qonto lane and underlying credential remain intact; no unrelated consumer changes. **Decision needed:** approve as proposed, reject, or send back to phase 1 with changes. Approval authorizes the structural access lane only, never disclosure or modification of the Qonto credential value.