Passerelle d'entrée Agent Gateway vers Agent Runtime avec Model Armor

1. Introduction

Cet atelier de programmation explore la gouvernance des entrées Agent Gateway pour les agents d'IA hébergés sur Agent Runtime.

Agent Gateway fonctionnant en mode entrée (client vers agent) permet de régir les communications entre les clients (utilisateurs finaux humains, agents de bureau, IDE de programmation, agents pairs, etc.) et les agents Agent Runtime hébergés. Ce mode permet de protéger les agents contre les attaques par injection de requêtes entrantes ou les contenus nuisibles envoyés par les clients. Tout le trafic entrant est traité à l'aide d'extensions d'autorisation et de Model Armor pour sécuriser le point d'entrée du réseau pour toutes les interactions avec l'agent.

Objectif de l'atelier

  • Agent Gateway en mode entrée (client vers agent)
  • Extension d'autorisation Model Armor
  • Agent ADK Agent Runtime avec identité d'agent
  • Données de fichier Cloud Storage interrogées par l'agent à l'aide de MCP
  • Modèles Model Armor pour filtrer les requêtes et les réponses des LLM
  • Modèles Sensitive Data Protection pour anonymiser les données

figure1

Fig. 1. Architecture de l'atelier de programmation

Objectifs

  • Déployer Agent Gateway pour filtrer le trafic entrant vers un agent
  • Configurer les extensions d'autorisation et la délégation Model Armor
  • Créer et déployer des modèles Model Armor personnalisés
  • Créer et déployer des modèles Sensitive Data Protection personnalisés
  • Tester et valider les règles de filtrage des LLM

Ce dont vous avez besoin

  • Un projet Google Cloud avec facturation activée
  • Autorisations IAM pour provisionner des services réseau, des ensembles de données BigQuery et des ressources de la plate-forme d'agents
  • Un shell compatible POSIX (bash ou zsh) avec Google Cloud CLI (composant gcloud) installé
  • Outils de ligne de commande : git, curl, jq (processeur JSON), Python 3 et uv (gestionnaire de packages Python)

2. Concepts

Sens du trafic et rôles de passerelle

Agent Gateway fonctionne comme un proxy réseau compatible avec les agents, mais son rôle opérationnel change en fonction de la direction du trafic :

  • Mode Agent-to-anywhere (sortie) : fonctionne comme un proxy sortant. Lorsqu'un agent appelle des outils de base de données externes, des serveurs MCP tiers ou des API, la passerelle de sortie gère la découverte de services, le routage, le TLS mutuel (mTLS), l'injection dynamique d'identifiants OAuth et le contrôle des accès aux points de terminaison.
  • Mode client-agent (entrée) : fonctionne comme une passerelle de sécurité de l'interface utilisateur. Son objectif principal est de protéger l'entrée du runtime d'exécution de l'agent en interceptant et en assainissant les requêtes en langage naturel entrantes avant qu'elles n'atteignent le code agentique ou les modèles d'IA.

Chemin d'accès entrant à Agent Runtime

Les requêtes client ciblant un agent hébergé sur Agent Runtime sont destinées au point de terminaison de l'API aiplatform.googleapis.com.

POST https://${REGION}-aiplatform.googleapis.com/v1/projects/${PROJECT_ID}/locations/${REGION}/reasoningEngines/${RE_ENGINE_ID}:query

Ce flux de communication entrant vers le point de terminaison de l'API représente le chemin d'accès entrant client-agent.

Pour sécuriser ce chemin d'accès Ingress géré par Google, Agent Gateway s'intègre directement au Google Front End (GFE) au niveau de la couche d'infrastructure de diffusion d'API. Lorsque vous déployez un agent géré sur Agent Runtime, Google lie de manière native la règle d'autorisation de la passerelle d'entrée aux requêtes client entrantes en périphérie du réseau.

figure2

Fig. 2. Gouvernance des entrées avec Agent Gateway vers Agent Runtime

Comme l'inspection a lieu au niveau du frontend avant que les requêtes n'entrent dans Agent Runtime, cette architecture n'introduit aucune surcharge réseau ni latence de saut interne supplémentaires. La mise à l'échelle est gérée automatiquement par l'infrastructure de l'interface utilisateur, ce qui élimine la nécessité de gérer les plages d'adresses IP internes, les équilibreurs de charge ou les routes DNS personnalisées.

Nettoyage des menaces en ligne avec Model Armor

L'évaluation des identifiants de l'appelant et l'application du contrôle des accès IAM (roles/aiplatform.user) sont gérées de manière native par le niveau d'hébergement de l'API aiplatform. La passerelle d'entrée elle-même n'effectue pas d'autorisation d'identité, mais se concentre plutôt sur la sécurité du contenu à l'aide des extensions d'autorisation configurées avec un profil CONTENT_AUTHZ. La passerelle sert de point d'application des règles en ligne. Elle intercepte les requêtes en langage naturel en transit avant qu'elles n'atteignent la boucle de raisonnement de l'agent d'IA ou le LLM sous-jacent.

Lorsqu'une requête utilisateur entrante arrive au service d'interface, la passerelle lance un appel ext_proc (traitement externe) au service d'extension d'autorisation Model Armor régional, qui transmet l'appel au plan de données Model Armor. Model Armor agit comme un pare-feu en langage naturel. Il évalue le texte par rapport aux modèles actifs pour détecter les risques de sécurité :

  • Injections de prompts indirectes et tentatives de jailbreaking
  • URL malveillantes, langage toxique ou contenu dangereux
  • Informations permettant d'identifier personnellement l'utilisateur (PII) et fuite de données sensibles

Si le modèle inclut des filtres Sensitive Data Protection (SDP), Model Armor effectue un appel gRPC supplémentaire au service Cloud SDP. Cloud SDP inspecte la charge utile à l'aide du modèle spécifié, effectue toute anonymisation ou masquage demandé, puis renvoie le résultat nettoyé en amont pour qu'il soit transféré de manière sécurisée.

Si un cas de non-respect des règles ou une correspondance de données sensibles non masquées est détecté, la passerelle bloque ou masque la charge utile à la périphérie avant qu'elle n'entre dans l'environnement d'exécution. Par conséquent, l'application d'agent d'IA en cours d'exécution reste protégée et ne traite jamais de charges utiles malveillantes ou non masquées.

La partie sur les concepts est terminée. Passons à la section Configuration.

3. Configuration

Rôles IAM requis

Les rôles suivants sont requis pour créer les ressources dans cet atelier de programmation :

Catégorie

Rôle IAM requis (ID)

Description

Gestion des API

roles/serviceusage.serviceUsageAdmin

Activer les services d'API Google Cloud

Mise en réseau et passerelle

roles/networkservices.admin

Provisionner une passerelle d'agent

Extensions de service

roles/serviceextensions.admin

Configurer les extensions de routage

Sécurité du réseau

roles/networksecurity.admin

Déployer des règles d'autorisation

Protection des données sensibles

roles/dlp.admin

Gérer les modèles d'inspection et d'anonymisation SDP

Model Armor

roles/modelarmor.admin

Créer et gérer des modèles de sécurité

Agent Platform

roles/aiplatform.admin

Déployer des charges de travail Agent Runtime

Cloud Storage

roles/storage.admin

Gérer les buckets de déploiement et de données client

Administration IAM

roles/resourcemanager.projectIamAdmin

Associer des autorisations au niveau du projet pour l'identité de l'agent

Journaux et audit

roles/logging.viewer

Inspecter les traces et les journaux d'audit

Vous pouvez également utiliser un rôle de base étendu, comme roles/admin, ou un rôle hérité, comme roles/owner.

Accéder à votre projet

Cet atelier de programmation utilise un seul projet Google Cloud. Les étapes de configuration utilisent la CLI gcloud et les commandes du shell Linux.

Commencez par accéder à la ligne de commande de votre projet Google Cloud :

Définir votre ID de projet

gcloud config set project SET_YOUR_PROJECT_ID_HERE

Authentifier la session

# login to gcloud cli
gcloud auth login
# login for gcloud api
gcloud auth application-default login

Définir des variables d'environnement de shell

# set custom var for slug (eg, "foo") and region preference
export SLUG="foo"
export REGION="us-central1"
echo ${SLUG}
echo ${REGION}
# create project vars (automatic)
export PROJ_ID=$(gcloud config list --format="value(core.project)")
export PROJ_NO=$(gcloud projects describe ${PROJ_ID} --format="value(projectNumber)")
export ORG_ID=$(gcloud projects get-ancestors ${PROJ_ID} --format="value(id)" | tail -n 1)
export USER_IDENTITY=$(gcloud config get-value account)
echo ${PROJ_ID}
echo ${PROJ_NO}
echo ${ORG_ID}
echo ${USER_IDENTITY}
# create resource vars (automatic)
export AGW_NAME="agw-${SLUG}-${REGION}-cta"
export AGW_URI="projects/${PROJ_ID}/locations/${REGION}/agentGateways/${AGW_NAME}"
export RE_AGENT_NAME="agent-crm"
export RE_AGENT_ID_SET="principalSet://agents.global.org-${ORG_ID}.system.id.goog/attribute.platformContainer/aiplatform/projects/${PROJ_NO}"
export STAGING_BUCKET="agent-staging-${PROJ_NO}"
export DATA_BUCKET="customer-data-${PROJ_NO}"
export MCP_URL="https://storage.mtls.googleapis.com/storage/mcp"
echo ${AGW_NAME}
echo ${AGW_URI}
echo ${RE_AGENT_NAME}
echo ${RE_AGENT_ID_SET}
echo ${STAGING_BUCKET}
echo ${DATA_BUCKET}
echo ${MCP_URL}
# create local dir for config files
mkdir -p cfg

Si vous exécutez une installation autogérée du SDK Google Cloud (c'est-à-dire en dehors de Cloud Shell), mettez à jour les composants vers la dernière version.

# update gcloud cli
gcloud components update

Activer les services d'API

# enable google apis (agent platform bundle, part 1)
gcloud services enable \
  agentregistry.googleapis.com \
  aiplatform.googleapis.com \
  apphub.googleapis.com \
  apptopology.googleapis.com \
  cloudapiregistry.googleapis.com \
  cloudtrace.googleapis.com \
  compute.googleapis.com \
  dataform.googleapis.com \
  iam.googleapis.com \
  iamconnectors.googleapis.com \
  iap.googleapis.com \
  logging.googleapis.com \
  modelarmor.googleapis.com \
  monitoring.googleapis.com \
  networksecurity.googleapis.com \
  networkservices.googleapis.com \
  notebooks.googleapis.com \
  observability.googleapis.com
# enable google apis (agent platform bundle, part 2)
gcloud services enable \
  securitycenter.googleapis.com \
  saasservicemgmt.googleapis.com \
  storage.googleapis.com \
  telemetry.googleapis.com \
  texttospeech.googleapis.com
# enable google apis (all the rest)
gcloud services enable \
  dlp.googleapis.com

La partie concernant la configuration est terminée. Passons à la section Passerelle.

4. Passerelle

Déployez une passerelle Agent Gateway gérée par Google fonctionnant en mode client-agent (CLIENT_TO_AGENT). Contrairement aux passerelles de sortie qui nécessitent des associations au registre d'agents pour acheminer les appels sortants, la passerelle d'entrée se lie directement au niveau du frontend pour servir de point d'application intégré pour les requêtes entrantes ciblant Agent Runtime.

Alors que les règles de sortie commencent souvent en mode DRY_RUN au niveau de la passerelle, la gouvernance du contenu d'entrée (CONTENT_AUTHZ) est déployée directement en mode appliqué. La journalisation ou le blocage actif et précis sont plutôt contrôlés en amont dans les modèles Model Armor individuels.

Créer une passerelle

# create agent gateway config file
cat > cfg/${AGW_NAME}.yaml <<EOF
name: ${AGW_NAME}
protocols:
  - MCP
googleManaged:
  governedAccessPath: CLIENT_TO_AGENT
EOF
# import agent gateway config file (create gateway)
gcloud network-services agent-gateways import ${AGW_NAME} \
  --source="cfg/${AGW_NAME}.yaml" \
  --location=${REGION}

Valider la passerelle

# list agent gateways (in region)
gcloud network-services agent-gateways list --location=${REGION}
# show agent gateway details (verify deployment state)
gcloud network-services agent-gateways describe ${AGW_NAME} --location=${REGION}

La partie sur la passerelle est terminée. Passons maintenant à la section Model Armor.

5. Model Armor

Modèles de Fiche

Créez un modèle d'inspection et d'anonymisation Sensitive Data Protection (SDP) à utiliser dans le modèle de réponse Model Armor. Cette configuration signale les numéros de sécurité sociale américains (SSN) à masquer.

Créer un modèle d'inspection

Le modèle d'inspection identifie les informations sensibles (US_SOCIAL_SECURITY_NUMBER) dans les données.

# create inspect template
curl -fsS -X POST "https://dlp.googleapis.com/v2/projects/${PROJ_ID}/locations/${REGION}/inspectTemplates" \
  -H "Authorization: Bearer $(gcloud auth application-default print-access-token)" \
  -H "Content-Type: application/json" -H "x-goog-user-project: ${PROJ_ID}" \
  -d @- << EOF
{
  "templateId": "agw-ssn-inspect-template",
  "inspectTemplate": {
    "displayName": "ssn inspect template",
    "inspectConfig": {
      "infoTypes": [
        { "name": "US_SOCIAL_SECURITY_NUMBER" }
      ],
      "minLikelihood": "POSSIBLE"
    }
  }
}
EOF

Créer un modèle d'anonymisation

Le modèle d'anonymisation spécifie la transformation à appliquer aux numéros de sécurité sociale trouvés par le modèle d'inspection. Dans ce cas, la transformation consiste à remplacer le numéro de sécurité sociale par l'infoType.

# create de-identify template
curl -fsS -X POST "https://dlp.googleapis.com/v2/projects/${PROJ_ID}/locations/${REGION}/deidentifyTemplates" \
  -H "Authorization: Bearer $(gcloud auth application-default print-access-token)" \
  -H "Content-Type: application/json" -H "x-goog-user-project: ${PROJ_ID}" \
  -d @- << EOF
{
  "templateId": "agw-ssn-redaction-template",
  "deidentifyTemplate": {
    "displayName": "SSN Redaction Template",
    "deidentifyConfig": {
      "infoTypeTransformations": {
        "transformations": [{
          "primitiveTransformation": { "replaceWithInfoTypeConfig": {} }
        }]
      }
    }
  }
}
EOF

Valider les modèles SDP

# get (describe) inspect template
curl -fsS -X GET "https://dlp.googleapis.com/v2/projects/${PROJ_ID}/locations/${REGION}/inspectTemplates" \
  -H "Authorization: Bearer $(gcloud auth application-default print-access-token)" \
  -H "Content-Type: application/json" -H "x-goog-user-project: ${PROJ_ID}" | jq
# get (describe) de-identify template
curl -fsS -X GET "https://dlp.googleapis.com/v2/projects/${PROJ_ID}/locations/${REGION}/deidentifyTemplates" \
  -H "Authorization: Bearer $(gcloud auth application-default print-access-token)" \
  -H "Content-Type: application/json" -H "x-goog-user-project: ${PROJ_ID}" | jq

Modèles Model Armor

Le point de terminaison par défaut de l'API Model Armor est global (modelarmor.googleapis.com). Toutefois, les ressources Model Armor pour les modèles et les moteurs d'évaluation sont localisées dans des régions géographiques spécifiques. Le proxy de point de terminaison régional Google Cloud (REP), ou point de terminaison d'API régional, pour Model Armor est https://modelarmor.${LOCATION}.rep.googleapis.com/.

Par défaut, lorsque vous exécutez gcloud model-armor ..., l'interface CLI tente d'envoyer des requêtes API au point de terminaison mondial standard (https://modelarmor.googleapis.com/). Un remplacement du point de terminaison de l'API est utilisé pour rediriger toutes les requêtes HTTP du SDK/de l'interface CLI pour Model Armor directement vers le niveau d'API rep.googleapis.com régional où ces modèles liés à la localisation sont réellement créés, stockés et interrogés.

Définir un remplacement d'API

# set api endpoint override per location
gcloud config set api_endpoint_overrides/modelarmor "https://modelarmor.${REGION}.rep.googleapis.com/"

Vérifier le remplacement de l'API

# view api overrides on active gcloud config
gcloud config list api_endpoint_overrides/

Créer un modèle de filtre de requête

Créez un modèle de filtre de requête pour bloquer les contenus incitant à la haine, le harcèlement, les contenus à caractère sexuel explicite et les attaques par injection d'URI. La journalisation sera activée pour enregistrer des informations détaillées sur l'application des règles. Des codes et messages d'erreur personnalisés sont également configurés pour les requêtes bloquées.

# create model armor template (request)
gcloud beta model-armor templates create ${AGW_NAME}-modar-req-template \
  --project=${PROJ_ID} \
  --location=${REGION} \
  --rai-settings-filters='[
    { "filterType": "HATE_SPEECH", "confidenceLevel": "MEDIUM_AND_ABOVE" },
    { "filterType": "HARASSMENT", "confidenceLevel": "MEDIUM_AND_ABOVE" },
    { "filterType": "SEXUALLY_EXPLICIT", "confidenceLevel": "MEDIUM_AND_ABOVE" }
  ]' \
  --pi-and-jailbreak-filter-settings-enforcement=enabled \
  --pi-and-jailbreak-filter-settings-confidence-level=medium-and-above \
  --template-metadata-enforcement-type=INSPECT_AND_BLOCK \
  --malicious-uri-filter-settings-enforcement=enabled \
  --template-metadata-custom-llm-response-safety-error-code=798 \
  --template-metadata-custom-llm-response-safety-error-message="ahoy! model response blocked by content filter :(" \
  --template-metadata-custom-prompt-safety-error-code=799 \
  --template-metadata-custom-prompt-safety-error-message="ahoy! the request was blocked by ye content filter... so rephrase the prompt and try again!" \
  --template-metadata-ignore-partial-invocation-failures \
  --template-metadata-log-operations \
  --template-metadata-log-sanitize-operations

Créer un modèle de filtre de réponse

Créez un modèle de filtre de réponse pour bloquer le même contenu que le modèle de filtre de requête. La protection DLP est configurée sur la partie réponse pour désidentifier les numéros de sécurité sociale des messages renvoyés au client par l'agent.

# create model armor template (response)
gcloud beta model-armor templates create ${AGW_NAME}-modar-resp-template \
  --project=${PROJ_ID} \
  --location=${REGION} \
  --rai-settings-filters='[
      { "filterType": "HATE_SPEECH", "confidenceLevel": "MEDIUM_AND_ABOVE" },
      { "filterType": "HARASSMENT", "confidenceLevel": "MEDIUM_AND_ABOVE" },
      { "filterType": "SEXUALLY_EXPLICIT", "confidenceLevel": "MEDIUM_AND_ABOVE" }
  ]' \
  --malicious-uri-filter-settings-enforcement=enabled \
  --advanced-config-inspect-template=projects/${PROJ_ID}/locations/${REGION}/inspectTemplates/agw-ssn-inspect-template \
  --advanced-config-deidentify-template=projects/${PROJ_ID}/locations/${REGION}/deidentifyTemplates/agw-ssn-redaction-template \
  --template-metadata-enforcement-type=INSPECT_AND_BLOCK \
  --template-metadata-custom-llm-response-safety-error-code=798 \
  --template-metadata-custom-llm-response-safety-error-message="ahoy! model response blocked by content filter :(" \
  --template-metadata-custom-prompt-safety-error-code=799 \
  --template-metadata-custom-prompt-safety-error-message="ahoy! the request was blocked by ye content filter... so rephrase the prompt and try again!" \
  --template-metadata-ignore-partial-invocation-failures \
  --template-metadata-log-operations \
  --template-metadata-log-sanitize-operations

Vérifier les modèles Model Armor

# list model armor templates
gcloud model-armor templates list --location=${REGION}
# show request filter template details
gcloud model-armor templates describe ${AGW_NAME}-modar-req-template --location=${REGION}
# show response filter template details
gcloud model-armor templates describe ${AGW_NAME}-modar-resp-template --location=${REGION}

Autorisations IAM

Model Armor effectue des appels d'API pour invoquer le service Sensitive Data Protection (SDP). Accordez à l'identité de service Model Armor les autorisations IAM nécessaires pour utiliser les modèles d'inspection et d'anonymisation de la protection des données de service.

Associer une stratégie IAM pour la protection des données sensibles

# grant dlp (sdp) user role to the model armor service identity
gcloud projects add-iam-policy-binding ${PROJ_ID} \
  --member="serviceAccount:service-${PROJ_NO}@gcp-sa-modelarmor.iam.gserviceaccount.com" \
  --role="roles/dlp.user"

Vérifier les autorisations IAM

# show iam policy for all dlp (sdp) roles on project
gcloud projects get-iam-policy ${PROJ_ID} \
  --flatten="bindings[].members" \
  --filter="bindings.role:roles/dlp" \
  --format="table(bindings.role:label=ROLE, bindings.members:label=PRINCIPAL_IDENTITY)"

La partie sur Model Armor est terminée. Passons à la section Autorisation.

6. Autorisation

Autorisations IAM

Pour inspecter le trafic intégré à l'aide de Model Armor, l'agent de service des extensions de service (DEP) nécessite des liaisons IAM explicites (même pour les ressources d'un même projet) :

  • roles/modelarmor.calloutUser et roles/serviceusage.serviceUsageConsumer : accordées sur le projet de passerelle pour autoriser les encadrés d'inspection intégrée.
  • roles/modelarmor.user : accordé sur le projet de modèle pour permettre l'accès aux modèles Model Armor et leur évaluation.

Associer une stratégie IAM pour Model Armor

# grant model armor callout user role to dep (service extension) service agent
gcloud projects add-iam-policy-binding ${PROJ_ID} \
  --member="serviceAccount:service-${PROJ_NO}@gcp-sa-dep.iam.gserviceaccount.com" \
  --role="roles/modelarmor.calloutUser"

# grant service usage consumer role to dep (service extension) service agent
gcloud projects add-iam-policy-binding ${PROJ_ID} \
  --member="serviceAccount:service-${PROJ_NO}@gcp-sa-dep.iam.gserviceaccount.com" \
  --role="roles/serviceusage.serviceUsageConsumer"

# grant model armor user role to dep (service extension) service agent
gcloud projects add-iam-policy-binding ${PROJ_ID} \
  --member="serviceAccount:service-${PROJ_NO}@gcp-sa-dep.iam.gserviceaccount.com" \
  --role="roles/modelarmor.user"

Vérifier les autorisations IAM

# show iam policy on project for dep (service extension) service agent
gcloud projects get-iam-policy ${PROJ_ID} \
  --flatten="bindings[].members" \
  --filter="bindings.members:serviceAccount:service-${PROJ_NO}@gcp-sa-dep.iam.gserviceaccount.com" \
  --format="table(bindings.members:label=PRINCIPAL_IDENTITY, bindings.role:label=ROLE)"

Extension d'autorisation

La configuration de l'extension d'autorisation pour Agent Gateway définit les paramètres d'intégration qui s'appliqueront au trafic de charge utile entrant et sortant. La configuration définit le service de traitement externe (service) qui fait référence à l'API Model Armor régionale et établit un lien vers les modèles de requête et de réponse spécifiques à l'aide du champ de métadonnées model_armor_settings.

Créer une extension d'autorisation

# create authz extension config file (enforced mode)
cat > cfg/${AGW_NAME}-svc-ext-authz-modar.yaml <<EOF
name: ${AGW_NAME}-svc-ext-authz-modar
service: modelarmor.${REGION}.rep.googleapis.com
metadata:
  model_armor_settings: '[
    {
      "request_template_id": "projects/${PROJ_ID}/locations/${REGION}/templates/${AGW_NAME}-modar-req-template",
      "response_template_id": "projects/${PROJ_ID}/locations/${REGION}/templates/${AGW_NAME}-modar-resp-template"
    }
  ]'
failOpen: true
timeout: 5s
EOF

Importer une extension d'autorisation

# import authz extension file
gcloud service-extensions authz-extensions import ${AGW_NAME}-svc-ext-authz-modar \
  --source=cfg/${AGW_NAME}-svc-ext-authz-modar.yaml \
  --location=${REGION}

Valider l'extension d'autorisation

# list authz extensions
gcloud service-extensions authz-extensions list --location=${REGION}
# show authz extension details
gcloud service-extensions authz-extensions describe ${AGW_NAME}-svc-ext-authz-modar \
  --location=${REGION}

Règle d'autorisation

Les règles d'autorisation utilisent des profils de règles pour déterminer le type d'évaluation effectuée. Alors que les profils basés sur les requêtes (REQUEST_AUTHZ) évaluent les en-têtes HTTP, cette configuration utilise un profil d'autorisation basé sur le contenu (CONTENT_AUTHZ) pour lier l'extension Model Armor à la passerelle pour une inspection approfondie de la charge utile.

Créer une règle d'autorisation

# create authz policy config file (attach dry-run authz extension)
cat > cfg/${AGW_NAME}-authz-policy-modar.yaml <<EOF
name: ${AGW_NAME}-authz-policy-modar
target:
  resources:
    - "projects/${PROJ_ID}/locations/${REGION}/agentGateways/${AGW_NAME}"
policyProfile: CONTENT_AUTHZ
action: CUSTOM
customProvider:
  authzExtension:
    resources:
      - "projects/${PROJ_ID}/locations/${REGION}/authzExtensions/${AGW_NAME}-svc-ext-authz-modar"
EOF

Importer une règle d'autorisation

# import authz policy config file (enable authz policy)
gcloud beta network-security authz-policies import ${AGW_NAME}-authz-policy-modar \
  --source=cfg/${AGW_NAME}-authz-policy-modar.yaml \
  --location=${REGION}

Valider la règle d'autorisation

# list authz policies
gcloud beta network-security authz-policies list --location=${REGION}
# show authz policy details
gcloud beta network-security authz-policies describe ${AGW_NAME}-authz-policy-modar \
  --location=${REGION}

La partie concernant l'autorisation est terminée. Passons à la section Codebase.

7. Codebase

Le code de l'agent et les données de fichier utilisés pour cet atelier de programmation sont conservés dans un dépôt GitHub Google Cloud distant. Les étapes suivantes cloneront le dépôt en local, copieront les fichiers nécessaires dans la structure du répertoire de travail actuel, puis supprimeront les fichiers temporaires.

Récupérer des artefacts distants

# clone remote repository to temp local dir
git clone https://github.com/GoogleCloudPlatform/cloud-networking-solutions.git ./temp_agw_cuj_arun_ingress_modar
# copy agent runtime and endpoint definitions to working project dir
cp -r temp_agw_cuj_arun_ingress_modar/codelabs/agw-cuj-arun-ingress-modar/agent-crm ./agent-crm
# remove temporary directory
rm -rf temp_agw_cuj_arun_ingress_modar

Un bucket de stockage pour la préproduction est utilisé par Agent Runtime pour importer, compiler et déployer le code de l'application d'agent empaqueté et ses artefacts de dépendance.

Créer un bucket de stockage pour la préproduction

# create storage bucket
gcloud storage buckets create gs://${STAGING_BUCKET} --location=${REGION}

Vérifier le bucket de stockage

# list storage buckets
gcloud storage buckets list --format="value(storage_url)"

La partie concernant la base de code est terminée. Passons à la section Données client GCS.

Données client

Créez un bucket Cloud Storage pour stocker les données client. L'agent effectue une lecture directe à l'aide de la bibliothèque cliente Google Cloud standard, en appelant le point de terminaison MCP Cloud Storage.

Créer un bucket de stockage pour les données client

# create storage bucket
gcloud storage buckets create gs://${DATA_BUCKET} --location=${REGION}

Vérifier le bucket de stockage

# list storage buckets
gcloud storage buckets list --format="value(storage_url)"

Importer des données clients

# copy local data to bucket
gcloud storage cp -r ./agent-crm/data/* gs://${DATA_BUCKET}/

Valider les données client

# list bucket objects
gcloud storage ls gs://${DATA_BUCKET}/ --long

La partie sur les données client GCS est terminée. Passons à la section sur l'agent ADK.

8. Agent ADK

L'agent ADK agent-crm déployé sur Agent Runtime est configuré avec les paramètres suivants dans le script de déploiement pour s'intégrer à Agent Platform :

  • "identity_type": types.IdentityType.AGENT_IDENTITY pour provisionner une identité principale basée sur SPIFFE unique pour l'agent
  • "client_to_agent_config": {"agent_gateway": "${AGW_URI}"} pour rediriger tout le trafic entrant de l'agent vers le chemin d'évaluation et d'application des règles de la passerelle d'agent

L'URL du serveur MCP mTLS pour le serveur MCP Cloud Storage et le nom du bucket de données sont également transmis à l'agent pour appeler l'outil MCP GCS via une connexion sécurisée.

Déployer l'agent

# deploy agent
uv --directory agent-crm run python3 deploy_agent.py \
  --project=${PROJ_ID} \
  --region=${REGION} \
  --src-dir=./agent \
  --staging-bucket=${STAGING_BUCKET} \
  --display-name="${RE_AGENT_NAME}" \
  --description="agent for customer data" \
  --mcp-server-url="${MCP_URL}" \
  --data-bucket=${DATA_BUCKET} \
  --enable-telemetry \
  --enable-agent-identity \
  --agent-gateway-ingress=${AGW_URI} \
  --allow-token-sharing

Vérifier le déploiement

Récupérer les constantes vitales du déploiement

# fetch agent runtime (reasoning engine) resource id
export RE_ENGINE_ID=$(curl -s -X GET "https://${REGION}-aiplatform.googleapis.com/v1/projects/${PROJ_ID}/locations/${REGION}/reasoningEngines" \
  -H "Authorization: Bearer $(gcloud auth application-default print-access-token)" \
  | jq -r --arg name "${RE_AGENT_NAME}" '.reasoningEngines[] | select(.displayName==$name) | .name | split("/") | last')
echo ${RE_ENGINE_ID}
# fetch agent runtime (reasoning engine) agent identity
export RE_AGENT_IDENTITY=$(gcloud agent-registry agents list \
  --project=${PROJ_ID} --location=${REGION} --filter="displayName=${RE_AGENT_NAME}" \
  --format="value(attributes.'agentregistry.googleapis.com/system/RuntimeIdentity'.principal)")
echo ${RE_AGENT_IDENTITY}

Vérifier la configuration de la passerelle

# show agent runtime config details (gateway config)
curl -s -X GET "https://${REGION}-aiplatform.googleapis.com/v1/projects/${PROJ_ID}/locations/${REGION}/reasoningEngines/${RE_ENGINE_ID}" \
  -H "Authorization: Bearer $(gcloud auth application-default print-access-token)" \
  | jq '{displayName: .displayName, name: .name, effectiveIdentity: .spec.effectiveIdentity, agentGatewayConfig: .spec.deploymentSpec.agentGatewayConfig}'

Autorisations IAM

Associer des stratégies IAM à l'identité de l'agent

# grant mcp tool user role to agent set (all agent runtime agents in project)
gcloud projects add-iam-policy-binding ${PROJ_ID} \
  --member="${RE_AGENT_ID_SET}" \
  --role="roles/mcp.toolUser"
# grant storage object viewer role to agent identity
gcloud projects add-iam-policy-binding ${PROJ_ID} \
  --member="${RE_AGENT_IDENTITY}" \
  --role="roles/storage.objectViewer"

# grant aiplatform user role to agent identity
gcloud projects add-iam-policy-binding ${PROJ_ID} \
  --member="${RE_AGENT_IDENTITY}" \
  --role="roles/aiplatform.user"

# grant cloudtrace agent role to agent identity
gcloud projects add-iam-policy-binding ${PROJ_ID} \
  --member="${RE_AGENT_IDENTITY}" \
  --role="roles/cloudtrace.agent"

# grant cloud monitoring metric writer role to agent identity
gcloud projects add-iam-policy-binding ${PROJ_ID} \
  --member="${RE_AGENT_IDENTITY}" \
  --role="roles/monitoring.metricWriter"
# grant cloud logging log writer role to agent identity
gcloud projects add-iam-policy-binding ${PROJ_ID} \
  --member="${RE_AGENT_IDENTITY}" \
  --role="roles/logging.logWriter"

# grant telemetry writer role to agent identity
gcloud projects add-iam-policy-binding ${PROJ_ID} \
  --member="${RE_AGENT_IDENTITY}" \
  --role="roles/telemetry.writer"

# grant service usage consumer role to agent identity
gcloud projects add-iam-policy-binding ${PROJ_ID} \
  --member="${RE_AGENT_IDENTITY}" \
  --role="roles/serviceusage.serviceUsageConsumer"

# grant browser role to agent identity
gcloud projects add-iam-policy-binding ${PROJ_ID} \
  --member="${RE_AGENT_IDENTITY}" \
  --role="roles/browser"

Vérifier les autorisations IAM

# show agent identity roles on project
gcloud projects get-iam-policy ${PROJ_ID} \
  --flatten="bindings[].members" \
  --filter="bindings.members:${RE_AGENT_IDENTITY}" \
  --format="table(bindings.members.sub('^.*locations/', 'principal://agents.[...]/locations/'):label=PRINCIPAL_IDENTITY, bindings.role:label=ROLE)"
# show agent set roles on project
gcloud projects get-iam-policy ${PROJ_ID} \
  --flatten="bindings[].members" \
  --filter="bindings.members:${RE_AGENT_ID_SET}" \
  --format="table(bindings.members.sub('^.*platformContainer/', 'principalSet://agents.[...]/'):label=PRINCIPAL_IDENTITY, bindings.role:label=ROLE)"

La partie concernant l'agent ADK est terminée. Passons à la section Test.

9. Test

Envoyer des requêtes depuis la CLI

Tester un prompt sûr

# post query to agent streamQuery
curl --no-buffer -s -X POST "https://${REGION}-aiplatform.googleapis.com/v1beta1/projects/${PROJ_ID}/locations/${REGION}/reasoningEngines/${RE_ENGINE_ID}:streamQuery" \
  -H "Authorization: Bearer $(gcloud auth application-default print-access-token)" \
  -H "Content-Type: application/json" -H "X-Goog-User-Project: ${PROJ_ID}" \
  -d @- <<EOF | jq -r --unbuffered 'if type == "array" then .[] else . end | select(.content.parts != null) | .content.parts[].text // empty'
{
  "input": {
    "message": "what are the names of our west customers?",
    "user_id": "test-user"
  }
}
EOF

Vous devriez obtenir une réponse semblable à celle-ci : "Nos clients de l'Ouest sont Bob Johnson et Alice Brown."

Tester un déclencheur de masquage

# post query to agent streamQuery
curl --no-buffer -s -X POST "https://${REGION}-aiplatform.googleapis.com/v1beta1/projects/${PROJ_ID}/locations/${REGION}/reasoningEngines/${RE_ENGINE_ID}:streamQuery" \
  -H "Authorization: Bearer $(gcloud auth application-default print-access-token)" \
  -H "Content-Type: application/json" -H "X-Goog-User-Project: ${PROJ_ID}" \
  -d @- <<EOF | jq -r --unbuffered 'if type == "array" then .[] else . end | select(.content.parts != null) | .content.parts[].text // empty'
{
  "input": {
    "message": "what are ssn's for bob johnson and alice brown?",
    "user_id": "test-user"
  }
}
EOF

Tester un autre prompt sûr

# post query to agent streamQuery
curl --no-buffer -s -X POST "https://${REGION}-aiplatform.googleapis.com/v1/projects/${PROJ_ID}/locations/${REGION}/reasoningEngines/${RE_ENGINE_ID}:streamQuery" \
  -H "Authorization: Bearer $(gcloud auth application-default print-access-token)" \
  -H "Content-Type: application/json" -H "X-Goog-User-Project: ${PROJ_ID}" \
  -d @- <<EOF | jq -r --unbuffered 'if type == "array" then .[] else . end | select(.content.parts != null) | .content.parts[].text // empty'
{
  "input": {
    "message": "what are bob johnson's and alice brown's email addresses?",
    "user_id": "test-user"
  }
}
EOF

Journaux d'audit

Afficher les journaux de trace

Lorsque la télémétrie est activée, Agent Runtime diffuse des événements structurés représentant les requêtes utilisateur, les paramètres d'outils, les flux d'exécution et les sorties de choix de modèle.

# show agent runtime (reasoning engine) telemetry and trace logs
gcloud logging read \
  "logName:\"projects/${PROJ_ID}/logs/aiplatform.googleapis.com%2Freasoning_engine_stdout\" AND labels.managed-by=\"reasoning-engine\"" \
  --project=${PROJ_ID} \
  --limit=15 \
  --format="table(
    timestamp.date(format=\"%I:%M:%S %p\", tz=LOCAL):label=TIME,
    trace.basename().sub('^(.{8}).*$', '\\1'):label=TRACE_ID,
    labels.\"event.name\".scope(-1):label=EVENT,
    jsonPayload.content.role:label=ROLE,
    jsonPayload.content.parts[0].text:label=TEXT_CONTENT,
    jsonPayload.content.parts[0].function_call.name:label=TOOL_CALL
  )"

TRACE_ID regroupe la requête de l'utilisateur, les appels d'outils intermédiaires et les décisions du modèle dans une seule chronologie :

TIME         TRACE_ID  EVENT                  ROLE   TEXT_CONTENT                                     TOOL_CALL
HH:MM:SS PM  3070a1fd  gen_ai.choice          model  Bob Johnson's SSN is 219-45-7895.
                                                     Alice Brown's SSN is 219-45-7896.
HH:MM:SS PM  3070a1fd  gen_ai.user.message    user
HH:MM:SS PM  3070a1fd  gen_ai.user.message    model                                                   read_customer_file
HH:MM:SS PM  3070a1fd  gen_ai.user.message    user
HH:MM:SS PM  3070a1fd  gen_ai.user.message    model                                                   read_customer_file
HH:MM:SS PM  3070a1fd  gen_ai.user.message    user
HH:MM:SS PM  3070a1fd  gen_ai.user.message    model                                                   list_customer_files
HH:MM:SS PM  3070a1fd  gen_ai.user.message    user   what are ssn's for bob johnson and alice brown?
HH:MM:SS PM  3070a1fd  gen_ai.system.message
HH:MM:SS PM  3070a1fd  gen_ai.choice          model                                                   read_customer_file

Afficher les journaux de nettoyage Model Armor

Ces journaux affichent les menaces et la désinfection en temps réel et bidirectionnelles effectuées par Model Armor lorsque le trafic transite par l'Agent Gateway.

# show model armor logs
gcloud logging read \
  "logName:\"projects/${PROJ_ID}/logs/modelarmor.googleapis.com%2Fsanitize_operations\"" \
  --project=${PROJ_ID} \
  --limit=50 \
  --format="table(
    timestamp.date(format=\"%I:%M:%S %p\", tz=LOCAL):label=TIME,
    jsonPayload.sanitizationResult.sanitizationVerdict:label=VERDICT,
    jsonPayload.sanitizationInput.byteItem.byteData.decode(base64).decode(utf-8).sub('\n', ' \\\\\\\\n ').trailoff(123):label=INPUT_DATA
  )"

Notez l'entrée de journal pour la requête nettoyée et bloquée.

TIME         VERDICT                                 INPUT_DATA
HH:MM:SS PM  MODEL_ARMOR_SANITIZATION_VERDICT_ALLOW  Bob Johnson's email address is bob.j@example.com. \n Alice Brown's email address is alice.b...
HH:MM:SS PM  MODEL_ARMOR_SANITIZATION_VERDICT_ALLOW  what are bob johnson's and alice brown's email addresses?
HH:MM:SS PM  MODEL_ARMOR_SANITIZATION_VERDICT_BLOCK  6��
HH:MM:SS PM  MODEL_ARMOR_SANITIZATION_VERDICT_ALLOW  what are ssn's for bob johnson and alice brown?
HH:MM:SS PM  MODEL_ARMOR_SANITIZATION_VERDICT_ALLOW  Our west customers are: Bob Johnson and Alice Brown.
HH:MM:SS PM  MODEL_ARMOR_SANITIZATION_VERDICT_ALLOW  what are the names of our west customers?

La partie "Test" est terminée. Passons à la section Nettoyage.

10. Nettoyage

# remove agent iam bindings
gcloud -q projects remove-iam-policy-binding ${PROJ_ID} --member="${RE_AGENT_IDENTITY}" --role="roles/storage.objectViewer"
gcloud -q projects remove-iam-policy-binding ${PROJ_ID} --member="${RE_AGENT_IDENTITY}" --role="roles/aiplatform.user"
gcloud -q projects remove-iam-policy-binding ${PROJ_ID} --member="${RE_AGENT_IDENTITY}" --role="roles/cloudtrace.agent"
gcloud -q projects remove-iam-policy-binding ${PROJ_ID} --member="${RE_AGENT_IDENTITY}" --role="roles/monitoring.metricWriter"

# next
# remove more agent and agent set iam bindings
gcloud -q projects remove-iam-policy-binding ${PROJ_ID} --member="${RE_AGENT_IDENTITY}" --role="roles/logging.logWriter"
gcloud -q projects remove-iam-policy-binding ${PROJ_ID} --member="${RE_AGENT_IDENTITY}" --role="roles/telemetry.writer"
gcloud -q projects remove-iam-policy-binding ${PROJ_ID} --member="${RE_AGENT_IDENTITY}" --role="roles/serviceusage.serviceUsageConsumer"
gcloud -q projects remove-iam-policy-binding ${PROJ_ID} --member="${RE_AGENT_IDENTITY}" --role="roles/browser"

# next
# remove rest of iam bindings
gcloud -q projects remove-iam-policy-binding ${PROJ_ID} --member="${RE_AGENT_ID_SET}" --role="roles/mcp.toolUser"
gcloud -q projects remove-iam-policy-binding ${PROJ_ID} --member="serviceAccount:service-${PROJ_NO}@gcp-sa-modelarmor.iam.gserviceaccount.com" --role="roles/dlp.user"

# next
# delete agent runtime (reasoning engine) agent
curl -s -X DELETE "https://${REGION}-aiplatform.googleapis.com/v1/projects/${PROJ_ID}/locations/${REGION}/reasoningEngines/${RE_ENGINE_ID}?force=true" \
  -H "Authorization: Bearer $(gcloud auth application-default print-access-token)" \
  -H "Content-Type: application/json"

# next
# delete storage
gcloud -q storage rm --recursive gs://${STAGING_BUCKET}
gcloud -q storage rm --recursive gs://${DATA_BUCKET}

# next
# delete authz resources
gcloud -q beta network-security authz-policies delete ${AGW_NAME}-authz-policy-modar --location=${REGION}

gcloud -q beta service-extensions authz-extensions delete ${AGW_NAME}-svc-ext-authz-modar --location=${REGION} --async

# next
# remove dep (service extensions) service agent iam bindings
gcloud -q projects remove-iam-policy-binding ${PROJ_ID} \
  --member="serviceAccount:service-${PROJ_NO}@gcp-sa-dep.iam.gserviceaccount.com" \
  --role="roles/modelarmor.calloutUser"

gcloud -q projects remove-iam-policy-binding ${PROJ_ID} \
  --member="serviceAccount:service-${PROJ_NO}@gcp-sa-dep.iam.gserviceaccount.com" \
  --role="roles/serviceusage.serviceUsageConsumer"

gcloud -q projects remove-iam-policy-binding ${PROJ_ID} \
  --member="serviceAccount:service-${PROJ_NO}@gcp-sa-dep.iam.gserviceaccount.com" \
  --role="roles/modelarmor.user"

# next
# delete model armor templates
gcloud -q model-armor templates delete ${AGW_NAME}-modar-resp-template --location=${REGION}
gcloud -q model-armor templates delete ${AGW_NAME}-modar-req-template --location=${REGION}

# unset model armor api endpoint override
gcloud config unset api_endpoint_overrides/modelarmor

# next
# delete sdp (dlp) templates
curl -fsS -X DELETE "https://dlp.googleapis.com/v2/projects/${PROJ_ID}/locations/${REGION}/deidentifyTemplates/agw-ssn-redaction-template" \
  -H "Authorization: Bearer $(gcloud auth application-default print-access-token)" \
  -H "x-goog-user-project: ${PROJ_ID}"

curl -fsS -X DELETE "https://dlp.googleapis.com/v2/projects/${PROJ_ID}/locations/${REGION}/inspectTemplates/agw-ssn-inspect-template" \
  -H "Authorization: Bearer $(gcloud auth application-default print-access-token)" \
  -H "x-goog-user-project: ${PROJ_ID}"

# next
# delete agent gateway ingress
gcloud -q network-services agent-gateways delete ${AGW_NAME} --location=${REGION} --async

# end

La partie sur le nettoyage est terminée. Passons à la section Conclusion.

11. Conclusion

Félicitations ! Vous avez déployé Agent Gateway et contrôlé le trafic entrant vers un agent d'IA.

cosmopup

Cosmopup pense que les ateliers de programmation sont super !

Et ensuite ?

N'hésitez pas à nous faire part de vos commentaires, questions ou corrections en utilisant ce formulaire.

Merci !