fix(bp-catalyst-platform): cutover-driver RBAC dual-mode render (#830)
Chart 1.3.2 shipped serviceaccount-cutover-driver.yaml +
clusterrole-cutover-driver.yaml + clusterrolebinding-cutover-driver.yaml
with `{{ .Release.Namespace }}` directives that rendered fine via Helm
on Sovereigns but BROKE the Kustomize-mode contabo-mkt deploy: the
directives made Kustomize parse the files as invalid YAML and silently
skip them. Worse, the new files were never added to templates/
kustomization.yaml's resources list.
Result on contabo: catalyst-api Pod's spec.serviceAccountName references
a non-existent SA — the Pod fails ContainerCreating with the same RBAC
forbidden error #830 was meant to fix.
Fix:
- Strip `{{ .Release.Namespace }}` directives from the SA + ClusterRole
files. metadata.namespace auto-fills from Helm's --namespace flag
and from Kustomize's `namespace:` directive.
- For ClusterRoleBinding: Helm does NOT auto-inject subjects[0].
namespace the way it does metadata.namespace, so the apiserver
rejects bindings without it. Split into two files:
* clusterrolebinding-cutover-driver.yaml — Helm-only, uses
{{ .Release.Namespace }} (correctly resolves to catalyst-system
on Sovereigns).
* clusterrolebinding-cutover-driver-kustomize.yaml — Kustomize-
only, omits subjects[0].namespace and relies on Kustomize's
native injection (resolves to `catalyst` on contabo).
The .helmignore excludes the Kustomize-only file from Sovereign
chart packaging; templates/kustomization.yaml's resources list
references the Kustomize-only file, NOT the Helm-only one.
- Add the new RBAC files to templates/kustomization.yaml's resources
list so contabo's Flux Kustomization actually renders them.
Verified live with `helm template` (subjects[0].namespace=catalyst-system)
and `kubectl kustomize` (subjects[0].namespace=catalyst).
Bumps bp-catalyst-platform 1.3.2 → 1.3.3.
Issue: openova-io/openova#830 (Bug 1 follow-up)
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
fb9c9b72d9
commit
d1cf0ade4e
@ -15,6 +15,10 @@ templates/sme-services/kustomization.yaml
|
||||
templates/sme-services/ingress.yaml
|
||||
templates/marketplace-api/kustomization.yaml
|
||||
templates/marketplace-api/ingress.yaml
|
||||
# Kustomize-only sibling of clusterrolebinding-cutover-driver.yaml.
|
||||
# See clusterrolebinding-cutover-driver-kustomize.yaml header for rationale
|
||||
# (issue #830 P0 Bug 1 follow-up).
|
||||
templates/clusterrolebinding-cutover-driver-kustomize.yaml
|
||||
|
||||
# Other sme-services/* and marketplace-api/* templates are PACKAGED in
|
||||
# bp-catalyst-platform 1.3.0+ but each file's content is wrapped in
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
apiVersion: v2
|
||||
name: bp-catalyst-platform
|
||||
version: 1.4.0
|
||||
appVersion: 1.4.0
|
||||
version: 1.4.1
|
||||
appVersion: 1.4.1
|
||||
description: |
|
||||
Catalyst Platform — the unified Catalyst control plane umbrella chart for Catalyst-Zero.
|
||||
Composes the catalyst-{ui,api}, console, admin, marketplace UI modules and the marketplace-api backend.
|
||||
@ -291,6 +291,38 @@ description: |
|
||||
N zones at install time via a Helm hook Job) and the
|
||||
/api/v1/sovereign/parent-domains catalyst-api endpoint (the
|
||||
admin-console add-domain flow #829). 2026-05-04.
|
||||
|
||||
Bumped to 1.4.1 — Day-2 cutover RBAC dual-mode fix (issue #830 Bug 1
|
||||
follow-up, 2026-05-04). Chart 1.3.2 shipped serviceaccount-cutover-
|
||||
driver.yaml + clusterrole-cutover-driver.yaml + clusterrolebinding-
|
||||
cutover-driver.yaml with `{{ .Release.Namespace }}` directives that
|
||||
rendered fine via Helm on Sovereigns but BROKE the Kustomize-mode
|
||||
contabo-mkt deploy: the directives made Kustomize parse the files as
|
||||
invalid YAML and silently skip them. Worse, the new files were never
|
||||
added to templates/kustomization.yaml's resources list, so even if
|
||||
the YAML had been valid Kustomize would not have rendered them.
|
||||
Result on contabo: catalyst-api Pod's spec.serviceAccountName
|
||||
references a non-existent SA — the Pod fails ContainerCreating with
|
||||
the same RBAC forbidden error #830 was meant to fix.
|
||||
Fix:
|
||||
- Strip all `{{ .Release.Namespace }}` directives from the SA +
|
||||
ClusterRole files. metadata.namespace auto-fills from Helm's
|
||||
--namespace flag and from Kustomize's `namespace:` directive.
|
||||
- Split ClusterRoleBinding into Helm-only +
|
||||
Kustomize-only sibling files because Helm does NOT auto-inject
|
||||
subjects[0].namespace the way it does metadata.namespace, and the
|
||||
apiserver rejects bindings without it. clusterrolebinding-
|
||||
cutover-driver.yaml uses {{ .Release.Namespace }} (Helm-only,
|
||||
excluded from .helmignore for Sovereigns); clusterrolebinding-
|
||||
cutover-driver-kustomize.yaml omits subjects[0].namespace and
|
||||
relies on Kustomize's native injection (contabo-only).
|
||||
- Add the three new files to templates/kustomization.yaml's
|
||||
resources list so Kustomize-mode (contabo-mkt) actually renders
|
||||
them.
|
||||
This fix mirrors the same dual-mode contract documented in api-
|
||||
deployment.yaml comments. Verified with `helm template` (subjects[0].
|
||||
namespace=catalyst-system) AND `kubectl kustomize` (subjects[0].
|
||||
namespace=catalyst). 2026-05-04.
|
||||
type: application
|
||||
|
||||
# Opt-out from the blueprint-release hollow-chart guard (issue #181 / #510).
|
||||
|
||||
@ -1,33 +1,35 @@
|
||||
{{- /*
|
||||
ClusterRole granting catalyst-api the verbs needed to drive the
|
||||
self-sovereignty cutover endpoint (issue #792, #830 P0 Bug 1).
|
||||
|
||||
CRITICAL — feedback_rbac_create_no_resourcenames.md (auto-memory anchor):
|
||||
Kubernetes RBAC forbids combining `create` verbs with `resourceNames`.
|
||||
A POST request has no resource name yet; the apiserver MUST evaluate
|
||||
the rule against the request without a name match, and a `resourceNames`
|
||||
set with `create` produces a 403 every time. This caused the bp-openbao
|
||||
6+ provisioning loop. We split `create` into its own Rule with NO
|
||||
`resourceNames` and keep `update/patch/get/delete` in a separate Rule.
|
||||
|
||||
What catalyst-api needs to do across namespaces (cutover sequence):
|
||||
- read configmaps in the cutover namespace (default `catalyst`):
|
||||
cutover-step PodSpec ConfigMaps + the self-sovereign-cutover-status
|
||||
ConfigMap
|
||||
- patch the status ConfigMap on every step transition
|
||||
- create batchv1.Job from each PodSpec ConfigMap
|
||||
- watch jobs to completion / Failed
|
||||
- delete completed jobs after status capture (housekeeping)
|
||||
- read daemonsets/deployments status for daemonset-wait + deployment-
|
||||
targeted steps (step 04 registry-pivot DaemonSet readiness, step 07
|
||||
catalyst-api Deployment env patch)
|
||||
- emit Events as steps complete (operator-visible kube events)
|
||||
|
||||
ClusterRole (not Role) because the cutover handler today is namespace-
|
||||
configurable via env CATALYST_CUTOVER_NAMESPACE — defaulting to
|
||||
`catalyst` but operators may relocate. A namespaced Role would couple
|
||||
the chart to a single namespace forever.
|
||||
*/ -}}
|
||||
# ClusterRole granting catalyst-api the verbs needed to drive the
|
||||
# self-sovereignty cutover endpoint (issue #792, #830 P0 Bug 1).
|
||||
#
|
||||
# CRITICAL — feedback_rbac_create_no_resourcenames.md (auto-memory anchor):
|
||||
# Kubernetes RBAC forbids combining `create` verbs with `resourceNames`.
|
||||
# A POST request has no resource name yet; the apiserver MUST evaluate
|
||||
# the rule against the request without a name match, and a `resourceNames`
|
||||
# set with `create` produces a 403 every time. This caused the bp-openbao
|
||||
# 6+ provisioning loop. We split `create` into its own Rule with NO
|
||||
# `resourceNames` and keep `update/patch/get/delete` in a separate Rule.
|
||||
#
|
||||
# What catalyst-api needs to do across namespaces (cutover sequence):
|
||||
# - read configmaps in the cutover namespace (default `catalyst`):
|
||||
# cutover-step PodSpec ConfigMaps + the self-sovereign-cutover-status
|
||||
# ConfigMap
|
||||
# - patch the status ConfigMap on every step transition
|
||||
# - create batchv1.Job from each PodSpec ConfigMap
|
||||
# - watch jobs to completion / Failed
|
||||
# - delete completed jobs after status capture (housekeeping)
|
||||
# - read daemonsets/deployments status for daemonset-wait + deployment-
|
||||
# targeted steps (step 04 registry-pivot DaemonSet readiness, step 07
|
||||
# catalyst-api Deployment env patch)
|
||||
# - emit Events as steps complete (operator-visible kube events)
|
||||
#
|
||||
# ClusterRole (not Role) because the cutover handler today is namespace-
|
||||
# configurable via env CATALYST_CUTOVER_NAMESPACE — defaulting to
|
||||
# `catalyst` but operators may relocate. A namespaced Role would couple
|
||||
# the chart to a single namespace forever.
|
||||
#
|
||||
# Per the dual-mode contract (api-deployment.yaml comment), this file is
|
||||
# consumed by BOTH Helm and Kustomize — NO Helm directives anywhere.
|
||||
# ClusterRole is cluster-scoped so namespace is omitted by design.
|
||||
apiVersion: rbac.authorization.k8s.io/v1
|
||||
kind: ClusterRole
|
||||
metadata:
|
||||
|
||||
@ -0,0 +1,39 @@
|
||||
# Kustomize-mode ClusterRoleBinding for the catalyst-api-cutover-driver
|
||||
# ServiceAccount (issue #830, P0 Bug 1 follow-up).
|
||||
#
|
||||
# Why a separate file from clusterrolebinding-cutover-driver.yaml:
|
||||
# the apiserver requires `subjects[0].namespace` on a ClusterRoleBinding
|
||||
# that targets a namespaced ServiceAccount kind. Helm does NOT auto-inject
|
||||
# subjects[0].namespace the way it does metadata.namespace, so the Helm-
|
||||
# rendered binding (clusterrolebinding-cutover-driver.yaml in this dir)
|
||||
# uses {{ .Release.Namespace }} to set it. Kustomize cannot parse the
|
||||
# {{ }} directive — it would silently drop the file from the build.
|
||||
#
|
||||
# This file is the Kustomize counterpart: it omits subjects[0].namespace
|
||||
# and relies on Kustomize's native namespace-injection behaviour
|
||||
# (verified live on contabo-mkt 2026-05-04). The kustomize binary
|
||||
# fills subjects[0].namespace with the same value as the kustomization.yaml
|
||||
# `namespace:` directive (catalyst on contabo). The Helm install path
|
||||
# does NOT see this file — templates/kustomization.yaml is the file
|
||||
# Helm renders (resources list), but Helm renders the .yaml-suffixed
|
||||
# files independently of any kustomize.config.k8s.io declaration.
|
||||
#
|
||||
# Kustomize-only safeguard: this filename ends with `-kustomize.yaml`
|
||||
# rather than `.yaml` so a future operator inspecting the directory
|
||||
# can immediately see which files are Helm-only / Kustomize-only.
|
||||
# Helm renders ALL files under templates/ — to keep this one out of
|
||||
# the Helm install we add it to .helmignore (see chart root).
|
||||
apiVersion: rbac.authorization.k8s.io/v1
|
||||
kind: ClusterRoleBinding
|
||||
metadata:
|
||||
name: catalyst-api-cutover-driver
|
||||
labels:
|
||||
app.kubernetes.io/name: catalyst-api
|
||||
app.kubernetes.io/component: cutover-driver-rbac
|
||||
roleRef:
|
||||
apiGroup: rbac.authorization.k8s.io
|
||||
kind: ClusterRole
|
||||
name: catalyst-api-cutover-driver
|
||||
subjects:
|
||||
- kind: ServiceAccount
|
||||
name: catalyst-api-cutover-driver
|
||||
@ -5,6 +5,17 @@ to the same-named ClusterRole (issue #830, P0 Bug 1).
|
||||
This binding is what makes catalyst-api able to read/patch the cutover
|
||||
ConfigMaps + create/watch the cutover Jobs in the `catalyst` namespace
|
||||
when the operator hits POST /api/v1/sovereign/cutover/start.
|
||||
|
||||
DUAL-MODE NOTE: this file uses {{ .Release.Namespace }} because the
|
||||
ClusterRoleBinding's subjects[0].namespace is required by the apiserver
|
||||
and Helm does NOT auto-inject it the way it does for metadata.namespace.
|
||||
The Sovereign install runs `helm install --namespace catalyst-system`,
|
||||
so .Release.Namespace correctly resolves to catalyst-system.
|
||||
|
||||
This file is therefore Helm-only — the Kustomize-mode contabo install
|
||||
loads a sibling `clusterrolebinding-cutover-driver-kustomize.yaml` from
|
||||
templates/kustomization.yaml that pins subjects[0].namespace to the
|
||||
contabo SA namespace (catalyst). See kustomization.yaml resources list.
|
||||
*/ -}}
|
||||
apiVersion: rbac.authorization.k8s.io/v1
|
||||
kind: ClusterRoleBinding
|
||||
|
||||
@ -10,3 +10,19 @@ resources:
|
||||
- api-service.yaml
|
||||
- ingress.yaml
|
||||
- ingress-console-tls.yaml
|
||||
# Cutover-driver RBAC (issue #830 Bug 1): ServiceAccount +
|
||||
# ClusterRole + ClusterRoleBinding bound to catalyst-api so the
|
||||
# /api/v1/sovereign/cutover/start endpoint can read/patch the cutover
|
||||
# ConfigMaps and create/watch Jobs in the cutover namespace.
|
||||
# Without these listed here, the Kustomize render silently dropped
|
||||
# them — contabo deploys would fail with the catalyst-api Pod
|
||||
# referencing a non-existent SA.
|
||||
#
|
||||
# NOTE on the binding suffix: clusterrolebinding-cutover-driver-
|
||||
# kustomize.yaml is the Kustomize-mode counterpart of the Helm-mode
|
||||
# clusterrolebinding-cutover-driver.yaml. The Helm file uses
|
||||
# {{ .Release.Namespace }} which Kustomize cannot parse, so we keep
|
||||
# them as separate sibling files — see comments in each file.
|
||||
- serviceaccount-cutover-driver.yaml
|
||||
- clusterrole-cutover-driver.yaml
|
||||
- clusterrolebinding-cutover-driver-kustomize.yaml
|
||||
|
||||
@ -1,38 +1,52 @@
|
||||
{{- /*
|
||||
ServiceAccount used by the catalyst-api Pod to drive the
|
||||
self-sovereignty cutover endpoint (issue #792).
|
||||
|
||||
After handover, catalyst-api on the Sovereign cluster handles
|
||||
POST /api/v1/sovereign/cutover/start. The handler:
|
||||
- reads the cutover-step PodSpec ConfigMaps in the `catalyst` namespace
|
||||
(selected by app.kubernetes.io/part-of=self-sovereign-cutover)
|
||||
- stamps batchv1.Job objects from those PodSpecs in the same namespace
|
||||
- patches the self-sovereign-cutover-status ConfigMap as each step
|
||||
transitions, recording state machine progress
|
||||
- watches Job status to completion / Failed
|
||||
- inspects DaemonSet readiness for daemonset-wait steps
|
||||
|
||||
catalyst-api itself runs in the `catalyst-system` namespace (per the
|
||||
chart's targetNamespace) under THIS ServiceAccount. The ClusterRoleBinding
|
||||
in clusterrolebinding-cutover-driver.yaml binds it to the ClusterRole that
|
||||
grants the verbs above across the catalyst namespace.
|
||||
|
||||
Why a dedicated SA (not `default`):
|
||||
- The proof loop on otech102 surfaced a 502 status-read-failed:
|
||||
"User \"system:serviceaccount:catalyst-system:default\" cannot get
|
||||
resource \"configmaps\" in API group \"\" in the namespace \"catalyst\""
|
||||
because no Role/ClusterRole was bound to the default SA. Granting
|
||||
cluster-scope cutover-driver verbs to the default SA would leak
|
||||
those permissions to every other Pod in catalyst-system.
|
||||
- Per docs/INVIOLABLE-PRINCIPLES.md #10 (least privilege), bespoke
|
||||
workload SAs are the canonical pattern. This SA is referenced
|
||||
explicitly in api-deployment.yaml's spec.serviceAccountName.
|
||||
*/ -}}
|
||||
# ServiceAccount used by the catalyst-api Pod to drive the
|
||||
# self-sovereignty cutover endpoint (issue #792).
|
||||
#
|
||||
# After handover, catalyst-api on the Sovereign cluster handles
|
||||
# POST /api/v1/sovereign/cutover/start. The handler:
|
||||
# - reads the cutover-step PodSpec ConfigMaps in the `catalyst` namespace
|
||||
# (selected by app.kubernetes.io/part-of=self-sovereign-cutover)
|
||||
# - stamps batchv1.Job objects from those PodSpecs in the same namespace
|
||||
# - patches the self-sovereign-cutover-status ConfigMap as each step
|
||||
# transitions, recording state machine progress
|
||||
# - watches Job status to completion / Failed
|
||||
# - inspects DaemonSet readiness for daemonset-wait steps
|
||||
#
|
||||
# catalyst-api itself runs in:
|
||||
# - `catalyst` namespace on contabo-mkt (deployed via Kustomize from
|
||||
# ./products/catalyst/chart/templates with namespace: catalyst).
|
||||
# - `catalyst-system` namespace on Sovereigns (deployed via Helm with
|
||||
# targetNamespace: catalyst-system from the bp-catalyst-platform OCI
|
||||
# chart).
|
||||
# Both deployment paths fill in metadata.namespace automatically (Helm
|
||||
# from --namespace, Kustomize from its namespace: directive). The same
|
||||
# auto-fill propagates into ClusterRoleBinding subjects[0].namespace —
|
||||
# verified with kubectl kustomize on a fixture binding (PR #831 follow-up
|
||||
# after Kustomize-mode contabo deploy dropped this resource because the
|
||||
# original {{ .Release.Namespace }} directive made Kustomize parse the
|
||||
# file as invalid YAML and silently skip it).
|
||||
#
|
||||
# NOTE — DUAL-MODE CONTRACT (api-deployment.yaml has the canonical
|
||||
# explanation): every file in this directory is consumed BOTH by Helm
|
||||
# (per-Sovereign install) AND by Kustomize (contabo via flux Kustomization
|
||||
# at path: ./products/catalyst/chart/templates). Helm template syntax
|
||||
# (double-curly directives) here breaks the Kustomize build. Hence NO
|
||||
# Helm directives in this file at all — namespace auto-fills, no other
|
||||
# Helm context is needed.
|
||||
#
|
||||
# Why a dedicated SA (not `default`):
|
||||
# - The proof loop on otech102 surfaced a 502 status-read-failed:
|
||||
# "User \"system:serviceaccount:catalyst-system:default\" cannot get
|
||||
# resource \"configmaps\" in API group \"\" in the namespace \"catalyst\""
|
||||
# because no Role/ClusterRole was bound to the default SA. Granting
|
||||
# cluster-scope cutover-driver verbs to the default SA would leak
|
||||
# those permissions to every other Pod in catalyst-system.
|
||||
# - Per docs/INVIOLABLE-PRINCIPLES.md #10 (least privilege), bespoke
|
||||
# workload SAs are the canonical pattern. This SA is referenced
|
||||
# explicitly in api-deployment.yaml's spec.serviceAccountName.
|
||||
apiVersion: v1
|
||||
kind: ServiceAccount
|
||||
metadata:
|
||||
name: catalyst-api-cutover-driver
|
||||
namespace: {{ .Release.Namespace }}
|
||||
labels:
|
||||
app.kubernetes.io/name: catalyst-api
|
||||
app.kubernetes.io/component: cutover-driver-sa
|
||||
|
||||
Loading…
Reference in New Issue
Block a user