Files
5548405fcb feat(credentials): add provider credential storage drivers (#2437)
* feat(credentials): add provider credential storage drivers

Signed-off-by: Taylor Mutch <taylormutch@gmail.com>

* fix(credentials): harden credential update handling

Signed-off-by: Taylor Mutch <taylormutch@gmail.com>

* fix(credentials): harden credential driver security, correctness, and performance

Address review findings from the credential storage drivers PR:

- Route additional_credentials through the driver on refresh to prevent
  silent data loss for multi-credential providers (e.g. AWS STS)
- Clean up stored credential handles on CAS failure during refresh to
  prevent orphaned secrets in external backends
- Enforce namespace validation in the Kubernetes Secrets driver to
  prevent cross-namespace credential access when allow_reference_namespace
  is not enabled
- Cache Vault Kubernetes auth tokens with 80% TTL to avoid re-authenticating
  on every credential operation
- Parallelize resolve_credentials in all three drivers using try_join_all
  for faster sandbox startup
- Add existingSecret support for the KEK Secret to fix helm template/GitOps
  workflows where lookup returns empty and regenerates the key
- Document RBAC blast radius for the Kubernetes Secrets credential driver
  and recommend a dedicated namespace

Signed-off-by: Varsha Prasad <varshaprasad96@gmail.com>
Signed-off-by: Varsha Prasad Narsing <varshaprasad96@gmail.com>

* fix(credentials): add optimistic concurrency, fix thundering herd, parallelize operations

Use resourceVersion optimistic concurrency with retry loop for K8s
Secret ownership checks to prevent TOCTOU races. Switch Vault token
cache from RwLock to Mutex with double-check pattern to prevent
thundering herd on cache miss. Parallelize credential store and delete
operations across independent keys using try_join_all.

Signed-off-by: Varsha Prasad <varshaprasad96@gmail.com>
Signed-off-by: Varsha Prasad Narsing <varshaprasad96@gmail.com>

* fix(credentials): handle partial failures, add delete retry, consolidate cleanup

Replace try_join_all with join_all in credential store/delete operations
to handle partial failures — successfully-stored handles are cleaned up
when another key fails. Add retry loop with conflict detection to
db-credstore delete_credential, matching the K8s driver pattern.
Consolidate 4 manual cleanup_pre_stored_provider_credentials call sites
into a single error handler using an async block. Remove inconsistent
.trim() from db-credstore validate_handle_owner.

Signed-off-by: Varsha Prasad <varshaprasad96@gmail.com>
Signed-off-by: Varsha Prasad Narsing <varshaprasad96@gmail.com>

* fix(credentials): fix retry loop guard and remove unprotected validation

Remove attempt-count guard from 409/Aborted match arms in retry loops
so the post-loop Status::aborted error is reachable after exhausting
retries. Previously, last-attempt conflicts fell through to the
catch-all error arm, producing misleading Status::unavailable errors.

Remove duplicate validation calls that ran after
prepare_provider_credential_update but outside the cleanup-protected
async block, which would leak pre-stored handles on failure.

Signed-off-by: Varsha Prasad <varshaprasad96@gmail.com>
Signed-off-by: Varsha Prasad Narsing <varshaprasad96@gmail.com>

* fix(credentials): add workspace/provider UUID to credential backend paths

Include workspace and provider ID in credential backend object paths to ensure
cross-workspace uniqueness and prevent credential collision (GATOR-1806c9be-01).

- Updated credential driver proto to include workspace and provider_id fields
- Modified Vault driver to include workspace/provider_id in managed_secret_path
- Modified Kubernetes Secrets driver to include workspace/provider_id in
  credential_owner_id and managed_secret_name
- Updated all credential runtime calls to pass workspace/provider_id
- Updated tests to use the new signatures

This prevents two workspaces sharing the same external credential store from
colliding on provider names, which was a critical security issue (CWE-639).

* fix(credentials): preserve provider-level expiration for handle-backed credentials

Compute effective expiration from both provider and driver values using the
earliest non-zero timestamp and skip expired values before insertion
(GATOR-1806c9be-02).

- Modified resolve_provider_handles to check provider credential_expires_at_ms
- Skip expired credentials during resolution instead of returning them
- Use effective expiration (min of provider and driver) in resolution results
- Fix inference.rs to preserve earliest expiration when merging

This ensures handle-backed credentials respect the same expiration semantics
as inline credentials.

* fix(credentials): stage refresh changes under new handles before validation

Stage credential replacements under new immutable handles instead of reusing
existing handles to prevent overwriting committed values before validation/CAS
(GATOR-1806c9be-03).

- Stage credentials with empty existing_handles map to force new handle creation
- Validate and CAS before the new values are committed to backend storage
- Delete old handles only after successful CAS
- On CAS failure, delete only the newly staged handles
- This prevents CWE-362/CWE-367 race conditions where failed refreshes could
  still modify or delete the active credential

The fix ensures that a rejected refresh cannot modify the backend object still
referenced by the committed provider record.

* fix(credentials): add timeouts to credential driver RPCs

Apply configured timeouts to both startup capability negotiation and runtime
RPCs to prevent indefinite hangs (GATOR-1806c9be-05).

- Add DEFAULT_CREDENTIAL_DRIVER_RPC_TIMEOUT_SECS constant (30s)
- Apply timeout to GetCapabilities during startup connection
- Apply timeout to all runtime RPCs (store, delete, resolve)
- Use tokio::time::timeout to bound the entire GetCapabilities operation
  during startup, not just the socket connection
- Return contextual deadline errors on timeout

This prevents a faulty or overloaded driver from hanging gateway operations
indefinitely.

* fix(credentials): fix test to use consistent workspace/provider identity

The Kubernetes auth Vault resolve test was constructing a managed path with
test-workspace/test-provider-id but sending default/prov-123 in the request,
causing validation to reject the request (GATOR-18e32351-01).

- Update test to use test-workspace and test-provider-id in the request to
  match the logical_path construction
- This ensures the test exercises the intended code path and validates
  Kubernetes auth resolution properly

The test now passes and correctly validates identity enforcement.

* fix(credentials): use unique staging ID for refresh to avoid overwrites

Stage refresh replacements under genuinely distinct immutable handles using
a unique staging ID to prevent overwriting committed values (GATOR-1806c9be-03).

- Generate a unique staging ID using UUID for each refresh operation
- Use this staging ID when storing credentials instead of the real provider ID
- Pass the same staging ID during cleanup on failure to delete only staged objects
- This ensures deterministic paths (Vault) and object names (K8s) don't collide
  with the committed provider's credentials

The fix prevents failed refreshes from silently replacing active credentials
or breaking providers by deleting still-referenced backend objects.

* fix(credentials): wrap credential driver RPCs in local timeouts

Add local tokio::time::timeout wrappers around credential driver RPCs to
bound non-compliant or stalled UDS peers (GATOR-1806c9be-05).

- Wrap StoreCredential, DeleteCredential, and ResolveCredentials in local timeouts
- Return contextual deadline_exceeded errors when timeouts occur
- Keep existing gRPC timeout metadata for compliant implementations
- GetCapabilities during startup was already wrapped in previous commit

This ensures a faulty local driver cannot hang gateway operations indefinitely,
even if it accepts the connection but never responds to the RPC.

* fix(credentials): preserve ownership for staged refreshes

Signed-off-by: Seth Jennings <sjenning@redhat.com>

* fix(credentials): bound startup capability probe

Signed-off-by: Seth Jennings <sjenning@redhat.com>

* test(provider): authenticate credential handler requests

Signed-off-by: Seth Jennings <sjenning@redhat.com>

* fix(ci): grant actions read to credential driver e2e

Signed-off-by: Seth Jennings <sjenning@redhat.com>

---------

Signed-off-by: Taylor Mutch <taylormutch@gmail.com>
Signed-off-by: Varsha Prasad <varshaprasad96@gmail.com>
Signed-off-by: Varsha Prasad Narsing <varshaprasad96@gmail.com>
Signed-off-by: Seth Jennings <sjenning@redhat.com>
Co-authored-by: Taylor Mutch <taylormutch@gmail.com>
Co-authored-by: Varsha Prasad Narsing <varshaprasad96@gmail.com>
2026-08-05 16:29:41 +00:00
..

OpenShell Helm Chart

Experimental - the Kubernetes deployment path is under active development. Expect rough edges and breaking changes.

This chart deploys the OpenShell gateway into a Kubernetes cluster. It is published as an OCI artifact to GHCR at oci://ghcr.io/nvidia/openshell/helm-chart.

Prerequisites

The Kubernetes Agent Sandbox CRDs and controller must be installed on the cluster before deploying OpenShell. Install them with:

kubectl apply -f https://github.com/kubernetes-sigs/agent-sandbox/releases/latest/download/manifest.yaml

Install on Kubernetes

helm install openshell oci://ghcr.io/nvidia/openshell/helm-chart --version <version>

Install on OpenShift

See the full OpenShift install guide for details. Quick start:

# Precreate the openshell namespace so we can create the SCC cluster role
oc create ns openshell

# Sandboxes are deployed into the openshell namespace and use the openshell-sandbox service account
oc adm policy add-scc-to-user privileged -z openshell-sandbox -n openshell

# Deploy openshell with overrides to allow SCC assignment of fsGroup and runAsUser for the gateway
helm install openshell oci://ghcr.io/nvidia/openshell/helm-chart --version <version> -n openshell \
  --set server.disableTls=true \
  --set podSecurityContext.fsGroup=null \
  --set securityContext.runAsUser=null

Available versions

Tag Source Notes
<semver> (e.g. 0.6.0) Tagged GitHub release Tracks the matching gateway and supervisor image versions. Recommended for production.
0.0.0-dev Latest commit on main Floating tag, overwritten on every push. appVersion is dev, so images resolve to the :dev tag.
0.0.0-dev.<commit-sha> A specific commit on main Per-commit pin. Chart version and appVersion both use the full 40-character commit SHA, which matches the image tag pushed by CI.

The dev tags are intended for testing changes ahead of a release. Production deployments should pin to a tagged release.

Configuration

See values.yaml for source defaults. Selected overlays:

Database backend

By default, OpenShell uses SQLite and runs the gateway as a StatefulSet so the database is backed by a per-pod PVC:

server:
  dbUrl: "sqlite:/var/openshell/openshell.db"

External PostgreSQL

Use external PostgreSQL when the gateway should connect to a database managed outside this chart. The OpenShell chart does not deploy a database; install PostgreSQL separately using the chart, operator, or managed service that fits your environment, then pass the connection URI through a Secret.

Create a Secret containing the PostgreSQL connection URI if one does not already exist:

kubectl create secret generic my-pg-credentials -n openshell \
  --from-literal=uri="postgresql://user:pass@host:5432/dbname"

Then install the chart pointing at that Secret:

helm install openshell oci://ghcr.io/nvidia/openshell/helm-chart --version <version> \
  -n openshell \
  --set workload.kind=deployment \
  --set server.externalDbSecret=my-pg-credentials

Use workload.kind=deployment for external database-backed multi-replica gateways. workload.kind=statefulset is still available for single-replica SQLite installs and for operators who explicitly need StatefulSet identity or storage semantics.

Credential storage

By default, the chart uses the gateway's encrypted database credential storage. The gateway writes encrypted provider credential envelopes to the OpenShell database. The chart creates a retained Kubernetes Secret with the shared key-encryption key and injects that key into every gateway pod, so the same default works for single-replica and external database-backed HA deployments.

Use kubernetes-secrets or vault instead when credentials should live in a cluster or external secret backend. Enabling one external credential driver disables the default credential-storage key-encryption key Secret and env injection.

OpenShift

Append these flags to any of the PostgreSQL commands above for OpenShift:

--set server.disableTls=true \
--set podSecurityContext.fsGroup=null \
--set securityContext.runAsUser=null

Secret bootstrap

By default, a pre-install/pre-upgrade hook Job runs openshell-gateway generate-certs to create the gateway's server/client mTLS Secrets and sandbox JWT signing Secret. The Job uses the gateway image itself, so air-gapped environments only need to mirror that one image (no separate openssl/alpine sidecar).

When certManager.enabled=true, cert-manager owns the TLS Secrets and the chart runs the same hook in JWT-only mode because cert-manager does not create the sandbox JWT signing Secret. This precedence applies even if pkiInitJob.enabled remains true. Set pkiInitJob.enabled=false only when an external non-cert-manager TLS source manages TLS and you pre-create the sandbox JWT signing Secret.

SPIFFE/SPIRE provider token grants

Set server.providerTokenGrants.spiffe.enabled=true to let sandbox supervisors use SPIFFE JWT-SVIDs for dynamic provider token grants. The chart keeps supervisor-to-gateway authentication on gateway-minted sandbox JWTs and passes the SPIFFE Workload API socket path to the Kubernetes driver so sandbox pods can mount the SPIFFE CSI socket.

For local development, uncomment the SPIRE Helm releases in skaffold.yaml and add ci/values-spire.yaml to the OpenShell release values files.

Values

Key Type Default Description
affinity object {} Affinity rules for the gateway pod.
certManager.caSecretName string "openshell-ca-tls" Secret created for the intermediate CA (Certificate with isCA: true).
certManager.certificateDuration string "8760h" Duration for cert-manager-issued certificates.
certManager.certificateRenewBefore string "720h" Renewal window for cert-manager-issued certificates.
certManager.clientCaFromServerTlsSecret bool true Mount gateway client CA from the server TLS secret's ca.crt (populated by cert-manager for certs issued by a CA Issuer). Avoids a separate openshell-server-client-ca Secret.
certManager.enabled bool false Create cert-manager Issuer and Certificate resources. When enabled, cert-manager owns TLS and the chart runs a JWT-only certgen hook to create the sandbox JWT signing Secret that cert-manager does not manage.
certManager.serverDnsNames list ["openshell","openshell.openshell.svc","openshell.openshell.svc.cluster.local","localhost","openshell.localhost","*.openshell.localhost","host.docker.internal"] DNS SANs on the cert-manager-issued server certificate.
certManager.serverIpAddresses list ["127.0.0.1"] IP SANs on the cert-manager-issued server certificate.
fullnameOverride string "" Override the full generated resource name.
grpcRoute.enabled bool false Create a Gateway API GRPCRoute for the gateway service.
grpcRoute.gateway.className string "eg" GatewayClass to reference. Envoy Gateway installs one named "eg".
grpcRoute.gateway.create bool false When true, a Gateway resource is created in the release namespace. Set to false and provide name/namespace to attach to a pre-existing Gateway.
grpcRoute.gateway.listener.allowedRoutes string "Same" "Same" restricts attached routes to the release namespace; "All" allows any namespace.
grpcRoute.gateway.listener.port int 80 Listener port for the generated Gateway resource. Use 443 with protocol HTTPS.
grpcRoute.gateway.listener.protocol string "HTTP" Listener protocol for the generated Gateway resource: HTTP or HTTPS. HTTPS terminates TLS at the Envoy Gateway listener; pair it with server.disableTls=true so Envoy forwards plaintext to the gateway pod, and use OIDC for client identity (the gateway never sees the client cert).
grpcRoute.gateway.listener.tls.certificateRefs list [] certificateRefs for the HTTPS listener. Required when protocol is HTTPS. Each entry needs a name pointing at a kubernetes.io/tls Secret in the Gateway's namespace. May reference a cert-manager-issued Secret or the existing openshell-server-tls Secret (its SANs must include the external hostname).
grpcRoute.gateway.name string "" Name of the Gateway resource. Defaults to the chart fullname.
grpcRoute.gateway.namespace string "" Namespace of the Gateway referenced by the GRPCRoute parentRef. Defaults to the release namespace.
grpcRoute.hostnames list [] Hostnames the GRPCRoute matches on. Leave empty to match all hosts.
image.pullPolicy string "IfNotPresent" Gateway image pull policy.
image.repository string "ghcr.io/nvidia/openshell/gateway" Gateway image repository.
image.tag string "" Gateway image tag. Defaults to the chart appVersion when empty.
imagePullSecrets list [] Image pull secrets attached to gateway and helper pods.
nameOverride string "openshell" Override the chart name used in generated resource names.
networkPolicy.enabled bool true Create a NetworkPolicy restricting SSH ingress on sandbox pods to the gateway.
nodeSelector object {} Node selector for the gateway pod.
pkiInitJob.enabled bool true Run a pre-install/pre-upgrade Job that creates gateway and client mTLS Secrets. When certManager.enabled=true, cert-manager owns TLS and this same hook runs in JWT-only mode even if pkiInitJob.enabled remains true.
pkiInitJob.serverDnsNames list [] Extra DNS SANs to append to the server certificate.
pkiInitJob.serverIpAddresses list [] Extra IP SANs to append to the server certificate.
podAnnotations object {} Extra annotations to add to the gateway pod.
podLabels object {} Extra labels to add to the gateway pod.
podLifecycle.terminationGracePeriodSeconds int 5 Grace period, in seconds, before Kubernetes terminates the gateway pod.
podSecurityContext.fsGroup int 1000 fsGroup assigned to the gateway pod.
probes.liveness.failureThreshold int 3 Liveness probe failure threshold before the container is restarted.
probes.liveness.initialDelaySeconds int 2 Liveness probe initial delay, in seconds.
probes.liveness.periodSeconds int 5 Liveness probe period, in seconds.
probes.liveness.timeoutSeconds int 1 Liveness probe timeout, in seconds.
probes.readiness.failureThreshold int 3 Readiness probe failure threshold before the pod is marked not ready.
probes.readiness.initialDelaySeconds int 1 Readiness probe initial delay, in seconds.
probes.readiness.periodSeconds int 2 Readiness probe period, in seconds.
probes.readiness.timeoutSeconds int 1 Readiness probe timeout, in seconds.
probes.startup.failureThreshold int 30 Startup probe failure threshold before the container is killed.
probes.startup.periodSeconds int 2 Startup probe period, in seconds.
probes.startup.timeoutSeconds int 1 Startup probe timeout, in seconds.
replicaCount int 1 Number of OpenShell gateway replicas. Values greater than 1 require server.externalDbSecret because the default SQLite backend is per pod.
resources object {} Gateway pod resource requests and limits.
sandboxServiceAccount.annotations object {} Annotations to add to the generated sandbox service account.
sandboxServiceAccount.create bool true Create a service account for sandbox pods.
sandboxServiceAccount.name string "" Existing service account name for sandbox pods when sandboxServiceAccount.create is false.
securityContext.allowPrivilegeEscalation bool false Whether the gateway container can gain additional privileges.
securityContext.capabilities.drop list ["ALL"] Linux capabilities dropped from the gateway container.
securityContext.runAsNonRoot bool true Require the gateway container to run as a non-root user.
securityContext.runAsUser int 1000 UID assigned to the gateway container.
server.appArmorProfile string "Unconfined" Kubernetes AppArmor profile requested for sandbox agent containers. Default Unconfined avoids runtime/default AppArmor blocking the supervisor's network namespace mount setup on AppArmor-enabled nodes. Set to "" to omit the field, "RuntimeDefault" to force the runtime default profile, or "Localhost/profile-name" for an operator-managed localhost profile.
server.auth.allowUnauthenticatedUsers bool false UNSAFE: accept unauthenticated CLI/user requests as a local developer principal. Intended only for trusted local Skaffold/k3d development or a fully trusted fronting proxy. Leave false for shared or production clusters.
server.credentialDrivers.kubernetesSecrets.allowReferenceNamespace bool false Deprecated compatibility field. Credential storage no longer supports user-authored namespace references.
server.credentialDrivers.kubernetesSecrets.enabled bool false Enable the in-tree Kubernetes Secret credential driver. WARNING: The RBAC Role grants read/write access to ALL Secrets in the configured namespace. Use a dedicated namespace to limit blast radius.
server.credentialDrivers.kubernetesSecrets.namespace string "" Namespace where OpenShell-managed provider Secret objects are stored. Empty = Helm release namespace. A dedicated namespace is RECOMMENDED to isolate OpenShell-managed Secrets from other workloads.
server.credentialDrivers.kubernetesSecrets.rbac.create bool true Create a Role/RoleBinding granting the gateway ServiceAccount read/write access to managed provider Secrets.
server.credentialDrivers.vault.address string "" Vault service base URL, for example http://vault.vault.svc.cluster.local:8200.
server.credentialDrivers.vault.authMethod string "kubernetes" Authentication method. Use "kubernetes" in-cluster or "token_file" for local/dev validation.
server.credentialDrivers.vault.enabled bool false Enable the in-tree Vault credential driver.
server.credentialDrivers.vault.kubernetesAuthMount string "kubernetes" Vault Kubernetes auth mount.
server.credentialDrivers.vault.kvVersion string "2" Default KV engine version. Use "1" or "2".
server.credentialDrivers.vault.mount string "secret" Default KV mount name.
server.credentialDrivers.vault.role string "" Vault Kubernetes auth role when authMethod is kubernetes.
server.credentialDrivers.vault.serviceAccountTokenPath string "/var/run/secrets/kubernetes.io/serviceaccount/token" ServiceAccount token path used for Kubernetes auth.
server.credentialDrivers.vault.timeoutSecs string "" HTTP request timeout in seconds. Empty = driver default.
server.credentialDrivers.vault.tokenPath string "" Mounted token file path when authMethod is token_file.
server.credentialStorage.existingSecret string "" Name of a pre-existing Secret containing the key-encryption key. When set, the chart does NOT generate a new Secret; it references this one instead. The Secret must contain a key named "key-encryption-key" with a base64-encoded 32-byte value. Required for GitOps workflows that render manifests with helm template (where lookup is unavailable).
server.dbUrl string "sqlite:/var/openshell/openshell.db" Gateway database URL (used for the default SQLite backend).
server.defaultRuntimeClassName string "" Default Kubernetes runtimeClassName for sandbox pods. Applied when a CreateSandbox request does not specify one. Empty (default) = omit the field, using the cluster's default RuntimeClass. Set to a RuntimeClass name (e.g. "kata-containers", "nvidia") to apply it to all sandboxes that don't explicitly override it.
server.disableTls bool false Disable TLS entirely - the server listens on plaintext HTTP. Set to true when a reverse proxy / tunnel terminates TLS at the edge.
server.enableLoopbackServiceHttp bool true Enable plaintext HTTP routing for loopback sandbox service URLs on TLS-enabled gateways.
server.enableUserNamespaces bool false Enable Kubernetes user namespace isolation (hostUsers: false) for sandbox pods. Requires Kubernetes 1.33+ with user namespace support available (beta through 1.35, GA in 1.36+), plus a supporting container runtime and Linux 5.12+. When enabled, container UID 0 maps to an unprivileged host UID and capabilities become namespaced.
server.externalDbSecret string "" Name of a pre-existing Opaque Secret containing a PostgreSQL connection URI (key: uri). When set, the gateway reads OPENSHELL_DB_URL from this Secret instead of using dbUrl. The Secret must contain a uri key, e.g. postgresql://user:pass@host:5432/dbname.
server.grpcEndpoint string "" gRPC endpoint sandboxes call back into the gateway. Leave empty to derive it from the chart fullname, release namespace, service port, and disableTls flag, for example https://openshell.openshell.svc.cluster.local:8080. Override only when sandboxes must reach the gateway via a different hostname (e.g. an external ingress or a host alias).
server.grpcRateLimit.requests int 0 Maximum gRPC requests allowed per window. Must be positive (alongside windowSeconds) to enable rate limiting; 0 (default) disables it.
server.grpcRateLimit.windowSeconds int 0 gRPC rate-limit window length in seconds. Must be positive (alongside requests) to enable rate limiting; 0 (default) disables it.
server.hostGatewayIP string "" Host gateway IP for sandbox pod hostAliases. When set, sandbox pods get hostAliases entries mapping host.docker.internal and host.openshell.internal to this IP, allowing them to reach services running on the Docker host. Auto-detected by the cluster entrypoint script.
server.logLevel string "info" Gateway log level.
server.oidc.adminRole string "" Role name for admin access. Leave empty (with userRole also empty) for authentication-only mode. Both must be set or both empty.
server.oidc.audience string "openshell-cli" Expected audience claim for the API resource server. This should match the server's --oidc-audience, NOT the CLI client ID.
server.oidc.caConfigMapName string "" Name of a ConfigMap containing a CA certificate bundle (key: ca.crt) for verifying the OIDC issuer's TLS certificate. Required when the issuer uses a non-public CA (e.g. OpenShift ingress, private PKI).
server.oidc.issuer string "" OIDC issuer URL (e.g. https://keycloak.example.com/realms/openshell).
server.oidc.jwksTtl int 3600 JWKS key cache TTL in seconds.
server.oidc.rolesClaim string "" Dot-separated path to the roles array in the JWT claims. Keycloak: "realm_access.roles", Entra ID: "roles", Okta: "groups".
server.oidc.scopesClaim string "" Dot-separated path to the scopes array in the JWT claims.
server.oidc.userRole string "" Role name for standard user access.
server.policyValidationFailureMode string "fail_closed" Posture when a candidate sandbox policy fails validation. fail_closed deactivates the previous policy; retain_last_valid keeps it active.
server.providerTokenGrants.spiffe.enabled bool false Mount the SPIFFE Workload API socket into sandbox pods for dynamic provider token grants.
server.providerTokenGrants.spiffe.workloadApiSocketPath string "/spiffe-workload-api/spire-agent.sock" Path to the SPIFFE Workload API socket mounted into sandbox pods.
server.sandboxImage string "ghcr.io/nvidia/openshell-community/sandboxes/base:latest" Default sandbox image used when requests do not specify one.
server.sandboxImagePullPolicy string "" Kubernetes imagePullPolicy for sandbox pods. Empty = Kubernetes default (Always for :latest, IfNotPresent otherwise). Set to "Always" for dev clusters so new images are picked up without manual eviction.
server.sandboxImagePullSecrets list [] Image pull secrets attached to sandbox pods. Referenced Secrets must exist in the sandbox namespace.
server.sandboxJwt.gatewayId string "" Stable gateway identity embedded in iss/aud of every minted token. Defaults to the release name so HA replicas share identity.
server.sandboxJwt.k8sSaTokenTtlSecs int 3600 Lifetime (seconds) of the projected ServiceAccount token kubelet writes into each sandbox pod for the IssueSandboxToken bootstrap exchange. Kubelet enforces a minimum of 600s; the driver clamps values outside [600, 86400]. Default 3600 — generous, since the supervisor consumes the token within seconds of pod start.
server.sandboxJwt.secretDefaultMode string "" File mode for the mounted JWT signing key Secret. Default 0400 (owner-read only). Override to 0440 or 0444 if the container UID does not match the volume file owner.
server.sandboxJwt.signingSecretName string "" Name of the Opaque Secret holding the signing key material. Empty falls back to the chart fullname with "-jwt-keys" appended.
server.sandboxJwt.ttlSecs int 3600 Token TTL in seconds. Defaults to 3600 (1h).
server.sandboxNamespace string "" Namespace where sandbox pods are created. Defaults to the Helm release namespace (.Release.Namespace) when left empty.
server.tls.certSecretName string "openshell-server-tls" K8s secret (type kubernetes.io/tls) with tls.crt and tls.key for the server.
server.tls.clientCaSecretName string "openshell-server-client-ca" K8s secret with ca.crt for client certificate verification (mTLS). Set to "" to disable mTLS and run HTTPS-only (use OIDC for auth instead).
server.tls.clientTlsSecretName string "openshell-client-tls" K8s secret mounted into sandbox pods for mTLS to the server.
server.workspaceDefaultStorageSize string "" Default storage size for the workspace PVC in sandbox pods. Uses Kubernetes quantity syntax (e.g. "2Gi", "10Gi", "500Mi"). Empty = built-in default (2Gi).
server.workspaceStorageClass string "" Kubernetes StorageClass for the workspace PVC in sandbox pods. Empty (default) = omit storageClassName, using the cluster's default StorageClass. Set this on clusters with no default StorageClass, otherwise the workspace PVC stays Pending and the sandbox never starts.
service.healthPort int 8081 Gateway health service port.
service.metricsPort int 9090 Gateway metrics service port.
service.port int 8080 Gateway gRPC/HTTP service port.
service.type string "ClusterIP" Kubernetes Service type for the gateway.
serviceAccount.annotations object {} Annotations to add to the generated service account.
serviceAccount.create bool true Create a service account for the gateway.
serviceAccount.name string "" Existing service account name to use when serviceAccount.create is false.
supervisor.image.pullPolicy string "" Supervisor image pull policy. Defaults to the gateway image pull policy when empty.
supervisor.image.repository string "ghcr.io/nvidia/openshell/supervisor" Supervisor image repository. Changing it uses the effective gateway image tag unless tag is also set.
supervisor.image.tag string "" Supervisor image tag override. Empty uses the version pinned into the gateway unless repository is changed.
supervisor.sidecar.processBinaryAwareNetworkPolicy bool true Keep process/binary-aware network policy enabled in sidecar topology. When false, the network sidecar runs as proxyUid, drops the extra /proc inspection capabilities, and enforces endpoint/L7 policy without matching policy.binaries.
supervisor.sidecar.proxyUid int 1337 UID for relaxed long-running network sidecars in sidecar topology. Strict process/binary-aware sidecars run as UID 0 so Kubernetes grants the required /proc inspection capabilities into the effective set. The network init container installs nftables rules that exempt the effective sidecar UID.
supervisor.sideloadMethod string "" How the supervisor binary is delivered into sandbox pods. Empty (default) = auto-detect from cluster version: K8s >= v1.35 -> "image-volume" (ImageVolume enabled by default; GA in v1.36) K8s < v1.35 -> "init-container" (copies via init container + emptyDir) On K8s v1.33-v1.34 with the ImageVolume feature gate manually enabled, set this to "image-volume" explicitly.
supervisor.topology string "combined" Supervisor pod topology for Kubernetes sandboxes. "combined" runs the current single supervisor container in the agent pod. "sidecar" runs network enforcement in a dedicated sidecar and the process supervisor as a low-capability wrapper in the agent container.
tolerations list [] Tolerations for the gateway pod.
workload.allowMultiReplicaStatefulSet bool false Allow replicaCount > 1 while rendering a StatefulSet. Prefer workload.kind=deployment for external database-backed multi-replica gateways; this override exists for operators who explicitly require StatefulSet identity or storage semantics.
workload.kind string "statefulset" Gateway workload controller kind. Use statefulset for the default SQLite database, or deployment when server.externalDbSecret points at an external database.

Autogenerated from chart metadata using helm-docs v1.14.2