ops-mason/plans/rapp-qonto-openbao-approle-bridge.md
tegwick cc9946c873 chore: reconcile ops-mason repository records
Assistant: codex
Assistant-Model: gpt-5.6-sol
Assistant-Session: 01a02878-7c21-7692-bcd6-ce2838c4b448
2026-08-22 11:32:34 +02:00

8.8 KiB

id demand_source consumer_repo credential_type status approved_by approved_at created updated
rapp-qonto-openbao-approle-bridge reef-railiance/workplans/REEF-RAILIANCE-WP-0003-T01 rapp-qonto openbao-approle-kv built Bernd Worsch 2026-07-27 2026-07-27 2026-08-22

Construction plan: rapp-qonto OpenBao AppRole bridge

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 directly to OpenBao and mint short-lived tokens; 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.

Topology verification established that this OpenBao instance runs on Coulombcore while the consumer runs on railiance01. Its existing Kubernetes auth mount validates Coulombcore service-account tokens and cannot validate a railiance01 token. A cross-cluster Kubernetes auth mount would additionally require stable TokenReview API reachability and a reviewer identity across the cluster boundary.

The existing read policy satisfies the requested data scope exactly. Reuse it; do not create the duplicate policy from CCR-2026-0009. Use a dedicated AppRole as a transitional bridge because it does not couple authentication to the wrong Kubernetes API. Retain same-cluster Kubernetes workload authentication as the target when secret custody and workload placement are unified.

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 AppRole external-secrets-rapp-qonto Dedicated non-human bridge; policy #1, token TTL 15 minutes, maximum TTL 30 minutes, bounded token uses
3 deliver Kubernetes Secret external-secrets/openbao-rapp-qonto-approle Holds only the AppRole role ID and secret ID for ESO; created through the approved Mason delivery path, never committed or logged
4 create ClusterSecretStore/openbao-rapp-qonto Uses AppRole #2 and secret #3 against the tenants KV v2 mount; namespace condition permits only rapp-qonto
5 modify rapp-qonto ExternalSecret Map remote fields API_USER -> QONTO_LOGIN and API_KEY -> QONTO_SECRET_KEY; no values enter Git or logs
6 retire proposed openbao-qonto-assistant static-token store Superseded before deployment; avoids a long-lived OpenBao token in a Kubernetes Secret
7 propose ops-warden catalog entry rapp-qonto-workload-kv Pointer to the built AppRole lane, warden_executes: false, initially draft
8 record migration residual Replace the AppRole bridge with same-cluster Kubernetes auth when OpenBao custody or workload placement is unified

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; maximum TTL is 30 minutes and token uses are bounded. 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.
  • Credential custody: the AppRole secret ID is a revocable bootstrap credential held only in a namespaced Kubernetes Secret accessible to ESO. It is not a Qonto credential and never enters Git, workplans, audit events, command output, or chat.
  • Migration: this bridge is intentionally transitional. A cross-cluster TokenReview dependency is rejected for now because it expands networking, reviewer-identity, and failure-domain coupling.

5. Executive summary

One-line ask: allow External Secrets on railiance01 to use a dedicated, revocable AppRole bridge to deliver the existing Binky Qonto read credential to rapp-qonto, issuing only short-lived OpenBao tokens.

Who/what gets access: a new AppRole named external-secrets-rapp-qonto. Its bootstrap identity is stored in external-secrets/openbao-rapp-qonto-approle and referenced only by the dedicated ClusterSecretStore. No application pod, human role, or unrelated store receives the identity.

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 AppRole login receives a 15-minute OpenBao token with a 30-minute maximum and bounded uses. The revocable AppRole secret ID persists for ESO continuity until rotated or the bridge is retired; no OpenBao token persists in Kubernetes.

Blast radius if the identity is abused: an attacker able to read the AppRole bootstrap Secret could mint bounded, short-lived tokens and read the Binky Qonto API credential. The OpenBao policy cannot read any sibling tenant secret. Revoke/rotate the AppRole secret ID immediately; rotate the downstream Qonto key too if it may have been read.

Cost to reverse: delete one AppRole, its bootstrap Kubernetes Secret, one ClusterSecretStore, and the rapp-qonto ExternalSecret/derived Secret. The existing human Qonto lane and underlying credential remain intact.

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.

Previous decision: the Kubernetes-auth design was approved by Bernd Worsch on 2026-07-27. That approval does not authorize this revised AppRole design.

6. Build result

Build attempted 2026-07-27 and stopped during verification. The OpenBao role was created, but the store login returned 403. Live topology inspection showed that bao.coulomb.social resolves to Coulombcore (92.205.130.254), while the ESO service account token belongs to railiance01 (92.205.62.239). The existing kubernetes/ auth mount therefore validates a different cluster.

The failed ClusterSecretStore, ExternalSecret, disposable namespace, and mis-targeted OpenBao role were removed without reading a secret value. No live object from the failed build remains.

The revised phase-2 recommendation is alternative 2: use the dedicated AppRole bridge described above until the OpenBao/cluster boundary is unified. The revised plan was explicitly approved by Bernd Worsch on 2026-07-27 (go on). Phase 4 may proceed.

7. Revised build result

Built and verified on 2026-07-27 through ops_mason.executor.build_approle_kv_lane:

  • reused policy workload-kv-read-binky-qonto-api, scoped to exactly tenants/binky/qonto-api;
  • created AppRole external-secrets-rapp-qonto with 15-minute token TTL, 30-minute maximum TTL, eight token uses, and a revocable secret ID;
  • delivered the role ID and secret ID directly into Kubernetes Secret external-secrets/openbao-rapp-qonto-approle; temporary local and remote delivery files were removed;
  • created namespace-restricted ClusterSecretStore/openbao-rapp-qonto;
  • synchronized ExternalSecret/rapp-qonto into Secret/rapp-qonto-runtime.

Metadata-only live verification reported the store Valid, the ExternalSecret SecretSynced, and exactly two keys in the derived Secret. No credential value was read, printed, committed, or recorded.

The bridge remains transitional. Replace it with same-cluster Kubernetes authentication when OpenBao custody and workload placement are unified.