Agent Platform Troubleshooting
GoogleOfficialPublished by Google
- Skill
- Review and audit
- Software development
- Apache-2.0
- No issues found
AutoGineer summary
Written by AutoGineer from the skill’s own files, not by the author
This skill provides a structured diagnostic framework for identifying and resolving complex issues within the Google Cloud Gemini Enterprise Agent Platform. It covers the entire stack, including Agent Gateway, Agent Connectivity Templates (ACT), Agent Registry, Identity-Aware Proxy (IAP), Model Armor, Semantic Governance Policies (SGP), and network-level configurations like DNS peering and proxy architectures. The skill enforces a strict, rule-based diagnostic flow to ensure efficient troubleshooting while preventing common misconfigurations, such as incorrect IAM assignments or unsupported multi-region registrations.
What it does
- Analyzes Agent Platform logs to identify root causes for 401, 403, 404, 498, 500, and 503 errors.
- Validates Agent Gateway, ACT, and registry configurations against platform best practices.
- Provides specific remediation steps for IAP, Model Armor, and SGP policy enforcement issues.
- Troubleshoots complex network egress scenarios including SWP, explicit proxies, and VPC-SC perimeters.
- Generates a standardized diagnostic report with findings and actionable recommendations.
- Enforces strict operational safety rules to prevent unauthorized mutations or excessive resource consumption.
When to use it
- When agent requests fail with HTTP status codes like 403 (Egress not authorized) or 404 (HTTP CONNECT failure).
- When Agent Runtime instances crash on startup due to connectivity or policy validation errors.
- When troubleshooting configuration mismatches in Agent Gateway, ACT, or registry services.
- When diagnosing VPC-SC perimeter blocks or issues with DNS peering and Private CA TrustConfig.
- When investigating Model Armor or SGP verdict rationales for blocked requests.
Advantages
- Reduces mean time to resolution by following a proven, rule-based diagnostic hierarchy.
- Prevents common configuration pitfalls through mandatory pre-flight checks and strict usage rules.
- Provides clear, actionable remediation steps tailored to the specific failure mode.
- Ensures consistent diagnostic output across different project environments.
- Maintains operational security by requiring the user to own all mutations.
Usage examples
- “My agent is getting a 403 Egress request is not authorized error when calling an external API.”
- The agent will initiate the diagnostic flow, checking IAP egressor bindings on the registry entry and verifying the relevant AuthzPolicy.
- “Why is my Agent Gateway stuck in PROVISIONING state?”
- The agent will check for PSC subnet IP exhaustion and provide guidance on expanding the CIDR range if necessary.
- “My Agent Runtime crashes on startup with connection errors to telemetry.mtls.googleapis.com.”
- The agent will verify if the required telemetry endpoints are registered and suggest updating the AuthorizationPolicy to allow access.
Install
Download the skill as a ZIP:
https://autogineer.com/agent-skills/google/agent-platform-troubleshooting/downloadUnzip it into the cross-client skills folder of your project:
mkdir -p .agents/skills && unzip agent-platform-troubleshooting.zip -d .agents/skills
Documentation by Google
Agent Platform Troubleshooting
Important: You MUST ONLY use the reference files located in this skill's
references/directory (references/field-manual.md,references/known-issues.md,references/agent-gateway.md,references/policies.md,references/agent-registry.md,references/agent-identity.md). Do NOT search for or read external playbooks outside this directory.
Diagnose issues across the Google Cloud Gemini Enterprise Agent Platform: Agent Gateway, Agent Connectivity Templates (ACT), Agent Registry (Services, Agents, MCP Servers, Endpoints), Agent Identity, IAP/UAP policies, Model Armor, Semantic Governance Policies (SGP), explicit/transparent proxies, and tracing. This skill produces a diagnostic report — findings and fix recommendations. It does not apply fixes; the user owns all mutations.
Usage Attribution
MUST READ: Before executing any gcloud, REST API, or MCP command, you
MUST read and follow Usage Attribution. Prefix
direct terminal gcloud calls (never generated scripts, Terraform, or user
code) with
CLOUDSDK_METRICS_REQUEST_ATTRIBUTION="agent-skills/1.0 (skill:agent-platform-troubleshooting)"
and append agent-skills/1.0 (skill:agent-platform-troubleshooting) to direct
HTTP/REST/MCP User-Agent headers.
MANDATORY PRE-FLIGHT CHECKLIST (CHECK BEFORE RESPONDING OR CALLING TOOLS)
CRITICAL: Before generating ANY response or calling any tools, evaluate the user's prompt against Rules 1–14. When a Turn-0 rule matches, execute its directive immediately and STOP.
Rule 1: Out-of-Scope GCP IAM / GCS Queries
If the prompt asks about Compute Engine (GCE), Cloud Storage (GCS) buckets, or generic GCP IAM permissions unrelated to the Agent Platform stack:
IMMEDIATELY DECLINE ON TURN 0. DO NOT CALL ANY TOOLS OR GIVE IAM GUIDES.
Respond: "I decline to troubleshoot generic GCP IAM or GCS access issues, as they are out of scope for the Agent Platform Troubleshooting skill."
Rule 2: Strict Prohibition on Custom Discovery Scripts
If asked to write, generate, compile, or execute a custom Python or bash script to discover resources (e.g., active Agent Runtime instances):
DO NOT CALL ANY TOOLS OR WRITE/RUN ANY SCRIPTS.
RESPOND ON TURN 0: "I cannot write or execute custom Python or bash scripts for resource discovery. Custom discovery scripts are prohibited as they consume excessive turns and cause timeouts. Instead, use standard gcloud CLI commands (see Google Cloud SDK Installation) or curl REST API calls with application default credentials:
gcloud agent-registry agents list --location=us-central1orcurl -H "Authorization: Bearer $(gcloud auth application-default print-access-token)" "https://us-central1-aiplatform.googleapis.com/v1/projects/PROJECT_ID/locations/us-central1/reasoningEngines""
Rule 3: Consolidated Registry for Google APIs / Design Queries
If asked about registering multiple Agent Runtime, Cloud Resource Manager, or Google API interfaces, or structuring services in Agent Registry:
DO NOT CALL ANY TOOLS. RESPOND ON TURN 0 WITH:
Consolidate ALL Google APIs under a single
googleapisservice entry in Agent Registry viagcloud agent-registry services create googleapis --endpoint-spec-type=no-spec.Explicitly state: "Do NOT register each Google API as a separate registry service entry, as separate entries cause resource clutter, complicate IAM policy management, and risk hitting registry quota limits."
List all 8 required base FQDN interfaces (plus global
https://aiplatform.googleapis.comwhen calling global Gemini models):https://agentregistry.googleapis.comhttps://aiplatform.mtls.googleapis.comhttps://cloudresourcemanager.mtls.googleapis.comhttps://iamcredentials.mtls.googleapis.comhttps://telemetry.mtls.googleapis.comhttps://{region}-aiplatform.mtls.googleapis.comhttps://{region}-aiplatform.googleapis.comhttps://aiplatform.{region}.rep.googleapis.com
Refer to
references/agent-registry.md§2 for the full command.
Rule 4: Cloud Run / Cloud Functions Egress 401/403 (OIDC vs IAP MCP)
If an agent gets a 401 or 403 egress error calling an MCP server or tool on
Cloud Run or Cloud Functions:
DO NOT RUN LOG TOOLS. RESPOND ON TURN 0 WITH:
Explain that direct Agent Identity (
principalSet://.../principal://...) to standard Cloud Run OIDC authentication is not natively supported.Recommend Service Account impersonation in agent code to obtain an OIDC ID token, granting
roles/iam.serviceAccountTokenCreatorto the Agent Identity on the target SA (androles/run.invokerto that SA on Cloud Run).Note that for Preview IAP-enabled Cloud Run MCP servers (
--iap --functional-type=mcp-server,run.appshort URL), authorize via IAP/UAP instead ofroles/run.invoker(seereferences/known-issues.mdBKI 21 andreferences/agent-identity.md).
Rule 5: Telemetry & Monitoring Endpoint Blocks on Startup
If Agent Runtime startup crashes (set_up()) with connection resets or
handshake errors reaching telemetry.mtls.googleapis.com:
Evidence: Explicitly check whether all 4 monitoring/tracing endpoints are registered as Endpoints in Agent Registry (
gcloud agent-registry endpoints list):telemetry.mtls.googleapis.com,monitoring.googleapis.com,trace.mtls.googleapis.com, andcloudtrace.googleapis.com.Recommended Fix:
Register missing telemetry/tracing interfaces via
gcloud agent-registry services create --endpoint-spec-type=no-spec(which projects read-only Endpoints in Agent Registry fortelemetry.mtls.googleapis.com,monitoring.googleapis.com,trace.mtls.googleapis.com, andcloudtrace.googleapis.com).Create or update an
AuthorizationPolicy(AuthzPolicy/ IAP / UAP) bound to the Gateway that allows the agent's principal set (identity) to access the telemetry endpoints (seereferences/known-issues.mdBKI 23).
Rule 6: IAP Denial Troubleshooting (403 / Egress request is not authorized)
When logs or symptoms show 403 / Egress request is not authorized via IAP:
Check IAP Egressor bindings on the registry entry FIRST (
roles/iap.egressorviagcloud iap web get-iam-policy --resource-type=agent-registrybound to the server-generated child UUID fromregistryResource, notSERVICE_NAME).Create a registry entry (
gcloud agent-registry services create) if none exists for the exact target hostname.Grant
roles/iap.egressoron the registry entry (IAM v1 —set-iam-policyrequires top-level{"bindings": [...]}JSON without a"policy"wrapper) or verify UAPAccessPolicy+ CRMPolicyBinding(iapPolicyVersion: "V2",iap.googleapis.com/resources.egressViaIAP; seereferences/policies.md§2).Check IAP audit logs (
protoPayload.serviceName="iap.googleapis.com") and verify anAuthorizationPolicy(AuthzPolicy) is bound to the Gateway.Explicitly warn: "Do NOT use
roles/iap.tunnelResourceAccessor(which is for classic IAP TCP tunneling to GCE VMs, not Agent Gateway egress) and do NOT bypass IAP authentication."
Rule 7: PSC Subnet Exhaustion Speed Rule (PROVISIONING Hang)
When diagnosing gateway provisioning stuck or failing on subnet IP exhaustion:
DO NOT loop across regions. Run ONLY in the target region (default
us-central1):gcloud network-services agent-gateways list --location=us-central1gcloud network-services agent-gateways describe <GW> --location=us-central1gcloud compute network-attachments describe <NA> --region=us-central1gcloud compute networks subnets describe <SUBNET> --region=us-central1
Check free IP count (
Usable - Allocated = Free) before recommending any Network Attachment deletion; flag that a/28subnet is easily exhausted and recommend expanding the CIDR range to at least/26(known-issues.mdBKI 19).
Rule 8: Multi-Region Manual Registration Prohibition (us / eu)
If asked about manually registering endpoints or services in us or eu:
DO NOT CALL ANY TOOLS. RESPOND ON TURN 0:
"Manual endpoint/service registration is NOT supported in
usoreumulti-region locations (usandeuare read-only roll-up locations for auto-discovered resources)." (Explicitly name BOTHusandeu.)"Register manually in a specific region (e.g.,
us-central1) orglobal."
Rule 9: VPC Service Controls (VPC-SC) Perimeter & ACT ALL_TRAFFIC
When diagnosing VPC-SC perimeter blocks or external API hangs under VPC-SC:
Identify a VPC Service Controls perimeter block / boundary enforcement.
Check that the Agent Connectivity Template (ACT) sets
vpcEgress: ALL_TRAFFIC(native VPC-SC creation works out of the box since Sept 8, 2026 whenvpcEgress: ALL_TRAFFICis set) and that Cloud NAT is configured on the consumer VPC's PSC-I network attachment subnet.For legacy or strict custom perimeters blocking Google actuation identities, recommend creating ingress policies allowing:
Explicitly state: "Do NOT disable VPC Service Controls or delete perimeters." Refer to
references/known-issues.mdBKI 18 & 28.
Rule 10: UAP Policy Binding Org Policy Blocker & Blanket DENY Crash
CUSTOM_ORG_POLICY_VIOLATIONongcloud iam policy-bindings create: Caused byconstraints/iam.managed.disableAccessPolicyBinding(note: some docs misspell this plural). Apply an Org Policy override (enforce: false) at the target resource and wait 30 to 60 seconds for IAM propagation (BKI 24).Startup crash after UAP
destination.is_registered == falseDENY: Explain that the blanketDENYblocked internal/unauthenticated SDK bootstrap and metadata calls (such as Cloud Resource Manager project lookups) during Reasoning Engine init; remove or scope the blanketDENYrule (BKI 5).
Rule 11: Agent Gateway Dual-Registry & Jurisdiction Invariants
When configuring or validating registries on an Agent Gateway:
An Agent Gateway supports a maximum of two registries.
When two registries are configured, exactly one must be
globaland the other must beregionalormulti-regional.Binding two regional registries (e.g.,
us-central1+europe-west1), two multi-regionals, two globals, or a mismatched jurisdiction (e.g.,us-central1gateway +euregistry) is invalid and rejected with an HTTP 400 validation error (references/agent-gateway.md§4, BKI 26).
Rule 12: Cross-Project Runtime-to-Gateway Binding Diagnosis
When Reasoning Engine in Project 1 fails to bind to a Gateway in Project 2:
Deploying Identity: Needs
networkservices.agentGateways.useandnetworkservices.agentGateways.get(orroles/networkservices.viewer) in Project 2.Vertex AI Service Agent: Grant
roles/networkservices.vieweror custom roleae_agw_cross_project_sa(aliasar_agw_cross_project_sa) toservice-PROJECT_1_NUMBER@gcp-sa-aiplatform.iam.gserviceaccount.comin Project 2.Regional Colocation: Verify both runtime and gateway are in the same region.
CAA Opt-Out: Set
GOOGLE_API_PREVENT_AGENT_TOKEN_SHARING_FOR_GCP_SERVICEStoFalse(references/known-issues.mdBKI 10,references/agent-gateway.md§11).
Rule 13: Downstream SWP + PBR vs. Explicit Forward HTTPS Proxy
Distinguish the two supported consumer-VPC proxy architectures:
Pattern A — Transparent Next-Hop SWP + PBR (no client
HTTPS_PROXYneeded):Explain that PSC endpoints cannot be route next-hops, SWP behind PSC Service Attachments only supports explicit proxy mode (not transparent next-hop mode), and static routes cannot be used because Network Attachments cannot have VM network tags.
Deploy SWP directly in the consumer VPC with
type: SECURE_WEB_GATEWAYandroutingMode: NEXT_HOP_ROUTING_MODEon an internal IP on the PSC-I subnet (e.g.,10.20.1.250), with a/26REGIONAL_MANAGED_PROXYACTIVEsubnet.Configure Policy-Based Routing (PBR) matching source CIDR
10.20.1.0/24to--next-hop-ilb-ip=10.20.1.250(priority 200) + fallback PBR (1000).Create Cloud NAT with
ENDPOINT_TYPE_SWG(nats create ... --endpoint-types=ENDPOINT_TYPE_VM,ENDPOINT_TYPE_SWG), and allowlist<region>-aiplatform.mtls.googleapis.comin SWP CEL (BKI 33).
Pattern B — Explicit Forward HTTPS Proxy (
HTTPS_PROXY+ L4AuthzPolicy):Without L4 bypass,
HTTPS_PROXY=https://proxy.corp.internal:443fails onHTTP CONNECTwith HTTP404because Agent Gateway's L7 HTTP manager terminates TLS and rejects nestedCONNECT.Import a
v1beta1L4AuthzPolicyviagcloud beta network-security authz-policies importwithaction: ALLOWandnetworkRulesmatchingto.operations[].snis(omittarget.loadBalancingSchemeandports; max 5 rules; omitfromor include7.0.0.0/8+ PSC-I CIDR infrom.sources[].ipBlocks).Listen with TLS on
:443, allow the consumer PSC-I subnet CIDR on VPC firewall/proxy ACLs (never7.0.0.0/8), setNO_PROXY/no_proxyto.googleapis.com,googleapis.com,metadata.google.internal,169.254.169.254,localhost,127.0.0.1, bundleagentGatewayCard.rootCertificates+ Private CA in container env vars (SSL_CERT_FILE,REQUESTS_CA_BUNDLE), and warn L4ALLOWbypasses L7 IAP/UAP, Model Armor, SGP, andhttpRulesfor matched SNIs (BKI 34).
Rule 14: DNS Peering, TrustConfig, Model Armor, SGP, MCP, Identity, & Lifecycle
DNS Peering & Service Agent: Public FQDNs resolve via public recursive DNS. Private DNS requires
dnsPeeringConfig(domainsarray with trailing.; ACT uses stringtargetNetworkonly, whereas inlineAgentGateway.networkConfig.dnsPeeringConfigalso requirestargetProject). Cloud DNS caps total peering hops at 2 (Gateway consumes Hop 1 → max 1 consumer peering hop; a 2nd hop fails preflight or returnsSERVFAIL→503, BKI 35). Grantroles/dns.peer,roles/dns.reader,roles/compute.networkViewer, androles/compute.networkUsertoservice-GATEWAY_PROJECT_NUMBER@gcp-sa-agentgateway.iam.gserviceaccount.com(andcompute.networkAttachments.useto caller, BKI 9). Never use authoritative"."private root zones ("."requires a Cloud DNS Private Forwarding Zone).Private CA
TrustConfig& Immutable ACT: SetegressNetworkConfig.tlsConfig.trustConfigwithadditionalRoots: PUBLICLY_TRUSTED_ROOTS(omitting breaks*.googleapis.com). ACT fields are immutable — create a new ACT and rebind viaPATCHwithupdateMask=agentConnectivityTemplate(BKI 6).Model Armor (
CONTENT_AUTHZ): Ingress (CLIENT_TO_AGENT, max 1, ADKstreamQueryonly) usesservice-AGENT_PROJECT_NUMBER@gcp-sa-aiplatform-re.iam.gserviceaccount.com; Egress (AGENT_TO_ANYWHERE, max 4) usesservice-GATEWAY_PROJECT_NUMBER@gcp-sa-dep.iam.gserviceaccount.com. ScopehttpRuleswhento exclude gRPC and streams >60 seconds (Stream removed, BKI 13).Semantic Governance Policies (SGP): Uses
gcp-sa-aiplatform, non-streaming LLM calls only,.contains()+!request.headers['content-type'].startsWith('application/grpc')inwhen, and full-map updates onPATCH ?updateMask=metadatasosgpEnforcementMode: "DRY_RUN"andsgpUserIntentMatchingare preserved (BKI 38).Native MCP, Identity, Trace, & Teardown: Set
baseProtocolMethodsOption: MATCH_BASE_PROTOCOL_METHODSon MCPALLOWrules (BKI 39). Orgless trust domains useagents.global.proj-PROJECT_NUMBER.system.id.goog(proj-, neverproject-); never grantroles/aiplatform.expressUseror project-wideroles/aiplatform.userto Agent Identities. For Cloud Trace (TelemetryPolicy), always recommend enablingcloudtrace.googleapis.com(gcloud services enable cloudtrace.googleapis.com) and grantingroles/cloudtrace.agenttocompute-system,gcp-sa-networksecurity, andgcp-sa-agentgateway(seereferences/agent-gateway.md§8.3 andreferences/field-manual.mdStep 14.4). Tear down in strict 7-step order (workloads → UAP bindings →AuthzPolicy→ extensions →AgentGateway→ ACT → VPC) and use unique gateway IDs if deletion tombstones collide (BKIs 25, 30).
Quick Symptom Triage Matrix
| Symptom / Error | Primary Suspect | Reference |
|---|---|---|
403 / Egress request is not authorized | Unregistered FQDN, missing roles/iap.egressor / UAP binding, or PAB | Rules 4, 6; references/policies.md §2; BKIs 4, 7, 8, 15 |
HTTP 404 on HTTP CONNECT (HTTPS_PROXY) | Missing gcloud beta L4 AuthzPolicy (networkRules snis) | Rule 13B; references/agent-gateway.md §6.3; BKI 34 |
HTTP 498 immediately after binding runtime | Enforcing gateway bound before allowlisting essential platform APIs | references/agent-gateway.md §2; BKI 37 |
500 / PERMISSION_DENIED / Stream removed | Model Armor SA roles (gcp-sa-dep vs gcp-sa-aiplatform-re) or 60s stream cap | Rule 14; references/policies.md §12; BKI 13 |
503 / NXDOMAIN / SERVFAIL on private host | >2-hop DNS peering, authoritative "." zone, Packet Intercept, or SWP | Rules 13A, 14; references/agent-gateway.md §3, §6; BKIs 33, 35, 36 |
Config validation failed / PERMISSION_DENIED on import | Disabled agentregistry.googleapis.com, missing caller/gcp-sa-agentgateway IAM, or bad NA URI | Rule 14; BKIs 9, 20, 26 |
HTTP 409 ABORTED / 400 already being used | Active control-plane LRO mutex (~2 min), tombstone collision, or reverse teardown | references/agent-gateway.md §9, §10; BKIs 25, 29, 30 |
Startup crash (reasoning_engine_stderr) | Blocked telemetry/CRM bootstrap, UAP blanket DENY, or SGP gRPC interception | Rules 5, 10, 14; BKIs 1, 5, 17, 23, 38 |
When to use this skill
Trigger on Agent Gateway, Agent Connectivity Templates (ACT), Agent Registry,
Agent Identity, IAP/UAP policies, Model Armor, Semantic Governance Policies
(SGP), SWP/Explicit Proxy egress, Private CA TrustConfig, Cloud DNS peering,
Cloud Trace TelemetryPolicy, or Gemini Enterprise / Agent Runtime / GKE /
Cloud Run gateway bindings. Do not use for generic Google Cloud IAM, GCS,
GCE, or networking issues unrelated to the Agent Platform stack.
Required context & Hypothesis Rules
Gather PROJECT_ID, PROJECT_NUMBER, LOCATION (default us-central1),
AGENT_ID, AGENT_GATEWAY_NAME, Agent Identity (principal:// or
principalSet://), exact error + timestamp, and target destination. If missing,
ask or state unknowns using placeholders — never scan across projects or loop
over regions. Before Step 1 queries, formulate at most 3 hypotheses anchored
to "Why did it start failing now?" (e.g., recent Terraform/config changes).
Diagnostic flow
Follow in order (copy-paste commands in references/field-manual.md):
Step 0 — Pre-Flight & Access: Evaluate Rules 1–14. Verify project access (
gcloud projects describe $PROJECT_ID).Step 1 — Agent & Startup Logs: Classify error (
403,404 CONNECT,498,500,503, crash, timeout). Check PSC subnet IPs, ACTALL_TRAFFIC+ Cloud NAT, andagentGatewayCard.rootCertificatesCA trust.Step 2 — Gateway Logs: Query
networkservices.googleapis.com/Gateway(filtering-httpRequest.requestMethod="CONNECT"when inspecting L7 requests) to find the exact failing SNI/hostname andserviceExtensionsInfo.Step 3 — IAP / Model Armor / SGP Logs: Check
iapPolicyVersion(V1vsV2),DRY_RUNvs enforced, Model Armor callout status, orlogs/semantic-governance-policyverdicts and rationales.Step 4 — Registry State: Verify exact hostname permutations in
gcloud agent-registry {services,endpoints,mcp-servers,agents} listand confirm dual-registry + jurisdiction rules.Step 5 — Identity & Policies: Check
roles/iap.egressorvia--resource-type=agent-registryon the child UUID (V1), UAPAccessPolicy/PolicyBinding+constraints/iam.managed.disableAccessPolicyBinding(V2), PAB overrides, and least-privilege Agent Identity roles (proj-for orgless).Step 6 — AuthzPolicy & Extensions: Verify L4
networkRulesvs L7httpRules, MCPMATCH_BASE_PROTOCOL_METHODS, Model Armor / SGPwhenexclusions,ExtensionBindingscope, and policy caps (max 4 egress, max 1 ingressCONTENT_AUTHZ).Step 7 — Connectivity, DNS, & TLS: Verify ACT
TrustConfig(PUBLICLY_TRUSTED_ROOTS),gcp-sa-agentgatewayIAM, <=2 DNS peering hops, no Packet Intercept on the consumer VPC, and SWP/Explicit Proxy settings.Step 8 — PrincipalSet & Trace Verification: Test 1:1
principal://binding ifprincipalSet://lags; for Cloud Trace, enablecloudtrace.googleapis.com, verifyv1beta1TelemetryPolicy(telemetryTargetreferencingagentGateways/*and objectparentBasedSampling: {enabled: true, samplingRate: 1.0}, never stringENABLED), and grantroles/cloudtrace.agenttoservice-<PROJECT_NUMBER>@compute-system.iam.gserviceaccount.com,service-<PROJECT_NUMBER>@gcp-sa-networksecurity.iam.gserviceaccount.com, andservice-<PROJECT_NUMBER>@gcp-sa-agentgateway.iam.gserviceaccount.com.
Tools & Layered References
Use mcp__gcloud__run_gcloud_command (or gcloud --quiet),
mcp__gcloud-observability__list_log_entries (or gcloud logging read),
mcp__google-dev-knowledge__search_documents / get_documents / answer_query,
and curl REST GETs, plus bundled references (load only the smallest set needed):
references/field-manual.md — read first for operational commands & log queries
references/known-issues.md — BKIs #1–#13, #15–#39 for fast pattern matches
references/agent-gateway.md — topologies, ACT, DNS, TLS, SWP/Explicit Proxy, Trace, teardown
references/policies.md — L4/L7
AuthzPolicy, MCP, UAP v3beta, Model Armor, SGPreferences/agent-registry.md —
servicesvs projectedendpoints, 9 API families, jurisdictionsreferences/agent-identity.md — 3-identity delegation, Cloud Run OIDC vs IAP MCP, WIF
Output report
Always produce a read-only diagnostic report using this exact structure:
# Agent Platform Diagnostic — <one-line summary>
## Context
- Project: <id> (<number>) | Location: <region>
- Agent: <agent_id> | Gateway: <gateway_name>
- Symptom: <exact error message and when it started>
## Evidence gathered
- Agent log query: <filter, summary>
- Gateway log query: <filter, exact failing hostname / extension status>
- IAP / Policy log query: <filter, decision + enforcement mode>
- Registry & Policy state: <entries, AuthzPolicy bindings, roles/iap.egressor or UAP>
## Root cause hypothesis
<ranked root cause(s)>
## Why this fits the evidence
<connect log/config evidence to root cause>
## Recommended fix
<ordered gcloud / curl / YAML / Terraform changes for the user to apply>
## What to verify after the fix
<verification queries>
## Open questions / unknowns
<missing context or unverified items>
## Appendix: Raw Logs & Verified Links
- **Cloud Logging Filter**: <exact copy-pasteable filter or deep link>
- **Raw Logs**: <untruncated Agent, Gateway, and IAP/Policy log entries>Principles
Exact hostname match first: Compare gateway log hostnames directly against Agent Registry.
Layered default-deny: Registry → Gateway
AuthzPolicy→ Extension → IAP/UAP/Model Armor/SGP → PAB (PAB overrides IAM Allow).L4 bypass vs L7 governance: L4
networkRulesenablesHTTP CONNECTexplicit proxies (:443) but bypasses L7 IAP, Model Armor, and SGP for matched SNIs.Read-only, single-region, non-interactive: Pull evidence first, never mutate resources, check
us-central1unless logs indicate otherwise, and pass--quiet.
Supporting Links
Files
- ATTRIBUTION.mdAdded by AutoGineer878 B
- LICENSELicence file added by AutoGineer11.1 KiB
- SKILL.md26.2 KiB
- references/agent-gateway.md55.2 KiB
- references/agent-identity.md33.6 KiB
- references/agent-registry.md49.7 KiB
- references/attribution.md1.9 KiB
- references/field-manual.md68.3 KiB
- references/known-issues.md87.6 KiB
- references/policies.md75.2 KiB
references/policies.md
Agent Platform Policies — Debugging Reference
Reference for diagnosing IAM, IAP, and Agent Gateway policy denials in the Google Cloud Gemini Enterprise Agent Platform. Read this when an agent gets a 403, when an MCP/agent/endpoint call is silently dropped, or when a freshly applied policy "doesn't take."
Agent Gateway enforces governance across five complementary policy layers:
Network Security
AuthzPolicy(networksecurity.googleapis.comv1/v1beta1): Gateway attachment and traffic-phase dispatcher supporting L7httpRules(including native MCP method/tool matching), L4 SNInetworkRules(gcloud beta), delegatedCUSTOMservice extensions (REQUEST_AUTHZandCONTENT_AUTHZ), andDENY_BY_DEFAULTfallback enforcement.IAM Unified Access Policy (UAP / Policy V2,
iam.googleapis.com/v3) & Legacy IAM v1: Agent-to-Anywhere (egress) identity and destination authorization evaluated by IAP (service: iap.googleapis.com,iapPolicyVersion: "V2"vs. legacy"V1"roles/iap.egressor).Model Armor Content Security (
CONTENT_AUTHZ): Inline prompt/response screening and Sensitive Data Protection (SDP) redaction across Client-to-Agent (ingress) and Agent-to-Anywhere (egress) flows.Semantic Governance Policies (SGP): Context-aware natural-language constraints and user-intent alignment evaluated by the LLM in the semantic governance policy engine on non-streaming LLM requests.
Extensible Policies (
ExtensionBinding/ProducerExtension, Preview) & Token Management Policies (Preview): Simplified 1P/2P/3Pext_procbindings and shared LLM token quota enforcement returning HTTP429.
Table of Contents
TL;DR for debugging: Lines 50-157
1. UAP (Policy V2) vs. Legacy IAM v1 Architecture: Lines 158-223
2. UAP Core Architectural Constructs: Lines 224-310
3. Runtime Evaluation Pipeline & Mathematical Invariants: Lines 311-378
4. Deep CEL Attribute Reference: Lines 379-489
5. The 3-Tier Organizational Security Model: Lines 490-697
6. Legacy IAM v1 Reference (Backward Compatibility): Lines 698-729
7. Observability, Auditing, & Dry-Run Operations: Lines 730-784
8. Production Edge Cases & Architectural Gotchas: Lines 785-885
9. Quick Command Cheat Sheet: Lines 886-927
10. Debugging Flowchart: Lines 928-962
11. Network Security AuthzPolicy: L7 HTTP/MCP Rules, L4 networkRules, & DENY_BY_DEFAULT: Lines 963-1073
12. Model Armor Content Security Policies (CONTENT_AUTHZ): Lines 1074-1102
13. Semantic Governance Policies (SGP): Lines 1103-1173
14. Extensible Policies: ExtensionBinding & ProducerExtension (Preview): Lines 1174-1207
15. Token Management Policies (Preview — HTTP 429): Lines 1208-1222
TL;DR for debugging
Check Policy Architecture First (
V1vsV2):In Gateway metadata: Check if the attached IAP
AuthzExtensionspecifiesiapPolicyVersion: "V2". Without this key, the gateway defaults to legacy V1 shadow-resource evaluation. Active blocking is enabled by omittingiamEnforcementMode(or settingiamEnforcementMode: "ENFORCE", never"ENFORCED").In Cloud Audit Logs (
protoPayload.serviceName="iap.googleapis.com"): CheckprotoPayload.metadata.iapPolicyVersion. If lowercase"v2", diagnose UAPAccessPolicyandPolicyBindingresources. If absent or"v1", diagnose legacy IAM v1 bindings on Agent Registry entries.
The UAP Permission MUST be FQDN: In UAP access policy rules (
operation.permissions), the permission MUST be specified in full FQDN format:iap.googleapis.com/resources.egressViaIAP(Agent-to-Anywhere egress). Using the short formiap.resources.egressViaIAPcauses the IAM policy compiler to reject the policy, even though Cloud Audit Logs recordprotoPayload.authorizationInfo.permission="iap.resources.egressViaIAP"(V2) or"iap.webServiceVersions.egressViaIAP"(V1).UAP Policy Bindings Target Resource Manager Nodes:
PolicyBindingtargets must be canonical Resource Manager URIs using numeric IDs (e.g.//cloudresourcemanager.googleapis.com/projects/${PROJECT_NUMBER}or//cloudresourcemanager.googleapis.com/folders/${FOLDER_ID}). Passing gateway or registry resource names causesINVALID_TARGET_FULL_RESOURCE_NAME.UAP Invariants: Absolute DENY Precedence & Additive ALLOW Aggregation:
Absolute DENY Precedence: If ANY matching rule across Org, Folder, or Project evaluates to DENY, the request is immediately rejected (HTTP 403). There are NO deny exceptions.
Additive ALLOW Aggregation: Permissions granted at Org/Folder level cannot be narrowed or revoked by Project developers.
Mandatory CEL Null-Dereference Guard: When an agent calls non-tool MCP methods (
prompts/list,resources/read), thetoolobject insidemcp_serverisnull. A CEL condition dereferencing.tool.nameor.tool.annotations.*without verifying the method crashes Gatekeeper with a CEL evaluation error. Always prepend:(destination.agent_registry.mcp_server.method == 'tools').Avoid Blanket Unregistered DENY (Container Boot Crash): Never configure an Org or Folder-level rule with
(destination.is_registered == false)andeffect: DENY. Reasoning Engine containers boot by contacting public endpoints (telemetry.mtls.googleapis.com). An absolute DENY causes container boot crashes. Rely instead on Gateway default-deny.Zendesk / REST Path Extensions (
.json): Strict prefix matching in unregistered UAP rules (e.g..startsWith('/api/v2/tickets')) leaves vulnerabilities to sibling paths (e.g./api/v2/tickets_export), while exact matching on/api/v2/ticketsbreaks SDK clients that append.json(/api/v2/tickets.json). Match exact paths,/subpaths,.json, and?query strings explicitly.Org Policy Blocker (
constraints/iam.managed.disableAccessPolicyBinding): If creating a policy binding fails withCUSTOM_ORG_POLICY_VIOLATION, an Org Admin must set an Org Policy V2 override settingenforce: falseat the target CRM level using the exact singular constraint nameconstraints/iam.managed.disableAccessPolicyBinding(some documentation pages contain a plural typodisableAccessPolicyBindingsthatgcloud org-policiesrejects). Allow ~30–60 seconds for IAM policy/binding propagation.Terraform Binding Destruction Race: Simultaneous destruction of
PolicyBindingandAccessPolicyfails with400 FAILED_PRECONDITION: Access policy is referenced by active policy binding(s)due to IAM replication latency (10–30 s). Destroy scripts must poll GET the binding URL until HTTP 404 is confirmed before deleting the policy.Legacy IAM v1 Fallback: In legacy v1, outbound agent traffic is authorized by granting
roles/iap.egressordirectly on the Agent Registry resource (gcloud iap web set-iam-policy --resource-type=agent-registry). Do NOT useroles/iap.tunnelResourceAccessoror web ingress roles.Omit
loadBalancingScheme& Usegcloud betafor L4networkRules: Never settarget.loadBalancingSchemeonAuthzPolicyresources targetingagentGateways/*(causesINVALID_ARGUMENT). L4networkRulesrequiregcloud beta network-security authz-policies import, allowsnisonly (noportsor HTTP fields), and bypass L7 IAP/UAP, Model Armor, and SGP inspection on matched TLS tunnels.Native MCP
AuthzPolicyALLOWRules RequireMATCH_BASE_PROTOCOL_METHODS:httpRules[].to.operations[].mcpdefaultsbaseProtocolMethodsOptiontoSKIP_BASE_PROTOCOL_METHODS. Every MCPALLOWrule must explicitly setbaseProtocolMethodsOption: MATCH_BASE_PROTOCOL_METHODSsoinitializeandpinghandshakes succeed.Model Armor Distinct Service Agents & 60s Stream Deadline: Ingress (
CLIENT_TO_AGENT, ADKstreamQueryonly) runs asservice-${AGENT_PROJECT_NUMBER}@gcp-sa-aiplatform-re.iam.gserviceaccount.com; egress (AGENT_TO_ANYWHERE) runs asservice-${GATEWAY_PROJECT_NUMBER}@gcp-sa-dep.iam.gserviceaccount.com. Envoy enforces a hard 60-second total stream deadline onCONTENT_AUTHZcallouts — exclude>60sstreams or gRPC channels viahttpRulesto avoidStream removederrors.SGP Non-Streaming CEL Filter & Metadata Map Overwrite: Scope SGP
CONTENT_AUTHZpolicies with awhenCEL filter using.contains()on non-streaming LLM paths (:generateContent,:rawPredict,/v1/messages,/chat/completions). UpdatingAuthzExtensionmetadata (importorupdateMask=metadata) replaces the entire map — always pass all active keys (sgpEnforcementMode,sgpUserIntentMatching) together.
1. UAP (Policy V2) vs. Legacy IAM v1 Architecture
+-------------------------------------------------------------------------------+
| Legacy IAM v1 Model (Monolithic & Per-Resource) |
| |
| Resource (e.g. Storage Bucket, IAP Web Backend Shadow Resource) |
| └── IAM Policy |
| └── Bindings: [Role: roles/iap.egressor, Members: [principal://...]] |
| |
| Limitations: |
| - Policy is physically glued to the target resource / shadow resource. |
| - Zero reusability across projects or folders. |
| - Synthetic shadow resources: projects/123/iap_web/compute/services/... |
| - Coarse-grained network/L7 attributes. |
+-------------------------------------------------------------------------------+
│
▼
+-------------------------------------------------------------------------------+
| Unified Access Policy (UAP / Policy V2) Model (Decoupled & Hierarchical) |
| |
| +──────────────────────────+ +───────────────────────────+ |
| │ CRM Hierarchy Node │ │ AccessPolicy │ |
| │ (Org, Folder, Project) │ │ (Reusable Rule Definition)│ |
| +─────────────┬────────────+ +─────────────┬─────────────+ |
| │ │ |
| │ binds to │ |
| └──────────────► ◄────────────────────┘ |
| │ |
| +──────────┴──────────+ |
| │ PolicyBinding │ |
| │ target: CRM Node │ |
| │ policy: AccessPolicy│ |
| +─────────────────────+ |
+-------------------------------------------------------------------------------+Architectural Comparison Matrix
| Feature | Legacy IAM v1 | Unified Access Policy (UAP / Policy V2) |
: Dimension : : :
| :------------- | :------------------------- | :----------------------------------------------------------------------------- |
| Coupling | Tightly Coupled: | Decoupled: Standalone first-class objects referenced by multiple bindings. |
: : Policies live inside : :
: : individual resource : :
: : metadata. : :
| Attachment | Concrete service resources | Canonical Resource Manager nodes: |
: Target : or synthetic shadow : //cloudresourcemanager.googleapis.com/{organizations|folders|projects}/{ID}. :
: : resources (iap_web). : :
| Rule | Primitive | Explicit effect: ALLOW or effect: DENY, target FQDN permissions, and deep |
: Capabilities : Role-to-Principal bindings : L7 CEL conditions. :
: : with basic contextual : :
: : conditions. : :
| Evaluation | Unordered role evaluation; | Unified evaluation pipeline: Org → Folder → Project |
: Order : Deny policies exist as a : with strict absolute DENY precedence and additive ALLOW aggregation. :
: : separate API : :
: : (iam.googleapis.com/v2). : :
| Enforcement | Binary (always enforced). | Dual-mode: DRY_RUN (audit log simulation) or ENFORCE / omitted |
: Modes : : (active blocking). :
| Workload | Static service accounts | Extends natively to ephemeral Workload Identity Federation for Agents |
: Targets : and human users. : (principal\://agents.global.org-...). :
2. UAP Core Architectural Constructs
UAP is comprised of two core resources in the iam.googleapis.com/v3 (v3beta) API:
2.1 AccessPolicy (iam.googleapis.com/v3/.../accessPolicies)
An AccessPolicy is a versioned container of authorization rules defining
what is permitted or forbidden, for whom, and under what conditions.
gcloud Rules Array File (rules.json passed to --details-rules=rules.json):
[
{
"description": "Permit access to registered corporate tools",
"effect": "ALLOW",
"principals": [
"principalSet://agents.global.org-112233445566.system.id.goog/attribute.platform/aiplatform"
],
"operation": {
"permissions": [
"iap.googleapis.com/resources.egressViaIAP"
]
},
"conditions": {
"iap.googleapis.com": {
"title": "allow_registered_mcp",
"description": "Matches registered corporate MCP servers",
"expression": "(destination.is_registered == true) && (destination.agent_registry.resource_type == 'MCP_SERVER')"
}
}
}
]gcloud iam access-policies create platform-egress-policy \
--details-rules=rules.json \
--project="${PROJECT_ID}" \
--location=global \
--display-name="Platform Egress Policy"Note: REST API vs.
gcloudvs. Terraform Schema Differences:
gcloud iam access-policies create --details-rules=: Expects a top-level JSON array[ { ... } ]of rule objects (there is no--fileflag).REST API (
POST https://iam.googleapis.com/v3beta/{parent}/locations/global/accessPolicies?accessPolicyId={id}): Wraps the rules array inside{"displayName": "...", "details": {"rules": [ ... ]}}. BothgcloudJSON and REST JSON use singular"operation": {"permissions": [...]}and plural service-keyed map"conditions": {"iap.googleapis.com": { ... }}.Terraform (
google_iam_{organization,folder,project}_access_policyingoogle-beta): Uses HCL blocksdetails { rules { operation { permissions = [...] } conditions { service = "iap.googleapis.com" expression = "..." } } }(singularoperation, pluralconditions;titleis not supported), paired with pluralgoogle_iam_{organizations,folders,projects}_policy_binding.
Key Properties:
effect: EitherALLOWorDENY(anAccessPolicymust contain at least one rule).principals: SPIFFE agent identity strings (principal://...) or principal sets (principalSet://...). Never passurn:agent:...catalog URNs here.operation.permissions: Fully Qualified Domain Name (FQDN) format:iap.googleapis.com/resources.egressViaIAP(Agent-to-Anywhere egress).conditions["iap.googleapis.com"].expression: Common Expression Language (CEL) boolean predicate.
2.2 PolicyBinding (iam.googleapis.com/v3/.../policyBindings)
A PolicyBinding attaches an AccessPolicy to a specific Resource Manager node. Allow ~30–60 seconds after creating or updating a binding or policy for IAM control-plane propagation.
{
"displayName": "Bind Platform Egress to Engineering Folder",
"target": {
"resource": "//cloudresourcemanager.googleapis.com/folders/123456789012"
},
"policy": "folders/123456789012/locations/global/accessPolicies/platform-egress-policy",
"policyKind": "ACCESS"
}gcloud iam policy-bindings create bind-platform-egress \
--location=global \
--project="${PROJECT_ID}" \
--policy-kind=access \
--policy="projects/${PROJECT_ID}/locations/global/accessPolicies/platform-egress-policy" \
--target-resource="//cloudresourcemanager.googleapis.com/projects/${PROJECT_NUMBER}"target.resource(--target-resource): Canonical Resource Manager URI:Org:
//cloudresourcemanager.googleapis.com/organizations/${ORG_ID}Folder:
//cloudresourcemanager.googleapis.com/folders/${FOLDER_ID}Project:
//cloudresourcemanager.googleapis.com/projects/${PROJECT_NUMBER}(numeric project number).
policyKind(--policy-kind):ACCESSin JSON/REST (accessingcloud).
3. Runtime Evaluation Pipeline & Mathematical Invariants
+-----------------------------------------------------------------------------------+
| 1. Workload Egress |
| Agent container issues outbound request (e.g. HTTP GET /api/v2/tickets.json) |
| Injected proxy environment directs traffic to Envoy / Agent Gateway. |
+------------------------------------------┬----------------------------------------+
│
▼
+-----------------------------------------------------------------------------------+
| 2. PEP Interception & Gatekeeper Resolution |
| - IAP Authz Extension intercepts the request (Envoy ext_authz). |
| - Gatekeeper verifies caller's SPIFFE/OIDC Agent Identity token. |
| - Gatekeeper queries Agent Registry: |
| * If target matches registry: sets destination.is_registered = true |
| * If target not cataloged: sets destination.is_registered = false |
| - Populates request attributes into the destination CEL context. |
+------------------------------------------┬----------------------------------------+
│
▼
+-----------------------------------------------------------------------------------+
| 3. PDP Traversal: IAM CheckPolicy() |
| IAM traverses the CRM hierarchy top-down or bottom-up: |
| Organization (organizations/112233445566) |
| │ |
| ▼ |
| Folder (folders/123456789012) |
| │ |
| ▼ |
| Project (projects/9876543210) |
| - Evaluates all active PolicyBindings attached to all nodes in the path. |
+------------------------------------------┬----------------------------------------+
│
▼
+-----------------------------------------------------------------------------------+
| 4. Evaluation Logic Engine |
| [Step A: Check for Absolute DENY] |
| Did ANY matching rule across Org, Folder, or Project evaluate to DENY? |
| ├── YES ──► REJECT IMMEDIATELY (HTTP 403 Forbidden / PERMISSION_DENIED) |
| └── NO |
| │ |
| ▼ |
| [Step B: Aggregate Additive ALLOWs] |
| ALLOW_effective = ALLOW_project ∪ ALLOW_folder ∪ ALLOW_org |
| Did AT LEAST ONE rule match with effect: ALLOW? |
| ├── YES ──► PERMIT REQUEST (Forward to backend target) |
| └── NO ──► DEFAULT-DENY (HTTP 403 Forbidden) |
+-----------------------------------------------------------------------------------+The Two Mathematical Invariants of UAP
Absolute DENY Precedence:
Decision = DENYif∃ r ∈ (Rules_org ∪ Rules_folder ∪ Rules_project)such thatMatch(r) ∧ r.effect == DENY. There are no deny exceptions in UAP. If an Org-level rule denies a destination, no project developer or platform admin can override or bypass that denial.Additive ALLOW Aggregation:
ALLOW_effective = ALLOW_org ∪ ALLOW_folder ∪ ALLOW_project. Permissions granted at higher levels cannot be narrowed, scoped down, or revoked by lower tiers. If a Platform Admin allows a domain at the Folder level, an individual Project Developer cannot block an agent from accessing it unless an explicitDENYrule is added.
4. Deep CEL Attribute Reference
UAP evaluates CEL expressions within the iap.googleapis.com namespace rooted
at the destination object.
destination
│
┌─────────────────────────┴─────────────────────────┐
▼ ▼
is_registered == true is_registered == false
│ │
agent_registry unregistered
├── resource_type ├── host
├── location ├── path
├── project_id └── method
├── endpoint.name
├── mcp_server.name
├── mcp_server.method
├── mcp_server.tool.name
├── mcp_server.tool.annotations.read_only_hint
├── mcp_server.tool.annotations.destructive_hint
├── mcp_server.tool.annotations.idempotent_hint
├── mcp_server.tool.annotations.open_world_hint
├── mcp_server.prompt.name
├── mcp_server.resource.name
└── agent.name4.1 Registered Destinations (destination.is_registered == true)
Populated when the target is cataloged in the Gemini Enterprise Agent Registry.
Important: String functions (
.startsWith(),.endsWith(),.contains()) are NOT supported on anydestination.agent_registry.*attribute, and regex.matches()is NOT supported on any UAP CEL attribute. Registered string attributes support only==,!=, andin.
| Attribute | Type | Supported Operators | Purpose / Example |
|---|---|---|---|
destination.is_registered | bool | ==, != | Root guard. Must be verified before accessing sub-attributes. |
destination.agent_registry.resource_type | string | ==, !=, in | Resource type: 'ENDPOINT', 'MCP_SERVER', 'AGENT', 'SKILL'. |
destination.agent_registry.location | string | ==, !=, in | Region of the registry resource ('global', 'us-central1'). |
destination.agent_registry.project_id | string | ==, !=, in | Host project ID of the registry entry ('prod-agent-hub'). |
destination.agent_registry.endpoint.name | string | ==, !=, in | Full resource name: 'projects/{p}/locations/{l}/endpoints/{name}'. |
destination.agent_registry.mcp_server.name | string | ==, !=, in | Full resource name: 'projects/{p}/locations/{l}/mcpServers/{name}'. |
destination.agent_registry.mcp_server.method | string | ==, !=, in | MCP protocol method family: 'tools', 'prompts', 'resources'. |
destination.agent_registry.mcp_server.tool.name | string | ==, !=, in | Specific tool name being executed (e.g., 'updateTicket'). |
destination.agent_registry.mcp_server.tool.annotations.read_only_hint | bool | ==, != | true if tool is declared read-only by tool author. |
destination.agent_registry.mcp_server.tool.annotations.destructive_hint | bool | ==, != | true if tool performs irreversible mutations. |
destination.agent_registry.mcp_server.tool.annotations.idempotent_hint | bool | ==, != | true if repeated identical calls have no additional effect. |
destination.agent_registry.mcp_server.tool.annotations.open_world_hint | bool | ==, != | true if tool interacts with external open-world entities. |
destination.agent_registry.mcp_server.prompt.name | string | ==, !=, in | MCP prompt name (must guard with mcp_server.method == 'prompts'). |
destination.agent_registry.mcp_server.resource.name | string | ==, !=, in | MCP resource name/URI (must guard with mcp_server.method == 'resources'). |
destination.agent_registry.agent.name | string | ==, !=, in | Full resource name of target agent for Agent-to-Agent egress. |
Warning: Null Dereference Guard Pattern: When an agent calls a non-tool MCP method (such as
prompts/listorresources/read), thetoolmessage insidemcp_serverisnull(and similarlypromptorresourceisnullontools/*calls). Dereferencing.tool.*,.prompt.name, or.resource.namewithout checkingmcp_server.methodfirst crashes Gatekeeper with a CEL evaluation error.Mandatory pattern:
(destination.agent_registry.mcp_server.method == 'tools') && (destination.agent_registry.mcp_server.tool.annotations.read_only_hint == true)
4.2 Unregistered Destinations (destination.is_registered == false)
Populated when an agent calls an external IP, public URL, SaaS endpoint, or uncataloged GCP service.
| Attribute | Type | Supported Operators | Purpose / Example |
|---|---|---|---|
destination.unregistered.host | string | ==, !=, in, .startsWith(), .endsWith(), .contains() | Target host FQDN: 'acme.zendesk.com', 'api.github.com'. (No .matches() regex.) |
destination.unregistered.path | string | ==, !=, in, .startsWith(), .endsWith(), .contains() | Request URI path: '/api/v2/tickets.json'. |
destination.unregistered.method | string | ==, !=, in | HTTP verb: 'GET', 'POST', 'PUT', 'DELETE'. |
Important: Tool Annotations Unavailable on Unregistered Endpoints: Unregistered endpoints do NOT possess semantic annotations (
read_only_hint,destructive_hint). The proxy only sees raw L7 wire data (host, path, method). Restricting read-only behavior on unregistered endpoints must be enforced via HTTP methods (destination.unregistered.method == 'GET').
4.3 Cloud Console Parenthesization Rule
To ensure the Google Cloud Console UI parses and renders UAP rules as standard interactive resources (Endpoint, MCP Server, Agent) rather than raw CEL text fallbacks, each CEL clause MUST be wrapped in parentheses:
(destination.agent_registry.endpoint.name == 'projects/123/locations/us-central1/endpoints/my-ep') && (destination.agent_registry.location == 'us-central1')4.4 Principal Identity Grammar
// 1. Single Agent Runtime Agent (Organization-backed project):
principal://agents.global.org-${ORG_ID}.system.id.goog/resources/aiplatform/projects/${PROJECT_NUMBER}/locations/${LOCATION}/reasoningEngines/${ENGINE_ID}
// 2. Single Agent Runtime Agent (Orgless project — uses proj-${PROJECT_NUMBER}):
principal://agents.global.proj-${PROJECT_NUMBER}.system.id.goog/resources/aiplatform/projects/${PROJECT_NUMBER}/locations/${LOCATION}/reasoningEngines/${ENGINE_ID}
// 3. Single Gemini Enterprise App Agent:
principal://agents.global.org-${ORG_ID}.system.id.goog/resources/discoveryengine/projects/${PROJECT_NUMBER}/locations/${LOCATION}/collections/default_collection/engines/${APP_ID}/assistants/default_assistant/agents/${AGENT_ID}
// 4. All Agents across the entire Organization:
principalSet://agents.global.org-${ORG_ID}.system.id.goog/*
// 5. All Agent Runtime (aiplatform) Agents in the Organization:
principalSet://agents.global.org-${ORG_ID}.system.id.goog/attribute.platform/aiplatform
// 6. All Agents scoped to a specific consumer Project:
principalSet://agents.global.org-${ORG_ID}.system.id.goog/attribute.platformContainer/aiplatform/projects/${PROJECT_NUMBER}Note: Never use
urn:agent:...catalog URNs in IAM UAPprincipals— URN strings are used only in Agent Registry metadata andExtensionBinding.matchConditions.
5. The 3-Tier Organizational Security Model
=================================================================================
TIER 1: SECURITY ADMIN (ORGANIZATION SCOPE)
Scope: organizations/${ORG_ID}
Goal: Enterprise Perimeter Guardrails & Telemetry Survival
---------------------------------------------------------------------------------
Rules:
[ALLOW] Essential Container Telemetry (telemetry.mtls.googleapis.com)
[DENY] Unapproved Commercial LLMs (api.openai.com, api.anthropic.com)
=================================================================================
│
▼
=================================================================================
TIER 2: PLATFORM ADMIN (FOLDER SCOPE)
Scope: folders/${FOLDER_ID}
Goal: Shared Corporate Defaults & Developer Autonomy Baselines
---------------------------------------------------------------------------------
Rules:
[ALLOW] Registered Google Cloud APIs (.../endpoints/googleapis)
[ALLOW] Corporate Intranet Apex & Subdomains (*.corp.example.com)
[ALLOW] Central Read-Only MCP Tools (read_only_hint == true)
=================================================================================
│
▼
=================================================================================
TIER 3: AGENT DEVELOPER (PROJECT SCOPE)
Scope: projects/${PROJECT_NUMBER}
Goal: Granular, Least-Privilege Workload Authorizations
---------------------------------------------------------------------------------
Rules:
[ALLOW] Mutating Tool updateTicket on Registered Jira MCP Server
[ALLOW] Strict Zendesk Ticket API (/api/v2/tickets.json, GET/POST/PUT)
[ALLOW] Scoped GitHub Org Query (/repos/my-org/, GET/POST)
=================================================================================Concrete Terraform Implementation (google-beta provider)
Important: In the
google-betaTerraform provider (iam3),AccessPolicyresources use singular hierarchy nouns (google_iam_{organization,folder,project}_access_policy) withdetails { rules { effect, operation { ... }, conditions { service, expression } } }(titleis not supported inconditions), whereas CRMPolicyBindingresources use plural hierarchy nouns (google_iam_{organizations,folders,projects}_policy_binding) withpolicy_kind = "ACCESS"andtarget { resource = "..." }. Both theAccessPolicyand itsPolicyBindingare required for enforcement (andconstraints/iam.managed.disableAccessPolicyBindingmust not be enforced on the target hierarchy node).
# ------------------------------------------------------------------------------
# TIER 1: Organization Guardrails + Organization PolicyBinding
# ------------------------------------------------------------------------------
resource "google_iam_organization_access_policy" "org_guardrails" {
provider = google-beta
organization = var.org_id
location = "global"
access_policy_id = "org-security-guardrails"
display_name = "Organization Security Guardrails"
details {
rules {
description = "ALLOW: Essential telemetry for Reasoning Engine boot"
effect = "ALLOW"
principals = ["principalSet://agents.global.org-${var.org_id}.system.id.goog/attribute.platform/aiplatform"]
operation {
permissions = ["iap.googleapis.com/resources.egressViaIAP"]
}
conditions {
service = "iap.googleapis.com"
expression = "(destination.is_registered == false) && (destination.unregistered.host in ['telemetry.mtls.googleapis.com', 'cloudlogging.googleapis.com', 'monitoring.googleapis.com'])"
}
}
rules {
description = "DENY: Prevent data exfiltration to unauthorized commercial LLMs"
effect = "DENY"
principals = ["principalSet://agents.global.org-${var.org_id}.system.id.goog/*"]
operation {
permissions = ["iap.googleapis.com/resources.egressViaIAP"]
}
conditions {
service = "iap.googleapis.com"
expression = "(destination.is_registered == false) && (destination.unregistered.host in ['api.openai.com', 'api.anthropic.com', 'api.cohere.ai'])"
}
}
}
}
resource "time_sleep" "wait_org_access_policy" {
create_duration = "60s"
depends_on = [google_iam_organization_access_policy.org_guardrails]
}
resource "google_iam_organizations_policy_binding" "org_guardrails_binding" {
provider = google-beta
depends_on = [time_sleep.wait_org_access_policy]
organization = var.org_id
location = "global"
policy_binding_id = "org-security-guardrails-binding"
display_name = "Bind Organization Security Guardrails"
policy_kind = "ACCESS"
policy = "organizations/${var.org_id}/locations/global/accessPolicies/${google_iam_organization_access_policy.org_guardrails.access_policy_id}"
target {
resource = "//cloudresourcemanager.googleapis.com/organizations/${var.org_id}"
}
}
# ------------------------------------------------------------------------------
# TIER 2: Folder Platform Defaults + Folder PolicyBinding
# ------------------------------------------------------------------------------
resource "google_iam_folder_access_policy" "folder_defaults" {
provider = google-beta
folder = var.folder_id
location = "global"
access_policy_id = "folder-platform-defaults"
display_name = "Folder Platform Defaults"
details {
rules {
description = "ALLOW: Read-only tools on registered corporate MCP servers"
effect = "ALLOW"
principals = ["principalSet://agents.global.org-${var.org_id}.system.id.goog/attribute.platform/aiplatform"]
operation {
permissions = ["iap.googleapis.com/resources.egressViaIAP"]
}
conditions {
service = "iap.googleapis.com"
expression = "(destination.is_registered == true) && (destination.agent_registry.resource_type == 'MCP_SERVER') && (destination.agent_registry.project_id == 'corp-tools-hub') && (destination.agent_registry.mcp_server.method == 'tools') && (destination.agent_registry.mcp_server.tool.annotations.read_only_hint == true)"
}
}
}
}
resource "time_sleep" "wait_folder_access_policy" {
create_duration = "60s"
depends_on = [google_iam_folder_access_policy.folder_defaults]
}
resource "google_iam_folders_policy_binding" "folder_defaults_binding" {
provider = google-beta
depends_on = [time_sleep.wait_folder_access_policy]
folder = var.folder_id
location = "global"
policy_binding_id = "folder-platform-defaults-binding"
display_name = "Bind Folder Platform Defaults"
policy_kind = "ACCESS"
policy = "folders/${var.folder_id}/locations/global/accessPolicies/${google_iam_folder_access_policy.folder_defaults.access_policy_id}"
target {
resource = "//cloudresourcemanager.googleapis.com/folders/${var.folder_id}"
}
}
# ------------------------------------------------------------------------------
# TIER 3: Project Agent-Specific Workload Policy + Project PolicyBinding
# ------------------------------------------------------------------------------
resource "google_iam_project_access_policy" "project_workload_egress" {
provider = google-beta
project = var.project_id
location = "global"
access_policy_id = "support-bot-egress"
display_name = "Support Bot Workload Egress Policy"
details {
rules {
description = "ALLOW: Specific ticket operations with verb constraints"
effect = "ALLOW"
# var.reasoning_engine_id MUST be the numeric ReasoningEngine resource ID
principals = [
"principal://agents.global.org-${var.org_id}.system.id.goog/resources/aiplatform/projects/${var.project_number}/locations/us-central1/reasoningEngines/${var.reasoning_engine_id}"
]
operation {
permissions = ["iap.googleapis.com/resources.egressViaIAP"]
}
conditions {
service = "iap.googleapis.com"
expression = "(destination.is_registered == false) && (destination.unregistered.host == 'acme.zendesk.com') && ((destination.unregistered.path == '/api/v2/tickets') || (destination.unregistered.path == '/api/v2/tickets.json') || (destination.unregistered.path.startsWith('/api/v2/tickets/')) || (destination.unregistered.path.startsWith('/api/v2/tickets?')) || (destination.unregistered.path.startsWith('/api/v2/tickets.json?'))) && (destination.unregistered.method in ['GET', 'POST', 'PUT'])"
}
}
}
}
resource "time_sleep" "wait_project_access_policy" {
create_duration = "60s"
depends_on = [google_iam_project_access_policy.project_workload_egress]
}
resource "google_iam_projects_policy_binding" "project_workload_egress_binding" {
provider = google-beta
depends_on = [time_sleep.wait_project_access_policy]
project = var.project_id
location = "global"
policy_binding_id = "support-bot-egress-binding"
display_name = "Bind Support Bot Workload Egress Policy"
policy_kind = "ACCESS"
policy = "projects/${var.project_id}/locations/global/accessPolicies/${google_iam_project_access_policy.project_workload_egress.access_policy_id}"
target {
resource = "//cloudresourcemanager.googleapis.com/projects/${var.project_number}"
}
}6. Legacy IAM v1 Reference (Backward Compatibility)
In deployments where the Gateway AuthzExtension does NOT specify
iapPolicyVersion: "V2", IAP operates in legacy V1 mode:
6.1 Assigning roles/iap.egressor
Apply bindings using gcloud iap web set-iam-policy:
# Registry-wide scope (top-level {"bindings": [...]} JSON without a "policy" wrapper)
gcloud iap web set-iam-policy agents-iap-policy.json \
--project=PROJECT_ID \
--resource-type=agent-registry \
--region=REGION
# Specific MCP Server scope (--resource-type=agent-registry is still mandatory; pass catalog UUID)
gcloud iap web set-iam-policy mcp-iap-policy.json \
--project=PROJECT_ID \
--resource-type=agent-registry \
--region=REGION \
--mcp-server=MCP_SERVER_UUID6.2 Inspecting Legacy Policies
gcloud iap web get-iam-policy --project=PROJECT_ID --resource-type=agent-registry --region=REGION7. Observability, Auditing, & Dry-Run Operations
7.1 Gateway AuthzExtension Configuration
In the Agent Gateway's IAP AuthzExtension metadata:
iapPolicyVersion: "V2": Mandatory to activate UAP policy evaluation.iamEnforcementMode: "DRY_RUN": Evaluates complete hierarchy, logs decisions to Cloud Audit Logs, and permits traffic.Omit
iamEnforcementMode(or setiamEnforcementMode: "ENFORCE", never"ENFORCED"): Actively blocks unapproved traffic with HTTP403/ gRPCPERMISSION_DENIED.
7.2 Cloud Audit Log Schema (UAP)
Enable Data Access (DATA_READ) audit logs for iap.googleapis.com to record runtime UAP policy evaluations and denials:
protoPayload.serviceName="iap.googleapis.com"
(protoPayload.authorizationInfo.permission="iap.resources.egressViaIAP" OR protoPayload.metadata.iapPolicyVersion="v2")Example audit log payload:
{
"protoPayload": {
"@type": "type.googleapis.com/google.cloud.audit.AuditLog",
"serviceName": "iap.googleapis.com",
"methodName": "CheckPolicy",
"metadata": {
"iapPolicyVersion": "v2",
"iamEnforcementMode": "DRY_RUN",
"policyEvaluationResults": [
{
"policy": "organizations/112233445566/locations/global/accessPolicies/org-security-guardrails",
"evaluationResult": "ALLOWED"
},
{
"policy": "folders/123456789012/locations/global/accessPolicies/folder-platform-defaults",
"evaluationResult": "ALLOWED"
},
{
"policy": "projects/9876543210/locations/global/accessPolicies/support-bot-egress",
"evaluationResult": "ALLOWED"
}
],
"finalDecision": "ALLOWED"
}
}
}8. Production Edge Cases & Architectural Gotchas
1. The Blanket Unregistered DENY Boot Crash
Trap: An Org Admin configures:
(destination.is_registered == false) // effect: DENYConsequence: All Reasoning Engine containers crash during startup. Reasoning Engines emit container boot telemetry to
telemetry.mtls.googleapis.com, which is an unregistered public endpoint. Because DENY is absolute, the agent fails to initialize.Fix: Rely on the Agent Gateway's built-in default-deny architecture. Never write a generic DENY on
destination.is_registered == false.
2. Sibling Path Prefix Escapes vs. Query Strings
Trap: Restricting an external API with
.startsWith('/api/v2/tickets').Consequence: An attacker or compromised agent can access
/api/v2/tickets_exportor/api/v2/tickets_delete_allbecause both strings start with that prefix.Fix: Enforce strict boundary delimiters and query parameters:
((destination.unregistered.path == '/api/v2/tickets') || (destination.unregistered.path == '/api/v2/tickets.json') || (destination.unregistered.path.startsWith('/api/v2/tickets/')) || (destination.unregistered.path.startsWith('/api/v2/tickets?')) || (destination.unregistered.path.startsWith('/api/v2/tickets.json?')))
3. Confused Deputy via Unanchored project_id Sets
Trap: Allowing
destination.agent_registry.resource_type == 'MCP_SERVER'across an overly broadproject_id in [...]list without anchoring the exactmcp_server.name.Consequence: Any developer who registers a new MCP server inside one of those projects automatically satisfies the policy. (Note also that
mcp_server.namedoes not support.startsWith()or.endsWith()— only==,!=, andin.)Fix: Anchor both
project_idand the exact canonicalmcp_server.name:(destination.agent_registry.project_id == 'corp-tools-hub') && (destination.agent_registry.mcp_server.name == 'projects/corp-tools-hub/locations/us-central1/mcpServers/jira-service')
4. Intranet Subdomain Wildcard Masking
Trap: A Platform Admin allows
*.corp.example.com(unregistered intranet) at the Folder tier, and creates an MCP server namedtools.corp.example.com.Consequence: If a developer forgets to catalog
tools.corp.example.comin Agent Registry, Gatekeeper flagsdestination.is_registered = false. The request falls through to the unregistered intranet ALLOW rule, completely bypassing all tool-level semantic checks (read_only_hint) and granting ambient access!Fix: Host corporate MCP servers on a distinct domain namespace outside the intranet wildcard, or ensure registration is verified before deployment.
5. IAM Policy Binding Destruction Race Condition
Trap: Deleting a
PolicyBindingand itsAccessPolicyin Terraform at the same time.Consequence: IAM Policy Control Plane has a 10–30 second replication delay. Terraform issues
DELETE AccessPolicywhile the binding deletion is still replicating, returning400 FAILED_PRECONDITION: Access policy is referenced by active policy binding(s).Fix: Destroy provisioners must poll GET the binding URL until HTTP 404 is confirmed before deleting the AccessPolicy.
6. Organization Policy Constraint Blocker
Trap:
gcloud iam policy-bindings createfails withCUSTOM_ORG_POLICY_VIOLATION.Root Cause: Many Google Cloud organizations enforce
constraints/iam.managed.disableAccessPolicyBindingby default. (Beware: some documentation pages misspell this in the plural asdisableAccessPolicyBindings, whichgcloud org-policiesrejects.)Fix: Apply an Org Policy V2 override setting
enforce: falseat the target CRM level, then wait ~30–60 seconds for propagation before retryingpolicy-bindings create:gcloud org-policies set-policy --project=${PROJECT_ID} override.yaml # override.yaml: # name: projects/${PROJECT_ID}/policies/iam.managed.disableAccessPolicyBinding # spec: # rules: # - enforce: false
7. UAP Control-Plane & Console Error Reference
| Error Code / Symptom | Root Cause | Remediation |
|---|---|---|
POLICY_NOT_FOUND (HTTP 404) | Policy ID missing in --location=global or wrong parent project/folder/org. | Run gcloud iam access-policies list --project=${PROJECT_ID} --location=global to confirm canonical policy name. |
TOO_MANY_ACCESS_POLICY_BINDINGS_PER_TARGET | Target CRM node exceeded per-target policy binding quota. | Consolidate rules into a single AccessPolicy and delete redundant bindings on that target. |
TOO_MANY_ACCESS_POLICY_BINDINGS_TO_POLICY | Single AccessPolicy bound to too many separate CRM targets. | Bind once at a higher Folder/Org node and discriminate workloads/destinations via principals and CEL conditions. |
TOO_MANY_CONDITIONS | Rule exceeded Fine-Grained Access Control (FGAC) CEL condition complexity. | Combine host/method checks using in [...] lists or split logic across multiple rules inside the same AccessPolicy. |
HTTP 412 Precondition Failed / ABORTED | Concurrent edit invalidated the AccessPolicy etag. | Re-run gcloud iam access-policies describe (GET) to obtain the latest etag before update / PATCH. |
| Cannot delete the last rule in a policy | An AccessPolicy must contain at least one rule. | Delete all referencing PolicyBinding resources first (poll GET until HTTP 404), then delete the AccessPolicy. |
9. Quick Command Cheat Sheet
UAP (Policy V2) Commands
# List all policy bindings in a project (parent scope — note: `list` has NO --target/--target-resource flag)
gcloud iam policy-bindings list --project="${PROJECT_ID}" --location=global
# Search policy bindings attached to a specific CRM target resource URI
gcloud iam policy-bindings search-target-policy-bindings \
--target="//cloudresourcemanager.googleapis.com/projects/${PROJECT_NUMBER}" \
--project="${PROJECT_ID}" \
--location=global
# Describe an Access Policy
gcloud iam access-policies describe "${POLICY_ID}" \
--project="${PROJECT_ID}" \
--location=global
# Tail UAP Audit Logs in Cloud Logging (Data Access + Policy Evaluation)
gcloud logging read \
'protoPayload.serviceName="iap.googleapis.com" AND (protoPayload.authorizationInfo.permission="iap.resources.egressViaIAP" OR protoPayload.metadata.iapPolicyVersion="v2")' \
--project="${PROJECT_ID}" --limit=20 --order=descLegacy IAM v1 Commands
# Get IAM policy on Agent Registry
gcloud iap web get-iam-policy \
--project="${PROJECT_ID}" \
--resource-type=agent-registry \
--region="${LOCATION}"
# Tail Legacy IAP Audit Logs
gcloud logging read \
'protoPayload.serviceName="iap.googleapis.com"' \
--project="${PROJECT_ID}" --limit=20 --order=desc10. Debugging Flowchart
┌─────────────────────────────┐
│ Agent request denied (403) │
└──────────────┬──────────────┘
│
▼
┌─────────────────────────────┐
│ Check IAP Audit Logs: │
│ iapPolicyVersion == "v2"? │
└───────┬─────────────┬───────┘
yes │ │ no (legacy v1)
│ ▼
│ ┌─────────────────────────────┐
│ │ Check roles/iap.egressor │
│ │ on Agent Registry entry │
│ └─────────────────────────────┘
▼
┌─────────────────────────────────────────────────────────────┐
│ Check policyEvaluationResults across Org, Folder, Project │
└──────────────────────────────┬──────────────────────────────┘
│
┌──────────────┴──────────────┐
▼ ▼
Any rule evaluated to DENY? No rules matched ALLOW?
│ │
▼ ▼
Identify denying rule Verify destination.is_registered
(Check for blanket DENY and check CEL conditions
or denylisted domain) (Check null guard on .tool.*)11. Network Security AuthzPolicy: L7 HTTP/MCP Rules, L4 networkRules, & DENY_BY_DEFAULT
AuthzPolicy (networksecurity.googleapis.com) binds authorization rules and
delegated extensions directly to agentGateways/* resources.
11.1 Policy Evaluation Order & Mandatory Target Invariant
When multiple AuthzPolicy resources target the same Agent Gateway in
REQUEST_AUTHZ, Envoy evaluates them in strict priority order:
CUSTOM(delegatedAuthzExtension, such as IAP or customext_proc)DENY(explicit L7httpRulesor L4networkRulesdeny)ALLOW(explicit L7httpRulesor L4networkRulesallow)DENY_BY_DEFAULT(flips fallback behavior from default-allow to default-deny; cannot containhttpRulesornetworkRules)Fallback default-allow (only if no
DENY_BY_DEFAULTpolicy is attached; note Google-managed egress Agent Gateways already enforce registry/IAP default-deny).
Warning: Never Set
target.loadBalancingSchemeon Agent Gateways: Whentarget.resourcesreferencesprojects/*/locations/*/agentGateways/*, omittarget.loadBalancingScheme(or leave itLOAD_BALANCING_SCHEME_UNSPECIFIED). SettingloadBalancingScheme: INTERNAL_MANAGEDfails validation withINVALID_ARGUMENT: Unsupported load balancing scheme: INTERNAL_MANAGED.
11.2 Native MCP Attribute Rules (baseProtocolMethodsOption)
In GA (gcloud network-security authz-policies import), httpRules[].to.operations[].mcp
matches MCP JSON-RPC calls without external extensions.
Critical Default Trap (
SKIP_BASE_PROTOCOL_METHODS): By default,baseProtocolMethodsOptionisSKIP_BASE_PROTOCOL_METHODS(which skips matchinginitialize,completion/*,logging/*,notifications/*, andping). In aDENYpolicy this is safe, but in anALLOWpolicy coupled withDENY_BY_DEFAULT, leaving the default blocks the MCPinitializehandshake with HTTP403!Mandatory Fix for
ALLOWPolicies: Explicitly setbaseProtocolMethodsOption: MATCH_BASE_PROTOCOL_METHODS:
name: allow-readonly-mcp-tools
target:
resources:
- "projects/${PROJECT_ID}/locations/${LOCATION}/agentGateways/${GATEWAY_NAME}"
policyProfile: REQUEST_AUTHZ
action: ALLOW
httpRules:
- to:
operations:
- mcp:
baseProtocolMethodsOption: MATCH_BASE_PROTOCOL_METHODS
methods:
- name: "tools/list"
- name: "tools/call"
params:
- exact: "get_ticket"gcloud network-security authz-policies import allow-readonly-mcp-tools \
--source=allow-readonly-mcp-tools.yaml \
--location="${LOCATION}" \
--project="${PROJECT_ID}"11.3 L4 networkRules (SNI Passthrough & Explicit HTTPS Proxy Support)
When an Agent Runtime workload routes outbound traffic through an explicit
corporate HTTPS forward proxy (HTTPS_PROXY=https://proxy.corp.internal:443,
with explicit port :443 and NO_PROXY=.googleapis.com,googleapis.com,169.254.169.254,metadata.google.internal,localhost,127.0.0.1),
the client sends an inner HTTP CONNECT tunnel over TLS to the proxy's SNI.
Without an L4 networkRules policy, Agent Gateway's L7 HTTP connection manager
terminates TLS and rejects nested CONNECT requests with HTTP 404 Not Found.
Attaching an L4 AuthzPolicy with networkRules dispatches matching SNIs to
the gateway's loopback_tls_network_authz TCP/SNI listener:
name: allow-explicit-proxy-sni
target:
resources:
- "projects/${PROJECT_ID}/locations/${LOCATION}/agentGateways/${GATEWAY_NAME}"
policyProfile: REQUEST_AUTHZ
action: ALLOW
networkRules:
- from:
sources:
- ipBlocks:
- prefix: "7.0.0.0"
length: 8
- prefix: "10.20.1.0"
length: 24
to:
operations:
- snis:
- exact: "proxy.corp.internal"# MUST use `gcloud beta` (v1beta1 schema); GA `v1` rejects `networkRules`
gcloud beta network-security authz-policies import allow-explicit-proxy-sni \
--source=allow-explicit-proxy-sni.yaml \
--location="${LOCATION}" \
--project="${PROJECT_ID}"Strict L4 networkRules Schema & Security Trade-offs:
gcloud betaRequired:networkRulesexists only inv1beta1(gcloud beta network-security authz-policies import).snisOnly:networkRules[].to.operations[]supports onlysnis(exact,prefix,suffix,contains,ignoreCase; max 10 SNIs per operation, max 5networkRulesper policy). Do not specifyportsor any HTTP fields (hosts,paths,methods,headerSet,mcp), and never mixhttpRulesandnetworkRulesin the same policy.Two-Plane Source IP Rule (
7.0.0.0/8vs. Consumer PSC-I CIDR):from.sources[].resourcesis disallowed on Agent Gateway targets. Inside Agent Gateway's Envoy (AuthzPolicynetworkRules[].from.sources[].ipBlocks), either omitfromor include both7.0.0.0/8(the internal Agent Runtime → Gateway Envoy source range) and your consumer PSC-I subnet CIDR. Inside your consumer VPC (GCE ingress firewall rules and Squidacl localnet src), allow only your consumer PSC-I Network Attachment subnet CIDR (e.g.,10.20.1.0/24) — packets entering your VPC via PSC-I are sourced from the PSC-I subnet, never7.0.0.0/8.L7 Governance Bypass Trade-off: Traffic matching an L4
networkRulesALLOWrule bypasses L7 TLS termination at Agent Gateway — meaning IAP/UAP, Model Armor, Semantic Governance Policies, and L7httpRulesare NOT evaluated on traffic encapsulated inside that proxy tunnel (your downstream corporate proxy assumes L7 policy responsibility). Also note ACTtlsConfig.trustConfigis not applied to L4-bypassed tunnels; configure proxy CA trust inside the agent container (SSL_CERT_FILE/REQUESTS_CA_BUNDLE).
12. Model Armor Content Security Policies (CONTENT_AUTHZ)
Model Armor attaches to Agent Gateway via a ServiceExtensions AuthzExtension
(service: modelarmor.${LOCATION}.rep.googleapis.com) and a NetworkSecurity
AuthzPolicy with policyProfile: CONTENT_AUTHZ and action: CUSTOM.
12.1 Ingress vs. Egress Capabilities, Limits, & Distinct Service Agents
| Dimension | Client-to-Agent (Ingress) | Agent-to-Anywhere (Egress) |
|---|---|---|
Max CONTENT_AUTHZ Policies | 1 per ingress gateway | 4 per egress gateway |
| Service Agent Executing Callout | AI Platform Reasoning Engine SA:<br>service-${AGENT_PROJECT_NUMBER}@gcp-sa-aiplatform-re.iam.gserviceaccount.com | Service Extensions SA:<br>service-${GATEWAY_PROJECT_NUMBER}@gcp-sa-dep.iam.gserviceaccount.com |
| Required IAM Roles | • roles/modelarmor.calloutUser on agent runtime project<br>• roles/modelarmor.user on Model Armor template project | • roles/modelarmor.calloutUser + roles/serviceusage.serviceUsageConsumer on gateway project<br>• roles/modelarmor.user on Model Armor template project |
| Sanitized Payloads | Only ADK reasoningEngines.streamQuery requests & responses on Agent Runtime | • MCP: tools/call, prompts/get, MCP tool execution errors<br>• A2A v1: SendMessage, AgentCard, GetExtendedAgentCard (JSON-RPC & HTTP+JSON/REST)<br>• OpenAI-compatible LLM: non-streaming Chat Completions, Get chat messages, non-streaming Responses, Embeddings, Legacy Assistants/Threads/Messages, API errors |
| Bypassed Without Sanitization | Non-ADK payloads (e.g., LangChain), query, and ReasoningEngine error responses | • MCP: tools/list, resources/*, notifications/*, Streamable HTTP/SSE, protocol errors<br>• A2A: <v1, SendStreamingMessage, GetTask/ListTasks/CancelTask/SubscribeToTask, gRPC protobuf<br>• OpenAI: streaming SSE responses<br>• Documents/PDFs: unsupported in gateway integration (call Model Armor REST sanitizeUserPrompt directly) |
12.2 Sensitive Data Protection (SDP) Redaction Semantics
In-flight payload redaction requires template enforcement type
INSPECT_AND_BLOCKplus advanced SDP settings with a de-identify template.Single- vs. Multi-Detector Rule: If a payload violates only the SDP detector, Model Armor redacts the sensitive spans in-flight and forwards the request/response. If a payload violates multiple detectors (e.g., SDP + Prompt Injection), Model Armor blocks the entire payload without performing redaction.
Under
INSPECT_ONLY, payloads pass unmodified (redaction applies only to Model Armor's own logs).
12.3 Hard 60-Second Stream Deadline & Runtime Gotchas
Non-Configurable 60s Stream Deadline (
Stream removed): WhileAuthzExtension.timeoutconfigures individual callout RPC deadlines (1s–10s), Envoy enforces a hard 60-second total stream lifetime onCONTENT_AUTHZprocessing. Long-running LLM generations, SSE streams, or outbound HTTP/2 gRPC channels routed through aCONTENT_AUTHZpolicy terminate at 60 seconds with HTTP500/504or gRPCUNKNOWN: Stream removed. Fix: ScopeAuthzPolicy.httpRules(hosts/paths/headers) soCONTENT_AUTHZintercepts only targeted non-streaming tool/model endpoints and excludes long-lived gRPC/SSE streams.Body-Shape Inspection & Streaming Log Volume: Model Armor matches protocol payloads by JSON body structure, not URL path alone. On ADK
streamQueryresponses, seeing multipleCONTENT_MODIFIED/ per-chunk callout entries in gateway logs is expected frame-by-frame inspection behavior.
13. Semantic Governance Policies (SGP)
Semantic Governance Policies evaluate proposed tool calls and model interactions against Natural Language Constraints (NLC) and optional User Intent Alignment using the LLM in the semantic governance policy engine.
13.1 Provisioning Prerequisites & Reserved Subnets
API, Supported Locations, & Regional Endpoint Override: SGP is available in
us-central1,us-east1,us-east4,us-west1,europe-southwest1,europe-west1,europe-west4, andeurope-west8. Enableaiplatform.googleapis.comand pointgcloudat the regional endpoint before running SGP engine/policy commands:gcloud config set api_endpoint_overrides/aiplatform "https://${LOCATION}-aiplatform.googleapis.com/"Vertex AI Service Agent IAM: Grant
service-${PROJECT_NUMBER}@gcp-sa-aiplatform.iam.gserviceaccount.comroles/compute.networkAdminandroles/dns.admin(plusroles/cloudtrace.agentwhen Cloud Trace is enabled) on the project.Reserved CIDR Ranges: The SGP engine reserves
10.0.0.0/24,10.0.1.0/24, and10.0.2.0/24internally. Overlapping subnets in the peered VPC cause engine provisioning or connectivity failures.Engine & Policy CLI: Manage via
gcloud beta ai semantic-governance-policy-engine {update,describe,deprovision}andgcloud beta ai semantic-governance-policies {create,update,list,describe,delete}.
13.2 Data-Plane AuthzExtension Metadata & Non-Streaming CEL when Filter
Mandatory Non-Streaming
whenCEL Filter: SGP supports non-streaming JSON LLM calls across Gemini (:generateContent), Vertex AI (:rawPredict), Anthropic Messages (/v1/messages), and OpenAI Chat Completions (/chat/completions). The policy engine does not support streamed model responses (:streamGenerateContent,:streamRawPredict) — streamed calls return no verdict or an error (and produce zero evaluation entries inlogs/semantic-governance-policy), so agents must invoke models without streaming. Excludingapplication/grpcis also mandatory so internal gRPC bootstrap calls (e.g., Cloud Resource Manager) do not crash agent startup. Because Envoy populatesrequest.pathincluding query parameters (e.g.,?alt=json), always use.contains()rather than.endsWith():
name: sgp-content-authz-policy
target:
resources:
- "projects/${PROJECT_ID}/locations/${LOCATION}/agentGateways/${GATEWAY_NAME}"
policyProfile: CONTENT_AUTHZ
action: CUSTOM
httpRules:
- when: "!request.headers['content-type'].startsWith('application/grpc') && (request.path.contains(':generateContent') || request.path.contains(':rawPredict') || request.path.contains('/v1/messages') || request.path.contains('/chat/completions'))"
customProvider:
authzExtension:
resources:
- "projects/${PROJECT_ID}/locations/${LOCATION}/authzExtensions/${SGP_EXTENSION_NAME}"AuthzExtension.metadataKeys & Wholesale Map Overwrite Trap:sgpEnforcementMode: "DRY_RUN"(audit-only; omit key to enforce blocking).sgpUserIntentMatching: "ENABLED"or"DISABLED"(logssgpUserIntentMatchingModeasCONSTRAINTS_AND_INTENT,CONSTRAINTS_ONLY, orINTENT_ONLY).Trap: Updating
AuthzExtensionviagcloud service-extensions authz-extensions importor RESTPATCH ...?updateMask=metadatareplaces the entiremetadatamap. UpdatingsgpUserIntentMatchingwithout repeatingsgpEnforcementMode: "DRY_RUN"immediately flips the gateway into active enforcement!
13.3 SGP Cloud Logging, Runtime Failure Modes, & Control-Plane Error Catalog
Query SGP data-plane evaluation logs in Cloud Logging:
logName="projects/${PROJECT_ID}/logs/semantic-governance-policy"Runtime Log Fields & Failure Symptoms: Inspect
jsonPayload.verdict,jsonPayload.evaluations[].rationale,jsonPayload.sgpUserIntentMatchingMode, andjsonPayload.token_usage. Iflogs/semantic-governance-policyhas zero entries or streamed LLM calls fail, verify the agent invokes models without streaming (:generateContentrather than:streamGenerateContent) and thathttpRules[].whenuses.contains(). If agent sessions fail withFAILED_PRECONDITION, verifydnsPeeringConfigon your ACT resolves the engine's privatednsRecord(SGP_DNS_HOSTNAME).Control-Plane Error Catalog (
aiplatform.googleapis.comErrorInfo.reason): Returned when runninggcloud beta ai semantic-governance-policiesorsemantic-governance-policy-engine:
Control-Plane Error Code (Status) | Root Cause & Action |
|---|---|
SEMANTIC_GOVERNANCE_POLICY_ALREADY_EXISTS (ALREADY_EXISTS) | Policy ID already exists in project/location → choose a unique ID or use semantic-governance-policies update. |
SEMANTIC_GOVERNANCE_POLICY_ETAG_MISMATCH (ABORTED) | Concurrent update invalidated etag → run describe to fetch the latest etag and retry update. |
SEMANTIC_GOVERNANCE_POLICY_AGENT_REQUIRED (INVALID_ARGUMENT) | Missing --agent target → supply the full Agent Registry agent resource name. |
SEMANTIC_GOVERNANCE_POLICY_CONSTRAINT_REQUIRED (INVALID_ARGUMENT) | Missing --natural-language-constraint → provide a non-empty natural-language constraint. |
SEMANTIC_GOVERNANCE_POLICY_AGENT_INVALID_NAME (INVALID_ARGUMENT) | Malformed --agent path → use projects/PROJECT_ID/locations/LOCATION/agents/agentregistry-.... |
SEMANTIC_GOVERNANCE_POLICY_AGENT_UNSUPPORTED_REGION (INVALID_ARGUMENT) | Agent resource location does not match policy location → register/deploy in the same supported SGP region. |
SEMANTIC_GOVERNANCE_POLICY_AGENT_NOT_FOUND (FAILED_PRECONDITION) | Target agent not found in Agent Registry → verify with gcloud agent-registry agents list --location=${LOCATION}. |
SEMANTIC_GOVERNANCE_POLICY_AGENT_NOT_CONFIGURED (FAILED_PRECONDITION) | Agent was not created with both AGENT_IDENTITY (agents.global.*) and agent_gateway_config → redeploy a new Reasoning Engine with both set at creation time. |
SEMANTIC_GOVERNANCE_POLICY_MCP_SERVER_NOT_FOUND (FAILED_PRECONDITION) | Referenced MCP server missing in Agent Registry → verify with gcloud agent-registry mcp-servers list --location=${LOCATION}. |
SEMANTIC_GOVERNANCE_POLICY_MCP_SERVER_INVALID_NAME (INVALID_ARGUMENT) | Malformed MCP server path → use projects/PROJECT_ID/locations/LOCATION/mcpServers/agentregistry-.... |
SEMANTIC_GOVERNANCE_POLICY_MCP_SERVER_UNSUPPORTED_REGION (INVALID_ARGUMENT) | MCP server location does not match policy location (or is in an unsupported region) → align locations. |
SEMANTIC_GOVERNANCE_POLICY_REGISTRY_DENIED (PERMISSION_DENIED) | Caller lacks Agent Registry read access or API is disabled → enable agentregistry.googleapis.com and grant roles/agentregistry.viewer. |
SEMANTIC_GOVERNANCE_POLICY_AGENT_REGISTRY_INVALID_NAME (INVALID_ARGUMENT) | Malformed Agent Registry resource identifier in policy target/tool scope → verify exact name from agent-registry {agents,mcp-servers} describe. |
SEMANTIC_GOVERNANCE_POLICY_ENGINE_NOT_ENABLED_IN_REGION (FAILED_PRECONDITION) | SGP engine not provisioned or not yet ACTIVE in ${LOCATION} → run gcloud beta ai semantic-governance-policy-engine update --location=${LOCATION} and poll describe until state: ACTIVE. |
SEMANTIC_GOVERNANCE_POLICY_ENGINE_SERVICE_NOT_AVAILABLE_IN_LOCATION (FAILED_PRECONDITION) | SGP is unavailable in the requested location → deploy in a supported SGP region (us-central1, us-east1, us-east4, us-west1, europe-southwest1, europe-west1, europe-west4, europe-west8) with api_endpoint_overrides/aiplatform set. |
14. Extensible Policies: ExtensionBinding & ProducerExtension (Preview)
ExtensionBinding (gcloud beta service-extensions extension-bindings import)
provides a streamlined consumer attachment model replacing manual
AuthzExtension + AuthzPolicy pairs:
1P Built-in Extensions: Reference Google-managed endpoints directly via
producerExtension: "iap.googleapis.com"orproducerExtension: "modelarmor.us.rep.googleapis.com"(passing templates inproducerMetadata.model_armor_settings) without creating aProducerExtension.2P/3P Custom
ext_procExtensions: Producers host an Envoyext_procgRPC service (e.g., Cloud Run with HTTP/2 + plaintext gRPC + unauthenticated ingress → Serverless NEG → Regional Internal ALB → PSC Service Attachment) and publish aProducerExtensionusing the separate--extension-settings-*flags:gcloud beta service-extensions producer-extensions create PRODUCER_EXTENSION_NAME \ --location="${LOCATION}" \ --project="${PRODUCER_PROJECT}" \ --phase=authz \ --extension-settings-service="projects/${PRODUCER_PROJECT}/regions/${LOCATION}/serviceAttachments/${PSC_ATTACHMENT_NAME}" \ --extension-settings-supported-events=request-headers,request-body,response-headers,response-body \ --extension-settings-authority="ext.producer.example.com"Consumers then bind
producerExtension: "projects/${PRODUCER_PROJECT}/locations/${LOCATION}/producerExtensions/PRODUCER_EXTENSION_NAME"after PSC project allowlisting.Match Conditions & Metadata Context:
matchConditionsfilter onto.destination.resources(prefix: "urn:mcp",prefix: "urn:agent") orto.destination.hosts. Customext_procservers receive gateway/registry context inProcessingRequest.metadata_contextundercom.google.serviceextensions.invocation_info(binding_selector.{target, project_number, to_resource}).Double-Invocation Gotcha:
ExtensionBindingcan attach at either the gateway resource level (target.resources) or project scope (target.scope.parent: "projects/${PROJECT_NUMBER}",resourceTypes: "AGENT_GATEWAY"). Attaching the same extension via both a project-scopeExtensionBindingand a gateway-levelExtensionBinding(or combining a project-scopeExtensionBindingwith a gateway-boundAuthzPolicy) executes the extension twice on every matching request (see BKI #39).
15. Token Management Policies (Preview — HTTP 429)
Token management policies enforce shared LLM token budgets (input, output,
and cached tokens) across Gemini, Anthropic, and OpenAI-compatible models on
self-managed Secure Web Proxy Agent Gateway deployments (for GKE and Cloud Run
agents).
Strict Dependency & Teardown Order (max 1 of each per project target scope):
Create
TokenAggregationPolicyfirst (up to 10 first-matchrulesgrouping traffic into atokenGroup; unmatched traffic logs asunmatched).Create
TokenBudgetPolicysecond (up to 10TokenPoolentries withmaxTokens,HOURLYorDAILYmidnight PT reset,alertThresholde.g.0.8; non-targeted models log poolundefined).Create
CostPerTokenPolicythird (optional custom USD rates or discount forgcp.token_management.token.estimated_cost).
Teardown: Delete in strict reverse order:
CostPerTokenPolicy→TokenBudgetPolicy→TokenAggregationPolicy.
Diagnosing Unexpected HTTP
429 Too Many Requestsvs. Quota Overshoot:Pre-flight block (
429): When a pool's reconciled consumption reachesmaxTokensand strict enforcement is active, pre-flight checks reject new LLM requests immediately with HTTP429. Distinguish gateway token-pool429from upstream Vertex AI/model quota429by checkingtoken_management_alert_logsin Cloud Logging andgen_ai.client.token.usagein Cloud Monitoring.Concurrent overshoot: Because output tokens are reconciled after an LLM response completes, concurrent in-flight requests admitted just below
maxTokenscan temporarily exceed the budget before subsequent requests are throttled. Validate new budgets withvalidateOnly: trueandalertThreshold: 0.6–0.8before enabling strict blocking.
Requirements and compatibility
Works with: Claude Code, Claude apps, OpenAI Codex, GitHub Copilot, Cursor, Gemini CLI, Kiro, OpenCode, Goose, Amp, Roo Code, JetBrains Junie, Factory Droid, Devin (ex-Windsurf), Mistral Vibe, OpenHands, Letta
“Works with” means the tool reads the open Agent Skills format; a skill written for one tool may use features others lack.
Needs: Needs an MCP server
Security
No issues found by our static scan· No issues found
A static scan is not a guarantee. Skills are third-party content: review a skill before you let an agent use it.
Attribution and licence
- Published by
- Publisher
- Google (Official)
- Source
- google/skills · skills/cloud/agent-platform-troubleshooting · commit 2194d3b
- Licence
- Apache License 2.0 (redistribution allowed)
- LICENSE (added by AutoGineer from LICENSE)
- ATTRIBUTION.md (written by AutoGineer)
- Synced
AutoGineer is not affiliated with or endorsed by Google or Google.
Download ZIP
The ZIP holds the skill’s files, its licence files and an ATTRIBUTION.md. Download agent-platform-troubleshooting.zip