|
| 1 | +# Keycloak ACM Multi-Cluster Setup |
| 2 | + |
| 3 | +This guide shows you how to set up Keycloak-based OIDC authentication for ACM multi-cluster environments with cross-realm token exchange. |
| 4 | + |
| 5 | +## Overview |
| 6 | + |
| 7 | +This setup enables: |
| 8 | +- Single Keycloak instance on the hub cluster |
| 9 | +- OIDC authentication for both hub (local-cluster) and managed clusters |
| 10 | +- Cross-realm token exchange for seamless multi-cluster access |
| 11 | +- Single MCP server instance accessing all clusters with user authentication |
| 12 | + |
| 13 | +## Prerequisites |
| 14 | + |
| 15 | +- ACM installed on hub cluster (see [acm.md](acm.md)) |
| 16 | +- At least one managed cluster imported |
| 17 | +- Hub cluster kubeconfig available |
| 18 | + |
| 19 | +## Step 1: Setup Keycloak and Hub Realm |
| 20 | + |
| 21 | +Deploy Keycloak on the hub cluster and configure the hub realm: |
| 22 | + |
| 23 | +```bash |
| 24 | +make keycloak-acm-setup-hub |
| 25 | +``` |
| 26 | + |
| 27 | +This performs: |
| 28 | +1. Deploys Keycloak and PostgreSQL on hub cluster |
| 29 | +2. Creates hub realm with OIDC configuration |
| 30 | +3. Creates `mcp-sts` client for token exchange |
| 31 | +4. Creates `mcp-server` scope with required mappers |
| 32 | +5. Creates test user `mcp` (username: `mcp`, password: `mcp`) |
| 33 | +6. Saves configuration to `.keycloak-config/hub-config.env` |
| 34 | + |
| 35 | +**Wait for Keycloak to be ready** (2-3 minutes): |
| 36 | + |
| 37 | +```bash |
| 38 | +make keycloak-status |
| 39 | +``` |
| 40 | + |
| 41 | +Expected output: |
| 42 | +``` |
| 43 | +Keycloak Status: |
| 44 | + Pod: keycloak-6c94fb478b-gxqzf (Running) |
| 45 | + Route: https://keycloak-keycloak.apps.example.com |
| 46 | + Admin Console: https://keycloak-keycloak.apps.example.com/admin |
| 47 | +``` |
| 48 | + |
| 49 | +## Step 2: Register Managed Cluster |
| 50 | + |
| 51 | +Register each managed cluster with ACM and configure OIDC authentication: |
| 52 | + |
| 53 | +```bash |
| 54 | +make keycloak-acm-register-managed-cluster \ |
| 55 | + CLUSTER_NAME=production-east \ |
| 56 | + MANAGED_KUBECONFIG=/path/to/production-east-kubeconfig |
| 57 | +``` |
| 58 | + |
| 59 | +**Parameters**: |
| 60 | +- `CLUSTER_NAME`: Name of the managed cluster (must match ACM ManagedCluster name) |
| 61 | +- `MANAGED_KUBECONFIG`: Path to the managed cluster's kubeconfig |
| 62 | + |
| 63 | +### What Happens During Registration |
| 64 | + |
| 65 | +1. Creates `ManagedCluster` resource (if not exists) |
| 66 | +2. Applies ACM import manifests to managed cluster |
| 67 | +3. Creates dedicated Keycloak realm for the cluster |
| 68 | +4. Configures `mcp-server` client with protocol mappers |
| 69 | +5. Creates identity provider linking to hub realm |
| 70 | +6. Enables TechPreviewNoUpgrade feature gate on managed cluster |
| 71 | +7. Configures OIDC authentication pointing to Keycloak |
| 72 | +8. Creates RBAC for authenticated users |
| 73 | +9. Saves configuration to `.keycloak-config/clusters/$CLUSTER_NAME.env` |
| 74 | + |
| 75 | +### Important: kube-apiserver Rollout |
| 76 | + |
| 77 | +**The kube-apiserver will restart to apply OIDC configuration** (10-15 minutes). Monitor the rollout: |
| 78 | + |
| 79 | +```bash |
| 80 | +kubectl --kubeconfig=/path/to/managed-kubeconfig get co kube-apiserver -w |
| 81 | +``` |
| 82 | + |
| 83 | +Wait for: |
| 84 | +``` |
| 85 | +NAME VERSION AVAILABLE PROGRESSING DEGRADED SINCE MESSAGE |
| 86 | +kube-apiserver 4.17.0 True False False 2m ... |
| 87 | +``` |
| 88 | + |
| 89 | +When `PROGRESSING` is `False`, the cluster is ready. |
| 90 | + |
| 91 | +### Quick Status Check |
| 92 | + |
| 93 | +```bash |
| 94 | +make keycloak-acm-status |
| 95 | +``` |
| 96 | + |
| 97 | +Shows all configured clusters and their Keycloak realms. |
| 98 | + |
| 99 | +## Step 3: Generate MCP Server Configuration |
| 100 | + |
| 101 | +Generate the MCP server configuration file with all cluster credentials: |
| 102 | + |
| 103 | +```bash |
| 104 | +make keycloak-acm-generate-toml |
| 105 | +``` |
| 106 | + |
| 107 | +This creates `_output/acm-kubeconfig.toml` with: |
| 108 | +- Hub cluster OAuth configuration |
| 109 | +- Token exchange settings for local-cluster (hub) |
| 110 | +- Token exchange settings for each managed cluster |
| 111 | +- Keycloak CA certificate |
| 112 | + |
| 113 | +**Example generated configuration**: |
| 114 | + |
| 115 | +```toml |
| 116 | +cluster_provider_strategy = "acm-kubeconfig" |
| 117 | +kubeconfig = "/path/to/hub-kubeconfig" |
| 118 | + |
| 119 | +# Hub OAuth Configuration |
| 120 | +require_oauth = true |
| 121 | +oauth_audience = "mcp-server" |
| 122 | +authorization_url = "https://keycloak-keycloak.apps.example.com/realms/hub" |
| 123 | +token_url = "https://keycloak-keycloak.apps.example.com/realms/hub/protocol/openid-connect/token" |
| 124 | +sts_client_id = "mcp-sts" |
| 125 | +sts_client_secret = "..." |
| 126 | +certificate_authority = "_output/keycloak-ca.crt" |
| 127 | + |
| 128 | +# Local cluster (hub) - same-realm token exchange |
| 129 | +[cluster_provider_configs.acm-kubeconfig.clusters."local-cluster"] |
| 130 | +token_url = "https://keycloak-keycloak.apps.example.com/realms/hub/protocol/openid-connect/token" |
| 131 | +client_id = "mcp-sts" |
| 132 | +client_secret = "..." |
| 133 | +audience = "mcp-server" |
| 134 | +subject_token_type = "urn:ietf:params:oauth:token-type:access_token" |
| 135 | + |
| 136 | +# Managed cluster - cross-realm token exchange |
| 137 | +[cluster_provider_configs.acm-kubeconfig.clusters."production-east"] |
| 138 | +token_url = "https://keycloak-keycloak.apps.example.com/realms/production-east/protocol/openid-connect/token" |
| 139 | +client_id = "mcp-server" |
| 140 | +client_secret = "..." |
| 141 | +subject_issuer = "hub-realm" |
| 142 | +audience = "mcp-server" |
| 143 | +subject_token_type = "urn:ietf:params:oauth:token-type:jwt" |
| 144 | +``` |
| 145 | + |
| 146 | +## Step 4: Run the MCP Server |
| 147 | + |
| 148 | +Start the MCP Server with OAuth authentication: |
| 149 | + |
| 150 | +```bash |
| 151 | +./kubernetes-mcp-server --config _output/acm-kubeconfig.toml --port 8080 |
| 152 | +``` |
| 153 | + |
| 154 | +The server will: |
| 155 | +1. Listen on `http://localhost:8080/mcp` |
| 156 | +2. Require OAuth authentication via Keycloak |
| 157 | +3. Support token exchange for all configured clusters |
| 158 | +4. Provide tools for multi-cluster operations |
| 159 | + |
| 160 | +## Step 5: Test with MCP Inspector |
| 161 | + |
| 162 | +Test the setup using the MCP Inspector: |
| 163 | + |
| 164 | +```bash |
| 165 | +npx @modelcontextprotocol/inspector@latest http://localhost:8080/mcp |
| 166 | +``` |
| 167 | + |
| 168 | +**Authentication flow**: |
| 169 | +1. Inspector opens browser for OAuth login |
| 170 | +2. You authenticate with Keycloak (user: `mcp`, password: `mcp`) |
| 171 | +3. Inspector receives OAuth token |
| 172 | +4. MCP server performs token exchange for each cluster access |
0 commit comments