Централизованное управление Agent Gateway с помощью межпроектного реестра агентов для Agent Runtime

1. Введение

По мере того как корпоративные организации внедряют генеративный ИИ, архитектуры быстро развиваются от автономных монолитных чат-ботов до распределенных мультиагентных систем (Agent-to-Agent/A2A). В этих современных топологиях высокоуровневые агенты-оркестраторы координируют сложные бизнес-процессы, делегируя задачи специализированным агентам-исполнителям, серверам инструментов Model Context Protocol (MCP) и корпоративным базам данных в независимых облачных проектах Google Cloud.

Однако работа с мультиагентными системами в больших масштабах сопряжена с серьезными проблемами в области безопасности, управления и эксплуатации:

  • Теневые агенты и разрастание инструментов. Когда команды разработчиков развертывают агентов в изолированных проектах без централизованного каталога, организации теряют видимость того, какие инструменты и субагенты существуют.
  • Неконтролируемый исходящий трафик между проектами. Если у агентов есть прямые непроверенные сетевые маршруты, это создает риски кражи данных и обхода периметров безопасности.
  • Ненадежные интеграции с жестко заданным кодом. Жесткое задание URL дочерних агентов и идентификаторов Reasoning Engine создает хрупкие зависимости, которые нарушаются при обновлениях или повторном развертывании.
  • Отсутствие идентификации с минимальными привилегиями. Общие сервисные аккаунты не обеспечивают криптографическую неотказуемость на уровне отдельных экземпляров агентов.

Чтобы решить эти проблемы, Gemini Enterprise Agent Platform предоставляет единую плоскость управления и контроля подключений, состоящую из четырех основных компонентов:

  1. Шлюз агента (networkservices.googleapis.com): управляемый региональный прокси-сервер для сети и применения правил. Работая в режиме исходящего трафика AGENT_TO_ANYWHERE, он перехватывает исходящий трафик агента, делегирует оценку авторизации расширениям безопасности и маршрутизирует запросы по периметрам проекта.
  2. Реестр агентов (agentregistry.googleapis.com). Единый каталог сервисов предприятия. Он предоставляет централизованный проверенный каталог всех доступных инструментов, серверов MCP и одноранговых агентов в организации, обеспечивая динамическое автоматическое обнаружение во время выполнения без жестко закодированных конечных точек.
  3. Agent Identity & IAP v2 Governance (iap.googleapis.com и iam.googleapis.com). Криптографическая платформа для идентификации и доступа. Выполняющие агенты получают уникальные подтвержденные URN SPIFFE (principal://...). Исходящий трафик оценивается на соответствие централизованным унифицированным правилам доступа IAM (UAP/IAP v2), которые проверяют универсальное разрешение iap.googleapis.com/resources.egressViaIAP с помощью условий каталога на языке Common Expression Language (CEL) (destination.agent_registry.*).
  4. Agent Runtime (Reasoning Engines). Полностью управляемая бессерверная платформа для выполнения агентных приложений на основе Python со встроенными привязками конфигурации (agent_gateway_config) к центральным шлюзам.

Бизнес-сценарий практической работы: закупка продуктов питания и напитков для нескольких проектов

В этой практической работе вы создадите и настроите реальную многопроектную экосистему закупок, охватывающую три отдельных облачных проекта Google Cloud:

  • Проект централизованного управления (PROJECT_GOVERNANCE). Принадлежит центральному ИТ-отделу и SecOps. В нем размещаются центральный шлюз агентов, центральный реестр агентов и унифицированные правила доступа IAM.
  • Проект Consumer Orchestrator (PROJECT_CONCIERGE). Принадлежит команде по закупкам. В нем размещен агент Purchasing Concierge, который динамически находит поставщиков и направляет заказы клиентов.
  • Проект поставщика домена (PROJECT_SELLERS). Принадлежит внешним или внутренним поставщикам, в нем размещены агент по продаже бургеров и агент по продаже пиццы.

figure1

Рис. 1. Архитектура централизованного управления несколькими проектами

Зачем нужно централизованное управление несколькими проектами?

В крупных организациях команды продуктов и специалисты по обработке данных создают ИИ-агентов в десятках независимых облачных проектов Google Cloud. Если предоставить каждой команде прямой контроль над регистрацией инструментов, маршрутами исходящего трафика и мерами безопасности, это приведет к бесконтрольному росту числа инструментов, несогласованности правил DLP, отсутствию мониторинга исходящего трафика VPC и фрагментации журналов аудита.

Централизованное управление несколькими проектами позволяет отделить создание правил от выполнения агентов.

  • Центральный ИТ-отдел и SecOps разрабатывают правила безопасности, проверяют инструменты и отслеживают исходящий трафик в рамках одного проекта централизованного управления.
  • Команды по продуктам и приложениям сосредотачиваются исключительно на бизнес-логике в своих независимых проектах среды выполнения агентов, напрямую связываясь с центральным шлюзом без операционных расходов на управление локальными VPC, межсоединениями или фрагментированными механизмами политик.

figure2

Рис. 2. Трехуровневая архитектура и границы управления межпроектными связями

Двухуровневая модель идентификации в единых правилах доступа

Когда агенты обмениваются данными через Central Agent Gateway, Identity-Aware Proxy (IAP v2) оценивает доступ на основе идентификатора агента вызывающей стороны – криптографически подтвержденного идентификатора на основе SPIFFE, автоматически выдаваемого контейнеру среды выполнения, – в соответствии с глобальными правилами доступа IAM:

  • Уровень 1. Базовые API Google Cloud (общие, через principalSet:// в правиле 1). Авторизация исходящего трафика на уровне проекта, позволяющая всем средам выполнения агентов в дочерних проектах обращаться к стандартным API Google (aiplatform, iamcredentials, telemetry, agentregistry) для обнаружения, создания токенов и логического вывода.
  • Уровень 2. Бизнес-инструменты и сервисы A2A (точная настройка с помощью principal:// в правилах 2 и 3). Строгий доступ с минимальными привилегиями, привязанный к отдельным экземплярам Reasoning Engine и обеспечиваемый условиями Common Expression Language (CEL), которые нацелены на определенные зарегистрированные сервисы Agent Registry (destination.agent_registry.agent.name).

Что вы создаете

  • Централизованный шлюз агента (centralized-agw) в PROJECT_GOVERNANCE
  • Расширение сервиса авторизации IAP v2 и правила авторизации в строгом режиме принудительного применения (failOpen: false)
  • Базовая унифицированная политика доступа IAM (uap-rules.json) и привязка политики проекта
  • Разрешения IAM для агента сервиса, работающего с несколькими проектами (ar_agw_cross_project_sa)
  • Общий центральный промежуточный сегмент Google Cloud Storage (GCS)
  • Изолированные агенты по продаже бургеров и пиццы в PROJECT_SELLERS
  • Агент Purchasing Concierge с динамическим автообнаружением REST в PROJECT_CONCIERGE
  • Регистрация сервисов в центральном реестре агентов с URL mTLS для нескольких проектов
  • Динамические обновления правил исходящего трафика IAP v2 с проверкой в реальном времени и аудитом Cloud Logging

figure3

Рис. 3. Пошаговое руководство по настройке

Что вы узнаете

  • Как настроить разрешения IAM для сервисного агента, работающего с несколькими проектами, для централизованных шлюзов
  • Как маршрутизировать исходящий трафик Agent Runtime через центральный шлюз агента в средах с несколькими проектами
  • Как делегировать авторизацию Agent Gateway прокси-серверу с идентификацией (IAP v2) с помощью расширений сервисов (iapPolicyVersion: "V2")
  • Как создавать и связывать единые правила доступа IAM с правилами Common Expression Language (CEL), регулирующими зарегистрированные целевые объекты Agent Registry (destination.agent_registry.*)
  • Как убрать заданные в коде идентификаторы и URL агентов, используя автоматическое обнаружение во время выполнения в реестре агентов
  • Как протестировать блокировку по периметру в рамках модели нулевого доверия (HTTP 403 Forbidden) и проверить обновление правил в Cloud Logging

Что вам потребуется

  • 3 облачных проекта Google Cloud с включенной оплатой:
    • PROJECT_GOVERNANCE – централизованное управление, шлюз, реестр и правила доступа IAM.
    • PROJECT_CONCIERGE: агент-оркестратор консьержа по покупкам
    • PROJECT_SELLERS: агенты по продаже бургеров и пиццы.
  • Пользователь IAM или сервисный аккаунт с разрешениями roles/owner или администратора для всех трех проектов.
  • Организация Google Cloud (для сопоставления домена доверия SPIFFE).
  • Google Cloud Shell или локальный компьютер с установленными gcloud CLI, python (3.11+) и uv.

На этом вводная часть заканчивается. Далее мы перейдем к разделу Настройка и среда.

2. Настройка

Хотя эта архитектура охватывает три отдельных облачных проекта Google Cloud, вы можете выполнять все команды развертывания терминала, скачивания репозитория и промежуточные операции из одного терминала Cloud Shell, настроенного на PROJECT_GOVERNANCE. Каждый скрипт развертывания и команда gcloud явно нацелены на соответствующий целевой проект через флаги CLI (--project).

Для начала откройте командную строку проекта Google Cloud:

Задайте контекст проекта

# set terminal project context to Central Governance Project
gcloud config set project SET_YOUR_GOVERNANCE_PROJECT_ID_HERE
# login to gcloud cli
gcloud auth login
# login for application default credentials
gcloud auth application-default login
# update gcloud components
gcloud components update --quiet

Как настроить переменные среды оболочки

Введите идентификаторы проекта.

# 1. Project Identifiers
export PROJECT_GOVERNANCE="SET_YOUR_GOVERNANCE_PROJECT_ID_HERE"
export PROJECT_CONCIERGE="SET_YOUR_CONCIERGE_PROJECT_ID_HERE"
export PROJECT_SELLERS="SET_YOUR_SELLERS_PROJECT_ID_HERE"

Эти переменные оболочки будут получены автоматически.

# 2. Regional & Gateway Settings
export REGION="us-central1"
export AGW_NAME="centralized-agw"
export UAP_POLICY_NAME="uap-policy-${AGW_NAME}"
export UAP_BINDING_NAME="uap-binding-${AGW_NAME}"

# 3. Retrieve Project Numbers
export PROJECT_NUMBER_GOVERNANCE=$(gcloud projects describe ${PROJECT_GOVERNANCE} --format="value(projectNumber)")
export PROJECT_NUMBER_CONCIERGE=$(gcloud projects describe ${PROJECT_CONCIERGE} --format="value(projectNumber)")
export PROJECT_NUMBER_SELLERS=$(gcloud projects describe ${PROJECT_SELLERS} --format="value(projectNumber)")

# 4. Obtain Organization ID
export ORG_ID=$(gcloud projects get-ancestors ${PROJECT_GOVERNANCE} --format="value(id, type)" | grep organization | awk '{print $1}')

# 5. Set Application Default Credentials (ADC) Quota Project
gcloud auth application-default set-quota-project ${PROJECT_GOVERNANCE}

echo "Governance Project: ${PROJECT_GOVERNANCE} (${PROJECT_NUMBER_GOVERNANCE})"
echo "Concierge Project:  ${PROJECT_CONCIERGE} (${PROJECT_NUMBER_CONCIERGE})"
echo "Sellers Project:    ${PROJECT_SELLERS} (${PROJECT_NUMBER_SELLERS})"
echo "Organization ID:    ${ORG_ID}"
echo "UAP Policy Name:    ${UAP_POLICY_NAME}"
echo "UAP Binding Name:   ${UAP_BINDING_NAME}"

Создайте локальный каталог для файлов конфигурации

# create config folder
mkdir -p cfg

Как назначить роль администратора правил доступа для унифицированных правил доступа

# grant Access Policy Admin and Project IAM Admin to current user in Governance Project
for ROLE in "roles/iam.accessPolicyAdmin" "roles/resourcemanager.projectIamAdmin"; do
  gcloud projects add-iam-policy-binding ${PROJECT_GOVERNANCE} \
    --member="user:$(gcloud config get-value account)" \
    --role="${ROLE}" \
    --condition=None
done

Как включить журналы доступа к данным Cloud Audit для IAP v2

По умолчанию Google Cloud отключает журналы аудита доступа к данным, чтобы предотвратить непредвиденные расходы на хранение. Поскольку IAP v2 передает решения об авторизации (granted=true и granted=false) в виде журналов аудита доступа к данным, включите ведение журналов ADMIN_READ, DATA_READ и DATA_WRITE для iap.googleapis.com в PROJECT_GOVERNANCE:

# 1. export current IAM policy for PROJECT_GOVERNANCE
gcloud projects get-iam-policy ${PROJECT_GOVERNANCE} \
  --format=json > cfg/gov_iam_policy.json
# 2. append auditConfigs for iap.googleapis.com
python3 -c "
import json
with open('cfg/gov_iam_policy.json') as f:
    policy = json.load(f)
audit_configs = [c for c in policy.get('auditConfigs', []) if c.get('service') != 'iap.googleapis.com']
audit_configs.append({
    'service': 'iap.googleapis.com',
    'auditLogConfigs': [
        {'logType': 'ADMIN_READ'},
        {'logType': 'DATA_READ'},
        {'logType': 'DATA_WRITE'}
    ]
})
policy['auditConfigs'] = audit_configs
with open('cfg/gov_iam_policy.json', 'w') as f:
    json.dump(policy, f, indent=2)
"
# 3. apply updated policy
gcloud projects set-iam-policy ${PROJECT_GOVERNANCE} cfg/gov_iam_policy.json
# 4. verify auditConfigs applied
gcloud projects get-iam-policy ${PROJECT_GOVERNANCE} --format="yaml(auditConfigs)"

Включите необходимые API Google Cloud

# enable google apis (agent platform & security bundle, part 1)
for PROJ in ${PROJECT_GOVERNANCE} ${PROJECT_CONCIERGE} ${PROJECT_SELLERS}; do
  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 \
    agentidentity.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 \
    --project=${PROJ}
done
# enable google apis (agent platform bundle, part 2)
for PROJ in ${PROJECT_GOVERNANCE} ${PROJECT_CONCIERGE} ${PROJECT_SELLERS}; do
  gcloud services enable \
    securitycenter.googleapis.com \
    saasservicemgmt.googleapis.com \
    storage.googleapis.com \
    telemetry.googleapis.com \
    texttospeech.googleapis.com \
    --project=${PROJ}
done
# enable google apis (foundational & agent runtime build bundle, part 3)
for PROJ in ${PROJECT_GOVERNANCE} ${PROJECT_CONCIERGE} ${PROJECT_SELLERS}; do
  gcloud services enable \
    artifactregistry.googleapis.com \
    cloudbuild.googleapis.com \
    cloudresourcemanager.googleapis.com \
    iamcredentials.googleapis.com \
    serviceusage.googleapis.com \
    run.googleapis.com \
    orgpolicy.googleapis.com \
    --project=${PROJ}
done

Проверьте, включен ли API во всех проектах

Если во всех трех проектах (PROJECT_GOVERNANCE, PROJECT_CONCIERGE и PROJECT_SELLERS) включены одни и те же API, это обеспечивает согласованность работы и предотвращает сбои при создании токенов во время выполнения, ошибки каталогизации схем и пропуски телеметрии.

Чтобы проверить паритет API во всех трех проектах, запустите в Cloud Shell следующий скрипт:

# validate that all required APIs are enabled across all 3 projects
python3 - << 'EOF'
import subprocess
import os
import sys

REQUIRED_APIS = [
    "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",
    "agentidentity.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",
    "securitycenter.googleapis.com",
    "saasservicemgmt.googleapis.com",
    "storage.googleapis.com",
    "telemetry.googleapis.com",
    "texttospeech.googleapis.com",
    "artifactregistry.googleapis.com",
    "cloudbuild.googleapis.com",
    "cloudresourcemanager.googleapis.com",
    "iamcredentials.googleapis.com",
    "serviceusage.googleapis.com",
    "run.googleapis.com",
    "orgpolicy.googleapis.com"
]

projects = {
    "GOVERNANCE": os.environ.get("PROJECT_GOVERNANCE", ""),
    "CONCIERGE": os.environ.get("PROJECT_CONCIERGE", ""),
    "SELLERS": os.environ.get("PROJECT_SELLERS", "")
}

enabled = {}
for role, proj in projects.items():
    if not proj:
        print(f"Error: Environment variable for {role} is not set.")
        sys.exit(1)
    res = subprocess.run(
        ["gcloud", "services", "list", "--enabled", f"--project={proj}", "--format=value(config.name)"],
        capture_output=True, text=True, check=True
    )
    enabled[role] = set(res.stdout.strip().splitlines())

print(f"\n{'API Name':<36} | {'GOVERNANCE':<12} | {'CONCIERGE':<12} | {'SELLERS':<12}")
print("-" * 78)

all_synced = True
for api in REQUIRED_APIS:
    g_status = "ENABLED" if api in enabled["GOVERNANCE"] else "MISSING"
    c_status = "ENABLED" if api in enabled["CONCIERGE"] else "MISSING"
    s_status = "ENABLED" if api in enabled["SELLERS"] else "MISSING"
    if "MISSING" in (g_status, c_status, s_status):
        all_synced = False
    print(f"{api:<36} | {g_status:<12} | {c_status:<12} | {s_status:<12}")

print("-" * 78)
if all_synced:
    print("✅ All 29 required APIs are ENABLED and synchronized across all three projects.\n")
else:
    print("❌ Discrepancies detected. Please re-run the enablement commands for missing services.\n")
    sys.exit(1)
EOF

Пример результата проверки:

Вы увидите, что все API включены.

✅ All 30 required APIs are ENABLED and synchronized across all three projects.

Настройка правил организации

Стандартные правила организации Google Cloud применяют ограничения, которые запрещают привязку правил доступа IAM v3 к ресурсам (constraints/iam.managed.disableAccessPolicyBinding).

Переопределите все унаследованные ограничения правил организации на уровне проекта, явно задав для enforce: false значение allow.

# disable iam v3 constraint (allow v3 access policies)
gcloud org-policies set-policy /dev/stdin << EOF
name: projects/${PROJECT_NUMBER_GOVERNANCE}/policies/iam.managed.disableAccessPolicyBinding
spec:
  rules:
  - enforce: false
EOF
# verify org policy constraints on project
gcloud org-policies describe iam.managed.disableAccessPolicyBinding \
  --project=${PROJECT_GOVERNANCE} --effective

На этом настройка завершена. Перейдите к разделу Регистрация основных API Google.

3. Реестр агентов

Зарегистрировать сервис конечных точек основных API Google

Для Agent Gateway требуется, чтобы URL API Google были зарегистрированы в Central Agent Registry. Это позволит агентам, настроенным с помощью agent_gateway_config, безопасно направлять исходящий трафик в основные бэкенд-сервисы Google Cloud, такие как aiplatform, IAM Credentials и Telemetry.

Создать core-gapi-services в реестре агентов

# register core google api endpoints in agent registry with standard and :443 port variants
gcloud agent-registry services create core-gapi-services \
  --project=${PROJECT_GOVERNANCE} \
  --location=${REGION} \
  --display-name="gapi.core.services" \
  --description="Core Google Cloud APIs and Service Endpoints" \
  --endpoint-spec-type=no-spec \
  --interfaces=protocolBinding=JSONRPC,url=https://telemetry.googleapis.com \
  --interfaces=protocolBinding=JSONRPC,url=https://telemetry.mtls.googleapis.com \
  --interfaces=protocolBinding=JSONRPC,url=https://${REGION}-aiplatform.googleapis.com \
  --interfaces=protocolBinding=JSONRPC,url=https://${REGION}-aiplatform.googleapis.com:443 \
  --interfaces=protocolBinding=JSONRPC,url=https://${REGION}-aiplatform.mtls.googleapis.com \
  --interfaces=protocolBinding=JSONRPC,url=https://${REGION}-aiplatform.mtls.googleapis.com:443 \
  --interfaces=protocolBinding=JSONRPC,url=https://aiplatform.googleapis.com \
  --interfaces=protocolBinding=JSONRPC,url=https://aiplatform.googleapis.com:443 \
  --interfaces=protocolBinding=JSONRPC,url=https://aiplatform.mtls.googleapis.com \
  --interfaces=protocolBinding=JSONRPC,url=https://aiplatform.mtls.googleapis.com:443 \
  --interfaces=protocolBinding=JSONRPC,url=https://cloudresourcemanager.googleapis.com \
  --interfaces=protocolBinding=JSONRPC,url=https://cloudresourcemanager.googleapis.com:443 \
  --interfaces=protocolBinding=JSONRPC,url=https://cloudresourcemanager.mtls.googleapis.com \
  --interfaces=protocolBinding=JSONRPC,url=https://cloudresourcemanager.mtls.googleapis.com:443 \
  --interfaces=protocolBinding=JSONRPC,url=https://iamcredentials.googleapis.com \
  --interfaces=protocolBinding=JSONRPC,url=https://iamcredentials.googleapis.com:443 \
  --interfaces=protocolBinding=JSONRPC,url=https://iamcredentials.mtls.googleapis.com \
  --interfaces=protocolBinding=JSONRPC,url=https://iamcredentials.mtls.googleapis.com:443 \
  --interfaces=protocolBinding=JSONRPC,url=https://agentregistry.googleapis.com \
  --interfaces=protocolBinding=JSONRPC,url=https://agentregistry.mtls.googleapis.com \
  --interfaces=protocolBinding=JSONRPC,url=https://agentregistry.googleapis.com:443 \
  --interfaces=protocolBinding=JSONRPC,url=https://agentregistry.mtls.googleapis.com:443

Идентификатор ресурса конечной точки Capture Core API

# capture the underlying Agent Registry endpoint ID
export ENDPOINT_ID=$(gcloud agent-registry services describe core-gapi-services \
  --project=${PROJECT_GOVERNANCE} \
  --location=${REGION} \
  --format="value(registryResource)" | awk -F'/' '{print $NF}')
echo "Core APIs Endpoint ID: ${ENDPOINT_ID}"

Разница между principalSet и principal в идентификации агента

В Google Cloud IAM и Gemini Enterprise Agent Platform идентификаторы машин, выданные выполняющимся контейнерам агентов, используют криптографически подтвержденные URN SPIFFE, которые оцениваются Identity-Aware Proxy (IAP v2). При настройке единых правил доступа IAM вы можете выбрать в качестве цели либо один определенный principal, либо principalSet на основе атрибутов:

Параметр

principal:// (идентификатор одного устройства)

principalSet:// (группа на основе атрибутов)

Синтаксис IAM

principal://...

principalSet://...

Детализация

Точный (на уровне экземпляра). Идентифицирует один конкретный экземпляр контейнера Reasoning Engine.

Общие (на уровне проекта). Позволяют найти все механизмы рассуждения, у которых совпадает атрибут проекта.

Шаблон URN

principal://agents.global.org-${ORG_ID}.system.id.goog/resources/aiplatform/projects/${PROJECT_NUMBER}/locations/${REGION}/reasoningEngines/${ENGINE_ID}

principalSet://agents.global.org-${ORG_ID}.system.id.goog/attribute.platformContainer/aiplatform/projects/${PROJECT_NUMBER}

Пример использования на платформе агентов

Уровень 2 (бизнес-инструменты и A2A). Авторизация определенных агентов-оркестраторов для вызова инструментов целевого домена (например, помощник по покупкам $\rightarrow$ продавец бургеров).

Уровень 1 (базовая инфраструктура). Предоставление всем агентам в проекте доступа к исходящему трафику для Google Cloud API (core-gapi-services).

Влияние на жизненный цикл

Если агент удален и создан заново, для его нового идентификатора Engine ID требуется обновленная привязка правила IAM.

Автоматически применяется к новым агентам, развернутым в этом проекте, без дополнительных обновлений IAM.

Декларативное управление с помощью унифицированных правил доступа (UAP/IAP v2)

В устаревшей версии IAP v1 правила исходящего трафика прикреплялись непосредственно к отдельным ресурсам реестра агентов с помощью gcloud beta iap web add-iam-policy-binding. В IAP v2 и унифицированных правилах доступа привязки для отдельных ресурсов заменены едиными централизованными правилами доступа IAM (cfg/uap-rules.json).

Базовая авторизация исходящего трафика для core-gapi-services будет настроена как Правило 1 в Единых правилах доступа в разделе 5. Это позволит убедиться, что для всех контейнеров агентов перед развертыванием настроены базовые маршруты исходящего трафика.

Дополнительные технические сведения об идентификаторах субъектов и механизмах Workload Identity можно найти в следующих статьях:

На этом регистрация конечных точек основных API завершена. Перейдите к разделу Развертывание централизованного шлюза агента.

4. Шлюз агента

Развертывание централизованного шлюза агента

Разверните централизованный шлюз Agent Gateway (centralized-agw) в режиме исходящего трафика AGENT_TO_ANYWHERE в проекте $PROJECT_GOVERNANCE.

Определите манифест конфигурации шлюза

Создайте cfg/${AGW_NAME}.yaml для управления исходящим трафиком:

# generate agent gateway config yaml
cat > cfg/${AGW_NAME}.yaml << EOF
name: ${AGW_NAME}
protocols:
  - MCP
googleManaged:
  governedAccessPath: AGENT_TO_ANYWHERE
registries:
  - "//agentregistry.googleapis.com/projects/${PROJECT_GOVERNANCE}/locations/${REGION}"
EOF

Импорт конфигурации шлюза агента

# import and create agent gateway
gcloud network-services agent-gateways import ${AGW_NAME} \
  --source="cfg/${AGW_NAME}.yaml" \
  --location=${REGION} \
  --project=${PROJECT_GOVERNANCE}

Проверьте сведения о шлюзе агента

# show agent gateway status
gcloud network-services agent-gateways describe ${AGW_NAME} \
  --location=${REGION} \
  --project=${PROJECT_GOVERNANCE}

Пример результата:

agentGatewayCard:
  mtlsEndpoint: projects/${AGW_TP_ID}/regions/us-central1/serviceAttachments/unitkind1-swp-mtls-psc-sa
  rootCertificates:
  - |
    -----BEGIN CERTIFICATE-----
    MIIDwzCCAqugAwIBAgITNQuWGopdOZaHdcK7r7AYFhonqDANBgkqhkiG9w0BAQsF
    ...
    -----END CERTIFICATE-----
  serviceExtensionsServiceAccount: service-${PROJ_NO}@gcp-sa-dep.iam.gserviceaccount.com
createTime: 'YYYY-MM-DDT12:34:56.789098765Z'
googleManaged:
  governedAccessPath: AGENT_TO_ANYWHERE
name: projects/${PROJECT_GOVERNANCE}/locations/us-central1/agentGateways/centralized-agw
protocols:
- MCP
registries:
- //agentregistry.googleapis.com/projects/${PROJECT_GOVERNANCE}/locations/us-central1
updateTime: 'YYYY-MM-DDT12:34:56.789098765Z'

На этом развертывание шлюза завершено. Перейдите к разделу Настройка авторизации.

5. Авторизация

Как настроить авторизацию Agent Gateway и базовую платформу UAP

Agent Gateway защищает исходящий трафик инструментов и агентов и управляет им с помощью правил авторизации (networksecurity.authzPolicies), интегрированных с унифицированными правилами доступа (UAP) Identity-Aware Proxy (IAP v2).

Обзор архитектуры авторизации

figure4

Рис. 4. Обзор архитектуры авторизации

Архитектура авторизации состоит из трех взаимосвязанных уровней:

  1. Расширение сервиса IAP (authzExtension). Региональный ресурс, настроенный с помощью service: iap.googleapis.com, metadata: iapPolicyVersion: "V2" и failOpen: false для строгого соблюдения принципа нулевого доверия по периметру.
  2. Gateway Authorization Policy (authzPolicy) – региональный ресурс, который направляет трафик на Agent Gateway с помощью policyProfile: REQUEST_AUTHZ и action: CUSTOM, а проверки авторизации – на расширение авторизации IAP.
  3. IAM Unified Access Policy & Binding (accessPolicy & policyBinding): глобальный ресурс IAM v3, оцениваемый IAP. Она проверяет универсальное разрешение iap.googleapis.com/resources.egressViaIAP на соответствие идентификаторам SPIFFE вызывающего абонента и условиям каталога CEL.

Шаг 1. Создайте и импортируйте расширение авторизации IAP v2

Создайте манифест расширения сервиса с iapPolicyVersion: "V2" и failOpen: false в строгом режиме ENFORCE:

# create authz extension config file in ENFORCE mode
cat > cfg/${AGW_NAME}-svc-ext-authz-iap.yaml << EOF
name: ${AGW_NAME}-svc-ext-authz-iap
service: iap.googleapis.com
failOpen: false
timeout: 1s
metadata:
  iapPolicyVersion: "V2"
EOF

Импортируйте расширение Authz:

# import IAP v2 authz extension
gcloud service-extensions authz-extensions import ${AGW_NAME}-svc-ext-authz-iap \
  --source=cfg/${AGW_NAME}-svc-ext-authz-iap.yaml \
  --location=${REGION} \
  --project=${PROJECT_GOVERNANCE}

Убедитесь, что расширение Authz активно:

# describe authz extension
gcloud service-extensions authz-extensions describe ${AGW_NAME}-svc-ext-authz-iap \
  --location=${REGION} \
  --project=${PROJECT_GOVERNANCE}

Пример результата:

createTime: 'YYYY-MM-DDT12:34:56.789098765Z'
failOpen: false
metadata:
  iapPolicyVersion: V2
name: projects/${PROJECT_GOVERNANCE}/locations/us-central1/authzExtensions/centralized-agw-svc-ext-authz-iap
service: iap.googleapis.com
timeout: 1s

Шаг 2. Создайте и импортируйте правила авторизации шлюза

Создайте конфигурацию правил авторизации, которая подключается к шлюзу агента и делегирует проверку запросов расширению авторизации IAP:

# create authz policy manifest
cat > cfg/${AGW_NAME}-authz-policy-profile-iap.yaml << EOF
name: ${AGW_NAME}-authz-policy-profile-iap
target:
  resources:
    - "projects/${PROJECT_GOVERNANCE}/locations/${REGION}/agentGateways/${AGW_NAME}"
policyProfile: REQUEST_AUTHZ
action: CUSTOM
customProvider:
  authzExtension:
    resources:
      - "projects/${PROJECT_GOVERNANCE}/locations/${REGION}/authzExtensions/${AGW_NAME}-svc-ext-authz-iap"
EOF

Импортируйте правило авторизации:

# import 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} \
  --project=${PROJECT_GOVERNANCE}

Проверьте активное правило авторизации:

# describe authz policy
gcloud beta network-security authz-policies describe ${AGW_NAME}-authz-policy-profile-iap \
  --location=${REGION} \
  --project=${PROJECT_GOVERNANCE}

Шаг 3. Создайте начальное единое правило доступа (правило 1: основные API Google)

Создайте cfg/uap-rules.json с правилом 1, разрешающим трем principalSet проекта доступ к core-gapi-services:

# create initial unified access policy rules manifest
cat > cfg/uap-rules.json << EOF
[
  {
    "description": "Rule 1: Allow agent runtimes across all 3 projects to reach Core Google APIs",
    "effect": "ALLOW",
    "principals": [
      "principalSet://agents.global.org-${ORG_ID}.system.id.goog/attribute.platformContainer/aiplatform/projects/${PROJECT_NUMBER_GOVERNANCE}",
      "principalSet://agents.global.org-${ORG_ID}.system.id.goog/attribute.platformContainer/aiplatform/projects/${PROJECT_NUMBER_CONCIERGE}",
      "principalSet://agents.global.org-${ORG_ID}.system.id.goog/attribute.platformContainer/aiplatform/projects/${PROJECT_NUMBER_SELLERS}"
    ],
    "operation": {
      "permissions": [
        "iap.googleapis.com/resources.egressViaIAP"
      ]
    },
    "conditions": {
      "iap.googleapis.com": {
        "expression": \
        "destination.is_registered == true && \
         destination.agent_registry.resource_type == 'ENDPOINT' && ( \
         destination.agent_registry.endpoint.name == 'projects/${PROJECT_GOVERNANCE}/locations/${REGION}/endpoints/core-gapi-services' || \
         destination.agent_registry.endpoint.name == 'projects/${PROJECT_GOVERNANCE}/locations/${REGION}/endpoints/${ENDPOINT_ID}' || \
         destination.agent_registry.endpoint.name == 'projects/${PROJECT_NUMBER_GOVERNANCE}/locations/${REGION}/endpoints/${ENDPOINT_ID}')"
      }
    }
  }
]
EOF

Шаг 4. Создайте и привяжите правило доступа IAM

Создайте глобальное правило доступа IAM:

# create global IAM access policy
gcloud iam access-policies create ${UAP_POLICY_NAME} \
  --details-rules=cfg/uap-rules.json \
  --project=${PROJECT_GOVERNANCE} \
  --location=global

Привяжите правило доступа к PROJECT_GOVERNANCE:

# bind access policy to governance project
gcloud iam policy-bindings create ${UAP_BINDING_NAME} \
  --policy="projects/${PROJECT_GOVERNANCE}/locations/global/accessPolicies/${UAP_POLICY_NAME}" \
  --target-resource="//cloudresourcemanager.googleapis.com/projects/${PROJECT_GOVERNANCE}" \
  --project=${PROJECT_GOVERNANCE} \
  --location=global

Убедитесь, что привязка правил активна:

# verify policy binding
gcloud iam policy-bindings describe ${UAP_BINDING_NAME} \
  --project=${PROJECT_GOVERNANCE} \
  --location=global

Пример результата:

name: projects/${PROJECT_GOVERNANCE}/locations/global/policyBindings/uap-binding-centralized-agw
policy: projects/${PROJECT_GOVERNANCE}/locations/global/accessPolicies/uap-policy-centralized-agw
policyKind: ACCESS_POLICY
target:
  resource: //cloudresourcemanager.googleapis.com/projects/${PROJECT_GOVERNANCE}

Исходящий трафик Foundational Google Cloud API теперь безопасно авторизуется во всех трех проектах в строгом режиме ENFORCE.

На этом настройка авторизации шлюза завершена. Перейдите к разделу Настройка разрешений IAM для нескольких проектов.

6. IAM для нескольких проектов

Как настроить разрешения IAM для нескольких проектов

В этой топологии с несколькими проектами среды выполнения агентов находятся в дочерних проектах (PROJECT_CONCIERGE и PROJECT_SELLERS), а центральный шлюз агента и реестр агентов – в PROJECT_GOVERNANCE.

Поскольку проекты Google Cloud представляют собой изолированные периметры безопасности, доступ между проектами должен быть явно предоставлен на двух операционных уровнях:

  1. Уровень управления (время развертывания). При развертывании контейнера агента, настроенного с помощью --agent-gateway-config, агент сервиса среды выполнения агента (service-@gcp-sa-aiplatform.iam.gserviceaccount.com) дочернего проекта должен подключить контейнер к центральному шлюзу. Мы создаем минимальную специальную роль (ar_agw_cross_project_sa), предоставляющую разрешения networkservices.agentGateways.use, get и operations.get в PROJECT_GOVERNANCE.
  2. Уровень данных (выполнение во время работы):
    • Обнаружение каталога. Идентификаторам дочерних узлов требуется роль roles/agentregistry.viewer в PROJECT_GOVERNANCE, чтобы динамически разрешать конечные точки целевых агентов.
    • Целевой вызов. Агенту Concierge требуется roles/aiplatform.user в PROJECT_SELLERS, чтобы выполнять запросы к механизмам рассуждения продавца.

Создание специальной роли IAM в PROJECT_GOVERNANCE

# create custom role in central governance project
gcloud iam roles create ar_agw_cross_project_sa \
  --project=${PROJECT_GOVERNANCE} \
  --title="Runtime Agent Gateway Cross-Project SA" \
  --description="Custom role for cross-project service agents to access Central Agent Gateway" \
  --permissions="networkservices.agentGateways.get,networkservices.agentGateways.use,networkservices.operations.get" \
  --stage="GA"

Назначение настраиваемой роли сервисным агентам Agent Runtime

# 1. ensure aiplatform service identities are provisioned across all projects
for PROJ in ${PROJECT_GOVERNANCE} ${PROJECT_CONCIERGE} ${PROJECT_SELLERS}; do
  gcloud beta services identity create --service=aiplatform.googleapis.com --project=${PROJ}
done
# 2. derive aiplatform service agent emails
export CONCIERGE_AI_SA="service-${PROJECT_NUMBER_CONCIERGE}@gcp-sa-aiplatform.iam.gserviceaccount.com"
export CONCIERGE_RE_SA="service-${PROJECT_NUMBER_CONCIERGE}@gcp-sa-aiplatform-re.iam.gserviceaccount.com"
export CONCIERGE_COMPUTE_SA="${PROJECT_NUMBER_CONCIERGE}-compute@developer.gserviceaccount.com"

export SELLERS_AI_SA="service-${PROJECT_NUMBER_SELLERS}@gcp-sa-aiplatform.iam.gserviceaccount.com"
export SELLERS_RE_SA="service-${PROJECT_NUMBER_SELLERS}@gcp-sa-aiplatform-re.iam.gserviceaccount.com"
export SELLERS_COMPUTE_SA="${PROJECT_NUMBER_SELLERS}-compute@developer.gserviceaccount.com"
# 3. grant custom role & network viewer to Concierge and Sellers Service Agents
for SA in ${CONCIERGE_AI_SA} ${SELLERS_AI_SA}; do
  gcloud projects add-iam-policy-binding ${PROJECT_GOVERNANCE} \
    --member="serviceAccount:${SA}" \
    --role="projects/${PROJECT_GOVERNANCE}/roles/ar_agw_cross_project_sa" \
    --condition=None

  gcloud projects add-iam-policy-binding ${PROJECT_GOVERNANCE} \
    --member="serviceAccount:${SA}" \
    --role="roles/networkservices.viewer" \
    --condition=None
done
# 4. grant agent registry viewer on Governance Project for dynamic autodiscovery
for MEMBER in "serviceAccount:${CONCIERGE_AI_SA}" "serviceAccount:${CONCIERGE_RE_SA}" "serviceAccount:${CONCIERGE_COMPUTE_SA}" "serviceAccount:${SELLERS_AI_SA}" "serviceAccount:${SELLERS_RE_SA}" "serviceAccount:${SELLERS_COMPUTE_SA}" "principalSet://agents.global.org-${ORG_ID}.system.id.goog/attribute.platformContainer/aiplatform/projects/${PROJECT_NUMBER_CONCIERGE}" "principalSet://agents.global.org-${ORG_ID}.system.id.goog/attribute.platformContainer/aiplatform/projects/${PROJECT_NUMBER_SELLERS}"; do
  gcloud projects add-iam-policy-binding ${PROJECT_GOVERNANCE} \
    --member="${MEMBER}" \
    --role="roles/agentregistry.viewer" \
    --condition=None
done
# 5. grant agent project viewer on Governance Project for dynamic autodiscovery
for SA in ${CONCIERGE_COMPUTE_SA} ${CONCIERGE_AI_SA}; do
  gcloud projects add-iam-policy-binding ${PROJECT_GOVERNANCE} \
    --member="serviceAccount:${SA}" \
    --role="roles/viewer" \
    --condition=None
done
# 6. grant aitplatform user on Sellers project to Concierge for cross-project A2A invocation
for MEMBER in "serviceAccount:${CONCIERGE_AI_SA}" "serviceAccount:${CONCIERGE_RE_SA}" "serviceAccount:${CONCIERGE_COMPUTE_SA}" "principalSet://agents.global.org-${ORG_ID}.system.id.goog/attribute.platformContainer/aiplatform/projects/${PROJECT_NUMBER_CONCIERGE}"; do
  gcloud projects add-iam-policy-binding ${PROJECT_SELLERS} \
    --member="${MEMBER}" \
    --role="roles/aiplatform.user" \
    --condition=None
done

На этом настройка IAM для нескольких проектов завершена. Перейдите к разделу Развертывание агентов Seller и Concierge.

7. Agent Runtime

Развертывание агентов Seller и Concierge

База кода мультиагентного приложения и скрипты развертывания, используемые в этой практической работе, хранятся в удаленном хранилище Google Cloud на GitHub. Выполните следующие действия, чтобы клонировать репозиторий локально, скопировать необходимые файлы в структуру текущего рабочего каталога, очистить временные файлы и установить зависимости с помощью uv.

Получение удаленных артефактов

# clone remote repository to temp local dir
git clone https://github.com/GoogleCloudPlatform/cloud-networking-solutions.git ./temp_agw_multiproject
# copy multi-agent application files to current working directory
cp -r temp_agw_multiproject/codelabs/agw-multiproject ./cross-project-multiagent
# remove temporary directory
rm -rf temp_agw_multiproject
# install dependencies
uv sync --directory ./cross-project-multiagent

Создание общего центрального промежуточного сегмента

# create shared central staging bucket
gcloud storage buckets create gs://${PROJECT_GOVERNANCE}-shared-staging \
  --project=${PROJECT_GOVERNANCE} \
  --location=${REGION}
# grant cross-project read/write access to runtime service agents
gcloud storage buckets add-iam-policy-binding gs://${PROJECT_GOVERNANCE}-shared-staging \
  --member="serviceAccount:service-${PROJECT_NUMBER_CONCIERGE}@gcp-sa-aiplatform.iam.gserviceaccount.com" \
  --role="roles/storage.objectAdmin"

gcloud storage buckets add-iam-policy-binding gs://${PROJECT_GOVERNANCE}-shared-staging \
  --member="serviceAccount:service-${PROJECT_NUMBER_SELLERS}@gcp-sa-aiplatform.iam.gserviceaccount.com" \
  --role="roles/storage.objectAdmin"

Как работает привязка шлюза агента между проектами

На этом шаге вы развернете агентов продавца в дочернем проекте (PROJECT_SELLERS) и настроите их так, чтобы исходящий трафик направлялся через центральный шлюз агентов в PROJECT_GOVERNANCE.

# !-- for example purposes -- NOT a command to execute --!
# snippet from deploy_burger.py
burger_config = {
    "staging_bucket": staging_bucket_uri,
    "gcs_dir_name": "burger_agent",
    "display_name": "burger-seller-agent-adk",
    "identity_type": "AGENT_IDENTITY",
    "agent_gateway_config": {
        "agent_to_anywhere_config": {
            "agent_gateway": f"projects/{args.governance_project}/locations/{args.region}/agentGateways/{args.gateway}"
        }
    },
}
deployed_burger = client.agent_engines.create(agent=burger_playground, config=burger_config)

Поскольку правило 1 было установлено в нашей единой политике доступа раньше, запросы на инициализацию контейнера к Google Cloud API разрешены через шлюз без прерывания.

Развертывание агентов по продаже бургеров и пиццы в PROJECT_SELLERS

# 1. deploy Burger Seller Agent to PROJECT_SELLERS
uv run --directory ./cross-project-multiagent python deploy_burger.py \
  --project=${PROJECT_SELLERS} \
  --region=${REGION} \
  --governance-project=${PROJECT_GOVERNANCE} \
  --gateway=projects/${PROJECT_GOVERNANCE}/locations/${REGION}/agentGateways/${AGW_NAME}
# 2. deploy Pizza Seller Agent to PROJECT_SELLERS
uv run --directory ./cross-project-multiagent python deploy_pizza.py \
  --project=${PROJECT_SELLERS} \
  --region=${REGION} \
  --governance-project=${PROJECT_GOVERNANCE} \
  --gateway=projects/${PROJECT_GOVERNANCE}/locations/${REGION}/agentGateways/${AGW_NAME}

Проверка маршрутизации шлюза продавца

# retrieve deployed seller reasoning engine IDs
export BURGER_ENGINE_ID=$(grep BURGER_SELLER_AGENT_ID cross-project-multiagent/burger_agent.env | awk -F'/' '{print $NF}')
export PIZZA_ENGINE_ID=$(grep PIZZA_SELLER_AGENT_ID cross-project-multiagent/pizza_agent.env | awk -F'/' '{print $NF}')

echo "Burger Engine ID: ${BURGER_ENGINE_ID}"
echo "Pizza Engine ID:  ${PIZZA_ENGINE_ID}"
# inspect runtime configuration for both Seller Agents
for ENGINE_ID in ${BURGER_ENGINE_ID} ${PIZZA_ENGINE_ID}; do
  curl -s -X GET "https://${REGION}-aiplatform.googleapis.com/v1beta1/projects/${PROJECT_SELLERS}/locations/${REGION}/reasoningEngines/${ENGINE_ID}" \
    -H "Authorization: Bearer $(gcloud auth application-default print-access-token)" \
    -H "Content-Type: application/json" \
    | jq '{displayName: .displayName, identityType: .spec.identityType, effectiveIdentity: .spec.effectiveIdentity, agentGatewayConfig: .spec.deploymentSpec.agentGatewayConfig}'
done

Разверните помощника по покупкам в PROJECT_CONCIERGE

# deploy Purchasing Concierge to PROJECT_CONCIERGE
uv run --directory ./cross-project-multiagent python deploy_concierge_adk.py \
  --project=${PROJECT_CONCIERGE} \
  --region=${REGION} \
  --staging-bucket=gs://${PROJECT_GOVERNANCE}-shared-staging \
  --gateway-name=${AGW_NAME} \
  --gateway-project=${PROJECT_GOVERNANCE}

Проверка маршрутизации шлюза покупок

# retrieve Concierge engine ID
export CONCIERGE_ENGINE_ID=$(grep CONCIERGE_AGENT_ID cross-project-multiagent/concierge_agent.env | awk -F'/' '{print $NF}')
echo "Concierge Engine ID: ${CONCIERGE_ENGINE_ID}"
# inspect runtime configuration for Purchasing Concierge
curl -s -X GET "https://${REGION}-aiplatform.googleapis.com/v1beta1/projects/${PROJECT_CONCIERGE}/locations/${REGION}/reasoningEngines/${CONCIERGE_ENGINE_ID}" \
  -H "Authorization: Bearer $(gcloud auth application-default print-access-token)" \
  -H "Content-Type: application/json" \
  | jq '{displayName: .displayName, identityType: .spec.identityType, effectiveIdentity: .spec.effectiveIdentity, agentGatewayConfig: .spec.deploymentSpec.agentGatewayConfig}'

В выходных данных должны быть указаны идентификатор среды выполнения агента Concierge, проект и привязка к Agent Gateway проекта Governance.

{
  "displayName": "purchasing-concierge-adk",
  "identityType": "AGENT_IDENTITY",
  "effectiveIdentity": "agents.global.org-${ORG_ID}.system.id.goog/resources/aiplatform/projects/${PROJECT_CONCIERGE}/locations/us-central1/reasoningEngines/${CONCIERGE_ENGINE_ID}",
  "agentGatewayConfig": {
    "agentToAnywhereConfig": {
      "agentGateway": "projects/${PROJECT_GOVERNANCE}/locations/us-central1/agentGateways/centralized-agw"
    }
  }
}

На этом развертывание агентов завершено. Перейдите к разделу Регистрация агентов в центральном реестре.

8. Реестр для нескольких проектов

Регистрация агентов в центральном реестре агентов

Зарегистрируйте всех трех агентов в Центральном реестре агентов в регионе PROJECT_GOVERNANCE, используя региональные конечные точки mTLS для нескольких проектов и числовые номера проектов.

Как зарегистрировать сервисы в качестве агентов, не относящихся к типу A2A, в реестре агентов

# 1. register Burger Seller Agent
gcloud agent-registry services create burger-seller-agent \
  --project=${PROJECT_GOVERNANCE} \
  --location=${REGION} \
  --display-name="Burger Seller Agent" \
  --description="Specialist agent that sells burgers and fries" \
  --agent-spec-type=no-spec \
  --interfaces=protocolBinding=JSONRPC,url=https://${REGION}-aiplatform.googleapis.com/v1/projects/${PROJECT_NUMBER_SELLERS}/locations/${REGION}/reasoningEngines/${BURGER_ENGINE_ID}:query \
  --interfaces=protocolBinding=JSONRPC,url=https://${REGION}-aiplatform.googleapis.com/v1beta1/projects/${PROJECT_NUMBER_SELLERS}/locations/${REGION}/reasoningEngines/${BURGER_ENGINE_ID}:query \
  --interfaces=protocolBinding=JSONRPC,url=https://${REGION}-aiplatform.googleapis.com:443/v1/projects/${PROJECT_NUMBER_SELLERS}/locations/${REGION}/reasoningEngines/${BURGER_ENGINE_ID}:query \
  --interfaces=protocolBinding=JSONRPC,url=https://${REGION}-aiplatform.googleapis.com:443/v1beta1/projects/${PROJECT_NUMBER_SELLERS}/locations/${REGION}/reasoningEngines/${BURGER_ENGINE_ID}:query \
  --interfaces=protocolBinding=JSONRPC,url=https://${REGION}-aiplatform.mtls.googleapis.com/v1/projects/${PROJECT_NUMBER_SELLERS}/locations/${REGION}/reasoningEngines/${BURGER_ENGINE_ID}:query \
  --interfaces=protocolBinding=JSONRPC,url=https://${REGION}-aiplatform.mtls.googleapis.com/v1beta1/projects/${PROJECT_NUMBER_SELLERS}/locations/${REGION}/reasoningEngines/${BURGER_ENGINE_ID}:query \
  --interfaces=protocolBinding=JSONRPC,url=https://${REGION}-aiplatform.mtls.googleapis.com:443/v1/projects/${PROJECT_NUMBER_SELLERS}/locations/${REGION}/reasoningEngines/${BURGER_ENGINE_ID}:query \
  --interfaces=protocolBinding=JSONRPC,url=https://${REGION}-aiplatform.mtls.googleapis.com:443/v1beta1/projects/${PROJECT_NUMBER_SELLERS}/locations/${REGION}/reasoningEngines/${BURGER_ENGINE_ID}:query
# 2. register Pizza Seller Agent
gcloud agent-registry services create pizza-seller-agent \
  --project=${PROJECT_GOVERNANCE} \
  --location=${REGION} \
  --display-name="Pizza Seller Agent" \
  --description="Specialist agent that sells pizzas and pasta" \
  --agent-spec-type=no-spec \
  --interfaces=protocolBinding=JSONRPC,url=https://${REGION}-aiplatform.googleapis.com/v1/projects/${PROJECT_NUMBER_SELLERS}/locations/${REGION}/reasoningEngines/${PIZZA_ENGINE_ID}:query \
  --interfaces=protocolBinding=JSONRPC,url=https://${REGION}-aiplatform.googleapis.com/v1beta1/projects/${PROJECT_NUMBER_SELLERS}/locations/${REGION}/reasoningEngines/${PIZZA_ENGINE_ID}:query \
  --interfaces=protocolBinding=JSONRPC,url=https://${REGION}-aiplatform.googleapis.com:443/v1/projects/${PROJECT_NUMBER_SELLERS}/locations/${REGION}/reasoningEngines/${PIZZA_ENGINE_ID}:query \
  --interfaces=protocolBinding=JSONRPC,url=https://${REGION}-aiplatform.googleapis.com:443/v1beta1/projects/${PROJECT_NUMBER_SELLERS}/locations/${REGION}/reasoningEngines/${PIZZA_ENGINE_ID}:query \
  --interfaces=protocolBinding=JSONRPC,url=https://${REGION}-aiplatform.mtls.googleapis.com/v1/projects/${PROJECT_NUMBER_SELLERS}/locations/${REGION}/reasoningEngines/${PIZZA_ENGINE_ID}:query \
  --interfaces=protocolBinding=JSONRPC,url=https://${REGION}-aiplatform.mtls.googleapis.com/v1beta1/projects/${PROJECT_NUMBER_SELLERS}/locations/${REGION}/reasoningEngines/${PIZZA_ENGINE_ID}:query \
  --interfaces=protocolBinding=JSONRPC,url=https://${REGION}-aiplatform.mtls.googleapis.com:443/v1/projects/${PROJECT_NUMBER_SELLERS}/locations/${REGION}/reasoningEngines/${PIZZA_ENGINE_ID}:query \
  --interfaces=protocolBinding=JSONRPC,url=https://${REGION}-aiplatform.mtls.googleapis.com:443/v1beta1/projects/${PROJECT_NUMBER_SELLERS}/locations/${REGION}/reasoningEngines/${PIZZA_ENGINE_ID}:query
# 3. register Purchasing Concierge Agent
gcloud agent-registry services create purchasing-concierge-adk \
  --project=${PROJECT_GOVERNANCE} \
  --location=${REGION} \
  --display-name="Purchasing Concierge Agent" \
  --description="Orchestrator concierge agent that routes purchasing requests" \
  --agent-spec-type=no-spec \
  --interfaces=protocolBinding=JSONRPC,url=https://${REGION}-aiplatform.googleapis.com/v1/projects/${PROJECT_NUMBER_CONCIERGE}/locations/${REGION}/reasoningEngines/${CONCIERGE_ENGINE_ID}:query \
  --interfaces=protocolBinding=JSONRPC,url=https://${REGION}-aiplatform.googleapis.com/v1beta1/projects/${PROJECT_NUMBER_CONCIERGE}/locations/${REGION}/reasoningEngines/${CONCIERGE_ENGINE_ID}:query \
  --interfaces=protocolBinding=JSONRPC,url=https://${REGION}-aiplatform.googleapis.com:443/v1/projects/${PROJECT_NUMBER_CONCIERGE}/locations/${REGION}/reasoningEngines/${CONCIERGE_ENGINE_ID}:query \
  --interfaces=protocolBinding=JSONRPC,url=https://${REGION}-aiplatform.googleapis.com:443/v1beta1/projects/${PROJECT_NUMBER_CONCIERGE}/locations/${REGION}/reasoningEngines/${CONCIERGE_ENGINE_ID}:query \
  --interfaces=protocolBinding=JSONRPC,url=https://${REGION}-aiplatform.mtls.googleapis.com/v1/projects/${PROJECT_NUMBER_CONCIERGE}/locations/${REGION}/reasoningEngines/${CONCIERGE_ENGINE_ID}:query \
  --interfaces=protocolBinding=JSONRPC,url=https://${REGION}-aiplatform.mtls.googleapis.com/v1beta1/projects/${PROJECT_NUMBER_CONCIERGE}/locations/${REGION}/reasoningEngines/${CONCIERGE_ENGINE_ID}:query \
  --interfaces=protocolBinding=JSONRPC,url=https://${REGION}-aiplatform.mtls.googleapis.com:443/v1/projects/${PROJECT_NUMBER_CONCIERGE}/locations/${REGION}/reasoningEngines/${CONCIERGE_ENGINE_ID}:query \
  --interfaces=protocolBinding=JSONRPC,url=https://${REGION}-aiplatform.mtls.googleapis.com:443/v1beta1/projects/${PROJECT_NUMBER_CONCIERGE}/locations/${REGION}/reasoningEngines/${CONCIERGE_ENGINE_ID}:query

Регистрационные идентификаторы базовых агентов

# capture underlying Agent Registry Agent UUIDs
export BURGER_AGENT_ID=$(gcloud agent-registry services describe burger-seller-agent --project=${PROJECT_GOVERNANCE} --location=${REGION} --format="value(registryResource)" | awk -F'/' '{print $NF}')
export PIZZA_AGENT_ID=$(gcloud agent-registry services describe pizza-seller-agent --project=${PROJECT_GOVERNANCE} --location=${REGION} --format="value(registryResource)" | awk -F'/' '{print $NF}')
export CONCIERGE_AGENT_ID=$(gcloud agent-registry services describe purchasing-concierge-adk --project=${PROJECT_GOVERNANCE} --location=${REGION} --format="value(registryResource)" | awk -F'/' '{print $NF}')

echo "Burger Agent ID:    ${BURGER_AGENT_ID}"
echo "Pizza Agent ID:     ${PIZZA_AGENT_ID}"
echo "Concierge Agent ID: ${CONCIERGE_AGENT_ID}"

На этом настройка реестра завершена. Перейдите к разделу Настройка правил исходящего трафика A2A.

9. Правила UAP

Как настроить правила исходящего трафика A2A в единых правилах доступа

В архитектуре Agent Gateway Запрет по умолчанию в строгом режиме ENFORCE:

  1. Правило 1 (базовые API Google Cloud). Позволяет контейнерам агентов во всех трех проектах подключаться к core-gapi-services.
  2. Правило 2 (Burger Seller Agent: ALLOW). Разрешает экземпляру Purchasing Concierge Agent вызывать Burger Seller Agent.
  3. Агент по продаже пиццы (ЗАПРЕЩЕНО по умолчанию). Намеренно не включен в правила. В режиме ENFORCE (failOpen: false) любая попытка Concierge вызвать Pizza Seller будет немедленно прервана на периметре шлюза с ошибкой HTTP 403 Forbidden.

Сформулируйте личность агента Concierge

# formulate the exact SPIFFE machine identity for the Concierge Agent
export CONCIERGE_SPIFFE_PRINCIPAL="principal://agents.global.org-${ORG_ID}.system.id.goog/resources/aiplatform/projects/${PROJECT_NUMBER_CONCIERGE}/locations/${REGION}/reasoningEngines/${CONCIERGE_ENGINE_ID}"
echo "Concierge SPIFFE Principal: ${CONCIERGE_SPIFFE_PRINCIPAL}"

Обновление манифеста с правилами 1 и 2

Чтобы безопасно обновить правила доступа, не затронув существующие, определите полный консолидированный манифест, содержащий правило 1 (основные API Google) и правило 2 (агент Burger Seller):

# create consolidated policy manifest with Rule 1 and Rule 2
cat > cfg/uap-rules-consolidated.json << EOF
[
  {
    "description": "Rule 1: Allow agent runtimes across all 3 projects to reach Core Google APIs",
    "effect": "ALLOW",
    "principals": [
      "principalSet://agents.global.org-${ORG_ID}.system.id.goog/attribute.platformContainer/aiplatform/projects/${PROJECT_NUMBER_GOVERNANCE}",
      "principalSet://agents.global.org-${ORG_ID}.system.id.goog/attribute.platformContainer/aiplatform/projects/${PROJECT_NUMBER_CONCIERGE}",
      "principalSet://agents.global.org-${ORG_ID}.system.id.goog/attribute.platformContainer/aiplatform/projects/${PROJECT_NUMBER_SELLERS}"
    ],
    "operation": {
      "permissions": [
        "iap.googleapis.com/resources.egressViaIAP"
      ]
    },
    "conditions": {
      "iap.googleapis.com": {
        "expression": \
        "destination.is_registered == true && \
         destination.agent_registry.resource_type == 'ENDPOINT' && ( \
         destination.agent_registry.endpoint.name == 'projects/${PROJECT_GOVERNANCE}/locations/${REGION}/endpoints/core-gapi-services' || \
         destination.agent_registry.endpoint.name == 'projects/${PROJECT_GOVERNANCE}/locations/${REGION}/endpoints/${ENDPOINT_ID}' || \
         destination.agent_registry.endpoint.name == 'projects/${PROJECT_NUMBER_GOVERNANCE}/locations/${REGION}/endpoints/${ENDPOINT_ID}')"
      }
    }
  },
  {
    "description": "Rule 2: Allow Purchasing Concierge to invoke Burger Seller Agent via Central Gateway",
    "effect": "ALLOW",
    "principals": [
      "${CONCIERGE_SPIFFE_PRINCIPAL}",
      "principal://agents.global.org-${ORG_ID}.system.id.goog/attribute.agentIdentity/projects/${PROJECT_NUMBER_CONCIERGE}/locations/${REGION}/reasoningEngines/${CONCIERGE_ENGINE_ID}",
      "principalSet://agents.global.org-${ORG_ID}.system.id.goog/attribute.agentIdentity/projects/${PROJECT_NUMBER_CONCIERGE}/locations/${REGION}/reasoningEngines/${CONCIERGE_ENGINE_ID}"
    ],
    "operation": {
      "permissions": [
        "iap.googleapis.com/resources.egressViaIAP"
      ]
    },
    "conditions": {
      "iap.googleapis.com": {
        "expression": \
        "destination.is_registered == true && \
         destination.agent_registry.resource_type == 'AGENT' && ( \
         destination.agent_registry.agent.name == 'projects/${PROJECT_GOVERNANCE}/locations/${REGION}/agents/burger-seller-agent' || \
         destination.agent_registry.agent.name == 'projects/${PROJECT_GOVERNANCE}/locations/${REGION}/agents/${BURGER_AGENT_ID}' || \
         destination.agent_registry.agent.name == 'projects/${PROJECT_NUMBER_GOVERNANCE}/locations/${REGION}/agents/${BURGER_AGENT_ID}')"
      }
    }
  }
]
EOF

Применить обновленные правила доступа

Примените манифест объединенных правил декларативно с помощью --details-rules:

# update IAM access policy with consolidated manifest (Rule 1 + Rule 2)
gcloud iam access-policies update ${UAP_POLICY_NAME} \
  --details-rules=cfg/uap-rules-consolidated.json \
  --project=${PROJECT_GOVERNANCE} \
  --location=global

Проверьте сведения о правиле доступа IAM

# inspect updated access policy
gcloud iam access-policies describe ${UAP_POLICY_NAME} \
  --project=${PROJECT_GOVERNANCE} \
  --location=global

Пример результата:

details:
  rules:
  - conditions:
      iap.googleapis.com:
        expression: destination.is_registered == true && destination.agent_registry.resource_type
          == 'ENDPOINT' && (destination.agent_registry.endpoint.name == 'projects/${PROJECT_GOVERNANCE}/locations/us-central1/endpoints/core-gapi-services'
          || destination.agent_registry.endpoint.name == 'projects/${PROJECT_NUMBER_GOVERNANCE}/locations/us-central1/endpoints/${ENDPOINT_ID}')
    description: 'Rule 1: Allow agent runtimes across all 3 projects to reach Core
      Google APIs'
    effect: ALLOW
    operation:
      permissions:
      - iap.googleapis.com/resources.egressViaIAP
    principals:
    - principalSet://agents.global.org-${ORG_ID}.system.id.goog/attribute.platformContainer/aiplatform/projects/${PROJECT_NUMBER_GOVERNANCE}
    - principalSet://agents.global.org-${ORG_ID}.system.id.goog/attribute.platformContainer/aiplatform/projects/${PROJECT_NUMBER_CONCIERGE}
    - principalSet://agents.global.org-${ORG_ID}.system.id.goog/attribute.platformContainer/aiplatform/projects/${PROJECT_NUMBER_SELLERS}
  - conditions:
      iap.googleapis.com:
        expression: (destination.is_registered == true) && (destination.agent_registry.resource_type
          == 'AGENT') && (destination.agent_registry.agent.name == 'projects/${PROJECT_GOVERNANCE}/locations/us-central1/agents/burger-seller-agent'
          || destination.agent_registry.agent.name == 'projects/${PROJECT_NUMBER_GOVERNANCE}/locations/us-central1/agents/${BURGER_AGENT_ID}')
    description: 'Rule 2: Allow Purchasing Concierge to invoke Burger Seller Agent
      via Central Gateway'
    effect: ALLOW
    operation:
      permissions:
      - iap.googleapis.com/resources.egressViaIAP
    principals:
    - principal://agents.global.org-${ORG_ID}.system.id.goog/resources/aiplatform/projects/${PROJECT_NUMBER_CONCIERGE}/locations/us-central1/reasoningEngines/${CONCIERGE_ENGINE_ID}
name: projects/${PROJECT_GOVERNANCE}/locations/global/accessPolicies/uap-policy-centralized-agw

На этом настройка правил завершена. Перейдите к разделу Тестирование и проверка правил управления.

10. Проверка правил

Тестирование и проверка правил управления с помощью Cloud Logging

В этом разделе вы протестируете межпроектные взаимодействия между агентами в Agent Runtime AI Playground, понаблюдаете за реальной блокировкой периметра HTTP 403 Forbidden в строгом режиме ENFORCE, измените единые правила доступа в реальном времени и проверите немедленное одобрение заказа.

Шаг 1. Откройте Agent Runtime ИИ-студию в PROJECT_CONCIERGE

  1. Откройте консоль Google Cloud.
  2. На панели выбора проекта вверху перейдите к проекту PROJECT_CONCIERGE.
  3. В меню навигации выберите Agent Platform (Платформа агентов) > Agents (Агенты) > Deployments (Развертывания).
  4. Нажмите на значок purchasing-concierge-adk.
  5. Выберите Playground (Игровая площадка), чтобы открыть интерактивный чат в правой части экрана.

Шаг 2. Тестирование заказа бургера (соответствие правилу 2 -> 200 OK)

В окне чата Playground введите следующий запрос:

I would like 10 Classic Cheeseburgers. Place this order now.

Если требуется подтверждение, отправь следующий ответ:

Confirmed, please place the order.

Вы также можете выполнить проверку программно из Cloud Shell или терминала:

uv run --directory ./cross-project-multiagent python -c "
import vertexai
from vertexai.preview import reasoning_engines
vertexai.init(project='${PROJECT_CONCIERGE}', location='${REGION}')
agent = reasoning_engines.ReasoningEngine('projects/${PROJECT_CONCIERGE}/locations/${REGION}/reasoningEngines/${CONCIERGE_ENGINE_ID}')
response = agent.query(input={'message': 'I would like 22 Spicy Cajun Burgers please. Place this order now.'})
print(response)
"

Если требуется подтверждение, используйте следующую команду:

uv run --directory ./cross-project-multiagent python -c "
import vertexai
from vertexai.preview import reasoning_engines
vertexai.init(project='${PROJECT_CONCIERGE}', location='${REGION}')
agent = reasoning_engines.ReasoningEngine('projects/${PROJECT_CONCIERGE}/locations/${REGION}/reasoningEngines/${CONCIERGE_ENGINE_ID}')
response = agent.query(input='Yes please place the order now.')
print(response['text'])
"

Что происходит за кулисами:

  1. Динамическое обнаружение. Во время запуска сеанса Purchasing Concierge запросил у Central Agent Registry в PROJECT_GOVERNANCE (через core-gapi-services через Agent Gateway, авторизованный правилом 1) региональную конечную точку mTLS для burger-seller-agent.
  2. Определение намерения и вызов A2A. Gemini в помощнике по покупкам анализирует намерение заказать еду и вызывает агента по продаже бургеров через исходящий RPC-запрос к https://${REGION}-aiplatform.mtls.googleapis.com/.../reasoningEngines/${BURGER_ENGINE_ID}.
  3. Перехват шлюза и распространение SPIFFE. Исходящий трафик перехватывается agent_gateway_config и направляется в центральный шлюз агента в PROJECT_GOVERNANCE, неся криптографическую идентификацию SPIFFE Concierge (principal://...).
  4. Оценка правил IAP v2. Центральный шлюз агента вызывает расширение авторизации IAP (authzExtension). IAP v2 оценивает правило 2 в единых правилах доступа IAM. Поскольку вызывающий абонент соответствует ${CONCIERGE_SPIFFE_PRINCIPAL}, а цель – burger-seller-agent, IAP возвращает ALLOW (granted: true).
  5. Выполнение в разных проектах. Шлюз агента передает авторизованный запрос в другой проект – PROJECT_SELLERS, где механизм рассуждений Burger Seller обрабатывает заказ и возвращает подтверждение.

Ожидаемый ответ:

Your order for 10 Classic Cheeseburger(s) has been placed!
Here is a summary of your order:
- 10x Classic Cheeseburger @ IDR 85,000/each = IDR 850,000

Total: IDR 850,000
Your Order ID is: e8f9c732-f347-4cc4-acff-cfe09ccbeddd

Шаг 3. Проверьте журналы аудита Agent Gateway и IAP v2 (HTTP 200/ALLOWED)

Запрос журналов запросов шлюза агента в PROJECT_GOVERNANCE:

# query Agent Gateway logs for successful 200 OK requests
gcloud logging read "
  logName=\"projects/${PROJECT_GOVERNANCE}/logs/networkservices.googleapis.com%2Fgateway_requests\"
  AND jsonPayload.authzPolicyInfo.result=\"ALLOWED\"
" \
  --project="${PROJECT_GOVERNANCE}" \
  --limit=10 \
  --format="table(
    timestamp.date('%H:%M:%S'):label=TIME,
    httpRequest.requestMethod:label=METHOD,
    httpRequest.status:label=STATUS,
    jsonPayload.authzPolicyInfo.result:label=AUTHZ,
    httpRequest.requestUrl:label=URL
  )"

Журналы должны фиксировать исходящий трафик, поступающий из обоих дочерних проектов (PROJECT_CONCIERGE и PROJECT_SELLERS), с полями исходящего трафика для вызовов Gemini (generateContent), телеметрии Cloud Trace (/v1/traces) и поиска учетных данных IAM, которые прозрачно перехватываются и авторизуются правилом 1 (core-gapi-services).

Чтобы проверить версию правил POLICY_VERSION_V2, запросите журналы доступа к данным Cloud Audit для IAP v2:

# query IAP v2 audit logs with shortened principal and resource fields
gcloud logging read "
  logName=\"projects/${PROJECT_GOVERNANCE}/logs/cloudaudit.googleapis.com%2Fdata_access\"
  AND protoPayload.serviceName=\"iap.googleapis.com\"
" \
  --project="${PROJECT_GOVERNANCE}" \
  --limit=5 \
  --format="table(
    timestamp.date('%H:%M:%S'):label=TIME,
    protoPayload.authenticationInfo.principalSubject.sub('\.global\..*\/reasoningEngines\/', '.[...]/reasoningEngines/'):label=CALLER,
    protoPayload.authorizationInfo[0].granted:label=GRANTED,
    protoPayload.metadata.destination.agent_registry.resource_type.basename():label=TYPE,
    protoPayload.metadata.destination.agent_registry.resource_id.basename():label=RESOURCE_ID,
    protoPayload.authorizationInfo[0].permission.basename():label=PERMISSION
  )"

Пример результата:

TIME      CALLER                                                            GRANTED  TYPE      RESOURCE_ID     PERMISSION
HH:MM:SS  principal://agents.[...]/reasoningEngines/${CONCIERGE_ENGINE_ID}  True     Endpoint  ${ENDPOINT_ID}  resources.egressViaIAP
HH:MM:SS  principal://agents.[...]/reasoningEngines/${BURGER_ENGINE_ID}     True     Endpoint  ${ENDPOINT_ID}  resources.egressViaIAP
HH:MM:SS  principal://agents.[...]/reasoningEngines/${CONCIERGE_ENGINE_ID}  True     Endpoint  ${ENDPOINT_ID}  resources.egressViaIAP
HH:MM:SS  principal://agents.[...]/reasoningEngines/${BURGER_ENGINE_ID}     True     Endpoint  ${ENDPOINT_ID}  resources.egressViaIAP

Шаг 4. Проверьте заказ пиццы (запрет по умолчанию -> HTTP 403 Forbidden ENFORCED)

В том же окне чата Playground отправьте следующий запрос на заказ пиццы:

I would like 10 BBQ Chicken Pizzas. Place this order now.

Если требуется подтверждение, отправь следующий ответ:

Confirmed, please place the order.

Вы также можете выполнить проверку программно из Cloud Shell или терминала:

uv run --directory ./cross-project-multiagent python -c "
import vertexai
from vertexai.preview import reasoning_engines
vertexai.init(project='${PROJECT_CONCIERGE}', location='${REGION}')
agent = reasoning_engines.ReasoningEngine('projects/${PROJECT_CONCIERGE}/locations/${REGION}/reasoningEngines/${CONCIERGE_ENGINE_ID}')
response = agent.query(input='I would like 8 Hawaiian pizzas, please. Place this order now.')
print(response)
"

Если требуется подтверждение, используйте следующую команду:

uv run --directory ./cross-project-multiagent python -c "
import vertexai
from vertexai.preview import reasoning_engines
vertexai.init(project='${PROJECT_CONCIERGE}', location='${REGION}')
agent = reasoning_engines.ReasoningEngine('projects/${PROJECT_CONCIERGE}/locations/${REGION}/reasoningEngines/${CONCIERGE_ENGINE_ID}')
response = agent.query(input='Yes please place the order now.')
print(response['text'])
"

Ожидаемый ответ:

I apologize, but I am unable to process that request at the moment. It seems
there was an issue connecting to the pizza seller agent. Please try again later.

Что происходит за кулисами:

  1. Динамическое обнаружение. Помощник по покупкам разрешил конечную точку pizza-seller-agent из центрального реестра агентов во время запуска.
  2. Определение намерения и вызов A2A. Gemini в помощнике по покупкам пытается отправить запрос на заказ пиццы в конечную точку продавца пиццы в PROJECT_SELLERS.
  3. Перехват шлюзом. Исходящий RPC перехватывается agent_gateway_config и направляется на центральный шлюз агента.
  4. Оценка правил IAP v2 (запрет по умолчанию). Центральный шлюз агента вызывает IAP v2. Поскольку в единых правилах доступа нет правила, соответствующего pizza-seller-agent, IAP возвращает DENY (granted: false).
  5. Строгая блокировка периметра. Поскольку расширение Authz находится в режиме принудительного применения (failOpen: false), шлюз центрального агента немедленно прерывает исходящее подключение и возвращает HTTP 403 Forbidden. Трафик не покидает шлюз и не достигает PROJECT_SELLERS.

Шаг 5. Проверьте журналы шлюза агента на наличие заблокированных запросов (HTTP 403/DENIED)

# query Agent Gateway logs for blocked 403 requests
gcloud logging read "
  logName=\"projects/${PROJECT_GOVERNANCE}/logs/networkservices.googleapis.com%2Fgateway_requests\"
  AND httpRequest.status=403
" \
  --project="${PROJECT_GOVERNANCE}" \
  --limit=5 \
  --format="table(
    timestamp.date('%H:%M:%S'):label=TIME,
    httpRequest.requestMethod:label=METHOD,
    httpRequest.status:label=STATUS,
    jsonPayload.authzPolicyInfo.result:label=AUTHZ,
    httpRequest.requestUrl:label=URL
  )"

Пример выходных данных журнала запрещенных действий:

TIME      METHOD  STATUS  AUTHZ   URL
HH:MM:SS  POST    403     DENIED  https://us-central1-aiplatform.mtls.googleapis.com/v1beta1/projects/${PROJECT_SELLERS}/locations/us-central1/reasoningEngines/${PIZZA_ENGINE_ID}:query

Запросите журналы аудита доступа к данным IAP v2, чтобы узнать причину отказа:

# query IAP v2 audit logs with shortened principal and resource fields
gcloud logging read "
  logName=\"projects/${PROJECT_GOVERNANCE}/logs/cloudaudit.googleapis.com%2Fdata_access\"
  AND protoPayload.serviceName=\"iap.googleapis.com\"
" \
  --project="${PROJECT_GOVERNANCE}" \
  --limit=5 \
  --format="table(
    timestamp.date('%H:%M:%S'):label=TIME,
    protoPayload.authenticationInfo.principalSubject.sub('\.global\..*\/reasoningEngines\/', '.[...]/reasoningEngines/'):label=CALLER,
    protoPayload.authorizationInfo[0].granted:label=GRANTED,
    protoPayload.metadata.destination.agent_registry.resource_type.basename():label=TYPE,
    protoPayload.metadata.destination.agent_registry.resource_id.basename():label=RESOURCE_ID,
    protoPayload.authorizationInfo[0].permission.basename():label=PERMISSION
  )"

Пример выходных данных журнала аудита для отклоненных запросов:

TIME      CALLER                                                            GRANTED  TYPE      RESOURCE_ID     PERMISSION
HH:MM:SS  principal://agents.[...]/reasoningEngines/${PIZZA_ENGINE_ID}      True     Endpoint  ${REGISTRY_ID}  resources.egressViaIAP
HH:MM:SS  principal://agents.[...]/reasoningEngines/${PIZZA_ENGINE_ID}      True     Endpoint  ${REGISTRY_ID}  resources.egressViaIAP
HH:MM:SS  principal://agents.[...]/reasoningEngines/${CONCIERGE_ENGINE_ID}  False    Agent     ${REGISTRY_ID}  resources.egressViaIAP
HH:MM:SS  principal://agents.[...]/reasoningEngines/${PIZZA_ENGINE_ID}      True     Endpoint  ${REGISTRY_ID}  resources.egressViaIAP

Шаг 6. Динамически предоставьте агенту Pizza доступ к исходящему трафику

Создайте новый файл cfg/uap-rules-update-3.json, в который будут входить Правило 1 (основные API), Правило 2 (агент по продаже бургеров) и Правило 3 (агент по продаже пиццы).

# create addendum to update policy manifest with Rule 3 for Pizza Agent
cat > cfg/uap-rules-update-3.json << EOF
[
  {
    "description": "Rule 3: Allow Purchasing Concierge to invoke Pizza Seller Agent via Central Gateway",
    "effect": "ALLOW",
    "principals": [
      "${CONCIERGE_SPIFFE_PRINCIPAL}"
    ],
    "operation": {
      "permissions": [
        "iap.googleapis.com/resources.egressViaIAP"
      ]
    },
    "conditions": {
      "iap.googleapis.com": {
        "expression": \
        "destination.is_registered == true && \
         destination.agent_registry.resource_type == 'AGENT' && ( \
         destination.agent_registry.agent.name == 'projects/${PROJECT_GOVERNANCE}/locations/${REGION}/agents/pizza-seller-agent' || \
         destination.agent_registry.agent.name == 'projects/${PROJECT_GOVERNANCE}/locations/${REGION}/agents/${PIZZA_AGENT_ID}' || \
         destination.agent_registry.agent.name == 'projects/${PROJECT_NUMBER_GOVERNANCE}/locations/${REGION}/agents/${PIZZA_AGENT_ID}')"
      }
    }
  }
]
EOF

Примените обновление правил в реальном времени:

# update IAM access policy with Pizza rule
gcloud iam access-policies update ${UAP_POLICY_NAME} \
  --add-details-rules=cfg/uap-rules-update-3.json \
  --project=${PROJECT_GOVERNANCE} \
  --location=global

Шаг 7. Повторный запрос к агенту Pizza Agent (немедленный ответ 200 OK)

В окне чата Playground повторно отправьте запрос на заказ пиццы:

I would like 10 BBQ Chicken Pizzas. Place this order now.

Если требуется подтверждение, отправьте следующий ответ:

Confirmed, please place the order.

Вы также можете выполнить проверку программно из Cloud Shell или терминала:

uv run --directory ./cross-project-multiagent python -c "
import vertexai
from vertexai.preview import reasoning_engines
vertexai.init(project='${PROJECT_CONCIERGE}', location='${REGION}')
agent = reasoning_engines.ReasoningEngine('projects/${PROJECT_CONCIERGE}/locations/${REGION}/reasoningEngines/${CONCIERGE_ENGINE_ID}')
response = agent.query(input='I would like 11 Veggie pizzas, please. Place this order now.')
print(response)
"

Если требуется подтверждение, используйте следующую команду:

uv run --directory ./cross-project-multiagent python -c "
import vertexai
from vertexai.preview import reasoning_engines
vertexai.init(project='${PROJECT_CONCIERGE}', location='${REGION}')
agent = reasoning_engines.ReasoningEngine('projects/${PROJECT_CONCIERGE}/locations/${REGION}/reasoningEngines/${CONCIERGE_ENGINE_ID}')
response = agent.query(input='Yes please place the order now.')
print(response['text'])
"

Ожидаемый ответ:

Your order has been placed!

**Order ID:** 8d6c13d7-31dc-4d80-b6a7-80d1e50b6411

**Order Details:**
*   10 x BBQ Chicken Pizza @ IDR 130,000 each = IDR 1,300,000

**Total: IDR 1,300,000**

Что происходит за кулисами:

  1. Динамическое обновление правил. Изменения в единых правилах доступа IAM сразу же вступают в силу в механизме оценки IAP без простоев и повторного развертывания контейнеров.
  2. Вызов A2A. Concierge отправляет запрос через Central Agent Gateway.
  3. Оценка правил IAP v2 (одобрение). IAP v2 соответствует правилу 3, проверяет идентификационные данные вызывающего абонента и целевое выражение CEL и возвращает ALLOW (granted: true).
  4. Выполнение в разных проектах. Центральный шлюз агента передает авторизованный трафик в PROJECT_SELLERS, где продавец пиццы обрабатывает заказ.

Шаг 8. Проверьте журналы шлюза агента на наличие запросов на пиццу

# query Agent Gateway logs for successful 200 OK requests
gcloud logging read "
  logName=\"projects/${PROJECT_GOVERNANCE}/logs/networkservices.googleapis.com%2Fgateway_requests\"
  AND jsonPayload.authzPolicyInfo.result=\"ALLOWED\"
" \
  --project="${PROJECT_GOVERNANCE}" \
  --limit=10 \
  --format="table(
    timestamp.date('%H:%M:%S'):label=TIME,
    httpRequest.requestMethod:label=METHOD,
    httpRequest.status:label=STATUS,
    jsonPayload.authzPolicyInfo.result:label=AUTHZ,
    httpRequest.requestUrl:label=URL
  )"

Пример выходных данных журнала с предоставленным доступом:

TIME      METHOD  STATUS  AUTHZ    URL
HH:MM:SS  POST    200     ALLOWED  https://aiplatform.mtls.googleapis.com/v1beta1/projects/${PROJECT_SELLERS}/locations/global/publishers/google/models/gemini-3.5-flash:generateContent
HH:MM:SS  POST    200     ALLOWED  https://us-central1-aiplatform.mtls.googleapis.com/v1beta1/projects/${PROJECT_SELLERS}/locations/us-central1/reasoningEngines/${PIZZA_ENGINE_ID}:query

На этом тестирование и проверка завершены. Перейдите к разделу Очистка.

11. Очистка

Чтобы избежать списания средств с вашего аккаунта Google Cloud за ресурсы, используемые в этой практической работе, выполните действия по удалению в строгом порядке обратной зависимости:

1. Как удалить развертывания Reasoning Engine

Выполните скрипт cleanup_old_deployments.py в обоих проектах среды выполнения, чтобы удалить механизмы рассуждения и дождаться завершения их продолжительных операций.

# delete all Reasoning Engines deployed in Concierge and Sellers projects
uv run --directory ./cross-project-multiagent python cleanup_old_deployments.py --project=${PROJECT_CONCIERGE} --region=${REGION}
uv run --directory ./cross-project-multiagent python cleanup_old_deployments.py --project=${PROJECT_SELLERS} --region=${REGION}

Вы также можете получить список движков и удалить их:

uv run --directory ./cross-project-multiagent python -c '
import vertexai
import os
from vertexai.preview import reasoning_engines

region = os.environ.get("REGION", "us-central1")
for proj in [os.environ.get("PROJECT_CONCIERGE"), os.environ.get("PROJECT_SELLERS")]:
    if not proj:
        continue
    print(f"Cleaning reasoning engines in {proj}...")
    vertexai.init(project=proj, location=region)
    for eng in reasoning_engines.ReasoningEngine.list():
        print(f"  Deleting {eng.resource_name} ({eng.display_name})...")
        eng.delete()
'

2. Удаление служб реестра агентов

# delete agent registry services in Central Governance Project
for SERVICE in burger-seller-agent pizza-seller-agent purchasing-concierge-adk core-gapi-services; do
  gcloud agent-registry services delete ${SERVICE} \
    --project=${PROJECT_GOVERNANCE} \
    --location=${REGION} \
    --quiet || true
done

3. Удаление привязки унифицированных правил доступа IAM и правил доступа

# 1. delete IAM policy binding
gcloud -q iam policy-bindings delete ${UAP_BINDING_NAME} \
  --project=${PROJECT_GOVERNANCE} \
  --location=global || true

# 2. delete IAM access policy
gcloud -q iam access-policies delete ${UAP_POLICY_NAME} \
  --project=${PROJECT_GOVERNANCE} \
  --location=global || true

4. Удаление шлюза агента и политик безопасности

# 1. delete authorization policy
gcloud beta network-security authz-policies delete ${AGW_NAME}-authz-policy-profile-iap \
  --location=${REGION} \
  --project=${PROJECT_GOVERNANCE} --quiet || true

# 2. delete authorization extension
gcloud service-extensions authz-extensions delete ${AGW_NAME}-svc-ext-authz-iap \
  --location=${REGION} \
  --project=${PROJECT_GOVERNANCE} --quiet || true
# 3. delete agent gateway
gcloud network-services agent-gateways delete ${AGW_NAME} \
  --project=${PROJECT_GOVERNANCE} \
  --location=${REGION} --quiet || true

5. Удалите привязки IAM между проектами и специальную роль

# 1. remove custom role and network viewer bindings for spoke service agents
for NUM in "${PROJECT_NUMBER_CONCIERGE}" "${PROJECT_NUMBER_SELLERS}"; do
  SA="service-${NUM}@gcp-sa-aiplatform.iam.gserviceaccount.com"
  
  gcloud projects remove-iam-policy-binding ${PROJECT_GOVERNANCE} \
    --member="serviceAccount:${SA}" \
    --role="projects/${PROJECT_GOVERNANCE}/roles/ar_agw_cross_project_sa" --quiet || true

  gcloud projects remove-iam-policy-binding ${PROJECT_GOVERNANCE} \
    --member="serviceAccount:${SA}" \
    --role="roles/networkservices.viewer" --quiet || true
done
# 2. remove registry viewer permissions across both spoke projects
for NUM in "${PROJECT_NUMBER_CONCIERGE}" "${PROJECT_NUMBER_SELLERS}"; do
  for MEMBER in \
    "serviceAccount:service-${NUM}@gcp-sa-aiplatform.iam.gserviceaccount.com" \
    "serviceAccount:service-${NUM}@gcp-sa-aiplatform-re.iam.gserviceaccount.com" \
    "serviceAccount:${NUM}-compute@developer.gserviceaccount.com" \
    "principalSet://agents.global.org-${ORG_ID}.system.id.goog/attribute.platformContainer/aiplatform/projects/${NUM}"; do
      gcloud projects remove-iam-policy-binding ${PROJECT_GOVERNANCE} \
        --member="${MEMBER}" \
        --role="roles/agentregistry.viewer" --quiet || true
  done
done
# 3. remove project viewer permissions
for MEMBER in \
  "serviceAccount:${PROJECT_NUMBER_CONCIERGE}-compute@developer.gserviceaccount.com" \
  "serviceAccount:service-${PROJECT_NUMBER_CONCIERGE}@gcp-sa-aiplatform.iam.gserviceaccount.com"; do
    gcloud projects remove-iam-policy-binding ${PROJECT_GOVERNANCE} \
      --member="${MEMBER}" \
      --role="roles/viewer" --quiet || true
done
# 4. remove spoke-to-spoke delegation in Sellers project
for MEMBER in \
  "serviceAccount:service-${PROJECT_NUMBER_CONCIERGE}@gcp-sa-aiplatform.iam.gserviceaccount.com" \
  "serviceAccount:service-${PROJECT_NUMBER_CONCIERGE}@gcp-sa-aiplatform-re.iam.gserviceaccount.com" \
  "serviceAccount:${PROJECT_NUMBER_CONCIERGE}-compute@developer.gserviceaccount.com" \
  "principalSet://agents.global.org-${ORG_ID}.system.id.goog/attribute.platformContainer/aiplatform/projects/${PROJECT_NUMBER_CONCIERGE}"; do
    gcloud projects remove-iam-policy-binding ${PROJECT_SELLERS} \
      --member="${MEMBER}" \
      --role="roles/aiplatform.user" --quiet || true
done
# 5. delete custom IAM role after all bindings have been unlinked
gcloud iam roles delete ar_agw_cross_project_sa \
  --project=${PROJECT_GOVERNANCE} --quiet || true

Если вы назначили роли roles/iam.accessPolicyAdmin и roles/resourcemanager.projectIamAdmin на этапе настройки, удалите их из активного аккаунта пользователя, чтобы восстановить минимальные привилегии:

# 6. remove Access Policy Admin and Project IAM Admin roles from user
for ROLE in "roles/iam.accessPolicyAdmin" "roles/resourcemanager.projectIamAdmin"; do
  gcloud projects remove-iam-policy-binding ${PROJECT_GOVERNANCE} \
    --member="user:$(gcloud config get-value account)" \
    --role="${ROLE}" \
    --condition=None --quiet || true
done

6. Отмена ведения журнала аудита и ограничений правил организации

# 1. Export current Central Governance IAM policy
gcloud projects get-iam-policy ${PROJECT_GOVERNANCE} --format=json > cfg/gov_iam_policy.json
# 2. Filter out iap.googleapis.com from auditConfigs
python3 -c "
import json
with open('cfg/gov_iam_policy.json') as f:
    policy = json.load(f)

if 'auditConfigs' in policy:
    # Remove iap.googleapis.com; if nothing else remains, clear the list
    policy['auditConfigs'] = [
        ac for ac in policy['auditConfigs'] if ac.get('service') != 'iap.googleapis.com'
    ]

with open('cfg/gov_iam_policy.json', 'w') as f:
    json.dump(policy, f, indent=2)
"
# 3. Apply the updated policy to revert audit logging to default
gcloud projects set-iam-policy ${PROJECT_GOVERNANCE} cfg/gov_iam_policy.json

7. Отменить ограничения правил организации

# revert iam v3 access policy binding org policy on project to org level setting
gcloud org-policies delete iam.managed.disableAccessPolicyBinding --project=${PROJECT_GOVERNANCE}

8. Удаление общей промежуточной корзины GCS и локальных артефактов

# delete central staging bucket
gcloud storage rm -r gs://${PROJECT_GOVERNANCE}-shared-staging
# remove local configuration manifests, environment files, and application
rm -rf cfg/ cross-project-multiagent/ *.env

На этом мы заканчиваем раздел, посвященный очистке. Далее переходим к заключению.

12. Заключение

Поздравляем! Вы развернули многопроектную архитектуру Agent-to-Agent (A2A) в Google Cloud и настроили управление ею с помощью Vertex AI Agent Runtime, Central Agent Gateway, Agent Registry и IAM Unified Access Policies (UAP).

Краткое изложение основных понятий

  • Централизованный периметр исходящего трафика. Контейнеры среды выполнения дочерних проектов (PROJECT_CONCIERGE, PROJECT_SELLERS) маршрутизируются через центральный шлюз Agent Gateway в PROJECT_GOVERNANCE с помощью agentGatewayConfig.
  • Декларативное управление (UAP). Фрагментированные привязки для отдельных ресурсов заменены единой политикой доступа IAM, подлежащей аудиту и оцениваемой на шлюзе с помощью IAP v2.
  • Криптографическая идентификация. Принудительное применение минимальных привилегий для исходящего трафика с использованием идентификаторов SPIFFE контейнера (principal://...) вместо долгосрочных ключей.
  • Динамическое обнаружение сервисов. Конечные точки одноранговых агентов разрешаются во время выполнения через центральный реестр агентов, что устраняет необходимость в жестко закодированных URL и идентификаторах проектов.
  • Гибкость правил во время выполнения. Переход pizza-seller-agent от запрета по умолчанию (403 Forbidden) к разрешению (200 OK) в реальном времени с помощью обновления правил без перезапуска контейнеров.

cosmopup

Космопес говорит: "Агенты – это здорово. Они выполняют всю работу по проектам, а я могу сосредоточиться на своей главной цели – вздремнуть!"

Дальнейшие действия и документация