Summary
AuthbridgeConfigReconciler unconditionally overwrites the KEYCLOAK_REALM key (and other Keycloak keys) of every watched namespace's authbridge-config ConfigMap with the platform-global realm on each reconcile. This silently discards any explicitly-set per-namespace realm, effectively removing the ability to run workloads against a non-default realm. It is a regression introduced by cdbed3b ("feat: operator-driven identity deployment").
Impact
Any namespace that needs a realm other than the platform default can no longer get one: even if an operator/admin/CI sets authbridge-config.KEYCLOAK_REALM=<other>, the reconciler resets it to Platform.KeycloakRealm (default "kagenti") within one reconcile cycle. The client-registration controller then registers the workload client in the wrong realm.
Concretely, this breaks the kagenti repo's token-exchange E2E suite (E2E K8s 1.35.0 (Kind) push workflow), which has been red continuously as a result. The first test TestClientCredentials fails with:
client_credentials failed: {"error":"invalid_client","error_description":"Invalid client or Invalid client credentials"}
assert 401 == 200
Root cause
On main, internal/controller/authbridgeconfig_controller.go:
// Update existing ConfigMap
...
for k, v := range desired { // ~line 114 — overwrites existing keys
cm.Data[k] = v
}
where desired is built from platform config:
realm := r.Platform.KeycloakRealm // ~line 132
if realm == "" {
realm = defaultKeycloakRealm // "kagenti" (line 28)
}
...
"KEYCLOAK_REALM": realm, // ~line 159
The client-registration controller then reads that overwritten value:
KeycloakRealm: cm.Data["KEYCLOAK_REALM"], // clientregistration_controller.go:357
So a workload in namespace tx-e2e (where the harness created realm tx-e2e and set KEYCLOAK_REALM=tx-e2e) gets its client registered in realm kagenti instead. A client_credentials grant against realm tx-e2e then returns invalid_client.
Reproduction
- Build/run the operator from
main.
- Create a Keycloak realm
tx-e2e.
- In a watched namespace,
kubectl apply an authbridge-config ConfigMap with KEYCLOAK_REALM: tx-e2e.
- Observe (within ~one reconcile) that
kubectl get cm authbridge-config -n <ns> -o jsonpath='{.data.KEYCLOAK_REALM}' reverts to kagenti.
- The registered client (
kagenti-keycloak-client-credentials-*, client-id.txt) lands in realm kagenti, not tx-e2e.
Version note
This regression is not present in v0.2.0-rc.6 — authbridgeconfig_controller.go does not exist there, so authbridge-config stays as created (e.g. Helm-managed) and per-namespace realms are honored. Verified on a live cluster running 0.2.0-rc.6: client_credentials for namespace clients returns HTTP 200. The breakage only appears when building the operator from main (which is what kagenti CI does via 20-build-operator.sh, default ref main).
Introduced by: cdbed3b — "feat: operator-driven identity deployment" (2026-06-05).
Proposed fix
Preserve an explicitly-set KEYCLOAK_REALM rather than clobbering it. Options:
- Don't overwrite a key that's already present / non-empty in the existing CM (only fill in missing keys when reconciling an existing
authbridge-config).
- Honor a per-namespace override via a Namespace annotation/label (e.g.
kagenti.io/keycloak-realm) that takes precedence over Platform.KeycloakRealm.
- Only manage CMs the reconciler owns (skip CMs not labeled
app.kubernetes.io/managed-by: kagenti-operator), so externally-provisioned config is left intact.
Option 1 or 2 restores per-namespace realm support without regressing the operator-driven defaulting added in cdbed3b.
Cross-refs
- Interim mitigation in kagenti: the token-exchange E2E step was made non-fatal (kagenti/kagenti PR #1982) so
main regains a green/red signal while this is fixed.
Summary
AuthbridgeConfigReconcilerunconditionally overwrites theKEYCLOAK_REALMkey (and other Keycloak keys) of every watched namespace'sauthbridge-configConfigMap with the platform-global realm on each reconcile. This silently discards any explicitly-set per-namespace realm, effectively removing the ability to run workloads against a non-default realm. It is a regression introduced bycdbed3b("feat: operator-driven identity deployment").Impact
Any namespace that needs a realm other than the platform default can no longer get one: even if an operator/admin/CI sets
authbridge-config.KEYCLOAK_REALM=<other>, the reconciler resets it toPlatform.KeycloakRealm(default"kagenti") within one reconcile cycle. The client-registration controller then registers the workload client in the wrong realm.Concretely, this breaks the kagenti repo's token-exchange E2E suite (
E2E K8s 1.35.0 (Kind)push workflow), which has been red continuously as a result. The first testTestClientCredentialsfails with:Root cause
On
main,internal/controller/authbridgeconfig_controller.go:where
desiredis built from platform config:The client-registration controller then reads that overwritten value:
So a workload in namespace
tx-e2e(where the harness created realmtx-e2eand setKEYCLOAK_REALM=tx-e2e) gets its client registered in realmkagentiinstead. Aclient_credentialsgrant against realmtx-e2ethen returnsinvalid_client.Reproduction
main.tx-e2e.kubectl applyanauthbridge-configConfigMap withKEYCLOAK_REALM: tx-e2e.kubectl get cm authbridge-config -n <ns> -o jsonpath='{.data.KEYCLOAK_REALM}'reverts tokagenti.kagenti-keycloak-client-credentials-*,client-id.txt) lands in realmkagenti, nottx-e2e.Version note
This regression is not present in
v0.2.0-rc.6—authbridgeconfig_controller.godoes not exist there, soauthbridge-configstays as created (e.g. Helm-managed) and per-namespace realms are honored. Verified on a live cluster running0.2.0-rc.6:client_credentialsfor namespace clients returns HTTP 200. The breakage only appears when building the operator frommain(which is what kagenti CI does via20-build-operator.sh, default refmain).Introduced by:
cdbed3b— "feat: operator-driven identity deployment" (2026-06-05).Proposed fix
Preserve an explicitly-set
KEYCLOAK_REALMrather than clobbering it. Options:authbridge-config).kagenti.io/keycloak-realm) that takes precedence overPlatform.KeycloakRealm.app.kubernetes.io/managed-by: kagenti-operator), so externally-provisioned config is left intact.Option 1 or 2 restores per-namespace realm support without regressing the operator-driven defaulting added in
cdbed3b.Cross-refs
mainregains a green/red signal while this is fixed.