The UI for ROS OCP uses OAuth2 Proxy with Keycloak OIDC as a sidecar container to provide authentication. This allows users to authenticate using Keycloak credentials.
| Component | Purpose | Location | Port |
|---|---|---|---|
| OAuth2 Proxy | Authentication proxy with Keycloak OIDC | Sidecar in ui pod | 8443 (HTTPS) |
| Nginx | Web server and API proxy | Inside UI app container | 8080 (HTTP) |
| UI App | Frontend application | Main container in pod | 8080 (HTTP) |
| ROS API | Backend API service | Separate service | 8000 (HTTP) |
| Keycloak | OIDC provider for user authentication | External/separate service | 443 (HTTPS) |
| OpenShift Route | External access with TLS reencrypt | Route resource | 443 (HTTPS) |
| Service CA Operator | Auto-generates TLS certificates | Cluster-wide operator | N/A |
User → OpenShift Route → OAuth2 Proxy → Keycloak OIDC → Validate
↓ ↓
localhost:8080 ← Session Cookie ← Success
↓
Nginx
↓
┌─────────┴─────────┐
↓ ↓
UI App ROS API
(Static files) (API requests with Bearer token)
%%{init: {'flowchart': {'curve': 'stepAfter'}}}%%
flowchart LR
subgraph User["User"]
U["<b>Browser</b><br/>Session Cookie"]
end
subgraph Route["OpenShift Route"]
R["<b>Route: ui</b><br/>TLS: reencrypt<br/>Port: 443"]
end
subgraph Pod["Pod: ui"]
subgraph Proxy["OAuth Proxy Container"]
P["<b>OAuth Proxy</b><br/>Port: 8443"]
end
subgraph App["UI App Container"]
N["<b>Nginx</b><br/>Port: 8080<br/>• Serves static files<br/>• Proxies /api to ROS API<br/>• Adds Bearer token"]
end
P -->|7. localhost:8080<br/>Authorization: Bearer <token>| N
N -->|8. /api requests<br/>Authorization: Bearer <token>| ROSAPI
end
subgraph ROSAPI["ROS API Service"]
API["<b>ROS API</b><br/>Port: 8000<br/>Backend API"]
end
subgraph OAuth["Keycloak OIDC"]
O["<b>Keycloak</b><br/>OIDC Provider"]
end
U -->|1. HTTPS Request| R
R -->|2. Forward| P
P -.->|3. No cookie:<br/>Redirect| U
U -->|4. Login| O
O -.->|5. Callback| P
P -.->|6. Set cookie| U
N -->|7. Response| P
P -->|8. Response| U
ROSAPI -->|9. API Response| N
style P fill:#90caf9,stroke:#333,stroke-width:2px,color:#000
style N fill:#81c784,stroke:#333,stroke-width:2px,color:#000
style ROSAPI fill:#ffb74d,stroke:#333,stroke-width:2px,color:#000
style O fill:#f48fb1,stroke:#333,stroke-width:2px,color:#000
style U fill:#e1bee7,stroke:#333,stroke-width:2px,color:#000
style R fill:#ffcc80,stroke:#333,stroke-width:2px,color:#000
%%{init: {'theme':'base', 'themeVariables': {'primaryTextColor':'#000', 'secondaryTextColor':'#000', 'tertiaryTextColor':'#000', 'noteTextColor':'#000', 'activationBorderColor':'#000', 'activationBkgColor':'#fff'}}}%%
sequenceDiagram
autonumber
participant User as User Browser
participant Route as OpenShift Route
participant Proxy as OAuth Proxy<br/>(Sidecar)
participant OAuth as Keycloak OIDC
participant Nginx as Nginx<br/>(in UI App)
participant ROSAPI as ROS API
Note over User,ROSAPI: First Request (No Session)
User->>Route: HTTPS GET /<br/>(no cookie)
Route->>Proxy: Forward to port 8443
Proxy->>Proxy: Check for session cookie<br/>Cookie not found
Proxy-->>User: 302 Redirect<br/>Location: /oauth/authorize?...
User->>OAuth: GET /oauth2/authorize
OAuth->>User: Present login page
User->>OAuth: POST credentials
OAuth->>OAuth: Validate credentials
OAuth-->>User: 302 Redirect<br/>Location: /oauth2/callback?code=...
User->>Route: GET /oauth2/callback?code=...
Route->>Proxy: Forward callback
Proxy->>OAuth: Exchange code for token<br/>POST /oauth2/token
OAuth-->>Proxy: Access token + user info
Proxy->>Proxy: Validate token<br/>Create session
Proxy-->>User: 302 Redirect to /<br/>Set-Cookie: _oauth_proxy=...
User->>Route: GET /<br/>Cookie: _oauth_proxy=...
Route->>Proxy: Forward with cookie
Proxy->>Proxy: Validate session cookie<br/>Session valid
Proxy->>Nginx: GET / (localhost:8080)
Nginx->>Nginx: Serve static files<br/>(from filesystem)
Nginx-->>Proxy: HTML response
Proxy-->>User: 200 OK<br/>HTML content
Note over User,ROSAPI: API Request (With Session)
User->>Route: GET /api/recommendations<br/>Cookie: _oauth_proxy=...
Route->>Proxy: Forward with cookie
Proxy->>Proxy: Validate session cookie<br/>Session valid<br/>Extract access token
Proxy->>Nginx: GET /api/recommendations<br/>Authorization: Bearer <token>
Nginx->>Nginx: Match /api location<br/>Forward Authorization header
Nginx->>ROSAPI: GET /api/recommendations<br/>Authorization: Bearer <token>
ROSAPI->>ROSAPI: Validate Bearer token
ROSAPI-->>Nginx: 200 OK<br/>JSON response
Nginx-->>Proxy: JSON response
Proxy-->>User: 200 OK<br/>JSON content
Note over User,ROSAPI: Session Expired
User->>Route: GET /<br/>Cookie: _oauth_proxy=... (expired)
Route->>Proxy: Forward with expired cookie
Proxy->>Proxy: Validate session cookie<br/>Session expired
Proxy-->>User: 302 Redirect<br/>Location: /oauth2/authorize?...<br/>(repeat login flow)
- OAuth2 Proxy: Sidecar container (
quay.io/oauth2-proxy/oauth2-proxy) handling authentication with Keycloak OIDC - Nginx: Web server running inside the UI app container that serves static files and proxies API requests to ROS API
- UI App: Frontend application container serving the web interface with embedded Nginx
- ROS API: Backend API service that receives authenticated requests with Bearer tokens
- Keycloak: External OIDC provider for user authentication
- OpenShift Route: Exposes the service with TLS reencrypt termination
- Service CA Operator: Automatically generates and rotates TLS certificates
- ServiceAccount: Service account for the UI pods
# UI (OpenShift only - Keycloak OIDC protected frontend)
ui:
replicaCount: 1
oauthProxy:
image:
repository: quay.io/oauth2-proxy/oauth2-proxy
pullPolicy: IfNotPresent
tag: "v7.7.1"
resources:
limits:
cpu: "100m"
memory: "128Mi"
requests:
cpu: "50m"
memory: "64Mi"
keycloak:
# Keycloak issuer URL
issuerUrl: "https://keycloak.example.com/realms/master"
# OAuth2 client ID
clientId: "cost-management-ui"
# OAuth2 client secret (provide via --set or external secret)
clientSecretValue: ""
# Optional: Email domain restriction
emailDomain: ""
# Optional: Allowed roles
allowedRoles: []
# Optional: Allowed groups
allowedGroups: []
app:
image:
repository: quay.io/insights-onprem/koku-ui-mfe-on-prem
tag: "0.0.14"
pullPolicy: IfNotPresent
port: 8080
resources:
limits:
cpu: "100m"
memory: "128Mi"
requests:
cpu: "50m"
memory: "64Mi"The OAuth2 Proxy is configured with the following arguments:
args:
- --https-address=:8443 # Listen on HTTPS port 8443
- --provider=keycloak-oidc # Use Keycloak OIDC provider
- --client-id=<client-id> # Keycloak client ID
- --client-secret-file=/etc/proxy/secrets/client-secret # Client secret
- --cookie-secret-file=/etc/proxy/secrets/session-secret # Session encryption key
- --oidc-issuer-url=<keycloak-url>/realms/<realm> # Keycloak issuer URL
- --redirect-url=https://<route>/oauth2/callback # OAuth callback URL
- --tls-cert=/etc/tls/private/tls.crt # TLS certificate (auto-generated)
- --tls-key=/etc/tls/private/tls.key # TLS private key (auto-generated)
- --upstream=http://localhost:8080 # Forward to UI app on localhost
- --pass-host-header=false # Don't forward original Host header
- --skip-provider-button # Skip provider button
- --skip-auth-preflight # Skip OAuth consent screen
- --pass-authorization-header # Pass Authorization header with Bearer token to upstream
- --code-challenge-method=S256 # Use PKCE for enhanced securityOptional authorization arguments:
- --email-domain=<domain> # Restrict to specific email domain
- --allowed-role=<role> # Allow specific Keycloak role
- --allowed-group=<group> # Allow specific Keycloak groupYou need to create an OIDC client in Keycloak with the following configuration:
- Client Type: OpenID Connect
- Client ID:
cost-management-ui(or your custom value) - Client Authentication: Enabled (confidential client)
- Valid Redirect URIs:
https://<ui-route-host>/oauth2/callback - Web Origins:
https://<ui-route-host>
Required Mappers:
- Create an audience mapper to include the client ID in the
audclaim
Optional Configuration:
- Configure role mappings if using
--allowed-role - Configure group mappings if using
--allowed-group - Set email domain requirements in Keycloak if needed
The Service CA Operator automatically generates TLS certificates via annotation:
apiVersion: v1
kind: Service
metadata:
name: <fullname>-ui
annotations:
service.beta.openshift.io/serving-cert-secret-name: <fullname>-ui-tls
spec:
ports:
- port: 8443
targetPort: https
name: httpsThe operator:
- Watches for the
serving-cert-secret-nameannotation - Generates a TLS certificate signed by the cluster CA
- Creates a Secret with
tls.crtandtls.key - Automatically rotates certificates before expiry
The cookie secret is persisted across Helm upgrades using the lookup function:
{{- $secret := (lookup "v1" "Secret" .Release.Namespace (printf "%s-ui-cookie-secret" (include "cost-onprem.fullname" .))) -}}
apiVersion: v1
kind: Secret
metadata:
name: {{ include "cost-onprem.fullname" . }}-ui-cookie-secret
type: Opaque
data:
session-secret: {{ if $secret }}{{ index $secret.data "session-secret" }}{{ else }}{{ randAlphaNum 32 | b64enc }}{{ end }}This ensures:
- ✅ Users remain logged in across Helm upgrades
- ✅ Existing sessions are not invalidated
- ✅ New secret is only generated on initial install or manual deletion
Nginx runs inside the UI app container and serves two primary functions:
- Static File Serving: Serves the React application's static files (HTML, CSS, JavaScript)
- API Proxy: Proxies API requests to the ROS API backend with Bearer token authentication
The Nginx configuration is embedded in the UI application container. Key configuration:
# API proxy location - forwards to ROS API
location /api {
proxy_pass ${API_PROXY_URL};
# OAuth2 proxy passes Authorization header with Bearer token via --pass-authorization-header
# Nginx forwards the Authorization header directly to ROS API
proxy_set_header Authorization $http_authorization;
# Standard proxy headers
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
# HTTP/1.1 connection settings
proxy_http_version 1.1;
proxy_set_header Connection "";
}
# Static file serving - React application
location / {
try_files $uri $uri/ /index.html;
}-
API Proxy URL:
- Set via
API_PROXY_URLenvironment variable - Points to ROS API service:
http://<fullname>-ros-api:8000 - Configured in Helm values:
ui.app.env.API_PROXY_URL
- Set via
-
Token Passing:
- OAuth2 proxy uses
--pass-authorization-headerflag to pass theAuthorizationheader with Bearer token - OAuth2 proxy sets
Authorization: Bearer <token>header when forwarding requests to upstream (Nginx) - Nginx explicitly forwards the
Authorizationheader to ROS API usingproxy_set_header Authorization $http_authorization - ROS API validates the Bearer token for authentication
- OAuth2 proxy uses
-
Static File Serving:
- All non-API requests (
/) are served as static files try_filesdirective enables client-side routing (React Router)- Falls back to
index.htmlfor all routes (SPA behavior)
- All non-API requests (
Static File Request:
User → Route → OAuth Proxy → Nginx → Serve /index.html → Response
API Request:
User → Route → OAuth2 Proxy (adds Authorization: Bearer <token> via --pass-authorization-header)
→ Nginx (forwards Authorization header)
→ ROS API (validates Bearer token)
→ Response
The API_PROXY_URL environment variable is set in the UI app container:
env:
- name: API_PROXY_URL
value: "http://{{ include "cost-onprem.fullname" . }}-ros-api:{{ .Values.ros.api.port }}"This ensures Nginx knows where to proxy API requests.
- Deployed on OpenShift cluster (UI is OpenShift-only)
- Keycloak OIDC server is accessible and configured
- User has valid Keycloak credentials
# Get the UI route URL
UI_ROUTE=$(oc get route -n cost-onprem -l app.kubernetes.io/component=ui -o jsonpath='{.items[0].spec.host}')
# Access in browser
echo "https://$UI_ROUTE"
# Or test with curl (will get redirect)
curl -v "https://$UI_ROUTE"
# Expected: 302 redirect to /oauth2/authorize# Check route exists
oc get route -n cost-onprem -l app.kubernetes.io/component=ui
# Verify TLS reencrypt termination
oc get route -n cost-onprem -l app.kubernetes.io/component=ui -o jsonpath='{.items[0].spec.tls.termination}'
# Expected: reencrypt
# Check route target
oc describe route -n cost-onprem -l app.kubernetes.io/component=ui# Check if TLS secret exists
oc get secret -n cost-onprem -l app.kubernetes.io/component=ui | grep tls
# View certificate details
oc get secret <fullname>-ui-tls -n cost-onprem -o jsonpath='{.data.tls\.crt}' | base64 -d | openssl x509 -text -noout
# Verify certificate is valid
oc get secret <fullname>-ui-tls -n cost-onprem -o jsonpath='{.data.tls\.crt}' | base64 -d | openssl x509 -noout -dates# Check OAuth proxy health endpoint
oc exec -n cost-onprem -l app.kubernetes.io/component=ui -c oauth-proxy -- \
curl -k https://localhost:8443/oauth/healthz
# Expected: "OK"
# Check OAuth proxy logs
oc logs -n cost-onprem -l app.kubernetes.io/component=ui -c oauth-proxy --tail=50
# Look for:
# - "Listening on :8443"
# - "HTTP: serving on :8443"# Check UI app health
oc exec -n cost-onprem -l app.kubernetes.io/component=ui -c app -- \
curl http://localhost:8080/
# Expected: HTML response
# Check UI app logs
oc logs -n cost-onprem -l app.kubernetes.io/component=ui -c app --tail=50# Check API_PROXY_URL environment variable
oc exec -n cost-onprem -l app.kubernetes.io/component=ui -c app -- \
env | grep API_PROXY_URL
# Expected: API_PROXY_URL=http://<fullname>-ros-api:8000
# Test Nginx API proxy (requires authenticated session)
# First, get a valid session cookie, then:
oc exec -n cost-onprem -l app.kubernetes.io/component=ui -c app -- \
curl -H "Authorization: Bearer <test-token>" \
http://localhost:8080/api/status
# Check Nginx logs (if available in container)
oc logs -n cost-onprem -l app.kubernetes.io/component=ui -c app --tail=50 | grep nginx
# Verify ROS API is accessible from UI pod
oc exec -n cost-onprem -l app.kubernetes.io/component=ui -c app -- \
curl http://<fullname>-ros-api:8000/status# 1. Open browser in private/incognito mode
# 2. Navigate to https://<ui-route>
# 3. Should redirect to Keycloak login
# 4. Enter Keycloak credentials
# 5. Should redirect back to UI
# 6. UI should load successfully
# Test session persistence
# 7. Refresh page - should not require login
# 8. Close browser and reopen
# 9. Navigate to https://<ui-route>
# 10. Should still be logged in (if session not expired)Symptom: Continuously redirected between UI and Keycloak OAuth
Diagnosis:
# Check ServiceAccount annotation
oc get serviceaccount -n cost-onprem -l app.kubernetes.io/component=ui -o yaml | grep oauth-redirectreference
# Verify route name matches annotation
oc get route -n cost-onprem -l app.kubernetes.io/component=ui -o jsonpath='{.items[0].metadata.name}'Solution:
# Reinstall to fix annotation mismatch
helm upgrade cost-onprem ./cost-onprem -n cost-onprem --forceSymptom: Pod fails to start with "tls.crt: no such file"
Diagnosis:
# Check if Service CA Operator is running
oc get pods -n openshift-service-ca -l app=service-ca
# Check service annotation
oc get service -n cost-onprem -l app.kubernetes.io/component=ui -o yaml | grep serving-cert-secret-name
# Check if secret was created
oc get secret -n cost-onprem | grep ui-tlsSolution:
# Wait for certificate generation (usually < 30 seconds)
oc wait --for=condition=ready secret/<fullname>-ui-tls -n cost-onprem --timeout=60s
# If timeout, check Service CA logs
oc logs -n openshift-service-ca -l app=service-ca
# Restart pod to pick up certificate
oc rollout restart deployment -n cost-onprem -l app.kubernetes.io/component=uiSymptom: OAuth proxy fails to start with error:
ERROR: Failed to initialise OAuth2 Proxy: tls: failed to verify certificate: x509: certificate signed by unknown authority
Cause: The keycloak-ca-cert secret is missing or contains wrong CA certificate.
Solution:
# Extract the cluster CA from OpenShift ingress operator
oc get secret router-ca -n openshift-ingress-operator -o jsonpath='{.data.tls\.crt}' | base64 -d > ca.crt
# Create secret in deployment namespace
kubectl create secret generic keycloak-ca-cert --from-file=ca.crt=./ca.crt -n cost-onpremNote: The install-helm-chart.sh script creates this secret automatically when ui.oauthProxy.tls.caCertEnabled=true. The secret must be created in the cost-onprem namespace, which is why it's handled by the Helm installation script rather than deploy-rhbk.sh.
Symptom: Users logged out frequently
Diagnosis:
# Check cookie secret age
oc get secret -n cost-onprem <fullname>-ui-cookie-secret -o jsonpath='{.metadata.creationTimestamp}'
# Check if secret changed recently
oc describe secret -n cost-onprem <fullname>-ui-cookie-secretSolution:
# Cookie secret should persist across Helm upgrades
# If manually deleted, users will need to re-login
# Verify lookup function is working
helm get manifest cost-onprem -n cost-onprem | grep -A 5 "lookup.*Secret"Symptom: Route returns 503 error
Diagnosis:
# Check pod status
oc get pods -n cost-onprem -l app.kubernetes.io/component=ui
# Check service endpoints
oc get endpoints -n cost-onprem -l app.kubernetes.io/component=ui
# Check route backend
oc describe route -n cost-onprem -l app.kubernetes.io/component=uiSolution:
# Ensure pod is ready
oc wait --for=condition=ready pod -n cost-onprem -l app.kubernetes.io/component=ui --timeout=60s
# Check readiness probe
oc describe pod -n cost-onprem -l app.kubernetes.io/component=ui | grep -A 5 Readiness
# View probe logs
oc logs -n cost-onprem -l app.kubernetes.io/component=ui -c oauth-proxy | grep healthzSymptom: OAuth proxy container constantly restarting
Diagnosis:
# Check pod events
oc describe pod -n cost-onprem -l app.kubernetes.io/component=ui
# Check oauth-proxy logs
oc logs -n cost-onprem -l app.kubernetes.io/component=ui -c oauth-proxy --previous
# Check resource limits
oc get pod -n cost-onprem -l app.kubernetes.io/component=ui -o jsonpath='{.spec.containers[?(@.name=="oauth-proxy")].resources}'Solution:
# Increase memory if OOMKilled
helm upgrade cost-onprem ./cost-onprem -n cost-onprem \
--set ui.oauth-proxy.resources.limits.memory=256Mi \
--set ui.oauth-proxy.resources.requests.memory=128MiSymptom: OAuth works but app returns errors
Diagnosis:
# Check app logs
oc logs -n cost-onprem -l app.kubernetes.io/component=ui -c app
# Test app directly
oc exec -n cost-onprem -l app.kubernetes.io/component=ui -c app -- \
curl http://localhost:8080/
# Check app port configuration
oc get deployment -n cost-onprem -l app.kubernetes.io/component=ui -o jsonpath='{.spec.template.spec.containers[?(@.name=="app")].ports[0].containerPort}'Solution:
# Verify app port matches configuration
helm upgrade cost-onprem ./cost-onprem -n cost-onprem --set ui.app.port=8080
# Increase app resources if needed
helm upgrade cost-onprem ./cost-onprem -n cost-onprem \
--set ui.app.resources.limits.memory=512MiSymptom: UI loads but API requests return authentication errors
Diagnosis:
# Check if API_PROXY_URL is set correctly
oc exec -n cost-onprem -l app.kubernetes.io/component=ui -c app -- \
env | grep API_PROXY_URL
# Verify ROS API service is accessible
oc get svc -n cost-onprem -l app.kubernetes.io/component=ros-api
# Test ROS API directly
oc exec -n cost-onprem -l app.kubernetes.io/component=ui -c app -- \
curl http://<fullname>-ros-api:8000/status
# Check if OAuth2 proxy is passing Authorization header
oc logs -n cost-onprem -l app.kubernetes.io/component=ui -c oauth-proxy | grep -i "authorization\|pass-authorization"Solution:
# Verify API_PROXY_URL environment variable
# Should be: http://<fullname>-ros-api:8000
oc get deployment -n cost-onprem -l app.kubernetes.io/component=ui -o yaml | \
grep -A 2 API_PROXY_URL
# Ensure OAuth2 proxy is configured with --pass-authorization-header flag
# This flag enables Authorization header to be passed to upstream
oc get deployment -n cost-onprem -l app.kubernetes.io/component=ui -o yaml | \
grep -i "pass-authorization-header"
# If missing, update Helm values to include:
# ui.oauthProxy.args: ["--pass-authorization-header"]
helm upgrade cost-onprem ./cost-onprem -n cost-onprem \
--set ui.oauthProxy.passAuthorizationHeader=trueSymptom: API requests fail with proxy errors or connection refused
Diagnosis:
# Check Nginx configuration (if accessible)
oc exec -n cost-onprem -l app.kubernetes.io/component=ui -c app -- \
cat /etc/nginx/nginx.conf 2>/dev/null || echo "Nginx config not accessible"
# Test ROS API connectivity from UI pod
oc exec -n cost-onprem -l app.kubernetes.io/component=ui -c app -- \
curl -v http://<fullname>-ros-api:8000/status
# Check DNS resolution
oc exec -n cost-onprem -l app.kubernetes.io/component=ui -c app -- \
nslookup <fullname>-ros-api || getent hosts <fullname>-ros-apiSolution:
# Verify ROS API service name matches API_PROXY_URL
# Service name format: <fullname>-ros-api
# API_PROXY_URL should be: http://<fullname>-ros-api:8000
# Check service exists and is accessible
oc get svc -n cost-onprem <fullname>-ros-api
# Verify ROS API pod is running
oc get pods -n cost-onprem -l app.kubernetes.io/component=ros-api
# Test network connectivity
oc exec -n cost-onprem -l app.kubernetes.io/component=ui -c app -- \
nc -zv <fullname>-ros-api 8000-
TLS Encryption
- Route uses TLS reencrypt termination
- Traffic is encrypted from user browser to route (TLS)
- Traffic is re-encrypted from route to OAuth proxy (TLS)
- Traffic from OAuth proxy to app is unencrypted (localhost only)
-
Session Management
- Sessions stored in encrypted cookies (
_oauth_proxy) - Cookie secret is 32 random alphanumeric characters
- Secret persists across Helm upgrades to maintain sessions
- Sessions have TTL and expire after inactivity
- Sessions stored in encrypted cookies (
-
Authentication
- Uses Keycloak OIDC provider
- Keycloak handles user authentication and authorization
- User credentials never touch the OAuth proxy
- OAuth proxy only receives validated tokens from Keycloak
-
Network Isolation
- UI app only accessible via OAuth proxy (no direct external access)
- OAuth proxy and Nginx communicate over pod-local
localhost - Nginx proxies API requests to ROS API within the cluster
- Only OpenShift Route can access OAuth proxy externally
- API requests are authenticated with Bearer tokens passed from OAuth proxy
-
ServiceAccount Permissions
- ServiceAccount has no special RBAC permissions
- OAuth redirect enabled via annotation only
- No cluster-wide permissions needed
-
Automatic Certificate Rotation
- TLS certificates auto-renewed by Service CA Operator
- No manual certificate management required
- Certificates signed by cluster CA
-
Health Check Bypass
/oauth/healthzendpoint does not require authentication- Required for Kubernetes liveness/readiness probes
- Only accessible from within the cluster
When deploying the chart, you must provide the Keycloak configuration:
helm install cost-onprem ./cost-onprem \
--set ui.oauthProxy.keycloak.issuerUrl="https://keycloak.example.com/realms/master" \
--set ui.oauthProxy.keycloak.clientId="cost-management-ui" \
--set ui.oauthProxy.keycloak.clientSecretValue="<your-client-secret>"Instead of passing the client secret via --set, you can create a secret manually:
kubectl create secret generic <fullname>-ui-oauth-client-secret \
--from-literal=client-secret=<your-client-secret> \
-n <namespace>Then deploy without the clientSecretValue:
helm install cost-onprem ./cost-onprem \
--set ui.oauthProxy.keycloak.issuerUrl="https://keycloak.example.com/realms/master" \
--set ui.oauthProxy.keycloak.clientId="cost-management-ui"Restrict access by roles or groups:
helm install cost-onprem ./cost-onprem \
--set ui.oauthProxy.keycloak.issuerUrl="https://keycloak.example.com/realms/master" \
--set ui.oauthProxy.keycloak.clientId="cost-management-ui" \
--set ui.oauthProxy.keycloak.clientSecretValue="<secret>" \
--set ui.oauthProxy.keycloak.allowedRoles[0]="cost-admin" \
--set ui.oauthProxy.keycloak.emailDomain="example.com"OpenShift Only: This UI authentication method is exclusively for OpenShift clusters because it depends on:
- ✅ OpenShift Routes with TLS reencrypt
- ✅ Service CA Operator for automatic certificate generation
- ✅ Keycloak OIDC instance (RHBK or community Keycloak) for user authentication
UI Deployment: The UI is deployed when enabled in the values:
{{- if .Values.ui }}
# UI resources deployed here
{{- end }}- OAuth2 Proxy
- OAuth2 Proxy Keycloak OIDC Provider
- Keycloak Documentation
- Service CA Operator
- OpenShift Routes
- Configuration Guide - Complete UI configuration reference with authentication flow diagrams
- Troubleshooting Guide - UI-specific troubleshooting procedures
- Platform Guide - Platform-specific requirements and differences