Files
OpenShell/deploy/helm/openshell
Brandon Squizzato cc780d4e17 feat(helm): add BackendTLSPolicy support (#2728)
* feat(helm): add optional BackendTLSPolicy for e2e TLS

Add grpcRoute.backendTLSPolicy values to optionally create a
BackendTLSPolicy resource that enables end-to-end TLS between the
Gateway proxy and the OpenShell gateway pod. The Gateway proxy
terminates client-facing TLS and re-encrypts when connecting to the
backend, validating the pod's certificate against a user-supplied CA
ConfigMap.

This removes the requirement to set server.disableTls=true when using
HTTPS at the Gateway listener. Supported on OpenShift 4.22+ and other
platforms with BackendTLSPolicy support in the Gateway API
implementation.

Update OpenShift and ingress documentation with e2e TLS instructions.

Signed-off-by: Brandon Squizzato <bsquizza@redhat.com>

* feat(helm,server): auto-create backend CA ConfigMap in certgen hook

Extend the generate-certs command with --backend-ca-configmap-name and
--backend-ca-source-secret flags. When BackendTLSPolicy is enabled, the
certgen pre-install hook creates the CA ConfigMap automatically:

- pkiInitJob mode (default): uses the CA from the generated PKI bundle.
  Fully automatic on first install.
- cert-manager mode: reads ca.crt from the server TLS Secret. On first
  install the Secret does not exist yet (cert-manager reconciles after
  templates are applied), so the ConfigMap is created on the first helm
  upgrade. Logs a warning on the initial skip.

The caCertificateConfigMapName value now defaults to <fullname>-backend-ca
when empty, so users only need to set backendTLSPolicy.enabled=true.

Update certgen RBAC to include configmaps get/create when the feature is
enabled. Add CLI arg parsing tests for the new flags.

Signed-off-by: Brandon Squizzato <bsquizza@redhat.com>

* refactor(helm): add server.tls.enableMtls flag for mTLS control

Replace automatic mTLS disabling based on BackendTLSPolicy with an
explicit server.tls.enableMtls flag that defaults to true. The user is
now responsible for setting this to false when using BackendTLSPolicy,
as ingress proxies cannot present client certificates to backends.

Updated:
- values.yaml: Added server.tls.enableMtls (default true)
- gateway-config.yaml: Check enableMtls instead of backendTLSPolicy
- _gateway-workload.tpl: Check enableMtls for client CA mount
- Tests: Updated to use enableMtls flag
- Docs: Added enableMtls=false to BackendTLSPolicy examples
- README: Document new flag and BackendTLSPolicy requirement

Signed-off-by: Brandon Squizzato <bsquizza@redhat.com>

* docs(helm): clarify cert-manager backend CA ConfigMap workflow

Update documentation to explain the two-step install process required
when using cert-manager with BackendTLSPolicy:

1. helm install - cert-manager issues the server certificate, but the
   certgen hook can't create the backend CA ConfigMap yet (cert-manager
   reconciles after templates are applied)
2. helm upgrade - certgen hook reads the CA from the cert-manager-issued
   certificate and creates the ConfigMap

Previously, the docs said "created on first upgrade" without explaining
why or that the feature won't work until then. The updated docs now:
- Explain the timing issue (cert-manager reconciles after chart install)
- Provide clear steps for the cert-manager workflow
- Note that pkiInitJob (default) creates it immediately on install
- Clarify that users must wait for the Certificate to be Ready before
  running the second upgrade

Signed-off-by: Brandon Squizzato <bsquizza@redhat.com>

* fix(docs): remove incorrect external hostname requirement for BackendTLSPolicy

BackendTLSPolicy validates the backend certificate against the service FQDN
(e.g., openshell.openshell.svc.cluster.local), not the external hostname.
The external hostname only needs to be on the Gateway listener certificate
for client-facing TLS.

The default certManager.serverDnsNames already includes all required service
FQDN variants, so no configuration is needed for BackendTLSPolicy to work.

Fixed incorrect documentation that claimed:
- "The server certificate SAN list must include the external hostname"
- Users need to "configure certManager.serverDnsNames with the external hostname"

Removed the unnecessary pkiInitJob.serverDnsNames override from the example
and clarified that:
- Gateway listener certificate needs the external hostname (for clients)
- Backend certificate needs the service FQDN (for Gateway proxy)
- The service FQDN is already in the defaults

Signed-off-by: Brandon Squizzato <bsquizza@redhat.com>

* docs: clarify ACME with LetsEncrypt reference

Change all references from "ACME issuer" to "LetsEncrypt/ACME issuer"
to help users understand that LetsEncrypt is the most common ACME
provider and what ACME means in practice.

Updated:
- docs/kubernetes/managing-certificates.mdx
- docs/kubernetes/openshift.mdx
- deploy/helm/openshell/values.yaml
- deploy/helm/openshell/README.md
- deploy/helm/openshell/ci/values-openshift-route-cert-manager.yaml

Signed-off-by: Brandon Squizzato <bsquizza@redhat.com>

* docs(openshift): restructure end-to-end TLS options and clarify Gateway hostname

Reorganize the OpenShift production deployment documentation:

1. Changed main section from "Production Deployments" to "Options for
   end-to-end TLS" for better clarity

2. Renamed subsections for consistency and clarity:
   - "End-to-end TLS using Gateway API and BackendTLSPolicy (OpenShift 4.22+)"
   - "End-to-end TLS using pass-through Route (all OpenShift versions)"

3. Clarified that the Gateway hostname is typically a wildcard:
   "typically a wildcard like *.openshell-ingress-gw.example.com"

4. Removed the recommendation to copy the cluster's wildcard certificate
   from openshift-ingress namespace, as this is not a recommended
   security best practice

These changes make it clearer that users have two end-to-end TLS options
and help them understand the typical naming pattern for Gateway hostnames.

Signed-off-by: Brandon Squizzato <bsquizza@redhat.com>

* feat(helm): eliminate two-stage install for BackendTLSPolicy with cert-manager

When using BackendTLSPolicy with cert-manager, the certgen hook now polls
for up to 90 seconds waiting for cert-manager to issue the TLS certificate
before creating the backend CA ConfigMap. This eliminates the need for a
second `helm upgrade` in most cases.

The hook polls every 2 seconds with progress logging every 10 seconds.
If cert-manager takes longer than 90 seconds, the hook times out gracefully
and logs a warning, preserving the fallback to manual ConfigMap creation
or a second upgrade.

The Job's activeDeadlineSeconds is 120s, so the 90s timeout leaves 30s
margin for ConfigMap creation and hook completion.

Signed-off-by: Brandon Squizzato <bsquizzato@nvidia.com>

* feat(helm): add configurable timeout for certgen hook

Add `pkiInitJob.timeoutSeconds` Helm value (default 120) to control how long
the certgen hook Job can run. When using cert-manager with BackendTLSPolicy,
the hook polls for (timeoutSeconds - 30) seconds to leave margin for ConfigMap
creation and cleanup.

This allows users to increase the timeout for environments where cert-manager
takes longer than 90 seconds to issue certificates, without requiring code
changes.

Example usage:
```yaml
pkiInitJob:
  timeoutSeconds: 180  # Hook polls for 150 seconds
```

Signed-off-by: Brandon Squizzato <bsquizzato@nvidia.com>

* docs(helm): document configurable certgen timeout

Update documentation to mention the pkiInitJob.timeoutSeconds value and
how it affects the cert-manager polling behavior when using BackendTLSPolicy.

Signed-off-by: Brandon Squizzato <bsquizzato@nvidia.com>

* feat(helm): add configurable failure behavior for certgen timeout

Add `pkiInitJob.failOnTimeout` Helm value (default false) to control whether
the certgen hook fails or succeeds when cert-manager does not issue a
certificate within the polling timeout.

When false (default), the hook succeeds with a warning and users can run
`helm upgrade` after cert-manager issues the certificate to create the
backend CA ConfigMap. This provides backwards-compatible behavior.

When true, the hook fails immediately if the timeout is reached, providing
clear feedback that BackendTLSPolicy is non-functional. This is useful for
strict validation requirements where incomplete installs should fail fast.

Example usage:
```yaml
pkiInitJob:
  timeoutSeconds: 180
  failOnTimeout: true  # Fail install if cert-manager takes >150s
```

Signed-off-by: Brandon Squizzato <bsquizzato@nvidia.com>

* feat(helm): change failOnTimeout default to true and add troubleshooting docs

Change `pkiInitJob.failOnTimeout` default from false to true to provide
immediate feedback when cert-manager does not issue certificates within
the polling timeout. This prevents silent failures where BackendTLSPolicy
is non-functional but the install appears to succeed.

Add comprehensive troubleshooting section to docs/kubernetes/ingress.mdx
documenting the specific error "TLS error: Secret is not supplied by SDS"
that occurs when the backend CA ConfigMap is missing, with step-by-step
resolution instructions.

Updated comments in values.yaml to clearly document the default behavior
and explain when administrators might see connectivity errors if they
override the default to failOnTimeout=false.

BREAKING CHANGE: pkiInitJob.failOnTimeout now defaults to true. Helm
installs will fail if cert-manager takes longer than (timeoutSeconds - 30)
seconds to issue certificates. To restore the old behavior of allowing
installs to succeed with a warning, set `pkiInitJob.failOnTimeout=false`.

Signed-off-by: Brandon Squizzato <bsquizzato@nvidia.com>

* fix(helm): make cert-manager resources pre-install hooks to fix ordering

Make Certificate and Issuer resources run as pre-install/pre-upgrade hooks
with weight -30, before the certgen hook (weight -20). This fixes the
chicken-and-egg problem where the certgen hook was waiting for Secrets
created by Certificates that hadn't been created yet.

**Hook ordering:**
1. Certificate and Issuer resources created (weight -30)
2. cert-manager issues certificates and creates Secrets
3. certgen hook runs (weight -20), finds Secrets, creates ConfigMap
4. Main resources (StatefulSet, Service, etc.) created

Previously, the certgen pre-install hook would run before any resources
were created, poll for a non-existent Secret, timeout, and fail. The
Certificate resources would never get created because Helm waits for
all pre-install hooks to succeed before creating main resources.

This fix allows single-stage installs to work reliably as long as
cert-manager can issue certificates within the polling timeout.

Signed-off-by: Brandon Squizzato <bsquizzato@nvidia.com>

* feat(helm): add validation to prevent enableMtls with BackendTLSPolicy

Add Helm chart validation that fails the install if both
server.tls.enableMtls=true and grpcRoute.backendTLSPolicy.enabled=true
are set, since this is an invalid configuration.

BackendTLSPolicy requires mTLS to be disabled because the Gateway proxy
cannot present client certificates to the backend. This validation provides
immediate, clear feedback at install time rather than allowing the
misconfiguration to be discovered through runtime errors.

Example error message:
```
Error: grpcRoute.backendTLSPolicy requires mTLS to be disabled because
the Gateway proxy cannot present client certificates to the backend;
set server.tls.enableMtls=false
```

Also updated documentation to mention this validation check.

Signed-off-by: Brandon Squizzato <bsquizzato@nvidia.com>

* docs(helm): clarify pkiInitJob.timeoutSeconds polling behavior

Improve documentation to clearly explain that pkiInitJob.timeoutSeconds
controls the Job deadline, but the actual polling timeout is
(timeoutSeconds - 30) to reserve 30 seconds for ConfigMap creation
and cleanup.

Added concrete example: "timeoutSeconds=180 allows 150 seconds of polling"
to make the relationship explicit and avoid confusion where users might
expect the hook to poll for the full timeout value.

Updated both values.yaml inline comments and ingress.mdx documentation
for consistency.

Signed-off-by: Brandon Squizzato <bsquizzato@nvidia.com>

* docs(openshift): remove outdated two-stage install instructions

Update OpenShift documentation to reflect that single-stage installs now
work with cert-manager and BackendTLSPolicy. The Certificate resources
run as pre-install hooks (weight -30) before certgen (weight -20),
allowing the hook to poll for and find the issued certificates.

Removed the outdated two-step process:
1. helm install (cert-manager issues cert, hook logs warning)
2. helm upgrade (hook creates ConfigMap)

Replaced with current single-stage behavior:
- Certificate resources created as pre-install hooks
- certgen hook polls for up to 90 seconds (configurable)
- Single helm install succeeds in most cases
- Fails fast by default if timeout reached

This brings openshift.mdx in line with the already-updated ingress.mdx
documentation.

Signed-off-by: Brandon Squizzato <bsquizzato@nvidia.com>

* fix(helm): make pkiInitJob.timeoutSeconds the actual polling duration

The timeout value now represents the actual polling time that users
experience when waiting for cert-manager to issue certificates.
The Job activeDeadlineSeconds is set to (timeoutSeconds + 30) to
allow buffer time for ConfigMap creation and cleanup.

Previously, the hook polled for (timeoutSeconds - 30) seconds, which
was confusing when users set timeoutSeconds=180 and only got 150
seconds of actual polling.

Updated documentation in values.yaml, ingress.mdx, and openshift.mdx
to reflect the clearer behavior.

Signed-off-by: Brandon Squizzato <bsquizza@nvidia.com>

* docs(helm): update values.yaml and README with correct polling duration

Updated the caCertificateConfigMapName description to reflect that the
hook polls for exactly pkiInitJob.timeoutSeconds seconds, not
(timeoutSeconds - 30) seconds.

Regenerated README.md with helm-docs.

Signed-off-by: Brandon Squizzato <bsquizza@nvidia.com>

* docs(kubernetes): add OIDC configuration to helm install and CLI examples

Updated all helm install and openshell gateway add examples in ingress.mdx
and openshift.mdx to include OIDC issuer and audience configuration.

Examples now use concrete placeholder values:
- OIDC issuer: https://keycloak.example.com/realms/openshell
- OIDC audience: openshell-cli
- Hostname: gateway.example.com
- ClusterIssuer: letsencrypt-prod

This makes it clearer how to configure OIDC authentication, which is
required when using BackendTLSPolicy or HTTPS termination since the
Gateway proxy cannot present client certificates to the backend.

Signed-off-by: Brandon Squizzato <bsquizza@nvidia.com>

* docs(kubernetes): explicitly list OIDC client ID in gateway add examples

Added --oidc-client-id openshell-cli to all openshell gateway add
commands in ingress.mdx and openshift.mdx, making the default client
ID explicit in the examples even though it's the CLI default.

This improves clarity and helps users understand the complete OIDC
configuration needed for gateway registration.

Signed-off-by: Brandon Squizzato <bsquizza@nvidia.com>

* fix(helm): address PR review feedback for BackendTLSPolicy

- Read backend CA from the authoritative server Secret instead of the
  in-memory PKI bundle so enabling BackendTLSPolicy on an existing
  release uses the CA that actually signed the server certificate.
- Reconcile the backend CA ConfigMap on every hook run (compare and
  update) instead of skipping when it already exists, so CA rotations
  propagate automatically.
- Remove hook annotations from cert-manager Issuer/Certificate resources
  so they remain regular release objects managed by Helm lifecycle. Split
  the cert-manager backend CA ConfigMap creation into a separate
  post-install/post-upgrade hook Job that polls after cert-manager
  Certificate resources are applied.
- Update architecture/gateway.md, docs/reference/gateway-config.mdx,
  debug-openshell-cluster skill, and helm-dev-environment skill with
  BackendTLSPolicy, backend CA ConfigMap, enableMtls, and timeout
  documentation.

Signed-off-by: Brandon Squizzato <bsquizza@nvidia.com>
Signed-off-by: Brandon Squizzato <bsquizza@redhat.com>

* fix(docs): resolve markdown lint errors in helm README and kubernetes docs

Escape inline HTML angle brackets in README.md template placeholders,
remove trailing spaces, and add blank lines around fenced code blocks
in numbered lists.

Signed-off-by: Brandon Squizzato <bsquizza@redhat.com>

* Update docs

* fix(helm): escape inline HTML in values.yaml descriptions and sync mise lockfile

Wrap `<fullname>` and `<namespace>` template placeholders in backticks
so markdownlint does not flag them as inline HTML (MD033). Regenerate
mise.lock to match current mise.toml after rebase onto main.

Signed-off-by: Brandon Squizzato <bsquizza@redhat.com>

* Run 'mise lock'

* fix: align mise.lock with CI mise version output

The lockfile was regenerated locally with mise 2026.8.10 which resolves
uv Linux artifacts to gnu variants and adds provenance_verified fields,
but CI uses v2026.4.25 which produces musl variants without those fields.

Signed-off-by: Brandon Squizzato <bsquizza@redhat.com>

* fix(docs): correct cert-manager hook ordering and clientCaSecretName comment

Update ingress.mdx and openshift.mdx to describe Certificate resources
as regular release objects with a post-install/post-upgrade Job, matching
the current implementation and architecture/gateway.md.

Fix values.yaml clientCaSecretName comment to state that "" disables
client certificate verification, matching the helper and access-control
docs.

Signed-off-by: Brandon Squizzato <bsquizza@redhat.com>

---------

Signed-off-by: Brandon Squizzato <bsquizza@redhat.com>
Signed-off-by: Brandon Squizzato <bsquizzato@nvidia.com>
Signed-off-by: Brandon Squizzato <bsquizza@nvidia.com>
2026-09-14 17:39:15 +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.

By default, this chart also creates the namespace-scoped resources needed by sandboxes. For a shared-gateway deployment, install it with workspaceResources.enabled=false, then install the deploy/helm/openshell-workspace chart in every pre-provisioned workspace namespace. The gateway and workspace releases can then be upgraded and removed independently. Use Kubernetes operator workspace mode when one gateway serves multiple pre-provisioned workspace namespaces.

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

The chart does not install this cluster-scoped dependency. By default, it fails before creating gateway resources when the cluster serves neither supported Sandbox API (agents.x-k8s.io/v1beta1 or agents.x-k8s.io/v1alpha1). Disable the check with agentSandbox.preflight.enabled=false for offline helm template rendering, where Helm cannot discover cluster APIs.

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

On OpenShift 4.22+, end-to-end TLS is supported via BackendTLSPolicy. See the OpenShift install guide for details.

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 the gateway and sandbox supervisors use SPIFFE JWT-SVIDs for dynamic provider token grants. The chart keeps supervisor-to-gateway authentication on gateway-minted sandbox JWTs, mounts the SPIFFE CSI socket into the gateway pod, exports OPENSHELL_GATEWAY_SPIFFE_WORKLOAD_API_SOCKET, and passes the socket path to the Kubernetes driver so sandbox pods can mount the same socket.

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

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

Values

Key Type Default Description
affinity object {} Affinity rules for the gateway pod.
agentSandbox.preflight.enabled bool true Check the live cluster for a supported Agent Sandbox API before rendering gateway resources. Disable only for offline rendering and linting.
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 LetsEncrypt/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.backendTLSPolicy.caCertificateConfigMapName string "" Name of the ConfigMap containing the CA certificate (key: ca.crt) used to validate the gateway pod's TLS certificate. Defaults to <fullname>-backend-ca when empty. The certgen hook auto-creates this: with pkiInitJob (default), immediately on install/upgrade; with cert-manager, the hook polls for pkiInitJob.timeoutSeconds seconds waiting for cert-manager to issue the server certificate, then creates the ConfigMap. A single install usually succeeds; if cert-manager takes longer, increase pkiInitJob.timeoutSeconds. By default (pkiInitJob.failOnTimeout=true), the install fails if the timeout is reached; set failOnTimeout=false to allow the install to succeed and run helm upgrade after the certificate is issued.
grpcRoute.backendTLSPolicy.enabled bool false Create a BackendTLSPolicy resource for end-to-end TLS between the Gateway proxy and the OpenShell gateway pod. The traffic flow is: client → HTTPS → Gateway (terminate) → TLS (re-encrypt) → gateway pod. Requires server.disableTls=false and server.tls.enableMtls=false. The certgen hook auto-creates the backend CA ConfigMap.
grpcRoute.backendTLSPolicy.hostname string "" Hostname the Gateway proxy validates against the backend's TLS certificate SAN. Defaults to the service FQDN (<fullname>.<namespace>.svc.cluster.local) when empty, which matches the SAN included by both cert-manager and the pkiInitJob.
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 Restrict SSH ingress on sandbox pods to the gateway. In managed mode, the driver applies the equivalent policy to each workspace namespace.
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.failOnTimeout bool true Fail the helm install/upgrade if cert-manager does not issue the certificate within the polling timeout. When true (default), the install fails immediately if the timeout is reached, providing clear feedback that BackendTLSPolicy is non-functional. When false, the hook succeeds with a warning and you can run helm upgrade after cert-manager issues the certificate to create the backend CA ConfigMap. If you set this to false and see "TLS error: Secret is not supplied by SDS" when connecting to the gateway, check if the TLS secret exists and run helm upgrade to create the ConfigMap.
pkiInitJob.serverDnsNames list [] Extra DNS SANs to append to the server certificate.
pkiInitJob.serverIpAddresses list [] Extra IP SANs to append to the server certificate.
pkiInitJob.timeoutSeconds int 120 Maximum time in seconds for the certgen hook to poll for cert-manager certificates. When using cert-manager with BackendTLSPolicy, the hook polls for this many seconds waiting for the certificate to be issued, then creates the backend CA ConfigMap. The Job deadline is set to (timeoutSeconds + 30) to allow time for ConfigMap creation and cleanup. Increase this if cert-manager takes longer than 120 seconds to issue certificates.
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.drivers.kubernetes.operatorNamespaceFile string "" Path to a JSON file containing an array of namespace names allowed in operator mode. Hot-reloaded on change.
server.drivers.kubernetes.operatorNamespaceLabel string "" K8s label selector for namespace discovery in operator mode. The driver watches namespaces matching this label.
server.drivers.kubernetes.workspaceMode string "shared" How workspaces map to Kubernetes namespaces. "shared" (default): all sandboxes in a single namespace. "managed": auto-creates per-workspace namespaces. "operator": uses pre-provisioned namespaces.
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.name string "" Operator-facing gateway name. Defaults to the chart fullname so all replicas in one installation share an identity. Set explicitly when one telemetry collector receives spans from multiple namespaces or clusters.
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. Must be greater than zero.
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.otlp.endpoint string "" OTLP/gRPC collector endpoint, conventionally using port 4317.
server.otlp.serviceName string "" Gateway OpenTelemetry service name. Empty uses openshell-gateway.
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 gateway and 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 gateway and 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 nil Pull policy for sandbox pods. Leave unset to use the Kubernetes image default (Always for :latest, IfNotPresent otherwise). Prefer always, if_not_present, or never; the chart also accepts legacy Kubernetes spellings Always, IfNotPresent, and Never.
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). Only used when enableMtls is true. Set to "" to disable client certificate verification for HTTPS-only mode.
server.tls.clientTlsSecretName string "openshell-client-tls" K8s secret mounted into sandbox pods for mTLS to the server.
server.tls.enableMtls bool true Enable mTLS client certificate authentication. When false, the gateway runs HTTPS-only without requiring client certificates (use OIDC for auth instead). Must be false when using BackendTLSPolicy because ingress proxies cannot present client certificates to the backend.
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 nil Sandbox supervisor pull policy. Leave unset to use the Kubernetes image default. Prefer always, if_not_present, or never; the chart also accepts legacy Kubernetes spellings Always, IfNotPresent, and Never.
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.
upstreamProxy object {"authAllowInsecure":false,"authSecret":{"key":"","name":""},"connectByHostname":false,"noProxy":"","url":""} Operator-owned corporate forward proxy for policy-approved TLS egress from Kubernetes sandboxes. The workload cannot select or override it.
upstreamProxy.authAllowInsecure bool false Required when authSecret is configured because Basic auth to an HTTP proxy is cleartext.
upstreamProxy.authSecret.key string "" Secret key containing the proxy credential.
upstreamProxy.authSecret.name string "" Existing Secret in the sandbox namespace containing a user:pass value.
upstreamProxy.connectByHostname bool false Last-resort option for hostname-filtering proxy ACLs. It lets the proxy resolve CONNECT targets.
upstreamProxy.noProxy string "" Comma-separated destinations that bypass only the corporate proxy.
upstreamProxy.url string "" HTTP proxy URL in http://host:port form. HTTPS-to-proxy is not supported.
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.
workspaceResources.enabled bool true Create the sandbox ServiceAccount, Role, RoleBinding, and NetworkPolicy from this chart. Disable for a gateway-only release.

Autogenerated from chart metadata using helm-docs v1.14.2