Skip to content

Commit f43bd53

Browse files
committed
fix(keycloak): complete realm-name coverage and guard non-string names
The enable-update-email hook Job in keycloak-configure still pinned REALM=cozy, so on a custom realmName it waited on a realm the chart never provisions and the cold install timed out — the one seventh consumer left behind, and the only value nobody could set without breaking the install. A bare-integer or YAML-reserved realm name reached the printf %s that builds the derived keycloakInternalUrl as a Go int, rendering a "%!s(...)" format error into the URL; coerce the name to a string once at the source. Document that renaming the realm on a running cluster provisions a fresh empty realm and orphans the accounts in the old one, and note in the OIDC tenant and grafana guides that the realm name is now the configurable default. info/Makefile carried NAME=etcd, so cozyhr drove the wrong release from that package; correct it while touching the file. Assisted-by: LLM Signed-off-by: Alexey Artamonov <[email protected]>
1 parent d289329 commit f43bd53

10 files changed

Lines changed: 112 additions & 6 deletions

File tree

‎docs/oidc-grafana.md‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,7 @@ CR:
7878
fallback, same pattern as `packages/system/dashboard`), preserved
7979
across upgrades.
8080
4. The Grafana CR's `spec.config.auth.generic_oauth` section wired to
81-
the cozy realm issuer + the per-instance audience scope. Two
81+
the platform realm issuer (`cozy` by default, `authentication.oidc.realmName`) + the per-instance audience scope. Two
8282
independent gates ride on top of the raw OAuth wiring:
8383

8484
**Tenant-membership gate — unconditional in System mode.** The chart

‎docs/oidc-tenant.md‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ Three modes:
3434
`<release>-admin-kubeconfig` Secret (the Kamaji-minted `super-admin.svc`
3535
kubeconfig). This is the default; existing clusters render byte-identical
3636
to before.
37-
- **System** — the apiserver trusts the platform `cozy` Keycloak realm via a
37+
- **System** — the apiserver trusts the platform Keycloak realm (`cozy` by default, set via `authentication.oidc.realmName`) via a
3838
per-cluster public client. Authenticates the users already in `cozy` (the
3939
realm cozystack ships with). Zero-config default.
4040
- **CustomConfig** — the apiserver trusts a tenant-supplied OIDC issuer

‎packages/core/platform/templates/apps.yaml‎

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -106,7 +106,10 @@ stringData:
106106
oidc-enabled: {{ .Values.authentication.oidc.enabled | quote }}
107107
oidc-insecure-skip-verify: {{ .Values.authentication.oidc.insecureSkipVerify | quote }}
108108
extra-keycloak-redirect-uri-for-dashboard: {{ index .Values.authentication.oidc.keycloakExtraRedirectUri | quote }}
109-
{{- $realmName := .Values.authentication.oidc.realmName | default "cozy" }}
109+
{{- /* toString so a bare-integer or YAML-reserved realm name does not
110+
become a Go int/bool: quote would still emit it, but the printf %s
111+
in the derived URL below would render a "%!s(...)" format error. */}}
112+
{{- $realmName := .Values.authentication.oidc.realmName | default "cozy" | toString }}
110113
oidc-realm-name: {{ $realmName | quote }}
111114
{{- /* null keycloakInternalUrl derives the in-cluster Service URL for the
112115
configured realm; "" is the external-discovery escape hatch and an

‎packages/core/platform/tests/apps_oidc_realm_name_test.yaml‎

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,3 +36,20 @@ tests:
3636
- matchRegex:
3737
path: stringData["values.yaml"]
3838
pattern: 'keycloak-internal-url:\s*"http://keycloak-http\.cozy-keycloak\.svc:8080/realms/acme"'
39+
40+
# A bare-integer realm name arrives as a Go int; the derived URL builds it with
41+
# printf %s, which renders a "%!s(int=...)" format error unless the value is
42+
# coerced to a string first. Pin both the realm key and the derived URL.
43+
- it: renders a numeric realm name as a string in both the key and the derived URL
44+
set:
45+
authentication.oidc.realmName: 123
46+
asserts:
47+
- matchRegex:
48+
path: stringData["values.yaml"]
49+
pattern: 'oidc-realm-name:\s*"123"'
50+
- matchRegex:
51+
path: stringData["values.yaml"]
52+
pattern: 'keycloak-internal-url:\s*"http://keycloak-http\.cozy-keycloak\.svc:8080/realms/123"'
53+
- notMatchRegex:
54+
path: stringData["values.yaml"]
55+
pattern: '%!s\(int'

‎packages/core/platform/values.yaml‎

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -463,6 +463,12 @@ authentication:
463463
# /realms/<realmName>. The apiserver OIDC issuer is set out-of-band via talm
464464
# oidcIssuerUrl and must carry the same realm, or the tokens minted here are
465465
# rejected. Rename it in one place; keep it in step with talm.
466+
#
467+
# Set this at install time. Changing it on a running cluster does not rename
468+
# the realm: the keycloak-operator provisions a fresh, empty realm under the
469+
# new name and abandons the old one, so every account, session and client
470+
# living in the previous realm is orphaned. Treat a rename on a live cluster
471+
# as a from-scratch re-provision of identities, not an in-place migration.
466472
realmName: "cozy"
467473
# In-cluster URL of the KeyCloak realm, used for backend-to-backend requests. oauth2-proxy runs
468474
# with --skip-oidc-discovery and sends every backend call (token, jwks, userinfo, logout) to this

‎packages/extra/info/Makefile‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
NAME=etcd
1+
NAME=info
22

33
include ../../../hack/package.mk
44

‎packages/system/dashboard/tests/gatekeeper_test.yaml‎

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -228,3 +228,25 @@ tests:
228228
- notContains:
229229
path: spec.template.spec.containers[0].args
230230
content: --oidc-issuer-url=https://keycloak.example.org/realms/cozy
231+
232+
# The external-discovery branch (empty internal URL) builds its own
233+
# backend-logout-url on the browser host, so the realm segment there must
234+
# follow oidc-realm-name too; reverting $realm in that branch alone would
235+
# otherwise survive the suite.
236+
- it: threads a custom realm into the external-discovery logout URL
237+
set:
238+
_cluster:
239+
root-host: example.org
240+
oidc-enabled: "true"
241+
oidc-realm-name: acme
242+
keycloak-internal-url: ""
243+
asserts:
244+
- notContains:
245+
path: spec.template.spec.containers[0].args
246+
content: --skip-oidc-discovery
247+
- contains:
248+
path: spec.template.spec.containers[0].args
249+
content: --backend-logout-url=https://keycloak.example.org/realms/acme/protocol/openid-connect/logout?id_token_hint={id_token}
250+
- notContains:
251+
path: spec.template.spec.containers[0].args
252+
content: --backend-logout-url=https://keycloak.example.org/realms/cozy/protocol/openid-connect/logout?id_token_hint={id_token}

‎packages/system/keycloak-configure/templates/enable-update-email.yaml‎

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
1+
{{- $realmName := index .Values._cluster "oidc-realm-name" | default "cozy" | toString }}
12
{{/*
2-
Enable the UPDATE_EMAIL required action in the `cozy` realm.
3+
Enable the UPDATE_EMAIL required action in the platform realm (cozy by
4+
default, authentication.oidc.realmName).
35

46
The portal user-settings page lets a user change their email via a
57
Keycloak `kc_action=UPDATE_EMAIL` redirect. That flow only works when
@@ -73,7 +75,7 @@ spec:
7375
set -eu
7476
KCADM=/opt/keycloak/bin/kcadm.sh
7577
CFG=/tmp/kcadm.config
76-
REALM=cozy
78+
REALM={{ $realmName }}
7779
7880
# The realm is reconciled asynchronously by the
7981
# keycloak-operator, so wait for it before configuring it.
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
suite: enable-update-email hook Job targets the configured realm
2+
templates:
3+
- templates/enable-update-email.yaml
4+
5+
release:
6+
name: keycloak-configure
7+
namespace: cozy-keycloak
8+
9+
# The enable-update-email hook Job is a consumer of the realm in the same chart
10+
# that creates it: it waits on and configures the realm by name via kcadm. On a
11+
# custom realm it must target the realm keycloak-configure actually provisions,
12+
# not the literal "cozy" — otherwise the Job waits for a realm that never
13+
# appears and the cold install times out, so a non-default realmName is the one
14+
# value nobody could set without breaking the install.
15+
16+
tests:
17+
- it: defaults the hook Job realm to cozy
18+
asserts:
19+
- matchRegex:
20+
path: spec.template.spec.containers[0].command[2]
21+
pattern: 'REALM=cozy(\s|$)'
22+
23+
- it: threads a custom realm name into the hook Job
24+
set:
25+
_cluster:
26+
oidc-realm-name: acme
27+
asserts:
28+
- matchRegex:
29+
path: spec.template.spec.containers[0].command[2]
30+
pattern: 'REALM=acme(\s|$)'
31+
- notMatchRegex:
32+
path: spec.template.spec.containers[0].command[2]
33+
pattern: 'REALM=cozy'

‎packages/system/linstor-gui/tests/ingress_auth_test.yaml‎

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -322,3 +322,26 @@ tests:
322322
- notContains:
323323
path: spec.template.spec.containers[0].args
324324
content: --oidc-issuer-url=https://keycloak.dev10.example.org/realms/cozy
325+
326+
# The external-discovery branch (empty internal URL) builds its own
327+
# backend-logout-url on the browser host; its realm segment must follow
328+
# oidc-realm-name too.
329+
- it: threads a custom realm into the external-discovery logout URL
330+
template: templates/gatekeeper.yaml
331+
set:
332+
_cluster:
333+
root-host: dev10.example.org
334+
oidc-enabled: "true"
335+
oidc-realm-name: acme
336+
keycloak-internal-url: ""
337+
expose-services: "linstor-gui"
338+
asserts:
339+
- notContains:
340+
path: spec.template.spec.containers[0].args
341+
content: --skip-oidc-discovery
342+
- contains:
343+
path: spec.template.spec.containers[0].args
344+
content: --backend-logout-url=https://keycloak.dev10.example.org/realms/acme/protocol/openid-connect/logout?id_token_hint={id_token}
345+
- notContains:
346+
path: spec.template.spec.containers[0].args
347+
content: --backend-logout-url=https://keycloak.dev10.example.org/realms/cozy/protocol/openid-connect/logout?id_token_hint={id_token}

0 commit comments

Comments
 (0)