# Kubeflow Dex & Keycloak Integration Guide In addition to the guidelines for GitHub, Google, Microsoft and other OIDC providers in https://github.com/kubeflow/manifests#dex and direct oauth2-proxy connection without DEX to typical OIDC IDP providers such as Azure in https://github.com/kubeflow/manifests/blob/master/common/oauth2-proxy/README.md#change-the-default-authentication-from-dex--oauth2-proxy-to-oauth2-proxy-only we try to roughly explain here how to configure Dex to use Keycloak as an external OpenID Connect provider for Kubeflow. > [Note] > ✅ Replace the domains of Keycloak and Kubeflow containing `example.com` with ones that are appropriately tailored to the actual situation. > ✅ If a realm already exists, there's no need to create one. > ✅ As you know, If the first attempt fails, you can just run it again. ## Configure Keycloak ### Create a Realm - Realm Name: `` ### Create a Client - Client Type: `OpenID Connect` - Client ID: `kubeflow-oidc-authservice` (⚠️ Never use a different value) - Client Authentication: `On` - Authentication Flow: Check `Standard flow` and `Direct access grants` - Root URL: `https://kubeflow.example.com` - Home URL: `https://kubeflow.example.com` - Valid Redirect URIs: `https://kubeflow.example.com/dex/callback` - Valid Post Logout Redirect URIs: `https://kubeflow.example.com/oauth2/sign_out` - Web Origins: `*` After creating the realm and client, note down the **Client Secret**(`YOUR_KEYCLOAK_CLIENT_SECRET`) which will be used in later steps. ## Update Dex Configuration Modify the Dex ConfigMap to connect to Keycloak. ```bash KEYCLOAK_ISSUER="https://keycloak.example.com/realms/" CLIENT_ID="kubeflow-oidc-authservice" CLIENT_SECRET="" REDIRECT_URI="https://kubeflow.example.com/dex/callback" DEX_ISSUER="https://kubeflow.example.com/dex" tee common/dex/overlays/oauth2-proxy/config-map.yaml <<- DEX_CONFIG apiVersion: v1 kind: ConfigMap metadata: name: dex data: config.yaml: | issuer: $DEX_ISSUER storage: type: kubernetes config: inCluster: true web: http: 0.0.0.0:5556 logger: level: "debug" format: text oauth2: skipApprovalScreen: true enablePasswordDB: false # staticPasswords: # - email: user@example.com # hashFromEnv: DEX_USER_PASSWORD # username: user # userID: "15841185641784" staticClients: - idEnv: OIDC_CLIENT_ID redirectURIs: ["/oauth2/callback"] name: 'Dex Login Application' secretEnv: OIDC_CLIENT_SECRET connectors: - type: oidc id: keycloak name: keycloak config: issuer: $KEYCLOAK_ISSUER clientID: $CLIENT_ID clientSecret: $CLIENT_SECRET redirectURI: $REDIRECT_URI insecure: false insecureSkipEmailVerified: true userNameKey: email scopes: - openid - profile - email - offline_access DEX_CONFIG kustomize build common/dex/overlays/oauth2-proxy | kubectl delete -f - kustomize build common/dex/overlays/oauth2-proxy | kubectl apply -f - ``` ## Update OAuth2 Proxy Configuration Configure the OAuth2 Proxy to use the newly configured Dex issuer. ```bash DEX_ISSUER="https://kubeflow.example.com/dex" tee common/oauth2-proxy/base/oauth2_proxy.cfg <<- OAUTH2_PROXY_CONFIG provider = "oidc" oidc_issuer_url = "$DEX_ISSUER" scope = "profile email offline_access openid" email_domains = "*" insecure_oidc_allow_unverified_email = "true" upstreams = [ "static://200" ] skip_auth_routes = [ "^/dex/", ] api_routes = [ "/api/", "/apis/", "^/ml_metadata", ] skip_oidc_discovery = true login_url = "/dex/auth" redeem_url = "http://dex.auth.svc.cluster.local:5556/dex/token" oidc_jwks_url = "http://dex.auth.svc.cluster.local:5556/dex/keys" skip_provider_button = false provider_display_name = "Dex" custom_sign_in_logo = "/custom-theme/kubeflow-logo.svg" banner = "-" footer = "-" prompt = "none" set_authorization_header = true set_xauthrequest = true cookie_name = "oauth2_proxy_kubeflow" cookie_expire = "24h" cookie_refresh = 0 code_challenge_method = "S256" redirect_url = "/oauth2/callback" relative_redirect_url = true OAUTH2_PROXY_CONFIG kustomize build common/oauth2-proxy/overlays/m2m-dex-only/ | kubectl delete -f - kustomize build common/oauth2-proxy/overlays/m2m-dex-only/ | kubectl apply -f - ``` ## Update Istio Request Authentication Adjust the Istio Request Authentication configuration to pass the correct JWT claims. ```bash DEX_ISSUER="https://kubeflow.example.com/dex" tee common/oauth2-proxy/components/istio-external-auth/requestauthentication.dex-jwt.yaml <<- ISTIO_REQUEST_AUTH_CONFIG apiVersion: security.istio.io/v1beta1 kind: RequestAuthentication metadata: name: dex-jwt namespace: istio-system spec: selector: matchLabels: app: istio-ingressgateway jwtRules: - issuer: $DEX_ISSUER forwardOriginalToken: true outputClaimToHeaders: - header: kubeflow-userid claim: email - header: kubeflow-groups claim: groups fromHeaders: - name: Authorization prefix: "Bearer " ISTIO_REQUEST_AUTH_CONFIG # For Kubeflow 1.91, change istio-1-24 to istio-1-22 kustomize build common/istio-1-24/istio-install/overlays/oauth2-proxy | kubectl delete -f - kustomize build common/istio-1-24/istio-install/overlays/oauth2-proxy | kubectl apply -f - kustomize build common/oauth2-proxy/overlays/m2m-dex-only/ | kubectl delete -f - kustomize build common/oauth2-proxy/overlays/m2m-dex-only/ | kubectl apply -f - ``` ## Final Checks - **Review Logs**: Make sure to tail the logs of the Dex, OAuth2 Proxy, and Istio ingress gateway deployments to verify that the configurations are working as expected. - **Test Authentication**: Try accessing your Kubeflow endpoint (ex. https://kubeflow.example.com) and verify that you’re redirected to Keycloak for authentication and that after login you are correctly returned to Kubeflow.