Files
OpenShell/deploy/helm/openshell
Jesse Jaggars c4b500a7de feat(helm): cert-manager external issuer + OpenShift passthrough Route (#2468)
* fix(core): trust public root CAs alongside the sandbox mTLS CA

The supervisor gRPC client only trusted the CA configured via
OPENSHELL_TLS_CA, since tonic ClientTlsConfig starts with an empty root
store unless with_native_roots()/with_webpki_roots() is also enabled.
Deployments where the gateway server certificate is issued by a public CA
(e.g. cert-manager against an ACME issuer) caused every supervisor
connection to fail the TLS handshake with "UnknownCA", since the sandbox
mTLS CA and the server cert issuer were no longer the same.

Enable both native and webpki roots in addition to the configured CA.
tonic root store is a union of all configured sources, so this does not
weaken verification for existing self-signed deployments. webpki-roots
(compiled in) is enabled alongside native-roots since the supervisor
binary may run in minimal sandbox images without a populated system CA
bundle.

Signed-off-by: Jesse Jaggars <jjaggars@redhat.com>

* feat(helm): support external cert-manager issuers and OpenShift Route passthrough

Add certManager.serverIssuerRef/clientIssuerRef so the gateway and mTLS
client certificates can be issued by a real Issuer/ClusterIssuer (e.g.
ACME) instead of only the chart built-in self-signed CA.

Add openshiftRoute template for exposing the gateway via a TLS
passthrough Route so the gateway keeps terminating its own TLS/mTLS.

The server Certificate excludes internal-only SANs (cluster-local,
localhost, loopback) when an external issuer is configured, since ACME
issuers reject those per CA/Browser Forum baseline requirements. A
template-time fail guard catches the misconfiguration at helm install
time rather than asynchronously at cert-manager issuance time.

Includes Helm unittest coverage for both issuerRef overrides and Route
rendering, plus a CI values overlay for lint coverage.

Signed-off-by: Jesse Jaggars <jjaggars@redhat.com>

* docs: document cert-manager external issuer and OpenShift Route

Update managing-certificates.mdx with the serverIssuerRef workflow and
install-time validation behavior. Add a production section to the
OpenShift guide covering passthrough Route with a real certificate.
Regenerate Helm README for new certManager and openshiftRoute values.
Sync debug-openshell-cluster skill with new troubleshooting steps for
ACME issuance failures and supervisor UnknownCA from mismatched CAs.

Signed-off-by: Jesse Jaggars <jjaggars@redhat.com>

* fix(helm,core): address PR review feedback on cert-manager external issuer

Addresses all five blocking review items from #2468:

1. Remove .with_native_roots() from supervisor gRPC client -- the
   supervisor runs inside the user-selected sandbox image, so the
   image CA bundle is not operator-controlled. Keep .with_webpki_roots()
   (compiled-in, not user-controlled) alongside the configured CA.

2. Fail at render time when serverIssuerRef.name is set but
   clientCaFromServerTlsSecret is still true. Add negative Helm test.

3. Remove clientIssuerRef -- changing only clientIssuerRef breaks both
   directions because trust bundles are not modeled separately. Change
   serverIssuerRef.kind default from ClusterIssuer to Issuer.

4. Add server.oidc.issuer and server.oidc.audience to the documented
   OpenShift production Helm command. Add Access Control prerequisite.

5. Fail at render time when openshiftRoute.enabled and disableTls are
   both true. Add negative Helm test.

Signed-off-by: Jesse Jaggars <jjaggars@redhat.com>

* fix(drivers): strip GATEWAY_TLS_SERVER_NAME from Docker and Podman env

Signed-off-by: Jesse Jaggars <jjaggars@redhat.com>

* fix(helm,drivers): guard default clientCaSecretName and add env-strip tests

Signed-off-by: Jesse Jaggars <jjaggars@redhat.com>

* feat(tls): SNI-based dual certificate for internal and external server TLS

Split the gateway server certificate into two: an internal cert issued by
the chart's own CA (for supervisor connections via cluster-local SANs) and
an external cert issued by an operator-configured Issuer such as ACME/Let's
Encrypt (for CLI and Route access via public SANs).

The gateway uses SNI-based certificate selection: connections whose SNI
hostname matches external_server_names receive the external cert; all
others (including those with no SNI) receive the internal cert.

Security improvement: remove .with_webpki_roots() from the supervisor
gRPC client so supervisors trust only the chart CA, closing a MITM vector
via publicly-trusted certificates in user-supplied container images.

Key changes:
- Add DualCertResolver with SNI-based cert selection and full test coverage
- Add external_cert_path, external_key_path, external_server_names to TlsConfig
- Validate partial external cert config (error on cert-without-key or vice versa)
- Validate empty external_server_names when external cert is configured
- Split cert-manager templates into internal + external Certificate resources
- Add Helm guards for misconfigured external issuer (empty serverDnsNames,
  internal-only SANs with external issuer, conflicting clientCaFromServerTlsSecret)
- Update gateway-config.mdx, managing-certificates.mdx, openshift.mdx docs
- Update debug-openshell-cluster skill for dual-cert troubleshooting

Signed-off-by: Pi Agent <agent@openshell.local>

* fix(drivers): strip GATEWAY_TLS_SERVER_NAME in VM driver and correct comments

Add the same GATEWAY_TLS_SERVER_NAME environment stripping to the VM
compute driver that Docker, Podman, and Kubernetes drivers already
perform. Without this, a sandbox user on the VM driver could override
the TLS server name the supervisor verifies.

Fix stale comments in Docker and Podman drivers that referenced
'with WebPKI roots trusted' — WebPKI roots are explicitly not trusted
after the tls-webpki-roots removal.

Use tls-ring instead of bare channel for tonic in openshell-core so the
TLS API (ClientTlsConfig, Endpoint::tls_config) is available without
pulling in any root certificate store.

Signed-off-by: Pi Agent <agent@openshell.local>

* fix(tls,helm): wildcard SNI matching and Route host validation

Add RFC 6125 single-level wildcard matching to DualCertResolver so
external_server_names entries like *.example.com correctly match SNI
hostnames like gw.example.com. Previously only exact matches worked,
silently falling back to the internal cert for wildcard configurations.

Add a Helm fail guard in route.yaml that rejects openshiftRoute.host
values not listed in certManager.serverDnsNames when an external issuer
is configured — catches cert/route hostname mismatches at install time
instead of at TLS connect time.

Quote the host field in route.yaml for robustness.

Signed-off-by: Pi Agent <agent@openshell.local>

* fix(helm): address blocking review items — client-CA guard, wildcard Route, serverIssuerRef gate

1. Remove the obsolete guard rejecting serverIssuerRef + clientCaFromServerTlsSecret=true.
   The internal server certificate is always signed by the chart CA (the same
   CA that signs the client cert), so clientCaFromServerTlsSecret=true is
   correct — its filtered ca.crt is exactly the right trust anchor.  The old
   workaround (mounting openshell-ca-tls directly) unnecessarily exposed the
   CA private key to the gateway container.  Remove the client-CA overrides
   from docs, CI overlay, and production examples.

2. Route host validation now supports wildcard certificates per RFC 6125:
   single-level wildcards like *.example.com match gateway.example.com but
   not deep.sub.example.com.  Require an explicit openshiftRoute.host when
   an external issuer is configured — without one, OpenShift generates a
   hostname absent from serverDnsNames.

3. Reject serverIssuerRef.name when certManager.enabled is false — the
   external certificate, its Secret mount, and the gateway TLS config all
   require cert-manager to be enabled.

Validated on ROSA (dev.dyee.p3) with branch-built images:
- Fresh install with letsencrypt-prod ClusterIssuer
- SNI dual-cert: external hostname served Let's Encrypt cert
- Supervisor mTLS via internal cert path: ConnectSupervisor accepted
- Client CA volume: filtered ca.crt from internal server secret (no key)
- CLI connected via Route + OIDC

Helm tests: 81 pass across 7 suites.

Signed-off-by: Jesse Jaggars <jjaggars@redhat.com>

---------

Signed-off-by: Jesse Jaggars <jjaggars@redhat.com>
Signed-off-by: Pi Agent <agent@openshell.local>
2026-08-14 00:12:11 +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 internal server TLS secret's ca.crt. The internal server certificate is always signed by the chart CA — the same CA that signs the client (mTLS) certificate — so the default (true) is correct for all configurations, including when serverIssuerRef is set. Only set to false if you mount the client CA from a separate secret via server.tls.clientCaSecretName.
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.
certManager.serverIssuerRef object {"group":"","kind":"","name":""} Override the issuerRef for the external server Certificate (e.g. a real ACME ClusterIssuer for a publicly-trusted cert on an external hostname). When set, the chart creates a second server certificate from this issuer with only the hostnames in serverDnsNames; the internal server certificate is always signed by the chart's own CA. Leave name empty to use the chart CA for all server certificates (default). Requires certManager.enabled=true.
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.
openshiftRoute.annotations object {} Extra annotations on the Route (e.g. haproxy.router.openshift.io/*).
openshiftRoute.enabled bool false Create an OpenShift Route with TLS passthrough.
openshiftRoute.host string "" Hostname for the Route. Must match a SAN on the gateway's server cert.
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.telemetryEnabled bool true Enable anonymous OpenShell telemetry from the gateway and the sandbox supervisors it launches.
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