mirror of
https://github.com/NVIDIA/OpenShell.git
synced 2026-10-21 06:11:31 +08:00
Install a ValidatingAdmissionPolicy, on by default in managed and operator workspace modes, that matches only the gateway ServiceAccount. It admits Secret, Pod, Sandbox, Service, ServiceAccount, and NetworkPolicy writes only in namespaces labeled as owned by this gateway and namespaces matching the operator selector. Secret writes are also admitted in the credential namespace when the Kubernetes Secrets credential driver is enabled. Namespace writes are admitted only for namespaces owned by this gateway, judged by their existing labels. - Skip rendering the policy when rbac.create or rbac.clusterScoped.create is false; a cluster-admin applies it with the other cluster-scoped objects. - Require Kubernetes 1.30. - Fail rendering with operatorNamespaceFile or set-based operator selectors, which the policy cannot evaluate. admissionPolicy.enabled set to false opts out. - Add e2e checks in managed and operator modes that send server-side dry-run requests as the gateway ServiceAccount outside its namespaces, including Pod creation in the sandbox and credential namespace, and assert the policy rejects them. - Document the policy and opt-out, raise the minimum Kubernetes version in the docs and support matrix, and add policy denials to the cluster debugging skill. Signed-off-by: Kris Hicks <khicks@nvidia.com>
329 lines
15 KiB
Go Template
329 lines
15 KiB
Go Template
# OpenShell Helm Chart
|
|
|
|
<!--
|
|
This file is generated by helm-docs.
|
|
Edit README.md.gotmpl and values.yaml, then run `mise run helm:docs`.
|
|
-->
|
|
|
|
> **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.
|
|
|
|
## Cluster-scoped vs namespaced objects
|
|
|
|
Most objects in this chart are namespaced and land in the release namespace.
|
|
These are cluster-scoped:
|
|
|
|
| Object | Default name |
|
|
| --- | --- |
|
|
| `ClusterRole` | `<fullname>-node-reader-<release namespace>` |
|
|
| `ClusterRoleBinding` | `<fullname>-node-reader-<release namespace>` |
|
|
| `ValidatingAdmissionPolicy` | `<fullname>-workspace-scope-<release namespace>` |
|
|
| `ValidatingAdmissionPolicyBinding` | `<fullname>-workspace-scope-<release namespace>` |
|
|
|
|
The admission policy and its binding render only in `managed` and `operator`
|
|
workspace modes. By default the release creates all of them, so an install by a cluster-admin is
|
|
unchanged. On clusters where cluster-scoped RBAC is owned by a different team,
|
|
split the install in two.
|
|
|
|
A cluster-admin applies the cluster-scoped objects once per gateway
|
|
ServiceAccount, rendered from the same values the release uses:
|
|
|
|
```shell
|
|
helm template openshell oci://ghcr.io/nvidia/openshell/helm-chart --version <version> \
|
|
--namespace openshell -f my-values.yaml \
|
|
--set rbac.create=true \
|
|
--set rbac.clusterScoped.create=true \
|
|
--set agentSandbox.preflight.enabled=false \
|
|
--show-only templates/clusterrole.yaml \
|
|
--show-only templates/clusterrolebinding.yaml | kubectl apply -f -
|
|
```
|
|
|
|
In `managed` and `operator` workspace modes, also add
|
|
`--show-only templates/workspace-admission-policy.yaml`, and pass
|
|
`--set admissionPolicy.enabled=false` to the namespace-admin install below.
|
|
|
|
A namespace-admin then installs and upgrades the release with cluster-scoped
|
|
objects omitted, using [`ci/values-namespace-admin.yaml`](ci/values-namespace-admin.yaml)
|
|
or the equivalent `--set`:
|
|
|
|
```shell
|
|
helm upgrade --install openshell oci://ghcr.io/nvidia/openshell/helm-chart --version <version> \
|
|
--namespace openshell -f my-values.yaml \
|
|
--set rbac.clusterScoped.create=false
|
|
```
|
|
|
|
The gateway ServiceAccount name and namespace do not change, so the
|
|
pre-created `ClusterRoleBinding` keeps matching the release. This works with
|
|
`serviceAccount.create=false` too: the `ClusterRoleBinding` subject follows
|
|
`serviceAccount.name`, so render the admin step with the same values.
|
|
|
|
### Which flag the installer needs
|
|
|
|
In the default `shared` workspace mode the release also creates a namespaced
|
|
sandbox `Role` granting Agent Sandbox (`agents.x-k8s.io`) permissions.
|
|
Kubernetes forbids granting permissions you do not hold, and the built-in
|
|
`admin` ClusterRole does not cover that CRD, so an installer holding only
|
|
`admin` cannot create it. `rbac.clusterScoped.create=false` alone is then not
|
|
enough and the install fails with `attempting to grant RBAC permissions not
|
|
currently held`.
|
|
|
|
| Workspace mode | Installer holds | Use |
|
|
| --- | --- | --- |
|
|
| `shared` | built-in `admin` only | `rbac.create=false`, cluster-admin pre-creates all gateway RBAC |
|
|
| `shared` | `admin` plus the sandbox permissions in the namespace | `rbac.clusterScoped.create=false` |
|
|
| `managed`, `operator` | built-in `admin` only | `rbac.clusterScoped.create=false`, `admissionPolicy.enabled=false` |
|
|
|
|
`managed` and `operator` render no namespaced sandbox `Role`, so no extra grant
|
|
is needed there.
|
|
|
|
With `rbac.create=false` the cluster-admin applies the namespaced RBAC too,
|
|
adding it to the same render:
|
|
|
|
```shell
|
|
helm template openshell oci://ghcr.io/nvidia/openshell/helm-chart --version <version> \
|
|
--namespace openshell -f my-values.yaml \
|
|
--set rbac.create=true \
|
|
--set rbac.clusterScoped.create=true \
|
|
--set agentSandbox.preflight.enabled=false \
|
|
--show-only templates/clusterrole.yaml \
|
|
--show-only templates/clusterrolebinding.yaml \
|
|
--show-only templates/role.yaml \
|
|
--show-only templates/rolebinding.yaml \
|
|
--show-only templates/peer-role.yaml | kubectl apply -f -
|
|
```
|
|
|
|
To grant the installer the sandbox permissions instead, bind it to a Role
|
|
carrying the same rules as the chart's `openshell-sandbox` Role.
|
|
|
|
The certgen hook and credential driver RBAC keep their own flags
|
|
(`pkiInitJob.enabled` and
|
|
`server.credentialDrivers.kubernetesSecrets.rbac.create`).
|
|
|
|
### Migrating an existing release
|
|
|
|
Helm deletes objects that leave a release manifest, so setting
|
|
`rbac.clusterScoped.create=false` on a release that already owns the
|
|
`ClusterRole` and `ClusterRoleBinding` deletes them. The gateway then loses
|
|
TokenReview until a cluster-admin re-applies them. Hand ownership over first, as
|
|
cluster-admin, so nothing is deleted:
|
|
|
|
```shell
|
|
kubectl annotate clusterrole "openshell-node-reader-<namespace>" \
|
|
helm.sh/resource-policy=keep --overwrite
|
|
kubectl annotate clusterrolebinding "openshell-node-reader-<namespace>" \
|
|
helm.sh/resource-policy=keep --overwrite
|
|
```
|
|
|
|
In `managed` and `operator` workspace modes, the namespace-admin release also
|
|
sets `admissionPolicy.enabled=false`, which deletes the admission policy and its
|
|
binding while the gateway keeps its cluster-wide permissions. Annotate them too:
|
|
|
|
```shell
|
|
kubectl annotate validatingadmissionpolicy "openshell-workspace-scope-<namespace>" \
|
|
helm.sh/resource-policy=keep --overwrite
|
|
kubectl annotate validatingadmissionpolicybinding "openshell-workspace-scope-<namespace>" \
|
|
helm.sh/resource-policy=keep --overwrite
|
|
```
|
|
|
|
The objects then survive the upgrade that sets the flags, and the cluster-admin
|
|
owns them from that point on. Fresh installs need no such step.
|
|
|
|
`rbac.clusterScoped.create` is independent of
|
|
`server.drivers.kubernetes.workspaceMode`. Managed and operator modes change
|
|
what the `ClusterRole` contains, but they never force the namespaced release to
|
|
apply it. Re-run the cluster-admin step after changing values that affect the
|
|
`ClusterRole` rules.
|
|
|
|
|
|
## Prerequisites
|
|
|
|
> **Required:** Your cluster CNI MUST enforce Kubernetes `NetworkPolicy` for
|
|
> ingress and egress in every sandbox namespace. OpenShell creates the policies,
|
|
> but Kubernetes accepts them even if no CNI enforces them. Without enforcement,
|
|
> sandbox workloads may connect directly and bypass supervisor network policy.
|
|
> Verify CNI support before installing OpenShell.
|
|
|
|
The Kubernetes Agent Sandbox CRDs and controller must be installed on the cluster before deploying OpenShell. Install them with:
|
|
|
|
```shell
|
|
kubectl apply -f https://github.com/kubernetes-sigs/agent-sandbox/releases/latest/download/sandbox.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
|
|
|
|
```shell
|
|
helm install openshell oci://ghcr.io/nvidia/openshell/helm-chart --version <version>
|
|
```
|
|
|
|
## Install on OpenShift
|
|
|
|
See the full [OpenShift install guide](https://docs.nvidia.com/openshell/latest/kubernetes/openshift) for details. Quick start:
|
|
|
|
```shell
|
|
# Precreate the openshell namespace
|
|
oc create ns 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](https://docs.nvidia.com/openshell/latest/kubernetes/openshift#end-to-end-tls-openshift-422) for details.
|
|
|
|
## Available versions
|
|
|
|
| Tag | Source | Notes |
|
|
| --- | --- | --- |
|
|
| `<semver>` (e.g. `0.6.0`) | Tagged GitHub release | Tracks the matching gateway, sandbox, and supervisor image versions. Recommended for production. |
|
|
| `<semver>-pre.N` (e.g. `0.1.0-pre.3`) | A specific prerelease candidate | Immutable candidate pin that tracks images with the same exact version. |
|
|
| `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. |
|
|
|
|
Prerelease and `dev` tags are intended for testing changes ahead of a release. Production deployments should pin to a stable tagged release.
|
|
|
|
## Configuration
|
|
|
|
See [`values.yaml`](values.yaml) for source defaults. Selected overlays:
|
|
|
|
- [`ci/values-gateway.yaml`](ci/values-gateway.yaml) - gateway-only configuration
|
|
- [`ci/values-cert-manager.yaml`](ci/values-cert-manager.yaml) - cert-manager integration
|
|
- [`ci/values-keycloak.yaml`](ci/values-keycloak.yaml) - Keycloak OIDC integration
|
|
- [`ci/values-high-availability.yaml`](ci/values-high-availability.yaml) - CI overlay for multi-replica external PostgreSQL testing
|
|
- [`ci/values-spire.yaml`](ci/values-spire.yaml) - SPIFFE/SPIRE provider token grants
|
|
- [`ci/values-spire-stack.yaml`](ci/values-spire-stack.yaml) - SPIRE hardened chart values for local development
|
|
|
|
### Database backend
|
|
|
|
By default, OpenShell uses SQLite and runs the gateway as a StatefulSet so the
|
|
database is backed by a per-pod PVC:
|
|
|
|
```yaml
|
|
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:
|
|
|
|
```bash
|
|
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:
|
|
|
|
```bash
|
|
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
|
|
```
|
|
|
|
### High availability
|
|
|
|
Set `replicaCount` above `1` only with `server.externalDbSecret`; the default
|
|
SQLite database is per pod and cannot coordinate multiple gateway replicas.
|
|
The chart creates a headless peer Service for gateway-to-gateway relay traffic.
|
|
StatefulSet pods use stable pod DNS names through that headless Service.
|
|
Deployment pods advertise their pod IP with `OPENSHELL_PEER_ENDPOINT`, because
|
|
Kubernetes does not assign stable per-pod DNS names to Deployment replicas.
|
|
|
|
Gateway peer traffic uses Kubernetes ServiceAccount identity. Each gateway pod
|
|
mounts a projected, pod-bound ServiceAccount token with audience
|
|
`openshell-gateway-peer`; receiving replicas validate that token with the
|
|
Kubernetes TokenReview API, verify the live pod UID and Helm selector labels,
|
|
and authorize only the internal `PeerRelay` RPC. The chart does not create or
|
|
accept a shared gateway peer Secret.
|
|
|
|
With gateway TLS enabled, peer calls use the chart CA and client TLS Secret for
|
|
server verification and mTLS. The client verifies the stable gateway Service
|
|
DNS name while connecting directly to the owning pod. Custom TLS Secrets must
|
|
include that Service DNS name in the server certificate and provide the CA and
|
|
client credentials configured by `server.tls`.
|
|
|
|
## 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.
|
|
|
|
{{ template "chart.valuesSection" . }}
|
|
{{ template "helm-docs.versionFooter" . }}
|