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.
This commit is contained in:
codex 2026-08-14 19:19:56 +02:00
parent 2769258631
commit 94b2f7cc9c
8 changed files with 160 additions and 143 deletions

View file

@ -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

View file

@ -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.

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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"]
}

View file

@ -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"]
}