From 917d3a1687e8f6e82d7416cb3c946c4750eeb8a0 Mon Sep 17 00:00:00 2001 From: codex Date: Fri, 14 Aug 2026 13:15:30 +0200 Subject: [PATCH 01/11] workplan: RAILIANCE-WP-0017 enforce consumption mode Residual of resource-control RESOURCE-WP-0005: refuse new orders that would exceed a restricted entity's published allowance. --- WORK-RECORDS.md | 2 + ...CE-WP-0017-consumption-mode-enforcement.md | 55 +++++++++++++++++++ 2 files changed, 57 insertions(+) create mode 100644 workplans/RAILIANCE-WP-0017-consumption-mode-enforcement.md diff --git a/WORK-RECORDS.md b/WORK-RECORDS.md index c90fa24..eb451a5 100644 --- a/WORK-RECORDS.md +++ b/WORK-RECORDS.md @@ -19,6 +19,7 @@ | workplan | RAILIANCE-WP-0015 | finished | — | workplans/RAILIANCE-WP-0015-platform-rapp-consistency.md | | workplan | RAILIANCE-WP-0016 | finished | — | workplans/RAILIANCE-WP-0016-apps-pg-resource-evidence.md | | workplan | RAILIANCE-WP-0016 | active | — | workplans/RAILIANCE-WP-0016-architecture-cleanup-backlog.md | +| workplan | RAILIANCE-WP-0017 | proposed | — | workplans/RAILIANCE-WP-0017-consumption-mode-enforcement.md | | task | RAILIANCE-WP-0005-T01 | done | — | workplans/RAILIANCE-WP-0005-credential-request-and-lease-broker.md | | task | RAILIANCE-WP-0005-T02 | done | — | workplans/RAILIANCE-WP-0005-credential-request-and-lease-broker.md | | task | RAILIANCE-WP-0005-T03 | done | — | workplans/RAILIANCE-WP-0005-credential-request-and-lease-broker.md | @@ -78,3 +79,4 @@ | task | RAILIANCE-WP-0016-T03 | done | — | workplans/RAILIANCE-WP-0016-architecture-cleanup-backlog.md | | task | RAILIANCE-WP-0016-T04 | done | — | workplans/RAILIANCE-WP-0016-architecture-cleanup-backlog.md | | task | RAILIANCE-WP-0016-T05 | todo | — | workplans/RAILIANCE-WP-0016-architecture-cleanup-backlog.md | +| task | RAILIANCE-WP-0017-T01 | todo | — | workplans/RAILIANCE-WP-0017-consumption-mode-enforcement.md | diff --git a/workplans/RAILIANCE-WP-0017-consumption-mode-enforcement.md b/workplans/RAILIANCE-WP-0017-consumption-mode-enforcement.md new file mode 100644 index 0000000..90a408d --- /dev/null +++ b/workplans/RAILIANCE-WP-0017-consumption-mode-enforcement.md @@ -0,0 +1,55 @@ +--- +id: RAILIANCE-WP-0017 +type: workplan +title: "Enforce resource-control consumption mode" +domain: financials +repo: railiance-platform +status: proposed +owner: codex +topic_slug: railiance +created: "2026-08-14" +updated: "2026-08-14" +related: + - RESOURCE-WP-0005 +origin: residual +origin_ref: RESOURCE-WP-0005 +state_hub_workstream_id: "f33be601-51b5-4bcf-82bf-31f8dba59b9f" +--- + +# RAILIANCE-WP-0017 — enforce consumption mode + +## Goal + +Consume the `open` / `restricted` signal published by +`resource-control` and enforce it on new orders and elastic usage. +`resource-control` does not throttle the cluster. + +Origin: `RESOURCE-WP-0005-T07`. Terms: +`resource-control/docs/TermsOfResourceProcurement.md` § 11. +Runbook: `resource-control/docs/resource-procurement-facility.md`. + +## Boundaries + +- Restricted monthly new-consumption is €50 transfer price minus that + month's interest. At the default €1 000 overdue balance that is + €45.83 transfer price / €38.19 delivered cost. +- Already-committed provider contracts are flagged to human financial + authority, not cancelled (OQ-2). +- Safety paths (backup, identity, ingress needed to avoid data loss) + may continue; overage is an exception line, not a hidden charge. + +## T01 — Subscribe to the consumption-mode signal + +```task +id: RAILIANCE-WP-0017-T01 +status: todo +priority: high +state_hub_task_id: "1c872918-0872-4b2a-8596-89b106239e54" +``` + +Define how platform procurement and admission read the latest +per-entity consumption mode and remaining transfer-price allowance for +the current period. + +Done when a restricted entity cannot place a new order that would +exceed the published allowance, and an `open` entity is unchanged. From 6e744b0adbfc9ed6d749c73536ed11514ff08643 Mon Sep 17 00:00:00 2001 From: codex Date: Fri, 14 Aug 2026 14:29:19 +0200 Subject: [PATCH 02/11] docs: cite RMASTER-WP ids for railiance-master workplans --- workplans/RAILIANCE-WP-0012-rapp-openbao-extraction-boundary.md | 2 +- workplans/RAILIANCE-WP-0015-platform-rapp-consistency.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/workplans/RAILIANCE-WP-0012-rapp-openbao-extraction-boundary.md b/workplans/RAILIANCE-WP-0012-rapp-openbao-extraction-boundary.md index 8dacbfe..76c86dc 100644 --- a/workplans/RAILIANCE-WP-0012-rapp-openbao-extraction-boundary.md +++ b/workplans/RAILIANCE-WP-0012-rapp-openbao-extraction-boundary.md @@ -127,4 +127,4 @@ copies without losing source-of-truth clarity. ## Notes This workplan is repo-local. The framework-level sequencing stays in -`railiance-master/workplans/RAILIANCE-WP-0017-rail-rapp-reef-repo-separation.md`. +`railiance-master/workplans/RMASTER-WP-0017-rail-rapp-reef-repo-separation.md`. diff --git a/workplans/RAILIANCE-WP-0015-platform-rapp-consistency.md b/workplans/RAILIANCE-WP-0015-platform-rapp-consistency.md index 1eb0861..f8cd4fe 100644 --- a/workplans/RAILIANCE-WP-0015-platform-rapp-consistency.md +++ b/workplans/RAILIANCE-WP-0015-platform-rapp-consistency.md @@ -37,7 +37,7 @@ conforms, and to supply the platform-service reference shape. Surveyed 2026-08-11 against the live cluster and all six family repos. The concepts are in better shape than their enforcement. The four families are -cleanly defined and `RAILIANCE-WP-0018`/`WP-0019` in `railiance-master` are both +cleanly defined and `RMASTER-WP-0018`/`RMASTER-WP-0019` in `railiance-master` are both `finished`. Nothing, however, validates a declaration file, so the model has been drifting quietly since the first wave. From 5a0c2da50157ea8f02d0704519226f1318e1720c Mon Sep 17 00:00:00 2001 From: codex Date: Fri, 14 Aug 2026 16:58:44 +0200 Subject: [PATCH 03/11] ccr: CCR-2026-0011 Scaleway bootstrap key lane Reserve platform/workloads/railiance/scaleway/bootstrap for the WP-0002 bucket create. Founder puts the value; agent does not accept it in chat. --- ...011-scaleway-object-storage-bootstrap.yaml | 68 +++++++++++++++++++ 1 file changed, 68 insertions(+) create mode 100644 credential-change-requests/CCR-2026-0011-scaleway-object-storage-bootstrap.yaml diff --git a/credential-change-requests/CCR-2026-0011-scaleway-object-storage-bootstrap.yaml b/credential-change-requests/CCR-2026-0011-scaleway-object-storage-bootstrap.yaml new file mode 100644 index 0000000..533a629 --- /dev/null +++ b/credential-change-requests/CCR-2026-0011-scaleway-object-storage-bootstrap.yaml @@ -0,0 +1,68 @@ +id: CCR-2026-0011 +kind: credential-change-request +schema_version: 1 +request_type: workload-kv-read +title: Scaleway bootstrap API key for reef-storage / WP-0002 bucket create +status: proposed +created: '2026-08-14' +updated: '2026-08-14' +requester: + agent: grok + reason: >- + RESOURCE-WP-0002 T03 is human-approved to create a private Scaleway + Multi-AZ bucket in nl-ams. This session has no Scaleway CLI profile. + Founder will provision a bootstrap org/project API key into OpenBao; + the agent reads it only to create the bucket and then writes the + scoped bucket key to a separate path. Values must not appear in Git + or chat. +review: + required: true + required_approvers: + - platform-operator + comments: + - at: '2026-08-14T00:00:00+00:00' + reviewer: bernd.worsch + decision: approved + comment: >- + Approved in chat (Grok session): put Scaleway bootstrap key in + OpenBao first, then create the bucket. +target: + domain: financials + tenant: railiance + workload: scaleway-bootstrap + environment: production + purpose: Create reef-storage object-store members; not the Barman runtime key +openbao: + mount: platform + kv_path: platform/workloads/railiance/scaleway/bootstrap + fields: + - ACCESS_KEY + - SECRET_KEY + - DEFAULT_ORGANIZATION_ID + - DEFAULT_PROJECT_ID + policy_name: operator-kv-scaleway-bootstrap +access_frontdoor: + type: operator-local + catalog_id: scaleway-bootstrap + selector: scaleway bootstrap api + command: bao kv put platform/workloads/railiance/scaleway/bootstrap + resolvable: false + readiness: waiting-on-founder-put +delivery: + surface: operator-workstation + target: reef-storage/tools/create-platform-audit-bucket.sh (reads, never prints) +risk: + classification: high + notes: + - Bootstrap key can create projects/buckets; rotate or delete after scoped key exists. + - Scoped Barman key is a different path: platform/workloads/railiance/backup/platform-pg-backup-s3 + - Values must not appear in Git, State Hub, or chat. +verification: + positive: + - Field names present on the KV path; values not printed. + negative: + - default-policy token denied on the data path. +lifecycle: + deactivate: Delete bootstrap key at Scaleway after the scoped bucket key works. + rotate: Put a new bootstrap key; do not reuse the Barman key. + compromised: Revoke both Scaleway keys; rotate bucket policy. From 015f0e43a9f0c4fef00bf82c433bf29fc032b7b5 Mon Sep 17 00:00:00 2001 From: codex Date: Fri, 14 Aug 2026 17:40:09 +0200 Subject: [PATCH 04/11] ccr: CCR-2026-0011 waiting on UI replace of placeholders --- .../CCR-2026-0011-scaleway-object-storage-bootstrap.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/credential-change-requests/CCR-2026-0011-scaleway-object-storage-bootstrap.yaml b/credential-change-requests/CCR-2026-0011-scaleway-object-storage-bootstrap.yaml index 533a629..877860c 100644 --- a/credential-change-requests/CCR-2026-0011-scaleway-object-storage-bootstrap.yaml +++ b/credential-change-requests/CCR-2026-0011-scaleway-object-storage-bootstrap.yaml @@ -3,7 +3,7 @@ kind: credential-change-request schema_version: 1 request_type: workload-kv-read title: Scaleway bootstrap API key for reef-storage / WP-0002 bucket create -status: proposed +status: apply_pending created: '2026-08-14' updated: '2026-08-14' requester: @@ -47,7 +47,7 @@ access_frontdoor: selector: scaleway bootstrap api command: bao kv put platform/workloads/railiance/scaleway/bootstrap resolvable: false - readiness: waiting-on-founder-put + readiness: waiting-on-ui-replace-of-xxx-placeholders delivery: surface: operator-workstation target: reef-storage/tools/create-platform-audit-bucket.sh (reads, never prints) From 276925863117135e24aee3ef6ee5b8b5124f646c Mon Sep 17 00:00:00 2001 From: codex Date: Fri, 14 Aug 2026 19:01:08 +0200 Subject: [PATCH 05/11] feat: propose CCR-2026-0012 for platform-pg Barman key Workload KV path and ESO drafts for Secret platform-pg-backup-s3. Founder mints the project-scoped Scaleway application; values stay out of git. --- ...platform-pg-backup.clustersecretstore.yaml | 32 ++++++ .../CCR-2026-0012-platform-pg-backup-s3.yaml | 103 ++++++++++++++++++ docs/put-platform-pg-backup-s3.md | 72 ++++++++++++ docs/rapp-credential-lane-binding.md | 2 +- ...workload-kv-read-platform-pg-backup-s3.hcl | 11 ++ 5 files changed, 219 insertions(+), 1 deletion(-) create mode 100644 argocd/platform-addons/openbao-secretstore/openbao-platform-pg-backup.clustersecretstore.yaml create mode 100644 credential-change-requests/CCR-2026-0012-platform-pg-backup-s3.yaml create mode 100644 docs/put-platform-pg-backup-s3.md create mode 100644 openbao/policies/workload-kv-read-platform-pg-backup-s3.hcl diff --git a/argocd/platform-addons/openbao-secretstore/openbao-platform-pg-backup.clustersecretstore.yaml b/argocd/platform-addons/openbao-secretstore/openbao-platform-pg-backup.clustersecretstore.yaml new file mode 100644 index 0000000..9f5fbc9 --- /dev/null +++ b/argocd/platform-addons/openbao-secretstore/openbao-platform-pg-backup.clustersecretstore.yaml @@ -0,0 +1,32 @@ +# DRAFT — CCR-2026-0012. Do not apply until the CCR is approved and the +# KV values are real. Deploy on railiance01 (databases lives there), not +# the CoulombCore ArgoCD kustomization. +# +# Prereq: ESO can authenticate to OpenBao with policy +# workload-kv-read-platform-pg-backup-s3. Interim railiance01 identity +# may be AppRole or a child token; Kubernetes auth role +# external-secrets-platform-pg-backup is the CCR-declared steady state. +apiVersion: external-secrets.io/v1 +kind: ClusterSecretStore +metadata: + name: openbao-platform-pg-backup + labels: + app.kubernetes.io/part-of: railiance-gitops + railiance-platform/component: external-secrets + app.kubernetes.io/name: platform-pg-backup +spec: + provider: + vault: + server: http://openbao.openbao.svc:8200 + path: platform + version: v2 + auth: + kubernetes: + mountPath: kubernetes + role: external-secrets-platform-pg-backup + serviceAccountRef: + name: external-secrets + namespace: external-secrets + conditions: + - namespaces: + - databases diff --git a/credential-change-requests/CCR-2026-0012-platform-pg-backup-s3.yaml b/credential-change-requests/CCR-2026-0012-platform-pg-backup-s3.yaml new file mode 100644 index 0000000..2b7206a --- /dev/null +++ b/credential-change-requests/CCR-2026-0012-platform-pg-backup-s3.yaml @@ -0,0 +1,103 @@ +id: CCR-2026-0012 +kind: credential-change-request +schema_version: 1 +request_type: workload-kv-read +title: Scaleway scoped Barman key for platform-pg backup +status: proposed +created: '2026-08-14' +updated: '2026-08-14' +requester: + agent: grok + reason: >- + RESOURCE-WP-0002 T04 needs a renewable, revocable, bucket-and-prefix + scoped Scaleway key in OpenBao so railiance-platform can vend Secret + platform-pg-backup-s3 into the databases namespace. The bootstrap key + (CCR-2026-0011) can create buckets and must not be the runtime key. +review: + required: true + required_approvers: + - platform-operator + comments: [] +target: + domain: financials + tenant: railiance + workload: platform-pg-backup + rapp: rapp-postgres + environment: production + purpose: >- + CNPG/Barman object-store credentials for platform-pg WAL and base + backups. Secret keys ACCESS_KEY_ID and ACCESS_SECRET_KEY only. + Endpoint, bucket, prefix, and region stay on reef-storage. +openbao: + mount: platform + kv_path: platform/workloads/railiance/backup/platform-pg-backup-s3 + fields: + - ACCESS_KEY_ID + - ACCESS_SECRET_KEY + - APPLICATION_ID + policy_name: workload-kv-read-platform-pg-backup-s3 + policy_file: openbao/policies/workload-kv-read-platform-pg-backup-s3.hcl + auth: + method: kubernetes + mount: kubernetes + role: external-secrets-platform-pg-backup + bound_claims: + service_account_names: + - external-secrets + service_account_namespaces: + - external-secrets + bound_claims_confirmed: false + policies: + - workload-kv-read-platform-pg-backup-s3 + ttl: 15m +access_frontdoor: + type: ops-warden + catalog_id: platform-pg-backup-s3 + selector: platform-pg Barman Scaleway key + command: warden access platform-pg-backup-s3 --fetch ACCESS_KEY_ID + resolvable: false + readiness: pending-review +delivery: + surface: external-secrets + target: >- + ClusterSecretStore openbao-platform-pg-backup (namespace condition + databases) → ExternalSecret databases/platform-pg-backup-s3 → Secret + platform-pg-backup-s3 with keys ACCESS_KEY_ID and ACCESS_SECRET_KEY. + Drafts live under railiance-platform/argocd/platform-addons/openbao-secretstore/ + and rapp-postgres/helm/platform-pg-backup-s3.externalsecret.yaml. + Do not apply until this CCR is approved and the KV values are real. +risk: + classification: high + notes: + - The runtime key can write and delete objects under the backup prefix. + Compromise can destroy recovery points or fill the bucket. + - The bootstrap key (CCR-2026-0011) must be revoked after this key works. + - Do not enable continuous WAL archiving until the empty-archive preflight + passes (RESOURCE-WP-0002 T05). + - Values must not appear in Git, State Hub, logs, or chat. +verification: + positive: + - Field names present on the KV path; values not printed. + - An approved databases-namespace ExternalSecret can sync ACCESS_KEY_ID + and ACCESS_SECRET_KEY to Secret platform-pg-backup-s3. + - The scoped key can list/put/delete only under prefix platform-pg/. + negative: + - A token without this policy cannot read the KV path. + - A namespace outside the ClusterSecretStore condition cannot use the store. + - A second, unused key (or revoked key) cannot access the bucket. + - The scoped key cannot list sibling buckets or create compute resources. + activation_conditions: + - Platform-operator approves this CCR. + - Founder creates the Scaleway IAM application and project-scoped + Object Storage key, then replaces OpenBao placeholders. + - ESO store and ExternalSecret applied only after values are real. +lifecycle: + deactivate: Disable the catalog entry, delete the ExternalSecret, revoke + the Scaleway API key, delete the IAM application. + rotate: Overlap-first. Put a new key in OpenBao, wait for ESO refresh, + revoke the old Scaleway key. + compromised: Revoke both the Barman key and the bootstrap key at Scaleway, + rotate this path, review bucket contents, open incident follow-up. +state_hub: + workplan_id: RESOURCE-WP-0002 + task_id: RESOURCE-WP-0002-T04 diff --git a/docs/put-platform-pg-backup-s3.md b/docs/put-platform-pg-backup-s3.md new file mode 100644 index 0000000..aa1bf8c --- /dev/null +++ b/docs/put-platform-pg-backup-s3.md @@ -0,0 +1,72 @@ +# Put the scoped Barman key (founder, local only) + +Do this on a trusted terminal. **Do not paste ACCESS_KEY_ID or +ACCESS_SECRET_KEY into chat, Git, or State Hub.** + +CCR: `credential-change-requests/CCR-2026-0012-platform-pg-backup-s3.yaml` +Path: `platform/workloads/railiance/backup/platform-pg-backup-s3` + +This is **not** the bootstrap key (`…/scaleway/bootstrap`). That key +created the bucket. This key is the CNPG/Barman runtime identity. + +## 1. Create a dedicated IAM application + +In [console.scaleway.com](https://console.scaleway.com) → IAM → Applications: + +1. Create application `railiance-barman-platform-pg`. +2. Create an API key **on that application** (not on your user): + - Description: `platform-pg Barman runtime` + - **Preferred Project for Object Storage:** the project that owns + bucket `railiance-platform-pg-backup` +3. Copy the access key and secret key into a local scratch file + (`chmod 600`). The secret is shown once. + +## 2. Attach a project-scoped Object Storage policy + +IAM → Policies → create `railiance-barman-platform-pg-objects`: + +- Principal: the application from step 1 +- Scope: **that one project**, not the whole Organization +- Permission sets, if the console lists them: + - `ObjectStorageBucketsRead` + - `ObjectStorageObjectsRead` + - `ObjectStorageObjectsWrite` + - `ObjectStorageObjectsDelete` +- If those sets are not listed, `ObjectStorageFullAccess` **on this + project only** is acceptable because the project should contain only + this backup bucket. + +Do **not** attach `IAM*` or compute permission sets. + +## 3. Put the values in OpenBao + +On this host, with a token that can write the `platform` mount: + +```bash +bao kv put platform/workloads/railiance/backup/platform-pg-backup-s3 \ + ACCESS_KEY_ID='SCWxxxxxxxx' \ + ACCESS_SECRET_KEY='xxxxxxxx' \ + APPLICATION_ID='xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx' +``` + +Or replace the `xxx` placeholders in the OpenBao UI on that same path. + +`APPLICATION_ID` is the IAM application UUID (not a secret). It is needed +later for the bucket policy. + +## 4. Confirm without printing values + +```bash +bao kv metadata get platform/workloads/railiance/backup/platform-pg-backup-s3 +``` + +You should see a current version greater than any placeholder version. +Then tell the agent: **“the Barman key is in bao.”** Also say the +application name if `APPLICATION_ID` is in bao. + +Do not enable WAL archiving yourself. T04 still has to prove a negative +key cannot access the bucket and to vend Secret `platform-pg-backup-s3`. +T05 enables continuous archiving. + +After the scoped key works, delete or lock down the bootstrap key from +CCR-2026-0011. diff --git a/docs/rapp-credential-lane-binding.md b/docs/rapp-credential-lane-binding.md index 2962312..d06eed7 100644 --- a/docs/rapp-credential-lane-binding.md +++ b/docs/rapp-credential-lane-binding.md @@ -180,7 +180,7 @@ shape unless it *is* a secrets engine. | --- | --- | --- | --- | | `rapp-openbao` | none | none — package is the store | correct | | `rapp-postgres` | `consumer_contract` + `openbao-database-secrets-engine` | `rapp-postgres/audit-core-runtime`, `rapp-postgres/audit-core-migration` | bound | -| `rapp-postgres` | `s3-backup-target` | no lane yet; Secret `platform-pg-backup-s3` is S3 custody after RESOURCE-WP-0002 | fail-closed on purpose | +| `rapp-postgres` | `s3-backup-target` / `secret_references: platform/workloads/railiance/backup/platform-pg-backup-s3` | `CCR-2026-0012` | pointer set; CCR `proposed`; Secret not vended | | `rapp-qonto` | `secret_references: tenants/binky/qonto-api` | `CCR-2026-0009` | pointer set; CCR itself is still `proposed` | Live CCRs without `target.rapp` are un-rapped workloads. They stay on diff --git a/openbao/policies/workload-kv-read-platform-pg-backup-s3.hcl b/openbao/policies/workload-kv-read-platform-pg-backup-s3.hcl new file mode 100644 index 0000000..aee15e3 --- /dev/null +++ b/openbao/policies/workload-kv-read-platform-pg-backup-s3.hcl @@ -0,0 +1,11 @@ +# Least-privilege read of the Barman runtime key for platform-pg. +# ESO (or the later Kubernetes auth role) is the only in-cluster reader. +# Values never belong in Git. + +path "platform/data/workloads/railiance/backup/platform-pg-backup-s3" { + capabilities = ["read"] +} + +path "platform/metadata/workloads/railiance/backup/platform-pg-backup-s3" { + capabilities = ["read"] +} From 94b2f7cc9c116c94f07122194053804aebd34b7a Mon Sep 17 00:00:00 2001 From: codex Date: Fri, 14 Aug 2026 19:19:56 +0200 Subject: [PATCH 06/11] retarget: CCR-2026-0012 is the general backup object-store lane Use IAM application resource-control and policy Scoped backup access. OpenBao path is platform/workloads/railiance/backup/object-storage. rapp-postgres keeps Secret platform-pg-backup-s3 as the first projection. --- ...up-object-storage.clustersecretstore.yaml} | 15 +-- ...011-scaleway-object-storage-bootstrap.yaml | 4 +- .../CCR-2026-0012-backup-object-storage.yaml | 112 ++++++++++++++++++ .../CCR-2026-0012-platform-pg-backup-s3.yaml | 103 ---------------- ...kup-s3.md => put-backup-object-storage.md} | 42 ++++--- docs/rapp-credential-lane-binding.md | 5 +- ...workload-kv-read-backup-object-storage.hcl | 11 ++ ...workload-kv-read-platform-pg-backup-s3.hcl | 11 -- 8 files changed, 160 insertions(+), 143 deletions(-) rename argocd/platform-addons/openbao-secretstore/{openbao-platform-pg-backup.clustersecretstore.yaml => openbao-backup-object-storage.clustersecretstore.yaml} (59%) create mode 100644 credential-change-requests/CCR-2026-0012-backup-object-storage.yaml delete mode 100644 credential-change-requests/CCR-2026-0012-platform-pg-backup-s3.yaml rename docs/{put-platform-pg-backup-s3.md => put-backup-object-storage.md} (55%) create mode 100644 openbao/policies/workload-kv-read-backup-object-storage.hcl delete mode 100644 openbao/policies/workload-kv-read-platform-pg-backup-s3.hcl diff --git a/argocd/platform-addons/openbao-secretstore/openbao-platform-pg-backup.clustersecretstore.yaml b/argocd/platform-addons/openbao-secretstore/openbao-backup-object-storage.clustersecretstore.yaml similarity index 59% rename from argocd/platform-addons/openbao-secretstore/openbao-platform-pg-backup.clustersecretstore.yaml rename to argocd/platform-addons/openbao-secretstore/openbao-backup-object-storage.clustersecretstore.yaml index 9f5fbc9..16a44ab 100644 --- a/argocd/platform-addons/openbao-secretstore/openbao-platform-pg-backup.clustersecretstore.yaml +++ b/argocd/platform-addons/openbao-secretstore/openbao-backup-object-storage.clustersecretstore.yaml @@ -1,19 +1,20 @@ # DRAFT — CCR-2026-0012. Do not apply until the CCR is approved and the -# KV values are real. Deploy on railiance01 (databases lives there), not -# the CoulombCore ArgoCD kustomization. +# KV values are real. Deploy on railiance01 (first consumer lives in +# databases), not the CoulombCore ArgoCD kustomization. # # Prereq: ESO can authenticate to OpenBao with policy -# workload-kv-read-platform-pg-backup-s3. Interim railiance01 identity +# workload-kv-read-backup-object-storage. Interim railiance01 identity # may be AppRole or a child token; Kubernetes auth role -# external-secrets-platform-pg-backup is the CCR-declared steady state. +# external-secrets-backup-object-storage is the CCR-declared steady state. +# Add further namespace conditions when another backup consumer is bound. apiVersion: external-secrets.io/v1 kind: ClusterSecretStore metadata: - name: openbao-platform-pg-backup + name: openbao-backup-object-storage labels: app.kubernetes.io/part-of: railiance-gitops railiance-platform/component: external-secrets - app.kubernetes.io/name: platform-pg-backup + app.kubernetes.io/name: backup-object-storage spec: provider: vault: @@ -23,7 +24,7 @@ spec: auth: kubernetes: mountPath: kubernetes - role: external-secrets-platform-pg-backup + role: external-secrets-backup-object-storage serviceAccountRef: name: external-secrets namespace: external-secrets diff --git a/credential-change-requests/CCR-2026-0011-scaleway-object-storage-bootstrap.yaml b/credential-change-requests/CCR-2026-0011-scaleway-object-storage-bootstrap.yaml index 877860c..67b74cd 100644 --- a/credential-change-requests/CCR-2026-0011-scaleway-object-storage-bootstrap.yaml +++ b/credential-change-requests/CCR-2026-0011-scaleway-object-storage-bootstrap.yaml @@ -55,7 +55,7 @@ risk: classification: high notes: - Bootstrap key can create projects/buckets; rotate or delete after scoped key exists. - - Scoped Barman key is a different path: platform/workloads/railiance/backup/platform-pg-backup-s3 + - Scoped backup key is a different path: platform/workloads/railiance/backup/object-storage - Values must not appear in Git, State Hub, or chat. verification: positive: @@ -64,5 +64,5 @@ verification: - default-policy token denied on the data path. lifecycle: deactivate: Delete bootstrap key at Scaleway after the scoped bucket key works. - rotate: Put a new bootstrap key; do not reuse the Barman key. + rotate: Put a new bootstrap key; do not reuse the scoped backup key. compromised: Revoke both Scaleway keys; rotate bucket policy. diff --git a/credential-change-requests/CCR-2026-0012-backup-object-storage.yaml b/credential-change-requests/CCR-2026-0012-backup-object-storage.yaml new file mode 100644 index 0000000..86d0a1f --- /dev/null +++ b/credential-change-requests/CCR-2026-0012-backup-object-storage.yaml @@ -0,0 +1,112 @@ +id: CCR-2026-0012 +kind: credential-change-request +schema_version: 1 +request_type: workload-kv-read +title: Scaleway scoped backup object-storage key +status: proposed +created: '2026-08-14' +updated: '2026-08-14' +requester: + agent: grok + reason: >- + RESOURCE-WP-0002 T04 needs a renewable, revocable, project-scoped + Scaleway backup key in OpenBao. Custody is the resource-control + procuring identity, not a Barman-specific application. First + consumer is rapp-postgres (Secret platform-pg-backup-s3). The + bootstrap key (CCR-2026-0011) must not be the runtime key. +review: + required: true + required_approvers: + - platform-operator + comments: [] +target: + domain: financials + tenant: railiance + workload: backup + rapp: rapp-postgres + environment: production + purpose: >- + Object-store credentials for Railiance backup destinations. + Secret keys ACCESS_KEY_ID and ACCESS_SECRET_KEY. APPLICATION_ID + is the resource-control IAM application (for a later bucket + policy). Endpoint, bucket, prefix, and region stay on reef-storage. +openbao: + mount: platform + kv_path: platform/workloads/railiance/backup/object-storage + fields: + - ACCESS_KEY_ID + - ACCESS_SECRET_KEY + - APPLICATION_ID + policy_name: workload-kv-read-backup-object-storage + policy_file: openbao/policies/workload-kv-read-backup-object-storage.hcl + auth: + method: kubernetes + mount: kubernetes + role: external-secrets-backup-object-storage + bound_claims: + service_account_names: + - external-secrets + service_account_namespaces: + - external-secrets + bound_claims_confirmed: false + policies: + - workload-kv-read-backup-object-storage + ttl: 15m +access_frontdoor: + type: ops-warden + catalog_id: backup-object-storage + selector: resource-control scoped backup access + command: warden access backup-object-storage --fetch ACCESS_KEY_ID + resolvable: false + readiness: pending-review +delivery: + surface: external-secrets + target: >- + ClusterSecretStore openbao-backup-object-storage (first namespace + condition: databases) → ExternalSecret databases/platform-pg-backup-s3 + → Secret platform-pg-backup-s3 (consumer projection). Drafts live + under railiance-platform/argocd/platform-addons/openbao-secretstore/ + and rapp-postgres/helm/platform-pg-backup-s3.externalsecret.yaml. + Do not apply until this CCR is approved and the KV values are real. +risk: + classification: high + notes: + - Scaleway policies attach to the IAM application, not to one API key. + Application resource-control must hold only backup-scoped keys. + - The runtime key can write and delete backup objects. Compromise can + destroy recovery points or fill the bucket. + - The bootstrap key (CCR-2026-0011) must stay off this application and + be revoked after this key works. + - Do not enable continuous WAL archiving until the empty-archive + preflight passes (RESOURCE-WP-0002 T05). + - Values must not appear in Git, State Hub, logs, or chat. +verification: + positive: + - Field names present on the KV path; values not printed. + - An approved databases-namespace ExternalSecret can sync ACCESS_KEY_ID + and ACCESS_SECRET_KEY to Secret platform-pg-backup-s3. + - The scoped key can list/put/delete backup prefixes on the procured + bucket. + negative: + - A token without this policy cannot read the KV path. + - A namespace outside the ClusterSecretStore condition cannot use the store. + - A second, unused key (or revoked key) cannot access the bucket. + - The scoped key cannot create compute resources. + activation_conditions: + - Platform-operator approves this CCR. + - Founder creates or reuses IAM application resource-control, binds + policy Scoped backup access, mints the API key of that name, then + replaces OpenBao placeholders. + - ESO store and ExternalSecret applied only after values are real. +lifecycle: + deactivate: Disable the catalog entry, delete consumer ExternalSecrets, + revoke the Scoped backup access API key. Keep application + resource-control if other backup keys remain. + rotate: Overlap-first. Put a new key in OpenBao, wait for ESO refresh, + revoke the old Scaleway key. + compromised: Revoke the Scoped backup access key and the bootstrap key + at Scaleway, rotate this path, review bucket contents, open incident + follow-up. +state_hub: + workplan_id: RESOURCE-WP-0002 + task_id: RESOURCE-WP-0002-T04 diff --git a/credential-change-requests/CCR-2026-0012-platform-pg-backup-s3.yaml b/credential-change-requests/CCR-2026-0012-platform-pg-backup-s3.yaml deleted file mode 100644 index 2b7206a..0000000 --- a/credential-change-requests/CCR-2026-0012-platform-pg-backup-s3.yaml +++ /dev/null @@ -1,103 +0,0 @@ -id: CCR-2026-0012 -kind: credential-change-request -schema_version: 1 -request_type: workload-kv-read -title: Scaleway scoped Barman key for platform-pg backup -status: proposed -created: '2026-08-14' -updated: '2026-08-14' -requester: - agent: grok - reason: >- - RESOURCE-WP-0002 T04 needs a renewable, revocable, bucket-and-prefix - scoped Scaleway key in OpenBao so railiance-platform can vend Secret - platform-pg-backup-s3 into the databases namespace. The bootstrap key - (CCR-2026-0011) can create buckets and must not be the runtime key. -review: - required: true - required_approvers: - - platform-operator - comments: [] -target: - domain: financials - tenant: railiance - workload: platform-pg-backup - rapp: rapp-postgres - environment: production - purpose: >- - CNPG/Barman object-store credentials for platform-pg WAL and base - backups. Secret keys ACCESS_KEY_ID and ACCESS_SECRET_KEY only. - Endpoint, bucket, prefix, and region stay on reef-storage. -openbao: - mount: platform - kv_path: platform/workloads/railiance/backup/platform-pg-backup-s3 - fields: - - ACCESS_KEY_ID - - ACCESS_SECRET_KEY - - APPLICATION_ID - policy_name: workload-kv-read-platform-pg-backup-s3 - policy_file: openbao/policies/workload-kv-read-platform-pg-backup-s3.hcl - auth: - method: kubernetes - mount: kubernetes - role: external-secrets-platform-pg-backup - bound_claims: - service_account_names: - - external-secrets - service_account_namespaces: - - external-secrets - bound_claims_confirmed: false - policies: - - workload-kv-read-platform-pg-backup-s3 - ttl: 15m -access_frontdoor: - type: ops-warden - catalog_id: platform-pg-backup-s3 - selector: platform-pg Barman Scaleway key - command: warden access platform-pg-backup-s3 --fetch ACCESS_KEY_ID - resolvable: false - readiness: pending-review -delivery: - surface: external-secrets - target: >- - ClusterSecretStore openbao-platform-pg-backup (namespace condition - databases) → ExternalSecret databases/platform-pg-backup-s3 → Secret - platform-pg-backup-s3 with keys ACCESS_KEY_ID and ACCESS_SECRET_KEY. - Drafts live under railiance-platform/argocd/platform-addons/openbao-secretstore/ - and rapp-postgres/helm/platform-pg-backup-s3.externalsecret.yaml. - Do not apply until this CCR is approved and the KV values are real. -risk: - classification: high - notes: - - The runtime key can write and delete objects under the backup prefix. - Compromise can destroy recovery points or fill the bucket. - - The bootstrap key (CCR-2026-0011) must be revoked after this key works. - - Do not enable continuous WAL archiving until the empty-archive preflight - passes (RESOURCE-WP-0002 T05). - - Values must not appear in Git, State Hub, logs, or chat. -verification: - positive: - - Field names present on the KV path; values not printed. - - An approved databases-namespace ExternalSecret can sync ACCESS_KEY_ID - and ACCESS_SECRET_KEY to Secret platform-pg-backup-s3. - - The scoped key can list/put/delete only under prefix platform-pg/. - negative: - - A token without this policy cannot read the KV path. - - A namespace outside the ClusterSecretStore condition cannot use the store. - - A second, unused key (or revoked key) cannot access the bucket. - - The scoped key cannot list sibling buckets or create compute resources. - activation_conditions: - - Platform-operator approves this CCR. - - Founder creates the Scaleway IAM application and project-scoped - Object Storage key, then replaces OpenBao placeholders. - - ESO store and ExternalSecret applied only after values are real. -lifecycle: - deactivate: Disable the catalog entry, delete the ExternalSecret, revoke - the Scaleway API key, delete the IAM application. - rotate: Overlap-first. Put a new key in OpenBao, wait for ESO refresh, - revoke the old Scaleway key. - compromised: Revoke both the Barman key and the bootstrap key at Scaleway, - rotate this path, review bucket contents, open incident follow-up. -state_hub: - workplan_id: RESOURCE-WP-0002 - task_id: RESOURCE-WP-0002-T04 diff --git a/docs/put-platform-pg-backup-s3.md b/docs/put-backup-object-storage.md similarity index 55% rename from docs/put-platform-pg-backup-s3.md rename to docs/put-backup-object-storage.md index aa1bf8c..fa582a5 100644 --- a/docs/put-platform-pg-backup-s3.md +++ b/docs/put-backup-object-storage.md @@ -1,31 +1,38 @@ -# Put the scoped Barman key (founder, local only) +# Put the scoped backup object-store key (founder, local only) Do this on a trusted terminal. **Do not paste ACCESS_KEY_ID or ACCESS_SECRET_KEY into chat, Git, or State Hub.** -CCR: `credential-change-requests/CCR-2026-0012-platform-pg-backup-s3.yaml` -Path: `platform/workloads/railiance/backup/platform-pg-backup-s3` +CCR: `credential-change-requests/CCR-2026-0012-backup-object-storage.yaml` +Path: `platform/workloads/railiance/backup/object-storage` This is **not** the bootstrap key (`…/scaleway/bootstrap`). That key -created the bucket. This key is the CNPG/Barman runtime identity. +created the bucket. This key is the general **backup** runtime identity +for application `resource-control`. First consumer is `rapp-postgres` +(Secret `platform-pg-backup-s3`). -## 1. Create a dedicated IAM application +## 1. Use IAM application `resource-control` In [console.scaleway.com](https://console.scaleway.com) → IAM → Applications: -1. Create application `railiance-barman-platform-pg`. +1. Create application `resource-control` if it does not exist. Do **not** + create a Barman- or postgres-specific application. 2. Create an API key **on that application** (not on your user): - - Description: `platform-pg Barman runtime` + - Description / name: `Scoped backup access` - **Preferred Project for Object Storage:** the project that owns bucket `railiance-platform-pg-backup` 3. Copy the access key and secret key into a local scratch file (`chmod 600`). The secret is shown once. -## 2. Attach a project-scoped Object Storage policy +Put **only** backup-scoped keys on this application. Scaleway attaches +policies to the application, not to one key. The bootstrap key must stay +off `resource-control`. -IAM → Policies → create `railiance-barman-platform-pg-objects`: +## 2. Bind policy `Scoped backup access` -- Principal: the application from step 1 +IAM → Policies → create (or reuse) `Scoped backup access`: + +- Principal: application `resource-control` - Scope: **that one project**, not the whole Organization - Permission sets, if the console lists them: - `ObjectStorageBucketsRead` @@ -34,7 +41,7 @@ IAM → Policies → create `railiance-barman-platform-pg-objects`: - `ObjectStorageObjectsDelete` - If those sets are not listed, `ObjectStorageFullAccess` **on this project only** is acceptable because the project should contain only - this backup bucket. + backup buckets. Do **not** attach `IAM*` or compute permission sets. @@ -43,7 +50,7 @@ Do **not** attach `IAM*` or compute permission sets. On this host, with a token that can write the `platform` mount: ```bash -bao kv put platform/workloads/railiance/backup/platform-pg-backup-s3 \ +bao kv put platform/workloads/railiance/backup/object-storage \ ACCESS_KEY_ID='SCWxxxxxxxx' \ ACCESS_SECRET_KEY='xxxxxxxx' \ APPLICATION_ID='xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx' @@ -51,21 +58,20 @@ bao kv put platform/workloads/railiance/backup/platform-pg-backup-s3 \ Or replace the `xxx` placeholders in the OpenBao UI on that same path. -`APPLICATION_ID` is the IAM application UUID (not a secret). It is needed -later for the bucket policy. +`APPLICATION_ID` is the `resource-control` IAM application UUID (not a +secret). It is needed later for the bucket policy. ## 4. Confirm without printing values ```bash -bao kv metadata get platform/workloads/railiance/backup/platform-pg-backup-s3 +bao kv metadata get platform/workloads/railiance/backup/object-storage ``` You should see a current version greater than any placeholder version. -Then tell the agent: **“the Barman key is in bao.”** Also say the -application name if `APPLICATION_ID` is in bao. +Then tell the agent: **“the backup key is in bao.”** Do not enable WAL archiving yourself. T04 still has to prove a negative -key cannot access the bucket and to vend Secret `platform-pg-backup-s3`. +key cannot access the bucket and to vend the first consumer Secret. T05 enables continuous archiving. After the scoped key works, delete or lock down the bootstrap key from diff --git a/docs/rapp-credential-lane-binding.md b/docs/rapp-credential-lane-binding.md index d06eed7..c604f63 100644 --- a/docs/rapp-credential-lane-binding.md +++ b/docs/rapp-credential-lane-binding.md @@ -47,7 +47,8 @@ the list does not say who provisions the thing that satisfies a name. `openbao-database-secrets-engine` and `s3-backup-target` are capabilities. The database engine is configured by S3 from statements the package owns; the backup bucket is a `resource-control` resource and the -provider credential is S3 custody (`Secret platform-pg-backup-s3`). +provider credential is S3 custody (`secret:railiance-platform/backup`, +first projection Secret `platform-pg-backup-s3`). Fail-closed until that handoff lands is correct, not a hole in the package. @@ -180,7 +181,7 @@ shape unless it *is* a secrets engine. | --- | --- | --- | --- | | `rapp-openbao` | none | none — package is the store | correct | | `rapp-postgres` | `consumer_contract` + `openbao-database-secrets-engine` | `rapp-postgres/audit-core-runtime`, `rapp-postgres/audit-core-migration` | bound | -| `rapp-postgres` | `s3-backup-target` / `secret_references: platform/workloads/railiance/backup/platform-pg-backup-s3` | `CCR-2026-0012` | pointer set; CCR `proposed`; Secret not vended | +| `rapp-postgres` | `s3-backup-target` / `secret_references: platform/workloads/railiance/backup/object-storage` | `CCR-2026-0012` | pointer set; CCR `proposed`; first consumer of the general backup key; Secret not vended | | `rapp-qonto` | `secret_references: tenants/binky/qonto-api` | `CCR-2026-0009` | pointer set; CCR itself is still `proposed` | Live CCRs without `target.rapp` are un-rapped workloads. They stay on diff --git a/openbao/policies/workload-kv-read-backup-object-storage.hcl b/openbao/policies/workload-kv-read-backup-object-storage.hcl new file mode 100644 index 0000000..e9aa431 --- /dev/null +++ b/openbao/policies/workload-kv-read-backup-object-storage.hcl @@ -0,0 +1,11 @@ +# Least-privilege read of the resource-control backup object-store key. +# ESO (or the later Kubernetes auth role) is the only in-cluster reader. +# Values never belong in Git. + +path "platform/data/workloads/railiance/backup/object-storage" { + capabilities = ["read"] +} + +path "platform/metadata/workloads/railiance/backup/object-storage" { + capabilities = ["read"] +} diff --git a/openbao/policies/workload-kv-read-platform-pg-backup-s3.hcl b/openbao/policies/workload-kv-read-platform-pg-backup-s3.hcl deleted file mode 100644 index aee15e3..0000000 --- a/openbao/policies/workload-kv-read-platform-pg-backup-s3.hcl +++ /dev/null @@ -1,11 +0,0 @@ -# Least-privilege read of the Barman runtime key for platform-pg. -# ESO (or the later Kubernetes auth role) is the only in-cluster reader. -# Values never belong in Git. - -path "platform/data/workloads/railiance/backup/platform-pg-backup-s3" { - capabilities = ["read"] -} - -path "platform/metadata/workloads/railiance/backup/platform-pg-backup-s3" { - capabilities = ["read"] -} From ddce013caceacfb39d6d9753934d19aaac392c2d Mon Sep 17 00:00:00 2001 From: codex Date: Fri, 14 Aug 2026 19:34:40 +0200 Subject: [PATCH 07/11] approve: CCR-2026-0012; verify scoped backup key in OpenBao Founder put ACCESS_KEY/SECRET_KEY. S3 prefix CRUD works. IAM write and ESO apply remain gated. WAL not enabled. --- .../CCR-2026-0012-backup-object-storage.yaml | 39 ++++++++++------ ...-2026-0012-backup-key-verify-2026-08-14.md | 46 +++++++++++++++++++ docs/put-backup-object-storage.md | 15 +++--- 3 files changed, 80 insertions(+), 20 deletions(-) create mode 100644 docs/evidence/CCR-2026-0012-backup-key-verify-2026-08-14.md diff --git a/credential-change-requests/CCR-2026-0012-backup-object-storage.yaml b/credential-change-requests/CCR-2026-0012-backup-object-storage.yaml index 86d0a1f..749a908 100644 --- a/credential-change-requests/CCR-2026-0012-backup-object-storage.yaml +++ b/credential-change-requests/CCR-2026-0012-backup-object-storage.yaml @@ -3,7 +3,7 @@ kind: credential-change-request schema_version: 1 request_type: workload-kv-read title: Scaleway scoped backup object-storage key -status: proposed +status: approved created: '2026-08-14' updated: '2026-08-14' requester: @@ -18,7 +18,13 @@ review: required: true required_approvers: - platform-operator - comments: [] + comments: + - at: '2026-08-14T17:30:00+00:00' + reviewer: bernd.worsch + decision: approved + comment: >- + Approved in chat (Grok session): CCR-2026-0012 and “the backup + key is in bao.” Founder used ACCESS_KEY/SECRET_KEY field names. target: domain: financials tenant: railiance @@ -27,16 +33,19 @@ target: environment: production purpose: >- Object-store credentials for Railiance backup destinations. - Secret keys ACCESS_KEY_ID and ACCESS_SECRET_KEY. APPLICATION_ID - is the resource-control IAM application (for a later bucket - policy). Endpoint, bucket, prefix, and region stay on reef-storage. + Secret keys ACCESS_KEY and SECRET_KEY (projected to Kubernetes as + ACCESS_KEY_ID / ACCESS_SECRET_KEY). Org and project ids are + non-secret companions. APPLICATION_ID can be added later for a + bucket policy. Endpoint, bucket, prefix, and region stay on + reef-storage. openbao: mount: platform kv_path: platform/workloads/railiance/backup/object-storage fields: - - ACCESS_KEY_ID - - ACCESS_SECRET_KEY - - APPLICATION_ID + - ACCESS_KEY + - SECRET_KEY + - DEFAULT_ORGANIZATION_ID + - DEFAULT_PROJECT_ID policy_name: workload-kv-read-backup-object-storage policy_file: openbao/policies/workload-kv-read-backup-object-storage.hcl auth: @@ -56,15 +65,16 @@ access_frontdoor: type: ops-warden catalog_id: backup-object-storage selector: resource-control scoped backup access - command: warden access backup-object-storage --fetch ACCESS_KEY_ID + command: warden access backup-object-storage --fetch ACCESS_KEY resolvable: false - readiness: pending-review + readiness: approved-pending-apply delivery: surface: external-secrets target: >- ClusterSecretStore openbao-backup-object-storage (first namespace condition: databases) → ExternalSecret databases/platform-pg-backup-s3 - → Secret platform-pg-backup-s3 (consumer projection). Drafts live + → Secret platform-pg-backup-s3 (ACCESS_KEY_ID / ACCESS_SECRET_KEY + projected from ACCESS_KEY / SECRET_KEY). Drafts live under railiance-platform/argocd/platform-addons/openbao-secretstore/ and rapp-postgres/helm/platform-pg-backup-s3.externalsecret.yaml. Do not apply until this CCR is approved and the KV values are real. @@ -83,8 +93,9 @@ risk: verification: positive: - Field names present on the KV path; values not printed. - - An approved databases-namespace ExternalSecret can sync ACCESS_KEY_ID - and ACCESS_SECRET_KEY to Secret platform-pg-backup-s3. + - An approved databases-namespace ExternalSecret can project ACCESS_KEY + and SECRET_KEY to Secret platform-pg-backup-s3 as ACCESS_KEY_ID and + ACCESS_SECRET_KEY. - The scoped key can list/put/delete backup prefixes on the procured bucket. negative: @@ -96,7 +107,7 @@ verification: - Platform-operator approves this CCR. - Founder creates or reuses IAM application resource-control, binds policy Scoped backup access, mints the API key of that name, then - replaces OpenBao placeholders. + puts ACCESS_KEY and SECRET_KEY in OpenBao. Done 2026-08-14. - ESO store and ExternalSecret applied only after values are real. lifecycle: deactivate: Disable the catalog entry, delete consumer ExternalSecrets, diff --git a/docs/evidence/CCR-2026-0012-backup-key-verify-2026-08-14.md b/docs/evidence/CCR-2026-0012-backup-key-verify-2026-08-14.md new file mode 100644 index 0000000..23402d3 --- /dev/null +++ b/docs/evidence/CCR-2026-0012-backup-key-verify-2026-08-14.md @@ -0,0 +1,46 @@ +# CCR-2026-0012 — scoped backup key verification + +Date: 2026-08-14 +Status: **key live in OpenBao; cluster Secret not vended** +CCR: approved in chat 2026-08-14 +Path: `platform/workloads/railiance/backup/object-storage` (KV v2 version 1) + +No secret values in this file. + +## What is in OpenBao + +| Field | Present | +| --- | --- | +| `ACCESS_KEY` | yes (len 20, `SCW…`) | +| `SECRET_KEY` | yes | +| `DEFAULT_ORGANIZATION_ID` | yes | +| `DEFAULT_PROJECT_ID` | yes | +| `APPLICATION_ID` | no (bucket policy deferred) | + +Distinct from the CCR-2026-0011 bootstrap key (`keys_differ=yes`). + +## Positive + +- `scw object bucket list` / `get railiance-platform-pg-backup` in `nl-ams` succeeded. +- boto3 put/get/delete of a probe object under `platform-pg/` succeeded; probe deleted. +- Sibling prefix write also succeeded: no bucket policy yet, so prefix isolation is not enforced. + +## Negative + +- Bogus secret against the same access key: `SignatureDoesNotMatch`. +- `scw iam application list` / `user list` / `api-key get`: insufficient permissions. +- `scw billing budget list`: not allowed. +- `scw k8s cluster list`: insufficient permissions. + +`instance` / `vpc` / `rdb` / `registry` / `lb` list returned empty success. That is consistent with a project that has no those resources; it is not a proven compute-write deny. Do not create a paid resource to prove it. + +## Not done + +- OpenBao policy `workload-kv-read-backup-object-storage` — this token cannot `sys/policies/acl` write (403). +- ClusterSecretStore / ExternalSecret — railiance01 has no `platform-pg-backup-s3` yet; applying the draft store would fail without ESO auth. +- Bucket policy — needs `APPLICATION_ID`. +- WAL / `barmanObjectStore` on `platform-pg` — T05. + +## Next + +ops-mason (or a token that can write policies and ESO auth) applies the OpenBao policy and the `databases`-scoped store. Then ExternalSecret can project the Secret. After that, empty-WAL-archive preflight; still no continuous archiving until T05. diff --git a/docs/put-backup-object-storage.md b/docs/put-backup-object-storage.md index fa582a5..bf979f5 100644 --- a/docs/put-backup-object-storage.md +++ b/docs/put-backup-object-storage.md @@ -51,15 +51,18 @@ On this host, with a token that can write the `platform` mount: ```bash bao kv put platform/workloads/railiance/backup/object-storage \ - ACCESS_KEY_ID='SCWxxxxxxxx' \ - ACCESS_SECRET_KEY='xxxxxxxx' \ - APPLICATION_ID='xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx' + ACCESS_KEY='SCWxxxxxxxx' \ + SECRET_KEY='xxxxxxxx' \ + DEFAULT_ORGANIZATION_ID='xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx' \ + DEFAULT_PROJECT_ID='xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx' ``` -Or replace the `xxx` placeholders in the OpenBao UI on that same path. +Same four field names as the bootstrap path. External Secrets projects +`ACCESS_KEY` / `SECRET_KEY` to Kubernetes `ACCESS_KEY_ID` / +`ACCESS_SECRET_KEY`. -`APPLICATION_ID` is the `resource-control` IAM application UUID (not a -secret). It is needed later for the bucket policy. +Optional later field: `APPLICATION_ID` (the `resource-control` IAM +application UUID, not a secret) so we can attach a bucket policy. ## 4. Confirm without printing values From 2c3504e368dc0502ee6aeb8c0cb631dee9783c3d Mon Sep 17 00:00:00 2001 From: codex Date: Fri, 14 Aug 2026 19:52:08 +0200 Subject: [PATCH 08/11] docs: how to add APPLICATION_ID for the backup bucket policy --- docs/put-backup-object-storage.md | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/docs/put-backup-object-storage.md b/docs/put-backup-object-storage.md index bf979f5..ceff57b 100644 --- a/docs/put-backup-object-storage.md +++ b/docs/put-backup-object-storage.md @@ -61,8 +61,16 @@ Same four field names as the bootstrap path. External Secrets projects `ACCESS_KEY` / `SECRET_KEY` to Kubernetes `ACCESS_KEY_ID` / `ACCESS_SECRET_KEY`. -Optional later field: `APPLICATION_ID` (the `resource-control` IAM -application UUID, not a secret) so we can attach a bucket policy. +Optional field for the bucket policy (not a secret): + +```bash +bao kv patch platform/workloads/railiance/backup/object-storage \ + APPLICATION_ID='xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx' +``` + +Copy the UUID from IAM → Applications → `resource-control`. Then tell +the agent **“APPLICATION_ID is in bao.”** They will run +`reef-storage/tools/apply-backup-bucket-policy.sh`. ## 4. Confirm without printing values From cbc63985831ba6b8e8f6b22b32c079c10cc8ea55 Mon Sep 17 00:00:00 2001 From: codex Date: Fri, 14 Aug 2026 19:55:53 +0200 Subject: [PATCH 09/11] note: backup bucket policy enforces platform-pg/ prefix --- docs/evidence/CCR-2026-0012-backup-key-verify-2026-08-14.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/evidence/CCR-2026-0012-backup-key-verify-2026-08-14.md b/docs/evidence/CCR-2026-0012-backup-key-verify-2026-08-14.md index 23402d3..f5f4bcd 100644 --- a/docs/evidence/CCR-2026-0012-backup-key-verify-2026-08-14.md +++ b/docs/evidence/CCR-2026-0012-backup-key-verify-2026-08-14.md @@ -15,7 +15,7 @@ No secret values in this file. | `SECRET_KEY` | yes | | `DEFAULT_ORGANIZATION_ID` | yes | | `DEFAULT_PROJECT_ID` | yes | -| `APPLICATION_ID` | no (bucket policy deferred) | +| `APPLICATION_ID` | yes as of version 2 (UUID, not printed here) | Distinct from the CCR-2026-0011 bootstrap key (`keys_differ=yes`). From 625991469ac9562d1560ed7885cb44d81c3a55f5 Mon Sep 17 00:00:00 2001 From: codex Date: Fri, 14 Aug 2026 20:00:15 +0200 Subject: [PATCH 10/11] feat: vend platform-pg-backup-s3 via AppRole ESO Policy and AppRole applied. Store Valid, ExternalSecret SecretSynced. Prefix empty. WAL not enabled. --- ...kup-object-storage.clustersecretstore.yaml | 24 ++++++++++--------- .../CCR-2026-0012-backup-object-storage.yaml | 4 ++-- ...-2026-0012-backup-key-verify-2026-08-14.md | 23 +++++++++--------- 3 files changed, 27 insertions(+), 24 deletions(-) diff --git a/argocd/platform-addons/openbao-secretstore/openbao-backup-object-storage.clustersecretstore.yaml b/argocd/platform-addons/openbao-secretstore/openbao-backup-object-storage.clustersecretstore.yaml index 16a44ab..a582e58 100644 --- a/argocd/platform-addons/openbao-secretstore/openbao-backup-object-storage.clustersecretstore.yaml +++ b/argocd/platform-addons/openbao-secretstore/openbao-backup-object-storage.clustersecretstore.yaml @@ -1,11 +1,9 @@ -# DRAFT — CCR-2026-0012. Do not apply until the CCR is approved and the -# KV values are real. Deploy on railiance01 (first consumer lives in +# CCR-2026-0012. Deploy on railiance01 (first consumer lives in # databases), not the CoulombCore ArgoCD kustomization. # -# Prereq: ESO can authenticate to OpenBao with policy -# workload-kv-read-backup-object-storage. Interim railiance01 identity -# may be AppRole or a child token; Kubernetes auth role -# external-secrets-backup-object-storage is the CCR-declared steady state. +# Prereq: Secret external-secrets/openbao-backup-object-storage-approle +# (role-id, secret-id) for AppRole external-secrets-backup-object-storage. +# Same-cluster Kubernetes auth remains the follow-up. # Add further namespace conditions when another backup consumer is bound. apiVersion: external-secrets.io/v1 kind: ClusterSecretStore @@ -22,12 +20,16 @@ spec: path: platform version: v2 auth: - kubernetes: - mountPath: kubernetes - role: external-secrets-backup-object-storage - serviceAccountRef: - name: external-secrets + appRole: + path: approle + roleRef: + name: openbao-backup-object-storage-approle namespace: external-secrets + key: role-id + secretRef: + name: openbao-backup-object-storage-approle + namespace: external-secrets + key: secret-id conditions: - namespaces: - databases diff --git a/credential-change-requests/CCR-2026-0012-backup-object-storage.yaml b/credential-change-requests/CCR-2026-0012-backup-object-storage.yaml index 749a908..6b95741 100644 --- a/credential-change-requests/CCR-2026-0012-backup-object-storage.yaml +++ b/credential-change-requests/CCR-2026-0012-backup-object-storage.yaml @@ -3,7 +3,7 @@ kind: credential-change-request schema_version: 1 request_type: workload-kv-read title: Scaleway scoped backup object-storage key -status: approved +status: applied created: '2026-08-14' updated: '2026-08-14' requester: @@ -67,7 +67,7 @@ access_frontdoor: selector: resource-control scoped backup access command: warden access backup-object-storage --fetch ACCESS_KEY resolvable: false - readiness: approved-pending-apply + readiness: applied-pending-verify delivery: surface: external-secrets target: >- diff --git a/docs/evidence/CCR-2026-0012-backup-key-verify-2026-08-14.md b/docs/evidence/CCR-2026-0012-backup-key-verify-2026-08-14.md index f5f4bcd..c51a459 100644 --- a/docs/evidence/CCR-2026-0012-backup-key-verify-2026-08-14.md +++ b/docs/evidence/CCR-2026-0012-backup-key-verify-2026-08-14.md @@ -1,7 +1,7 @@ # CCR-2026-0012 — scoped backup key verification Date: 2026-08-14 -Status: **key live in OpenBao; cluster Secret not vended** +Status: **applied — Secret `databases/platform-pg-backup-s3` synced** CCR: approved in chat 2026-08-14 Path: `platform/workloads/railiance/backup/object-storage` (KV v2 version 1) @@ -23,7 +23,9 @@ Distinct from the CCR-2026-0011 bootstrap key (`keys_differ=yes`). - `scw object bucket list` / `get railiance-platform-pg-backup` in `nl-ams` succeeded. - boto3 put/get/delete of a probe object under `platform-pg/` succeeded; probe deleted. -- Sibling prefix write also succeeded: no bucket policy yet, so prefix isolation is not enforced. +- Sibling prefix write is now denied (`AccessDenied`) after the + `resource-control-scoped-backup` bucket policy + (`BackupListPrefix`, `BackupObjectsUnderPrefix`) applied 2026-08-14. ## Negative @@ -34,13 +36,12 @@ Distinct from the CCR-2026-0011 bootstrap key (`keys_differ=yes`). `instance` / `vpc` / `rdb` / `registry` / `lb` list returned empty success. That is consistent with a project that has no those resources; it is not a proven compute-write deny. Do not create a paid resource to prove it. -## Not done +## Cluster delivery (same day) -- OpenBao policy `workload-kv-read-backup-object-storage` — this token cannot `sys/policies/acl` write (403). -- ClusterSecretStore / ExternalSecret — railiance01 has no `platform-pg-backup-s3` yet; applying the draft store would fail without ESO auth. -- Bucket policy — needs `APPLICATION_ID`. -- WAL / `barmanObjectStore` on `platform-pg` — T05. - -## Next - -ops-mason (or a token that can write policies and ESO auth) applies the OpenBao policy and the `databases`-scoped store. Then ExternalSecret can project the Secret. After that, empty-WAL-archive preflight; still no continuous archiving until T05. +- Policy `workload-kv-read-backup-object-storage` written. +- AppRole `external-secrets-backup-object-storage` (15m/30m, 8 uses). +- Secret `external-secrets/openbao-backup-object-storage-approle` applied; local delivery shredded. +- ClusterSecretStore `openbao-backup-object-storage` Ready/Valid, condition `databases` only. +- ExternalSecret `databases/platform-pg-backup-s3` SecretSynced. Keys `ACCESS_KEY_ID` (len 20) and `ACCESS_SECRET_KEY` (len 36). Values not read. +- Prefix `platform-pg/` object count: 0. +- `platform-pg` still has no `barmanObjectStore`. WAL is T05. From 2af19627e16d6007c506aeeb45c725fcbdb842a9 Mon Sep 17 00:00:00 2001 From: codex Date: Fri, 14 Aug 2026 20:47:16 +0200 Subject: [PATCH 11/11] feat: add r01-platform-pg to the Option A CNPG backup inventory --- tools/cmd/cnpg-option-a-backup | 1 + 1 file changed, 1 insertion(+) diff --git a/tools/cmd/cnpg-option-a-backup b/tools/cmd/cnpg-option-a-backup index 744ad77..490d9db 100755 --- a/tools/cmd/cnpg-option-a-backup +++ b/tools/cmd/cnpg-option-a-backup @@ -50,6 +50,7 @@ DEFAULT_TARGETS=( "core-net-kingdom-pg|KUBECONFIG_CORE|databases|net-kingdom-pg|interhub,privacyidea_db" "core-state-hub-db|KUBECONFIG_CORE|databases|state-hub-db|state_hub" "r01-forgejo-db|KUBECONFIG_R01|databases|forgejo-db|forgejo" + "r01-platform-pg|KUBECONFIG_R01|databases|platform-pg|audit_core,platform_meta,isolation_probe" "r01-net-kingdom-pg|KUBECONFIG_R01|databases|net-kingdom-pg|interhub,privacyidea_db" "r01-state-hub-db|KUBECONFIG_R01|databases|state-hub-db|state_hub" "r01-user-engine-pg|KUBECONFIG_R01|user-engine|user-engine-pg|user_engine"