MCP+AAzure APIM零信任架构全实践实验室
这个实验室被设计成一个故事 4法案。您将首先在云中部署一个简单的MCP服务器,并逐步发展您的架构,直到您达到具有身份验证和代表(OBO)流程的复杂企业场景。
目标(目标状态)
在本次旅程结束时,您将拥有一个安全的架构,其中AI代理代表登录用户访问敏感数据(Microsoft Graph),并受到MCP网关的保护。
______________________________________________________________________
场景
你是一名平台工程师,负责为公司的开发人员提供人工智能工具。
- 第1幕: 证明该概念有效(部署)。
- 第2幕: 管理对这些工具的访问(APIM MCP网关)。
- 第3幕: 保护周边(身份)。
- 第4幕: 确保AI使用用户自己的身份访问敏感数据(OBO Flow)。
______________________________________________________________________
先决条件
- Azure命令行界面 已安装并登录(
az login). - Azure功能核心工具 (
func). - Python 3.10+.
- Visual Studio Code 随着 GitHub Copilot 扩展(或兼容的MCP客户端)。
______________________________________________________________________
舞台准备(环境)
为了确保资源名称的唯一性和可重复性,我们将使用设置脚本。
- 导航到实验室文件夹:
cd mcp-lab- 配置环境变量(在Bash/WSL终端中运行):
source env-setup.sh*此命令生成一个随机后缀,并设置以下变量 $GRP, $FUNC, $APIM, $AZURE_CLI_APP_ID 等等让这个终端在整个实验室都保持开放。*
______________________________________________________________________
第一幕:MCP服务器的诞生
在此阶段,您的目标很简单:让MCP服务器在云中运行(Azure Functions)并可公开访问。
1.1提供基础设施
# Create Resource Group
az group create --name $GRP --location $LOC
# Create Storage Account (Required for Functions)
az storage account create --name $STG --location $LOC --resource-group $GRP --sku Standard_LRS
# Create the Function App (Serverless Linux)
az functionapp create --name $FUNC --storage-account $STG --consumption-plan-location $LOC --resource-group $GRP --runtime python --runtime-version 3.11 --functions-version 4 --os-type Linux1.2部署代码
当前代码有一个简单的工具(echo_message)以及一个安全的工具(get_my_profile_info).让我们发布它。
# Publish the Function App
func azure functionapp publish $FUNC1.3烟雾测试
让我们通过测试这个简单的工具来验证MCP服务器是否处于活动状态。
- 在VS Code中,编辑
.vscode/mcp.json文件(在项目的根目录下创建lab-apim-mcp如果它不存在):
{
"mcpServers": {
"azure-func-direct": {
"url": "https://.azurewebsites.net/mcp",
"type": "http"
}
}
}*(替换 ` 与价值 $FUNC.快跑 echo $FUNC` 在终端中查看)。*
- 重新启动VS代码或重新加载窗口。
- 打开Copilot Chat并键入:
> @azure-func-direct echo_message message="Hello Azure Function"
成功: 如果Copilot响应“来自Azure的回声:Hello Azure函数”,则动作1完成。
______________________________________________________________________
第二幕:守护者(Azure MCP网关)
直接访问该功能是不安全的,难以管理。让我们把 Azure API管理(APIM) 前端充当智能MCP网关。
2.1提供APIM
*注意:创建“开发人员”层大约需要30-45分钟。喝咖啡的好时机。*
az apim create --name $APIM --resource-group $GRP --location $LOC --publisher-name "Lab Admin" --publisher-email "admin@lab.com" --sku-name Developer2.2将MCP服务器导入APIM
APIM对MCP服务器(GenAI网关)具有本机支持。我们将通过门户网站进行此操作。
- 转到 Azure门户 > API管理 (
$APIM).
- 在左侧菜单中,选择 MCP服务器.
- 点击 +创建 > 暴露现有的MCP服务器.
- 按如下方式填写表单字段:
后端MCP服务器
- MCP服务器基本url: https://.azurewebsites.net/mcp *(替换 ` 随着 $FUNC`.*
新建MCP服务器
- 显示名称: MCP Lab Gateway - 名字: mcp-lab - 基础路径: lab - 描述: Azure Function acting as MCP Server
- 点击 创建.
*注意:为了简单起见,此设置使用公共后端端点。在生产环境中,始终使用专用端点或ExpressRoute限制隔离您的Function App,以防止直接访问。*
2.3通过网关进行测试
现在让我们将VS代码指向APIM。
- 更新
.vscode/mcp.json:
{
"mcpServers": {
"azure-apim-gw": {
"url": "https://.azure-api.net/lab/mcp",
"type": "http"
}
}
}- 重新启动VS代码。
- 再次测试:
> @azure-apim-gw echo_message message="Hello via APIM"
成功: 现在的流程是:VS代码->APIM->函数。
______________________________________________________________________
第3幕:身份与安全
现在让我们锁上门。没有有效的徽章(JWT令牌),任何人都不应调用APIM。
3.1创建应用程序标识(后端)
我们需要在Microsoft Entra ID中注册我们的API。
# Create App Registration
BACKEND_APP_ID=$(az ad app create --display-name "mcp-lab-backend-$RND" --sign-in-audience AzureADMyOrg --query appId -o tsv)
# Create Service Principal
az ad sp create --id $BACKEND_APP_ID
# Define API URI (api://)
az ad app update --id $BACKEND_APP_ID --identifier-uris "api://$BACKEND_APP_ID"
# Expose "MCP.Execute" scope (Using Python for safe JSON generation)
python3 -c "import sys, uuid, json; scope_id = str(uuid.uuid4()); print(scope_id)" > scope_id.txt
SCOPE_ID=$(cat scope_id.txt) && rm scope_id.txt
python3 -c "import sys, json; scope_id = sys.argv[1]; data = {'oauth2PermissionScopes': [{'adminConsentDescription': 'Access MCP', 'adminConsentDisplayName': 'Access MCP', 'id': scope_id, 'isEnabled': True, 'type': 'User', 'userConsentDescription': 'Access MCP', 'userConsentDisplayName': 'Access MCP', 'value': 'MCP.Execute'}]}; print(json.dumps(data))" "$SCOPE_ID" > scope.json
az ad app update --id $BACKEND_APP_ID --set api=@scope.json
rm scope.json
echo "Backend App ID: $BACKEND_APP_ID"3.2在APIM中应用策略(验证JWT)
让我们配置APIM以拒绝没有令牌的调用。
- 在门户中,转到 MCP服务器 >点击 MCP实验室网关.
- 寻找 政策 选项(或 `` 图标在 入站处理).
- 在中插入验证策略 `` 块:
*
*
*
api://{{YOUR_BACKEND_APP_ID}}
*(替换 {{YOUR_TENANT_ID}} 和 {{YOUR_BACKEND_APP_ID}} 具有实际价值)。*
*如果你不知道你的租户ID,运行az account show来检索它。*
3.3安全测试(预期失败)
尝试使用 @azure-apim-gw echo_message 再次在VS代码中。
预期结果: 您将收到 HTTP 401未经授权 立即出错。这意味着APIM策略正在发挥作用!
______________________________________________________________________
第四幕:代表(OBO)流动
“大结局”。我们将让VS Code发送一个令牌,APIM对其进行验证,然后函数将此令牌交换为另一个令牌以读取您的配置文件。
4.1配置机密和权限
该功能需要“代表用户发言”(OBO)的权限。
# 1. Generate Secret for the App
BACKEND_SECRET=$(az ad app credential reset --id $BACKEND_APP_ID --display-name "OBOSecret" --query password -o tsv)
# 2. Grant Graph permission (User.Read) using Global IDs
MS_GRAPH_ID="00000003-0000-0000-c000-000000000000" # Microsoft Graph API
USER_READ_ID="e1fe6dd8-ba31-4d61-89e7-88639da4683d" # User.Read Scope
az ad app permission add --id $BACKEND_APP_ID --api $MS_GRAPH_ID --api-permissions $USER_READ_ID=Scope
az ad app permission grant --id $BACKEND_APP_ID --api $MS_GRAPH_ID --scope User.Read4.2配置功能应用程序
将凭据发送到云中的Function。
az functionapp config appsettings set --name $FUNC --resource-group $GRP --settings "AZURE_TENANT_ID=$(az account show --query tenantId -o tsv)" "BACKEND_CLIENT_ID=$BACKEND_APP_ID" "BACKEND_CLIENT_SECRET=$BACKEND_SECRET"4.3配置客户端(VS代码)
我们需要一个真正的令牌。
- 预授权: 允许Azure CLI为您的API请求令牌。我们使用
az rest对CLI模式更改具有鲁棒性。
# 1. Get Object ID
BACKEND_OBJ_ID=$(az ad app show --id $BACKEND_APP_ID --query id -o tsv)
# 2. Generate JSON Config (Python one-liner for safe copy-paste)
python3 -c "import sys, json; scope_id = sys.argv[1]; cli_id = sys.argv[2]; data = {'api': {'oauth2PermissionScopes': [{'adminConsentDescription': 'Access MCP', 'adminConsentDisplayName': 'Access MCP', 'id': scope_id, 'isEnabled': True, 'type': 'User', 'userConsentDescription': 'Access MCP', 'userConsentDisplayName': 'Access MCP', 'value': 'MCP.Execute'}], 'preAuthorizedApplications': [{'appId': cli_id, 'delegatedPermissionIds': [scope_id]}]}}; print(json.dumps(data))" "$SCOPE_ID" "$AZURE_CLI_APP_ID" > patch.json
# 3. Apply via Graph API
az rest --method PATCH --uri "https://graph.microsoft.com/v1.0/applications/$BACKEND_OBJ_ID" --headers "Content-Type=application/json" --body @patch.json
rm patch.json- 生成令牌:
# Login (Refreshes session)
az login --tenant $(az account show --query tenantId -o tsv)
# Get Token
az account get-access-token --resource "api://$BACKEND_APP_ID" --query accessToken -o tsv*复制生成的令牌。*
- 配置MCP客户端:
更新 .vscode/mcp.json 包含您的令牌 直接. *(注意:我们在这里粘贴令牌是因为一些VS Code扩展尚不支持交互式输入提示。)*
{
"mcpServers": {
"azure-obo-final": {
"url": "https://.azure-api.net/lab/mcp",
"type": "http",
"headers": {
"Authorization": "Bearer YOUR_LONG_TOKEN_HERE"
}
}
}
}4.4最终测试
- 重新加载VS Code窗口。
- 打开Copilot Chat并调用安全工具:
> @azure-obo-final get_my_profile_info
预期结果: 工具应作出响应 “成功!OBO Flow工作正常”,显示从Microsoft Graph安全检索到的您的姓名和职位。
______________________________________________________________________
祝贺 你已经完成了旅程。
清理
*不要忘记删除此实验室期间创建的所有资源,以避免持续成本。*
az group delete --name $GRP --yes --no-wait