Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions deploy/helm/openshell/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -450,13 +450,15 @@ discovery endpoint or its TLS CA.
| 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.sandboxGid | string | `""` | GID for sandbox pods (`sandbox_gid`). Empty (default) = same as sandboxUid. Must be an integer between 1 and 4294967294. |
| 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.sandboxUid | string | `""` | UID for sandbox pods (`sandbox_uid`). Empty (default) = use the OpenShift SCC namespace annotation if present, otherwise the driver default. Must be an integer between 1 and 4294967294. |
| 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. |
Expand Down
16 changes: 16 additions & 0 deletions deploy/helm/openshell/templates/_helpers.tpl
Original file line number Diff line number Diff line change
Expand Up @@ -379,6 +379,22 @@ never
{{- end -}}
{{- end }}

{{/*
Render a sandbox UID/GID chart value as an integer, or nothing when unset.
Takes a dict with `name` (the values key, for errors) and `value`. The bounds
match openshell_policy::MIN_SANDBOX_UID..=MAX_SANDBOX_UID. Helm parses YAML
numbers as float64, so the integer conversion also avoids `2e+09` rendering.
*/}}
{{- define "openshell.sandboxId" -}}
{{- if not (or (kindIs "invalid" .value) (eq (toString .value) "")) -}}

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

gator-agent

Warning — GATOR-6a07ea84-01 · Reject boolean sandbox identities

Summary: An operator can set server.sandboxUid: true or server.sandboxGid: true; the numeric conversions both produce 1, so validation passes and Helm renders identity 1. This overrides SCC discovery, which can leave pods rejected on OpenShift or run them under an unintended system account elsewhere.

Fix: Reject booleans before conversion, or allow-list supported numeric/string scalar kinds while preserving empty and null omission. Add a boolean rejection test for the shared helper.

Verify: Set either value to true; rendering must fail with the documented integer-range error instead of producing sandbox_uid = 1 or sandbox_gid = 1.

Agent context
  • Ownership: This PR adds the Helm inputs and conversion-based validator.
  • Location: deploy/helm/openshell/templates/_helpers.tpl:389
  • Sibling callers: gateway-config.yaml:250 and gateway-config.yaml:253

{{- $id := int64 .value -}}
{{- if or (ne (float64 .value) (float64 $id)) (lt $id 1) (gt $id 4294967294) -}}
{{- fail (printf "%s must be an integer between 1 and 4294967294" .name) -}}
{{- end -}}
{{- $id -}}
{{- end -}}
{{- end }}

{{/*
Validate chart values that Helm would otherwise accept silently.
*/}}
Expand Down
6 changes: 6 additions & 0 deletions deploy/helm/openshell/templates/gateway-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -247,6 +247,12 @@ data:
{{- if .Values.server.defaultRuntimeClassName }}
default_runtime_class_name = {{ .Values.server.defaultRuntimeClassName | quote }}
{{- end }}
{{- with include "openshell.sandboxId" (dict "name" "server.sandboxUid" "value" .Values.server.sandboxUid) }}
sandbox_uid = {{ . }}
{{- end }}
{{- with include "openshell.sandboxId" (dict "name" "server.sandboxGid" "value" .Values.server.sandboxGid) }}
sandbox_gid = {{ . }}
{{- end }}
{{- if (.Values.supervisor.image.pullPolicy | default .Values.global.image.pullPolicy) }}
supervisor_image_pull_policy = {{ include "openshell.canonicalImagePullPolicy" (.Values.supervisor.image.pullPolicy | default .Values.global.image.pullPolicy) | quote }}
{{- end }}
Expand Down
79 changes: 79 additions & 0 deletions deploy/helm/openshell/tests/gateway_sandbox_identity_test.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
# SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
# SPDX-License-Identifier: Apache-2.0

suite: sandbox UID and GID
templates:
- templates/gateway-config.yaml
release:
name: openshell
namespace: my-namespace

tests:
- it: omits sandbox_uid and sandbox_gid by default
asserts:
- notMatchRegex:
path: data["gateway.toml"]
pattern: 'sandbox_uid|sandbox_gid'

- it: renders sandbox_uid and sandbox_gid as integers
set:
server.sandboxUid: 1500
server.sandboxGid: 2000
asserts:
- matchRegex:
path: data["gateway.toml"]
pattern: '(?m)^\s*sandbox_uid\s*=\s*1500$'
- matchRegex:
path: data["gateway.toml"]
pattern: '(?m)^\s*sandbox_gid\s*=\s*2000$'

- it: renders sandbox_uid alone
set:
server.sandboxUid: 1500
asserts:
- matchRegex:
path: data["gateway.toml"]
pattern: '(?m)^\s*sandbox_uid\s*=\s*1500$'
- notMatchRegex:
path: data["gateway.toml"]
pattern: 'sandbox_gid'

- it: renders large IDs without scientific notation
set:
server.sandboxUid: 1000680000
server.sandboxGid: 4294967294
asserts:
- matchRegex:
path: data["gateway.toml"]
pattern: '(?m)^\s*sandbox_uid\s*=\s*1000680000$'
- matchRegex:
path: data["gateway.toml"]
pattern: '(?m)^\s*sandbox_gid\s*=\s*4294967294$'

- it: rejects a zero sandbox UID
set:
server.sandboxUid: 0
asserts:
- failedTemplate:
errorPattern: "server.sandboxUid must be an integer between 1 and 4294967294"

- it: rejects a sandbox GID above the allowed range
set:
server.sandboxGid: 4294967295
asserts:
- failedTemplate:
errorPattern: "server.sandboxGid must be an integer between 1 and 4294967294"

- it: rejects a non-numeric sandbox UID
set:
server.sandboxUid: abc
asserts:
- failedTemplate:
errorPattern: "server.sandboxUid must be an integer between 1 and 4294967294"

- it: rejects a fractional sandbox UID
set:
server.sandboxUid: 1500.5
asserts:
- failedTemplate:
errorPattern: "server.sandboxUid must be an integer between 1 and 4294967294"
7 changes: 7 additions & 0 deletions deploy/helm/openshell/values.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -328,6 +328,13 @@ server:
# Set to a RuntimeClass name (e.g. "kata-containers", "nvidia") to apply it
# to all sandboxes that don't explicitly override it.
defaultRuntimeClassName: ""
# -- UID for sandbox pods (`sandbox_uid`). Empty (default) = use the OpenShift
# SCC namespace annotation if present, otherwise the driver default. Must be
# an integer between 1 and 4294967294.
sandboxUid: ""
# -- GID for sandbox pods (`sandbox_gid`). Empty (default) = same as
# sandboxUid. Must be an integer between 1 and 4294967294.
sandboxGid: ""
# -- 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.
Expand Down
1 change: 1 addition & 0 deletions docs/how-it-works/gateways/configuration.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -814,6 +814,7 @@ provider_spiffe_workload_api_socket_path = "/spiffe-workload-api/spire-agent.soc
# PVC init container. When unset, the driver auto-detects from OpenShift SCC
# namespace annotations (openshift.io/sa.scc.uid-range) if present, falling
# back to 1000 on non-OpenShift clusters. Any non-root Linux UID/GID is valid.
# Helm sets these with server.sandboxUid and server.sandboxGid.
# sandbox_uid = 1500
# sandbox_gid = 1500
# Operator-mode namespace discovery. At least one must be set when
Expand Down
1 change: 1 addition & 0 deletions docs/how-it-works/sandboxes/runtimes.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -226,6 +226,7 @@ Only the OpenShell gateway and the Agent Sandbox controller should be able to ma
| `supervisor_image` | `supervisor.image.*` | Override the supervisor image. |
| `workspace_default_storage_size` | `server.workspaceDefaultStorageSize` | Default workspace PVC size. |
| `workspace_storage_class` | `server.workspaceStorageClass` | `StorageClass` for workspace PVCs. Set this if the cluster has no default `StorageClass`. |
| `sandbox_uid` / `sandbox_gid` | `server.sandboxUid` / `server.sandboxGid` | Sandbox pod UID and GID. Takes priority over OpenShift SCC namespace annotations. `sandbox_gid` defaults to the UID. |
| `https_proxy` | `upstreamProxy.url` | Corporate proxy for sandbox egress. |
| `no_proxy` | `upstreamProxy.noProxy` | Destinations that bypass the corporate proxy. |
| `proxy_auth_secret_name` / `proxy_auth_secret_key` | `upstreamProxy.authSecret.name` / `.key` | Secret holding the proxy `user:pass` credential. |
Expand Down
Loading