From bd1089afd8efc22bf3f2cd033dd39bdc1f7c9483 Mon Sep 17 00:00:00 2001 From: Przemyslaw Roguski Date: Fri, 8 May 2026 15:05:03 +0200 Subject: [PATCH 1/8] Adding an option to ACS to use cluster CA, not self signed, for ACS Central --- charts/acs-central/templates/central-cr.yaml | 7 +++++++ charts/acs-central/templates/console-link.yaml | 4 ++++ .../acs-central/templates/jobs/create-auth-provider.yaml | 2 +- charts/acs-central/values.yaml | 3 ++- 4 files changed, 14 insertions(+), 2 deletions(-) diff --git a/charts/acs-central/templates/central-cr.yaml b/charts/acs-central/templates/central-cr.yaml index 6b91dac4..b11d9138 100644 --- a/charts/acs-central/templates/central-cr.yaml +++ b/charts/acs-central/templates/central-cr.yaml @@ -21,6 +21,13 @@ spec: port: 443 route: enabled: {{ .Values.central.exposure.route.enabled }} + {{- if .Values.central.exposure.route.reencrypt.enabled }} + reencrypt: + enabled: true + {{- if .Values.central.exposure.route.reencrypt.host }} + host: {{ .Values.central.exposure.route.reencrypt.host }} + {{- end }} + {{- end }} {{- if .Values.central.persistence.enabled }} persistence: diff --git a/charts/acs-central/templates/console-link.yaml b/charts/acs-central/templates/console-link.yaml index 3030fa41..9cb25e01 100644 --- a/charts/acs-central/templates/console-link.yaml +++ b/charts/acs-central/templates/console-link.yaml @@ -8,7 +8,11 @@ metadata: annotations: argocd.argoproj.io/sync-wave: "46" spec: + {{- if .Values.central.exposure.route.reencrypt.enabled }} + href: https://central-reencrypt-{{ .Release.Namespace }}.{{ .Values.global.localClusterDomain }} + {{- else }} href: https://central-{{ .Release.Namespace }}.{{ .Values.global.localClusterDomain }} + {{- end }} location: ApplicationMenu text: Advanced Cluster Security applicationMenu: diff --git a/charts/acs-central/templates/jobs/create-auth-provider.yaml b/charts/acs-central/templates/jobs/create-auth-provider.yaml index ae5dc4fb..71626a01 100644 --- a/charts/acs-central/templates/jobs/create-auth-provider.yaml +++ b/charts/acs-central/templates/jobs/create-auth-provider.yaml @@ -86,7 +86,7 @@ spec: exit 0 fi - ACS_CENTRAL_HOSTNAME="$(oc get route central -n stackrox -o jsonpath='{.spec.host}')" + ACS_CENTRAL_HOSTNAME="$(oc get route central-reencrypt -n stackrox -o jsonpath='{.spec.host}' 2>/dev/null || oc get route central -n stackrox -o jsonpath='{.spec.host}')" echo "ACS Central hostname: $ACS_CENTRAL_HOSTNAME" cat > /tmp/oidc-config.json << 'OIDCEOF' diff --git a/charts/acs-central/values.yaml b/charts/acs-central/values.yaml index 9c01ba9d..2f30f67e 100644 --- a/charts/acs-central/values.yaml +++ b/charts/acs-central/values.yaml @@ -73,10 +73,11 @@ central: exposure: route: enabled: true - # Use cluster wildcard certificate tls: enabled: true termination: passthrough + reencrypt: + enabled: true loadBalancer: enabled: false From 479f2f734537b34a526dda84f7a4eaa3ffa1051b Mon Sep 17 00:00:00 2001 From: Przemyslaw Roguski Date: Mon, 11 May 2026 19:24:35 +0200 Subject: [PATCH 2/8] Adding explanation how ACS handles two OCP routes --- docs/acs-deployment.md | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/docs/acs-deployment.md b/docs/acs-deployment.md index 1d694d0a..7e67ee48 100644 --- a/docs/acs-deployment.md +++ b/docs/acs-deployment.md @@ -48,6 +48,34 @@ The ACS deployment in the Layered Zero Trust pattern is implemented using: - Admission Controller (policy enforcement) - Collector (DaemonSet for runtime monitoring) +## Route and TLS Configuration + +ACS Central exposes two OpenShift routes with different TLS termination modes: + +| Route | TLS Mode | Purpose | +|---|---|---| +| `central` | Passthrough | Sensor/SecuredCluster gRPC communication (mTLS) | +| `central-reencrypt` | Reencrypt | Browser UI access using cluster wildcard certificate | + +The **passthrough route is required** for sensor communication. Sensors use +mutual TLS with certificates from the cluster init bundle, and the RHACS +operator [explicitly states](https://github.com/stackrox/stackrox/blob/master/operator/api/v1alpha1/central_types.go) +that the reencrypt route *"should not be used for sensor communication"* +because the router terminates the sensor's TLS session, breaking mTLS +authentication. + +The **reencrypt route** is enabled by default (`central.exposure.route.reencrypt.enabled: true`) +so that browser users see the cluster's wildcard certificate instead of +Central's self-signed certificate. This works on all platforms: + +- **Cloud (AWS, Azure, GCP)**: wildcard cert is signed by a public CA — no browser warning +- **BareMetal / vSphere**: wildcard cert uses the cluster ingress CA — trusted + if `ztvp-certificates` has injected it via `proxyCA` + +The RHACS operator auto-generates the reencrypt route hostname +(`central-reencrypt-stackrox.apps.`). The ConsoleLink and OIDC auth +provider `uiEndpoint` automatically point to the reencrypt route when enabled. + ## Deployment Workflow ### Phase 1: Operator Installation (Managed by Pattern Framework) From cd3dc6e4f576bb857062d0782327ab1275e5398a Mon Sep 17 00:00:00 2001 From: Przemyslaw Roguski Date: Wed, 13 May 2026 13:18:47 +0200 Subject: [PATCH 3/8] switching from the internal OCP image registry (image-registry.openshift-image-registry.svc:5000/openshift/cli) to the public Red Hat registry (registry.redhat.io/openshift4/ose-cli) --- charts/acs-central/values.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/charts/acs-central/values.yaml b/charts/acs-central/values.yaml index 2f30f67e..46a7dba0 100644 --- a/charts/acs-central/values.yaml +++ b/charts/acs-central/values.yaml @@ -171,8 +171,8 @@ integration: # Container image for the auth provider configuration job # Uses OpenShift CLI tools (curl, oc, jq, etc.) jobImage: - registry: image-registry.openshift-image-registry.svc:5000 - repository: openshift/cli + registry: registry.redhat.io + repository: openshift4/ose-cli tag: latest pullPolicy: IfNotPresent # Service account used by jobs (init bundle, auth provider, htpasswd) From ca9a5d5f70535dae2429bf42698b70847e31f3e1 Mon Sep 17 00:00:00 2001 From: Przemyslaw Roguski Date: Thu, 14 May 2026 11:11:56 +0200 Subject: [PATCH 4/8] UX polish, the user-facing URL becomes central.apps. which is simpler and more intuitive --- charts/acs-central/templates/central-cr.yaml | 3 +++ charts/acs-central/templates/console-link.yaml | 10 +++++++++- charts/acs-central/values.yaml | 2 ++ values-hub.yaml | 2 ++ 4 files changed, 16 insertions(+), 1 deletion(-) diff --git a/charts/acs-central/templates/central-cr.yaml b/charts/acs-central/templates/central-cr.yaml index b11d9138..ac56bd89 100644 --- a/charts/acs-central/templates/central-cr.yaml +++ b/charts/acs-central/templates/central-cr.yaml @@ -21,6 +21,9 @@ spec: port: 443 route: enabled: {{ .Values.central.exposure.route.enabled }} + {{- if .Values.central.exposure.route.host }} + host: {{ .Values.central.exposure.route.host }} + {{- end }} {{- if .Values.central.exposure.route.reencrypt.enabled }} reencrypt: enabled: true diff --git a/charts/acs-central/templates/console-link.yaml b/charts/acs-central/templates/console-link.yaml index 9cb25e01..01acf02a 100644 --- a/charts/acs-central/templates/console-link.yaml +++ b/charts/acs-central/templates/console-link.yaml @@ -9,10 +9,18 @@ metadata: argocd.argoproj.io/sync-wave: "46" spec: {{- if .Values.central.exposure.route.reencrypt.enabled }} - href: https://central-reencrypt-{{ .Release.Namespace }}.{{ .Values.global.localClusterDomain }} + {{- if .Values.central.exposure.route.reencrypt.host }} + href: https://{{ .Values.central.exposure.route.reencrypt.host }} + {{- else }} + href: https://central.{{ .Values.global.localClusterDomain }} + {{- end }} + {{- else }} + {{- if .Values.central.exposure.route.host }} + href: https://{{ .Values.central.exposure.route.host }} {{- else }} href: https://central-{{ .Release.Namespace }}.{{ .Values.global.localClusterDomain }} {{- end }} + {{- end }} location: ApplicationMenu text: Advanced Cluster Security applicationMenu: diff --git a/charts/acs-central/values.yaml b/charts/acs-central/values.yaml index 46a7dba0..605aad5d 100644 --- a/charts/acs-central/values.yaml +++ b/charts/acs-central/values.yaml @@ -73,11 +73,13 @@ central: exposure: route: enabled: true + host: "" # Autogenerate if not specified tls: enabled: true termination: passthrough reencrypt: enabled: true + host: "" # Autogenerate if not specified loadBalancer: enabled: false diff --git a/values-hub.yaml b/values-hub.yaml index dae08bf8..03ff30e6 100644 --- a/values-hub.yaml +++ b/values-hub.yaml @@ -594,6 +594,8 @@ clusterGroup: # value: gp3-csi # Example for AWS - name: central.exposure.route.enabled value: "true" + - name: central.exposure.route.reencrypt.host + value: "central.{{ $.Values.global.localClusterDomain }}" - name: integration.keycloak.enabled value: "true" - name: integration.keycloak.realm From 394e71a2fbd6ea6ce3eb7fc726654d4e92afb170 Mon Sep 17 00:00:00 2001 From: Przemyslaw Roguski Date: Thu, 14 May 2026 11:35:04 +0200 Subject: [PATCH 5/8] Using the newest OC CLI image, built based on RHEL9 --- charts/acs-central/values.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/charts/acs-central/values.yaml b/charts/acs-central/values.yaml index 605aad5d..1d8c965a 100644 --- a/charts/acs-central/values.yaml +++ b/charts/acs-central/values.yaml @@ -174,7 +174,7 @@ integration: # Uses OpenShift CLI tools (curl, oc, jq, etc.) jobImage: registry: registry.redhat.io - repository: openshift4/ose-cli + repository: openshift4/ose-cli-rhel9 tag: latest pullPolicy: IfNotPresent # Service account used by jobs (init bundle, auth provider, htpasswd) From 28d53bfb0445692915a7eb48e98d5ac3ae0afb11 Mon Sep 17 00:00:00 2001 From: Przemyslaw Roguski Date: Fri, 19 Jun 2026 19:04:12 +0200 Subject: [PATCH 6/8] feat: replace deprecated ACS init-bundle with CRS approach Replace the init-bundle Job with a Cluster Registration Secret (CRS) job per RHACS 4.10 recommendations. CRS uses a short-lived token instead of long-lived certificates, improving security posture. The CRS can be revoked after cluster registration without disconnecting the secured cluster. - Rename create-cluster-init-bundle.yaml to create-cluster-registration-secret.yaml - Switch API from /v1/cluster-init/init-bundles to /v1/cluster-init/crs - Simplify job logic: remove bundle listing/deletion/Python parsing - Add dual idempotency check (sensor-tls OR cluster-registration-secret) - Update comments in values files to reference CRS Co-authored-by: Cursor --- .../jobs/create-cluster-init-bundle.yaml | 149 ------------------ .../create-cluster-registration-secret.yaml | 119 ++++++++++++++ charts/acs-central/values.yaml | 2 +- charts/acs-secured-cluster/values.yaml | 5 +- values-hub.yaml | 2 +- 5 files changed, 124 insertions(+), 153 deletions(-) delete mode 100644 charts/acs-central/templates/jobs/create-cluster-init-bundle.yaml create mode 100644 charts/acs-central/templates/jobs/create-cluster-registration-secret.yaml diff --git a/charts/acs-central/templates/jobs/create-cluster-init-bundle.yaml b/charts/acs-central/templates/jobs/create-cluster-init-bundle.yaml deleted file mode 100644 index bf1bbfa0..00000000 --- a/charts/acs-central/templates/jobs/create-cluster-init-bundle.yaml +++ /dev/null @@ -1,149 +0,0 @@ -# NOTE: This init bundle is for the local cluster (hub) only. -# For multi-cluster deployments, create separate init bundle jobs -# for each remote secured cluster with their specific cluster names. -# The cluster name is sanitized to replace underscores and dots with hyphens. -# - -{{- if .Values.central.enabled }} -apiVersion: batch/v1 -kind: Job -metadata: - name: create-cluster-init-bundle - namespace: {{ .Release.Namespace }} - labels: - {{- include "acs-central.labels" . | nindent 4 }} - annotations: - argocd.argoproj.io/sync-options: SkipDryRunOnMissingResource=true - argocd.argoproj.io/sync-wave: "43" - argocd.argoproj.io/hook: Sync - argocd.argoproj.io/hook-delete-policy: BeforeHookCreation -spec: - template: - metadata: - name: create-cluster-init-bundle - labels: - app: create-cluster-init-bundle - {{- include "acs-central.labels" . | nindent 8 }} - spec: - containers: - - image: {{ .Values.integration.keycloak.jobImage.registry }}/{{ .Values.integration.keycloak.jobImage.repository }}:{{ .Values.integration.keycloak.jobImage.tag }} - imagePullPolicy: {{ .Values.integration.keycloak.jobImage.pullPolicy }} - env: - - name: PASSWORD - valueFrom: - secretKeyRef: - name: {{ .Values.central.adminPassword.secretName }} - key: {{ .Values.central.adminPassword.secretKey }} - command: - - /bin/bash - - -c - - | - #!/usr/bin/env bash - - # Check if init bundle already exists - if kubectl get secret/sensor-tls &> /dev/null; then - echo "✅ Cluster init bundle already configured, skipping" - exit 0 - fi - - echo "🔄 Waiting for ACS Central to be ready..." - attempt_counter=0 - max_attempts=30 - - until $(curl -k --output /dev/null --silent --head --fail https://central); do - if [ ${attempt_counter} -eq ${max_attempts} ]; then - echo "❌ Max attempts reached waiting for Central" - exit 1 - fi - - printf '.' - attempt_counter=$(($attempt_counter+1)) - sleep 10 - done - - echo "" - echo "✅ Central is ready" - - CLUSTER_NAME="{{ .Values.clusterName | default .Values.global.localClusterName }}" - echo "🔄 Checking for existing init bundles..." - - # List existing init bundles - curl -k -s -u "admin:$PASSWORD" \ - https://central/v1/cluster-init/init-bundles > /tmp/bundles_list.json - - # Check if bundle with this cluster name already exists - printf '%s\n' \ - 'import sys, json' \ - 'try:' \ - ' data = json.load(open("/tmp/bundles_list.json"))' \ - ' items = data.get("items", [])' \ - ' for item in items:' \ - ' if item.get("name") == sys.argv[1]:' \ - ' print(item.get("id", ""))' \ - ' break' \ - 'except:' \ - ' pass' \ - > /tmp/find_bundle.py - - EXISTING_BUNDLE_ID=$(python3 /tmp/find_bundle.py "$CLUSTER_NAME" 2>/dev/null) - - # If bundle exists, delete it first - if [ ! -z "$EXISTING_BUNDLE_ID" ]; then - echo "⚠️ Init bundle '$CLUSTER_NAME' already exists (ID: $EXISTING_BUNDLE_ID), deleting..." - curl -k -X DELETE -u "admin:$PASSWORD" \ - "https://central/v1/cluster-init/init-bundles/$EXISTING_BUNDLE_ID" \ - -o /tmp/delete_result.json - echo "✅ Existing bundle deleted" - sleep 2 - fi - - # Generate new init bundle via API - echo "🔄 Generating cluster init bundle..." - export DATA="{\"name\":\"$CLUSTER_NAME\"}" - - curl -k -o /tmp/bundle.json -X POST \ - -u "admin:$PASSWORD" \ - -H "Content-Type: application/json" \ - --data "$DATA" \ - https://central/v1/cluster-init/init-bundles - - if [ $? -ne 0 ]; then - echo "❌ Failed to generate init bundle" - cat /tmp/bundle.json - exit 1 - fi - - # Check if response contains kubectlBundle - if ! python3 -c 'import sys, json; json.load(open("/tmp/bundle.json"))["kubectlBundle"]' &> /dev/null; then - echo "❌ API response does not contain kubectlBundle:" - cat /tmp/bundle.json | python3 -m json.tool - exit 1 - fi - - echo "✅ Bundle received" - - # Extract and apply bundle - echo "🔄 Applying init bundle secrets..." - cat /tmp/bundle.json | python3 -c 'import sys, json; print(json.load(sys.stdin)["kubectlBundle"])' | base64 -d | oc apply -f - - - if [ $? -eq 0 ]; then - echo "✅ Init bundle secrets applied successfully" - - # Label SecuredCluster to trigger reconciliation - if oc get SecuredCluster stackrox-secured-cluster-services &> /dev/null; then - oc label SecuredCluster stackrox-secured-cluster-services cluster-init-job-status=created --overwrite - echo "✅ SecuredCluster labeled for reconciliation" - fi - else - echo "❌ Failed to apply init bundle" - exit 1 - fi - - echo "🎉 ACS cluster init bundle configuration complete" - name: create-cluster-init-bundle - dnsPolicy: ClusterFirst - restartPolicy: Never - serviceAccount: {{ .Values.integration.keycloak.serviceAccountName }} - serviceAccountName: {{ .Values.integration.keycloak.serviceAccountName }} - terminationGracePeriodSeconds: 30 -{{- end }} \ No newline at end of file diff --git a/charts/acs-central/templates/jobs/create-cluster-registration-secret.yaml b/charts/acs-central/templates/jobs/create-cluster-registration-secret.yaml new file mode 100644 index 00000000..375cfeab --- /dev/null +++ b/charts/acs-central/templates/jobs/create-cluster-registration-secret.yaml @@ -0,0 +1,119 @@ +# NOTE: This CRS (Cluster Registration Secret) is for the local cluster (hub) only. +# For multi-cluster deployments, create separate CRS jobs +# for each remote secured cluster with their specific cluster names. +# +# CRS replaces the deprecated init-bundle approach (RHACS 4.10+). +# Unlike init bundles which contain long-lived certificates, a CRS contains +# a token that the secured cluster uses to request its own certificates. +# The CRS can be revoked after registration without disconnecting the cluster. + +{{- if .Values.central.enabled }} +apiVersion: batch/v1 +kind: Job +metadata: + name: create-cluster-registration-secret + namespace: {{ .Release.Namespace }} + labels: + {{- include "acs-central.labels" . | nindent 4 }} + annotations: + argocd.argoproj.io/sync-options: SkipDryRunOnMissingResource=true + argocd.argoproj.io/sync-wave: "43" + argocd.argoproj.io/hook: Sync + argocd.argoproj.io/hook-delete-policy: BeforeHookCreation +spec: + template: + metadata: + name: create-cluster-registration-secret + labels: + app: create-cluster-registration-secret + {{- include "acs-central.labels" . | nindent 8 }} + spec: + containers: + - image: {{ .Values.integration.keycloak.jobImage.registry }}/{{ .Values.integration.keycloak.jobImage.repository }}:{{ .Values.integration.keycloak.jobImage.tag }} + imagePullPolicy: {{ .Values.integration.keycloak.jobImage.pullPolicy }} + env: + - name: PASSWORD + valueFrom: + secretKeyRef: + name: {{ .Values.central.adminPassword.secretName }} + key: {{ .Values.central.adminPassword.secretKey }} + command: + - /bin/bash + - -c + - | + #!/usr/bin/env bash + + # Skip if cluster is already registered (init-bundle) or CRS already applied + if kubectl get secret/sensor-tls &> /dev/null; then + echo "Cluster already registered (sensor-tls exists), skipping" + exit 0 + fi + if kubectl get secret/cluster-registration-secret &> /dev/null; then + echo "CRS already applied (cluster-registration-secret exists), skipping" + exit 0 + fi + + echo "Waiting for ACS Central to be ready..." + attempt_counter=0 + max_attempts=30 + + until $(curl -k --output /dev/null --silent --head --fail https://central); do + if [ ${attempt_counter} -eq ${max_attempts} ]; then + echo "Max attempts reached waiting for Central" + exit 1 + fi + + printf '.' + attempt_counter=$(($attempt_counter+1)) + sleep 10 + done + + echo "" + echo "Central is ready" + + CLUSTER_NAME="{{ .Values.clusterName | default .Values.global.localClusterName }}" + echo "Generating cluster registration secret for: $CLUSTER_NAME" + + curl -k -o /tmp/crs.json -X POST \ + -u "admin:$PASSWORD" \ + -H "Content-Type: application/json" \ + --data "{\"name\":\"$CLUSTER_NAME\"}" \ + https://central/v1/cluster-init/crs + + if [ $? -ne 0 ]; then + echo "Failed to generate CRS" + cat /tmp/crs.json + exit 1 + fi + + if ! python3 -c 'import sys, json; json.load(open("/tmp/crs.json"))["crs"]' &> /dev/null; then + echo "API response does not contain crs field:" + cat /tmp/crs.json | python3 -m json.tool 2>/dev/null || cat /tmp/crs.json + exit 1 + fi + + echo "CRS received" + + echo "Applying cluster registration secret..." + cat /tmp/crs.json | python3 -c 'import sys, json; print(json.load(sys.stdin)["crs"])' | base64 -d | oc apply -f - + + if [ $? -eq 0 ]; then + echo "Cluster registration secret applied successfully" + + if oc get SecuredCluster stackrox-secured-cluster-services &> /dev/null; then + oc label SecuredCluster stackrox-secured-cluster-services cluster-init-job-status=created --overwrite + echo "SecuredCluster labeled for reconciliation" + fi + else + echo "Failed to apply cluster registration secret" + exit 1 + fi + + echo "ACS cluster registration secret configuration complete" + name: create-cluster-registration-secret + dnsPolicy: ClusterFirst + restartPolicy: Never + serviceAccount: {{ .Values.integration.keycloak.serviceAccountName }} + serviceAccountName: {{ .Values.integration.keycloak.serviceAccountName }} + terminationGracePeriodSeconds: 30 +{{- end }} diff --git a/charts/acs-central/values.yaml b/charts/acs-central/values.yaml index a609df8b..4d7e4787 100644 --- a/charts/acs-central/values.yaml +++ b/charts/acs-central/values.yaml @@ -1,7 +1,7 @@ # Default values for ACS Central Services # This chart deploys the Central and Scanner components # -# Cluster name for init bundle +# Cluster name for CRS (Cluster Registration Secret) registration clusterName: "" # Will be set from global.localClusterName by pattern framework global: diff --git a/charts/acs-secured-cluster/values.yaml b/charts/acs-secured-cluster/values.yaml index c53a32bd..cc54222e 100644 --- a/charts/acs-secured-cluster/values.yaml +++ b/charts/acs-secured-cluster/values.yaml @@ -9,11 +9,12 @@ clusterName: "" # Will be overridden by values-hub.yaml # Central endpoint configuration centralEndpoint: "" # e.g., central-stackrox.apps.cluster.example.com:443 -# Init bundle secret (from Vault) +# Cluster registration is handled by the CRS job in acs-central chart. +# The CRS (Cluster Registration Secret) replaces the deprecated init-bundle approach. +# Legacy init-bundle values kept for reference: initBundle: useExternalSecret: true secretName: collector-tls - # If not using external secret, provide bundle data data: "" # Sensor configuration diff --git a/values-hub.yaml b/values-hub.yaml index 6ddd6c07..5a9d832d 100644 --- a/values-hub.yaml +++ b/values-hub.yaml @@ -595,7 +595,7 @@ clusterGroup: value: "ztvp" - name: integration.keycloak.clientId value: "acs-central" - # Must match acs-secured-cluster clusterName (init bundle API name) + # Must match acs-secured-cluster clusterName (CRS registration name) - name: clusterName value: hub # ACS to scan images stored in Quay (Uncomment to enable) From 8b529e290a1f4ed99ef2d3ad4587147184dd1846 Mon Sep 17 00:00:00 2001 From: Przemyslaw Roguski Date: Fri, 19 Jun 2026 19:25:47 +0200 Subject: [PATCH 7/8] fix: handle existing init-bundle/CRS name collision during migration The CRS API rejects names that collide with existing init-bundles or CRS entries. Add logic to revoke legacy init-bundles and stale CRS entries with the same cluster name before generating a new CRS. Uses printf-based Python file writing to avoid YAML block scalar indentation issues with Helm template rendering. Tested on live cluster: CRS generation, init-bundle revocation, sensor registration via CRS init container all verified working. Co-authored-by: Cursor --- .../create-cluster-registration-secret.yaml | 43 +++++++++++++++++++ 1 file changed, 43 insertions(+) diff --git a/charts/acs-central/templates/jobs/create-cluster-registration-secret.yaml b/charts/acs-central/templates/jobs/create-cluster-registration-secret.yaml index 375cfeab..5ff1aa2a 100644 --- a/charts/acs-central/templates/jobs/create-cluster-registration-secret.yaml +++ b/charts/acs-central/templates/jobs/create-cluster-registration-secret.yaml @@ -74,6 +74,49 @@ spec: CLUSTER_NAME="{{ .Values.clusterName | default .Values.global.localClusterName }}" echo "Generating cluster registration secret for: $CLUSTER_NAME" + # Helper script to find an entry by name in a JSON list response + printf '%s\n' \ + 'import json, sys' \ + 'data = json.load(open(sys.argv[1]))' \ + 'for item in data.get("items", []):' \ + ' if item.get("name") == sys.argv[2]:' \ + ' print(item["id"])' \ + ' break' \ + > /tmp/find_entry.py + + # Revoke any existing init-bundle with the same name (migration from init-bundle to CRS). + # The CRS API rejects names that collide with existing init-bundles or CRS entries. + curl -k -s -u "admin:$PASSWORD" \ + https://central/v1/cluster-init/init-bundles > /tmp/bundles_list.json + + EXISTING_BUNDLE_ID=$(python3 /tmp/find_entry.py /tmp/bundles_list.json "$CLUSTER_NAME" 2>/dev/null) + + if [ -n "$EXISTING_BUNDLE_ID" ]; then + echo "Revoking legacy init-bundle '$CLUSTER_NAME' (ID: $EXISTING_BUNDLE_ID)..." + curl -k -s -X PATCH -u "admin:$PASSWORD" \ + -H "Content-Type: application/json" \ + --data "{\"ids\":[\"$EXISTING_BUNDLE_ID\"],\"confirmImpactedClustersIds\":[]}" \ + https://central/v1/cluster-init/init-bundles/revoke > /tmp/revoke_result.json + echo "Legacy init-bundle revoked" + sleep 2 + fi + + # Revoke any existing CRS with the same name (handles re-run after partial failure) + curl -k -s -u "admin:$PASSWORD" \ + https://central/v1/cluster-init/crs > /tmp/crs_list.json + + EXISTING_CRS_ID=$(python3 /tmp/find_entry.py /tmp/crs_list.json "$CLUSTER_NAME" 2>/dev/null) + + if [ -n "$EXISTING_CRS_ID" ]; then + echo "Revoking existing CRS '$CLUSTER_NAME' (ID: $EXISTING_CRS_ID)..." + curl -k -s -X PATCH -u "admin:$PASSWORD" \ + -H "Content-Type: application/json" \ + --data "{\"ids\":[\"$EXISTING_CRS_ID\"],\"confirmImpactedClustersIds\":[]}" \ + https://central/v1/cluster-init/crs/revoke > /tmp/crs_revoke_result.json + echo "Existing CRS revoked" + sleep 2 + fi + curl -k -o /tmp/crs.json -X POST \ -u "admin:$PASSWORD" \ -H "Content-Type: application/json" \ From 762b39b46cf40f64b8f478be2d38e7d941fc4e6f Mon Sep 17 00:00:00 2001 From: Przemyslaw Roguski Date: Mon, 22 Jun 2026 09:25:04 +0200 Subject: [PATCH 8/8] chore: bump ACS chart versions and appVersion to 4.11 - acs-central: 1.0.0 -> 1.1.0 (CRS replaces deprecated init-bundle) - acs-secured-cluster: 1.0.0 -> 1.0.1 (comment updates only) - appVersion: 4.9 -> 4.11 (reflects current ACS stable stream) Co-authored-by: Cursor --- charts/acs-central/Chart.yaml | 4 ++-- charts/acs-secured-cluster/Chart.yaml | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/charts/acs-central/Chart.yaml b/charts/acs-central/Chart.yaml index de4093f5..7e550c62 100644 --- a/charts/acs-central/Chart.yaml +++ b/charts/acs-central/Chart.yaml @@ -2,8 +2,8 @@ apiVersion: v2 name: acs-central description: Red Hat Advanced Cluster Security Central Services type: application -version: 1.0.0 -appVersion: "4.9" +version: 1.1.0 +appVersion: "4.11" keywords: - security - compliance diff --git a/charts/acs-secured-cluster/Chart.yaml b/charts/acs-secured-cluster/Chart.yaml index 0874c87a..d7585836 100644 --- a/charts/acs-secured-cluster/Chart.yaml +++ b/charts/acs-secured-cluster/Chart.yaml @@ -2,8 +2,8 @@ apiVersion: v2 name: acs-secured-cluster description: Red Hat Advanced Cluster Security Secured Cluster Services type: application -version: 1.0.0 -appVersion: "4.9" +version: 1.0.1 +appVersion: "4.11" keywords: - security - runtime-protection