diff --git a/deploy/docker/Dockerfile.gateway.openshift b/deploy/docker/Dockerfile.gateway.openshift new file mode 100644 index 0000000000..9d4c90eb84 --- /dev/null +++ b/deploy/docker/Dockerfile.gateway.openshift @@ -0,0 +1,38 @@ +# syntax=docker/dockerfile:1.4 + +# Multi-stage gateway build for OpenShift BuildConfig. +# Compiles openshell-gateway from source with bundled Z3. + +FROM registry.access.redhat.com/ubi9/ubi:latest AS builder + +RUN dnf install -y --setopt=tsflags=nodocs \ + gcc-toolset-15-gcc gcc-toolset-15-gcc-c++ gcc-toolset-15-libstdc++-devel \ + gcc-toolset-15-libatomic-devel gcc-toolset-15-binutils \ + make cmake openssl-devel pkg-config perl-FindBin perl-File-Compare \ + clang-libs clang-devel llvm-devel \ + && dnf clean all + +ENV PATH="/opt/rh/gcc-toolset-15/root/usr/bin:${PATH}" \ + CC=gcc CXX=g++ \ + LD_LIBRARY_PATH="/opt/rh/gcc-toolset-15/root/usr/lib64" + +RUN curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | \ + sh -s -- -y --default-toolchain 1.95.0 --profile minimal +ENV PATH="/root/.cargo/bin:/opt/rh/gcc-toolset-15/root/usr/bin:${PATH}" + +WORKDIR /build +COPY . . + +RUN cargo build --release -p openshell-server --features bundled-z3 \ + && strip target/release/openshell-gateway + +# --- runtime --- +FROM registry.access.redhat.com/ubi9/ubi-minimal:latest + +COPY --from=builder /build/target/release/openshell-gateway /usr/local/bin/openshell-gateway + +USER 1000:1000 +EXPOSE 8080 + +ENTRYPOINT ["/usr/local/bin/openshell-gateway"] +CMD ["--bind-address", "0.0.0.0", "--port", "8080"] diff --git a/deploy/docker/Dockerfile.supervisor.openshift b/deploy/docker/Dockerfile.supervisor.openshift new file mode 100644 index 0000000000..d1b598a6dd --- /dev/null +++ b/deploy/docker/Dockerfile.supervisor.openshift @@ -0,0 +1,42 @@ +# syntax=docker/dockerfile:1.4 + +# Multi-stage supervisor build for OpenShift BuildConfig. +# Compiles a static musl binary of openshell-sandbox. + +FROM registry.access.redhat.com/ubi9/ubi:latest AS builder + +RUN dnf install -y --setopt=tsflags=nodocs \ + gcc gcc-c++ make openssl-devel pkg-config \ + perl-FindBin perl-File-Compare \ + && dnf clean all + +RUN curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | \ + sh -s -- -y --default-toolchain 1.95.0 --profile minimal \ + && /root/.cargo/bin/rustup target add x86_64-unknown-linux-musl +ENV PATH="/root/.cargo/bin:${PATH}" + +# Install musl-gcc (not in UBI repos, build from source) +RUN curl -fsSL https://musl.libc.org/releases/musl-1.2.5.tar.gz | tar xz \ + && cd musl-1.2.5 \ + && ./configure --prefix=/usr/local/musl --disable-shared \ + && make -j$(nproc) && make install \ + && cd .. && rm -rf musl-1.2.5 \ + && ln -s /usr/local/musl/bin/musl-gcc /usr/local/bin/musl-gcc + +ENV CC_x86_64_unknown_linux_musl=musl-gcc + +WORKDIR /build +COPY . . + +RUN cargo build --release -p openshell-sandbox \ + --target x86_64-unknown-linux-musl \ + && strip target/x86_64-unknown-linux-musl/release/openshell-sandbox + +# --- runtime --- +FROM scratch + +COPY --chmod=0550 --from=builder \ + /build/target/x86_64-unknown-linux-musl/release/openshell-sandbox \ + /openshell-sandbox + +ENTRYPOINT ["/openshell-sandbox"] diff --git a/docs/kubernetes/access-control.mdx b/docs/kubernetes/access-control.mdx index 5409a4b11d..8ad82dace4 100644 --- a/docs/kubernetes/access-control.mdx +++ b/docs/kubernetes/access-control.mdx @@ -3,8 +3,8 @@ # SPDX-License-Identifier: Apache-2.0 title: "Access Control" sidebar-title: "Access Control" -description: "Configure OIDC user authentication or reverse-proxy auth termination for a Kubernetes-deployed OpenShell gateway." -keywords: "Generative AI, Cybersecurity, Kubernetes, Authentication, mTLS, OIDC, Keycloak, Entra ID, Okta, Gateway Auth" +description: "Configure OIDC user authentication, Keycloak setup, OpenShift identity federation, or reverse-proxy auth termination for a Kubernetes-deployed OpenShell gateway." +keywords: "Generative AI, Cybersecurity, Kubernetes, Authentication, mTLS, OIDC, Keycloak, Entra ID, Okta, Gateway Auth, OpenShift, Identity Federation" position: 5 --- @@ -82,6 +82,256 @@ Both `adminRole` and `userRole` must be set, or both must be empty. Setting only | Microsoft Entra ID | `roles` | | Okta | `groups` | +### Keycloak setup + +Keycloak public clients do not include `sub`, `aud`, or realm roles in access tokens by default. Without these claims, the gateway rejects tokens with errors like `missing field 'sub'`, audience mismatch, or `role 'openshell-user' required`. + +After creating a realm and a public client (with PKCE S256, redirect URIs `http://localhost:*` and `http://127.0.0.1:*`), add these protocol mappers to the client: + +| Mapper name | Mapper type | Key config | +|---|---|---| +| `sub` | Subject (sub) | access.token.claim: true | +| `openshell-audience` | Audience | included.client.audience: `openshell-cli` | +| `realm-roles` | User Realm Role | claim.name: `realm_access.roles`, multivalued: true | + +Add these via the Keycloak admin console under **Clients → openshell-cli → Client scopes → Dedicated scope → Add mapper**, or via the CLI: + + + + +```shell +KC_ADM="kcadm.sh --config /tmp/kcadm.config" + +$KC_ADM config credentials \ + --server http://localhost:8080 \ + --realm master \ + --user \ + --password + +$KC_ADM create realms \ + -s realm=openshell \ + -s enabled=true + +$KC_ADM create clients -r openshell \ + -s clientId=openshell-cli \ + -s enabled=true \ + -s publicClient=true \ + -s directAccessGrantsEnabled=true \ + -s standardFlowEnabled=true \ + -s 'redirectUris=["http://localhost:*","http://127.0.0.1:*"]' \ + -s 'webOrigins=["http://localhost","http://127.0.0.1"]' \ + -s 'attributes={"pkce.code.challenge.method":"S256"}' + +CLIENT_UUID=$($KC_ADM get clients -r openshell \ + -q clientId=openshell-cli --fields id --format csv --noquotes) + +$KC_ADM create clients/$CLIENT_UUID/protocol-mappers/models -r openshell \ + -s name=sub \ + -s protocol=openid-connect \ + -s protocolMapper=oidc-sub-mapper \ + -s 'config={"access.token.claim":"true","id.token.claim":"true"}' + +$KC_ADM create clients/$CLIENT_UUID/protocol-mappers/models -r openshell \ + -s name=openshell-audience \ + -s protocol=openid-connect \ + -s protocolMapper=oidc-audience-mapper \ + -s 'config={"included.client.audience":"openshell-cli","access.token.claim":"true","id.token.claim":"true"}' + +$KC_ADM create clients/$CLIENT_UUID/protocol-mappers/models -r openshell \ + -s name=realm-roles \ + -s protocol=openid-connect \ + -s protocolMapper=oidc-usermodel-realm-role-mapper \ + -s 'config={"claim.name":"realm_access.roles","jsonType.label":"String","multivalued":"true","access.token.claim":"true","id.token.claim":"true","userinfo.token.claim":"true"}' + +$KC_ADM create roles -r openshell -s name=openshell-user +$KC_ADM create roles -r openshell -s name=openshell-admin +``` + + + + +```shell +KC_URL=https://keycloak.example.com + +TOKEN=$(curl -sk "${KC_URL}/realms/master/protocol/openid-connect/token" \ + -d "client_id=admin-cli" \ + -d "username=" \ + -d "password=" \ + -d "grant_type=password" | jq -r .access_token) + +curl -sk -X POST "${KC_URL}/admin/realms" \ + -H "Authorization: Bearer $TOKEN" \ + -H "Content-Type: application/json" \ + -d '{"realm":"openshell","enabled":true}' + +curl -sk -X POST "${KC_URL}/admin/realms/openshell/clients" \ + -H "Authorization: Bearer $TOKEN" \ + -H "Content-Type: application/json" \ + -d '{ + "clientId": "openshell-cli", + "enabled": true, + "publicClient": true, + "directAccessGrantsEnabled": true, + "standardFlowEnabled": true, + "redirectUris": ["http://localhost:*", "http://127.0.0.1:*"], + "webOrigins": ["http://localhost", "http://127.0.0.1"], + "attributes": {"pkce.code.challenge.method": "S256"} + }' + +CLIENT_UUID=$(curl -sk "${KC_URL}/admin/realms/openshell/clients?clientId=openshell-cli" \ + -H "Authorization: Bearer $TOKEN" | jq -r '.[0].id') + +for MAPPER in \ + '{"name":"sub","protocol":"openid-connect","protocolMapper":"oidc-sub-mapper","config":{"access.token.claim":"true","id.token.claim":"true"}}' \ + '{"name":"openshell-audience","protocol":"openid-connect","protocolMapper":"oidc-audience-mapper","config":{"included.client.audience":"openshell-cli","access.token.claim":"true","id.token.claim":"true"}}' \ + '{"name":"realm-roles","protocol":"openid-connect","protocolMapper":"oidc-usermodel-realm-role-mapper","config":{"claim.name":"realm_access.roles","jsonType.label":"String","multivalued":"true","access.token.claim":"true","id.token.claim":"true","userinfo.token.claim":"true"}}'; do + curl -sk -X POST "${KC_URL}/admin/realms/openshell/clients/${CLIENT_UUID}/protocol-mappers/models" \ + -H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" -d "$MAPPER" +done + +curl -sk -X POST "${KC_URL}/admin/realms/openshell/roles" \ + -H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" \ + -d '{"name":"openshell-user"}' + +curl -sk -X POST "${KC_URL}/admin/realms/openshell/roles" \ + -H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" \ + -d '{"name":"openshell-admin"}' +``` + + + + +Assign `openshell-user` to users who need sandbox access and `openshell-admin` to administrators. + + +On Kubernetes, run `kcadm.sh` via `kubectl exec` into the Keycloak pod. On OpenShift, use `oc exec`. The `--config /tmp/kcadm.config` flag is required when the container runs as non-root. For OpenShift-specific OIDC gateway configuration (Helm upgrade with OIDC values and CLI registration), see the [OpenShift](/kubernetes/openshift) install guide. + + +### OpenShift Identity Federation (optional) + +If your organisation wants OpenShell users to authenticate with their existing OpenShift cluster credentials instead of managing separate Keycloak accounts, configure Keycloak to federate to the OpenShift OAuth server. This is optional — the [Keycloak setup](#keycloak-setup) above works standalone with its own user directory. + +When federation is configured, users are auto-imported into the Keycloak realm on first login. + +**Prerequisites:** [Keycloak setup](#keycloak-setup) completed and working, `cluster-admin` access to create `OAuthClient` resources. + +#### Create an OAuthClient in OpenShift + +Generate a client secret and create the `OAuthClient` that Keycloak uses to initiate the OAuth flow: + +```shell +OCP_SECRET=$(openssl rand -hex 32) +KC_URL=https://keycloak.example.com + +cat < + + +```shell +KC_POD="oc exec keycloak-0 -n keycloak --" +KC_ADM="/opt/keycloak/bin/kcadm.sh --config /tmp/kcadm.config" + +$KC_POD $KC_ADM create identity-provider/instances -r openshell \ + -s alias=openshift \ + -s displayName=OpenShift \ + -s providerId=openshift-v4 \ + -s enabled=true \ + -s trustEmail=true \ + -s "firstBrokerLoginFlowAlias=first broker login" \ + -s "config.clientId=keycloak-openshell" \ + -s "config.clientSecret=${OCP_SECRET}" \ + -s "config.baseUrl=$(oc whoami --show-server)" \ + -s "config.defaultScope=user:info" \ + -s "config.syncMode=IMPORT" +``` + + + + +```shell +TOKEN=$(curl -sk "${KC_URL}/realms/master/protocol/openid-connect/token" \ + -d "client_id=admin-cli" \ + -d "username=" \ + -d "password=" \ + -d "grant_type=password" | jq -r .access_token) + +curl -sk -X POST "${KC_URL}/admin/realms/openshell/identity-provider/instances" \ + -H "Authorization: Bearer $TOKEN" \ + -H "Content-Type: application/json" \ + -d "{ + \"alias\": \"openshift\", + \"displayName\": \"OpenShift\", + \"providerId\": \"openshift-v4\", + \"enabled\": true, + \"trustEmail\": true, + \"firstBrokerLoginFlowAlias\": \"first broker login\", + \"config\": { + \"clientId\": \"keycloak-openshell\", + \"clientSecret\": \"${OCP_SECRET}\", + \"baseUrl\": \"$(oc whoami --show-server)\", + \"defaultScope\": \"user:info\", + \"syncMode\": \"IMPORT\" + } + }" +``` + + + + + +**ROSA HCP**: set `baseUrl` to the API server URL (`https://api.:443`), not the OAuth route (`oauth.`). On ROSA HCP, the OAuth metadata endpoint (`/.well-known/oauth-authorization-server`) is served by the API server. The OAuth route returns 404. + + +#### Assign roles to federated users + +Users imported from OpenShift do not receive the `openshell-user` or `openshell-admin` realm roles automatically. After a user's first login through the "OpenShift" button, assign the appropriate role: + +**Keycloak Admin Console → Users → select user → Role Mappings → Assign role → openshell-user** + +Or via the CLI/API: + + + + +```shell +$KC_POD $KC_ADM add-roles -r openshell \ + --uusername \ + --rolename openshell-user +``` + + + + +```shell +USER_ID=$(curl -sk "${KC_URL}/admin/realms/openshell/users?username=&exact=true" \ + -H "Authorization: Bearer $TOKEN" | jq -r '.[0].id') + +ROLE_ID=$(curl -sk "${KC_URL}/admin/realms/openshell/roles/openshell-user" \ + -H "Authorization: Bearer $TOKEN" | jq -r '.id') + +curl -sk -X POST "${KC_URL}/admin/realms/openshell/users/${USER_ID}/role-mappings/realm" \ + -H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" \ + -d "[{\"id\":\"${ROLE_ID}\",\"name\":\"openshell-user\"}]" +``` + + + + +The Keycloak login page shows a "Login with OpenShift" button below the standard username/password form. After authentication, Keycloak issues an OIDC token with the user's identity and assigned roles. + ## Reverse-Proxy Auth Termination When an access proxy, such as Cloudflare Access, ngrok, or a corporate SSO gateway, handles authentication in front of the OpenShell gateway, you can explicitly allow unauthenticated user calls at the gateway: diff --git a/docs/kubernetes/ingress.mdx b/docs/kubernetes/ingress.mdx index 66178bc3a0..b75c10ca18 100644 --- a/docs/kubernetes/ingress.mdx +++ b/docs/kubernetes/ingress.mdx @@ -3,8 +3,8 @@ # SPDX-License-Identifier: Apache-2.0 title: "Ingress" sidebar-title: "Ingress" -description: "Expose the OpenShell gateway externally using the Kubernetes Gateway API and a GRPCRoute." -keywords: "Generative AI, Cybersecurity, Kubernetes, Gateway API, Envoy Gateway, GRPCRoute, Ingress, External Access" +description: "Expose the OpenShell gateway externally using the Kubernetes Gateway API, OpenShift Routes, or Istio." +keywords: "Generative AI, Cybersecurity, Kubernetes, Gateway API, Envoy Gateway, GRPCRoute, Ingress, External Access, OpenShift, Route, Istio" position: 4 --- @@ -135,6 +135,135 @@ openshell status See [Authentication](/kubernetes/setup) for OIDC issuer, audience, and roles configuration. +## OpenShift: Reencrypt Route + +On OpenShift, the Ingress Operator manages routing. The simplest way to expose the gateway externally is an OpenShift Route. However, the gateway multiplexes gRPC and HTTP on a single port, and not all route types preserve HTTP/2: + +| Route type | gRPC support | Issue | +|---|---|---| +| Edge | Broken | Terminates TLS and forces HTTP/1.1 to the backend, which drops gRPC frames. | +| Passthrough | Broken | Preserves HTTP/2 but exposes the gateway's mTLS client certificate requirement to browsers, causing `ERR_BAD_SSL_CLIENT_AUTH_CERT`. | +| **Reencrypt** | **Works** | Terminates external TLS at the router, re-establishes TLS to the backend, and supports HTTP/2 with the `backend-protocol=h2` annotation. | + +Create a reencrypt route using the gateway's CA certificate for the backend TLS connection: + +```shell +DEST_CA=$(oc get secret openshell-server-tls -n openshell \ + -o jsonpath='{.data.ca\.crt}' | base64 -d) + +oc create route reencrypt openshell \ + --service=openshell --port=8080 \ + --dest-ca-cert=<(echo "$DEST_CA") \ + -n openshell + +oc annotate route openshell -n openshell \ + haproxy.router.openshift.io/backend-protocol=h2 --overwrite +``` + +Register the gateway with the CLI using the route hostname: + +```shell +ROUTE_HOST=$(oc get route openshell -n openshell -o jsonpath='{.spec.host}') +openshell gateway add https://${ROUTE_HOST} --gateway-insecure --name openshift +openshell status +``` + + +The reencrypt route requires a TLS-enabled gateway install. If you installed with `server.disableTls=true`, either reinstall with TLS enabled (see [OpenShift install](/kubernetes/openshift)) or use `oc port-forward` for local access. + + +## OpenShift: Gateway API with Istio + +The Kubernetes [Gateway API](https://gateway-api.sigs.k8s.io) provides native gRPC routing via `GRPCRoute` resources, without needing HTTP/2 annotations. On OpenShift, the Ingress Operator manages Gateway API CRDs, so Envoy Gateway cannot be installed directly. Instead, use the Istio-based Gateway controller that OpenShift provides. + + + +## Identify the GatewayClass + +```shell +oc get gatewayclass +``` + +Look for a class with an Istio-based controller (e.g. `istio` or `data-science-gateway-class`). The class must show `ACCEPTED: True`. + +## Create the Gateway and GRPCRoute + +```shell +kubectl apply -f - <<'EOF' +apiVersion: gateway.networking.k8s.io/v1 +kind: Gateway +metadata: + name: openshell-gateway + namespace: openshell +spec: + gatewayClassName: istio + listeners: + - name: grpc + protocol: HTTPS + port: 443 + tls: + mode: Terminate + certificateRefs: + - name: openshell-server-tls + kind: Secret + allowedRoutes: + namespaces: + from: Same +--- +apiVersion: gateway.networking.k8s.io/v1 +kind: GRPCRoute +metadata: + name: openshell + namespace: openshell +spec: + parentRefs: + - name: openshell-gateway + namespace: openshell + rules: + - backendRefs: + - name: openshell + port: 8080 +EOF +``` + +## Configure TLS to the backend + +The Istio envoy proxy terminates external TLS at the Gateway, then connects to the backend in plaintext by default. Since the OpenShell gateway expects TLS, create a `DestinationRule` for TLS origination: + +```shell +kubectl apply -f - <<'EOF' +apiVersion: networking.istio.io/v1 +kind: DestinationRule +metadata: + name: openshell-tls-origination + namespace: openshell +spec: + host: openshell.openshell.svc.cluster.local + trafficPolicy: + tls: + mode: SIMPLE + insecureSkipVerify: true +EOF +``` + +## Get the external address + +The Gateway controller creates a LoadBalancer service. Wait for the external address: + +```shell +oc get gateway openshell-gateway -n openshell \ + -o jsonpath='{.status.addresses[0].value}' +``` + +## Register the gateway + +```shell +openshell gateway add https:// --gateway-insecure --name openshift +openshell status +``` + + + ## SSH Relay Sandbox SSH uses the gateway endpoint registered with the CLI. No separate Helm SSH host or port values are required. diff --git a/docs/kubernetes/openshift.mdx b/docs/kubernetes/openshift.mdx index 7512eaa65e..ed95dee063 100644 --- a/docs/kubernetes/openshift.mdx +++ b/docs/kubernetes/openshift.mdx @@ -3,13 +3,13 @@ # SPDX-License-Identifier: Apache-2.0 title: "OpenShift" sidebar-title: "OpenShift" -description: "Install the OpenShell Helm chart on OpenShift, including the SCC binding and chart overrides required by OpenShift's Security Context Constraints." -keywords: "Generative AI, Cybersecurity, Kubernetes, OpenShift, SCC, Security Context Constraints, Helm, Gateway, Installation" +description: "Install the OpenShell Helm chart on OpenShift with SCC overrides, TLS options, OIDC authentication, and external access." +keywords: "Generative AI, Cybersecurity, Kubernetes, OpenShift, SCC, Security Context Constraints, Helm, Gateway, Installation, TLS, OIDC, Keycloak" position: 6 --- -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. +The OpenShift install path is experimental. It currently requires running sandbox pods under the `privileged` SCC. 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. @@ -47,6 +47,26 @@ 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 \ + --namespace openshell \ + --set podSecurityContext.fsGroup=null \ + --set securityContext.runAsUser=null +``` + +| Override | Reason | +|---|---| +| `podSecurityContext.fsGroup=null` / `securityContext.runAsUser=null` | Clear the chart's hardcoded UID and fsGroup so OpenShift's SCC admission can assign them. | + +TLS and the PKI init job remain enabled by default. The certgen hook generates a self-signed CA, server certificate, and client certificate for mTLS between the gateway and sandbox supervisors. + + + + ```shell helm install openshell oci://ghcr.io/nvidia/openshell/helm-chart \ --version \ @@ -61,6 +81,9 @@ helm install openshell oci://ghcr.io/nvidia/openshell/helm-chart \ | `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 @@ -74,7 +97,7 @@ If you set `workload.kind=deployment`, use ## Connect to the gateway -The gateway is now running over plaintext HTTP. Connect with `oc port-forward`: +For quick local evaluation, forward the gateway port to your workstation: ```shell oc -n openshell port-forward svc/openshell 8080:8080 @@ -82,13 +105,53 @@ oc -n openshell port-forward svc/openshell 8080:8080 Register the gateway with the CLI: + + + +```shell +openshell gateway add https://127.0.0.1:8080 --local --gateway-insecure --name openshift +openshell status +``` + + + + ```shell openshell gateway add http://127.0.0.1:8080 --local --name openshift openshell status ``` + + + ## Next Steps -- For TLS-enabled deployments, refer to [Managing Certificates](/kubernetes/managing-certificates). -- To expose the gateway externally, refer to [Ingress](/kubernetes/ingress). -- To configure OIDC authentication, refer to [Access Control](/kubernetes/access-control). +- To expose the gateway externally, refer to the [OpenShift ingress options](/kubernetes/ingress#openshift-reencrypt-route) (reencrypt Route or Gateway API with Istio). +- To configure OIDC authentication with Keycloak, refer to the [Keycloak setup](/kubernetes/access-control#keycloak-setup) section, then upgrade the Helm release with OIDC values: + + ```shell + helm upgrade openshell oci://ghcr.io/nvidia/openshell/helm-chart \ + --version \ + --namespace openshell \ + --reuse-values \ + --set server.oidc.issuer=https://keycloak.example.com/realms/openshell \ + --set server.oidc.audience=openshell-cli \ + --set server.oidc.rolesClaim=realm_access.roles \ + --set server.oidc.adminRole=openshell-admin \ + --set server.oidc.userRole=openshell-user + ``` + + Then register the gateway with `--oidc-issuer`: + + ```shell + openshell gateway add https:// \ + --gateway-insecure \ + --name openshift \ + --oidc-issuer https://keycloak.example.com/realms/openshell \ + --oidc-client-id openshell-cli \ + --oidc-audience openshell-cli + ``` + +- To federate OpenShift identities into Keycloak, refer to [OpenShift Identity Federation](/kubernetes/access-control#openshift-identity-federation-optional). +- For TLS certificate management with cert-manager, refer to [Managing Certificates](/kubernetes/managing-certificates). +- For the generic OIDC configuration reference, refer to [Access Control](/kubernetes/access-control).