fix(linstor): restrict linstor-gui to cozystack-cluster-admin group (#2415)

## What this PR does

Tightens access control on the `linstor-gui` Ingress so it is reachable
only by members of the `cozystack-cluster-admin` Keycloak group (the
same group that grants cluster-admin RBAC on the host cluster).

Before this change, the oauth2-proxy gatekeeper in front of
`linstor-gui` enforced only that the user could authenticate against the
`cozy` realm (`--email-domain=*`, no group restriction). Any realm user
— including tenant-scoped accounts — could reach the UI and the
underlying LINSTOR controller REST API, which the gatekeeper proxies
with a static mTLS client cert and no per-user RBAC.

Changes:

- `packages/system/linstor-gui/templates/gatekeeper.yaml` — add
`--allowed-group=cozystack-cluster-admin` and include `groups` in
`--scope` so the claim is available for validation. Extend the
top-of-file rationale block to document the gate.
- `packages/system/linstor-gui/tests/ingress_auth_test.yaml` — assert
both new args on the rendered Deployment.
- `packages/system/linstor-gui/README.md` — document the group
restriction under "Option 1 — Keycloak-protected Ingress".

No Keycloak-side changes are required: the `cozystack-cluster-admin`
KeycloakRealmGroup and the `groups` client scope are already provisioned
by `keycloak-configure`.

Port-forward access (Option 2) is unchanged; it remains gated by kubectl
RBAC on `cozy-linstor`.

### Release note

```release-note
fix(linstor): restrict `linstor-gui` Ingress access to members of the `cozystack-cluster-admin` Keycloak group.
```
This commit is contained in:
myasnikovdaniil 2026-04-17 12:36:11 +05:00 committed by GitHub
commit 985cb9c889
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 18 additions and 3 deletions

View file

@ -13,8 +13,11 @@ using mTLS with the `linstor-client-tls` secret created by the `linstor` package
The chart ships an `oauth2-proxy` based gatekeeper plus a `KeycloakClient` CRD
so the UI can be published on `linstor-gui.<root-host>` behind the cluster
Keycloak realm. Access is granted to any user who can authenticate against the
`cozy` realm.
Keycloak realm. Access is restricted to members of the
`cozystack-cluster-admin` Keycloak group — the same group that grants
cluster-admin RBAC on the host cluster. Authenticating against the `cozy`
realm alone is not sufficient; users outside that group receive a 403 from
oauth2-proxy before any request reaches the UI or the LINSTOR controller.
To turn it on, add `linstor-gui` to `publishing.exposedServices` in the core
`cozystack` values (same list that controls `dashboard`). OIDC must be

View file

@ -13,6 +13,11 @@ Only rendered when oidc-enabled=true. If the cluster has OIDC disabled we
deliberately do not ship an unauthenticated token-proxy fallback (unlike
dashboard): linstor-gui surfaces raw storage state and should not be
reachable via Ingress without Keycloak login.
Access is further restricted to the cozystack-cluster-admin group
(--allowed-group below). The proxy authenticates with a static mTLS client
cert and LINSTOR itself has no per-user RBAC, so group membership is the
only boundary between a realm user and raw storage state.
*/ -}}
{{- if eq $oidcEnabled "true" }}
apiVersion: apps/v1
@ -84,7 +89,8 @@ spec:
- --cookie-secure=true
- --cookie-secret=$(OAUTH2_PROXY_COOKIE_SECRET)
- --skip-provider-button
- --scope=openid email profile offline_access
- --scope=openid email profile groups offline_access
- --allowed-group=cozystack-cluster-admin
{{- if eq $oidcInsecureSkipVerify "true" }}
- --ssl-insecure-skip-verify=true
{{- end }}

View file

@ -109,6 +109,12 @@ tests:
- contains:
path: spec.template.spec.containers[0].args
content: --oidc-issuer-url=https://keycloak.dev10.example.org/realms/cozy
- contains:
path: spec.template.spec.containers[0].args
content: --scope=openid email profile groups offline_access
- contains:
path: spec.template.spec.containers[0].args
content: --allowed-group=cozystack-cluster-admin
- contains:
path: spec.template.spec.containers[0].env
content: