MCPIdentity PoC
使用Keycloak 26.6对Kubernetes服务进行无密钥MCP身份验证 两个名称空间中的两个MCP代理与另外两个名称环境中的两台MCP服务器进行通信。 无预先注册的Keycloak客户端。任何pod上都没有凭据文件。 SPIRE认证+CIMD+联合客户端身份验证+DPoP+OPA信任矩阵。
该PoC伴随着KeyclokCon@KubeCon Japan 2026提案。正是 该提案声称普通Kubernetes服务 可以充当MCP代理和MCP服务器,而无需在pod上存储任何凭据。
______________________________________________________________________
拓扑学
ns: mcp-agents ns: mcp-agents-restricted
┌─────────────────────────┐ ┌─────────────────────────┐
│ mcp-agent-alpha │ │ mcp-agent-beta │
│ SA: alpha-sa │ │ SA: beta-sa │
│ Trust: trusted │ │ Trust: semi-trusted │
│ SPIFFE: .../role/agent │ │ SPIFFE: .../role/agent │
│ │ │ │
│ /.well-known/oauth-cli. │ │ /.well-known/oauth-cli. │
│ /.well-known/jwks.json │ │ /.well-known/jwks.json │
└─────────────┬───────────┘ └─────────────┬───────────┘
│ │
│ POST /token (CIMD client_id + │
│ private_key_jwt + DPoP proof) │
▼ ▼
┌────────────────────────────────────────┐
│ Keycloak 26.6 (--features=cimd) │
│ Federated Client Authentication (GA) │
│ Realm: ZERO pre-registered MCP agents │
└────────────┬───────────────────────────┘
│ DPoP-bound access token │
▼ ▼
│ Tool call (Authorization: DPoP )
│
ns: mcp-servers-data ns: mcp-servers-actions
┌─────────────────────────┐ ┌─────────────────────────┐
│ mcp-server-data │ │ mcp-server-actions │
│ Trust: trusted │ │ Trust: semi-trusted │
│ │ │ │
│ /mcp │ │ /mcp │
│ /.well-known/oauth- │ │ /.well-known/oauth- │
│ protected-resource │ │ protected-resource │
│ /.well-known/jwks.json │ │ /.well-known/jwks.json │
│ │ │ │
│ Tools: │ │ Tools: │
│ query_database │ │ send_notification │
│ list_tables │ │ create_ticket │
│ describe_schema │ │ lookup_user │
└─────────────┬───────────┘ └─────────────┬───────────┘
│ │
└──────────────┬───────────────────┘
▼
┌────────────────────┐
│ ns: opa-system │
│ Trust matrix │
│ (role × tier × │
│ tool) │
└────────────────────┘
ns: spire-system
SPIRE Server + Agent DaemonSet
→ Attests every MCP pod via the Workload API (no SA tokens read by the app)信任矩阵
|| mcp服务器数据(受信任)|mcp服务器操作(半受信任)| |--|---------------------------|-----------------------------------| | mcp试剂α (受信任)|query_database、list_tables、describe_schema|send_notiation、create_ticket、lookup_user| | mcp试剂beta (半信任)|列表表,描述模式(无查询数据库)|查找用户(无发送通知,无创建票)|
OPA政策 policies/trust-matrix.rego 对每个人强制执行此矩阵 工具调用。演示了六种情况——四种允许,两种拒绝 遵循 demo/06-trust-matrix.sh.
这是什么无钥匙的?
| 关注 | 传统方法 | MCPIdentity |
|---|---|---|
| 每个客户端凭据 | env或文件中的client_secret | none——SPIRE在内存中发出JWT-SVID |
| 预先注册的Keycloak客户端 | 每个代理一个 | 零——CIMD通过URL进行自我描述 |
| ServiceAccount令牌使用 | 装载于 /var/run/secrets/... | automountServiceAccountToken: false |
| RSA私钥文件 | 已装载卷 | 无-- SpiffeJWKS 在内存中生成EC密钥对 |
| 令牌重放 | 如果令牌泄漏,则可能重放 | DPoP绑定到内存中的密钥,无法重放 |
| 添加新客户端 | 手动领域更改 | 部署pod,完成 |
存储库布局
mcp-identity-poc/
├── manifests/
│ ├── namespaces/ 4 MCP namespaces + ServiceAccounts
│ ├── keycloak/ Keycloak 26.6 + PostgreSQL 16 backend + realm.json
│ ├── opa/ OPA deployment (loads trust-matrix.rego)
│ ├── spire/ SPIRE Server + Agent
│ ├── spire-registrations/ ClusterSPIFFEID CRDs (production reference)
│ ├── mcp-agents/ mcp-agent-alpha, mcp-agent-beta
│ └── mcp-servers/ mcp-server-data, mcp-server-actions
├── policies/
│ └── trust-matrix.rego role × tier × tool decision policy
├── src/
│ ├── shared/ SPIFFE workload, JWKS, DPoP, CIMD, RFC 9728,
│ │ Keycloak federated client auth, OPA client,
│ │ MCP server base
│ ├── mcp_agent/ MCP agent app (main + caller)
│ ├── mcp_server_data/ Postgres-style tools
│ ├── mcp_server_actions/ Notification / ticket / lookup tools
│ ├── Dockerfile Single image for all four pods
│ └── requirements.txt
├── scripts/
│ ├── 01-create-cluster.sh k3d + 4 MCP namespaces
│ ├── 02-deploy-spire.sh SPIRE + role-aware registration entries
│ ├── 03-deploy-keycloak.sh Keycloak 26.6 with --features=cimd
│ ├── 04-deploy-opa.sh OPA with trust-matrix.rego
│ ├── 05-build-image.sh Build mcp-identity:latest, import into k3d
│ ├── 06-deploy-mcp-pods.sh Deploy 2 clients + 2 servers
│ ├── 06-delete-mcp-pods.sh Reverse of 06 (also cleans demo gamma pod)
│ ├── 05-delete-image.sh Reverse of 05
│ ├── 04-delete-opa.sh Reverse of 04
│ ├── 03-delete-keycloak.sh Reverse of 03 (drops realm first)
│ ├── 02-delete-spire.sh Reverse of 02 (drops registration entries first)
│ ├── 01-delete-cluster.sh Reverse of 01 (drops MCP namespaces first)
│ ├── benchmark.sh
│ ├── reset-cimd-clients.sh Delete all CIMD clients + restart pods (clean Demo 1)
│ ├── setup-all.sh Run everything in order
│ └── remove-all.sh Reverse setup-all.sh end-to-end
├── demo/
│ ├── 00-lib.sh Shared shell helpers
│ ├── 01-no-keys-no-clients.sh Prove the absence
│ ├── 02-discovery.sh RFC 9728 discovery
│ ├── 03-svid.sh SVID + CIMD + JWKS introspection
│ ├── 04-cimd-auth.sh Full Keycloak round-trip
│ ├── 05-replay-attack.sh DPoP replay defence
│ ├── 06-trust-matrix.sh Six scenarios: 4 allow + 2 deny
│ ├── 07-add-client-live.sh Bonus: add an agent at runtime
│ ├── 08-amplification-attack.sh Adversarial: CIMD amplification (optional)
│ ├── run-all.sh Run demos 1-7 interactively
│ └── DEMO_SCENARIOS.md Speaker-facing demo documentation
├── videos/
│ ├── record-cast.sh asciinema rec wrapper (preflight + cleanup)
│ ├── play-cast.sh Narration script played inside the cast
│ ├── mcpidentity-demo.cast Demo 1-7 recording
│ └── mcpidentity-demo8-amplification.cast Demo 8 recording
├── tests/ pytest suite
└── README.md快速开始
必备条件:Docker、k3d、kubectl、Python 3.12(用于测试)。
# 1. End-to-end bring-up
./scripts/setup-all.sh
# 2. Run the demo (each script is short and self-contained)
./demo/01-no-keys-no-clients.sh
./demo/02-discovery.sh
./demo/03-svid.sh
./demo/04-cimd-auth.sh
./demo/05-replay-attack.sh
./demo/06-trust-matrix.sh
./demo/07-add-client-live.sh
# Or all at once
./demo/run-all.sh
# 3. Inspect the running cluster
kubectl get pods --all-namespaces -l app.kubernetes.io/component=mcp-agent
kubectl get pods --all-namespaces -l app.kubernetes.io/component=mcp-server
kubectl logs -n keycloak deployment/keycloak | grep -i cimd重置(不拆除集群)
返回到Keycloak领域为零的干净“演示1”状态 MCP客户端,无需破坏k3d集群:
# Delete all CIMD-persisted clients + restart Keycloak + restart pods
./scripts/reset-cimd-clients.sh
# Delete clients only, don't restart pods
./scripts/reset-cimd-clients.sh --no-restart这将删除每个基于URL的 client_id 从王国,潮红 Keycloak的Infinispan JWKS缓存,清理任何剩余的伽马吊舱, 并重新启动所有MCP代理/服务器Pod,使其自动引导。
拆除
每 NN-deploy/build/create-*.sh 脚本有匹配项 NN-delete-*.sh 脚本。它们都是幂等的——与一个已经干净的 国家是禁止的。
# Wipe everything in reverse order (asks for confirmation)
./scripts/remove-all.sh
# Or skip the confirmation prompt (for CI / scripted teardown)
./scripts/remove-all.sh --yes
# Or take down individual layers (useful when iterating)
./scripts/06-delete-mcp-pods.sh # remove MCP pods only, keep infra
./scripts/03-delete-keycloak.sh # drop the mcp-identity realm + Keycloak
./scripts/04-delete-opa.sh # drop OPA + trust-matrix policy
./scripts/02-delete-spire.sh # drop SPIRE + registration entries
./scripts/05-delete-image.sh # drop mcp-identity:latest container image
./scripts/01-delete-cluster.sh # drop the k3d cluster + MCP namespaces拆卸后,系统返回“无k3d集群,无本地图像”。 重新运行 ./scripts/setup-all.sh 从头开始重建。
测试
pip install -r requirements-test.txt
pytest -v测试包括:
- 共享库:SpiffeJWKS、DPoPSigner、DPoPVerifier、CIMD文档、受保护资源
- OPA信任矩阵决策(所有6种情况+身份先决条件)
- MCP代理和服务器FastAPI应用程序构建(没有真正的集群)
Keycloak 26.6(此处使用)的新增功能
| 功能 | 26.6中的状态 | 由使用 |
|---|---|---|
| 联合客户端身份验证 | 支持 (新GA) | 每个MCP代理的客户确认 |
| OAuth客户端ID元数据文档 | 实验性的 (--features=cimd) | 每个MCP代理的基于URL的client_id |
| DPoP指南 | 发布新指南 | 客户端和服务器(RFC 9449) |
两个上游问题草案(令牌端点CIMD执行器缺口+ /auth 放大 表面)和其他侧面发现记录在 upstream/ 带复制器 脚本和源代码证据,准备在Keycloak公共跟踪器上归档。
Keycloak后端
Keycloak与 PostgreSQL 16后端 (manifests/keycloak/postgres.yaml) 而不是存储器H2中的默认值。PoC取决于领域、客户端策略、应用程序和应用程序, 以及任何动态注册的CIMD状态幸存的吊舱重启——探测触发 重新启动, kubectl rollout restart,甚至在演示期间重新安排节点 否则将擦除 mcp-identity 王国和强制完全重新进口。
keycloak-db部署+PVC(2 GiB)与Keycloak一起使用keycloak命名空间KC_DB=postgres+KC_DB_URL=jdbc:postgresql://keycloak-db...在Keycloak吊舱中- Keycloak吊舱有一个
initContainer这阻碍了pg_isready直到Postgres接受连接 ./scripts/03-delete-keycloak.sh去除PVC,重新开始清洗
许可证
Apache 2.0——请参阅 LICENSE.
