OpenShift SSO
OGO includes an auth-bridge sidecar that translates OpenShift OAuth tokens into standard OIDC JWTs. This lets users log in to the OpenShell gateway with their OpenShift credentials - no external identity provider needed.
How it works
Browser ──▶ OpenShift OAuth ──▶ auth-bridge ──▶ JWT
│
CLI ◀── stores JWT ◀────────────────┘
- User runs
openshell gateway login - Browser opens the auth-bridge's
/authorizeendpoint - Auth-bridge redirects to OpenShift OAuth login
- User authenticates with OpenShift credentials
- OAuth redirects back to auth-bridge with an authorization code
- Auth-bridge exchanges the code for an OpenShift token
- Auth-bridge calls the OpenShift UserInfo API to get identity
- Auth-bridge mints a short-lived JWT with the user's identity
- CLI receives and stores the JWT
Configuration
Auth-bridge is enabled by default on OpenShift. Control it via the CR:
spec:
auth:
openshift:
enabled: true # default on OpenShift, false on vanilla K8s
userGroup: openshell-users # required - only members can authenticate
adminGroup: openshell-admins # OpenShift group for admin role
tokenTTL: "8h" # JWT lifetime
Browser SSO requires spec.route.enabled to be true and an explicit
spec.route.hostname. The OAuth callback uses the derived public auth Route
hostname. With routing disabled, use the internal HTTPS Service for headless
token exchange instead; no browser callback is available.
If browser SSO and routing are both enabled but spec.route.hostname is
left empty, OGO does not create the auth Route or OAuthClient — a
router-assigned host isn't known until after the Route exists, so there is
nothing stable to register as the OAuth redirect URI. The gateway reports
Available: False with a BrowserSSOReady condition
(Reason: HostnameMissing) instead of silently registering a redirect that
can't work. Headless (mTLS/ServiceAccount) access is unaffected either way.
User group (required)
The userGroup field specifies the OpenShift group required for SSO access.
Only members of this group can authenticate via the browser login flow.
Users not in the group are rejected with a 403 error at login.
Set up the group and add users:
oc adm groups new openshell-users
oc adm groups add-users openshell-users alice bob
This check does not affect mTLS authentication (used by CI/automation) or the internal sandbox bootstrap (K8s ServiceAccount tokens).
Internal workload clients
When gateway TLS is enabled, the auth bridge exposes HTTPS through the
Gateway Service on port 8085. OGO publishes the verification certificate
without private keys in the <gateway>-auth-ca ConfigMap in the gateway
namespace.
This ConfigMap is specific to the auth bridge's HTTPS listener. Clients
connecting to the gateway's own gRPC listener with an OIDC or bearer token
instead should use <gateway>-gateway-ca — see
TLS trust for OIDC and bearer-token clients.
A workload in that namespace can mount the ConfigMap directly. For a
workload in another namespace, copy only ca.crt into a ConfigMap in the
workload namespace and mount that copy. The client can then verify the
internal endpoint without access to a TLS Secret:
curl --cacert /path/to/ca.crt \
https://<gateway>.<gateway-namespace>.svc:8085/healthz
The co-located OpenShell gateway continues to use the loopback-only HTTP listener for OIDC discovery. Plain HTTP is not exposed through the Service when TLS is enabled.
If spec.tls.serverCertSecretName supplies a custom server certificate, its
SANs must cover <gateway>.<gateway-namespace>.svc and, when browser SSO is
used, the derived auth Route hostname. The Secret should include ca.crt; OGO
falls back to publishing the public tls.crt when it is absent.
The auth bridge validates the certificate and key before serving, then caches the last valid pair for five minutes. It reloads rotated files after the cache expires. If the files are temporarily inconsistent during rotation, the bridge continues serving the last valid pair until it can load the replacement.
Troubleshooting
Gateway reports BrowserSSOReady: False, Reason: HostnameMissing
Browser SSO (spec.auth.openshift.enabled) and routing are both enabled,
but spec.route.hostname is empty. Set it explicitly:
spec:
route:
hostname: openshell.apps.example.com
Or, for headless-only access (mTLS/ServiceAccount, no browser login), disable browser SSO instead:
spec:
auth:
openshift:
enabled: false
"access denied: you are not a member of the required OpenShift group"
Your OpenShift user is not in the group specified by spec.auth.openshift.userGroup.
Ask your cluster admin to add you:
oc adm groups add-users <group-name> <your-username>
Check your current groups:
oc get users <your-username> -o jsonpath='{.groups}'
Emergency token revocation
To immediately invalidate all active tokens (e.g., a user who should not have access obtained a valid token):
oc delete secret openshell-auth-bridge-keys -n ogo
oc delete pod -n ogo -l app.kubernetes.io/name=openshell
The operator generates new signing keys and the gateway restarts with
fresh JWKS. All existing tokens become invalid within ~30 seconds.
Every user must re-login, and users removed from the userGroup will
be blocked.
For less disruptive revocation, reduce spec.auth.openshift.tokenTTL
to a shorter duration (e.g., "30m"). Revoked users lose access when
their token expires naturally.
"authentication failed" after gateway restart
As of v0.2.1, the operator syncs the OAuthClient secret and redirect URI
on every reconcile, so this self-heals within one reconcile loop (a few
seconds) — no manual action needed.
On older versions, or if the issue persists, force it by deleting both and letting the operator recreate them:
oc delete secret openshell-oauth-client -n ogo
oc delete oauthclient openshell
# Wait 60s for the operator to reconcile, then restart the gateway pod
oc delete pod -n ogo -l app.kubernetes.io/name=openshell
Token lifetime
The tokenTTL field controls how long the JWT is valid. After expiry, the
CLI prompts for re-authentication. The default is 8 hours.
The auth-bridge Ed25519 signing keypair is persisted in a Kubernetes Secret
(openshell-auth-bridge-keys). Tokens survive pod restarts. The operator
creates the keypair once; subsequent pod restarts load the same keys.
OAuthClient
The operator creates an OpenShift OAuthClient CR named openshell with
the auth-bridge route as the redirect URI. The client secret is stored in
the openshell-oauth-client Secret.
If you delete and recreate the OAuthClient, also delete the
openshell-oauth-client Secret to avoid a client secret mismatch.
Admin role
Users in the OpenShift group specified by spec.auth.openshift.adminGroup
are granted the openshell-admin role in the gateway. This role allows
managing other users' sandboxes and viewing all sandbox logs.