|
| 1 | +# ACM Keycloak Declarative Configuration |
| 2 | + |
| 3 | +This directory contains declarative JSON configuration files for setting up Keycloak for ACM (Advanced Cluster Management) multi-realm token exchange. |
| 4 | + |
| 5 | +## Architecture |
| 6 | + |
| 7 | +- **Hub Realm**: Central realm where users authenticate |
| 8 | +- **Managed Cluster Realms**: One realm per managed cluster |
| 9 | +- **Token Exchange**: V1 token exchange using `subject_issuer` parameter |
| 10 | + - Same-realm: `mcp-sts` → `mcp-server` within hub realm |
| 11 | + - Cross-realm: Hub realm token → Managed cluster realm token |
| 12 | + |
| 13 | +## Directory Structure |
| 14 | + |
| 15 | +``` |
| 16 | +dev/acm/config/keycloak/ |
| 17 | +├── realm/ |
| 18 | +│ ├── hub-realm-create.json # Hub realm configuration |
| 19 | +│ └── managed-realm-create.json # Template for managed cluster realms |
| 20 | +├── clients/ |
| 21 | +│ ├── mcp-server.json # OAuth client (confidential) |
| 22 | +│ ├── mcp-client.json # Browser OAuth client (public) |
| 23 | +│ └── mcp-sts.json # STS client for token exchange |
| 24 | +├── client-scopes/ |
| 25 | +│ ├── openid.json # OpenID Connect scope |
| 26 | +│ └── mcp-server.json # MCP audience scope |
| 27 | +├── mappers/ |
| 28 | +│ ├── mcp-server-audience-mapper.json # Adds mcp-server to aud claim |
| 29 | +│ └── sub-claim-mapper.json # Maps user ID to sub claim |
| 30 | +├── users/ |
| 31 | +│ └── mcp.json # Test user (mcp/mcp) |
| 32 | +└── identity-providers/ |
| 33 | + └── hub-realm-idp-template.json # IDP config for cross-realm trust |
| 34 | +``` |
| 35 | + |
| 36 | +## Configuration Files |
| 37 | + |
| 38 | +### Hub Realm (`realm/hub-realm-create.json`) |
| 39 | + |
| 40 | +- Realm name: `hub` |
| 41 | +- User registration: disabled |
| 42 | +- Password reset: enabled |
| 43 | +- Brute force protection: enabled |
| 44 | +- Token lifespans configured for security |
| 45 | + |
| 46 | +### Clients |
| 47 | + |
| 48 | +#### `mcp-server` (Confidential Client) |
| 49 | +- Used by MCP server for OAuth authentication |
| 50 | +- Direct access grants enabled (password flow) |
| 51 | +- Service accounts enabled |
| 52 | +- Default scopes: `openid`, `profile`, `email`, `mcp-server` |
| 53 | + |
| 54 | +#### `mcp-client` (Public Client) |
| 55 | +- Used by browser-based tools (e.g., MCP Inspector) |
| 56 | +- PKCE enabled for security |
| 57 | +- Authorization code flow only |
| 58 | +- No service accounts |
| 59 | + |
| 60 | +#### `mcp-sts` (STS Client) |
| 61 | +- Used for token exchange operations |
| 62 | +- Service accounts only (no user login) |
| 63 | +- No redirect URIs (not for browser flows) |
| 64 | + |
| 65 | +### Client Scopes |
| 66 | + |
| 67 | +#### `openid` |
| 68 | +- Standard OpenID Connect scope |
| 69 | +- Provides basic user claims (sub, iss, aud, exp, iat) |
| 70 | + |
| 71 | +#### `mcp-server` |
| 72 | +- Custom audience scope |
| 73 | +- Adds `mcp-server` to the `aud` claim in access tokens |
| 74 | +- Required for token validation |
| 75 | + |
| 76 | +### Protocol Mappers |
| 77 | + |
| 78 | +#### `mcp-server-audience` |
| 79 | +- Type: `oidc-audience-mapper` |
| 80 | +- Adds `mcp-server` to the audience claim |
| 81 | +- Applied to `mcp-server` client scope |
| 82 | + |
| 83 | +#### `sub` |
| 84 | +- Type: `oidc-sub-mapper` |
| 85 | +- Maps user ID to `sub` claim |
| 86 | +- Used for federated identity linking |
| 87 | + |
| 88 | +### Users |
| 89 | + |
| 90 | +#### `mcp` User |
| 91 | +- Username: `mcp` |
| 92 | +- Password: `mcp` |
| 93 | + |
| 94 | +- Full name: MCP User |
| 95 | +- Used for testing and development |
| 96 | + |
| 97 | +### Identity Provider |
| 98 | + |
| 99 | +#### Hub Realm IDP Template |
| 100 | +- Provider: `oidc` (generic OIDC, not keycloak-oidc) |
| 101 | +- Trust email: enabled |
| 102 | +- Store token: disabled |
| 103 | +- Sync mode: IMPORT (create local users) |
| 104 | +- Signature validation: enabled via JWKS URL |
| 105 | + |
| 106 | +## Variable Substitution |
| 107 | + |
| 108 | +JSON templates use `${VARIABLE_NAME}` placeholders that are replaced at runtime: |
| 109 | + |
| 110 | +- `${KEYCLOAK_URL}`: Base Keycloak URL (e.g., `https://keycloak-keycloak.apps.example.com`) |
| 111 | +- `${HUB_CLIENT_SECRET}`: Secret for mcp-server client in hub realm |
| 112 | +- `${MANAGED_REALM}`: Name of managed cluster realm (e.g., `managed-cluster-one`) |
| 113 | + |
| 114 | +## Usage |
| 115 | + |
| 116 | +These JSON files are applied via the Keycloak Admin REST API using the setup scripts: |
| 117 | + |
| 118 | +1. **Hub Setup**: `hack/acm/acm-keycloak-setup-hub-declarative.sh` |
| 119 | + - Creates hub realm |
| 120 | + - Creates clients (mcp-server, mcp-client, mcp-sts) |
| 121 | + - Creates client scopes (openid, mcp-server) |
| 122 | + - Adds protocol mappers |
| 123 | + - Creates test user |
| 124 | + - Configures same-realm token exchange permissions |
| 125 | + |
| 126 | +2. **Managed Cluster Registration**: `hack/acm/acm-register-managed-cluster-declarative.sh` |
| 127 | + - Creates managed cluster realm |
| 128 | + - Registers identity provider (hub realm) |
| 129 | + - Creates federated user link |
| 130 | + - Configures cross-realm token exchange permissions |
| 131 | + |
| 132 | +## Token Exchange Configuration |
| 133 | + |
| 134 | +### Same-Realm Token Exchange (Hub) |
| 135 | + |
| 136 | +Allows `mcp-sts` client to exchange tokens for `mcp-server` audience within the hub realm. |
| 137 | + |
| 138 | +**Steps** (applied by setup script): |
| 139 | +1. Enable management permissions on `mcp-server` client |
| 140 | +2. Get token-exchange permission ID |
| 141 | +3. Create client policy allowing `mcp-sts` |
| 142 | +4. Link policy to token-exchange permission |
| 143 | + |
| 144 | +**Test Command**: |
| 145 | +```bash |
| 146 | +source .keycloak-config/hub-config.env |
| 147 | +./hack/acm/test-same-realm-token-exchange.sh |
| 148 | +``` |
| 149 | + |
| 150 | +### Cross-Realm Token Exchange (Hub → Managed) |
| 151 | + |
| 152 | +Allows exchanging hub realm token for managed cluster realm token. |
| 153 | + |
| 154 | +**Steps** (applied by setup script): |
| 155 | +1. Create identity provider in managed realm pointing to hub realm |
| 156 | +2. Create federated identity link (hub user → managed user via `sub` claim) |
| 157 | +3. Enable fine-grained permissions on IDP |
| 158 | +4. Create client policy allowing hub realm's `mcp-sts` |
| 159 | +5. Link policy to token-exchange permission on IDP |
| 160 | + |
| 161 | +**Test Command**: |
| 162 | +```bash |
| 163 | +source .keycloak-config/hub-config.env |
| 164 | +source .keycloak-config/clusters/managed-cluster-one.env |
| 165 | +./hack/acm/test-cross-realm-token-exchange.sh |
| 166 | +``` |
| 167 | + |
| 168 | +## Keycloak Admin API Endpoints |
| 169 | + |
| 170 | +Configuration is applied using these endpoints: |
| 171 | + |
| 172 | +- **Realm**: `POST /admin/realms` |
| 173 | +- **Clients**: `POST /admin/realms/{realm}/clients` |
| 174 | +- **Client Scopes**: `POST /admin/realms/{realm}/client-scopes` |
| 175 | +- **Protocol Mappers**: `POST /admin/realms/{realm}/client-scopes/{scope-id}/protocol-mappers/models` |
| 176 | +- **Users**: `POST /admin/realms/{realm}/users` |
| 177 | +- **Identity Providers**: `POST /admin/realms/{realm}/identity-provider/instances` |
| 178 | +- **Client Permissions**: `PUT /admin/realms/{realm}/clients/{client-id}/management/permissions` |
| 179 | +- **Authorization Policies**: `POST /admin/realms/{realm}/clients/{client-id}/authz/resource-server/policy/client` |
| 180 | + |
| 181 | +## Benefits of Declarative Approach |
| 182 | + |
| 183 | +1. **Version Control**: Configuration as code |
| 184 | +2. **Repeatability**: Same configuration every time |
| 185 | +3. **Testability**: Easy to test in different environments |
| 186 | +4. **Documentation**: Self-documenting via JSON structure |
| 187 | +5. **Validation**: JSON schema validation possible |
| 188 | +6. **Idempotency**: Can reapply without side effects |
| 189 | +7. **Debugging**: Easy to compare configurations |
| 190 | + |
| 191 | +## References |
| 192 | + |
| 193 | +- Keycloak Admin REST API: https://www.keycloak.org/docs-api/26.0/rest-api/index.html |
| 194 | +- Token Exchange: https://www.keycloak.org/docs/latest/securing_apps/#_token-exchange |
| 195 | +- Identity Brokering: https://www.keycloak.org/docs/latest/server_admin/#_identity_broker |
0 commit comments