docs(monitoring): add OIDC authentication guide for Grafana#597
docs(monitoring): add OIDC authentication guide for Grafana#597IvanHunters wants to merge 5 commits into
Conversation
✅ Deploy Preview for cozystack ready!
To edit notification comments on pull requests, go to your Netlify project configuration. |
|
Note Reviews pausedIt looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the Use the following commands to manage reviews:
Use the checkboxes below for quick actions:
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (1)
🚧 Files skipped from review as they are similar to previous changes (1)
📝 WalkthroughWalkthroughThis PR adds a documentation page covering OIDC authentication for Cozystack Monitoring Grafana instances, including authentication modes, audience isolation, role and group handling, reconciliation, break-glass access, and operational constraints. ChangesOIDC Authentication Documentation
Estimated code review effort: 1 (Trivial) | ~5 minutes Suggested reviewers: 🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Code Review
This pull request introduces documentation for configuring OIDC authentication for Grafana in Cozystack, detailing the available modes (System and CustomConfig), role assignment, and prerequisites. The review feedback suggests minor formatting improvements, such as replacing Unicode ellipsis characters with standard ASCII characters. Additionally, it corrects a technical inaccuracy by clarifying that Grafana does not support Kubernetes-style CEL claimValidationRules and that any such validation must be handled via JMESPath in role_attribute_path.
Important
The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.
| role_attribute_path: "contains(groups[*], 'grafana-admins') && 'Admin' || 'Viewer'" | ||
| ``` | ||
|
|
||
| …or via a pre-existing Secret in the tenant namespace holding a ready-made `[auth.generic_oauth]` ini fragment in the `auth.ini` key: |
There was a problem hiding this comment.
Using standard ASCII characters (like starting the sentence directly with Or) is preferred over the Unicode ellipsis character … to ensure consistent rendering and avoid copy-paste issues.
| …or via a pre-existing Secret in the tenant namespace holding a ready-made `[auth.generic_oauth]` ini fragment in the `auth.ini` key: | |
| Or via a pre-existing Secret in the tenant namespace holding a ready-made `[auth.generic_oauth]` ini fragment in the `auth.ini` key: |
| username: alice@acme.example | ||
| email: alice@acme.example | ||
| emailVerified: true | ||
| password: "…" |
|
|
||
| ## Prerequisites and gotchas | ||
|
|
||
| - **`emailVerified: true` on Keycloak users.** Phase 1 does not add a `claimValidationRules` entry — so `email_verified` is not chart-enforced. Set `emailVerified: true` on the `KeycloakRealmUser` (or complete the email-verify flow through the Keycloak UI) so the identity holding a given email is guaranteed authentic. The `cozy` realm's default `duplicateEmails: false` prevents a second account from claiming an already-registered address. CEL `claimValidationRules` to make this a hard gate is a follow-up hardening path. |
There was a problem hiding this comment.
The mention of CEL claimValidationRules seems to be a copy-paste leftover from the Kubernetes OIDC guide. Grafana's generic OAuth does not support Kubernetes-style CEL (Common Expression Language) or claimValidationRules. Instead, claim validation or enforcement of email_verified in Grafana is typically achieved using JMESPath expressions within role_attribute_path (potentially combined with role_attribute_strict: true). Let's update this to refer to JMESPath-based enforcement instead of CEL claimValidationRules.
| - **`emailVerified: true` on Keycloak users.** Phase 1 does not add a `claimValidationRules` entry — so `email_verified` is not chart-enforced. Set `emailVerified: true` on the `KeycloakRealmUser` (or complete the email-verify flow through the Keycloak UI) so the identity holding a given email is guaranteed authentic. The `cozy` realm's default `duplicateEmails: false` prevents a second account from claiming an already-registered address. CEL `claimValidationRules` to make this a hard gate is a follow-up hardening path. | |
| - **`emailVerified: true` on Keycloak users.** The default configuration does not enforce `email_verified` in the role mapping. Set `emailVerified: true` on the `KeycloakRealmUser` (or complete the email-verify flow through the Keycloak UI) so the identity holding a given email is guaranteed authentic. The `cozy` realm's default `duplicateEmails: false` prevents a second account from claiming an already-registered address. Enforcing this in Grafana's `role_attribute_path` via JMESPath is a follow-up hardening path. |
| - **Per-tenant Keycloak realms.** Managed multi-tenant identity is a separate proposal, evaluated against Keycloak Organizations. Track it in the [community proposal](https://github.com/cozystack/community/pull/24). | ||
| - **Federating an external IdP into the platform `cozy` realm.** BYO-for-Cozystack-itself is a distinct problem — this feature is BYO-for-a-managed-service. | ||
| - **Full-logout through Keycloak's end-session endpoint.** Native `auth.generic_oauth` covers the OAuth part; `backend-logout-url` wiring is a follow-up. | ||
| - **CEL `claimValidationRules` for `email_verified`.** Explicit-gate hardening; not required for Phase 1 given the layered guarantees above. |
There was a problem hiding this comment.
Since Grafana does not support CEL or claimValidationRules, we should clarify that this is a limitation of Grafana's OAuth capabilities rather than an out-of-scope feature that could be added later using CEL.
| - **CEL `claimValidationRules` for `email_verified`.** Explicit-gate hardening; not required for Phase 1 given the layered guarantees above. | |
| - **CEL `claimValidationRules` for `email_verified`.** Grafana does not support Kubernetes-style CEL validation rules; any such validation must be done via JMESPath in `role_attribute_path`. |
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@content/en/docs/next/operations/services/monitoring/oidc-authentication.md`:
- Line 53: Update both fenced configuration blocks in the OIDC authentication
documentation to include an appropriate language identifier, such as ini, on
each opening fence while leaving their configuration content unchanged.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: 45d325e9-d03d-4247-9dbd-612f36f426c1
📒 Files selected for processing (1)
content/en/docs/next/operations/services/monitoring/oidc-authentication.md
Aleksei Sviridkin (lexfrei)
left a comment
There was a problem hiding this comment.
NOT LGTM — the page is well-structured and the tone matches the tenant Kubernetes OIDC guide, but three of its claims contradict the code PR it ships alongside (cozystack/cozystack#3176). I checked each against that PR's templates, values and unit tests rather than against its prose.
Business context: a user-facing guide for the new spec.oidc selector on the Monitoring CR, landing in docs/next/ ahead of v1.6.
Blockers
B1: spec.oidc.grafanaAdmin does not exist, and the behaviour it describes is explicitly out of scope
Line 70: "Server-level GrafanaAdmin promotion is opt-in via spec.oidc.grafanaAdmin: true — the chart then sets allow_assign_grafana_admin: true … The platform bundle flips this on for the platform Grafana release."
The oidc struct in packages/extra/monitoring/values.yaml (as added by #3176) has exactly three fields: mode, customConfig, users. There is no grafanaAdmin. The string grafanaAdmin does not appear anywhere in #3176's diff. That PR's own operator guide states the opposite outright: "Server-level GrafanaAdmin promotion (allow_assign_grafana_admin) is out of scope for Phase 1. All Grafana instances — platform and tenant — cap at org-level Admin." And packages/system/monitoring/tests/oidc_test.yaml pins the absence with notExists: spec.config["auth.generic_oauth"].allow_assign_grafana_admin.
An operator following this paragraph would add a field the CR schema rejects. Remove the paragraph, and instead list server-level GrafanaAdmin under "What's out of scope".
B2: the example clientId is wrong
Line 47: "clientId set to <namespace>-<release> (for the CR above: tenant-acme-monitoring-system)."
The rule is right, the worked example is not. monitoring.oidc.clientId is printf "%s-%s" .Release.Namespace .Release.Name, and the Monitoring ApplicationDefinition sets release.prefix: "", so the CR shown just above (name: monitoring, namespace: tenant-acme) produces the release monitoring and therefore the clientId tenant-acme-monitoring. monitoring-system is the platform release name — the one living in cozy-monitoring, which the page itself names correctly two paragraphs earlier. As written, a reader hunting for their KeycloakClient looks for an object that does not exist.
B3: the "mode toggle prunes accounts" gotcha inverts the code
Line 150: "Flipping spec.oidc.mode from System to None prunes every user the users-Job provisioned … Flipping back re-provisions them empty."
oidc-users-job.yaml renders only when spec.oidc.users is non-empty, and its header comment says this is deliberate, naming mode=None as one of three empty-users states it guards: "a prune pass with an empty desired list would silently delete every non-admin Main-Org member on every helm upgrade … Removing the last users: entry therefore no longer prunes it."
So flipping System → None runs no Job and prunes nothing; the local Grafana accounts survive. The page tells operators to treat the toggle "like an admin-Secret rotation" — the opposite of the actual, deliberately-safe behaviour. Worth also re-checking the neighbouring claim under "Assigning roles" that removing a user from the list "deletes the account": the Job's documented action is pruning non-admin Main-Org members, which is an org-membership change, not necessarily account deletion.
Non-blocking follow-ups
- Two operator guides for one feature. #3176 also ships
docs/oidc-grafana.md(410 lines) inside the monorepo, covering the same ground. Two authoritative guides will drift — this PR is already the proof, since all three blockers above are places where this page and that one disagree. Worth deciding which is canonical and having the other link to it. - Docs are ahead of the code. #3176 is still open and unmerged, so nothing yet pins these claims. Sequencing the guide before the code it documents is precisely how the three errors above got in. Re-verify every claim against the merged code once #3176 lands.
- The branch is
BEHINDits base; rebase before merge.
|
|
||
| `skip_org_role_sync=true` keeps a login from overwriting the users-Job's role assignments; `oauth_allow_insecure_email_lookup=true` lets Grafana attach the OIDC identity to the pre-provisioned local account by email; `allow_sign_up=false` is the isolation lever — without it, Grafana's default `allow_sign_up=true` combined with `skip_org_role_sync=true` would mint a Viewer account for every `cozy`-realm identity that hits the login flow. | ||
|
|
||
| Server-level `GrafanaAdmin` promotion is opt-in via `spec.oidc.grafanaAdmin: true` — the chart then sets `allow_assign_grafana_admin: true` on the rendered `[auth.generic_oauth]` block. The platform bundle flips this on for the platform Grafana release (`monitoring-system` in `cozy-monitoring`); tenant Grafana releases inherit the chart default `false` and stay at org-level Admin. |
There was a problem hiding this comment.
spec.oidc.grafanaAdmin does not exist in the code PR this page ships alongside. The oidc struct in packages/extra/monitoring/values.yaml has exactly three fields — mode, customConfig, users — and the string grafanaAdmin appears nowhere in cozystack/cozystack#3176.
That PR states the opposite explicitly in its own guide: "Server-level GrafanaAdmin promotion (allow_assign_grafana_admin) is out of scope for Phase 1. All Grafana instances — platform and tenant — cap at org-level Admin." Its unit suite pins the absence: notExists: spec.config["auth.generic_oauth"].allow_assign_grafana_admin.
An operator following this paragraph would set a field the CR schema rejects. Please drop it and move server-level GrafanaAdmin into the "What's out of scope" section instead.
|
|
||
| Cozystack provisions: | ||
|
|
||
| - A per-instance **`KeycloakClient`** in the `cozy` realm with `clientId` set to `<namespace>-<release>` (for the CR above: `tenant-acme-monitoring-system`). Confidential (`clientAuthenticatorType: client-secret`), `secret` sourced from a chart-owned Kubernetes Secret. `redirectUris` locked to `https://grafana.<host>/login/generic_oauth`. |
There was a problem hiding this comment.
The rule (<namespace>-<release>) is right but the worked example is not. monitoring.oidc.clientId is printf "%s-%s" .Release.Namespace .Release.Name, and the Monitoring ApplicationDefinition sets release.prefix: "" — so the CR shown directly above (name: monitoring in tenant-acme) yields the release monitoring and the clientId tenant-acme-monitoring.
monitoring-system is the platform release name, in cozy-monitoring — which this page names correctly two paragraphs earlier. As written, a reader goes looking for a KeycloakClient that does not exist.
There was a problem hiding this comment.
Retracting this — you were right and I was wrong. extra/monitoring creates the inner HelmRelease as {{ .Release.Name }}-system (templates/helmrelease.yaml), and that inner release is the one carrying the OIDC templates, so .Release.Name there is monitoring-system and the clientId for the CR above really is tenant-acme-monitoring-system. Your current text is correct as it stands.
What misled me is _helpers.tpl's own header comment, which still claims tenant-foo/monitoring for the per-tenant case, plus the release.name: monitoring fixture in tests/oidc_test.yaml — a release name production never produces. Both are chart-side and worth fixing upstream; this page is currently more accurate than they are.
| - **`groups_attribute_path` is not optional on Grafana v11.x+.** The chart wires it automatically for `System` mode; in `CustomConfig` inline the operator must add it explicitly (`groups_attribute_path: groups`) if their IdP emits a top-level `groups` array. Otherwise `allowed_groups` becomes a silent no-op and every login fails. | ||
| - **BYO issuer with a self-signed CA.** In `CustomConfig` mode the `secretRef` path is the way to ship a CA bundle alongside the `[auth.generic_oauth]` block — you package `auth.ini` and any `ca-cert` files into the Secret and mount both under `/etc/grafana/oidc`. | ||
| - **`admin_user` stays a break-glass path.** Even under `mode: System` the login form and the `grafana-admin-password` Secret remain wired. Locking the form off is a follow-up hardening. | ||
| - **Mode toggle drops the users-Job's local accounts.** Flipping `spec.oidc.mode` from `System` to `None` prunes every user the users-Job provisioned (the accounts are ephemeral shadows of `spec.oidc.users[]`). Any dashboards those users starred / owned move to their original creators; the local passwords / API keys go away. Flipping back re-provisions them empty. Treat mode toggle like an admin-Secret rotation. |
There was a problem hiding this comment.
This inverts the code. oidc-users-job.yaml renders only when spec.oidc.users is non-empty, and its header comment says so deliberately, naming mode=None as one of three empty-users states it guards against: "a prune pass with an empty desired list would silently delete every non-admin Main-Org member on every helm upgrade … Removing the last users: entry therefore no longer prunes it."
So System → None runs no Job and prunes nothing — the local Grafana accounts survive. Telling operators to treat the toggle "like an admin-Secret rotation" is the opposite of the actual, deliberately-safe behaviour.
While you are here, please re-check the related claim under "Assigning roles" that removing a user from the list "deletes the account" — the Job's documented action is pruning non-admin Main-Org members, which is an org-membership change, not necessarily account deletion.
|
Aleksei Sviridkin (@lexfrei) thanks for the careful cross-check against #3176. I re-verified all three against the now-merged code. B1 and B3 were correct — both fixed in the latest commit. On B2 I have to push back: the worked example The key detail: the OIDC objects are rendered by the inner HelmRelease, not the outer
kind: HelmRelease
metadata:
name: {{ .Release.Name }}-systemThe The merged e2e test pins exactly this — for a CR named CR_NAME="monitoring"
INNER_REL="${CR_NAME}-system"
CID="tenant-test-${INNER_REL}" # tenant-test-monitoring-system
...
[ "${client_id}" = "${CID}" ]( So for the CR in the guide ( B1, B3 and the org-membership-vs-account-deletion wording are all fixed in the same commit. On the non-blocking follow-ups: canonical-doc cross-linking and the rebase are noted. |
Guide covers spec.oidc modes (None/System/CustomConfig) on the Monitoring CR, how System mode provisions a per-instance Keycloak client + audience scope + three role-mapping groups in the cozy realm, and how a user logs in through the Grafana browser flow. Placed under docs/next/operations/services/monitoring/ (weight 5, between setup and dashboards); ships alongside the code PR that adds the spec.oidc selector to the Monitoring chart. Signed-off-by: IvanHunters <xorokhotnikov@gmail.com>
… loss note
Two doc updates paired with the cozystack chart fix:
- Server-level GrafanaAdmin promotion is now driven by an explicit
spec.oidc.grafanaAdmin: true field (was: chart-side .Release.Name
detection). Document the new field and where the platform bundle
flips it on.
- Mode toggle (System → CustomConfig/None) deletes the three
chart-owned <clientId>-{admin,editor,viewer} KeycloakRealmGroups
and every user's membership goes with them. Not a bug, but a one-
way UX cost on the toggle — document it under Prerequisites and
gotchas so operators know before flipping.
Signed-off-by: IvanHunters <xorokhotnikov@gmail.com>
…ob architecture
The website doc was written against the initial PR#3176 shape
(three chart-owned KeycloakRealmGroups + role_attribute_path mapping
groups to Admin/Editor/Viewer). Later commits on the code PR dropped
that architecture entirely — chart-owned groups gone (commit
3b36966e2), role_attribute_path replaced with skip_org_role_sync +
app-side users-map reconciled by a Job (commit sequence 2b3751edc /
7e2ac3cb8 / 4e8c5f4ed / e47308961 / b370998fc / efc27b33c). The
website guide had drifted six revisions behind the code.
Rewrite covers the actual current surface:
- spec.oidc.users[] with Grafana org-role (Admin/Editor/Viewer) is
the assignment mechanism; users-Job reconciles it on every helm
action.
- Login authorization is separate: allowed_groups gate on the
tenant-scoped <ns>-{view,use,admin,super-admin} groups from the
platform-managed cozy realm.
- groups_attribute_path=groups is REQUIRED on Grafana v11.x+
alongside allowed_groups (matches the code fix committed on the
cozystack side).
- CustomConfig inline vs secretRef with the users[] compatibility
contract (secretRef requires users: []).
- Mode-toggle gotcha now describes the users-Job's ephemeral-account
cleanup, not the deleted KeycloakRealmGroups.
Also drops the stale role_attribute_path example that used a
grafana-admins group name that this chart never provisioned.
Signed-off-by: IvanHunters <xorokhotnikov@gmail.com>
Reconcile the guide with cozystack/cozystack#3176 as merged: - Drop the spec.oidc.grafanaAdmin paragraph: the field does not exist (oidc struct is mode/customConfig/users only) and server-level GrafanaAdmin promotion is out of scope for Phase 1; list it under What's out of scope instead. - Fix the mode-toggle note: the users-Job renders only when mode is not None and users[] is non-empty, so flipping to None prunes nothing and the provisioned accounts survive. - Clarify that removing a user is an org-membership removal, not deletion of the Grafana account. - Clarify that OIDC identifiers derive from the inner <CR-name>-system HelmRelease, so the example clientId tenant-acme-monitoring-system is correct. - Add ini language to the two config fenced blocks and replace Unicode ellipsis with ASCII. Signed-off-by: IvanHunters <xorokhotnikov@gmail.com>
9b34d67 to
23e94ad
Compare
Aleksei Sviridkin (lexfrei)
left a comment
There was a problem hiding this comment.
NOT LGTM — all three blockers from the previous round are fixed and the rebase is done. Re-verifying the page against the now-merged chart (cozystack/cozystack#3176 landed 2026-07-15) surfaces five remaining factual errors; two of them make copy-pasted examples fail outright.
A correction I owe you first: my earlier B2 was wrong and your current text is right. I claimed the example CR yields clientId tenant-acme-monitoring. It does not. packages/extra/monitoring/templates/helmrelease.yaml creates the inner HelmRelease as {{ .Release.Name }}-system, and that inner release is the one carrying the OIDC templates — so <release> is monitoring-system and the clientId is tenant-acme-monitoring-system. Lines 45-48 stand as written. I was misled by _helpers.tpl's own header comment, which still claims tenant-foo/monitoring for the per-tenant case; that comment is wrong, not your page.
Blockers
B1: the KeycloakRealmUser example is not a valid CR
Line 126: realm: cozy.
Evidence: packages/system/keycloak-operator/charts/keycloak-operator/crds/v1.edp.epam.com_keycloakrealmusers.yaml declares required: [realmRef, username], and the full spec property set is attributes, clientRoles, email, emailVerified, enabled, firstName, groups, identityProviders, keepResource, lastName, password, passwordSecret, realmRef, reconciliationStrategy, requiredUserActions, roles, username. There is no realm. The schema is structural, so realm: cozy is pruned and the apply is rejected with spec.realmRef: Required value.
Impact: the page's only user-provisioning example fails every time. It is also the only KeycloakRealmUser example on the entire site, so a reader has nothing to cross-check it against.
Fix: use realmRef, with kind spelled out — the CRD defaults realmRef.kind to KeycloakRealm, which would resolve to a nonexistent object, while the cozy realm is a ClusterKeycloakRealm named keycloakrealm-cozy (the shape every in-repo consumer uses, including oidc-keycloak.yaml in this same chart).
B2: the CustomConfig inline example never enables OIDC
Lines 82-89: the example map omits enabled.
Evidence: in System mode grafana.yaml builds $systemOidc with "enabled" "true". The CustomConfig inline branch renders merge $chartForcedOidc $customInline, and $chartForcedOidc holds only skip_org_role_sync / oauth_allow_insecure_email_lookup / allow_sign_up — nothing injects enabled on that path. Grafana's [auth.generic_oauth] defaults enabled = false. The chart's own test mode=CustomConfig inline preserves the operator's map + chart-forced settings uses a config map without enabled and never asserts it.
Impact: an operator copies the block, gets a rendered auth.generic_oauth section, and no sign-in button ever appears — with no error surfaced anywhere.
Fix: add enabled: "true" to the example's customConfig.config, and state in prose that CustomConfig requires it while System wires it automatically.
B3: line 110 inverts the inline-config merge direction
Line 110: "The inline config path merges the operator's map on top of the chart-forced users-map contract".
Evidence: grafana.yaml renders merge $chartForcedOidc $customInline. Helm's merge dst src keeps dst and only fills missing keys from src, so the chart-forced trio wins and the operator cannot override it. The chart states this inline ("So the operator can NOT override skip_org_role_sync=true or oauth_allow_insecure_email_lookup=true — those are contract"), and tests/oidc_test.yaml pins it with mode=CustomConfig inline — operator cannot override chart-forced OIDC settings, which sets allow_sign_up: "true" and asserts the render is "false".
Impact: exactly backwards. A BYO-IdP operator reads this, sets allow_sign_up: "true" in their inline map, watches it silently render as false, and has nothing to debug against.
Fix: reverse the sentence — the chart-forced settings win over the operator's map whenever users[] is non-empty.
B4: line 10 contradicts line 45 on release naming
Line 10: "each Monitoring release (per-tenant monitoring, plus the platform's monitoring-system)".
Evidence: packages/apps/tenant/templates/monitoring.yaml creates HR monitoring in the tenant namespace; extra/monitoring's check-release-name.yaml hard-fails unless that release is named monitoring; its helmrelease.yaml then creates the inner HR {{ .Release.Name }}-system. The OIDC-carrying release is therefore monitoring-system in every namespace, tenant and platform alike, and instances are distinguished by the namespace prefix rather than the release name — there is no release named monitoring that renders OIDC at all. The platform Grafana is the root tenant's, in tenant-root; cozy-monitoring holds only the ExternalName redirects (packages/system/cozystack-basics/templates/monitoring-external-services.yaml).
Impact: the opening paragraph teaches a naming model that line 45 then explicitly refutes ("resolves to monitoring-system, not monitoring").
Fix: drop the parenthetical, or reword it so the namespace is what distinguishes instances.
B5: CEL claimValidationRules cannot exist on Grafana
Lines 144 and 155: both present CEL claimValidationRules as the follow-up hardening path for email_verified.
Evidence: claimValidationRules with CEL expressions is a kube-apiserver AuthenticationConfiguration feature, which is why it is a genuine follow-up on the tenant Kubernetes side (cozystack/cozystack#3044). Grafana's auth.generic_oauth has no such option and no CEL anywhere — its claim handling is JMESPath (role_attribute_path, groups_attribute_path, email_attribute_path).
Impact: promises a Phase-2 path that cannot be built on this component and sends the next contributor down a dead end. This is also the one bot finding still open on the PR.
Fix: drop both mentions, or restate the hardening path in terms Grafana or Keycloak can actually deliver.
Non-blocking follow-ups
- Line 132: the CRD marks
passworddeprecated ("Deperecated: use PasswordSecret instead" — upstream's typo), and both upstream CRD examples usepasswordSecret: {name, key}. A first-contact page should model the non-deprecated form. - Line 146: mounting a CA bundle under
/etc/grafana/oidcdoes not by itself make Grafana trust it — the operator still has to pointtls_client_caat the file from inside theirauth.ini. As written it reads as though placing the file is sufficient. - Line 45:
<CR-name>-systemimplies the CR could be named something else.check-release-name.yamlrejects any name butmonitoring, so<release>is always literallymonitoring-system. - Line 51:
spec.config.auth.generic_oauthis a literalauth.generic_oauthkey underspec.config(an ini section name), not nestedauth→generic_oauth. The chart's tests address it asspec.config["auth.generic_oauth"]; worth spelling out for anyone writing a jsonpath. - Still open from last round: #3176 also ships
docs/oidc-grafana.mdcovering the same ground, and this round is more evidence that two authoritative guides drift — that file's line 4 says the platform release lives incozy-monitoring, which is where the redirects live, not the stack. Worth deciding which one is canonical. - Not yours to fix, but worth an upstream issue:
_helpers.tpl's header comment,docs/oidc-grafana.md:4, and therelease.name: monitoringfixture intests/oidc_test.yamlall encode a release name production never produces. Line 45 of this page is currently more accurate than the chart's own comments — that asymmetry is what generated my incorrect B2 last round, and it will mislead the next reader too.
| metadata: | ||
| name: alice-acme | ||
| namespace: cozy-keycloak | ||
| spec: |
There was a problem hiding this comment.
B1 — this CR does not apply. spec.realm is not a field on KeycloakRealmUser; the CRD requires realmRef (required: [realmRef, username] in v1.edp.epam.com_keycloakrealmusers.yaml), so this is rejected with spec.realmRef: Required value while realm: cozy is pruned as unknown.
kind has to be explicit — it defaults to KeycloakRealm, but the cozy realm is a ClusterKeycloakRealm named keycloakrealm-cozy, which is the shape oidc-keycloak.yaml in this same chart uses.
| spec: | |
| realmRef: | |
| name: keycloakrealm-cozy | |
| kind: ClusterKeycloakRealm |
| mode: CustomConfig | ||
| customConfig: | ||
| config: | ||
| client_id: my-grafana |
There was a problem hiding this comment.
B2 — this example leaves OIDC switched off. enabled: "true" is only injected on the System path ($systemOidc in grafana.yaml); the CustomConfig inline branch renders merge $chartForcedOidc $customInline, and $chartForcedOidc carries only skip_org_role_sync / oauth_allow_insecure_email_lookup / allow_sign_up. Grafana defaults [auth.generic_oauth] enabled = false, so a reader who copies this gets a rendered config block and no sign-in button, with nothing logged.
| client_id: my-grafana | |
| enabled: "true" | |
| client_id: my-grafana |
Reconcile the guide with the merged chart (cozystack/cozystack#3176): - KeycloakRealmUser example: use spec.realmRef {name: keycloakrealm-cozy, kind: ClusterKeycloakRealm} instead of the non-existent spec.realm, and passwordSecret instead of the deprecated inline password. - CustomConfig inline: add enabled: "true" to the example and state that System wires it automatically while CustomConfig does not. - Correct the inline-merge direction: chart-forced settings win over the operator's map (Helm merge keeps the chart's values). - Fix the release-naming model: the OIDC-carrying release is monitoring-system in every namespace; instances differ by namespace, not release name. - Drop CEL claimValidationRules mentions: Grafana generic_oauth has no such option; enforce email_verified on the Keycloak side instead. - Note tls_client_ca is required for a self-signed BYO CA, and that spec.config["auth.generic_oauth"] is a literal ini-section key. Signed-off-by: IvanHunters <xorokhotnikov@gmail.com>
Summary
Adds a new guide under
content/en/docs/next/operations/services/monitoring/: OIDC authentication for Grafana.Documents how to enable per-user OIDC authentication on the Grafana instance shipped by every
Monitoringrelease, using the newspec.oidcselector. Ships alongside cozystack/cozystack#3176, the code PR that adds the selector to the chart. The identity model is per-instance audience isolation on the flatcozyrealm — same shape as the tenant kube-apiserver's Phase 1 (cozystack/cozystack#3044); architectural rationale in cozystack/community#24.What
content/en/docs/next/operations/services/monitoring/oidc-authentication.md(weight: 5, positioned betweensetup.md(2) anddashboards.md(10)).None,System,CustomConfig), the three role-mappingKeycloakRealmGroups the chart owns, how a user is added viaKeycloakRealmUser, browser login flow, and gotchas:emailVerifiedprescriptive requirement, self-signed CA underCustomConfig.secretRef,admin_userbreak-glass posture.claimValidationRules) so readers know where the boundary sits.docs/next/; version-specific pages (v1.5, ...) get the guide when the code PR lands in a release.Why
spec.oidcselector on theMonitoringCR is a new user-facing surface — needs a user-facing guide before v1.6.content/en/docs/next/kubernetes/oidc-authentication.md, docs(kubernetes): add OIDC authentication guide #596), so operators land on a familiar layout./docs/next/operations/services/monitoring/oidc-authentication/.Summary by CodeRabbit
None,System,CustomConfig), required Grafanaauth.generic_oauthsettings, and group-based access control via namespace-scoped authorization withgroups_attribute_path.SystemKeycloak artifact provisioning and automated user/role syncing, plus the forced Grafana behaviors when OIDC users are configured.CustomConfigvalidation/precedence, key gotchas (e.g.,emailVerified), CA/tls guidance, and how switching modes affects user reconciliation.