从 Agent Runtime 到外部 MCP 的 Agent Gateway 出站流量

1. 简介

本 Codelab 探讨了 AI 智能体访问 Google Cloud 外部托管的外部 Model Context Protocol (MCP) 服务器时的智能体网关出站流量治理。

从独立聊天机器人到具有多智能体工作流的自主系统,AI 应用可以动态调用外部工具。为代理提供受控的数据库查询、网页内容提取和操作执行权限,对于确保代理的安全性和高效性至关重要。不过,在企业环境中,确保代理工具执行的安全性是一项挑战。如果代理具有直接网络访问权限,提示注入攻击或模型幻觉可能会导致代理泄露敏感数据或运行破坏性命令。

为了大规模管理自主智能体,Agent Gateway 提供了一个集中式零信任强制执行点,可直接集成到 Agent Platform 架构中。Agent Gateway 不依赖于每个应用或代理代码特有的自定义实现,而是在平台级强制执行网络路由、代理治理和运行时安全性。

当 Agent Gateway 以出站流量(代理到任何位置)模式运行时,它会代理配置为使用该网关的代理的所有出站请求。每个代理工作负载请求都使用唯一的代理身份进行身份验证,并使用 Identity-Aware Proxy (IAP) 政策进行授权。系统会动态解码和检查 MCP 工具调用,以强制执行政策。安全管理员可以集中强制执行精细的权限,以确保代理只能调用经过批准的端点和方法。

构建内容

  • 出站流量(代理到任意目的地)模式下的 Agent Gateway
  • Identity-Aware Proxy (IAP) 授权扩展程序
  • 具有代理身份的 Agent Runtime ADK 代理
  • 包含 Google API 和 MCP 端点的代理注册表
  • 外部 MCP 工具与 Data Commons 公共数据集的连接
  • 使用 IAM 访问权限控制的授权政策

figure1

图 1. Codelab 架构

学习内容

  • 如何在结构化配置序列中部署 Agent Gateway
  • 如何将授权政策从试运行模式过渡到强制执行模式
  • 如何在代理注册表中注册 Google API 端点和 MCP 服务器
  • 如何使用基于 MCP 协议属性的条件性授权政策
  • 如何审核 Agent Gateway 日志以验证出站流量治理

所需条件

  • 启用了结算功能的 Google Cloud 项目
  • 用于配置网络服务和 Agent Platform 资源的 IAM 权限
  • 安装了 Google Cloud CLI(gcloudbq 组件)的 POSIX 兼容 shell(bashzsh
  • 命令行工具:gitcurljq (JSON 处理器)、Python 3 和 uv (Python 软件包管理器)
  • 用于访问查询测试数据集的免费 Data Commons API 密钥

2. 概念

部署序列

此 Codelab 遵循结构化的配置顺序,以便代理在初始化时能够访问必要的资源,并且可以在应用强制执行的访问权限政策之前确认连接。

此 Codelab 使用以下部署顺序:

  1. DRY_RUN 模式启动:确保代理请求成功到达网关并顺利通过(仅限审核模式)。
  2. 注册所有服务:在代理注册表中注册所有代理组件,并确认代理工具访问权限。
  3. 创建授权政策:创建并应用授权政策,以允许从代理到 MCP 服务器和端点的出站流量。
  4. 切换到 ENFORCED 模式:更新授权扩展程序政策,以强制执行政策并阻止未经授权的出站流量。

figure2

图 2. 部署序列

出站流量路由拓扑

Agent Gateway 出站 (agent-to-anywhere) 充当智能体工作负载的集中式零信任出站代理。当代理向外部工具或 API 发出请求时,代理网关会拦截出站请求,并根据治理政策对其进行评估,然后再将其路由到目的地。在 Google Cloud Agent Platform 架构中,Agent Gateway 提供数据平面连接,以实现以下功能:

  • 专用网络 (VPC):出站流量的目标是内部企业 API、微服务、托管在专用 VPC 中的数据库,以及可通过混合连接访问的本地或跨云网络。
  • 外部网络(互联网):以第三方 Web 服务、SaaS API 或公共端点为目标的出站流量。
  • AI 服务和 Agent Platform:出站流量,目标是受管理的 Google Cloud API、基础模型、Google MCP 端点(例如 BigQuery)和平台治理服务(Agent Registry 和 IAP 政策)。

figure3

图 3. Agent Gateway 出站流量路由拓扑

本 Codelab 的重点是 Agent Runtime 上自定义代理的出站流量,该流量的目标是可通过互联网访问的外部第三方 MCP 服务器端点。

MCP 协议检查

代理网关出站流量(代理到任何位置)能够解析 MCP 请求的内容,并根据精细的协议属性评估访问权限政策。代理到 MCP 服务器的出站政策可以包含以通用表达式语言 (CEL) 表示的条件,这些条件会在运行时针对每个请求强制执行。

MCP 调用通常使用 JSON-RPC 有线协议格式和 HTTP 传输。HTTP 正文中的 JSON 载荷包含所调用的 MCP 方法的详细信息(例如 tools/call)、目标工具名称和任何实参。

代理网关通过解析 JSON 正文来提取 iap.googleapis.com/mcp.toolName 属性,以查找请求中指定的工具标识符。

iap.googleapis.com/mcp.tool.isReadOnlymcp.tool.isDestructive 等属性通常不会在每个请求正文中发送。相反,这些是与工具相关联的注释(元数据)。因此,当 Agent Gateway 从正文中提取 toolName 时,它会从注册了 toolspec.json 内容的 Agent Registry 中查找工具定义和关联的属性。

figure4

图 4. 代理网关 MCP 检查

IAP IAM 条件中使用的 CEL 表达式采用标准格式 api.getAttribute('iap.googleapis.com/ATTRIBUTE_NAME', 'DEFAULT_VALUE')== 'DESIRED_VALUE'

下表介绍了可应用于 MCP 服务器请求的代理网关出站流量治理属性:

属性

MCP 方法 / 位置

说明

mcp.toolName

tools/call(正文)

RPC 载荷中请求的目标工具标识符(例如 "delete_instance"

mcp.tool.isReadOnly

Agent Registry (toolspec.json)

指明工具是否为只读(不修改其环境)

mcp.tool.isDestructive

Agent Registry (toolspec.json)

指示调用该工具是否会导致不可逆的修改或数据丢失

mcp.tool.isIdempotent

Agent Registry (toolspec.json)

表示使用相同实参重复执行工具是否会产生完全相同的状态

mcp.tool.isOpenWorld

Agent Registry (toolspec.json)

指示工具是否超出有界内部系统范围,到达无界公共互联网

概念部分到此结束,接下来进入设置部分。

3. 设置

所需 IAM 角色

您必须拥有以下角色,才能在此 Codelab 中创建资源:

类别

所需 IAM 角色 (ID)

说明

API 管理

roles/serviceusage.serviceUsageAdmin

启用 Google Cloud API 服务

网络和网关

roles/networkservices.admin

配置 Agent Gateway

Service Extensions

roles/serviceextensions.admin

配置路由扩展程序

网络安全

roles/networksecurity.admin

部署授权政策

Agent Registry

roles/agentregistry.admin

目录允许的主机

Vertex AI 和代理

roles/aiplatform.admin

部署 Agent Runtime 工作负载

出站流量政策

roles/iap.admin

应用 roles/iap.egressor 项政策

Cloud Storage

roles/storage.admin

管理部署过渡存储分区

日志和审核

roles/logging.viewer

检查轨迹和审核日志

或者,您也可以使用广泛的基本角色(例如 roles/admin)或旧版角色 roles/owner

访问您的项目

本 Codelab 使用单个 Google Cloud 项目。配置步骤使用 gcloud CLI 和 Linux shell 命令。

首先,访问 Google Cloud 云项目命令行:

设置项目 ID

gcloud config set project SET_YOUR_PROJECT_ID_HERE

验证会话

# login to gcloud cli
gcloud auth login
# login for gcloud api
gcloud auth application-default login

设置 shell 环境变量

# set custom var for slug (eg, "foo") and region preference
export SLUG="foo"
export REGION="us-central1"
export MREGION="us"
echo ${SLUG}
echo ${REGION}
echo ${MREGION}
# create project vars (automatic)
export PROJ_ID=$(gcloud config list --format="value(core.project)")
export PROJ_NO=$(gcloud projects describe ${PROJ_ID} --format="value(projectNumber)")
export ORG_ID=$(gcloud projects get-ancestors ${PROJ_ID} --format="value(id)" | tail -n 1)
echo ${PROJ_ID}
echo ${PROJ_NO}
echo ${ORG_ID}
# create resource vars (automatic)
export AGW_NAME="agw-${SLUG}-${REGION}-ata"
export AGW_URI="projects/${PROJ_ID}/locations/${REGION}/agentGateways/${AGW_NAME}"
export RE_AGENT_NAME="agent-datacommons"
export RE_AGENT_ID_SET="principalSet://agents.global.org-${ORG_ID}.system.id.goog/attribute.platformContainer/aiplatform/projects/${PROJ_NO}"
export STAGING_BUCKET="agent-staging-${PROJ_NO}"
export MCP_DISPLAY_NAME="api.datacommons.org"
export MCP_RESOURCE_NAME="${REGION}-datacommons-mcp"
export MCP_URL="https://api.datacommons.org/mcp"
echo ${AGW_NAME}
echo ${AGW_URI}
echo ${RE_AGENT_NAME}
echo ${RE_AGENT_ID_SET}
echo ${STAGING_BUCKET}
echo ${MCP_DISPLAY_NAME}
echo ${MCP_RESOURCE_NAME}
echo ${MCP_URL}
# create local dir for config files
mkdir -p cfg

如果您运行的是自行管理的 Google Cloud SDK 安装(即在 Cloud Shell 外部),请将组件更新到最新版本。

# update gcloud cli
gcloud components update

启用 API 服务

# enable google apis (agent platform bundle, part 1)
gcloud services enable \
  agentregistry.googleapis.com \
  aiplatform.googleapis.com \
  apphub.googleapis.com \
  apptopology.googleapis.com \
  cloudapiregistry.googleapis.com \
  cloudtrace.googleapis.com \
  compute.googleapis.com \
  dataform.googleapis.com \
  iam.googleapis.com \
  iamconnectors.googleapis.com \
  iap.googleapis.com \
  logging.googleapis.com \
  modelarmor.googleapis.com \
  monitoring.googleapis.com \
  networksecurity.googleapis.com \
  networkservices.googleapis.com \
  notebooks.googleapis.com \
  observability.googleapis.com
# enable google apis (agent platform bundle, part 2)
gcloud services enable \
  securitycenter.googleapis.com \
  saasservicemgmt.googleapis.com \
  storage.googleapis.com \
  telemetry.googleapis.com \
  texttospeech.googleapis.com

设置部分到此结束…接下来请参阅网关部分。

4. 网关

在强制执行访问权限控制之前,请部署 Agent Gateway 并在 DRY_RUN 模式下配置其授权扩展程序。这样一来,出站工具调用便可成功完成,同时记录评估结果以供审核。

在此示例中,Agent Gateway 使用区域级注册表来支持区域级 Agent Runtime 资源。

创建网关

# create agent gateway config file (regional registry)
cat > cfg/${AGW_NAME}.yaml << EOF
name: ${AGW_NAME}
protocols:
  - MCP
googleManaged:
  governedAccessPath: AGENT_TO_ANYWHERE
registries:
  - "//agentregistry.googleapis.com/projects/${PROJ_ID}/locations/${REGION}"
EOF
# import agent gateway config file (create gateway)
gcloud network-services agent-gateways import ${AGW_NAME} \
  --source="cfg/${AGW_NAME}.yaml" \
  --location=${REGION}
# show agent gateway details (verify deployment state)
gcloud network-services agent-gateways describe ${AGW_NAME} \
  --location=${REGION}

网关部分到此结束…接下来进入授权部分。

5. 授权

Identity-Aware Proxy (IAP) 的 Agent Gateway 授权扩展程序是一种Service Extension,用于委托所有Agent Platform通信的授权决策。

  1. Agent Gateway 流程:当代理尝试调用外部端点或 MCP 服务器时,它会将请求路由到 Agent Gateway。网关不进行本地访问权限评估,而是使用授权扩展程序向 IAP 评估服务发送回调。IAP 会根据代理注册表中目标资源的 IAM 政策评估代理(基于 SPIFFE)身份。IAP 会向网关返回 ALLOWDENY 判定结果,网关会转发流量或使用 HTTP 403 Forbidden 状态代码阻止流量。
  2. 强制执行模式:在 DRY_RUN 模式下,IAP 会评估请求并在 Cloud Logging 中记录决策,而不会阻止流量。在 ENFORCE 模式下,来自未经授权的代理或发送到未注册的目标的任何请求都会立即被屏蔽。
  3. 绑定层:服务扩展程序使用配置了 REQUEST_AUTHZ 配置文件的授权政策连接到 Agent Gateway。

授权扩展程序

创建授权扩展服务

# create authz extension config file (dry run mode)
cat > cfg/${AGW_NAME}-svc-ext-authz-iap-dryrun.yaml << EOF
name: ${AGW_NAME}-svc-ext-authz-iap-dryrun
service: iap.googleapis.com
failOpen: true
timeout: 1s
metadata:
  iamEnforcementMode: "DRY_RUN"
  iapPolicyVersion: "V1"
EOF
# import authz extension file (create extension)
gcloud service-extensions authz-extensions import ${AGW_NAME}-svc-ext-authz-iap-dryrun \
  --source=cfg/${AGW_NAME}-svc-ext-authz-iap-dryrun.yaml \
  --location=${REGION}
# show authz extension details (verify extension state)
gcloud service-extensions authz-extensions describe ${AGW_NAME}-svc-ext-authz-iap-dryrun \
  --location=${REGION}

授权政策

授权政策将安全提供方 (IAP) 绑定到目标网关资源,并指定拦截配置文件 (REQUEST_AUTHZ)。

创建授权政策

# create authz policy config file (attach dry-run authz extension)
cat > cfg/${AGW_NAME}-authz-policy-profile-iap.yaml << EOF
name: ${AGW_NAME}-authz-policy-profile-iap
target:
  resources:
    - "projects/${PROJ_ID}/locations/${REGION}/agentGateways/${AGW_NAME}"
policyProfile: REQUEST_AUTHZ
action: CUSTOM
customProvider:
  authzExtension:
    resources:
      - "projects/${PROJ_ID}/locations/${REGION}/authzExtensions/${AGW_NAME}-svc-ext-authz-iap-dryrun"
EOF
# import authz policy config file (enable authz policy)
gcloud beta network-security authz-policies import ${AGW_NAME}-authz-policy-profile-iap \
  --source=cfg/${AGW_NAME}-authz-policy-profile-iap.yaml \
  --location=${REGION}
# show authz policy details (verify dry run mode)
gcloud beta network-security authz-policies describe ${AGW_NAME}-authz-policy-profile-iap \
  --location=${REGION}

授权部分到此结束…接下来进入代码库部分。

6. 代码库

此 Codelab 中使用的代理代码和端点注册脚本维护在远程 Google Cloud GitHub 代码库中。以下步骤会将代码库克隆到本地,将必要的文件复制到当前工作目录结构,然后清理临时文件。

系统会为 Agent Runtime 创建一个存储暂存桶,以便上传、构建和部署打包的代理应用代码及其依赖项制品。

提取远程制品

# clone remote repository to temp local dir
git clone https://github.com/GoogleCloudPlatform/cloud-networking-solutions.git ./temp_agw_cuj_arun_egress_emcp
# copy agent runtime and endpoint definitions to working project dir
cp -r temp_agw_cuj_arun_egress_emcp/codelabs/agw-cuj-arun-egress-emcp/agent-datacommons ./agent-datacommons
cp -r temp_agw_cuj_arun_egress_emcp/codelabs/agw-cuj-arun-egress-emcp/endpoints ./endpoints
# remove temporary directory
rm -rf temp_agw_cuj_arun_egress_emcp

创建临时存储分区

# create storage bucket for agent deployment artifacts
gcloud storage buckets create gs://${STAGING_BUCKET} --location=${REGION}
# verify staging bucket url
gcloud storage buckets list --format="value(storage_url)"

代码库部分到此结束…接下来是注册表部分。

7. 注册表

Agent Registry 是 AI 生态系统中所有代理、MCP 服务器和端点 (API) 的集中式清单。它还是一个目录,可用于列出、搜索和发现其他已注册的工具和服务,以供 AI 应用使用。代理网关出站 (agent-to-anywhere) 模式使用注册表数据模型作为治理框架来强制执行出站访问权限控制。系统会根据绑定到注册表资源的政策来检查主账号调用代理的 IAM 角色授予情况。

为了引导环境并支持使用各种 Google API 和服务的代理,系统使用脚本通过 endpoints/googleapis.txt 中的主机名和位置快速注册一组常见的 API 端点。

注册端点

# run python script to register google api endpoints
python3 endpoints/register_endpoints.py \
  --multi-region=${MREGION} \
  --region=${REGION} \
  --mtls-endpoints=include

验证端点

# list registry regional endpoints (verify registration)
gcloud agent-registry endpoints list --location=${REGION} \
  --format="table(displayName, name.basename():label=ENDPOINT_ID, interfaces[0].url:label=URL)"

注册部分到此结束…接下来是 Data Commons 部分。

8. Data Commons

Data Commons 是 Google 推出的一项计划,旨在整理公共数据集,并让任何人都可以使用和访问这些数据。这是一项免费服务,创建账号后即可使用 API 密钥访问该服务。

创建 API 密钥

创建账号后,请按照以下步骤获取 API 密钥:

  • 登录 Data Commons API 门户
  • 点击右上角的“我的应用”按钮
  • 点击右上角用户名下方的“+ 新应用”按钮
  • 为应用命名(例如“Codelab Agent MCP”)
  • 点击 Data Commons API 的“启用”按钮 api.datacommons.org
  • 点击右下角的“保存”按钮

这会创建 API 密钥和 Secret。点击密钥值旁边的“复制”图标以“复制 API 密钥”。将该值粘贴到终端命令中,以设置 DC_API_KEY 环境变量。

# set api key env var
 export DC_API_KEY="YOUR_API_KEY_HERE"

创建 MCP 服务器 toolspec

手动注册 MCP 服务器时,必须在注册期间添加工具规范文件。上传 toolspec.json 文件会列出端点提供的所有工具,并让其他用户发现这些工具。

agent-datacommons/ 代码库中包含的 test_mcp.py 脚本通过连接到目标 MCP 服务器(通过 SSE 或 HTTP)来生成 toolspec.json 文件,并调用 list_tools() 来检索服务器公开的所有可用工具。

# generate toolspec for registry ingestion
uv --directory agent-datacommons run python3 test_mcp.py --toolspec=include --token="${DC_API_KEY}"

查看生成的 toolspec 输出,其中显示了代理网关可用于出站政策评估和强制执行的 MCP 属性。

# show toolspec mcp attributes
jq '.tools[] | {name, isReadOnly, isDestructive, isIdempotent, isOpenWorld}' agent-datacommons/toolspec.json

注册 MCP 服务器

# register mcp server
gcloud agent-registry services create ${MCP_RESOURCE_NAME} \
  --location=${REGION} \
  --display-name="${MCP_DISPLAY_NAME}" \
  --description="mcp server for data commons" \
  --mcp-server-spec-type=tool-spec \
  --mcp-server-spec-content=agent-datacommons/toolspec.json \
  --interfaces=url=${MCP_URL},protocolBinding=JSONRPC
# list registry regional mcp servers
gcloud agent-registry mcp-servers list --location=${REGION} --format="table(displayName, interfaces.url)"
# show mcp server registry details
gcloud agent-registry services describe ${MCP_RESOURCE_NAME} --location=${REGION}
# fetch mcp server registry id
export MCP_REGISTRY_ID=$(gcloud agent-registry services describe ${MCP_RESOURCE_NAME} \
  --location=${REGION} \
  --format="value(registryResource.basename())")
echo ${MCP_REGISTRY_ID}

“Data Commons”部分到此结束,接下来是运行时部分。

9. 运行时

部署到 Agent Runtime 的 agent-datacommons ADK 代理在部署脚本中配置了以下设置,以便与 Agent Platform 集成:

  • "identity_type": types.IdentityType.AGENT_IDENTITY 为代理预配基于 SPIFFE 的唯一主账号身份
  • "agent_gateway_config": { "agent_to_anywhere_config": {"agent_gateway": } } 将所有出站的智能体发起的流量定向到 Agent Gateway,以进行政策评估和执行

部署代理

# deploy agent runtime workload
uv --directory agent-datacommons run python3 deploy_agent.py \
  --project=${PROJ_ID} \
  --region=${REGION} \
  --src-dir=./agent \
  --staging-bucket=${STAGING_BUCKET} \
  --display-name="${RE_AGENT_NAME}" \
  --description="agent for data commons stats" \
  --enable-telemetry \
  --enable-agent-identity \
  --agent-gateway-egress=${AGW_URI} \
  --env-var="DC_API_KEY=${DC_API_KEY}"

提取部署生命体征

# fetch agent runtime resource (reasoning engine) id
export RE_ENGINE_ID=$(curl -s -X GET \
  "https://${REGION}-aiplatform.googleapis.com/v1/projects/${PROJ_ID}/locations/${REGION}/reasoningEngines" \
  -H "Authorization: Bearer $(gcloud auth application-default print-access-token)" | \
  jq -r --arg name "${RE_AGENT_NAME}" '.reasoningEngines[] | select(.displayName==$name) | .name | split("/") | last')
echo ${RE_ENGINE_ID}
# fetch agent runtime (reasoning engine) agent identity
export RE_AGENT_IDENTITY=$(gcloud agent-registry agents list \
  --location=${REGION} --filter="displayName=${RE_AGENT_NAME}" \
  --format="value(attributes.'agentregistry.googleapis.com/system/RuntimeIdentity'.principal)")
echo ${RE_AGENT_IDENTITY}
# fetch agent registry uid for agent
export RE_AGENT_REGISTRY_ID=$(gcloud agent-registry agents list \
  --location=${REGION} \
  --filter="displayName=${RE_AGENT_NAME}" \
  --format="value(uid)")
echo ${RE_AGENT_REGISTRY_ID}

验证注册表条目

# show agent resource registry details
gcloud agent-registry agents describe ${RE_AGENT_REGISTRY_ID} --location=${REGION}

10. 政策

代理治理授权政策允许流量从 AI 代理通过代理网关到达目的地(例如,SaaS 应用的第三方 MCP 服务器端点)。当流量到达目的地后,常规的服务级或应用级权限会授权数据访问。

针对 Agent Gateway 出站流量的 IAM 角色授予可启用访问政策,允许代理主账号身份通过 Agent Gateway 访问已注册的目标资源。政策在 Agent Gateway 级别强制执行,并使用 Identity-Aware Proxy (IAP) IAM 政策进行验证。

为代理网关出站流量授予代理 IAM 角色

Agent Gateway 会检查传入的请求,以验证调用代理身份是否对目标资源具有 iap.webServiceVersions.egressViaIAP 权限(由角色 roles/iap.egressor 授予)。

向所选目标资源上的代理身份或 Agent Runtime 正文集授予 roles/iap.egressor 权限,即可允许此出站流量。在此 Codelab 中,该角色应用于主账号 agent,从而向特定的 Agent Runtime 代理身份授予权限。

IAM 政策会绑定到代理注册表数据模型中定义的目标资源。iap_web/agentRegistry 资源表示 IAM 层次结构中的“注册表级”级别。其中,agentRegistryagentsmcpServersendpoints 的各个子资源的父级。在此 Codelab 中,IAM 政策绑定到 mcpServerendpoint 级别,从而将权限应用于特定的子资源。

有条件的 MCP 过滤简介

Data Commons MCP 服务器包含两个工具:

  • search_indicators:用于查询有关发现哪些数据可用、搜索变量或了解数据覆盖范围的信息
  • get_observations:用于检索特定数据点或时间序列的查询

在 MCP 服务器上配置有条件治理政策时,需要注意一些标准 MCP 操作。在 MCP 客户端可以针对 search_indicatorsget_observations 发出 tools/call 之前,必须先发送协议握手和发现请求:

  • initialize
  • notifications/initialized
  • tools/list

为了演示条件政策过滤,我们将为以下条件配置政策:

表格。针对 MCP 请求的政策评估

请求 / 方法

mcp.toolName

CEL 评估

结果

initialize

""

"" in ['search_indicators', '']

✅ 允许 (200)

tools/list

""

"" in ['search_indicators', '']

✅ 允许 (200)

search_indicators

"search_indicators"

"search_indicators" in [...]

✅ 允许 (200)

get_observations

"get_observations"

"get_observations" in [...]

❌ DENIED (403)

为了允许协议握手 search_indicators 工具调用,条件政策规则包含对 "search_indicators"""(空字符串)的允许。

配置注册表 IAP MCP 服务器 IAM 政策

# show iap iam policy for mcp server
gcloud beta iap web get-iam-policy \
  --region=${REGION} \
  --resource-type=agent-registry \
  --mcp-server=${MCP_REGISTRY_ID}

注意: 由于尚未定义任何政策,etag: 应返回默认值 ACAB

# fetch active etag on policy for mcp server
export IAP_ETAG=$(gcloud beta iap web get-iam-policy \
  --resource-type=agent-registry --region=${REGION} \
  --mcp-server=${MCP_REGISTRY_ID} --format="value(etag)")
echo ${IAP_ETAG}
# create policy config file
cat > cfg/iap-policy-dc-agent-to-dc-mcp-tool-1.json << EOF
{
  "bindings": [
    {
      "role": "roles/iap.egressor",
      "members": [
        "${RE_AGENT_IDENTITY}"
      ],
      "condition": {
        "title": "allow ${RE_AGENT_NAME} to use search_indicators tool for ${MCP_DISPLAY_NAME}",
        "expression": "api.getAttribute('iap.googleapis.com/mcp.toolName', '') in ['search_indicators', '']"
      }
    }
  ],
  "etag": "${IAP_ETAG}",
  "version": 3
}
EOF
# import policy file (bind iam policy)
gcloud beta iap web set-iam-policy cfg/iap-policy-dc-agent-to-dc-mcp-tool-1.json \
  --resource-type=agent-registry \
  --mcp-server=${MCP_REGISTRY_ID} \
  --region=${REGION}
# verify iap iam policy for mcp server
gcloud beta iap web get-iam-policy \
  --region=${REGION} \
  --resource-type=agent-registry \
  --mcp-server=${MCP_REGISTRY_ID}

政策部分到此结束…接下来是审核部分。

11. 审核

Agent Gateway 授权扩展程序以 DRY_RUN 模式运行。系统会观察并记录出站请求,但不会阻止这些请求。

测试代理查询

打开浏览器窗口,进入 Google Cloud 控制台界面,然后依次前往:

  • Agent Platform → 代理 → 部署 → agent-datacommons
  • 选择实验标签页

或者,您也可以回显此终端命令,然后点击链接跳转到代理游乐场:

# playground
echo "https://console.cloud.google.com/agent-platform/runtimes/locations/${REGION}/agent-engines/${RE_ENGINE_ID}/playground?project=${PROJ_ID}"

提交一些测试查询。

  • What data do you have on water quality in Zimbabwe?
  • Compare the life expectancy, economic inequality, and GDP growth for BRICS nations

验证查询是否返回有效响应。

分析审核日志

查看日志并检查试运行政策评估。

# list unique urls with http status and iap authorization result
gcloud logging read \
  "resource.type=\"networkservices.googleapis.com/Gateway\" \
  AND httpRequest.requestUrl:*" \
  --project=${PROJ_ID} \
  --limit=200 \
  --format="value(httpRequest.status, jsonPayload.authzPolicyInfo.result, httpRequest.requestUrl)" | sort -u
200     ALLOWED https://api.datacommons.org/mcp
200     ALLOWED https://cloudresourcemanager.googleapis.com:443/google.cloud.resourcemanager.v3.Projects/GetProject
200     ALLOWED https://telemetry.googleapis.com/v1/traces
200     ALLOWED https://${REGION}-aiplatform.googleapis.com/v1beta1/projects/${PROJ_ID}/locations/${REGION}/publishers/google/models/gemini-2.5-flash:generateContent
200     ALLOWED https://${REGION}-aiplatform.googleapis.com/v1beta1/projects/${PROJ_ID}/locations/${REGION}/reasoningEngines/${RE_ENGINE_ID}/sessions/${RE_SESSION_ID}
200     ALLOWED https://${REGION}-aiplatform.googleapis.com/v1beta1/projects/${PROJ_ID}/locations/${REGION}/reasoningEngines/${RE_ENGINE_ID}/sessions/${RE_SESSION_ID}:appendEvent
200     ALLOWED https://${REGION}-aiplatform.googleapis.com/v1beta1/projects/${PROJ_ID}/locations/${REGION}/reasoningEngines/${RE_ENGINE_ID}/sessions/${RE_SESSION_ID}/events
202     ALLOWED https://api.datacommons.org/mcp

更新注册表 IAP 端点 IAM 政策

为遥测端点授予代理出站权限

# set var for endpoint display name
export ENDPOINT_1_DISPLAY_NAME="telemetry.googleapis.com"
echo ${ENDPOINT_1_DISPLAY_NAME}
# fetch endpoint registry id
export ENDPOINT_1_REGISTRY_ID=$(gcloud agent-registry services list \
  --location=${REGION} \
  --filter="displayName=${ENDPOINT_1_DISPLAY_NAME}" \
  --format="value(registryResource.basename())")
echo ${ENDPOINT_1_REGISTRY_ID}
# create policy config file
cat > cfg/iap-policy-re-agent-to-ep-1.json << EOF
{
  "bindings": [
    {
      "role": "roles/iap.egressor",
      "members": [
        "${RE_AGENT_IDENTITY}"
      ]
    }
  ]
}
EOF
# import policy file (bind iam policy)
gcloud -q beta iap web set-iam-policy cfg/iap-policy-re-agent-to-ep-1.json \
  --resource-type=agent-registry \
  --endpoint=${ENDPOINT_1_REGISTRY_ID} \
  --region=${REGION}
# verify iap iam policy for telemetry endpoint
gcloud beta iap web get-iam-policy \
  --region=${REGION} \
  --resource-type=agent-registry \
  --endpoint=${ENDPOINT_1_REGISTRY_ID}

为 aiplatform 端点授予代理出站权限

# set var for endpoint display name
export ENDPOINT_2_DISPLAY_NAME="${REGION}-aiplatform.googleapis.com"
echo ${ENDPOINT_2_DISPLAY_NAME}
# fetch endpoint registry id
export ENDPOINT_2_REGISTRY_ID=$(gcloud agent-registry services list \
  --location=${REGION} \
  --filter="displayName=${ENDPOINT_2_DISPLAY_NAME}" \
  --format="value(registryResource.basename())")
echo ${ENDPOINT_2_REGISTRY_ID}
# create policy config file
cat > cfg/iap-policy-re-agent-to-ep-2.json << EOF
{
  "bindings": [
    {
      "role": "roles/iap.egressor",
      "members": [
        "${RE_AGENT_IDENTITY}"
      ]
    }
  ]
}
EOF
# import policy file (bind iam policy)
gcloud -q beta iap web set-iam-policy cfg/iap-policy-re-agent-to-ep-2.json \
  --resource-type=agent-registry \
  --endpoint=${ENDPOINT_2_REGISTRY_ID} \
  --region=${REGION}
# verify iap iam policy for aiplatform endpoint
gcloud beta iap web get-iam-policy \
  --region=${REGION} \
  --resource-type=agent-registry \
  --endpoint=${ENDPOINT_2_REGISTRY_ID}

为 cloudresourcemanager 端点授予代理出站权限

# set var for endpoint display name
export ENDPOINT_3_DISPLAY_NAME="cloudresourcemanager.googleapis.com"
echo ${ENDPOINT_3_DISPLAY_NAME}
# fetch endpoint registry id
export ENDPOINT_3_REGISTRY_ID=$(gcloud agent-registry services list \
  --location=${REGION} \
  --filter="displayName=${ENDPOINT_3_DISPLAY_NAME}" \
  --format="value(registryResource.basename())")
echo ${ENDPOINT_3_REGISTRY_ID}
# create policy config file
cat > cfg/iap-policy-re-agent-to-ep-3.json << EOF
{
  "bindings": [
    {
      "role": "roles/iap.egressor",
      "members": [
        "${RE_AGENT_IDENTITY}"
      ]
    }
  ]
}
EOF
# import policy file (bind iam policy)
gcloud -q beta iap web set-iam-policy cfg/iap-policy-re-agent-to-ep-3.json \
  --resource-type=agent-registry \
  --endpoint=${ENDPOINT_3_REGISTRY_ID} \
  --region=${REGION}
# verify iap iam policy for cloudresourcemanager endpoint
gcloud beta iap web get-iam-policy \
  --region=${REGION} \
  --resource-type=agent-registry \
  --endpoint=${ENDPOINT_3_REGISTRY_ID}

审核部分到此结束…接下来进入强制执行部分。

12. 强制执行

代理能够以 DRY_RUN 模式通过网关成功发出请求。通过更新授权政策,将代理网关 IAP 授权扩展程序过渡到 ENFORCE 模式。

更新授权扩展程序

# create authz extension config file (enforced mode)
cat > cfg/${AGW_NAME}-svc-ext-authz-iap-enforced.yaml << EOF
name: ${AGW_NAME}-svc-ext-authz-iap-enforced
service: iap.googleapis.com
failOpen: false
timeout: 1s
metadata:
  iapPolicyVersion: "V1"
EOF
# import authz extension file (create extension)
gcloud service-extensions authz-extensions import ${AGW_NAME}-svc-ext-authz-iap-enforced \
  --source=cfg/${AGW_NAME}-svc-ext-authz-iap-enforced.yaml \
  --location=${REGION}
# list authz extensions (imported configs)
gcloud service-extensions authz-extensions list --location=${REGION}

更新授权政策

# re-write authz policy config file (pointing to enforced authz extension)
cat > cfg/${AGW_NAME}-authz-policy-profile-iap.yaml << EOF
name: ${AGW_NAME}-authz-policy-profile-iap
target:
  resources:
    - "projects/${PROJ_ID}/locations/${REGION}/agentGateways/${AGW_NAME}"
policyProfile: REQUEST_AUTHZ
action: CUSTOM
customProvider:
  authzExtension:
    resources:
      - "projects/${PROJ_ID}/locations/${REGION}/authzExtensions/${AGW_NAME}-svc-ext-authz-iap-enforced"
EOF
# import authz policy config file (enable new authz policy)
gcloud beta network-security authz-policies import ${AGW_NAME}-authz-policy-profile-iap \
  --source=cfg/${AGW_NAME}-authz-policy-profile-iap.yaml \
  --location=${REGION}
# show authz policy details (verify enforced mode)
gcloud beta network-security authz-policies describe ${AGW_NAME}-authz-policy-profile-iap \
  --location=${REGION}

验证有条件允许

返回控制台界面中的代理 Playground

提交额外的测试查询(尝试调用允许search_indicators 工具)…

  • What data topics do you have for Peru?

观察到代理对 Gemini 基础模型 (${REGION}-aiplatform.googleapis.com/...)、遥测 API (telemetry.googleapis.com/...) 和 Data Commons MCP 服务器端点 (api.datacommons.org/mcp) 的请求是通过 HTTP 200 OK 状态代码传递的。

# show gateway logs for http requests
gcloud logging read \
  "resource.type=\"networkservices.googleapis.com/Gateway\" \
  AND httpRequest.requestUrl:*" \
  --project=${PROJ_ID} \
  --limit=10 \
  --format="table(
    timestamp.date(tz=LOCAL):label=TIMESTAMP,
    httpRequest.requestMethod:label=METHOD,
    httpRequest.status:label=STATUS,
    jsonPayload.authzPolicyInfo.result:label=IAP_AUTHZ,
    jsonPayload.agentGatewayInfo.mcpInfo.method:label=MCP_METHOD,
    httpRequest.requestUrl:label=URL)"

检查是否有请求被屏蔽。在网关审核日志中查找任何被拒绝的出站流量尝试,这些尝试具有 HTTP 403 Forbidden 状态代码。

# show gateway logs for authz denies
gcloud logging read \
  "resource.type=\"networkservices.googleapis.com/Gateway\" \
  AND (jsonPayload.authzPolicyInfo.result=\"DENIED\" OR httpRequest.status=403)" \
  --project=${PROJ_ID} \
  --limit=10 \
  --format="table(
    timestamp.date(tz=LOCAL):label=TIMESTAMP,
    httpRequest.requestMethod:label=METHOD,
    httpRequest.status:label=STATUS,
    jsonPayload.enforcedGatewaySecurityPolicy.serverNameIndication:label=SNI,
    jsonPayload.authzPolicyInfo.result:label=AUTHZ_RESULT,
    jsonPayload.authzPolicyInfo.policies[0].name.basename():label=DENYING_POLICY
  )"
# show gateway logs for mcp requests with method and tool name
gcloud logging read \
  "resource.type=\"networkservices.googleapis.com/Gateway\" \
  AND jsonPayload.agentGatewayInfo.mcpInfo.method:*" \
  --project=${PROJ_ID} \
  --limit=10 \
  --format="table(
    timestamp.date(tz=LOCAL):label=TIMESTAMP,
    httpRequest.status:label=STATUS,
    jsonPayload.authzPolicyInfo.result:label=IAP_AUTHZ,
    jsonPayload.agentGatewayInfo.mcpInfo.method:label=MCP_METHOD,
    jsonPayload.agentGatewayInfo.mcpInfo.parameter:label=TOOL_NAME,
    httpRequest.requestUrl:label=URL
  )"

验证有条件拒绝

提交额外的测试查询(尝试调用被拒绝get_observations 工具)…

  • What are the diabetes levels in Peru compared to Slovakia?

代理会先查看 search_indicators,然后尝试使用 get_observations,但会失败,并且代理回答将不完整。

# show gateway logs for mcp requests with method and tool name
gcloud logging read \
  "resource.type=\"networkservices.googleapis.com/Gateway\" \
  AND jsonPayload.agentGatewayInfo.mcpInfo.method:*" \
  --project=${PROJ_ID} \
  --limit=10 \
  --format="table(
    timestamp.date(tz=LOCAL):label=TIMESTAMP,
    httpRequest.status:label=STATUS,
    jsonPayload.authzPolicyInfo.result:label=IAP_AUTHZ,
    jsonPayload.agentGatewayInfo.mcpInfo.method:label=MCP_METHOD,
    jsonPayload.agentGatewayInfo.mcpInfo.parameter:label=TOOL_NAME,
    httpRequest.requestUrl:label=URL
  )"
TIMESTAMP            STATUS  IAP_AUTHZ  MCP_METHOD                 TOOL_NAME          URL
YYYY-MM-DDTHH:MM:SS  403     DENIED     tools/call                 get_observations   https://api.datacommons.org/mcp
YYYY-MM-DDTHH:MM:SS          ALLOWED    tools/call                 get_observations   https://api.datacommons.org/mcp
YYYY-MM-DDTHH:MM:SS  403     DENIED     tools/call                 get_observations   https://api.datacommons.org/mcp
YYYY-MM-DDTHH:MM:SS  200     ALLOWED    tools/call                 search_indicators  https://api.datacommons.org/mcp
YYYY-MM-DDTHH:MM:SS  200     ALLOWED    tools/list                                    https://api.datacommons.org/mcp
YYYY-MM-DDTHH:MM:SS  202     ALLOWED    notifications/initialized                     https://api.datacommons.org/mcp

这会验证治理政策是否按预期运行!

请注意,第二行表示空白状态和 ALLOWED,这是代理网关在发送 403 响应后关闭套接字时发出的辅助连接关闭日志事件(因为连接关闭事件没有响应状态代码,所以 httpRequest.status 为空白)。

“强制执行”部分到此结束,接下来进入清理部分。

13. 清理

# delete agent
curl -s -X DELETE "https://${REGION}-aiplatform.googleapis.com/v1/projects/${PROJ_ID}/locations/${REGION}/reasoningEngines/${RE_ENGINE_ID}?force=true" \
  -H "Authorization: Bearer $(gcloud auth application-default print-access-token)"

# next
# delete storage bucket
gcloud -q storage rm --recursive gs://${STAGING_BUCKET}

# next
# clear policy on mcp server
export IAP_ETAG=$(gcloud beta iap web get-iam-policy --resource-type=agent-registry --mcp-server=${MCP_REGISTRY_ID} --region=${REGION} --format="value(etag)")
cat > cfg/iap-policy-empty.json << EOF
{
  "bindings": [],
  "etag": "${IAP_ETAG}"
}
EOF

gcloud -q beta iap web set-iam-policy cfg/iap-policy-empty.json \
  --resource-type=agent-registry \
  --mcp-server=${MCP_REGISTRY_ID} \
  --region=${REGION}

# next
# clear policy on endpoint 1
export IAP_ETAG=$(gcloud beta iap web get-iam-policy --resource-type=agent-registry --endpoint=${ENDPOINT_1_REGISTRY_ID} --region=${REGION} --format="value(etag)")
cat > cfg/iap-policy-empty.json << EOF
{
  "bindings": [],
  "etag": "${IAP_ETAG}"
}
EOF

gcloud -q beta iap web set-iam-policy cfg/iap-policy-empty.json \
  --resource-type=agent-registry \
  --endpoint=${ENDPOINT_1_REGISTRY_ID} \
  --region=${REGION}

# next
# clear policy on endpoint 2
export IAP_ETAG=$(gcloud beta iap web get-iam-policy --resource-type=agent-registry --endpoint=${ENDPOINT_2_REGISTRY_ID} --region=${REGION} --format="value(etag)")
cat > cfg/iap-policy-empty.json << EOF
{
  "bindings": [],
  "etag": "${IAP_ETAG}"
}
EOF

gcloud -q beta iap web set-iam-policy cfg/iap-policy-empty.json \
  --resource-type=agent-registry \
  --endpoint=${ENDPOINT_2_REGISTRY_ID} \
  --region=${REGION}

# next
# clear policy on endpoint 3
export IAP_ETAG=$(gcloud beta iap web get-iam-policy --resource-type=agent-registry --endpoint=${ENDPOINT_3_REGISTRY_ID} --region=${REGION} --format="value(etag)")
cat > cfg/iap-policy-empty.json << EOF
{
  "bindings": [],
  "etag": "${IAP_ETAG}"
}
EOF

gcloud -q beta iap web set-iam-policy cfg/iap-policy-empty.json \
  --resource-type=agent-registry \
  --endpoint=${ENDPOINT_3_REGISTRY_ID} \
  --region=${REGION}

# next
# remove manual registry entry
gcloud -q agent-registry services delete ${MCP_RESOURCE_NAME} --location=${REGION}
# delete gateway resources
gcloud -q beta network-security authz-policies delete ${AGW_NAME}-authz-policy-profile-iap --location=${REGION}

gcloud -q beta service-extensions authz-extensions delete ${AGW_NAME}-svc-ext-authz-iap-dryrun --location=${REGION}

gcloud -q beta service-extensions authz-extensions delete ${AGW_NAME}-svc-ext-authz-iap-enforced --location=${REGION}

gcloud -q network-services agent-gateways delete ${AGW_NAME} --location=${REGION}

# next
# clean up local working directories and generated configs
rm -rf cfg agent-datacommons endpoints

# end

清理部分到此结束…接下来是总结

14. 总结

恭喜!您已成功部署并管控了智能体使用 Agent Gateway 访问工具的出站通信!

cosmopup

Cosmopup 认为 Codelab 非常棒!

接下来会发生什么?

欢迎随时使用此反馈表单提供任何意见、提出问题或做出更正

谢谢!