This guide explains how to deploy Agent Gateway for cross-cloud on standard Kubernetes clusters to extend Gemini Enterprise Agent Platform governance to your environment. For an overview of the three-plane architecture, see the Agent Gateway for cross-cloud overview.
Before you begin
Before you begin, ensure that you have the following:
- A Cloud Native Computing Foundation (CNCF)-conformant Kubernetes cluster.
- An existing service mesh or network stack configured in your cluster.
- Outbound network connectivity from your cluster to Google Cloud APIs over
HTTPS (TCP port
443). - The Google Cloud CLI version 591.0.0 or later installed. To update to the
latest version, run
gcloud components update. - The Identity and Access Management (IAM) roles on your Google Cloud project that you need to enable APIs, create a workload identity pool and provider, and grant IAM roles.
Enable APIs and register the cluster
Enable the required Google Cloud APIs, and then register your external cluster.
Enable the required APIs
To enable the APIs that Agent Gateway for cross-cloud requires, run the following command:
gcloud services enable \
agentregistry.googleapis.com \
apphubendpoint.googleapis.com \
cloudresourcemanager.googleapis.com \
cloudtrace.googleapis.com \
iam.googleapis.com \
iamcredentials.googleapis.com \
iap.googleapis.com \
logging.googleapis.com \
modelarmor.googleapis.com \
monitoring.googleapis.com \
networkservices.googleapis.com \
serviceextensions.googleapis.com \
sts.googleapis.com \
telemetry.googleapis.com \
trafficdirector.googleapis.com \
--project=PROJECT_ID
Replace PROJECT_ID with the ID of your
Google Cloud project.
Extract the Kubernetes API server public key
To register your cluster, you need the public key that your cluster uses to sign service account tokens. To extract the public key, do the following:
Set your current
kubectlcontext to the cluster that you want to register.Retrieve the JSON Web Key Set (JWKS) of your cluster, and then convert the first RSA key to a public key file named
pubkey.pem:kubectl get --raw /openid/v1/jwks > cluster-jwks.json N_HEX=$(jq -r '[.keys[] | select((.kty // "" | ascii_upcase) == "RSA" and (.n // "") != "" and (.e // "") != "")][0].n | gsub("-";"+") | gsub("_";"/") | . + ("==="[:(4 - length % 4) % 4])' cluster-jwks.json | base64 -d | od -An -tx1 | tr -d ' \n') E_HEX=$(jq -r '[.keys[] | select((.kty // "" | ascii_upcase) == "RSA" and (.n // "") != "" and (.e // "") != "")][0].e | gsub("-";"+") | gsub("_";"/") | . + ("==="[:(4 - length % 4) % 4])' cluster-jwks.json | base64 -d | od -An -tx1 | tr -d ' \n') openssl asn1parse -genconf <(printf "asn1=SEQUENCE:rsa_key\n[rsa_key]\nmodulus=INTEGER:0x%s\nexponent=INTEGER:0x%s\n" "$N_HEX" "$E_HEX") -noout -out /dev/stdout \ | openssl rsa -RSAPublicKey_in -inform DER -pubout -out pubkey.pemThese commands require a Bash shell and the
jqandopensslutilities.
Register the cluster
To register your cluster, do the following:
Retrieve the OpenID Connect (OIDC) issuer URL of your cluster:
kubectl get --raw /.well-known/openid-configuration | jq -r '.issuer'
The output is the value of the
--service-account-issuerflag of your cluster's Kubernetes API server. This value might differ from the external URL of your Kubernetes API server. Copy the output exactly. Don't add or remove a trailing slash.Register the cluster:
gcloud alpha network-services registered-clusters create CLUSTER_NAME \ --project=PROJECT_ID \ --location=LOCATION \ --oidc-issuer-url=OIDC_ISSUER_URL \ --api-server-public-key="$(cat pubkey.pem)"Replace the following:
CLUSTER_NAME: a name for the registered cluster.PROJECT_ID: the ID of your Google Cloud project.LOCATION: the Google Cloud region for the registered cluster—for example,us-central1.OIDC_ISSUER_URL: the exact issuer URL that you retrieved in the previous step. The URL must start withhttps://.
Prevent accidental resource deletion
Deleting your Google Cloud project or unregistering a registered cluster while cross-cloud workloads are active disconnects in-cluster controllers and disrupts policy enforcement. To protect your Google Cloud resources from accidental deletion, apply the following safeguards:
- Protect the Google Cloud project with liens: Apply a project lien to the Google Cloud project that hosts your registered cluster, Agent Registry, and federated identity pools to block accidental project deletion until all cross-cloud clusters are decommissioned.
- Restrict destructive IAM permissions: Limit
permissions that can delete registered clusters or gateway resources (such
as
networkservices.registeredClusters.deleteandnetworkservices.agentGateways.delete) to dedicated platform administrator roles. - Retain Kubernetes resource finalizers: Don't remove finalizers on
in-cluster custom resources managed by the
agent-governance-cross-cloud-controllercontroller. Finalizers ensure that corresponding Google Cloud control plane registrations are cleanly deprovisioned before local cluster resources are removed. - Delete custom resources before you remove the controller: Before you
uninstall the
agent-governance-cross-cloudHelm chart or delete your Kubernetes cluster, delete allGCPExtensionBindingandGCPAgentGatewaycustom resources, and wait for the deletion to complete. If you remove the controller first, the corresponding Agent Gateway resource remains orphaned in Network Services. The orphaned gateway blocks deletion of the agent connectivity template and the registered cluster.
Configure Workload Identity Federation
The in-cluster components of Agent Gateway for cross-cloud—the
agent-governance-cross-cloud-controller controller, the telemetry collector,
and the proxy—use Workload Identity Federation to authenticate to
Google Cloud APIs. Each component exchanges its Kubernetes service account
(KSA) token for a federated access token. You grant IAM roles
directly to these federated identities, so you don't need to create
Google Cloud service accounts or store service account keys in your
cluster.
The Agent Identity Runtime (AIR) sidecar that runs with each proxy uses the workload identity pool to provide SPIFFE identities to your agent workloads. These identities are the Agent Identities of your agents. Model Armor and IAP policies identify agents by their Agent Identity, not by the federated identities of the in-cluster components. For the principal format, see Identify the agent principal.
Create a workload identity pool and provider
To create a workload identity pool and an OIDC provider for your cluster, do the following:
If your cluster's OIDC issuer URL isn't publicly reachable, save the JSON Web Key Set (JWKS) of your cluster to a file:
kubectl get --raw /openid/v1/jwks > cluster-jwks.json
When you provide the JWKS to the provider, Google Cloud can verify KSA tokens without connecting to your cluster's OIDC issuer.
Create a workload identity pool:
gcloud iam workload-identity-pools create POOL_ID \ --project=PROJECT_ID \ --location=global \ --display-name="POOL_DISPLAY_NAME"Create an OIDC provider in the pool:
gcloud iam workload-identity-pools providers create-oidc PROVIDER_ID \ --project=PROJECT_ID \ --location=global \ --workload-identity-pool=POOL_ID \ --issuer-uri=OIDC_ISSUER_URL \ --jwk-json-path=cluster-jwks.json \ --attribute-mapping="google.subject=assertion.sub,attribute.namespace=assertion['kubernetes.io']['namespace'],attribute.service_account=assertion['kubernetes.io']['serviceaccount']['name']"Don't specify the
--allowed-audiencesflag. The cross-cloud components request tokens for the default audience, which is the full resource name of the provider://iam.googleapis.com/projects/PROJECT_NUMBER/locations/global/workloadIdentityPools/POOL_ID/providers/PROVIDER_ID.
Replace the following:
POOL_ID: an ID for the workload identity pool.PROJECT_ID: your Google Cloud project ID.POOL_DISPLAY_NAME: a display name for the pool.PROVIDER_ID: an ID for the OIDC provider.OIDC_ISSUER_URL: the exact OIDC issuer URL that you retrieved when you registered the cluster. This value must match the value that you passed to--oidc-issuer-url, including whether it ends with a trailing slash.
Deploy the control plane
Use the provided Helm chart to deploy the in-cluster controller,
agent-governance-cross-cloud-controller and telemetry collector in the
geap-system namespace.
Ensure connectivity for the controller
Ensure that your cluster firewall, proxy, egress rules, and network policies allow the following connectivity:
- Google Cloud APIs: The
agent-governance-cross-cloud-controllercontroller pods and the telemetry collector must have outbound HTTPS and gRPC connectivity on TCP port443to*.googleapis.com. - Kubernetes API server: The controller pods must be able to reach the Kubernetes API server to watch Kubernetes services, workloads, and custom resources.
- Agent workloads in all namespaces: The controller pods must be able to call agent and MCP server pods in any namespace in the cluster. The controller fetches the Agent Card from each agent workload to register the agent in Agent Registry.
Grant IAM roles to the controller and telemetry collector
Grant IAM roles to the federated identities of the
agent-governance-cross-cloud-controller controller and the telemetry
collector, which run in the geap-system namespace:
- Satellite Operator (
roles/networkservices.satelliteOperator) - Cloud Telemetry Writer (
roles/telemetry.writer)
To grant the roles, run the following command:
for ROLE in \
roles/networkservices.satelliteOperator \
roles/telemetry.writer; do
gcloud projects add-iam-policy-binding PROJECT_ID \
--role="${ROLE}" \
--member="principalSet://iam.googleapis.com/projects/PROJECT_NUMBER/locations/global/workloadIdentityPools/POOL_ID/attribute.namespace/geap-system"
done
Replace the following:
PROJECT_ID: your Google Cloud project ID.PROJECT_NUMBER: your Google Cloud project number.POOL_ID: the ID of the workload identity pool.
Install the control plane Helm chart
To install the control plane, do the following:
Install the
agent-governance-cross-cloudHelm chart in thegeap-systemnamespace:helm upgrade --install agent-governance-cross-cloud \ oci://gcr.io/gke-release/agent-governance-cross-cloud \ --version 0.1.0-preview \ --namespace geap-system \ --create-namespace \ --set gcp.projectId=PROJECT_ID \ --set-string gcp.projectNumber=PROJECT_NUMBER \ --set-string gcp.orgId=ORGANIZATION_ID \ --set gcp.location=LOCATION \ --set gcp.registeredCluster=CLUSTER_NAME \ --set gcp.workloadIdentityPool=POOL_ID \ --set gcp.identityProvider=PROVIDER_IDReplace the following:
PROJECT_ID: your Google Cloud project ID.PROJECT_NUMBER: your Google Cloud project number.ORGANIZATION_ID: the ID of your Google Cloud organization. If your project isn't associated with an organization, omit the--set-string gcp.orgIdflag.LOCATION: the region where you registered the cluster—for example,us-central1.CLUSTER_NAME: the name of the registered cluster.POOL_ID: the ID of the workload identity pool.PROVIDER_ID: the ID of the workload identity pool provider.
If your cluster's nodes have taints—for example, arm64 nodes on most cloud providers, including Google Cloud, AWS, and Azure—set pod tolerations in the
k8s.tolerationsHelm value. The tolerations apply to the controller, the OpenTelemetry (OTel) collector, and the gateway deployments that the controller creates. For example, save the following astolerations-values.yaml, and then add--values tolerations-values.yamlto the preceding command:k8s: tolerations: - key: "kubernetes.io/arch" operator: "Equal" value: "arm64" effect: "NoSchedule"Wait for the controller deployment to become ready:
kubectl rollout status deployment/agent-governance-cross-cloud-controller \ --namespace geap-system --timeout=180s
Deploy the data plane
Deploy the Agent Gateway for cross-cloud proxy (Envoy proxy) by
creating a GCPAgentGateway custom resource. The proxy inspects traffic and
enforces policies for the agent workloads that it governs. Each proxy runs with
an Agent Identity Runtime (AIR) sidecar that brokers workload identities, Secret
Discovery Service (SDS) certificates, and local token exchange for the proxy.
You must deploy Agent Gateway for cross-cloud in a dedicated namespace
(for example, agent-gateway-system) that contains no agent workloads, MCP
servers, or other application pods. A single gateway in this dedicated
namespace can govern outbound traffic from agent workloads across your cluster.
Grant IAM roles to the gateway
Grant IAM roles to the federated identities of the Agent Gateway for cross-cloud proxy. The binding applies to all KSAs in the gateway namespace, so grant these roles for each gateway namespace:
- Traffic Director Client (
roles/trafficdirector.client) - App Hub Endpoint Viewer (
roles/apphubendpoint.viewer) - Agent Registry Viewer (
roles/agentregistry.viewer) - Service Extensions Editor (
roles/serviceextensions.editor), which lets the proxy call Service Extensions policy callouts
To grant the roles, run the following command:
for ROLE in \
roles/trafficdirector.client \
roles/apphubendpoint.viewer \
roles/agentregistry.viewer \
roles/serviceextensions.editor; do
gcloud projects add-iam-policy-binding PROJECT_ID \
--role="${ROLE}" \
--member="principalSet://iam.googleapis.com/projects/PROJECT_NUMBER/locations/global/workloadIdentityPools/POOL_ID/attribute.namespace/GATEWAY_NAMESPACE"
done
Replace the following:
PROJECT_ID: your Google Cloud project ID.PROJECT_NUMBER: your Google Cloud project number.POOL_ID: the ID of the workload identity pool.GATEWAY_NAMESPACE: a dedicated namespace for the gateway. This namespace must not contain agent workloads.
Create an agent connectivity template
Each gateway references an agent connectivity template, which is a Google Cloud resource that references your registered cluster. You don't need to create a separate template for each gateway: you can map multiple gateways to a single template. You can also create multiple templates for the same cluster.
If you already have an agent connectivity template for your cluster that you want to use, skip to Create the gateway.
To create an agent connectivity template, do the following:
Save the following template definition as
act.yaml:description: "Agent connectivity template for CLUSTER_NAME" agentCompute: K8S registeredCluster: "projects/PROJECT_ID/locations/LOCATION/registeredClusters/CLUSTER_NAME"
Import the template:
gcloud alpha network-services agent-connectivity-templates import ACT_NAME \ --project=PROJECT_ID \ --location=LOCATION \ --source=act.yaml
Replace the following:
CLUSTER_NAME: the name of the registered cluster.PROJECT_ID: your Google Cloud project ID.LOCATION: the region where you registered the cluster.ACT_NAME: a name for the agent connectivity template.
Create the gateway
To create the gateway, do the following:
If the gateway namespace doesn't already exist, create it:
kubectl create namespace GATEWAY_NAMESPACE
Replace
GATEWAY_NAMESPACEwith the dedicated namespace for the gateway that you used when you granted roles to the gateway.Save the following manifest as
agent-gateway.yaml:apiVersion: networkservices.googleapis.com/v1alpha1 kind: GCPAgentGateway metadata: name: GATEWAY_NAME namespace: GATEWAY_NAMESPACE spec: agentConnectivityTemplateUri: "projects/PROJECT_ID/locations/LOCATION/agentConnectivityTemplates/ACT_NAME"
Replace the following:
GATEWAY_NAME: a name for the gateway.GATEWAY_NAMESPACE: the dedicated namespace for the gateway that you used when you granted roles to the gateway.PROJECT_ID: your Google Cloud project ID.LOCATION: the region of the agent connectivity template. This region must match the region where you registered the cluster.ACT_NAME: the name of the agent connectivity template.
Apply the manifest:
kubectl apply -f agent-gateway.yaml
Wait for the gateway to be deployed and ready:
kubectl wait --for=condition=Reconciled=True \ gcpagentgateway/GATEWAY_NAME \ -n GATEWAY_NAMESPACE \ --timeout=300sWhen the gateway is ready, the command returns
gcpagentgateway.networkservices.googleapis.com/GATEWAY_NAME condition met.Configure your service mesh to forward outbound agent traffic, such as large language model (LLM), Model Context Protocol (MCP), and Agent-to-Agent (A2A) requests, through the gateway.
Add tolerations to existing gateways
If you add the k8s.tolerations Helm value after you create a
GCPAgentGateway resource, do the following so that the controller re-creates
the gateway deployment with the new tolerations:
Run the
helm upgrade --installcommand from Install the control plane Helm chart with--values tolerations-values.yaml.Delete the existing gateway deployment:
kubectl delete deployment GATEWAY_NAME \ --namespace GATEWAY_NAMESPACERepeat this step for each gateway in your cluster.
Restart the controller so that it re-creates the gateway deployment:
kubectl rollout restart deployment/agent-governance-cross-cloud-controller \ --namespace geap-system
This procedure doesn't affect the Google Cloud AgentGateway resource or
any attached GCPExtensionBinding resources. Outbound traffic through the
gateway is interrupted until the controller re-creates the gateway deployment.
Allow outbound connectivity to Google APIs
Ensure that the Agent Gateway for cross-cloud proxy pods have outbound
HTTPS and gRPC connectivity to *.googleapis.com on TCP port 443.
Configure TLS inspection
Configure Transport Layer Security (TLS) inspection to ensure that Agent Gateway for cross-cloud can inspect and govern outbound traffic.
Secure the TLS inspection CA secret
Agent Gateway for cross-cloud uses a root certificate authority (CA) to
issue the certificates that it presents when it inspects outbound TLS traffic.
The controller creates this CA when it starts. The CA, including its private
key, is stored in Kubernetes Secret resources in two namespaces: the
controller namespace (geap-system) and the gateway namespace, where the
Agent Gateway for cross-cloud proxy and AIR sidecar are deployed.
Anyone who can read these Secret resources can impersonate external endpoints
to your agent workloads. To protect the CA, apply the following safeguards:
- Restrict RBAC access to the
Secret: Limit theget,list, andwatchpermissions onSecretresources in thegeap-systemand gateway namespaces to the Agent Gateway for cross-cloud service accounts and dedicated platform administrators. Thelistandwatchpermissions also exposeSecretcontents. - Isolate the controller and gateway namespaces: Users who can create
pods in a namespace can mount any
Secretin that namespace. Run only Agent Gateway for cross-cloud components in thegeap-systemnamespace and the gateway namespace. Don't deploy agent workloads or other pods in these namespaces. Limit permissions to create or modify pods, deployments, and other workload resources in these namespaces to trusted administrators. - Encrypt
Secretdata at rest: Enable encryption at rest forSecretresources in your cluster's etcd datastore. Where possible, use a KMS provider to manage the encryption keys. - Audit access to the
Secret: Enable Kubernetes audit logging and monitor read access to the CASecret.