How to secure and govern agents on Gemini Enterprise Agent Platform. Topics: who the agent is (Agent Identity), how it authenticates to tools (auth manager, Secret Manager), what it may reach (IAM, Principal Access Boundary), where its traffic is enforced (Agent Gateway + Agent Registry), what content it may see or emit (Model Armor, Sensitive Data Protection, Gemini safety filters), how its network and data are isolated (VPC-SC, PSC, CMEK, residency), how it's audited (Cloud Audit Logs, SCC AI Protection), and when a human must approve (HITL).
Test Yourself: Agent Platform Security Practice Quiz →This guide covers only security and governance. Build/runtime/retrieval/evaluation are in Agent Platform. Vendor-neutral guardrail concepts are in Responsible AI (stakes & HITL, governance lifecycle). ADK-level controls are in ADK callbacks & guardrails, HITL and sandboxing. Protocols are in MCP and A2A. Operations are in AgentOps.
Several controls here are Preview or Experimental (see the status table in §00). Preview features may change and have limited support. Re-check the docs before relying on a status.
| Component | Answers | Product |
|---|---|---|
| Identity | Who is acting? | Agent Identity (SPIFFE-based per-agent principal), or service accounts (legacy/default) |
| Registry | What destinations exist and are approved? | Agent Registry (agents, MCP servers/tools, endpoints, skills) |
| Policies | What actions are allowed? | IAM allow/deny, Principal Access Boundary (PAB), Unified Access Policies on the gateway, Semantic Governance |
| Gateway | Where is it enforced? | Agent Gateway (Client-to-Agent ingress, Agent-to-Anywhere egress) |
Content controls (Model Armor, SDP, Gemini safety filters), network/data controls (VPC-SC, PSC, CMEK, residency), detection (Audit Logs, SCC) and humans (HITL) sit on top of these four as defense in depth.
| Threat / requirement | Primary control | Why not the others |
|---|---|---|
| Shared service-account key leaked; you need per-agent, non-impersonable credentials | Agent Identity | Service accounts are shared by default and can be impersonated |
| Stolen agent access token replayed from another machine | Agent Identity (certificate-bound tokens + default Context-Aware Access: mTLS + DPoP) | IAM roles don't stop replay of a valid token |
| Agent must act on a user's Jira/GitHub data with that user's consent | Auth manager, 3-legged OAuth (user-delegated) | An API key or the agent's own identity would act with the agent's authority, not the user's |
| API key hard-coded in agent source | Secret Manager (secret env var or ADK SecretManagerClient) + secretAccessor for the agent | Env-var plaintext and code are both visible to anyone who can read the deployment |
| Agent can reach any project in the org despite narrow intent | Principal Access Boundary (boundary on eligible resources) | Allow policies only grant; PAB caps eligibility no matter what roles exist |
| A broad role must never include one dangerous permission | IAM deny policy | PAB limits resources, not specific permissions |
Agent may call MCP tool get_order but not delete_order | Unified Access Policy on Agent Gateway (CEL on tool.name / destructive_hint) | IAM on the Google Cloud API can't see MCP tool names |
| Agent calls an unregistered external host | Agent Gateway + Agent Registry (default-deny egress; UAP on destination.unregistered.*) | Model Armor screens content, not destinations |
| An email in context instructs the agent to forward mail to an attacker (indirect injection → rogue tool call) | Semantic Governance (intent alignment) + Model Armor prompt-injection filter | UAP sees an allowed tool name and can't judge intent |
| Refund over a business limit (e.g., >$75) must never auto-execute | Semantic Governance natural-language constraint, or deterministic check + HITL tool confirmation | Model Armor has no business rules |
| Jailbreak / prompt injection / malicious URL in user input | Model Armor (input template) | Gemini safety filters only cover harm categories |
| Every team's Model Armor templates must keep prompt-injection detection on, whatever each team configures | Model Armor floor settings at org or folder level (template conformance) | A template protects only the workload that references it |
| Credit-card numbers leak in responses | Model Armor SDP filter (advanced mode with de-identify template) / Sensitive Data Protection | Safety filters don't detect custom infoTypes |
| Hateful output from the model | Gemini safety filters (model level) and/or Model Armor RAI filter (model-agnostic) | — |
| Data exfiltration from Google APIs to an outside project | VPC Service Controls perimeter | IAM alone can't stop a legitimately authorized identity copying data out |
| Agent must reach on-prem/private VPC services | PSC interface (network attachment) | Agent Runtime can't reach your private VPC/on-prem by default (Google-managed tenant) |
| Regulator requires customer-controlled keys | CMEK (encryption_spec, same-region key) | Google default encryption isn't customer-controlled |
| Data and ML processing must stay in the EU | Data residency: EU multi-region/regional endpoint; block the global endpoint | The global endpoint has no residency guarantee |
| "Who changed the agent's IAM?" | Cloud Audit Logs (Admin Activity, always on) | Data Access logs must be enabled explicitly |
| Detect anomalous grants to agent identities / over-privileged agents | SCC AI Protection + Event Threat Detection | Audit logs record but don't alert |
| Irreversible, high-stakes action needs sign-off | HITL: ADK tool confirmation (require_confirmation) | Automated filters can be wrong; accountability needs a human |
| Hundreds of unknown agents/tools across teams (agent sprawl) | Agent Registry (central catalog) + gateway default-deny + org policy | Ad-hoc spreadsheets aren't enforced |
| Capability | Status | Note |
|---|---|---|
| Agent Identity | GA (since 2026-04-22) | Falls back to service accounts if not enabled |
| Agent Identity auth manager | GA (since 2026-08-22, with the agentidentity / agentidentitycredentials APIs) | Older IAM Connectors API stays Preview and will not reach GA |
| Agent Gateway | GA for Agent Runtime + Gemini Enterprise (GE: egress only) | GKE, Cloud Run, cross-cloud: coming soon / early preview |
| Agent Registry | GA (verify); standalone skills in Registry = Preview | — |
| Skill Registry | Preview | — |
| Semantic Governance policies | Preview | — |
| Token-management policies, extension bindings | Coming soon | — |
| Agent Platform Threat Detection (SCC) | Preview, Premium tier | — |
| ADK tool confirmation (HITL) | Experimental | — |
| Model Armor image screening, template modality selection, exclusion rules | Preview | Text and document screening carry no Preview label |
| Model Armor floor settings for Google Cloud MCP servers | Preview | Labeled Preview on the floor settings page |
| Model Armor spans in Cloud Trace | Preview | Span attribute names may change |
| Agent Gateway Cloud Trace integration | Preview | Gateway request logs and the Observability dashboard carry no Preview label |
| Model Armor insights (Agent Platform Security tab) | Preview | Needs tracing set up for the agent |
| Agent Anomaly Detection | Preview, allowlisted users only | Findings go to SCC (Standard, Premium or Enterprise) |
"Preview" isn't a reason to reject a control in a design question, but you must know which features are not GA. Some security controls (e.g., CMEK/VPC-SC support) aren't available for preview models.
| Option | What it is | Strengths | Weaknesses |
|---|---|---|---|
| Default service agent | AI Platform Reasoning Engine Service Agent service-PROJECT_NUMBER@gcp-sa-aiplatform-re.iam.gserviceaccount.com (roles/aiplatform.reasoningEngineServiceAgent) | Zero setup | Shared by every agent in the project, so no per-agent least privilege |
| Custom service account | You attach your own SA | Per-workload roles possible | Can be shared and impersonated; key-management risk |
| Agent Identity (GA) | Per-agent, SPIFFE-based, strongly attested principal tied to the agent's lifecycle and resource URI | Not shared, can't be impersonated, no long-lived keys; tokens bound to the agent's X.509 cert (24h, auto-rotated); Context-Aware Access enforces mTLS + DPoP by default | GA since 2026-04-22; new principal on every redeploy (see traps) |
| Workload Identity Federation (WIF) | Federates external/other-cloud workloads to Google Cloud without keys | Used by Agent Gateway / auth manager for cross-cloud and external identity exchange | Not an agent-native identity |
Agent Identity is supported on Agent Runtime, Gemini Enterprise, and Cloud Run.
spiffe://agents.global.org-ORG_ID.system.id.goog/resources/aiplatform/projects/PROJECT_NUMBER/locations/LOCATION/reasoningEngines/AGENT_IDprincipal://agents.global.org-ORG_ID.system.id.goog/resources/aiplatform/projects/PROJECT_NUMBER/locations/LOCATION/reasoningEngines/AGENT_IDprincipalSet://agents.global.org-ORG_ID.system.id.goog/attribute.platformContainer/aiplatform/projects/PROJECT_NUMBERprincipalSet://…/attribute.platform/aiplatformapi_version="v1beta1"): client.runtimes.create(config={"identity_type": types.IdentityType.AGENT_IDENTITY, ...}). You can create the identity before deploying code so you can set up IAM first, then add code with client.runtimes.update(...).agents-cli deploy --agent-identity; for adk deploy, set it in .agent_engine_config.json.roles/aiplatform.agentContextEditor, roles/aiplatform.agentDefaultAccess.roles/aiplatform.expressUser, roles/serviceusage.serviceUsageConsumer, roles/browser, roles/secretmanager.secretAccessor.| Trap | Consequence | Fix |
|---|---|---|
| Agent redeployed (new resource ID) | New principal, so old bindings don't apply | After deploy, read spec.effectiveIdentity; put baseline roles on the project principalSet, which survives redeploys |
| Agent deleted | IAM bindings remain (orphaned) | Remove bindings as part of teardown |
| Granting legacy Cloud Storage bucket roles | Not supported for agent identities | Use the IAM predefined roles |
| Parent agent delegates to a subagent (A2A) | Parent needs discovery + invoke rights | Grant the parent's agent identity roles/agentregistry.viewer and roles/aiplatform.user on the subagent; don't grant them to the user |
"Use a service account key stored in the container" is never the answer. Prefer Agent Identity, or an attached SA with no exported keys.
| Model | Agent acts as… | Mechanism | Use when |
|---|---|---|---|
| User-delegated | the end user (with consent) | 3-legged OAuth via the auth manager | Per-user data in SaaS (Jira, GitHub, Salesforce, ServiceNow, Workspace) |
| Agent's own authority | itself | Agent Identity → Google Cloud APIs; OIDC ID tokens → external services (federation); 2-legged OAuth (machine-to-machine); API key; HTTP basic (not recommended) | Shared back-end services, batch jobs, service-to-service calls |
Audit logs record both the agent and the user when the agent acts on a user's behalf, and only the agent when it acts on its own authority.
| Identity | What it represents | Where it comes from | Typical use |
|---|---|---|---|
| End user | The person typing the prompt | Cloud Identity or an external IdP through Workforce Identity Federation | Carries the user's own data entitlements (document sharing, row-level rules) |
| Agent Identity | One deployed agent | Provisioned by Agent Runtime, Gemini Enterprise or Cloud Run; SPIFFE ID with a certificate-bound credential | Calls Google Cloud APIs and the gateway as the agent; is granted roles/agentidentity.user on the auth providers it may use |
| Service account | A workload, not a specific agent | Attached by you, or the default Reasoning Engine service agent | Fallback when Agent Identity is off; also the runtime identity of a self-hosted MCP server (e.g., on Cloud Run) |
| OAuth client in an auth provider | The registered app at a third-party service (Jira, Salesforce, GitHub) | Client ID and secret stored in an auth manager auth provider | The "application" side of 2LO and 3LO flows |
Authorization / X-Goog-Api-Key headers into tool and MCP calls, so the credentials never live in agent code.iamconnectors.googleapis.com, Preview), which will not reach GA. Use the Agent Identity API (agentidentity.googleapis.com; credential calls on agentidentitycredentials.googleapis.com), where resources are named authProviders/ instead of connectors/. Both run side by side during migration.env_vars={"API_KEY": {"secret": "SECRET_ID", "version": "latest"}}. Only same-project secrets can be referenced.roles/secretmanager.secretAccessor to the agent identity. When secrets are injected as env vars at deploy time, also grant it to the Agent Platform service agent.SecretManagerClient fetches secrets at runtime, so a rotated secret takes effect without a redeploy or restart.| Pattern | Who consents | What the auth manager stores | Refresh | Use when |
|---|---|---|---|---|
| 3-legged OAuth (authorization code + user consent) | The end user, once per provider, via a consent page reached from the chat UI; the app supplies a continue_uri to return to | The user's resulting OAuth tokens in a Google-managed vault | Handled by the auth manager | Per-user SaaS data; the downstream service enforces that user's permissions |
| 2-legged OAuth (client credentials) | No user; the agent's identity is authorized to use the provider | The OAuth client ID and secret (Google-managed storage) | Handled by the auth manager | Machine-to-machine calls on the agent's own authority |
| API key | No user | The key | Not applicable; rotate the key | Services without OAuth |
| Token exchange (RFC 8693) | No new consent; an existing credential is swapped for another | Not an auth manager provider type | Short-lived results | Workload Identity Federation: the Security Token Service swaps an external OIDC/SAML or Kubernetes credential for a Google credential. Agent Gateway cross-cloud mode (early preview) uses WIF to swap Kubernetes workload credentials for Agent Identity credentials |
roles/agentidentity.user on it), then injects the returned token as a request header on the tool or MCP call.| Flow | Principal the destination sees | User consent | Confused-deputy resistance | Credential lifecycle |
|---|---|---|---|---|
| Agent's own identity | The deployed agent (principal://agents.global…) | None | Limited: one authority serves every user. Bound tokens stop replay, not misuse, so pair with PAB, UAP and HITL | Google-issued, certificate-bound, rotated automatically |
| 2LO | The OAuth client registered at the provider | None | Low: the same app privilege answers every user's request | Client ID and secret held by the auth manager; access tokens minted and refreshed for you |
| 3LO | The end user | Once per provider; revocable | High: the destination applies that user's own permissions | Per-user tokens vaulted and refreshed by the auth manager |
| API key | Whoever holds the key | None | Lowest: no caller identity at all | Static; kept in the auth manager or Secret Manager; rotate it yourself |
Per-user consent → 3LO through the auth manager. Static third-party key → Secret Manager (or an auth-manager API-key provider). Google Cloud API → the agent's own identity with least-privilege IAM. Never put secrets in code, prompts or plaintext env vars.
Each hop checks a different credential against a different policy. Access to the data succeeds only when every hop allows it.
User sign-in session (Cloud Identity or federated IdP), checked by Gemini Enterprise or the front end: may this user use the app and agent?
Caller identity plus the user's context, checked by Agent Runtime: IAM permission to query the agent (e.g., roles/aiplatform.user) and ingress gateway policies if Client-to-Agent is enabled.
Agent Identity credential (mTLS + DPoP) plus the injected tool credential. REQUEST_AUTHZ (IAP: UAP, PAB) runs first, then CONTENT_AUTHZ (Model Armor, Semantic Governance).
The destination's credential (3LO token, 2LO token, API key or the agent's Google token), checked by destination IAM, e.g., roles/iap.httpsResourceAccessor for an IAP-protected Cloud Run server or roles/mcp.toolUser for Google remote MCP, plus OAuth scopes.
The user's delegated token, the agent's identity, or the MCP server's own service account, checked by source IAM and ACLs (row/column rules, document sharing) and the VPC-SC perimeter.
Example: on first use the auth manager redirects the user to Jira for consent and vaults the token; the agent then sends tools/call with its Agent Identity and that token through the gateway, the MCP server forwards the user token, and Jira returns only that user's records, which Model Armor screens on the way back.
If the MCP server queries the data source with its own broad service account, the user's identity stops at the server and the source can no longer filter by user. Propagate the user token (3LO) to the last hop when results must be user-specific.
| Policy | Effect | Granularity | Answers |
|---|---|---|---|
| IAM allow policy | Grants roles | Resource + role (+ condition) | "What may this principal do?" |
| IAM deny policy | Denies specific permissions; overrides allow | Permission-level | "What must it never do, whatever its roles?" |
| Principal Access Boundary (PAB) | Limits which resources a principal is eligible to access | Org/folder/project (up to 500 resources per policy, effect ALLOW only) | "Where can it ever operate?" |
| Unified Access Policy (gateway) | Allow/deny on agent traffic through Agent Gateway | MCP tool, agent, endpoint, unregistered host/path/method | "Which tools and destinations?" |
Give each agent its own identity (Agent Identity), not the shared default service agent.
Grant specific predefined roles on specific resources. Avoid Owner/Editor. Put baseline roles on the project principalSet; per-agent extras on the agent's principal://.
Add deny policies for permissions that must never be used (e.g., IAM admin). Bind a PAB policy to the agents' principal set so they can't reach resources outside approved projects, even if someone over-grants.
Govern tool-level access at the gateway (UAP) because Google Cloud IAM can't see MCP tool names.
enforcementVersion and blocks only the permissions supported by that version.gcloud iam principal-access-boundary-policies bindings create … --target-principal-set=… (bound to a principal set, e.g., the agents in a project or org).roles/bigquery.admin at the org level to the agents' principalSet. A PAB bound to that set that lists only the analytics-prod project keeps the agents out of every other project's datasets.resource.name.startsWith("projects/_/buckets/agent-reports") or request.time < timestamp("2027-01-01T00:00:00Z") for a time-boxed grant.destination.agent_registry.mcp_server.tool.annotations.read_only_hint == true.| Failure | How it happens | Controls that prevent it |
|---|---|---|
| Confused deputy | The agent holds one strong credential (2LO token or broad service account) and a user's prompt steers it to read or change records that user can't access | 3LO so the source checks the requesting user; UAP deny on destructive_hint == true; Semantic Governance intent check; HITL confirmation for writes |
| Token leakage in prompts, logs or traces | A token or key is placed in the prompt, returned in tool output, or captured in logs and spans, then replayed | Auth manager header injection (credentials never in code or prompts); gateway-side decryption of end-user credentials with Gemini Enterprise; Model Armor SDP filter on prompts and responses; certificate-bound Agent Identity tokens that fail when replayed elsewhere; SCC finding Agentic Identity Credential Used Outside of Google Cloud |
| Over-privileged service account | Every agent in a project runs as the default service agent or one shared SA with roles/editor, so one hijacked agent reaches everything | Per-agent Agent Identity; PAB on the agents' principalSet; deny policies for admin permissions; per-provider roles/agentidentity.user; SCC over-privileged agent detection |
PAB doesn't grant anything. A principal needs an allow-policy role and must be inside its boundary and must not be denied.
https://<service>.googleapis.com/mcp, enabled with the service API and requiring roles/mcp.toolUser / mcp.tools.call plus service IAM roles). Manual registration uses Service resources for remote, custom (e.g., MCP Toolbox for Databases or third-party SaaS connectors on Cloud Run/GKE), or cross-project components (gcloud agent-registry mcp-servers list).urn:agent: identifiers are for inventory, not for IAM bindings).gcp-skill-registry skill. Semantic Governance can constrain which skills an agent loads.| Path | Direction | Typical controls |
|---|---|---|
| Client-to-Agent | Ingress (users/apps → agent) | mTLS termination, IAP access policy, Model Armor screening of ADK streamQuery |
| Agent-to-Anywhere | Egress (agent → tools, agents, LLMs, APIs) | UAPs, PAB, Semantic Governance, Model Armor on MCP tools/call, A2A messages and OpenAI-compatible payloads |
Gemini Enterprise supports only Agent-to-Anywhere (egress); Client-to-Agent is for Agent Runtime. Ingress governs query/streamQuery only. All agents in the same project + region bind to the same egress/ingress gateway. SCC Agent Engine Threat Detection isn't available while a gateway is enabled. VPC-SC works with Agent Gateway when the agent uses VPC connectivity (route all egress through your VPC, restricted VIP).
iap.googleapis.com/resources.egressViaIAP.destination.agent_registry.mcp_server.tool.name, …tool.annotations.read_only_hint / destructive_hint, and destination.unregistered.host, .path, .method.iamEnforcementMode: "DRY_RUN" in the IAP authorization extension metadata so IAP logs would-be denials to Cloud Audit Logs without blocking. Remove the field to start enforcing (see Audit and detection).destructive_hint == true); audit regularly.ALLOW or DENY with a rationale. A custom denial message (≤1,000 chars) can replace the rationale.REQUEST_AUTHZ extensions see only headers (IAP evaluates identity and UAPs). CONTENT_AUTHZ extensions see full request and response bodies (Model Armor, Semantic Governance). A custom engine can use either profile.| Need | Use |
|---|---|
| Hard allow/deny by tool name, host, method | UAP (deterministic) |
| "Does this action match what the user wanted?" | Semantic Governance |
| Business rule expressible in natural language | Semantic Governance (backed by a deterministic check for critical limits) |
| Custom authorization logic | Service Extensions custom authorization engine |
| Risk | Attack | Controls |
|---|---|---|
| Tool poisoning / rug pull | A tool description, or a server update after approval, carries hidden instructions the model follows | Register only vetted servers in Agent Registry; gateway default-deny for unregistered destinations; UAP names each allowed tool. Model Armor on the gateway doesn't screen tools/list, so review tool metadata at registration |
| Over-broad scopes | An auth provider or role grants far more than the tool needs, so any misuse has a large blast radius | Request minimal OAuth scopes per provider; grant roles/agentidentity.user only on the providers each agent needs; UAP allow on read_only_hint; PAB |
| Indirect prompt injection via tool output | A ticket, email or web page returned by a tool contains instructions that steer the next tool call | Model Armor (CONTENT_AUTHZ) screens tools/call responses; Semantic Governance blocks calls that don't match the user's request. Streamable HTTP/SSE streams aren't screened |
| Data exfiltration | The agent is steered to send sensitive results to an attacker-controlled host or tool | Gateway default-deny plus UAP on destination.unregistered.host; Model Armor SDP on outbound arguments; VPC-SC with egress through your VPC |
| Filter | Detects | Notes |
|---|---|---|
| Responsible AI | Hate, harassment, sexually explicit, dangerous (CSAM always on) | Confidence: HIGH, MEDIUM_AND_ABOVE, LOW_AND_ABOVE. The floor settings console defaults to Medium and above when left blank |
| Prompt injection & jailbreak | Attempts to override instructions, in prompts and in responses | Inputs under 3 words return NO_MATCH_FOUND. In a floor setting update that omits the level, it defaults to LOW_AND_ABOVE |
| Sensitive Data Protection | Basic: six fixed infoTypes (credit card, US SSN, financial account, US ITIN, Google Cloud credentials, Google Cloud API key), inspect only, no SDP templates. Advanced: an SDP inspect template plus an optional de-identify template | Redaction needs advanced mode. Only infoTypes named in both templates are de-identified. SDP templates in another project need roles/dlp.user and roles/dlp.reader for the Model Armor service agent. Uses SDP match likelihood, not the confidence enum |
| Malicious URL detection | Phishing/malware links | Only the first 256 URLs in a payload are screened |
| Document screening | Text in PDF, CSV, TXT, Word, PowerPoint and Excel files | Files over 4 MB are skipped; files under 69 bytes are rejected. Direct API or the Gemini Enterprise integration only; other integrations scan text |
| Image screening (Preview) | Text in images (OCR); visual content through the advanced SDP filter | JPEG, PNG, BMP; one image per request, 4 MB max; us and eu multi-regions only |
roles/modelarmor.calloutUser + roles/modelarmor.user for the Reasoning Engine service agent. Egress: the same roles for the Service Extensions service agent.INSPECT_AND_BLOCK. If multiple detectors fire, the whole payload is blocked. Logs are redacted either way.streamQuery. Egress screens A2A SendMessage/AgentCard, MCP tools/call, prompts/get, tool-execution errors, and OpenAI-compatible LLM payloads. It does not screen tools/list, resources/*, notifications or MCP Streamable HTTP/SSE streams.Floor settings solve for: dozens of teams writing their own templates, where one team quietly switching off a detector reopens a hole the security team closed.
| Dimension | Floor settings | Templates |
|---|---|---|
| Purpose | Minimum baseline (template conformance) and, at project level, inline enforcement on Gemini and Google Cloud MCP servers | The filter set and enforcement type for one workload or integration point |
| Who manages | Central security team | Application or platform team |
| IAM role | roles/modelarmor.floorSettingsAdmin (Model Armor Floor Setting Admin) | roles/modelarmor.admin on the project that holds the template |
| Scope and resource | organizations/ORG_ID, folders/FOLDER_ID or projects/PROJECT_ID, always at locations/global/floorSetting | A regional resource, projects/PROJECT_ID/locations/LOCATION/templates/TEMPLATE_ID |
| How it is set | Console at project level only (inherit, custom or disable). Org and folder need the API, gcloud or Terraform google_model_armor_floorsetting | Console, gcloud, REST or client libraries |
| Inheritance | Org applies to everything below; folder to its projects; project to itself. On conflict the lower level wins (project over folder) | Not inherited; referenced explicitly |
| Enforced by | The Model Armor API at template create/update; inline on Gemini generateContent and Google Cloud MCP traffic | Callers of sanitize*, Agent Gateway, Gemini Enterprise, Agent Runtime, Service Extensions, Apigee |
| Enforcement type | Set per integrated service (Agent Platform, Google MCP server) | enforcement_type in template_metadata |
| Effect takes hold | A few minutes after a change | On next request |
"Lower level wins" cuts both ways. A project admin holding roles/modelarmor.floorSettingsAdmin can choose Disable and drop the inherited floor for that project (API flag enableFloorSettingEnforcement=false). Grant the role at org or folder level to the central team only, and restrict it at project level.
Project floor settings can screen traffic with no code change by picking integrated services:
| Integrated service | What is screened | Key behaviors |
|---|---|---|
| Agent Platform (Gemini) | Gemini API generateContent prompts and responses in the project, including calls through the Google Gen AI SDK or Firebase AI Logic | Grant roles/modelarmor.user to service-PROJECT_NUMBER@gcp-sa-aiplatform.iam.gserviceaccount.com. A request can name its own promptTemplateName / responseTemplateName instead. No documents or images. De-identified text is not returned to Gemini; an SDP match under INSPECT_AND_BLOCK becomes a block. Floor settings use the Stable filter version |
| Google MCP server (Preview) | tools/call, prompts/get and tool-execution errors on supported Google and Google Cloud remote MCP servers | Unsupported servers aren't covered. Uses filter version v1, which you can't override. If the agent and the MCP server sit in different projects, each project's floor runs, so Model Armor is called twice |
| Template creation & update | Template conformance only | Rejects weaker templates; no traffic screening |
INSPECT_AND_BLOCK. For guaranteed screening, also screen in the app or at the gateway.gcloud model-armor floorsettings update with --full-uri='projects/PROJECT_ID/locations/global/floorSetting', --enable-floor-setting-enforcement=TRUE, --add-integrated-services=GOOGLE_MCP_SERVER, --google-mcp-server-enforcement-type=INSPECT_ONLY and --enable-google-mcp-server-cloud-logging.| Mode | Detection | Traffic | Use for |
|---|---|---|---|
INSPECT_ONLY | Logged to Cloud Logging | Passes unchanged | Measuring false positives, monitoring new attack patterns, compliance evidence |
INSPECT_AND_BLOCK | Logged | The caller (the enforcement point) blocks the prompt or response; redaction applies where the integration supports it | Production protection |
INSPECT_ONLY produces nothing useful without Cloud Logging.
Separate input and output templates; RAI at HIGH; prompt injection at HIGH or MEDIUM_AND_ABOVE (HIGH for Gemini Enterprise to avoid false positives).
Run known-good and known-bad prompts and record the false-positive baseline.
Check SanitizeOperationLogEntry records; a model refusal in the response body is not a Model Armor block.
Move to INSPECT_AND_BLOCK; lower a threshold only for the one category that is under-protected.
Turning on LOW_AND_ABOVE across all RAI categories on day one. The docs flag it as high false-positive risk for general content; reserve it for categories where a miss costs more than a wrong block.
| Signal | Default | Contents | Notes |
|---|---|---|---|
| Processing | Stateless | Prompts and responses handled in memory and discarded | Nothing is retained unless you enable logging |
| Template logging | Off | log_template_operations (template CRUD); log_sanitize_operations (full prompt and response text plus verdicts) | Sanitize logs hold raw user data; route them to an access-controlled sink. Anyone with roles/logging.privateLogViewer can read them |
| Floor setting logging | Console: on for Agent Platform, off for MCP | --enable-vertex-ai-cloud-logging, --enable-google-mcp-server-cloud-logging | Detection logs land under modelarmor.googleapis.com/detection. With residency needs, create a regional log sink before enabling |
| Audit logs | Admin Activity on | Template and floor setting changes | See Audit and detection |
| Cloud Trace (Preview) | When tracing is set up for the agent | Parent span apply_guardrail "Google Cloud Model Armor" with Request Path and Response Path children; attributes include gen_ai.security.decision.type (modify means redacted), gen_ai.security.policy.id, gcp.modelarmor.violations, gcp.modelarmor.sdp.info_types | Viewed from Agent Registry (agent, Traces tab) or Trace explorer; needs roles/cloudtrace.user and roles/logging.viewer. Separates guardrail latency and blocks from model errors |
| SCC findings | Premium or Enterprise tier | High-severity floor setting violations | Complements the AI Protection view in Audit and detection |
| Pattern | Floor settings | Template | Outcome |
|---|---|---|---|
| Org baseline, team redaction | Org: prompt injection and malicious URL enabled, RAI minimums | Payments agent: input and output templates with advanced SDP (inspect + de-identify), INSPECT_AND_BLOCK, bound to Agent Gateway in the gateway's region | No team can drop below the baseline; card numbers are masked on gateway traffic |
| Staged tightening | Staging project: custom floor with the tighter thresholds, Agent Platform service set to Inspect only (inline inspection is configured per project, not per folder) | Existing templates unchanged | Logged would-be blocks show false-positive rates before production moves to block |
| Zero-code MCP guard | Project: Google MCP server service, malicious URI and RAI dangerous at MEDIUM_AND_ABOVE, logging on | None required | Calls to supported Google Cloud MCP servers are screened even if a developer attaches no template |
| Gemini Enterprise assistant | Optional | Template bound to the Gemini Enterprise app | Violating turns are blocked; SDP hits block rather than mask, since this integration doesn't de-identify |
BLOCK_LOW_AND_ABOVE, BLOCK_MEDIUM_AND_ABOVE, BLOCK_ONLY_HIGH, BLOCK_NONE, OFF. Method: SEVERITY (default) or PROBABILITY.OFF for configurable filters (verify for your model version).| Method | SDP config | Reversible | Referential integrity | Format kept | Key | Best agent use |
|---|---|---|---|---|---|---|
| Replace with infoType | ReplaceWithInfoTypeConfig | No | No: every email becomes the same [EMAIL_ADDRESS] label | No | None | Free text bound for RAG chunks, logs and prompts where who-is-who doesn't matter |
| Redaction | RedactConfig | No | No | No | None | Dropping a value entirely; the model loses even the hint that something was there |
| Character masking | CharacterMaskConfig | No | No | Length kept; chosen characters stay visible | None | Support agents confirming the last digits of a card or account |
| Crypto hash | CryptoHashConfig | No | Yes | No (fixed-length hashed surrogate) | Yes | Stable join keys when nobody may ever re-identify |
| Deterministic tokenization | CryptoDeterministicConfig (AES-SIV, optional surrogate infoType) | Yes, via content.reidentify with the same key | Yes | No; any input accepted | Yes | Multi-turn, multi-tool flows that must keep customer A and customer B apart; an authorized back end re-identifies |
| Format-preserving encryption | CryptoReplaceFfxFpeConfig (FFX mode) | Yes | Yes | Same length and alphabet (2 to 95 characters) | Yes | A tool or legacy schema validates length or character set |
| Date shifting | DateShiftConfig | No | Same shift per context (e.g., per patient), so sequence and duration hold | Still a valid date | Optional, for consistent shifts | Clinical or financial timelines where intervals matter |
CryptoDeterministicConfig.Once raw values are embedded, stored in sessions or copied into telemetry, finding and deleting them is slow and incomplete. Clean data before each storage boundary:
| Boundary | Risk if raw data lands | Control | Constraints |
|---|---|---|---|
| Before indexing (RAG Engine, Agent Search, vector stores) | Retrieval can surface embedded PII to anyone allowed to query the corpus | Land files in a quarantine bucket; run an SDP inspection job with a de-identify action (console: Make a de-identified copy) that writes to a clean bucket; index only the clean bucket | Output bucket must differ from the input bucket; CSV, TSV, text and image file groups |
| Gemini Enterprise connectors and uploads | Sensitive entities from SaaS sources appear in answers; sensitive files get uploaded | SDP content policy (projects/PROJECT_ID/locations/LOCATION/contentPolicies/POLICY_ID) set on the connector, the assistant or Gemini Notebook Enterprise | Blocks content rather than masking it. Covers third-party, custom MCP and Workspace connectors, not older Cloud Storage, BigQuery or website data stores. Not in the Business edition. The Gemini Enterprise service account needs roles/dlp.user; a policy from another project must sit in the same VPC-SC perimeter |
| Before session and Memory Bank writes | A pasted SSN stays in session events and can be distilled into a long-lived memory | De-identify upstream (Model Armor advanced SDP de-identify on the gateway, or in the front end) so the raw value never becomes an event. ADK before_model_callback / after_tool_callback can also scrub what the model sees and what tools return | Use deterministic tokens when the agent still has to tell entities apart |
| Before log and trace export | Prompts, tool arguments and responses copied into spans, logs, prompt archives in Cloud Storage and BigQuery | ADK_CAPTURE_MESSAGE_CONTENT_IN_SPANS=false keeps PII out of span attributes. OTEL_INSTRUMENTATION_GENAI_CAPTURE_MESSAGE_CONTENT=EVENT_ONLY still records prompts, as log events, so restrict and de-identify those logs and the prompt and response bucket before analysts query them | Model Armor sanitize logs also hold raw text (see the logging table above) |
Prompt-only privacy: a system instruction such as "never repeat card numbers" is not a control. The raw value is still indexed, stored and logged.
| Need | Gemini safety filters | Model Armor |
|---|---|---|
| Scope | One Gemini call | Any model, any cloud, plus agent traffic via the gateway |
| Prompt injection / jailbreak | ✗ | ✓ |
| Sensitive data / redaction | ✗ (beyond built-in) | ✓ (SDP) |
| Malicious URLs | ✗ | ✓ |
| Central org baseline | ✗ | ✓ floor settings |
agentidentity.googleapis.com, agentidentitycredentials.googleapis.com) can be added to a perimeter (restricted VIP), and agent identities can appear in ingress/egress rules.encryption_spec at creation, using a single-region key in the same region. Can't be changed after deploy.roles/cloudkms.cryptoKeyEncrypterDecrypter to the gcp-sa-aiplatform-re service agent.| Endpoint | At-rest data | ML processing |
|---|---|---|
Regional (e.g., us-central1) | In region | In region |
Jurisdictional multi-region (us, eu) | In jurisdiction | Within jurisdiction (EU excludes UK/CH) |
| Global | In chosen location | No residency guarantee |
For a strict residency requirement, use a regional or jurisdictional endpoint and block the global endpoint with org policy.
| Log type | Default | Captures | Viewer role |
|---|---|---|---|
| Admin Activity | Always on, can't be disabled | Config/metadata changes (create/delete/patch resources, setIamPolicy) | roles/logging.viewer |
| Data Access | Off: must be enabled | Admin read, data read/write (e.g., model/endpoint usage) | roles/logging.privateLogViewer |
| Policy Denied | On | Requests denied by a security policy (e.g., VPC-SC) | roles/logging.viewer |
| System Event | Always on | Google-initiated changes | roles/logging.viewer |
iamEnforcementMode: "DRY_RUN") land in Cloud Audit Logs, which makes them a safe policy-tuning loop.| Signal | Where | What it holds |
|---|---|---|
| Gateway request log | Resource networkservices.googleapis.com/Gateway, log gateway_requests | httpRequest (URL, status); jsonPayload.agentGatewayInfo with mcpInfo (MCP method and tool name) and agentRegistryResource (matched registry entry); authzPolicyInfo result (ALLOWED / DENIED); serviceExtensionsInfo (which extension handled the call); trace. Dry-run traffic is logged too |
| IAP decision | cloudaudit.googleapis.com/data_access, service iap.googleapis.com | authorizationInfo[].granted; authenticationInfo.principalSubject (the agent's SPIFFE or principal:// ID); resolved resource; label iap.googleapis.com/audited_resource_name reads unregisteredResource for unregistered hosts; metadata.iamEnforcementMode |
| Model Armor | Sanitize and detection logs; Agent Platform Security tab (Preview) | Matched filter and verdict; total, flagged and blocked interaction counts |
| Semantic Governance | Log semantic-governance-policy | Overall verdict; evaluations[] with toolName, verdict, rationale; token_usage |
| Agent output | reasoning_engine_stderr / reasoning_engine_stdout | Startup failures and the agent's view of a denied call |
| Observability dashboard | Agent Gateway page, Observability tab | Attempted authorizations, authorization failures, requests per second, egress logs including 403s and unregistered destinations. Needs the _Default log bucket upgraded to Observability Analytics |
| Cloud Trace (Preview) | Telemetry policy attached to the gateway | Gateway spans joined to agent and MCP spans through W3C traceparent; compare with metric networkservices.googleapis.com/agentgateway/total_latencies to split gateway latency from destination latency |
telemetryPolicies resource with samplingRate (the docs suggest 0.01 or 0.001 for busy production traffic) and parentBasedSampling to honor upstream sampling. Grant roles/cloudtrace.agent to the Compute Engine, Network Security and Agent Gateway service agents, and to the calling agent's identity, or the parent span is missing.| Control | Shadow setting | Read results in | To enforce |
|---|---|---|---|
UAP through IAP (REQUEST_AUTHZ) | iamEnforcementMode: "DRY_RUN" in the IAP authorization extension metadata | Audit logs filtered on protoPayload.metadata.iamEnforcementMode="DRY_RUN" | Remove the field |
Semantic Governance (CONTENT_AUTHZ) | sgpEnforcementMode: DRY_RUN in the authorization extension metadata; every call is allowed and verdicts are logged | semantic-governance-policy log | Remove the key |
Model Armor (CONTENT_AUTHZ) | Template or floor setting at INSPECT_ONLY | Sanitize logs, Security tab flagged count | Switch to INSPECT_AND_BLOCK |
iap-egress-authz with service: iap.googleapis.com, failOpen: false and metadata iapPolicyVersion: "V2" plus iamEnforcementMode: "DRY_RUN".updateMask=metadata replaces the whole map. Clearing sgpEnforcementMode that way also drops sgpUserIntentMatching: DISABLED, which turns intent matching back on.| Symptom | Layer | Check | Likely cause and fix |
|---|---|---|---|
| 403 "Egress request is not authorized" | IAP (REQUEST_AUTHZ) | IAP audit entry with granted: false; principalSubject | No egressViaIAP grant for this agent at registry, resource or unregistered-endpoint scope; a CEL condition silently excludes the agent; a PAB leaves the destination out of scope. Test with a direct principal:// binding to rule out principal-set sync delays or missing attributes |
| 403 and no IAP entry at all | Gateway, before IAP | Gateway log authzPolicyInfo = DENIED, requestUrl | Hostname not registered exactly (regional and mtls variants count as different hosts), or no authorization policy targets the gateway, so the extension never runs |
| 403 with different text | Destination | Agent stderr | The tool rejected the call: fix destination IAM (e.g., roles/iap.httpsResourceAccessor, roles/mcp.toolUser) or the injected token |
| Auth error from a third-party tool | Auth manager | Agent logs | Agent lacks roles/agentidentity.user on the auth provider, or the user never completed 3LO consent |
| 403s while Agent Runtime starts | Default-deny egress | Gateway logs, status 403 | Google Cloud APIs the runtime calls at startup (Agent Platform, Resource Manager, Logging, Trace, Secret Manager hostnames) not registered or allowed: dry-run, register, grant |
| "Failed to convert project number to project ID" at startup | Agent identity roles | reasoning_engine_stderr | Missing resourcemanager.projects.get: grant roles/browser alongside the default agent roles |
| Payload blocked | Model Armor | Sanitize log filter; span gen_ai.security.decision.type | Template threshold, or a floor setting stricter than the template |
| Tool call denied mid-turn | Semantic Governance | rationale in the policy log | Constraint too strict, or the call didn't match the user's request (intent matching is on by default); tune in dry run |
| No semantic verdict | Semantic Governance | Gateway log serviceExtensionsInfo lacks the extension | Engine not ACTIVE, gateway not bound, request path outside the policy's when condition, or a streamed model call (not supported) |
| TLS handshake failures | Egress connectivity | Agent logs | Destination uses a private CA or self-signed cert: set tlsConfig in the agent connectivity template (keep PUBLICLY_TRUSTED_ROOTS for Google APIs) and rebind it after changes. BYOC containers must trust the gateway's root CA |
| Binding fails or false "unregistered destination" denials | Project setup | Error text | iap.googleapis.com API disabled |
| Gateway spans appear as separate root traces | Trace propagation | Trace explorer | parentBasedSampling off, caller lacks roles/cloudtrace.agent, or the app doesn't propagate traceparent |
| Latency spike | Gateway vs. destination | agentgateway/total_latencies against the span breakdown | Separate guardrail and policy time (Model Armor and Semantic Governance spans) from tool time before tuning |
Logs explain why one call was denied; dry run shows what would break before you enforce. Enforcing without a dry-run window is a common cause of startup 403s on Agent Runtime.
Audit logs record, and SCC detects and alerts. A question asking "be notified when an agent's credential is used from outside Google Cloud" points to SCC threat detection, not to enabling Data Access logs.
FunctionTool(fn, require_confirmation=True), or pass a function returning a bool (e.g., confirm only when amount > 1000).| Lever | Control |
|---|---|
| Inventory | Agent Registry (auto + manual registration); SCC AI asset inventory |
| Only approved destinations | Gateway default-deny + registry-backed UAPs |
| Only approved gateways | Custom org policy constraints on gateway association |
| Uniform content baseline | Model Armor floor settings at org/folder |
| Uniform reach | PAB on the org/project agent principal set |
| Residency | Org policy blocking the global endpoint |
| Detection | SCC AI Protection; Audit Logs |
| Lifecycle hygiene | Remove orphaned bindings on deletion; principalSet baselines for redeploys |
Agent Identity with least-privilege roles + PAB. Auth manager (3LO/2LO/API keys) + Secret Manager.
Registry-backed, default-deny UAPs (dry-run → enforce). Semantic Governance on proposed tool calls.
Model Armor input/output templates + SDP; Gemini safety filters. VPC-SC, PSC-I, CMEK, regional endpoints.
Tool confirmation for high-stakes actions. Cloud Audit Logs + SCC AI Protection.
roles/agentidentity.user on it.REQUEST_AUTHZ / CONTENT_AUTHZgenerateContent and Google Cloud MCP traffic, with no template reference in code.INSPECT_ONLY / INSPECT_AND_BLOCKCryptoReplaceFfxFpeConfig) that keeps the input's length and alphabet.content.reidentify can find and reverse it.iamEnforcementMode / sgpEnforcementModeDRY_RUN (log only); removing the key enforces.telemetryPolicies resource that turns on Cloud Trace sampling for an Agent Gateway.Listen to the 48-minute Agent Platform Security & Governance audio deep dive, open the 21-slide security deck and sketchnote infographic in the Hub Viewer, or drill scenario questions across Agent Identity, auth manager, PAB, Agent Gateway policies, Model Armor, VPC-SC/PSC/CMEK, audit logs, and HITL.
Q1. Why do bindings break after a redeploy, and how do you prevent it?
spec.effectiveIdentity in CI/CD and put baseline roles on the project principalSet.
Q2. IAM deny vs PAB?
Q3. Why can't IAM alone stop a destructive MCP tool call?
tool.name/destructive_hint does.
Q4. When does Semantic Governance help where UAP can't?
Q5. What's needed to redact PII through Model Armor on the gateway?
INSPECT_AND_BLOCK.
Q6. Which audit logs must you enable to see model usage?
roles/logging.privateLogViewer).