Access Control

View as Markdown

The OpenShell gateway supports two access-control models for human callers on Kubernetes:

ModelWhen to use
OIDC (recommended)Production deployments. Integrates with an existing identity provider, supports role-based access control, and gives each user their own identity without distributing certificates.
Reverse-proxy auth terminationAn access proxy (Cloudflare Access, ngrok, corporate SSO) authenticates callers in front of the gateway. The gateway trusts the proxy and skips its own client-cert check.

The Helm chart always generates mTLS certificates at install time. The gateway uses them for transport-layer security regardless of which access-control model you choose. The client bundle in the openshell-client-tls secret is used internally by sandbox supervisors, not for granting access to individual users.

For how the CLI resolves gateways and stores credentials, refer to Gateway Authentication.

Sandbox Supervisor Identity

Kubernetes sandbox supervisors authenticate back to the gateway as sandbox workloads. By default, the Kubernetes compute driver validates each projected ServiceAccount token and returns the authenticated sandbox ID to the gateway. The gateway verifies the sandbox still exists and mints its own sandbox JWT.

Dynamic provider token grants can use SPIFFE without changing supervisor-to-gateway authentication. Set server.providerTokenGrants.spiffe.enabled=true to mount the SPIFFE CSI Workload API socket into gateway and sandbox pods while keeping the projected ServiceAccount token bootstrap and gateway-minted sandbox JWT path.

Provider token grants require a SPIFFE implementation such as SPIRE and identities for the gateway and sandbox pods. The repository’s local SPIRE overlay assigns sandbox IDs from the pod’s openshell.ai/sandbox-id annotation, but the gateway validation path only requires the supervisor SVID to be valid and in the same SPIFFE trust domain as the gateway SVID. Provider profiles with token_grant metadata cause the sandbox supervisor to request JWT-SVIDs and exchange them for upstream OAuth2 access tokens. Token-exchange profiles also require a gateway SPIFFE identity because the gateway brokers the intermediate token exchange with its own JWT-SVID.

The gateway verifies supervisor JWT-SVIDs with JWT bundles fetched from the SPIFFE Workload API, so intermediate token exchange does not require gateway access to the SPIRE OIDC discovery endpoint or its TLS CA.

OIDC User Authentication

Set server.oidc.issuer to enable OIDC. The gateway validates the Authorization: Bearer <token> header on every request against the issuer’s JWKS endpoint. It accepts JWTs signed with RS256, RS384, RS512, PS256, PS384, PS512, ES256, ES384, or EdDSA (Ed25519) keys published in the issuer JWKS. Okta tenants that sign access tokens with ES256 work without additional gateway configuration.

helm upgrade openshell \
oci://ghcr.io/nvidia/openshell/helm-chart \
--version <version> \
--namespace openshell \
--set server.oidc.issuer=https://your-idp.example.com/realms/openshell \
--set server.oidc.audience=openshell-cli \
--set server.tls.clientCaSecretName=""

Set server.tls.clientCaSecretName="" when the gateway terminates TLS directly and browsers or CLI clients connect without client certificates. The chart omits client_ca_path from gateway.toml and does not mount the client-CA volume, leaving HTTPS-only transport with OIDC for user authentication. Do not set the value to null; omit the key to use the chart default, or set it to "" to disable client certificate verification.

The audience value must match the client ID configured in your identity provider for the OpenShell resource server.

OIDC values reference

ValueDefaultPurpose
server.oidc.issuer""OIDC issuer URL. Empty disables OIDC.
server.oidc.audienceopenshell-cliExpected aud claim in the JWT.
server.oidc.jwksTtl3600JWKS key cache TTL in seconds. Must be greater than zero.
server.oidc.rolesClaim""Dot-separated path to the roles array in JWT claims.
server.oidc.adminRole""Role name that grants admin access.
server.oidc.userRole""Role name that grants standard user access.
server.oidc.scopesClaim""Dot-separated path to the scopes array in JWT claims.

Auth-only mode vs. RBAC mode

Leave both adminRole and userRole empty to use auth-only mode: any request with a valid JWT from the configured issuer is accepted, but no role distinction is enforced.

Set both values to enable RBAC mode, where the gateway checks the role claim and enforces access based on the assigned role:

helm upgrade openshell \
oci://ghcr.io/nvidia/openshell/helm-chart \
--version <version> \
--namespace openshell \
--set server.oidc.issuer=https://your-idp.example.com/realms/openshell \
--set server.oidc.audience=openshell-cli \
--set server.tls.clientCaSecretName="" \
--set server.oidc.rolesClaim=realm_access.roles \
--set server.oidc.adminRole=openshell-admin \
--set server.oidc.userRole=openshell-user

Both adminRole and userRole must be set, or both must be empty. Setting only one is not supported.

OIDC RBAC is method-level authorization. It controls which API operations a caller can perform, but provider and sandbox records are not owned by individual OIDC subjects. In shared clusters, treat provider credentials as gateway-wide resources and use separate gateways or external tenancy controls when users must not see or attach each other’s providers and sandboxes.

Provider-specific rolesClaim paths

ProviderrolesClaim value
Keycloakrealm_access.roles
Microsoft Entra IDroles
Oktagroups

Reverse-Proxy Auth Termination

When an access proxy, such as Cloudflare Access, ngrok, or a corporate SSO gateway, handles authentication in front of the OpenShell gateway, you can explicitly allow unauthenticated user calls at the gateway:

helm upgrade openshell \
oci://ghcr.io/nvidia/openshell/helm-chart \
--version <version> \
--namespace openshell \
--set server.auth.allowUnauthenticatedUsers=true

The gateway still serves TLS and sandbox supervisors still authenticate with gateway-minted sandbox JWTs. User-facing CLI/API calls without OIDC or mTLS credentials are accepted as an unauthenticated local developer principal. The proxy is responsible for authenticating callers and forwarding only authorized traffic.

When the gateway terminates TLS directly and callers connect without client certificates, also set server.tls.clientCaSecretName="" as described in the OIDC section above.

To also disable TLS entirely (when the proxy terminates TLS before the request reaches the gateway):

--set server.disableTls=true \
--set server.auth.allowUnauthenticatedUsers=true

Only enable unauthenticated users when the gateway is not reachable from outside a trusted local development environment or the proxy path is fully trusted. Never expose a plaintext, auth-disabled gateway to a public network.

Register the gateway with the CLI using the proxy’s public URL. The browser-based login flow runs automatically on first use:

openshell gateway add https://gateway.example.com --name production