diff --git a/docs/KEYCLOAK_OIDC_SETUP.md b/docs/KEYCLOAK_OIDC_SETUP.md new file mode 100644 index 00000000..2d6d53c6 --- /dev/null +++ b/docs/KEYCLOAK_OIDC_SETUP.md @@ -0,0 +1,217 @@ +# Keycloak OIDC Setup for Kubernetes MCP Server + +This guide shows you how to set up a local development environment with Keycloak for OIDC authentication testing. + +## Overview + +The local development environment includes: +- **Kind cluster** with OIDC-enabled API server +- **Keycloak** (deployed in the cluster) for OIDC provider +- **Kubernetes MCP Server** configured for OAuth/OIDC authentication + +## Quick Start + +Set up the complete environment with one command: + +```bash +make local-env-setup +``` + +This will: +1. Install required tools (kind) to `./_output/bin/` +2. Create a Kind cluster with OIDC configuration +3. Deploy Keycloak in the cluster +4. Configure Keycloak realm and clients +5. Build the MCP server binary +6. Generate a configuration file at `_output/config.toml` + +## Running the MCP Server + +After setup completes, run the server: + +```bash +# Start the server +./kubernetes-mcp-server --port 8008 --config _output/config.toml +``` + +Or use the MCP Inspector for testing: + +```bash +npx @modelcontextprotocol/inspector@latest $(pwd)/kubernetes-mcp-server --config _output/config.toml +``` + +## Quick Walkthrough + +### 1. Start MCP Inspector and Connect + +After running the inspector, in the `Authentication`'s **OAuth 2.0 Flow** set the `Client ID` to be `mcp-client` and the `Scope` to `mcp-server`, afterwards click the "Connect" button. + + + MCP Inspector Connect Button + + +### 2. Login to Keycloak + +You'll be redirected to Keycloak. Enter the test credentials: +- Username: `mcp` +- Password: `mcp` + + + Keycloak Login Page + + +### 3. Use MCP Tools + +After authentication, you can use the **Tools** from the Kubernetes-MCP-Server from the MCP Inspector, like below where we run the `pods_list` tool, to list all pods in the current cluster from all namespaces. + + + MCP Inspector Tool Results + + +## Architecture + +### Keycloak Deployment +- Runs as a StatefulSet in the `keycloak` namespace +- Exposed via Ingress with TLS at `https://keycloak.127-0-0-1.sslip.io:8443` +- Uses cert-manager for TLS certificates +- Accessible from both host and cluster pods + +### Kind Cluster with OIDC +- Kubernetes API server configured with OIDC authentication +- Points to Keycloak's `openshift` realm as the OIDC issuer +- Validates bearer tokens against Keycloak's JWKS endpoint +- API server trusts the cert-manager CA certificate + +### Authentication Flow + +``` +User Browser + | + | 1. OAuth login (https://keycloak.127-0-0-1.sslip.io:8443) + v +Keycloak + | + | 2. ID Token (aud: mcp-server) + v +MCP Server + | + | 3. Token Exchange (aud: openshift) + v +Keycloak + | + | 4. Exchanged Access Token + v +MCP Server + | + | 5. Bearer Token in API request + v +Kubernetes API Server + | + | 6. Validate token via OIDC + v +Keycloak JWKS + | + | 7. Token valid, execute tool + v +MCP Server → User +``` + +## Keycloak Configuration + +The setup automatically configures: + +### Realm: `openshift` +- Token lifespan: 30 minutes +- Session idle timeout: 30 minutes + +### Clients + +1. **mcp-client** (public) + - Public client for browser-based OAuth login + - PKCE required for security + - Valid redirect URIs: `*` + +2. **mcp-server** (confidential) + - Confidential client with client secret + - Standard token exchange enabled + - Can exchange tokens with `aud: openshift` + - Default scopes: `openid`, `groups`, `mcp-server` + - Optional scopes: `mcp:openshift` + +3. **openshift** (confidential) + - Target client for token exchange + - Accepts exchanged tokens from `mcp-server` + - Used by Kubernetes API server for OIDC validation + +### Client Scopes +- **mcp-server**: Default scope with audience mapper +- **mcp:openshift**: Optional scope for token exchange with audience mapper +- **groups**: Group membership mapper (included in tokens) + +### Default User +- **Username**: `mcp` +- **Password**: `mcp` +- **Email**: `mcp@example.com` +- **RBAC**: `cluster-admin` (full cluster access) + +## MCP Server Configuration + +The generated `_output/config.toml` includes: + +```toml +require_oauth = true +oauth_audience = "mcp-server" +oauth_scopes = ["openid", "mcp-server"] +validate_token = false # Validation done by K8s API server +authorization_url = "https://keycloak.127-0-0-1.sslip.io:8443/realms/openshift" + +sts_client_id = "mcp-server" +sts_client_secret = "..." # Auto-generated +sts_audience = "openshift" # Triggers token exchange +sts_scopes = ["mcp:openshift"] + +certificate_authority = "_output/cert-manager-ca/ca.crt" # For HTTPS validation +``` + +## Useful Commands + +### Check Keycloak Status + +```bash +make keycloak-status +``` + +Shows: +- Keycloak pod status +- Service endpoints +- Access URL +- Admin credentials + +### View Keycloak Logs + +```bash +make keycloak-logs +``` + +### Access Keycloak Admin Console + +Open your browser to: +``` +https://keycloak.127-0-0-1.sslip.io:8443 +``` + +**Admin credentials:** +- Username: `admin` +- Password: `admin` + +Navigate to the `openshift` realm to view/modify the configuration. + +## Teardown + +Remove the local environment: + +```bash +make local-env-teardown +``` + +This deletes the Kind cluster (Keycloak is removed with it). diff --git a/docs/README.md b/docs/README.md index b6918bff..0eaa634e 100644 --- a/docs/README.md +++ b/docs/README.md @@ -18,4 +18,5 @@ Choose the guide that matches your needs: ## Additional Documentation +- **[Keycloak OIDC Setup](KEYCLOAK_OIDC_SETUP.md)** - Developer guide for local Keycloak environment and testing with MCP Inspector - **[Main README](../README.md)** - Project overview and general information diff --git a/docs/images/keycloak-login-page.png b/docs/images/keycloak-login-page.png new file mode 100644 index 00000000..2e35d403 Binary files /dev/null and b/docs/images/keycloak-login-page.png differ diff --git a/docs/images/keycloak-mcp-inspector-connect.png b/docs/images/keycloak-mcp-inspector-connect.png new file mode 100644 index 00000000..4a582d1c Binary files /dev/null and b/docs/images/keycloak-mcp-inspector-connect.png differ diff --git a/docs/images/keycloak-mcp-inspector-results.png b/docs/images/keycloak-mcp-inspector-results.png new file mode 100644 index 00000000..7498e4d1 Binary files /dev/null and b/docs/images/keycloak-mcp-inspector-results.png differ