> For clean Markdown of any page, append .md to the page URL.
> For a complete documentation index, see https://docs.nvidia.com/openshell/llms.txt.
> For AI client integration (Claude Code, Cursor, etc.), connect to the MCP server at https://docs.nvidia.com/openshell/_mcp/server.

# OpenShift

> Install the OpenShell Helm chart on OpenShift, including the SCC binding and chart overrides required by OpenShift's Security Context Constraints.

The OpenShift install path is experimental. It currently requires running sandbox pods under the `privileged` SCC and installing the gateway with TLS disabled. Use only for evaluation on a private network.

OpenShift's [Security Context Constraints](https://docs.openshift.com/container-platform/latest/authentication/managing-security-context-constraints.html) reject the chart's default pod security settings. Installing on OpenShift requires precreating the namespace, granting the `privileged` SCC to the sandbox service account, and overriding a few chart values so the cluster admission controller can assign UIDs and FS groups itself.

OpenShell installs sandbox nftables rules as individual commands. On OpenShift
nodes where optional conntrack or packet log expressions are unavailable, those
optional rules can fail without rolling back the required proxy bypass reject
rules.

## Prerequisites

* OpenShift 4.x cluster with `oc` configured
* Helm 3.x
* [Agent Sandbox](/kubernetes/setup#install-agent-sandbox) controller and CRDs installed

## Install

## Create the namespace

Pre-create the namespace so the SCC binding can be applied before the chart installs:

```shell
oc create ns openshell
```

## Grant the privileged SCC to sandbox pods

Sandbox pods run under the `openshell-sandbox` service account in the `openshell` namespace and require the `privileged` SCC:

```shell
oc adm policy add-scc-to-user privileged -z openshell-sandbox -n openshell
```

## Install the chart with OpenShift overrides

```shell
helm install openshell oci://ghcr.io/nvidia/openshell/helm-chart \
  --version <version> \
  --namespace openshell \
  --set server.disableTls=true \
  --set podSecurityContext.fsGroup=null \
  --set securityContext.runAsUser=null
```

| Override                                                             | Reason                                                                                    |
| -------------------------------------------------------------------- | ----------------------------------------------------------------------------------------- |
| `server.disableTls=true`                                             | Runs the gateway over plaintext HTTP for simpler evaluation.                              |
| `podSecurityContext.fsGroup=null` / `securityContext.runAsUser=null` | Clear the chart's hardcoded UID and fsGroup so OpenShift's SCC admission can assign them. |

## Wait for the gateway to be ready

```shell
oc -n openshell rollout status statefulset/openshell
```

If you set `workload.kind=deployment`, use
`oc -n openshell rollout status deployment/openshell` instead.

## Connect to the gateway

The gateway is now running over plaintext HTTP. Connect with `oc port-forward`:

```shell
oc -n openshell port-forward svc/openshell 8080:8080
```

Register the gateway with the CLI:

```shell
openshell gateway add http://127.0.0.1:8080 --local --name openshift
openshell status
```

## Options for end-to-end TLS

The steps above run the gateway over plaintext HTTP for quick evaluation. For production deployments, choose one of the approaches below based on your OpenShift version and preferences.

### End-to-end TLS using Gateway API and BackendTLSPolicy (OpenShift 4.22+)

OpenShift 4.22 and later support `BackendTLSPolicy` in the Gateway API, enabling end-to-end TLS between the OpenShift router and the OpenShell gateway pod. The traffic flow is:

```text
client → HTTPS → OpenShift Gateway (terminate TLS) → TLS (re-encrypt) → openshell gateway pod
```

This removes the requirement to run the gateway with `server.disableTls=true`. The OpenShift router terminates client-facing TLS at the listener and re-encrypts when connecting to the backend service, validating the backend's certificate against a CA you provide.

#### Prerequisites

* OpenShift 4.22+ cluster with the Gateway API enabled
* cert-manager installed (recommended) or the built-in pkiInitJob for server certificates
* A `GatewayClass` registered for the OpenShift gateway controller

#### Create the GatewayClass

If your cluster does not already have an OpenShift GatewayClass, create one:

```shell
oc apply -f - <<'EOF'
apiVersion: gateway.networking.k8s.io/v1
kind: GatewayClass
metadata:
  name: openshift-default
spec:
  controllerName: openshift.io/gateway-controller/v1
EOF
```

#### Create the Gateway

Create a Gateway resource in the `openshift-ingress` namespace. Replace `<external-hostname>` with your cluster's route hostname (typically a wildcard like `*.openshell-ingress-gw.example.com`):

```shell
oc apply -f - <<'EOF'
apiVersion: gateway.networking.k8s.io/v1
kind: Gateway
metadata:
  name: openshell-gateway
  namespace: openshift-ingress
spec:
  gatewayClassName: openshift-default
  listeners:
    - name: grpc
      hostname: "<external-hostname>"
      port: 443
      protocol: HTTPS
      tls:
        mode: Terminate
        certificateRefs:
          - name: <listener-tls-secret>
            kind: Secret
      allowedRoutes:
        namespaces:
          from: Selector
          selector:
            matchLabels:
              kubernetes.io/metadata.name: openshell
EOF
```

The listener TLS Secret should contain the certificate for the external hostname.

#### Install with e2e TLS

Install the chart with the GRPCRoute and BackendTLSPolicy enabled. The certgen hook automatically creates the backend CA ConfigMap from the generated PKI bundle:

```shell
helm install openshell oci://ghcr.io/nvidia/openshell/helm-chart \
  --version <version> \
  --namespace openshell \
  --set podSecurityContext.fsGroup=null \
  --set securityContext.runAsUser=null \
  --set server.tls.enableMtls=false \
  --set grpcRoute.enabled=true \
  --set grpcRoute.gateway.name=openshell-gateway \
  --set grpcRoute.gateway.namespace=openshift-ingress \
  --set 'grpcRoute.hostnames[0]=gateway.example.com' \
  --set grpcRoute.backendTLSPolicy.enabled=true \
  --set server.oidc.issuer=https://keycloak.example.com/realms/openshell \
  --set server.oidc.audience=openshell-cli
```

| Override                                                             | Reason                                                                                                                                                                                                                                                             |
| -------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `podSecurityContext.fsGroup=null` / `securityContext.runAsUser=null` | Let OpenShift's SCC admission assign UIDs.                                                                                                                                                                                                                         |
| `server.tls.enableMtls=false`                                        | Disable mTLS client certificate authentication. BackendTLSPolicy only validates the server certificate; the ingress proxy cannot present a client certificate to the backend. Use OIDC for authentication instead.                                                 |
| `grpcRoute.enabled=true`                                             | Create a GRPCRoute pointing at the external Gateway.                                                                                                                                                                                                               |
| `grpcRoute.gateway.name` / `namespace`                               | Reference the Gateway created above in `openshift-ingress`.                                                                                                                                                                                                        |
| `grpcRoute.backendTLSPolicy.enabled=true`                            | Create a BackendTLSPolicy for TLS re-encryption to the gateway pod. The certgen hook auto-creates the backend CA ConfigMap. The Gateway proxy validates the backend certificate against the service FQDN, which is already in the default server certificate SANs. |
| `grpcRoute.hostnames`                                                | External hostname for the GRPCRoute. This goes on the Gateway listener certificate, not the backend certificate.                                                                                                                                                   |

Note that `server.disableTls` is **not** set — the gateway pod serves TLS over HTTPS without requiring client certificates. Use OIDC for authentication (see [Access Control](/kubernetes/access-control)).

**Using cert-manager instead of pkiInitJob:** Add `--set certManager.enabled=true` to the install command. The default `certManager.serverDnsNames` already includes the service FQDN needed for BackendTLSPolicy validation. The Certificate resources are regular release objects, and a separate post-install/post-upgrade Job (`<release>-certgen-backend-ca`) polls for up to 120 seconds waiting for cert-manager to issue the server certificate, then creates the backend CA ConfigMap. A single `helm install` is sufficient in most cases.

If cert-manager takes longer than 120 seconds to issue certificates, increase the polling timeout with `--set pkiInitJob.timeoutSeconds=<seconds>`. The hook polls for exactly this many seconds. For example, `timeoutSeconds=180` polls for 180 seconds. By default (`pkiInitJob.failOnTimeout=true`), the install fails if the timeout is reached, providing clear feedback that the BackendTLSPolicy is non-functional.

#### Register over HTTPS

```shell
openshell gateway add https://gateway.example.com \
  --name openshift \
  --oidc-issuer https://keycloak.example.com/realms/openshell \
  --oidc-client-id openshell-cli
openshell status
```

### End-to-end TLS using pass-through Route (all OpenShift versions)

For OpenShift versions prior to 4.22, or when you prefer Route-based ingress, cert-manager can issue the gateway's server certificate from a real Issuer or ClusterIssuer (for example, a LetsEncrypt/ACME issuer), and an OpenShift Route with TLS passthrough exposes it externally while the gateway keeps terminating its own TLS and mTLS.

Install cert-manager and configure a working `ClusterIssuer` first — see
[Managing Certificates](/kubernetes/managing-certificates) for the
`certManager.serverIssuerRef` details. Configure an OIDC provider as described
in [Access Control](/kubernetes/access-control) — remote gateways authenticate
CLI users via OIDC, not mTLS, so the gateway must know the OIDC issuer URL.
Install the chart with:

```shell
helm install openshell oci://ghcr.io/nvidia/openshell/helm-chart \
  --version <version> \
  --namespace openshell \
  --set podSecurityContext.fsGroup=null \
  --set securityContext.runAsUser=null \
  --set server.disableTls=false \
  --set certManager.enabled=true \
  --set certManager.serverIssuerRef.name=letsencrypt-prod \
  --set certManager.serverIssuerRef.kind=ClusterIssuer \
  --set certManager.serverDnsNames[0]=gateway.example.com \
  --set openshiftRoute.enabled=true \
  --set openshiftRoute.host=gateway.example.com \
  --set server.oidc.issuer=https://keycloak.example.com/realms/openshell \
  --set server.oidc.audience=openshell-cli
```

| Override                                         | Reason                                                                                                                                                                                                                                                                                                                                                                                       |
| ------------------------------------------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `certManager.serverIssuerRef`                    | Creates a second server certificate from your Issuer or ClusterIssuer for external clients. The gateway uses SNI to present this cert for the external hostname while continuing to present the internal (chart CA) cert to supervisors. The internal certificate's `ca.crt` is the chart CA that also signed the client cert, so the default `clientCaFromServerTlsSecret=true` is correct. |
| `openshiftRoute.enabled` / `openshiftRoute.host` | Creates an OpenShift Route with TLS passthrough — the router forwards the encrypted connection by SNI without decrypting, so the gateway uses the SNI hostname to select the external certificate.                                                                                                                                                                                           |
| `server.oidc.issuer` / `server.oidc.audience`    | Configures server-side OIDC validation. Without these, the gateway expects mTLS client certificates and rejects OIDC-only CLI connections. See [Access Control](/kubernetes/access-control).                                                                                                                                                                                                 |

Register the gateway with the CLI over OIDC. Remote gateways authenticate CLI
users via OIDC, not mTLS — see [Access Control](/kubernetes/access-control):

```shell
openshell gateway add https://gateway.example.com \
  --name openshift \
  --oidc-issuer https://keycloak.example.com/realms/openshell \
  --oidc-client-id openshell-cli
openshell gateway login openshift
```

## Next Steps

* For more on certificate provisioning modes, refer to [Managing Certificates](/kubernetes/managing-certificates).
* To expose the gateway externally through the Kubernetes Gateway API instead of a Route, refer to [Ingress](/kubernetes/ingress).
* To configure OIDC authentication, refer to [Access Control](/kubernetes/access-control).