Governança centralizada do Gateway de Agente com o Agent Registry entre projetos para o Agent Runtime

1. Introdução

À medida que as empresas adotam a IA generativa, as arquiteturas estão evoluindo rapidamente de chatbots monolíticos e independentes para sistemas multiagente distribuídos (agente para agente / A2A). Nessas topologias modernas, os agentes orquestradores de alto nível coordenam fluxos de trabalho empresariais complexos delegando tarefas a agentes de domínio especializados, servidores de ferramentas do Protocolo de Contexto de Modelo (MCP) e bancos de dados corporativos de back-end em projetos na nuvem independentes do Google.

No entanto, operar sistemas multiagente em grande escala apresenta desafios críticos de segurança, governança e operação:

  • Proliferação de agentes e ferramentas ocultos: quando as equipes de desenvolvimento implantam agentes em projetos isolados sem um catálogo centralizado, as organizações perdem a visibilidade de quais ferramentas e subagentes existem.
  • Saída entre projetos não monitorada: permitir que os agentes usem rotas de rede diretas e não inspecionadas cria riscos de exfiltração de dados e ignora os perímetros de segurança.
  • Integrações frágeis fixadas no código: fixar no código URLs de agentes downstream e IDs do mecanismo de raciocínio cria dependências frágeis que se rompem durante upgrades ou reimplantações.
  • Falta de identidade de privilégio mínimo: as contas de serviço compartilhadas não oferecem não repúdio criptográfico no nível da instância do agente individual.

Para resolver esses desafios, a Gemini Enterprise Agent Platform oferece um plano de controle unificado de governança e conectividade composto por quatro pilares principais:

  1. Gateway de Agente (networkservices.googleapis.com): um proxy gerenciado de rede regional e aplicação de políticas. Operando no modo de saída AGENT_TO_ANYWHERE, ele intercepta o tráfego de saída do agente, delega avaliações de autorização a extensões de segurança e roteia solicitações entre perímetros de projeto.
  2. Agent Registry (agentregistry.googleapis.com): o catálogo de serviços empresariais único. Ele fornece um diretório centralizado e verificado de todas as ferramentas, servidores MCP e agentes de peering disponíveis na organização, permitindo a descoberta automática dinâmica em tempo de execução com zero endpoints codificados.
  3. Governança de identidade do agente e IAP v2 (iap.googleapis.com e iam.googleapis.com): uma estrutura criptográfica de identidade e acesso. Os agentes em execução recebem URNs de máquina SPIFFE exclusivos e atestados (principal://...). A saída é avaliada em relação às Políticas de acesso unificado do IAM (UAP / IAP v2) centralizadas, verificando a permissão universal iap.googleapis.com/resources.egressViaIAP usando condições avançadas do catálogo da Common Expression Language (CEL) (destination.agent_registry.*).
  4. Agent Runtime (mecanismos de raciocínio): uma plataforma de execução sem servidor totalmente gerenciada para aplicativos de agentes baseados em Python, com vinculações de configuração nativas (agent_gateway_config) para gateways centrais.

Cenário de negócios do codelab: compras de alimentos e bebidas em vários projetos

Neste codelab, você vai criar e controlar um ecossistema de compras real que abrange três projetos distintos do Google Cloud:

  • Projeto de governança central (PROJECT_GOVERNANCE): de propriedade da TI central e das SecOps, hospedando o Gateway de Agente central, o registro de agente central e as políticas de acesso unificado do IAM.
  • Projeto do orquestrador do consumidor (PROJECT_CONCIERGE): pertence à equipe de compras e hospeda o agente concierge de compras, que descobre fornecedores de forma dinâmica e encaminha os pedidos dos clientes.
  • Projeto do fornecedor do domínio (PROJECT_SELLERS): de propriedade de fornecedores externos ou departamentais, que hospeda o Agente de vendas de hambúrgueres e o Agente de vendas de pizzas.

figure1

Fig. 1. Arquitetura de governança centralizada de vários projetos

Por que usar a governança centralizada entre projetos?

Em grandes empresas, as equipes de produtos e os grupos de ciência de dados criam agentes de IA em dezenas de projetos independentes do Google Cloud. Dar a cada equipe controle direto sobre o registro de ferramentas, as rotas de rede de saída e as proteções de segurança cria uma proliferação de ferramentas não verificadas, políticas de DLP inconsistentes, saída de VPC não monitorada e registros de auditoria fragmentados.

A governança centralizada entre projetos separa a criação de políticas da execução do agente:

  • As equipes de TI central e SecOps criam políticas de segurança, avaliam ferramentas e monitoram a saída em um único projeto de governança centralizada.
  • As equipes de produtos e aplicativos se concentram exclusivamente na lógica de negócios em seus projetos independentes do Agent Runtime, vinculando-se diretamente ao gateway central sem a sobrecarga operacional de gerenciar VPCs locais, interconexões ou Policy Engines fragmentados.

figure2

Fig 2. Arquitetura e limites de governança entre projetos de três camadas

Modelo de escopo de identidade de dois níveis nas políticas de acesso unificadas

Quando os agentes se comunicam pelo Gateway de Agente Central, o Identity-Aware Proxy (IAP v2) avalia o acesso com base na Identidade do Agente do autor da chamada, uma identidade criptograficamente atestada e baseada em SPIFFE emitida automaticamente para o contêiner de ambiente de execução, em relação a uma política de acesso global do IAM:

  • Nível 1: APIs de referência do Google Cloud (granularidade ampla usando principalSet:// na Regra 1): autorização de saída em todo o projeto que permite que todos os ambientes de execução de agentes em projetos spoke acessem as APIs padrão do Google (aiplatform, iamcredentials, telemetry, agentregistry) para descoberta, geração de tokens e inferência.
  • Nível 2: ferramentas de negócios e serviços A2A (detalhados via principal:// nas regras 2 e 3): acesso estrito de privilégio mínimo vinculado a instâncias individuais do mecanismo de raciocínio, aplicado com condições da Common Expression Language (CEL) direcionadas a serviços específicos do registro de agentes (destination.agent_registry.agent.name).

O que você vai criar

  • Gateway de Agente centralizado (centralized-agw) em PROJECT_GOVERNANCE
  • Extensão de serviço de autorização do IAP v2 e política de autorização no modo ENFORCE estrito (failOpen: false)
  • Política de acesso unificada do IAM (uap-rules.json) e vinculação de política do projeto
  • Permissões do IAM do agente de serviço entre projetos (ar_agw_cross_project_sa)
  • Bucket de preparo compartilhado e centralizado do Google Cloud Storage (GCS)
  • Agentes de venda de hambúrgueres e pizzas isolados em PROJECT_SELLERS
  • Agente de concierge de compras com descoberta automática de REST dinâmica em PROJECT_CONCIERGE
  • Registros de serviço no Central Agent Registry com URLs mTLS entre projetos
  • Atualizações dinâmicas da política de saída do IAP v2 com verificação em tempo real e auditorias do Cloud Logging

figure3

Fig. 3. Sequência de implementação detalhada

Conteúdo do laboratório

  • Como configurar permissões do IAM de agente de serviço entre projetos para gateways centralizados
  • Como rotear a saída do Agent Runtime por um Gateway de Agente central em ambientes de vários projetos
  • Como delegar a autorização do Gateway de Agente ao Identity-Aware Proxy (IAP v2) usando as extensões de serviço (iapPolicyVersion: "V2")
  • Como criar e vincular políticas de acesso unificado (UAP) do IAM com regras da Common Expression Language (CEL) que regem os destinos registrados do Agent Registry (destination.agent_registry.*)
  • Como eliminar IDs e URLs de agentes codificados usando a descoberta automática de ambiente de execução no Agent Registry
  • Como testar o bloqueio real de confiança zero do perímetro (HTTP 403 Forbidden) e verificar as atualizações de políticas em tempo real no Cloud Logging

O que é necessário

  • 3 projetos do Google Cloud com o faturamento ativado:
    • PROJECT_GOVERNANCE: governança central, gateway, registro e políticas de acesso do IAM
    • PROJECT_CONCIERGE: agente orquestrador de concierge de compras
    • PROJECT_SELLERS: agentes de vendas especializados em hambúrgueres e pizzas
  • Um usuário ou uma conta de serviço do IAM com roles/owner ou permissões administrativas nos três projetos.
  • Uma organização do Google Cloud (para mapeamento de domínio de confiança do SPIFFE)
  • Google Cloud Shell ou uma máquina local com a CLI gcloud, python (3.11+) e uv instalados

Isso conclui a parte de introdução. Agora, vamos para a seção Configuração e ambiente.

2. Configuração

Embora essa arquitetura abranja três projetos na nuvem distintos do Google Cloud, é possível executar 100% dos comandos de implantação do terminal, downloads de repositórios e operações de preparo em um único terminal do Cloud Shell definido como PROJECT_GOVERNANCE. Cada script de implantação e comando gcloud segmenta explicitamente o projeto de destino apropriado por meio de flags da CLI (--project).

Comece acessando a linha de comando do projeto na nuvem do Google Cloud:

Definir o contexto do projeto

# 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

Defina variáveis de ambiente shell.

Insira os identificadores específicos do projeto.

# 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"

Essas variáveis de shell serão derivadas automaticamente.

# 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}"

Criar um diretório local para arquivos de configuração

# create config folder
mkdir -p cfg

Atribuir o papel de administrador de políticas de acesso para políticas de acesso unificadas

# 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

Ativar os registros de auditoria de acesso a dados do Cloud para o IAP v2

Por padrão, o Google Cloud desativa os registros de auditoria de acesso a dados para evitar custos de armazenamento não intencionais. Como o IAP v2 emite decisões de autorização (granted=true e granted=false) como registros de auditoria de acesso a dados, ative a geração de registros ADMIN_READ, DATA_READ e DATA_WRITE para iap.googleapis.com em 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)"

Ativar as APIs obrigatórias do 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

Validar a ativação da API em todos os projetos

Garantir que os três projetos (PROJECT_GOVERNANCE, PROJECT_CONCIERGE e PROJECT_SELLERS) tenham as mesmas APIs ativadas estabelece consistência operacional e evita falhas na geração de tokens de ambiente de execução, erros de catalogação de esquemas ou interrupções de telemetria.

Execute o seguinte script de validação no Cloud Shell para verificar a paridade da API nos três projetos:

# 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

Exemplo de saída de validação:

Todas as APIs vão aparecer como ativadas.

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

Configurar políticas da organização

As políticas padrão da organização do Google Cloud aplicam restrições que limitam as vinculações de políticas de acesso do IAM v3 aos recursos (constraints/iam.managed.disableAccessPolicyBinding).

Substitua as restrições de política da organização herdadas no nível do projeto definindo explicitamente enforce: false como "permitir".

# 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

Isso conclui a parte de configuração. Agora, vamos para a seção Registrar APIs principais do Google.

3. Agent Registry

Registrar o serviço de endpoint de API das APIs principais do Google

O Gateway de Agente exige que os URLs da API do Google sejam registrados no Registro Central de Agentes para que os agentes configurados com agent_gateway_config possam rotear o tráfego de saída com segurança para os principais serviços de back-end do Google Cloud (como aiplatform, credenciais do IAM e telemetria).

Criar core-gapi-services no Agent Registry

# 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

Capturar o ID do recurso do endpoint de API principal

# 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}"

Noções básicas sobre principalSet e principal na identidade do agente

No IAM do Google Cloud e na Gemini Enterprise Agent Platform, as identidades de máquina emitidas para contêineres de agentes em execução usam URNs do SPIFFE atestadas criptograficamente avaliadas pelo Identity-Aware Proxy (IAP v2). Ao configurar as políticas de acesso unificado do IAM, é possível segmentar um principal específico ou um principalSet baseado em atributos:

Dimensão

principal:// (identidade de máquina única)

principalSet:// (grupo com base em atributos)

Sintaxe do IAM

principal://...

principalSet://...

Granularidade

Detalhado (nível de instância): identifica uma única instância específica de contêiner do Mecanismo de Raciocínio.

Granularidade grossa (para envolvidos no projeto): identifica todos os mecanismos de raciocínio que compartilham um atributo de projeto comum.

Padrão de 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}

Caso de uso na Agent Platform

Nível 2 (ferramentas de negócios e A2A): autorização de agentes orquestradores específicos para invocar ferramentas de domínio de destino (por exemplo, Concierge de Compras $\rightarrow$ Vendedor de Hambúrgueres).

Nível 1 (infraestrutura básica): conceder a todos os agentes em um projeto acesso de saída às APIs do Google Cloud (core-gapi-services).

Impacto no ciclo de vida

Se um agente for excluído e recriado, o novo ID do mecanismo vai exigir uma vinculação de política do IAM atualizada.

É aplicada automaticamente aos agentes recém-implantados no projeto sem outras atualizações do IAM.

Governança declarativa com políticas de acesso unificadas (UAP / IAP v2)

No IAP v1 legado, as políticas de saída eram anexadas diretamente a recursos individuais do Agent Registry usando gcloud beta iap web add-iam-policy-binding. No IAP v2 e nas Políticas de Acesso Unificadas, as vinculações por recurso são eliminadas em favor de uma política de acesso do IAM única e centralizada (cfg/uap-rules.json).

A autorização de saída fundamental para core-gapi-services será configurada como Regra 1 na Política de Acesso Unificado na Seção 5, garantindo que todos os contêineres de agente tenham rotas de saída fundamentais estabelecidas antes da implantação.

Para detalhes técnicos mais detalhados sobre identificadores principais e mecanismos de identidade de carga de trabalho, consulte:

Isso conclui o registro do endpoint das APIs principais. Agora, vamos para a seção Implantar o Gateway de Agente Centralizado.

4. Gateway de Agente

Implantar o Gateway de Agente centralizado

Implante o Gateway de Agente centralizado (centralized-agw) no modo de saída AGENT_TO_ANYWHERE dentro do projeto $PROJECT_GOVERNANCE.

Definir o manifesto de configuração do gateway

Crie cfg/${AGW_NAME}.yaml para governança de tráfego de saída:

# 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

Importar configuração do Gateway de Agente

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

Verificar os detalhes do Gateway de Agente

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

Exemplo de resposta:

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'

Isso conclui a implantação do gateway. Em seguida, acesse a seção Configurar autorização.

5. Autorização

Configurar a autorização do Gateway de Agente e o UAP básico

O Gateway de Agente protege e controla o tráfego de saída de ferramentas e agentes usando políticas de autorização (networksecurity.authzPolicies) integradas às políticas de acesso unificado (UAP) do Identity-Aware Proxy (IAP v2).

Visão geral da arquitetura de autorização

figure4

Fig. 4. Visão geral da arquitetura de autorização

A arquitetura de autorização é composta por três camadas interconectadas:

  1. Extensão de serviço do IAP (authzExtension): recurso regional configurado com service: iap.googleapis.com, metadata: iapPolicyVersion: "V2" e failOpen: false para aplicação rigorosa de zero trust no perímetro.
  2. Política de autorização do gateway (authzPolicy): recurso regional que segmenta seu gateway de agente com policyProfile: REQUEST_AUTHZ e action: CUSTOM, roteando as verificações de autorização para a extensão de autorização do IAP.
  3. Política de Acesso Unificada e vinculação do IAM (accessPolicy e policyBinding): recurso global do IAM v3 avaliado pelo IAP. Ele verifica a permissão universal iap.googleapis.com/resources.egressViaIAP em relação às identidades SPIFFE do autor da chamada e às condições do catálogo CEL.

Etapa 1: criar e importar a extensão de autorização do IAP v2

Crie o manifesto da extensão de serviço com iapPolicyVersion: "V2" e failOpen: false no modo ENFORCE estrito:

# 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

Importe a extensão de autorização:

# 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}

Verifique se a extensão de autorização está ativa:

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

Exemplo de resposta:

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

Etapa 2: criar e importar a política de autorização do gateway

Crie uma configuração de política de autorização que se conecte ao Gateway de Agente e delegue a verificação de solicitações à extensão de autorização do 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

Importe a política de autorização:

# 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}

Verifique a política de autorização ativa:

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

Etapa 3: criar a política de acesso unificada inicial (regra 1: APIs principais do Google)

Crie cfg/uap-rules.json com a Regra 1 autorizando os três principalSets do projeto a alcançar 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

Etapa 4: criar e vincular a política de acesso do IAM

Crie a política de acesso global do 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

Vincule a política de acesso a 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

Verifique se a vinculação de política está ativa:

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

Exemplo de resposta:

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}

A saída da API do Google Cloud agora está autorizada com segurança nos três projetos no modo estrito ENFORCE.

Isso conclui a configuração da autorização do gateway. Em seguida, acesse a seção Configurar permissões do IAM entre projetos.

6. IAM entre projetos

Configurar permissões do IAM entre projetos

Nessa topologia de vários projetos, os Agent Runtimes residem em projetos spoke (PROJECT_CONCIERGE e PROJECT_SELLERS), enquanto o Gateway de Agente Central e o Agent Registry residem em PROJECT_GOVERNANCE.

Como os projetos do Google Cloud são perímetros de segurança isolados, o acesso entre projetos precisa ser concedido explicitamente em duas camadas operacionais:

  1. Plano de controle (tempo de implantação): ao implantar um contêiner de agente configurado com --agent-gateway-config, o Agent Runtime Service Agent (service-@gcp-sa-aiplatform.iam.gserviceaccount.com) do projeto spoke precisa anexar o contêiner ao gateway central. Criamos um papel personalizado mínimo (ar_agw_cross_project_sa) que concede networkservices.agentGateways.use, get e operations.get em PROJECT_GOVERNANCE.
  2. Plano de dados (execução do ambiente de execução):
    • Descoberta de catálogo: as identidades de spoke precisam de roles/agentregistry.viewer em PROJECT_GOVERNANCE para resolver os endpoints do agente de destino de forma dinâmica.
    • Invocação de destino: o agente Concierge precisa de roles/aiplatform.user em PROJECT_SELLERS para executar consultas nos mecanismos de raciocínio do vendedor.

Criar um papel personalizado do IAM em 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"

Atribuir papel personalizado aos agentes de serviço do 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

Isso conclui a configuração do IAM entre projetos. Agora, vamos para a seção Implantar agentes de vendas e concierge.

7. Agent Runtime

Implantar agentes de vendas e concierge

A base de código do aplicativo multiagente e os scripts de implantação usados neste codelab são mantidos em um repositório remoto do Google Cloud no GitHub. As etapas a seguir vão clonar o repositório localmente, copiar os arquivos necessários para a estrutura do diretório de trabalho atual, limpar os arquivos temporários e instalar as dependências com uv.

Buscar artefatos remotos

# 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

Criar um bucket de preparo central compartilhado

# 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"

Como funciona a vinculação entre projetos do Gateway de Agente

Nesta etapa, você vai implantar os agentes de vendas no projeto spoke (PROJECT_SELLERS) e configurá-los para rotear a saída pelo Gateway de Agente central em 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)

Como a Regra 1 foi estabelecida anteriormente na nossa Política de Acesso Unificado, as solicitações de inicialização de contêiner para as APIs do Google Cloud são permitidas pelo gateway sem interrupção.

Implantar agentes de venda de hambúrgueres e pizzas no 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}

Validar o roteamento do gateway do vendedor

# 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

Implantar o agente de concierge de compras no 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}

Validar o roteamento do gateway de compras

# 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}'

A saída precisa mostrar a identidade e o projeto do Agent Runtime do Concierge e a vinculação ao Gateway de Agente do projeto de governança.

{
  "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"
    }
  }
}

Isso conclui as implantações de agentes. Em seguida, acesse a seção Registrar agentes no Agent Registry central.

8. Registro entre projetos

Registrar agentes no Central Agent Registry

Registre os três agentes no registro central de agentes em PROJECT_GOVERNANCE usando endpoints mTLS regionais entre projetos e números de projeto numéricos.

Registrar serviços como agentes não A2A no Agent Registry

# 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

Capturar IDs do Agent Registry subjacente

# 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}"

Isso conclui a configuração do registro. Em seguida, acesse a seção Configurar políticas de saída de A2A.

9. Políticas de UAP

Configurar políticas de saída de A2A na política de acesso unificada

Na arquitetura de Negação padrão do Gateway de Agente no modo ENFORCE estrito:

  1. Regra 1 (APIs de referência do Google Cloud): permite que os contêineres de agente nos três projetos acessem core-gapi-services.
  2. Regra 2 (Agente Vendedor de Hambúrgueres: PERMITIR): permite que a instância do Agente Concierge de Compras invoque especificamente o Agente Vendedor de Hambúrgueres.
  3. Agente de vendas de pizza (NEGADO por padrão): deixado de fora das regras da política intencionalmente. No modo ENFORCE (failOpen: false), qualquer tentativa do Concierge de invocar o Pizza Seller será encerrada imediatamente no perímetro do gateway com HTTP 403 Forbidden.

Formular a identidade do agente de 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}"

Atualizar o manifesto de atualização com as regras 1 e 2

Para atualizar a política de acesso com segurança sem interferir nas regras atuais, defina o manifesto consolidado completo que contém a Regra 1 (APIs principais do Google) e a Regra 2 (Agente de vendas de hambúrgueres):

# 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

Aplicar política de acesso atualizada

Aplique o manifesto de regras consolidadas de forma declarativa usando --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

Verificar os detalhes da política de acesso do IAM

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

Exemplo de resposta:

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

Isso conclui a configuração da política. Agora, vamos para a seção Testar e verificar as políticas de governança.

10. Verificar políticas

Testar e verificar políticas de governança usando o Cloud Logging

Nesta seção, você vai testar as interações entre agentes (A2A) de diferentes projetos no Espaço IA do Agent Runtime, observar o bloqueio real do perímetro HTTP 403 Forbidden no modo estrito ENFORCE, modificar as Políticas de Acesso Unificadas em tempo real e validar a aprovação imediata do pedido.

Etapa 1: abrir o Espaço IA do Agent Runtime no PROJECT_CONCIERGE

  1. Abra o Console do Google Cloud.
  2. Na barra de seleção de projetos na parte de cima, mude para PROJECT_CONCIERGE.
  3. No menu de navegação, acesse Agent Platform > Agentes > Implantações.
  4. Clique em purchasing-concierge-adk.
  5. Selecione Playground para abrir a interface de chat interativa no lado direito da tela.

Etapa 2: testar o pedido de hambúrguer (correspondência com a regra 2 -> 200 OK)

Na janela de chat do Playground, envie o seguinte comando de pedido:

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

Se uma resposta de confirmação for necessária, envie o seguinte:

Confirmed, please place the order.

Como alternativa, teste de forma programática no Cloud Shell / terminal:

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)
"

Se uma resposta de confirmação for necessária, use este comando:

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'])
"

O que acontece nos bastidores:

  1. Descoberta dinâmica: durante a inicialização da sessão, o Concierge de Compras consultou o Agent Registry Central em PROJECT_GOVERNANCE (via core-gapi-services pelo Gateway de Agente autorizado pela Regra 1) para descobrir o endpoint mTLS regional de burger-seller-agent.
  2. Resolução de intent e invocação de A2A: o Gemini no Assistente de Compras analisa a intent do pedido de comida e invoca o agente de venda de hambúrgueres por uma RPC de saída para https://${REGION}-aiplatform.mtls.googleapis.com/.../reasoningEngines/${BURGER_ENGINE_ID}.
  3. Intercepção de gateway e propagação de SPIFFE: o tráfego de saída é capturado por agent_gateway_config e direcionado para o Gateway de Agente Central em PROJECT_GOVERNANCE, carregando a identidade criptográfica SPIFFE do Concierge (principal://...).
  4. Avaliação da política do IAP v2: o Gateway de Agente Central invoca a extensão de autorização do IAP (authzExtension). O IAP v2 avalia a Regra 2 na Política de Acesso Unificado do IAM. Como o autor da chamada corresponde a ${CONCIERGE_SPIFFE_PRINCIPAL} e o destino corresponde a burger-seller-agent, o IAP retorna ALLOW (granted: true).
  5. Execução entre projetos: o Gateway de Agente faz o proxy da solicitação autorizada entre projetos para PROJECT_SELLERS, em que o mecanismo de raciocínio do vendedor de hambúrgueres processa o pedido e retorna a confirmação.

Resposta esperada:

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

Etapa 3: inspecionar os registros de auditoria do Gateway de Agente e do IAP v2 (HTTP 200 / ALLOWED)

Consulte os registros de solicitação do Gateway de Agente em 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
  )"

Os registros precisam capturar o tráfego de saída originado de ambos os projetos spoke (PROJECT_CONCIERGE e PROJECT_SELLERS) com campos de saída para chamadas de raciocínio do Gemini (generateContent), telemetria do Cloud Trace (/v1/traces) e pesquisas de credenciais do IAM, sendo interceptados e autorizados de forma transparente pela Regra 1 (core-gapi-services).

Consulte os registros de acesso a dados de auditoria do Cloud do IAP v2 para verificar a versão da política POLICY_VERSION_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
  )"

Exemplo de resposta:

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

Etapa 4: testar o pedido de pizza (negação padrão -> HTTP 403 Forbidden ENFORCED)

Na mesma janela de conversa do Playground, envie o seguinte comando de pedido de pizza:

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

Se uma resposta de confirmação for necessária, envie o seguinte:

Confirmed, please place the order.

Como alternativa, teste de forma programática no Cloud Shell / terminal:

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)
"

Se uma resposta de confirmação for necessária, use este comando:

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'])
"

Resposta esperada:

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.

O que acontece nos bastidores:

  1. Descoberta dinâmica: o Purchasing Concierge resolveu o endpoint pizza-seller-agent do Central Agent Registry durante a inicialização.
  2. Resolução de intent e invocação de A2A: o Gemini no Purchasing Concierge tenta enviar a solicitação de pedido de pizza para o endpoint do Pizza Seller em PROJECT_SELLERS.
  3. Intercepção do gateway: a RPC de saída é capturada por agent_gateway_config e direcionada ao Gateway de Agente Central.
  4. Avaliação de política do IAP v2 (negação padrão): o Gateway de Agente Central invoca o IAP v2. Como não há regra na política de acesso unificada que corresponda a pizza-seller-agent, o IAP retorna DENY (granted: false).
  5. Bloqueio de perímetro estrito: como a extensão de autorização está no modo restrito (failOpen: false), o Gateway de Agente central encerra imediatamente a conexão de saída e retorna HTTP 403 Forbidden. O tráfego nunca sai do gateway e nunca chega ao PROJECT_SELLERS.

Etapa 5: inspecionar os registros do Gateway de Agente em busca de solicitações bloqueadas (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
  )"

Exemplo de saída de registro negado:

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

Consulte os registros de auditoria de acesso a dados do IAP v2 para a decisão negada:

# 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
  )"

Exemplo de saída de registro de auditoria negada:

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

Etapa 6: conceder acesso de saída dinamicamente ao agente de pizza

Crie um novo cfg/uap-rules-update-3.json para incluir a Regra 1 (APIs principais), a Regra 2 (agente de venda de hambúrgueres) e agora a Regra 3 (agente de venda de pizzas).

# 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

Aplique a atualização da política em tempo real:

# 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

Etapa 7: consultar o agente de pizza novamente (sucesso imediato com 200 OK)

Na janela de chat do Playground, envie novamente o comando para pedir pizza:

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

Se uma resposta de confirmação for necessária, envie o seguinte:

Confirmed, please place the order.

Como alternativa, teste de forma programática no Cloud Shell / terminal:

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)
"

Se uma resposta de confirmação for necessária, use este comando:

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'])
"

Resposta esperada:

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**

O que acontece nos bastidores:

  1. Atualização dinâmica de políticas: a atualização da política de acesso unificado do IAM entra em vigor imediatamente no mecanismo de avaliação do IAP, sem tempo de inatividade e sem a necessidade de reimplantar contêineres.
  2. Invocação do A2A: o Concierge envia a solicitação pelo Gateway de Agente Central.
  3. Avaliação da política do IAP v2 (aprovação): o IAP v2 corresponde à regra 3, verifica a identidade do autor da chamada e a expressão CEL de destino e retorna ALLOW (granted: true).
  4. Execução entre projetos: o Gateway de Agente Central encaminha o tráfego autorizado para o PROJECT_SELLERS, onde o vendedor de pizzas processa o pedido.

Etapa 8: inspecionar os registros do Gateway de Agente para solicitações de pizza concedidas

# 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
  )"

Exemplo de saída de registro de acesso concedido:

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

Isso conclui o teste e a verificação. Agora, vamos para a seção Limpeza.

11. Limpeza

Para evitar cobranças na sua conta do Google Cloud pelos recursos usados neste codelab, execute as etapas de remoção na ordem inversa de dependência:

1. Limpar implantações do mecanismo de raciocínio

Execute o script cleanup_old_deployments.py incluído nos dois projetos de ambiente de execução para excluir os mecanismos de raciocínio e aguardar as operações de longa duração:

# 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}

Como alternativa, é possível listar e excluir mecanismos de raciocínio inline:

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. Excluir serviços do Agent Registry

# 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. Excluir a vinculação da política de acesso unificada do IAM e a política de acesso

# 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. Excluir o gateway de agente e as políticas de segurança

# 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. Remover vinculações do IAM entre projetos e papel personalizado

# 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

Se você atribuiu roles/iam.accessPolicyAdmin e roles/resourcemanager.projectIamAdmin durante a fase de configuração, remova-os da sua conta de usuário ativa para restaurar o privilégio mínimo:

# 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. Reverter a geração de registros de dados de auditoria e as restrições da política da organização

# 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. Reverter restrições da política da organização

# 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. Excluir o bucket de preparo compartilhado do GCS e os artefatos locais

# 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

Isso conclui a parte de limpeza. Agora, vamos para a Conclusão.

12. Conclusão

Parabéns! Você implantou e governou uma arquitetura de agente de IA para agente de IA (A2A) em vários projetos no Google Cloud usando o Vertex AI Agent Runtime, o Gateway de Agente Central, o Registro de Agentes e as Políticas de Acesso Unificado (UAP) do IAM.

Resumo dos principais conceitos

  • Perímetro de saída centralizado: contêineres de ambiente de execução de spoke roteados (PROJECT_CONCIERGE, PROJECT_SELLERS) por um Gateway de Agente central em PROJECT_GOVERNANCE usando agentGatewayConfig.
  • Governança declarativa (UAP): as vinculações fragmentadas por recurso foram substituídas por uma única política de acesso do IAM auditável, avaliada no gateway pelo IAP v2.
  • Identidade criptográfica: saída de privilégio mínimo aplicada usando identidades SPIFFE de contêiner (principal://...) em vez de chaves de longa duração.
  • Descoberta dinâmica de serviços: endpoints de agentes de peering resolvidos no ambiente de execução pelo Registro Central de Agentes, eliminando URLs e IDs de projeto codificados.
  • Agilidade da política de ambiente de execução: transição de pizza-seller-agent de negação padrão (403 Forbidden) para permitido (200 OK) em tempo real por meio da atualização da política, sem reinicializações de contêiner.

cosmopup

Cosmopup diz: "Os agentes são ótimos. Eles fazem todo o trabalho entre projetos enquanto eu me concentro no meu objetivo principal: tirar uma soneca!"

Próximas etapas e documentação