MCP远程Vault演示
使用HashiCorp Vault,模型上下文协议(MCP)服务器管理特定用户的动态凭据的系统演示。
目标
其核心目标是验证远程MCP服务器是否正确导入不同用户的凭据。
HashiCorp的官方博客 验证模式:使用HashiCorp Vault的AI代理身份 介绍如何使用JWT的On-Behalf-Of(OBO)令牌进行MCP身份验证。但在这种方式下,配置是为了管理特定用户的凭据,因此JWT的 sub 使用claim创建Entity,并为每个Entity创建连接到JWT auth method的Entity alias。
体系结构
演示
组件
- 钥匙斗篷:用户认证和JWT发放
- HashiCorp保险库:基于JWT的身份验证和用户身份验证管理(使用Policy模板)
- KV秘密引擎:Jira,保存Github凭据 - 数据库秘密引擎:创建PostgreSQL动态凭据
- PostgreSQL:关系数据库(用于MCP服务器)
- Streamlit客户端:在Web UI中显示MCP服务器的选择和使用、身份验证流跟踪和身份验证调试信息
- 远程MCP服务器 (基于FastMCP):
- Jira MCP服务器:Jira问题和项目管理 - Github MCP服务器:GitHub存储库和问题管理 - PostgreSQL MCP服务器:查询和管理PostgreSQL数据库
- 模拟后端服务:Jira和Github API模拟
数据流
- 初始化阶段:
init-vault.sh脚本在Keycloak中查看用户列表,并为每个用户预创建Vault Entity(Entity name=username,例如“alice”、“bob”)。为每个Entity创建连接到JWT auth method的Entity alias(alias name=用户UUID,JWT的subclaim值)。 - 用户身份验证:用户通过Streamlit Client登录Keycloak以获得JWT令牌(JWT的
subclaim包括用户UUID) - MCP请求:Streamlit Client将请求与JWT令牌一起发送到选定的MCP服务器(可选)
- Vault认证:如果MCP服务器将JWT传递给Vault,Vault将通过Keycloak的JWKS URL验证JWT签名,并查找现有Entity并连接alias。
- 查询凭据:
- KV秘密(Jira,Github):通过Vault Policy模板按用户路径(secret/data/users/{{identity.entity.name}}/*),MCP服务器使用Entity name查询凭据 - 数据库秘密(PostgreSQL):通过Vault Database Secrets Engine创建动态数据库凭据(database/creds/{{identity.entity.name}})
- API/DB调用:MCP服务器使用从Vault导入的凭据调用Mock API或PostgreSQL数据库
核心概念
分离每个用户的凭据
每个用户只能查看自己的凭据:
- Entity字典创建:
init-vault.sh脚本在Keycloak中查看用户列表,并为每个用户预先创建Vault Entity。Entity name设置为username(例如“alice”、“bob”),管理员可以轻松识别。 - Entity Alias连接:为每个Entity创建连接到JWT auth method的Entity alias。Alias name是用户UUID(JWT的
subclaim值),与JWT匹配。 - 用户身份验证:用户以Keycloak登录并获得JWT令牌(JWT的
subclaim包括用户UUID) - Vault认证:如果MCP服务器将JWT传递给Vault,Vault将使用Keycloak的公钥验证JWT签名,JWT的
sub使用claim查找现有的Entity alias并连接Entity。 - 政策模板:通过Vault Policy模板允许访问以下路径:
- KV秘密: secret/data/users/{{identity.entity.name}}/* (Jira,Github) - 数据库机密: database/creds/{{identity.entity.name}}, database/roles/{{identity.entity.name}} (PostgreSQL) Entity name是username,管理员可以轻松跟踪它。
- 查询凭据:
- KV秘密:MCP服务器使用Entity name secret/data/users/{entity_name}/jira 或 secret/data/users/{entity_name}/github 通过路径查询凭据。 - 数据库机密:MCP服务器使用Entity name database/creds/{entity_name} 通过路径创建动态数据库凭据。
- 访问控制:每个用户只能查看自己的凭据,无法访问其他用户的凭据。
Keycloak-Vault集成
Vault使用Keycloak的JWKS URL验证JWT令牌:
- JWKS网址:
http://keycloak:8080/realms/mcp-demo/protocol/openid-connect/certs - 设置位置:
init-vault.sh在脚本中设置为以下命令:
vault write auth/jwt/config \
jwks_url="http://keycloak:8080/realms/mcp-demo/protocol/openid-connect/certs" \
bound_issuer="http://localhost:8080/realms/mcp-demo"- 动作原理:
1. Vault从JWKS URL获取Keycloak的公钥 1. 使用公钥验证JWT签名 1. bound_issuer通过JWT的 iss 确保claim与Keycloak的issuer匹配 1. 如果验证成功,JWT的 sub 基于claim创建Entity
开始
运行所需的前期准备工作包括:
- 安装Docker和Docker Compose
- curl和Python3
1.启动服务
docker-compose up -d2.初始化Keycloak
等待Keycloak启动(约30秒),然后运行初始化脚本:
./init-keycloak.sh3.初始化Vault
Vault启动后运行初始化脚本:
./init-vault.sh此脚本执行以下操作:
- 启用JWT身份验证方法
- 与Keycloak集成设置
- 启用KV secrets engine(Jira,Github)
- 启用Database secrets engine(用于PostgreSQL)
- PostgreSQL连接设置和动态角色创建
- 在Keycloak中查看用户列表
- 为每个用户创建Vault Entity字典(Entity name=username,例如“alice”、“bob”)
- 为每个Entity创建连接到JWT auth方法的Entity alias(alias name=用户UUID)
- 使用Policy模板创建特定于用户的策略(
{{identity.entity.name}}使用) - 创建JWT role
- 初始化特定于用户的凭据(alice:Jira、Github、PostgreSQL/bob:Github、PostgreSQL)
4.确认用户凭据
init-vault.sh 脚本将自动生成以下凭据:
- 爱丽丝:生成Jira、Github和PostgreSQL的所有证书
- 鲍勃:Github,仅生成PostgreSQL凭据(Jira不存在-用于演示)
要手动检查或修改,请:
# Alice의 Jira 자격증명 확인
docker exec -e VAULT_TOKEN=root-token vault vault kv get secret/users/alice/jira
# Alice의 Github 자격증명 확인
docker exec -e VAULT_TOKEN=root-token vault vault kv get secret/users/alice/github
# Alice의 PostgreSQL role 확인
docker exec -e VAULT_TOKEN=root-token vault vault read database/roles/alice
# Bob의 Github 자격증명 확인
docker exec -e VAULT_TOKEN=root-token vault vault kv get secret/users/bob/github
# Bob의 PostgreSQL role 확인
docker exec -e VAULT_TOKEN=root-token vault vault read database/roles/bob参考:Entity name使用username(alice,bob),便于管理。
使用方法
1.连接Streamlit客户端
在浏览器中http://localhost:8501连接
2.登录
- 用户名:
alice或bob - 密码:
alice123或bob123
3.选择MCP服务器并加载工具
- 选择一个或多个MCP服务器(可通过复选框选择多个):
- Jira:Jira问题和项目管理 - GitHub:GitHub存储库和问题管理 - PostgreSQL:查询和管理PostgreSQL数据库
- 单击“Load Tools”按钮
身份验证流跟踪(Authentication Flow Trace):单击“Load Tools”时自动显示以下信息:
- 步骤1-2:用户登录和JWT签发信息
- 步骤3:MCP服务器请求状态
- 步骤4-5:Vault认证和Entity信息
- 步骤6:每个MCP服务器的凭据状态
- User ID(JWT的 sub 索赔) - 用户名、电子邮件 - Vault路径 - 掩蔽的凭据(为安全起见,仅显示一部分) - 凭据存在性和错误信息
4.使用工具
展开每个工具,输入所需参数,然后单击“Execute”按钮
演示方案
方案1:验证每个用户的凭据分离
- 以alice登录
- 在Streamlit中 alice / alice123登录到 - 选择MCP服务器(例如Jira、Github、PostgreSQL中的一个或多个) - 单击“Load Tools” - 在Authentication Flow Trace中查看凭据信息: - User ID:alice的UUID(JWT的 sub 索赔) - 实体名称: alice (使用预生成的Entity name和username) - 保险库路径: - 吉拉: secret/data/users/alice/jira - github: secret/data/users/alice/github - PostgreSQL: database/creds/alice - Credentials:alice的凭据(按每个MCP服务器显示)
- 以bob身份登录 (其他浏览器/秘密模式)
- 在Streamlit中 bob / bob123登录到 - 选择相同的MCP服务器 - 单击“Load Tools” - 在Authentication Flow Trace中查看凭据信息: - User ID:bob的UUID(与alice不同,JWT的 sub 索赔) - 实体名称: bob (使用预生成的Entity name和username) - 保险库路径: - Jira:没有凭据(步骤6显示失败) - github: secret/data/users/bob/github - PostgreSQL: database/creds/bob - Credentials:bob的凭据(与alice不同)
结果:确保每个用户只查看自己的凭据
方案2:运行MCP工具
- 选择工具(例如:
get_issue) - 输入参数(例如:
issue_key:项目1) - 单击“Execute”
- 验证结果(通过用户的凭据调用Mock API)
服务端口
- 钥匙斗篷: http://localhost:8080
- 金库: http://localhost:8200(UI:http://localhost:8200/ui,根令牌:
root-token) - PostgreSQL:本地主机:5432
- Streamlit客户端: http://localhost:8501
- Jira MCP服务器: http://localhost:3001
- Github MCP服务器: http://localhost:3002
- PostgreSQL MCP服务器: http://localhost:3003
- 模仿Jira API: http://localhost:8001
- 模拟Github API: http://localhost:8002
文件结构
mcp-remote/
├── docker-compose.yml # 모든 서비스 정의
├── init-keycloak.sh # Keycloak 초기화 스크립트
├── init-vault.sh # Vault 초기화 스크립트
├── README.md # 이 문서
├── flow-chart.svg # 아키텍처 다이어그램
├── vault/
│ ├── config.hcl # Vault 설정
│ └── setup.sh # Vault 설정 스크립트
├── keycloak/
│ └── (디렉토리 - 현재 사용되지 않음)
├── mcp-servers/
│ ├── jira-server/
│ │ ├── main.py # Jira MCP 서버
│ │ ├── requirements.txt # Python 의존성
│ │ └── Dockerfile # Docker 이미지 정의
│ ├── github-server/
│ │ ├── main.py # Github MCP 서버
│ │ ├── requirements.txt # Python 의존성
│ │ └── Dockerfile # Docker 이미지 정의
│ └── postgresql-server/
│ ├── main.py # PostgreSQL MCP 서버
│ ├── requirements.txt # Python 의존성
│ └── Dockerfile # Docker 이미지 정의
├── streamlit-client/
│ ├── app.py # Streamlit 웹 UI
│ ├── auth_trace.py # 인증 흐름 추적 모듈
│ ├── requirements.txt # Python 의존성
│ ├── Dockerfile # Docker 이미지 정의
│ └── .streamlit/
│ └── config.toml # Streamlit 설정
├── init-postgresql.sql # PostgreSQL 초기화 스크립트
└── mock-services/
├── jira-api/
│ ├── main.py # Mock Jira API
│ ├── requirements.txt # Python 의존성
│ └── Dockerfile # Docker 이미지 정의
└── github-api/
├── main.py # Mock Github API
├── requirements.txt # Python 의존성
└── Dockerfile # Docker 이미지 정의故障排除
Keycloak无法启动
PostgreSQL必须先启动:
docker-compose logs postgres
docker-compose logs keycloak
docker-compose restart keycloakVault设置错误
等待Vault完全启动:
docker exec -e VAULT_TOKEN=root-token vault vault status
docker-compose logs vaultMCP服务器连接错误
检查服务日志:
docker-compose logs jira-mcp-server
docker-compose logs github-mcp-server查询凭据失败
验证Vault策略设置是否正确:
docker exec -e VAULT_TOKEN=root-token vault vault policy read user-secrets
docker-compose logs vault整理
停止所有服务:
docker-compose down删除到卷:
docker-compose down -v技术堆栈
- FastMCP:用于MCP服务器实现的Python框架
- 快速API:为MCP服务器提供HTTP端点
- 政策模板:Vault的动态策略创建功能(
{{identity.entity.name}}使用) - JWT身份验证:Keycloak和Vault之间的身份验证
- Vault数据库机密引擎:创建PostgreSQL动态凭据
- 溪流:可视化Web UI和身份验证流跟踪
