动态凭证管理系统

基于HashiCorp Vault的用户动态凭证管理系统,支持JWT认证和用户隔离的凭证管理,适用于多服务集成场景。

  • by Great-Stone
  • Python
  • 本地部署
  • 更新于
在 GitHub 查看 Great-Stone/mcp-remote-vault-demo

使用说明

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模拟

数据流

  1. 初始化阶段: init-vault.sh 脚本在Keycloak中查看用户列表,并为每个用户预创建Vault Entity(Entity name=username,例如“alice”、“bob”)。为每个Entity创建连接到JWT auth method的Entity alias(alias name=用户UUID,JWT的 sub claim值)。
  2. 用户身份验证:用户通过Streamlit Client登录Keycloak以获得JWT令牌(JWT的 sub claim包括用户UUID)
  3. MCP请求:Streamlit Client将请求与JWT令牌一起发送到选定的MCP服务器(可选)
  4. Vault认证:如果MCP服务器将JWT传递给Vault,Vault将通过Keycloak的JWKS URL验证JWT签名,并查找现有Entity并连接alias。
  5. 查询凭据:
    • KV秘密(Jira,Github):通过Vault Policy模板按用户路径(secret/data/users/{{identity.entity.name}}/*),MCP服务器使用Entity name查询凭据
    • 数据库秘密(PostgreSQL):通过Vault Database Secrets Engine创建动态数据库凭据(database/creds/{{identity.entity.name}})
  6. API/DB调用:MCP服务器使用从Vault导入的凭据调用Mock API或PostgreSQL数据库

核心概念

分离每个用户的凭据

每个用户只能查看自己的凭据:

  1. Entity字典创建: init-vault.sh 脚本在Keycloak中查看用户列表,并为每个用户预先创建Vault Entity。Entity name设置为username(例如“alice”、“bob”),管理员可以轻松识别。
  2. Entity Alias连接:为每个Entity创建连接到JWT auth method的Entity alias。Alias name是用户UUID(JWT的 sub claim值),与JWT匹配。
  3. 用户身份验证:用户以Keycloak登录并获得JWT令牌(JWT的 sub claim包括用户UUID)
  4. Vault认证:如果MCP服务器将JWT传递给Vault,Vault将使用Keycloak的公钥验证JWT签名,JWT的 sub 使用claim查找现有的Entity alias并连接Entity。
  5. 政策模板:通过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,管理员可以轻松跟踪它。
  6. 查询凭据:
    • KV秘密:MCP服务器使用Entity name secret/data/users/{entity_name}/jira 或 secret/data/users/{entity_name}/github 通过路径查询凭据。
    • 数据库机密:MCP服务器使用Entity name database/creds/{entity_name} 通过路径创建动态数据库凭据。
  7. 访问控制:每个用户只能查看自己的凭据,无法访问其他用户的凭据。

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的公钥
    2. 使用公钥验证JWT签名
    3. bound_issuer通过JWT的 iss 确保claim与Keycloak的issuer匹配
    4. 如果验证成功,JWT的 sub 基于claim创建Entity

开始

运行所需的前期准备工作包括:

  • 安装Docker和Docker Compose
  • curl和Python3

1.启动服务

docker-compose up -d

2.初始化Keycloak

等待Keycloak启动(约30秒),然后运行初始化脚本:

./init-keycloak.sh

3.初始化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:验证每个用户的凭据分离

  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服务器显示)
  2. 以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工具

  1. 选择工具(例如: get_issue)
  2. 输入参数(例如: issue_key:项目1)
  3. 单击“Execute”
  4. 验证结果(通过用户的凭据调用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 keycloak

Vault设置错误

等待Vault完全启动:

docker exec -e VAULT_TOKEN=root-token vault vault status
docker-compose logs vault

MCP服务器连接错误

检查服务日志:

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和身份验证流跟踪

参考资料