Skip to main content
Celum’s backend owns authentication (OIDC) and authorization (IAM). A user signs in through your identity provider, receives a signed session cookie, and every action is checked against IAM policies and recorded in the audit log.

Authentication

Modes

OIDC

Production. Sign-in delegated to your provider (Entra ID or any OIDC issuer).

AUTH_DISABLED

Development only. Injects a mock dev@localhost admin and skips all token validation.

The OIDC flow

The backend serves four auth endpoints: The ID token is verified (signature + expiry) and the user’s email, name, and groups are read from it. The groups claim drives IAM membership. On successful callback the backend issues an HS256-signed JWT stored in the k8s-gate-session cookie:
  • HttpOnly, Secure, SameSite=Lax
  • Claims: email, name, groups, issuer k8s-gate, plus issued/expiry times
  • Lifetime from SESSION_MAX_AGE (default 86400 = 24h)

Configuration

Authorization (IAM)

IAM is AWS-inspired: policies with Allow/Deny statements are attached to groups, groups map to your provider’s groups (or hold manual members), and every request is evaluated against the user’s effective policies. The mechanics — KRN matching, wildcard rules, unmapped routes, cache refresh — are covered in depth in Permissions model.

Enforcement

IAM_ENABLED defaults to audit-only. When false, denied requests are logged but still allowed. Set IAM_ENABLED=true to enforce — otherwise authorization is effectively off.
When enforcing, a denied request returns 403, and any route that isn’t mapped to a permission is denied by default.

KRN — resource names

Resources are addressed by KRN (krn:vks:...), built from type:id segments:
Wildcards are supported: * matches one segment, a trailing * matches the rest, and prefixes like prod-* match prod-cluster-1.

Policy document

Actions support exact (cluster:List), service (cluster:*), and full (*:*) wildcards. Every action Celum defines is listed in Permissions reference.

Evaluation order

1

Default deny

With no matching Allow, access is denied.
2

Explicit deny wins

Any matching Deny statement immediately denies, overriding any Allow.
3

Explicit allow

Otherwise, a matching Allow grants access.

Groups

A user’s groups come from two sources, combined:
  • Provider-mapped — a group whose oidc_group_id matches one of the user’s OIDC group/role claims (per OIDC_MAPPING_SOURCE).
  • Manual — emails added directly to a group.
The match on oidc_group_id is exact, against whichever claim OIDC_MAPPING_SOURCE / OIDC_GROUPS_CLAIM selects. With OIDC_MAPPING_SOURCE=groups that means the provider’s group object ID, not its display name; with roles it means the app role’s value.

Bootstrapping the first admin

A fresh database is seeded with an Administrators group holding the K8sGateAdmin policy and mapped to the OIDC claim value krn:vks:admin. Without that seed, turning on IAM_ENABLED against an empty database locks everyone out — there would be policies, but no group carrying them and no claim mapping to anything, so every request ends in the default deny. To get in, either assign the claim value krn:vks:admin to your account at the identity provider, or edit the Administrators group’s mapped OIDC group so it matches a claim you already carry.

Built-in policies

These are reconciled at startup and are read-only in the UI.
Credential downloads are deliberately not in Operator or Viewer. Attach K8sGateSensitiveRead separately to the people who should be able to pull a kubeconfig, talosconfig, or SSH password.

Managing IAM in the UI

API clients can authenticate with a Bearer token issued from My permissions instead of the session cookie.

What commonly goes wrong

Next steps

Permissions model

How routes resolve to actions and KRNs, and how matching really works.

Permissions reference

Every action Celum defines, by service.

Architecture

Where session and IAM checks sit in the request pipeline.

Create a cluster

Cluster creation is gated by cluster:Create.