fix(platform): orphan the shared OpenBaoConnection on XR retirement #684

Merged
binjovi-bot merged 1 commit from feat/openbao-connection-orphan into trunk 2026-09-14 16:28:03 +00:00
Owner

What

Set deletionPolicy: Orphan on the openbao-connection composed Object. It has no explicit policy today, so it takes the provider default, Delete.

Why this is the blocking step for Crossplane retirement

seanfarm PR #750 landed all ten OpenBao CRs this composition manages as plain Flux manifests (kubernetes/flux/infrastructure/openbao-platform). That was the last extraction: this XR tracks exactly ten composed resources, and all ten are now declared in Git and co-managed by kustomize-controller with zero field conflict.

What remains is retiring the XR safely. This Object is the one thing that made that unsafe.

The real dependency, traced

The OpenBaoConnection this Object wraps is referenced by name through a two-hop chain:

OpenBaoDynamicSecret --openbaoAuthRef--> OpenBaoAuth --openbaoConnectionRef--> openbao/openbao

Five live pipelines resolve credentials through it, across four tenant namespaces:

namespace OpenBaoDynamicSecret
forgejo forgejo-db-user
n8n n8n-secrets
ory hydra-secrets
ory kratos-secrets
outline outline-database

Under the Delete default, retiring the XR deletes that record and breaks credential renewal for Forgejo, n8n, Hydra, Kratos and Outline until Flux restores it. Orphan hands the CR to Flux cleanly instead.

Note the other nine CRs are not affected by this: they carry an inline openbaoRef.connection.address, not a named reference. They do not depend on this record.

Why the nine are already safe

Each carries spec.deletionPolicy: Retain, and that is enforced, not decorative. In libseanfarm-operator v0.4.13 -- the exact version the deployed openbao-operator v0.12.32 pins -- reconcile/harness.go:401:

if policy != DeletionPolicyRetain && !dryRun {
    ... h.Delete(ctx, obj, c) ...
}

Retain skips the whole remote-delete block. The UnmountSecrets call that would destroy the transit mount and the cosign signing keys lives inside it (openbaotransitbackend_impl.go:152), so it is never reached. The accessor returns spec.deletionPolicy directly, with no defaulting.

So when the XR is retired, those nine CRs are removed and recreated by Flux, while the OpenBao-side state -- both PKI mounts, the intermediate CA, the three issuing roles, the transit mount and its three keys -- is untouched. There is no functional outage, only a window with no drift correction.

Risk

None to live state. Adding a deletion policy changes nothing while the XR exists; it only changes what happens when the Object is eventually removed.

tests/check.sh exits 0.

https://claude.ai/code/session_01GfkEuwuvGSqGyXVuxwT7PA

## What Set `deletionPolicy: Orphan` on the `openbao-connection` composed `Object`. It has no explicit policy today, so it takes the provider default, `Delete`. ## Why this is the blocking step for Crossplane retirement `seanfarm` PR #750 landed all ten OpenBao CRs this composition manages as plain Flux manifests (`kubernetes/flux/infrastructure/openbao-platform`). That was the last extraction: this XR tracks exactly ten composed resources, and all ten are now declared in Git and co-managed by `kustomize-controller` with zero field conflict. What remains is retiring the XR safely. This `Object` is the one thing that made that unsafe. ## The real dependency, traced The `OpenBaoConnection` this `Object` wraps is referenced **by name** through a two-hop chain: ``` OpenBaoDynamicSecret --openbaoAuthRef--> OpenBaoAuth --openbaoConnectionRef--> openbao/openbao ``` Five live pipelines resolve credentials through it, across four tenant namespaces: | namespace | OpenBaoDynamicSecret | |---|---| | forgejo | forgejo-db-user | | n8n | n8n-secrets | | ory | hydra-secrets | | ory | kratos-secrets | | outline | outline-database | Under the `Delete` default, retiring the XR deletes that record and breaks credential renewal for Forgejo, n8n, Hydra, Kratos and Outline until Flux restores it. `Orphan` hands the CR to Flux cleanly instead. Note the other nine CRs are **not** affected by this: they carry an inline `openbaoRef.connection.address`, not a named reference. They do not depend on this record. ## Why the nine are already safe Each carries `spec.deletionPolicy: Retain`, and that is enforced, not decorative. In `libseanfarm-operator` v0.4.13 -- the exact version the deployed `openbao-operator` v0.12.32 pins -- `reconcile/harness.go:401`: ```go if policy != DeletionPolicyRetain && !dryRun { ... h.Delete(ctx, obj, c) ... } ``` `Retain` skips the whole remote-delete block. The `UnmountSecrets` call that would destroy the transit mount and the cosign signing keys lives inside it (`openbaotransitbackend_impl.go:152`), so it is never reached. The accessor returns `spec.deletionPolicy` directly, with no defaulting. So when the XR is retired, those nine CRs are removed and recreated by Flux, while the OpenBao-side state -- both PKI mounts, the intermediate CA, the three issuing roles, the transit mount and its three keys -- is untouched. There is no functional outage, only a window with no drift correction. ## Risk None to live state. Adding a deletion policy changes nothing while the XR exists; it only changes what happens when the `Object` is eventually removed. `tests/check.sh` exits 0. https://claude.ai/code/session_01GfkEuwuvGSqGyXVuxwT7PA
fix(platform): orphan the shared OpenBaoConnection on XR retirement
All checks were successful
binjovi/ci Binjovi completed the frozen plan
f6599ee180
The openbao-connection composed Object sets no deletionPolicy, so it takes
the provider default, Delete. Retiring this XR would therefore delete the
shared OpenBaoConnection record.

Five live OpenBaoDynamicSecrets resolve credentials through that record, by
name, over two hops: OpenBaoDynamicSecret -> openbaoAuthRef -> OpenBaoAuth ->
openbaoConnectionRef openbao/openbao. They are forgejo-db-user, n8n-secrets,
hydra-secrets, kratos-secrets and outline-database, across four tenant
namespaces. Deleting the record breaks credential renewal in all four until
Flux restores it.

The CR is already declared in seanfarm
kubernetes/flux/infrastructure/openbao-platform, so Orphan hands it to Flux
instead of destroying it.

The other nine composed OpenBao CRs are unaffected: they carry an inline
openbaoRef.connection.address, not a named reference to this record.

Nothing changes while the XR exists. A deletion policy only takes effect when
the Object is removed.

Claude-Session: https://claude.ai/code/session_01GfkEuwuvGSqGyXVuxwT7PA
binjovi-bot deleted branch feat/openbao-connection-orphan 2026-09-14 16:28:03 +00:00
Sign in to join this conversation.
No reviewers
No labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
sean/platform-seanfarm!684
No description provided.