Sign-in
KubeGlass can sign people in itself, with no proxy in front of it. Two auth modes do this:
oidc: people sign in with your OpenID Connect provider, and KubeGlass impersonates them on every Kubernetes call, so their RBAC applies.token: people paste a Kubernetes bearer token, which KubeGlass sends to the API server as their own credential.
The other modes are impersonation, where a sign-in proxy such as OAuth2 Proxy sends the user in headers, and local, where nobody signs in. See Running for a team.
How OIDC sign-in works
Section titled “How OIDC sign-in works”- Someone opens KubeGlass without a session and lands on the sign-in page, with one button: Sign in with your provider.
- KubeGlass sends the browser to the provider with the authorization code flow and PKCE (S256), a
stateand anonce, and the redirect URI exactly as configured. - The provider sends the browser back to
/auth/callback. KubeGlass checks that the state belongs to this browser, exchanges the code for tokens, and checks the ID token: signature, issuer, audience, expiry and nonce. - KubeGlass creates a session and sets an httpOnly cookie that holds only a random session ID. The browser returns to the page it started on.
The ID token, refresh token and identity stay on the server. When the ID token is about to expire, KubeGlass uses the refresh token to get a new one; if the provider refuses (the person was removed or disabled there), the session ends. Without a refresh token the session ends when the ID token does, and the next page asks the person to sign in again, which takes one click while they are still signed in at the provider.
Sign out in the user menu ends the KubeGlass session. If the provider supports RP-initiated logout (it lists an end_session_endpoint), KubeGlass then posts the sign-out to it, so the person is signed out there too, and the provider sends them back to the sign-in page.
Set it up
Section titled “Set it up”Register KubeGlass at your provider as a web application (the sections below show where), with:
- Redirect URI: KubeGlass’s address followed by
/auth/callback, for examplehttps://kubeglass.example.com/auth/callback, orhttps://tools.example.com/kubeglass/auth/callbackwhen KubeGlass is served under a path. - Post-logout redirect URI: the same address followed by
/sign-in, for providers that support signing out there.
Then install the chart with config.authMode=oidc. Keep the client secret in a Secret:
kubectl -n kubeglass create secret generic kubeglass-oidc --from-literal=client-secret='<client secret>'kubectl -n kubeglass create secret generic kubeglass-session --from-literal=session-key="$(openssl rand -base64 32)"config: authMode: oidcoidc: issuerURL: https://dex.example.com clientID: kubeglass clientSecretRef: name: kubeglass-oidc scopes: "openid profile email groups offline_access" usernameClaim: email groupsClaim: groupssession: keySecretRef: name: kubeglass-sessioningress: enabled: true hosts: - host: kubeglass.example.com paths: - path: / pathType: Prefix tls: - secretName: kubeglass-tls hosts: [kubeglass.example.com]helm upgrade --install kubeglass oci://ghcr.io/kubeglass/charts/kubeglass -n kubeglass -f values-oidc.yamloidc.redirectURL defaults to the first Ingress host with config.basePath and /auth/callback; set it when KubeGlass is reached another way. With neither, browser sign-in is off (see Bearer tokens only). The values and their environment variables:
| Chart value | Variable | Default | What it does |
|---|---|---|---|
oidc.issuerURL |
KUBEGLASS_OIDC_ISSUER_URL |
The provider’s issuer; KubeGlass reads /.well-known/openid-configuration under it |
|
oidc.clientID |
KUBEGLASS_OIDC_CLIENT_ID |
KubeGlass’s client ID | |
oidc.clientSecretRef |
KUBEGLASS_OIDC_CLIENT_SECRET |
none | The client secret. Leave it out for a public client, which PKCE alone protects |
oidc.redirectURL |
KUBEGLASS_OIDC_REDIRECT_URL |
the Ingress host | The registered redirect URI. It must end in /auth/callback, after the base path if there is one |
oidc.scopes |
KUBEGLASS_OIDC_SCOPES |
openid profile email |
Scopes asked for at sign-in. Add the one your provider gives refresh tokens for |
oidc.usernameClaim |
KUBEGLASS_OIDC_USER_CLAIM |
email (binary: sub) |
The claim that names the user |
oidc.usernamePrefix |
KUBEGLASS_OIDC_USERNAME_PREFIX |
none | Put in front of the user name |
oidc.groupsClaim |
KUBEGLASS_OIDC_GROUPS_CLAIM |
groups |
The claim that lists the groups |
oidc.groupsPrefix |
KUBEGLASS_OIDC_GROUPS_PREFIX |
none | Put in front of each group |
oidc.providerName |
KUBEGLASS_OIDC_PROVIDER_NAME |
from the issuer | The name on the sign-in button |
oidc.audience |
KUBEGLASS_OIDC_AUDIENCE |
the client ID | The audience a bearer token in an Authorization header must have |
session.keySecretRef |
KUBEGLASS_SESSION_KEY |
generated | The key that encrypts sessions in the data directory |
session.idleTimeout |
KUBEGLASS_SESSION_MAX_AGE |
4h |
A session nobody used for this long ends. Typing into a shell is use; a log stream or a watch isn’t |
session.lifetime |
KUBEGLASS_SESSION_LIFETIME |
24h |
Every session ends this long after sign-in |
The NetworkPolicy the chart creates lets KubeGlass reach port 443 anywhere. If your provider runs in the cluster on another port, such as Dex on 5556, add an egress rule for it in networkPolicy.additionalEgress.
Behind an Ingress or a load balancer, set config.trustedProxies (KUBEGLASS_TRUSTED_PROXIES) to the ingress controller’s addresses, or the range its pods get them from. KubeGlass then takes each person’s address from X-Forwarded-For for its rate limits, sign-in’s included, and sees from X-Forwarded-Proto that the Ingress ends HTTPS, so it sends HSTS. Without it everyone counts as the ingress controller, in one limit, and KubeGlass logs a warning. In oidc mode it doesn’t make KubeGlass believe identity headers; it signs people in itself.
Who KubeGlass acts as
Section titled “Who KubeGlass acts as”KubeGlass impersonates usernamePrefix plus the usernameClaim value, in the groups from groupsClaim, each with groupsPrefix. Write RoleBindings for those names:
apiVersion: rbac.authorization.k8s.io/v1kind: RoleBindingmetadata: name: platform-edit namespace: shoproleRef: apiGroup: rbac.authorization.k8s.io kind: ClusterRole name: editsubjects: - apiGroup: rbac.authorization.k8s.io kind: User name: jane@example.com - apiGroup: rbac.authorization.k8s.io kind: Group name: platformIf your API server also accepts OIDC tokens from the same provider (for kubectl), use the same claims and prefixes as its --oidc-username-claim, --oidc-username-prefix, --oidc-groups-claim and --oidc-groups-prefix, so one set of bindings serves both. config.adminUsers and config.adminGroups use the same names.
Because KubeGlass impersonates with its own ServiceAccount, it refuses identities the API server reserves: a user name that starts with system:, the name local-user, and, with usernameClaim: email, a token whose email_verified is false. Groups that start with system:, such as system:masters, are dropped. Limit whom KubeGlass may impersonate with rbac.impersonation.resourceNames.
In Dex’s configuration, add KubeGlass as a static client:
staticClients: - id: kubeglass name: KubeGlass secret: <client secret> redirectURIs: - https://kubeglass.example.com/auth/callbackValues: issuerURL is Dex’s issuer, scopes: "openid profile email groups offline_access", usernameClaim: email, groupsClaim: groups. The groups scope makes Dex include the groups from its connector, and offline_access gives a refresh token. Dex has no RP-initiated logout, so Sign out ends the KubeGlass session only.
Keycloak
Section titled “Keycloak”- In your realm, Clients › Create client: type OpenID Connect, client ID
kubeglass. - Turn on Client authentication, and keep Standard flow on.
- Valid redirect URIs:
https://kubeglass.example.com/auth/callback. Valid post logout redirect URIs:https://kubeglass.example.com/sign-in. - Copy the secret from the Credentials tab into the
kubeglass-oidcSecret. - For groups, open the client’s dedicated scope (Client scopes ›
kubeglass-dedicated), add a mapper by configuration of type Group Membership with token claim namegroups, and turn off Full group path so the groups come without a leading slash (platform).
Values: issuerURL: https://keycloak.example.com/realms/<realm>, scopes: "openid profile email", usernameClaim: email (or preferred_username), groupsClaim: groups. Keycloak returns a refresh token without extra scopes. Under the client’s Advanced settings you can require PKCE with the S256 method.
- Applications › Create App Integration: OIDC, Web Application.
- Grant types: Authorization Code and Refresh Token.
- Sign-in redirect URIs:
https://kubeglass.example.com/auth/callback. Sign-out redirect URIs:https://kubeglass.example.com/sign-in. - Assign the people or groups who may use KubeGlass, and copy the client ID and secret.
- For groups, open Security › API › Authorization Servers ›
default› Claims and add a claim namedgroups, included in the ID token, with value type Groups and a filter such as Matches regex.*.
Values: issuerURL: https://<your-org>.okta.com/oauth2/default, scopes: "openid profile email offline_access", usernameClaim: email, groupsClaim: groups.
Microsoft Entra ID
Section titled “Microsoft Entra ID”- App registrations › New registration. Under Redirect URI choose Web and enter
https://kubeglass.example.com/auth/callback. - Under Authentication add
https://kubeglass.example.com/sign-inas a second Web redirect URI, so signing out can return there. - Certificates & secrets › New client secret, and copy its value.
- For groups, Token configuration › Add groups claim, choose Security groups, and for the ID token Group ID. The claim lists group object IDs. Someone in more groups than fit in a token (200) gets no
groupsclaim; filter the groups to those assigned to the application in that case.
Values: issuerURL: https://login.microsoftonline.com/<tenant ID>/v2.0, scopes: "openid profile email offline_access", usernameClaim: oid, groupsClaim: groups. The oid claim is the user’s object ID, which doesn’t change; Microsoft advises against authorizing on preferred_username or email, which can. Bindings then name users by object ID.
- In the Google Cloud console, APIs & Services › Credentials › Create credentials › OAuth client ID, application type Web application.
- Authorized redirect URIs:
https://kubeglass.example.com/auth/callback.
Values: issuerURL: https://accounts.google.com, scopes: "openid profile email", usernameClaim: email. Google’s ID tokens carry no groups, so bind roles to users. Google has no offline_access scope (don’t add it; Google refuses the sign-in) and no RP-initiated logout, so sessions last the ID token’s hour, after which the next page signs the person in again with one click.
To let in only your organization’s accounts, make the OAuth consent screen Internal (Google Workspace), and bind roles only to your domain’s addresses.
Token mode
Section titled “Token mode”With config.authMode=token the sign-in page asks for a Kubernetes bearer token instead, for example:
kubectl -n team-a create token viewer --duration 8hKubeGlass asks the API server who the token belongs to (a SelfSubjectReview sent with the token), and keeps the token in the session on the server. Requests go to the API server with that token and none of KubeGlass’s own credentials, so the token’s RBAC applies, and the chart leaves the impersonate rule out of KubeGlass’s ClusterRole. The token is only sent to the cluster it was checked against, the one KubeGlass runs in; other clusters answer token_other_cluster. A session that holds a JWT ends when the token expires.
Behind an Ingress, set config.trustedProxies to the ingress controller’s addresses, as for oidc mode (see Set it up), so that each person gets their own rate limit and KubeGlass sees the HTTPS the Ingress ends. It doesn’t make KubeGlass believe identity headers in token mode either.
Scripts and the MCP server
Section titled “Scripts and the MCP server”A request with an Authorization: Bearer header needs no session. In oidc mode the token must be a JWT from the provider whose audience is oidc.audience (the client ID by default); KUBEGLASS_OIDC_REQUIRED_SCOPES can require scopes in it. In token mode it is a Kubernetes token, checked with the API server. Pass it to the MCP server with --token.
Bearer tokens only
Section titled “Bearer tokens only”Without a redirect URI (oidc_redirect_url, or the chart’s oidc.redirectURL and no Ingress), oidc mode works as it did before browser sign-in: it needs only the issuer and the audience, and accepts requests with a bearer token, for example from a proxy in front that adds the header. KubeGlass logs one warning at startup, browser sign-in is off, naming what to set, and the sign-in page tells visitors without a token that sign-in isn’t set up. Releases that set the oidc variables with the chart’s extraEnv upgrade unchanged: the chart adds no oidc settings of its own while oidc.issuerURL is empty.
Sessions and their security
Section titled “Sessions and their security”- The cookie,
kubeglass-auth(__Host-kubeglass-authover HTTPS), is httpOnly,SameSite=Lax, and Secure whenever KubeGlass is reached over HTTPS. Under a base path its name ends with a short hash of the path. It holds a random 256-bit session ID and nothing else. - Sessions are stored in the data directory, sealed with AES-256-GCM under the session key, by a hash of their ID. With a PersistentVolumeClaim (
persistence.existingClaim) people stay signed in across restarts; changing the key signs everyone out. - Requests that change something must come from KubeGlass’s own pages: the browser’s
Sec-Fetch-Site(orOrigin) must say same-origin, and the request must carry the session’s CSRF token inX-CSRF-Token. Pages from other origins inKUBEGLASS_CORS_ALLOWED_ORIGINSare trusted. - The sign-in itself is bound to the browser that started it, works once, and expires after 10 minutes. The page to return to must be a path on KubeGlass.
- Codes, states and tokens are never written to the log, and tokens never go in a URL.
- Keep one replica: sessions live in the one pod’s data directory.
When sign-in fails
Section titled “When sign-in fails”The sign-in page says what went wrong, and the log has a Sign-in failed line from the login component with the reason:
| Page says | Look at |
|---|---|
| The sign-in took too long, or started in another tab or browser | The person took over 10 minutes, or opened the callback in another browser. Signing in again works |
| Provider didn’t sign you in | The provider refused: the person isn’t assigned to the application, or chose not to continue |
| Provider answered with an error | The provider’s message is in the log. Often a redirect URI that isn’t registered exactly, or a scope it doesn’t know |
| KubeGlass couldn’t finish signing you in | The token request failed: client ID, client secret, or a redirect URI that differs from the registered one |
| Provider sent an identity KubeGlass doesn’t accept | The ID token failed a check: wrong audience, the user claim missing, an unverified email, or a reserved user name |