Repository navigation
Provision a dedicated R2 bucket and identity for the wedding database backupΒ #3251
Description
π€ Generated by the Agentic Engineer
Evidence
wedding-db's backup consumes the shared platform backup identity. Measured on main:
k8s/bases/apps/wedding-app/external-secret-db-backup.yamlreads OpenBao
infrastructure/backup/r2β the same path Velero,vault-backupandumami-dbread.k8s/bases/apps/wedding-app/object-store.yamlwrites tos3://${r2_bucket}/cnpg/wedding-db,
i.e. a prefix inside the shared bucket.
Cloudflare permanent R2 identities are bucket-scoped, not prefix-scoped, so no change on our side
can narrow the shared credential to that prefix. A dedicated destination is required before the
tenant's access can be reduced.
Why this is its own child
This child isolates the Cloudflare account dependency from the downstream manifest changes.
Account authentication and any required approval for the exact credential scope come first; the
agent can then inspect and carry out the scoped setup and encrypted handoff. It is not a standing
rule that the maintainer must manually provision the destination or handle a raw secret.
Expected outcome
A dedicated production R2 bucket for the wedding database backup, plus an Object Read and Write
identity scoped to that bucket only, with no access to the shared backup bucket.
Acceptance criteria
- A dedicated R2 bucket exists for the wedding database backup.
- An Object Read and Write identity exists that is scoped to only that bucket.
- The identity has no read or write access to the shared backup bucket (verified by attempting
access and observing denial). - The credential is handed over through the existing encrypted per-environment bootstrap flow so
the next child can seed it declaratively.
Verified account setup β September 8, 2026
The exact account-token grant was approved in the session. The dedicated token is now created with Object Read and Write permission restricted to the wedding backup bucket.
A real S3 API probe successfully wrote, read, listed and deleted a synthetic object in that bucket. Read, list and write attempts against the shared backup bucket each returned 403 AccessDenied. The synthetic object was removed and its absence verified. R2 ignored the conditional-delete header, so cleanup used the reviewed ownership-checking fallback; this is not evidence of conditional-delete enforcement.
The credentials were passed directly into SOPS and saved in the production bootstrap source. The encrypted handoff is being reviewed for delivery under #3252. That final acceptance item remains open until the source handoff is published; seed/projection verification, backup cutover and restore proof remain engineering work. The token grant itself is complete.
Delivery status β September 8: public publication of this encrypted credential file with the existing production recipient was explicitly approved in the session. The reviewed handoff is now published in PR3690, with all 20 committed files independently matched to the reviewed candidate. Hosted checks and source integration remain in progress, so the handoff acceptance item stays open until delivery. No personal action or repeated token approval is required.
Part of #2867.
Metadata
Metadata
Assignees
Labels
Type
Fields
Projects
- StatusShow more project fieldsβ Done
Activity
coderabbitai commentedon Aug 19, 2026
π Related PRs
#3203 - security(vault-backup): disposition the last two CKV_K8S_40 findings as one deferred exception [merged]
π Issue Planner
Check the box below or use the
@coderabbitai plancommand to generate an implementation plan and prompts that you can use with your favorite coding assistant.π§ͺ Issue enrichment is currently in open beta.
You can configure auto-planning by selecting labels in the issue_enrichment configuration.
To disable automatic issue enrichment, add the following to your
.coderabbit.yaml:π¬ Have feedback or questions? Drop into our discord!
devantler commentedon Sep 3, 2026
This has been blocked for 15 days and nobody has actually asked. Asking now.
@devantler β this issue is the only thing holding the wedding-backup isolation chain
(#3251 β #3252 β #3253, under #2867). Its parent's blocker line has been re-verified as recently as
2026-09-02, so the loop has been checking it daily while never raising it. That is my defect, not
yours; the general fix is tracked at
monorepo#3171.
Why an agent cannot do this one. I checked rather than assumed: this platform holds Cloudflare
credentials for DNS only, and its Crossplane providers are
provider-aws-iam,provider-family-aws,provider-githubandprovider-upjet-unifi. There is no Cloudflare provider, so no declarative pathto an R2 bucket exists today. R2 permanent identities are also bucket-scoped rather than
prefix-scoped, so no manifest change can narrow the existing shared credential to the
cnpg/wedding-dbprefix. The bucket has to exist first, and creating it is an account action.What is needed, as small as I can make it:
destination.
to the shared backup bucket. That non-access is the whole point of the issue.
prefer. Do not paste them here β this repository is public.
Once those exist, everything downstream is mechanical and I can take it from there: the seeding path
(
vault-seed/push-secret-seed-r2-credentials.yamlβ OpenBao βExternalSecret) already exists and#3252 is an additive use of it, with #3253 covering the restore proof, the denial check against the
shared destination, retiring the old access, and the DR runbook updates.
If you would rather not add a bucket, the alternative worth considering is accepting the shared
identity and closing #2867 with that reasoning recorded β that is a legitimate outcome and better
than this sitting open indefinitely. Either answer unblocks the chain; only silence does not.
devantler commentedon Sep 9, 2026
Final acceptance criterion delivered β closing. The encrypted per-environment handoff was
published in #3690, which merged 2026-09-08T22:39:13Z (
8e11a1b321). Verified onmainratherthan inferred from the merge:
k8s/clusters/prod/bootstrap/secret-wedding-db-backup-r2.enc.yamland itslocalcounterpart bothexist, each carrying 3
ENC[AES256_GCMβ¦]values β the credential material is encrypted at rest,with
sops.encrypted_regex^(data|stringData)$, anagerecipient, and sops3.13.3.bootstrap/kustomization.yaml, so the flow actuallyconsumes them rather than merely containing them.
k8s/bases/infrastructure/vault-seed/push-secret-seed-wedding-db-backup-r2.yamlplus the dedicatedexternal-secret-db-backup-dedicated.yaml..enc.yamlfiles are JSON, notblock YAML, so a
^sops:-anchored probe reports the sops block ABSENT on a perfectly valid file. It ispresent as a
"sops"object. YAML is a JSON superset, so kustomize and sops both parse it.What this issue does and does not now assert. All four acceptance criteria are met: the dedicated
bucket and bucket-scoped Object Read and Write identity exist, denial against the shared bucket was
observed directly (403 on read, list and write), and the encrypted handoff is delivered. It does not
assert that the tenant has been cut over β seed and projection verification, the backup cutover, and
restore proof are #3252 and #3253's engineering work, and the shared credential is still what
wedding-dbuses until those land. The isolation is available, not yet in force.Part of #2867, whose remaining children are #3252 and #3253.