No auth by default. When running locally or without --auth-mode, everything works exactly as before - no login, no restrictions.
How It Works
- Authentication identifies the user (proxy headers or OIDC login)
- Reads are filtered by namespace - Radar discovers which namespaces the user can access via
SubjectAccessReviewand only returns resources from those namespaces. Cluster-scoped resources (Nodes, PersistentVolumes, StorageClasses, ClusterRoles whenrbac.viewRBACis on, cluster-scoped CRDs, etc.) have no namespace to filter on, so they are gated per-kind viaSubjectAccessReview: a user sees them only if their own RBAC permits listing that kind. Cluster-wide pod visibility does not imply Node/PV visibility - each cluster-scoped read goes through its own check. - Writes use K8s impersonation - Radar makes the K8s API call as the authenticated user, so K8s RBAC decides whether it’s allowed
- UI adapts - capability checks run per-user, so buttons (exec, restart, scale, Helm) only appear if the user has permission
kubectl, Terraform, GitOps, etc.).
Read granularity: namespace-level
For namespaced resources, reads are authorized at namespace granularity: if your RBAC lets you access a namespace, Radar shows the namespaced resources in it. Radar does not additionally check per-resource-kind RBAC for most namespaced kinds - so a user who can read Pods in a namespace also sees ConfigMaps, Services, etc. in that namespace through Radar, even if their RBAC wouldn’t allowkubectl get configmaps directly.
This is a deliberate tradeoff that follows from how Radar reads the cluster. Rather than calling the Kubernetes API on every request, Radar watches each resource kind once under its own ServiceAccount and serves reads from an in-memory cache shared by all users - that’s what makes it fast on large clusters and able to render topology and real-time views. Because the cache is populated by Radar’s identity, not yours, per-user access has to be re-derived by asking Kubernetes “is this user allowed?” via SubjectAccessReview. Doing that at namespace granularity is one such check per namespace; doing it per-resource-kind would be a check for every kind in every namespace on every read - many times the API-server load, and worst exactly on the large, multi-namespace clusters where the shared cache matters most. So namespace membership is the per-user filter, and per-kind checks are reserved for the cases below where the exposure is highest.
Two kinds are gated more tightly, per-resource-kind, because the shared cache can hold data the user’s own RBAC wouldn’t grant:
- Secrets (and Secret-derived data such as TLS certificate metadata) - shown only if the user can list Secrets in that namespace.
- Cluster-scoped resources (Nodes, PersistentVolumes, ClusterRoles, cluster-scoped CRDs, etc.) - shown only if the user’s RBAC permits listing that kind.
Role/RoleBinding, no ClusterRole) limited to that boundary’s namespaces. Radar detects the restricted permissions at startup and only watches and caches what its ServiceAccount can list - so the instance simply never holds another team’s data, and there’s nothing to over-expose. Point each team at their own instance (an ingress or auth proxy can route them). See In-Cluster Deployment for the namespace-scoped RBAC setup.
Auth Modes
| Mode | Flag | When to Use |
|---|---|---|
none | --auth-mode=none | Local use, single-user, no restrictions needed (default) |
proxy | --auth-mode=proxy | You already have an auth proxy (oauth2-proxy, Pomerium, Cloudflare Access, etc.) in front of your ingress |
oidc | --auth-mode=oidc | You want Radar to handle login directly - no separate auth proxy needed |
Proxy Mode
Use this when you already have (or plan to deploy) an auth proxy like oauth2-proxy, Pomerium, Authelia, or similar. The proxy authenticates users and forwards their identity to Radar via HTTP headers. Flow:Security: Your ingress must stripLogout behavior: The user menu always shows a Logout button. Clicking it clears Radar’s session cookie. On its own, that isn’t enough to switch users: your proxy re-injects the identity header on the next request and signs the same user back in. To make logout actually switch users, point Radar at your proxy’s sign-out URL withX-Forwarded-UserandX-Forwarded-Groupsheaders from external requests to prevent spoofing. The auth proxy should be the only path to Radar. Radar logs a warning at startup as a reminder.
--auth-proxy-logout-url (or auth.proxy.logoutURL in Helm) - e.g. oauth2-proxy’s /oauth2/sign_out. Radar then redirects the browser there after clearing its own cookie, so the upstream session is torn down too. When unset, the menu shows a note that the proxy may re-authenticate automatically.
Note: HTTP Basic Auth has no reliable logout mechanism - browsers cache credentials and resend them. If you need user-switching, prefer a proxy with a real sign-out endpoint (oauth2-proxy, Authelia) over plain Basic Auth.
OIDC Mode
Use this when you want Radar to handle login directly - no separate auth proxy needed. Radar redirects to your identity provider (Google, Okta, Dex, Keycloak, etc.), validates the token, and creates a session cookie. Flow:openid profile email groups at the authorization endpoint. The groups scope is required by Dex, Keycloak, and most IdPs to actually include the groups claim in the ID token. If your IdP rejects unknown scopes (Google in particular doesn’t define groups), override via auth.oidc.scopes / --auth-oidc-scopes to drop it or substitute the provider-specific equivalent.
Logout behavior:
When a user clicks logout, Radar clears the local session cookie and - if the identity provider supports it - redirects the browser to the provider’s logout endpoint (RP-Initiated Logout) to terminate the SSO session as well. This prevents the common issue where the user appears to log out but is silently re-authenticated on the next visit.
Radar discovers the provider’s end_session_endpoint automatically from the OIDC discovery document at startup. If the provider supports it (Okta, Keycloak, Azure AD), Radar redirects the browser there to terminate the SSO session. If the provider doesn’t advertise end_session_endpoint (e.g., Google), Radar uses prompt=login on the next authorization request to force the IdP to show a login screen instead of silently re-authenticating. Check the startup logs for confirmation:
--auth-oidc-post-logout-redirect-url (or auth.oidc.postLogoutRedirectURL in Helm). This URL must be registered with your identity provider as a valid post-logout redirect URI.
Back-Channel Logout (IdP-initiated session revocation):
When an admin disables a user at the IdP level (e.g., disables an Okta account), Radar has no way to know - the existing session cookie remains valid until it expires. Back-Channel Logout (spec) solves this: the IdP POSTs a signed logout_token to Radar’s /auth/backchannel-logout endpoint, and Radar immediately revokes the matching session.
To enable, set --auth-oidc-backchannel-logout (or auth.oidc.backchannelLogout: true in Helm), then register https://radar.example.com/auth/backchannel-logout as the Back-Channel Logout URI in your IdP.
- Single-replica only. Revocations are stored in memory and not shared across pods. If Radar runs multiple replicas behind a load balancer, a revocation hitting one pod won’t affect sessions on other pods.
- Lost on restart. If Radar restarts, all pending revocations are lost. The session will expire naturally at cookie TTL (4h default).
sidrequired for targeted revocation. If the IdP’s logout_token contains onlysub(nosid), Radar cannot target the specific session - it logs a warning and the session expires at cookie TTL. Most major IdPs (Okta, Keycloak, Auth0, Azure AD) includesid.- Google does not support back-channel logout. For Google, session exposure is bounded by cookie TTL only.
existingSecret for the client secret but pass clientID as a plain value (it’s not sensitive).
TLS configuration for self-signed certificates:
If your OIDC provider uses a self-signed or internal CA certificate (e.g., on-prem Keycloak), use one of:
caCert in Kubernetes, mount the CA certificate into the pod via a ConfigMap or Secret volume.
Radar Cloud mode
If you seeRADAR_CLOUD_MODE or cloud.* values in the chart, they control a specialized deployment mode used by Radar Cloud - a hosted SaaS that lets a single Cloud frontend manage many in-cluster Radar instances over an outbound tunnel. You don’t need to use it to run Radar standalone; leave cloud.enabled: false (the default).
Under cloud-mode (RADAR_CLOUD_MODE=true, set automatically by the chart when cloud.enabled=true), Radar:
- Forces
--auth-mode=proxywith pinnedX-Forwarded-User/X-Forwarded-Groupsheaders - the Cloud tunnel is the trust boundary. - Ships three default ClusterRoleBindings mapping Cloud’s
cloud:owner/cloud:member/cloud:viewergroups to the standard K8sadmin/edit/viewClusterRoles. Configurable viacloud.defaultRbac.*invalues.yaml. - Hardens the listener (no
/debug/pprof/*, narrower exempt paths).
rbac.helm=true). Helm’s pre-flight existence check needs cluster-scoped reads/writes that the K8s built-in admin/edit/view ClusterRoles don’t grant. The chart emits two add-on ClusterRoles, split by trust tier:
radar-helm- CRDs, StorageClasses, RuntimeClasses, PriorityClasses, PodDisruptionBudgets, Namespaces. Bound tocloud:ownerANDcloud:member.radar-helm-admin- RBAC objects (Roles/Bindings, Cluster variants), validating/mutating webhooks, ApiServices. Bound tocloud:ownerONLY. Granting these to a tier weaker than owner would let a member self-promote to cluster-admin in oneClusterRoleBindingwrite, collapsing the owner/member distinction.
cloud:member attempting to install a chart that bundles its own RBAC will get a typed rbac_preflight 403 with an actionable “ask an owner” message. Day-to-day app charts and operator-CRD installs still work for members.
Customer-facing documentation for Radar Cloud lives on radarhq.io. The authoritative reference for the Cloud-mode chart values is the comment block in deploy/helm/radar/values.yaml under cloud:.
Setting Up User Permissions
Radar delegates authorization entirely to K8s RBAC via impersonation. It doesn’t have its own role system - permissions are managed with standard K8s tooling (kubectl, Terraform, Helm, GitOps, etc.). If your cluster already has RBAC bindings (most production clusters do), they work automatically. Radar impersonates the authenticated user, and K8s evaluates their existing bindings. You only need to create new bindings if users don’t already have the access they need.How It Works with Cloud Providers
Cloud-managed clusters typically map their IAM to K8s RBAC automatically:- GKE: Google Groups for RBAC maps Google Workspace groups to K8s groups. IAM roles (
roles/container.viewer, etc.) grant K8s access. The username is the user’s Google email. - EKS: The
aws-authConfigMap maps IAM roles/users to K8s users and groups. The username is typically the IAM role ARN or a mapped alias. - AKS: Azure AD integration maps Azure AD groups directly to K8s groups. The username is the Azure AD user principal name.
Understanding the Chain
User (a string like alice@company.com) and Group (a string like sre-team). These strings just need to match what the identity provider returns.
Using Built-in K8s Roles
K8s ships with built-in ClusterRoles that work well with Radar. You don’t need to create custom roles unless you want fine-grained control:| Built-in Role | What Users Can Do in Radar |
|---|---|
view | See resources, topology, events, logs. No restart, scale, or Helm writes. |
edit | Everything in view + restart, scale, edit resources. Does NOT include exec or port-forward (those require pods/exec and pods/portforward subresources - use the custom radar-operator role below). |
admin | Everything in edit + manage RBAC within namespaces. Same exec/port-forward caveat as edit. |
cluster-admin | Full access to everything across all namespaces, including exec and port-forward. |
Step 1: Create a ClusterRole with the permissions you want
Step 2: Bind it to your users or groups
By group (recommended - matches group from your identity provider):Step 3: Verify
After deploying with auth enabled, you can verify impersonation works:What Users See
When auth is enabled:- A username appears in the Radar header with a logout option
- The namespace selector only shows namespaces the user can access
- Topology, resources, events, dashboard are filtered to accessible namespaces
- Cluster-scoped resources (Nodes, PersistentVolumes, StorageClasses) are gated per-kind via
SubjectAccessReview- a user sees them only if their own RBAC permits listing that kind, independent of namespace access - Helm releases are visible to all authenticated users (reads use the ServiceAccount, not impersonation, because the K8s
viewrole doesn’t includelist secretswhich Helm requires). Write operations (install, upgrade, rollback, uninstall) are impersonated and require the user to have appropriate RBAC. - Write buttons (restart, scale, exec, Helm install, etc.) only appear if the user has permission
- Write operations return 403 from K8s if RBAC denies them (shown as an error toast)
- The /api/auth/me endpoint returns the current user info and whether auth is enabled
ServiceAccount RBAC
When auth is enabled, Radar’s ServiceAccount needs two additional permissions (added automatically by the Helm chart):Session Cookies
Radar uses stateless HMAC-SHA256 signed cookies for sessions. The cookie contains the username and groups - no server-side session storage.- Cookie TTL: 4 hours by default (sliding), configurable with
--auth-cookie-ttlorauth.cookieTTLin Helm values. Sessions auto-extend while you’re active; idle sessions expire after the configured TTL. Active users won’t notice - Radar’s frontend polling keeps the session alive automatically. - Proxy mode: When the cookie expires, the middleware transparently re-creates the session from proxy headers on the next request, so the shorter default TTL has no UX impact.
- Session secret: Set
auth.secretorRADAR_AUTH_SECRETenv var. If empty, a random key is generated at startup (sessions won’t survive pod restarts) - For production: Use
auth.existingSecretto reference a K8s Secret, so sessions survive restarts
Configuration Reference
| Parameter | CLI Flag | Helm Value | Default |
|---|---|---|---|
| Auth mode | --auth-mode | auth.mode | none |
| Session secret | --auth-secret | auth.secret | auto-generated |
| Cookie TTL | --auth-cookie-ttl | auth.cookieTTL | 4h (sliding) |
| User header (proxy) | --auth-user-header | auth.proxy.userHeader | X-Forwarded-User |
| Groups header (proxy) | --auth-groups-header | auth.proxy.groupsHeader | X-Forwarded-Groups |
| Proxy logout URL (proxy) | --auth-proxy-logout-url | auth.proxy.logoutURL | - |
| OIDC issuer | --auth-oidc-issuer | auth.oidc.issuerURL | - |
| OIDC client ID | --auth-oidc-client-id | auth.oidc.clientID | - |
| OIDC client secret | --auth-oidc-client-secret | auth.oidc.clientSecret | - |
| OIDC client secret (K8s Secret) | - | auth.oidc.existingSecret | - |
| OIDC client secret key | - | auth.oidc.clientSecretKey | client-secret |
| OIDC redirect URL | --auth-oidc-redirect-url | auth.oidc.redirectURL | - |
| OIDC groups claim | --auth-oidc-groups-claim | auth.oidc.groupsClaim | groups |
| OIDC scopes | --auth-oidc-scopes | auth.oidc.scopes | openid,profile,email,groups |
| OIDC post-logout redirect | --auth-oidc-post-logout-redirect-url | auth.oidc.postLogoutRedirectURL | - |
| OIDC username prefix | --auth-oidc-username-prefix | auth.oidc.usernamePrefix | - |
| OIDC groups prefix | --auth-oidc-groups-prefix | auth.oidc.groupsPrefix | - |
| OIDC CA certificate | --auth-oidc-ca-cert | auth.oidc.caCert | - |
| OIDC skip TLS verify | --auth-oidc-insecure-skip-verify | auth.oidc.insecureSkipVerify | false |
| OIDC backchannel logout | --auth-oidc-backchannel-logout | auth.oidc.backchannelLogout | false |
Troubleshooting
User is logged back in immediately after logout (OIDC)
This happens when the identity provider’s SSO session is not terminated during logout. Check:- Check the startup logs. Look for
[oidc] RP-Initiated Logout enabledor theprompt=loginfallback message. Both approaches should prevent silent re-authentication. - Is
post_logout_redirect_uriregistered? If you set--auth-oidc-post-logout-redirect-url, it must be registered as a valid post-logout redirect URI with your identity provider. If not registered, the IdP may show an error instead of redirecting. - Browser extensions or aggressive caching? Some browser extensions may interfere with the redirect to the IdP’s logout endpoint.
Users get 401 on every request
- Proxy mode: Check that the auth proxy is setting
X-Forwarded-User. Inspect with: - OIDC mode: Verify the issuer URL, client ID, and redirect URL are correct. Check Radar logs for OIDC errors.
Users authenticate but see empty dashboard / no resources
The user will see a “No Namespace Access” message if they have no K8s RBAC bindings. Radar usesSubjectAccessReview to discover which namespaces each user can access - with no bindings, the result is zero namespaces.
Verify the user’s access:
All users see empty data even with correct RBAC
Radar’s ServiceAccount may be missing thesubjectaccessreviews permission needed to check user access. When this fails, Radar denies all namespace access (fail-closed). Verify:
no, the Helm chart’s RBAC was not applied. Re-install with rbac.create: true (the default) or add the rule manually.
Write operations return 403
The user’s K8s RBAC doesn’t include the required verb. Check with:Impersonation errors in Radar logs
Radar’s ServiceAccount is missing impersonation permissions. Verify:rbac.create: false in Helm, make sure your custom ClusterRole includes the impersonation rules.