| 
 | 1 | +# Keycloak OIDC Setup for Kubernetes MCP Server  | 
 | 2 | + | 
 | 3 | +This guide shows you how to set up a local development environment with Keycloak for OIDC authentication testing.  | 
 | 4 | + | 
 | 5 | +## Overview  | 
 | 6 | + | 
 | 7 | +The local development environment includes:  | 
 | 8 | +- **Kind cluster** with OIDC-enabled API server  | 
 | 9 | +- **Keycloak** (deployed in the cluster) for OIDC provider  | 
 | 10 | +- **Kubernetes MCP Server** configured for OAuth/OIDC authentication  | 
 | 11 | + | 
 | 12 | +## Quick Start  | 
 | 13 | + | 
 | 14 | +Set up the complete environment with one command:  | 
 | 15 | + | 
 | 16 | +```bash  | 
 | 17 | +make local-env-setup  | 
 | 18 | +```  | 
 | 19 | + | 
 | 20 | +This will:  | 
 | 21 | +1. Install required tools (kind) to `./_output/bin/`  | 
 | 22 | +2. Create a Kind cluster with OIDC configuration  | 
 | 23 | +3. Deploy Keycloak in the cluster  | 
 | 24 | +4. Configure Keycloak realm and clients  | 
 | 25 | +5. Build the MCP server binary  | 
 | 26 | +6. Generate a configuration file at `_output/config.toml`  | 
 | 27 | + | 
 | 28 | +## Running the MCP Server  | 
 | 29 | + | 
 | 30 | +After setup completes, run the server:  | 
 | 31 | + | 
 | 32 | +```bash  | 
 | 33 | +# Start the server  | 
 | 34 | +./kubernetes-mcp-server --port 8008 --config _output/config.toml  | 
 | 35 | +```  | 
 | 36 | + | 
 | 37 | +Or use the MCP Inspector for testing:  | 
 | 38 | + | 
 | 39 | +```bash  | 
 | 40 | +npx @modelcontextprotocol/inspector@latest $(pwd)/kubernetes-mcp-server --config _output/config.toml  | 
 | 41 | +```  | 
 | 42 | + | 
 | 43 | +## Quick Walkthrough  | 
 | 44 | + | 
 | 45 | +### 1. Start MCP Inspector and Connect  | 
 | 46 | + | 
 | 47 | +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.  | 
 | 48 | + | 
 | 49 | +<a href="images/keycloak-mcp-inspector-connect.png">  | 
 | 50 | +  <img src="images/keycloak-mcp-inspector-connect.png" alt="MCP Inspector Connect Button" width="600" />  | 
 | 51 | +</a>  | 
 | 52 | + | 
 | 53 | +### 2. Login to Keycloak  | 
 | 54 | + | 
 | 55 | +You'll be redirected to Keycloak. Enter the test credentials:  | 
 | 56 | +- Username: `mcp`  | 
 | 57 | +- Password: `mcp`  | 
 | 58 | + | 
 | 59 | +<a href="images/keycloak-login-page.png">  | 
 | 60 | +  <img src="images/keycloak-login-page.png" alt="Keycloak Login Page" width="600" />  | 
 | 61 | +</a>  | 
 | 62 | + | 
 | 63 | +### 3. Use MCP Tools  | 
 | 64 | + | 
 | 65 | +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.  | 
 | 66 | + | 
 | 67 | +<a href="images/keycloak-mcp-inspector-results.png">  | 
 | 68 | +  <img src="images/keycloak-mcp-inspector-results.png" alt="MCP Inspector Tool Results" width="600" />  | 
 | 69 | +</a>  | 
 | 70 | + | 
 | 71 | +## Architecture  | 
 | 72 | + | 
 | 73 | +### Keycloak Deployment  | 
 | 74 | +- Runs as a StatefulSet in the `keycloak` namespace  | 
 | 75 | +- Exposed via Ingress with TLS at `https://keycloak.127-0-0-1.sslip.io:8443`  | 
 | 76 | +- Uses cert-manager for TLS certificates  | 
 | 77 | +- Accessible from both host and cluster pods  | 
 | 78 | + | 
 | 79 | +### Kind Cluster with OIDC  | 
 | 80 | +- Kubernetes API server configured with OIDC authentication  | 
 | 81 | +- Points to Keycloak's `openshift` realm as the OIDC issuer  | 
 | 82 | +- Validates bearer tokens against Keycloak's JWKS endpoint  | 
 | 83 | +- API server trusts the cert-manager CA certificate  | 
 | 84 | + | 
 | 85 | +### Authentication Flow  | 
 | 86 | + | 
 | 87 | +```  | 
 | 88 | +User Browser  | 
 | 89 | +    |  | 
 | 90 | +    | 1. OAuth login (https://keycloak.127-0-0-1.sslip.io:8443)  | 
 | 91 | +    v  | 
 | 92 | +Keycloak  | 
 | 93 | +    |  | 
 | 94 | +    | 2. ID Token (aud: mcp-server)  | 
 | 95 | +    v  | 
 | 96 | +MCP Server  | 
 | 97 | +    |  | 
 | 98 | +    | 3. Token Exchange (aud: openshift)  | 
 | 99 | +    v  | 
 | 100 | +Keycloak  | 
 | 101 | +    |  | 
 | 102 | +    | 4. Exchanged Access Token  | 
 | 103 | +    v  | 
 | 104 | +MCP Server  | 
 | 105 | +    |  | 
 | 106 | +    | 5. Bearer Token in API request  | 
 | 107 | +    v  | 
 | 108 | +Kubernetes API Server  | 
 | 109 | +    |  | 
 | 110 | +    | 6. Validate token via OIDC  | 
 | 111 | +    v  | 
 | 112 | +Keycloak JWKS  | 
 | 113 | +    |  | 
 | 114 | +    | 7. Token valid, execute tool  | 
 | 115 | +    v  | 
 | 116 | +MCP Server → User  | 
 | 117 | +```  | 
 | 118 | + | 
 | 119 | +## Keycloak Configuration  | 
 | 120 | + | 
 | 121 | +The setup automatically configures:  | 
 | 122 | + | 
 | 123 | +### Realm: `openshift`  | 
 | 124 | +- Token lifespan: 30 minutes  | 
 | 125 | +- Session idle timeout: 30 minutes  | 
 | 126 | + | 
 | 127 | +### Clients  | 
 | 128 | + | 
 | 129 | +1. **mcp-client** (public)  | 
 | 130 | +   - Public client for browser-based OAuth login  | 
 | 131 | +   - PKCE required for security  | 
 | 132 | +   - Valid redirect URIs: `*`  | 
 | 133 | + | 
 | 134 | +2. **mcp-server** (confidential)  | 
 | 135 | +   - Confidential client with client secret  | 
 | 136 | +   - Standard token exchange enabled  | 
 | 137 | +   - Can exchange tokens with `aud: openshift`  | 
 | 138 | +   - Default scopes: `openid`, `groups`, `mcp-server`  | 
 | 139 | +   - Optional scopes: `mcp:openshift`  | 
 | 140 | + | 
 | 141 | +3. **openshift** (confidential)  | 
 | 142 | +   - Target client for token exchange  | 
 | 143 | +   - Accepts exchanged tokens from `mcp-server`  | 
 | 144 | +   - Used by Kubernetes API server for OIDC validation  | 
 | 145 | + | 
 | 146 | +### Client Scopes  | 
 | 147 | +- **mcp-server**: Default scope with audience mapper  | 
 | 148 | +- **mcp:openshift**: Optional scope for token exchange with audience mapper  | 
 | 149 | +- **groups**: Group membership mapper (included in tokens)  | 
 | 150 | + | 
 | 151 | +### Default User  | 
 | 152 | +- **Username**: `mcp`  | 
 | 153 | +- **Password**: `mcp`  | 
 | 154 | + | 
 | 155 | +- **RBAC**: `cluster-admin` (full cluster access)  | 
 | 156 | + | 
 | 157 | +## MCP Server Configuration  | 
 | 158 | + | 
 | 159 | +The generated `_output/config.toml` includes:  | 
 | 160 | + | 
 | 161 | +```toml  | 
 | 162 | +require_oauth = true  | 
 | 163 | +oauth_audience = "mcp-server"  | 
 | 164 | +oauth_scopes = ["openid", "mcp-server"]  | 
 | 165 | +validate_token = false  # Validation done by K8s API server  | 
 | 166 | +authorization_url = "https://keycloak.127-0-0-1.sslip.io:8443/realms/openshift"  | 
 | 167 | + | 
 | 168 | +sts_client_id = "mcp-server"  | 
 | 169 | +sts_client_secret = "..."  # Auto-generated  | 
 | 170 | +sts_audience = "openshift"  # Triggers token exchange  | 
 | 171 | +sts_scopes = ["mcp:openshift"]  | 
 | 172 | + | 
 | 173 | +certificate_authority = "_output/cert-manager-ca/ca.crt"  # For HTTPS validation  | 
 | 174 | +```  | 
 | 175 | + | 
 | 176 | +## Useful Commands  | 
 | 177 | + | 
 | 178 | +### Check Keycloak Status  | 
 | 179 | + | 
 | 180 | +```bash  | 
 | 181 | +make keycloak-status  | 
 | 182 | +```  | 
 | 183 | + | 
 | 184 | +Shows:  | 
 | 185 | +- Keycloak pod status  | 
 | 186 | +- Service endpoints  | 
 | 187 | +- Access URL  | 
 | 188 | +- Admin credentials  | 
 | 189 | + | 
 | 190 | +### View Keycloak Logs  | 
 | 191 | + | 
 | 192 | +```bash  | 
 | 193 | +make keycloak-logs  | 
 | 194 | +```  | 
 | 195 | + | 
 | 196 | +### Access Keycloak Admin Console  | 
 | 197 | + | 
 | 198 | +Open your browser to:  | 
 | 199 | +```  | 
 | 200 | +https://keycloak.127-0-0-1.sslip.io:8443  | 
 | 201 | +```  | 
 | 202 | + | 
 | 203 | +**Admin credentials:**  | 
 | 204 | +- Username: `admin`  | 
 | 205 | +- Password: `admin`  | 
 | 206 | + | 
 | 207 | +Navigate to the `openshift` realm to view/modify the configuration.  | 
 | 208 | + | 
 | 209 | +## Teardown  | 
 | 210 | + | 
 | 211 | +Remove the local environment:  | 
 | 212 | + | 
 | 213 | +```bash  | 
 | 214 | +make local-env-teardown  | 
 | 215 | +```  | 
 | 216 | + | 
 | 217 | +This deletes the Kind cluster (Keycloak is removed with it).  | 
0 commit comments