Lesson 1 — Mise en œuvre de la note

Gemini Enterprise : socle de sécurité (hérité du projet 06)

38 min read

This content is not translated yet. Showing the original version.

Ce document décrit le socle (couches 1 à 6). Les mesures ajoutées pour la note de sécurité Acme sont dans le README du projet 08.

Pour qui : équipes sécurité, architecture cloud et data de Acme. Objectif : déployer Gemini Enterprise (l'assistant IA des collaborateurs, connecté aux outils de l'entreprise) avec un socle de sécurité complet, expliqué et reproductible. Pour chaque mesure, le guide dit ce qu'elle fait, pourquoi, comment elle est configurée (code commenté) et ce qu'elle apporte.

Le guide est volontairement pédagogique : on peut le lire sans connaître Terraform ni Google Cloud. Le code est dans terraform/. Chaque fichier correspond à une couche décrite ici. Vous débutez en Terraform ? Chaque fichier .tf a sa fiche explicative ligne par ligne dans terraform/docs/, en commençant par le cours de base.


Sommaire

  1. En une page
  2. Les notions à connaître
  3. Architecture
  4. Les risques que l'on traite
  5. Couche 1 : identité et accès
  6. Couche 2 : protection des données (CMEK, secrets, conservation)
  7. Couche 3 : connecteurs (Google, Microsoft, GitHub, Databricks…)
  8. Couche 4 : filtrage IA avec Model Armor
  9. Couche 5 : exposition et fonctionnalités
  10. Couche 6 : audit et détection
  11. Options avancées : Agent Gateway, VPC Service Controls
  12. Déploiement pas à pas
  13. Recette : prouver que la sécurité fonctionne
  14. Exploitation au quotidien
  15. Compromis assumés
  16. Vérifié / à valider en recette
  17. Dépannage
  18. Sources

1. En une page

Gemini Enterprise permet à chaque collaborateur de poser une question en langage naturel (« quelles sont les ventes running du T3 ? », « résume la note du comité produit ») et d'obtenir une réponse construite à partir des outils de l'entreprise : Google Drive, SharePoint, Teams, GitHub, Jira, BigQuery…

Le risque est proportionnel à cette puissance : un assistant qui voit tout peut tout divulguer. Le socle répond avec 6 couches, toutes écrites en Terraform :

CoucheEn une phraseBénéfice pour Acme
1. Identité et accèsConnexion avec le compte d'entreprise ; accès par groupes ; la sécurité seule touche aux garde-fousUn départ coupe l'accès immédiatement ; pas de « super-admin » qui peut tout désactiver
2. Protection des donnéesDonnées chiffrées avec les clés de Acme (CMEK) ; secrets dans un coffre ; conversations conservées 30 joursMaîtrise du chiffrement (révocable à tout moment) ; aucun mot de passe dans le code ; minimisation RGPD
3. ConnecteursPar défaut fédérés : l'assistant interroge la source en direct, avec les droits de l'utilisateurChacun ne voit que ce qu'il voit déjà ; aucune copie des données chez Google
4. Filtrage IAModel Armor analyse chaque question et chaque réponse : injection, contenus dangereux, liens malveillants, NIR, IBAN, carte bancaire, secretsProtection contre la manipulation de l'IA et contre les fuites de données personnelles
5. ExpositionDonnées en UE ; pas de recherche web publique ; partages contrôlésConformité RGPD et contrats ; pas de fuite involontaire vers Internet
6. Audit et détectionJournaux d'audit et d'usage rangés en UE, lisibles par la sécurité seule ; alertes (garde-fou modifié, pic de blocages)Preuves pour l'audit ; détection des attaques sans exposer les questions des collaborateurs

Le tout est reproductible (même code en recette et en production), relu (chaque changement passe par une revue de code) et traçable (l'historique Git est une preuve d'audit).


2. Les notions à connaître

TermeExplication simple
Gemini EnterpriseL'application web d'assistant IA pour les collaborateurs (anciennement « Agentspace »). Techniquement : l'API Discovery Engine, d'où les ressources Terraform google_discovery_engine_*
App (engine)Une instance de Gemini Enterprise : ses sources de données, son assistant, ses réglages
AssistantLe « cerveau » conversationnel de l'app : instructions, langue, filtrage Model Armor
ConnecteurLe branchement entre l'app et un outil (Drive, SharePoint, GitHub…). Il crée un ou plusieurs data stores
Mode fédéréL'assistant interroge l'outil en direct, avec le jeton de l'utilisateur. Rien n'est copié ; les droits de l'outil s'appliquent
Mode ingestionLes données sont copiées et indexées chez Google (plus rapide, meilleure qualité). Les droits sont synchronisés pour les connecteurs tiers, pas pour BigQuery
Service agentUn compte technique géré par Google qui agit pour un service dans le projet (ex. service-<n°>@gcp-sa-discoveryengine… pour Gemini Enterprise). On lui donne uniquement les droits nécessaires
Cloud KMS / CMEKLe coffre de clés de chiffrement. CMEK (Customer-Managed Encryption Keys) : les données sont chiffrées avec une clé dont Acme a le contrôle. Désactiver la clé rend les données illisibles
Secret ManagerLe coffre à secrets (identifiants OAuth des connecteurs). Le code ne contient que le nom du secret, jamais sa valeur
Model ArmorUn « pare-feu » pour le texte échangé avec l'IA : il analyse questions et réponses et bloque ce qui est dangereux
Template Model ArmorUn jeu de filtres nommé (ex. ge-prompt-shield) appliqué quelque part
Floor setting (plancher)Le minimum de sécurité du projet : aucun template ne peut être moins strict
DLP / Sensitive Data ProtectionLe service de détection de données sensibles (NIR, IBAN…), utilisé par Model Armor
Agent RegistryLe catalogue des agents, serveurs MCP et API de l'entreprise
Agent GatewayLe point de passage contrôlé (pare-feu applicatif) des appels des agents vers les outils et API
VPC Service ControlsUn périmètre autour des API Google du projet : empêche l'exfiltration de données, même avec des identifiants volés
IP statiques de connecteurLes adresses fixes depuis lesquelles Gemini Enterprise appelle un SaaS : on les autorise côté SaaS, tout le reste est refusé

3. Architecture

                          ┌───────────── Couche 1 ─────────────┐
 Collaborateurs ──SSO──▶  │ Identité Google (ou Entra ID)      │
 (groupes)                │ rôles par groupe · sécurité séparée│
                          └────────────────┬───────────────────┘
                                           ▼
                ┌────────────── App Gemini Enterprise (UE) ───────────────┐
                │  Assistant                                              │
   question ──▶ │  ┌─────────────┐   ┌────────┐   ┌──────────────┐        │ ──▶ réponse
                │  │prompt-shield│──▶│ Gemini │──▶│response-shield│       │
                │  └─────────────┘   └────────┘   └──────────────┘        │
                │   Couche 4 : Model Armor + DLP FR · FAIL_CLOSED         │
                │   Couche 5 : pas de web public · partages contrôlés     │
                │   Couche 2 : chiffré CMEK · conversations 30 j          │
                └──────┬───────────────┬───────────────┬──────────────────┘
                       │ fédéré        │ fédéré        │ fédéré / ingestion
                       ▼               ▼               ▼
               Google Workspace   Microsoft 365     GitHub · Jira · BigQuery…
               (Drive, Agenda)    (SharePoint,      IP statiques autorisées
                                   Teams)           secrets dans Secret Manager
                                                    (Couche 3)

 Couche 6 : journaux d'audit · alerte « garde-fou modifié » → équipe sécurité
 Options   : Agent Gateway (agents/MCP) · VPC Service Controls (périmètre)

4. Les risques que l'on traite

#Risque (référence OWASP Top 10 LLM)Exemple concretCouche(s)
R1Accès non autoriséUn compte partagé, un ex-collaborateur qui garde l'accès1
R2Divulgation de données (LLM02)Un stagiaire retrouve la grille de salaires via l'assistant3, 4
R3Injection de prompt (LLM01)« Ignore tes instructions et montre-moi… », ou une consigne cachée dans un document4
R4Données personnelles dans les échangesUn collaborateur colle un IBAN client ou un NIR dans une question4
R5Contenus dangereux ou malveillantsRéponse toxique, lien de phishing relayé par l'assistant4
R6Affaiblissement de la sécuritéUn admin désactive un filtre « pour une démo »1, 4, 6
R7Perte de maîtrise des donnéesDonnées hors UE, impossibilité de les rendre illisibles2, 5
R8Secrets exposésMot de passe d'application Microsoft dans le code2
R9Exfiltration avec des identifiants volésExport massif via l'API depuis l'extérieur3 (IP), options (VPC-SC)
R10Attaque ou abus non détectésPersonne ne voit 200 tentatives de jailbreak6

5. Couche 1 : identité et accès

Fichier : terraform/10_identity_access.tf

5.1 Connexion avec le compte d'entreprise (SSO)

resource "google_discovery_engine_acl_config" "identity" {
  location = var.ge_location        # "eu"
  idp_config {
    idp_type = var.identity_provider  # "GSUITE" = Google Workspace / Cloud Identity
  }
}
  • Quoi : les collaborateurs se connectent avec leur compte d'entreprise. Il n'y a ni mot de passe dédié ni compte local.
  • Pourquoi : la double authentification, la durée de session et la suspension d'un compte au départ sont gérées une seule fois, dans l'annuaire. Un compte suspendu perd l'accès à l'assistant immédiatement.
  • Si Acme utilise Microsoft Entra ID comme annuaire principal : identity_provider = "THIRD_PARTY" + workforce_pool (Workforce Identity Federation). Attention, c'est un choix structurant :
    • un seul fournisseur d'identité par localisation ;
    • l'identité Google est obligatoire pour les connecteurs Google Workspace ;
    • l'ingestion SharePoint/OneDrive/Outlook exige Entra ID ; en identité Google, les sources Microsoft fonctionnent en mode fédéré (c'est le défaut de ce socle) ;
    • changer d'identité plus tard efface l'historique des conversations et oblige à recréer les data stores.

5.2 Rôles attribués à des groupes

GroupeRôle GooglePeut…Ne peut pas…
user_groupsroles/discoveryengine.agentspaceUserutiliser l'appadministrer
admin_groupsroles/discoveryengine.agentspaceAdmingérer l'app, les connecteurs, les agentsmodifier Model Armor, les clés, lire les journaux privés
viewer_groupsroles/discoveryengine.agentspaceViewerconsulter la configuration et les statistiquesmodifier
security_groupsroles/modelarmor.admin, roles/cloudkms.admin, roles/logging.privateLogViewergérer les filtres, les clés, lire les journaux sensibles—
  • Pourquoi des groupes : les droits nominatifs ne sont jamais nettoyés. Avec des groupes, les arrivées et départs se gèrent dans l'annuaire, et une revue d'accès consiste à relire 4 groupes.
  • Pourquoi la séparation des rôles : celui qui exploite l'outil ne doit pas pouvoir désactiver sa propre sécurité. Les admins Gemini Enterprise n'ont aucun droit modelarmor.* ni cloudkms.*. C'est le principe des « quatre yeux ».
  • Bénéfice : moindre privilège démontrable, revue d'accès simple, risque R6 neutralisé.

Pour aller plus loin : l'accès peut être donné par app plutôt que par projet (google_discovery_engine_search_engine_iam_member), avec un rôle projet minimal personnalisé. C'est utile si plusieurs apps cohabitent dans le projet (ex. une app RH réservée aux RH).


6. Couche 2 : protection des données

Fichier : terraform/20_data_protection.tf

6.1 CMEK : chiffrement avec les clés de Acme

Quoi : tout ce que Gemini Enterprise stocke (index des connecteurs en ingestion, conversations, données de l'app) est chiffré avec une clé Cloud KMS appartenant au projet Acme, et non avec une clé gérée par Google.

Pourquoi :

  • Contrôle : Acme peut désactiver la clé à tout moment. En 15 minutes, l'app ne peut plus lire ni servir les données (« crypto-shredding »). C'est un interrupteur d'urgence en cas d'incident majeur ou de fin de contrat.
  • Conformité : exigence fréquente des politiques de sécurité et des contrats pour les données sensibles.
  • Traçabilité : chaque utilisation de la clé est visible dans Cloud KMS.

Comment (et pourquoi chaque ligne) :

resource "google_kms_key_ring" "ge" {
  name     = "gemini-enterprise"
  location = local.kms_location   # "europe" : multi-région KMS qui correspond à l'app "eu"
}

resource "google_kms_crypto_key" "ge" {
  name     = "gemini-enterprise-data"
  key_ring = google_kms_key_ring.ge[0].id
  purpose  = "ENCRYPT_DECRYPT"
  # PAS de rotation_period : Gemini Enterprise exige une rotation MANUELLE.
  lifecycle { prevent_destroy = true }   # Terraform refuse de détruire la clé (voir plus bas)
}
Règle GoogleComment le socle la respecte
CMEK uniquement pour les apps eu ou us, pas globalge_location est validé : eu ou us seulement
Clé multi-région europe pour une app eulocal.kms_location = "europe"
Rotation manuellepas de rotation_period
Clés régionales europe-west1, europe-west4, europe-north1 pour les connecteurs tiers en ingestiongoogle_kms_crypto_key.regional (3 clés) + single_region_keys
Le service agent Gemini Enterprise et le service agent Cloud Storage (bucket temporaire d'import) doivent pouvoir utiliser la clégoogle_kms_crypto_key_iam_member.key_users : ces deux comptes seulement, rôle cryptoKeyEncrypterDecrypter
La clé doit être enregistrée AVANT de créer l'app et les connecteurs, sinon ils restent chiffrés par Googlegoogle_discovery_engine_cmek_config + depends_on sur l'app et les connecteurs
resource "google_discovery_engine_cmek_config" "ge" {
  location       = "eu"
  cmek_config_id = "default-cmek"
  kms_key        = google_kms_crypto_key.ge[0].id
  set_default    = true                         # appliquée par défaut à tout ce qui sera créé
  dynamic "single_region_keys" { ... }          # les 3 clés régionales
}

Pourquoi prevent_destroy : détruire la clé rend définitivement illisibles toutes les données de l'app. Un terraform destroy lancé par erreur ne doit jamais pouvoir le faire. Pour vraiment révoquer la clé, la procédure est volontaire et documentée (§14.3).

Ce que CMEK ne couvre pas : en mode fédéré, rien n'est stocké chez Google, il n'y a donc rien à chiffrer (c'est encore mieux). Les sources elles-mêmes (BigQuery, SharePoint…) ont leur propre chiffrement. Model Armor est un service sans stockage (il analyse et oublie).

6.2 Secrets des connecteurs : jamais dans le code

data "google_secret_manager_secret" "connector" { secret_id = "sharepoint-client-secret" }  # le NOM seulement

resource "google_secret_manager_secret_iam_member" "ge_reads" {
  secret_id = data.google_secret_manager_secret.connector[each.value].id
  role      = "roles/secretmanager.secretAccessor"
  member    = local.ge_sa          # Gemini Enterprise seulement, secret par secret
}

Dans le connecteur, le paramètre vaut projects/<n°>/secrets/sharepoint-client-secret/versions/latest : une référence, que Gemini Enterprise résout au moment de l'appel.

  • Pourquoi : un secret dans le code ou dans l'état Terraform finit tôt ou tard dans un dépôt Git, un log de CI ou une capture d'écran. Ici, l'équipe sécurité crée les secrets (valeurs), Terraform ne connaît que leur nom.
  • Rotation : on ajoute une nouvelle version du secret ; versions/latest la prend automatiquement, sans redéploiement.
  • Bénéfice : risque R8 neutralisé ; chaque lecture de secret est journalisée (audit Secret Manager activé, §10).

6.3 Minimisation : conservation des conversations

sessionConfig = { sessionTtl = { days = var.session_ttl_days } }   # 30 jours (Google : 60 par défaut)
  • Pourquoi : principe de minimisation du RGPD. Les conversations contiennent souvent des informations internes ; on ne les garde pas plus que nécessaire. La durée est à valider avec le DPO de Acme.
  • Comment : ce réglage n'existe pas encore dans le provider Terraform. Il est appliqué par un appel à l'API (terraform_data.engine_settings dans 50_app.tf), relancé automatiquement si la valeur change.

7. Couche 3 : connecteurs

Fichier : terraform/40_connectors.tf. Configuration dans terraform.tfvars.

7.1 Le principe : un modèle générique

Toutes les sources se déclarent de la même façon. On ajoute une entrée dans connectors, puis terraform apply :

connectors = {
  sharepoint = {
    data_source   = "sharepoint"                 # le type de source
    display_name  = "SharePoint"
    modes         = ["FEDERATED"]                # défaut : pas de copie
    params        = { instance_uri = "https://acme.sharepoint.com", tenant_id = "..." }
    secret_params = { client_id = "sharepoint-client-id", client_secret = "sharepoint-client-secret" }  # NOMS de secrets
    entities      = [{ name = "file" }]
    static_ip     = true                         # IP sortantes fixes
  }
}

Terraform transforme chaque entrée en google_discovery_engine_data_connector, résout les secrets, rattache les data stores créés à l'app, et applique la clé CMEK si la source est en ingestion.

7.2 Fédéré ou ingestion : la décision de sécurité la plus importante

Fédéré (défaut du socle)Ingestion
Où sont les données ?Restent dans la source ; interrogées en directCopiées et indexées chez Google (UE, CMEK)
Droits d'accèsCeux de la source, avec le jeton de l'utilisateurSynchronisés (connecteurs tiers) ; perdus pour BigQuery
Qualité / vitesseBonne ; moins bonne sur les PDF complexesMeilleure (index, recherche sémantique)
Usage recommandéWorkspace, Microsoft 365, GitHub, données sensiblesBases documentaires volumineuses à faibles enjeux de confidentialité

Pourquoi « fédéré » par défaut : c'est la seule façon de garantir par construction que chacun ne voit que ce qu'il voit déjà, et qu'aucune copie n'existe hors de la source. On passe une source en ingestion au cas par cas, de façon justifiée.

⚠️ En mode fédéré, la question (éventuellement reformulée par l'IA) est envoyée à l'éditeur de la source (Microsoft, GitHub…). Elle relève alors des conditions de cet éditeur.

7.3 Les sources de Acme

Sourcedata_sourceModeAuthentificationPoints de sécurité
Google Drive / Agendagoogle_drive, google_calendarFédéréApplication OAuth gérée par Google, aucun secretDroits Drive natifs. Si les applications OAuth sont restreintes, un super-admin Workspace doit marquer l'app Google comme Trusted
Gmailgoogle_mailFédéréidemDésactivé par défaut (minimisation) : à activer sur décision explicite
SharePoint / OneDrivesharepoint, onedriveFédéréApplication Entra ID mono-tenant, consentement admin, secret dans Secret ManagerDroits délégués en lecture (Sites.Search.All, AllSites.Read ou mieux Sites.Selected)
OutlookoutlookFédéréEntra IDDroits délégués Mail.Read, Calendars.Read
TeamsteamsFédéré uniquementEntra IDDroits délégués de lecture des canaux/chats
GitHubgithubFédéré (+ actions optionnelles)GitHub App (pas de jeton personnel), installée sur l'organisationPermissions minimales de l'app ; « expire user tokens » activé
Jira / Confluencejira, confluenceFédéré ou ingestionOAuth AtlassianIP statiques recommandées
BigQuerybigquery (ingestion) / bigquery_mcp (fédéré, preview)voir ci-dessousService agentEn ingestion, les droits BigQuery sont perdus : n'indexer qu'un dataset curé
Autresservicenow, salesforce, slack, snowflake, box, notion, workday…selon la sourceOAuth, secretsMême modèle

BigQuery : en ingestion, tout utilisateur de l'app voit toutes les lignes indexées. Règle du socle : n'indexer qu'un dataset dédié et curé (aucune donnée personnelle), ou préférer le mode fédéré bigquery_mcp (preview), qui conserve les droits et la sécurité ligne à ligne de BigQuery.

7.4 Databricks

Il n'existe pas de connecteur Databricks natif dans Gemini Enterprise (octobre 2026). Trois options, par ordre de préférence sécurité :

OptionPrincipeSécurité
A. Serveur MCP Databricks (data store « Custom MCP Server »)Gemini Enterprise appelle le serveur MCP managé de Databricks (Unity Catalog, Genie) en OAuthLes droits Unity Catalog s'appliquent ; configuration console ; exige d'autoriser custom_mcp et le domaine Databricks dans les politiques d'organisation
B. Agent dédié (ADK/A2A sur Agent Runtime) enregistré dans Gemini EnterpriseUn agent interroge Databricks avec son identité propreContrôle fin ; à filtrer lui-même avec Model Armor (Gemini Enterprise ne filtre pas les agents personnalisés) ; idéalement derrière Agent Gateway
C. Publication vers BigQueryUn pipeline publie un jeu de données curé dans BigQuerySimple ; mêmes règles que BigQuery ingestion

7.5 IP statiques : n'autoriser que Gemini Enterprise côté SaaS

static_ip = true : Gemini Enterprise appelle la source depuis des adresses IP fixes. Côté SaaS (Microsoft, GitHub, Atlassian…), on autorise ces IP uniquement.

  • Récupérer les IP : terraform output connector_static_ips.
  • Tant qu'elles ne sont pas autorisées, terraform output connector_states affiche ALLOWLIST_STATIC_IP et la synchronisation attend.
  • ⚠️ Immuable : on ne peut pas l'activer après coup sans recréer le connecteur. Le socle l'active d'emblée pour les sources tierces.

7.6 Ajouter une source (procédure)

  1. Côté source : créer l'application OAuth (Entra ID, GitHub App, Atlassian…) avec les permissions minimales en lecture et l'URL de redirection https://vertexaisearch.cloud.google.com/oauth-redirect.
  2. Sécurité : créer les secrets dans Secret Manager (UE) :
    gcloud secrets create github-client-secret --replication-policy=user-managed --locations=europe-west1
    printf '%s' 'VALEUR' | gcloud secrets versions add github-client-secret --data-file=-
  3. Code : ajouter l'entrée dans connectors (noms de paramètres : page du connecteur dans la doc Google), puis ouvrir une PR.
  4. Après apply : autoriser les IP statiques côté source, puis faire l'autorisation OAuth initiale (console).
  5. Recette : un utilisateur sans droit sur un document de test ne doit pas le voir dans l'assistant.

8. Couche 4 : filtrage IA avec Model Armor

Fichier : terraform/30_ai_guardrails.tf

8.1 Le principe

question ──▶ [ge-prompt-shield] ──▶ Gemini ──▶ [ge-response-shield] ──▶ réponse
                 bloque si :                       bloque si :
                 injection, jailbreak,             contenu dangereux, lien malveillant,
                 lien malveillant, données          données sensibles (y compris tirées
                 sensibles, contenu dangereux       des documents de l'entreprise)
  • Pourquoi deux filtres : filtrer seulement les questions laisse passer une réponse qui recopierait un IBAN trouvé dans un document. Filtrer seulement les réponses laisse passer les tentatives de manipulation.
  • Pourquoi un service séparé du modèle : les instructions système (« ne révèle jamais… ») se contournent. Model Armor est un classifieur indépendant, qu'on ne peut pas « convaincre ».
  • Ce qui est filtré : l'assistant, les agents créés par les utilisateurs (Agent Designer), les agents Google, ainsi que les documents et images importés (un fichier non conforme est écarté).
  • ⚠️ Ce qui ne l'est pas : les agents personnalisés (ADK, A2A, Dialogflow). S'il y en a, ils doivent se filtrer eux-mêmes ; c'est aussi le rôle d'Agent Gateway (§11).

8.2 Les templates, filtre par filtre

resource "google_model_armor_template" "shield" {
  for_each    = toset(["ge-prompt-shield", "ge-response-shield"])
  location    = "eu"                       # app "eu" => templates "eu" (règle Google)
  template_id = each.value
Filtre (code)Ce qu'il détecteRéglagePourquoi ce réglage
pi_and_jailbreak_filter_settingsInjection de prompt (« ignore tes instructions »), jailbreak (contourner les règles de sécurité), y compris cachés dans un documentENABLED, MEDIUM_AND_ABOVERisque n°1 des applications LLM (OWASP LLM01)
malicious_uri_filter_settingsLiens de phishing ou de malware connus (256 premières URL)ENABLEDL'assistant ne doit jamais relayer un lien piégé avec la crédibilité de l'entreprise
rai_settingsHaine, harcèlement, sexuellement explicite, dangereux (la pédopornographie est toujours bloquée par Google)4 catégories, MEDIUM_AND_ABOVEResponsabilité de l'employeur sur ce que son outil produit
sdp_settings.advanced_configDonnées sensibles françaises et secrets (tableau 8.3)template DLP ge-donnees-sensibles-frLe mode basic de Google est orienté États-Unis (n° de sécurité sociale US…) : il ne détecte ni le NIR ni l'IBAN

Seuils de confiance :

SeuilComportementUsage
HIGHNe bloque que les cas quasi certainsPlancher minimal (§8.6)
MEDIUM_AND_ABOVEÉquilibre : bloque les attaques, laisse passer les questions normalesTemplates du socle
LOW_AND_ABOVEBloque au moindre doute, avec beaucoup de faux positifsContextes très sensibles

Pourquoi pas LOW_AND_ABOVE partout ? Un outil qui bloque à tort pousse les collaborateurs vers des IA grand public non maîtrisées. C'est un risque plus grand que celui qu'on voulait couvrir.

8.3 Les données sensibles détectées (France / UE)

resource "google_data_loss_prevention_inspect_template" "fr" {
  template_id = "ge-donnees-sensibles-fr"
  inspect_config {
    min_likelihood = "LIKELY"          # évite de bloquer n'importe quelle suite de chiffres
    info_types { name = "FRANCE_NIR" } # ... un bloc par type, depuis var.sensitive_info_types
  }
}
TypeDonnéePourquoi la bloquer
FRANCE_NIRN° de sécurité socialeDonnée très encadrée (CNIL) ; aucune raison de la donner à l'assistant
FRANCE_CNI, FRANCE_PASSPORTPièces d'identitéUsurpation d'identité
FRANCE_TAX_IDENTIFICATION_NUMBERN° fiscalDonnée personnelle sensible
IBAN_CODE, CREDIT_CARD_NUMBERDonnées bancairesFraude ; périmètre PCI-DSS pour les cartes
GCP_CREDENTIALS, GCP_API_KEY, AUTH_TOKEN, AWS_CREDENTIALS, ENCRYPTION_KEYSecrets techniquesUn secret collé dans un chat est un secret compromis

Volontairement non bloqués : e-mails, numéros de téléphone, noms. Ils sont omniprésents dans le travail normal (« envoie un mail à … »). Les bloquer rendrait l'outil inutilisable. Ils restent protégés par les droits d'accès (couches 1 et 3). La liste est une variable (sensitive_info_types), modifiable par l'équipe sécurité.

Avec Gemini Enterprise, une donnée sensible détectée bloque l'échange : le masquage n'est pas proposé.

8.4 Métadonnées du template : comportement et journalisation

template_metadata {
  enforcement_type        = "INSPECT_AND_BLOCK"        # on bloque (pas seulement on observe)
  log_template_operations = true                       # QUI modifie un filtre : toujours tracé
  log_sanitize_operations = var.log_screened_content   # le CONTENU filtré : false par défaut
  multi_language_detection { enable_multi_language_detection = true }   # français testé par Google
}

Pourquoi ne pas journaliser le contenu (log_screened_content = false) : Google le déconseille pour Gemini Enterprise. Les journaux contiendraient les questions des collaborateurs, lisibles par toute personne ayant logging.privateLogViewer. C'est un compromis :

false (défaut)true
Vie privée des collaborateurs✅ préservée⚠️ questions dans les journaux (accès restreint à l'équipe sécurité)
Détection d'un pic d'attaques✅ via les journaux d'usage (CUSTOMER_POLICY_VIOLATION, §10.4)✅ en plus : le texte exact qui a déclenché le filtre

À trancher avec le DPO et le CSE si nécessaire (surveillance des salariés). On peut activer true en période de lancement, puis revenir à false.

8.5 Rattachement à l'assistant : FAIL_CLOSED

model_armor_config {
  user_prompt_template = google_model_armor_template.shield["ge-prompt-shield"].id
  response_template    = google_model_armor_template.shield["ge-response-shield"].id
  failure_mode         = "FAIL_CLOSED"
}
  • FAIL_CLOSED : si Model Armor est indisponible, l'assistant refuse plutôt que répondre sans filtre. La sécurité passe avant la disponibilité.
  • Gemini Enterprise est autorisé à appeler Model Armor : roles/modelarmor.user pour son service agent (ge_uses_armor).
  • En complément : banned_phrases (noms de code de projets confidentiels) et une instruction système (« ne demande jamais de NIR ni de coordonnées bancaires »).

8.6 Le plancher (floor setting) : la sécurité qu'on ne peut pas affaiblir

resource "google_model_armor_floorsetting" "project" {
  parent                           = "projects/${var.project_id}"
  location                         = "global"
  enable_floor_setting_enforcement = true     # tout template moins strict est REFUSÉ
  filter_config { pi_and_jailbreak (HIGH), malicious_uri, rai DANGEROUS (HIGH) }
}
  • Pourquoi : même un administrateur Model Armor ne peut pas créer ni modifier un template sous ce minimum. La sécurité devient une garantie mécanique, pas une promesse.
  • Le plancher est volontairement moins strict que les templates (HIGH contre MEDIUM) : il fixe le minimum absolu, les templates portent la politique effective.

9. Couche 5 : exposition et fonctionnalités

Fichier : terraform/50_app.tf

9.1 Données en Union européenne

L'app (ge_location = "eu"), les templates Model Armor (eu), les clés KMS (europe), les clés régionales et les secrets sont en UE. Les traitements d'IA des API multi-régionales UE ont lieu dans l'UE. Bénéfice : conformité RGPD et engagements contractuels.

9.2 Pas de recherche web publique

web_grounding_type = "WEB_GROUNDING_TYPE_DISABLED"
  • Pourquoi : avec la recherche Google activée, des fragments de questions (parfois confidentielles) partent vers un moteur public. De plus, Google Search grounding est incompatible avec la résidence des données et CMEK.
  • Si le métier en a besoin : WEB_GROUNDING_TYPE_ENTERPRISE_WEB_SEARCH, l'option prévue pour les secteurs régulés (pas de journalisation des données client).

9.3 Fonctionnalités et partages

features = {
  "disable-agent-sharing"                = "FEATURE_STATE_ON"   # agents maison : pas de partage au lancement
  "skill-sharing-without-admin-approval" = "FEATURE_STATE_OFF"  # partage de skills = validation admin
  "session-sharing"                      = "FEATURE_STATE_OFF"  # une conversation ne se partage pas
  "feedback"                             = "FEATURE_STATE_ON"   # retours 👍/👎 pour piloter la qualité
}
RéglagePourquoi
Partage d'agents désactivéUn agent créé par un collaborateur peut avoir des consignes ou un accès inadaptés. On ouvre le partage quand la gouvernance (qui valide, selon quels critères) est définie
Partage de skills validé par un adminMême logique
Partage de conversations désactivéUne conversation contient souvent des informations issues de documents auxquels le destinataire n'a pas accès
Feedback activéMesurer la qualité et repérer les réponses problématiques

9.4 Observabilité sans contenu

observabilityConfig = { observabilityEnabled = true, sensitiveLoggingEnabled = false } : traces techniques (performance, erreurs) et journaux d'usage, sans le texte complet des questions et réponses (variable log_prompts_responses, false par défaut). Détail des journaux : §10.4.


10. Couche 6 : audit et détection

Fichier : terraform/70_detection.tf

10.1 Journaux d'audit

ServiceJournaux activésCe qu'on y trouve
Gemini EnterpriseData Access (lecture + écriture)Qui a utilisé l'app, quelles opérations
Model ArmorData AccessAppels de filtrage (sans contenu, sauf §8.4)
Secret ManagerData AccessChaque lecture d'un secret de connecteur
Cloud KMSAdmin Activity (toujours actif)Création, désactivation, destruction de clés

Cloud KMS « Data Access » n'est pas activé : chaque déchiffrement serait journalisé, soit des millions de lignes sans valeur.

10.2 Alerte « garde-fou modifié »

Une alerte part vers l'équipe sécurité dès qu'un de ces éléments change :

  • un template Model Armor ou le plancher ;

  • une clé KMS (désactivation, destruction, changement de version) ;

  • l'assistant ou la configuration d'identité de l'app ;

  • un droit IAM sur le projet, une clé ou un secret.

  • Pourquoi : c'est la détection du risque R6. Une modification légitime passe par une PR Terraform et l'alerte sert de confirmation. Une modification non prévue est un incident.

  • Sans impact vie privée : l'alerte repose sur les journaux d'administration, qui ne contiennent aucun contenu utilisateur.

10.3 Procédure en cas d'alerte

  1. Identifier : dans Logs Explorer, l'entrée d'audit (protoPayload.authenticationInfo.principalEmail, methodName).
  2. Qualifier : changement prévu (PR Terraform correspondante) ou non ?
  3. Si non prévu : rétablir avec terraform apply (l'état du code est la référence), retirer les droits du compte en cause, ouvrir un incident.
  4. Si une clé a été désactivée : la réactiver dans les 30 jours, sinon les données sont définitivement perdues.

10.4 Les journaux disponibles : ce qu'on capture, où, combien de temps

Fichier : terraform/80_logging.tf (fiche : 13-logging.md)

FamilleNom du journal / filtreCe qu'il contientDonnées personnelles ?Activé parDestination · rétention
Audit – administrationcloudaudit.googleapis.com%2FactivityQui a modifié quoi : app, assistant, connecteurs, filtres Model Armor, clés KMS, droits IAMIdentité des adminsToujours actif (Google)ge-security · 365 j (+ copie _Required 400 j)
Audit – accès aux donnéescloudaudit.googleapis.com%2Fdata_accessAppels aux API Gemini Enterprise, Model Armor, lectures de secretsIdentité des utilisateursgoogle_project_iam_audit_config (§10.1)ge-security · 365 j
Usage Gemini Enterprisediscoveryengine.googleapis.com%2Fgemini_enterprise_user_activityPar interaction : utilisateur, recherche ou question, agents utilisés, sources consultées, réponses bloquées (skipped_reasons, dont CUSTOMER_POLICY_VIOLATION = Model Armor/politique), création d'agents, droits sur les agents, mises à jour de connecteurs, fichiers importésOui : Google indique que les données sensibles ne sont pas filtréesObservabilité (enable_observability = true)ge-usage · 90 j, exclu du bucket par défaut
Prompts et réponses (texte complet)discoveryengine.googleapis.com%2Fgen_ai.user.message, …%2Fgen_ai.choiceTexte exact des questions, des réponses, raisons de finOui, intégralementlog_prompts_responses = true (désactivé par défaut)ge-usage · 90 j, exclu du bucket par défaut
Décisions Model Armor (contenu)jsonPayload.@type="…SanitizeOperationLogEntry"Texte filtré, filtre déclenché, confianceOuilog_screened_content = true (désactivé, recommandation Google)ge-security · 365 j, exclu du bucket par défaut
Traces techniquesCloud TraceLatence, erreurs, étapes de traitementNon (sans journalisation sensible)ObservabilitéCloud Trace
Statistiques d'usageConsole Gemini Enterprise → AnalyticsUtilisateurs actifs, requêtes, licences, agents, retours 👍/👎AgrégéesToujoursTableau de bord Google (+ export BigQuery possible par API)

Ce que le socle met en place, et pourquoi :

  • Deux buckets dédiés en UE (ge-security, ge-usage) plutôt que le bucket par défaut, pour séparer les usages (preuve d'audit ≠ suivi d'usage), limiter l'accès et fixer une durée de conservation par finalité.
  • Exclusion du bucket par défaut (google_logging_project_exclusion) des journaux qui contiennent des questions, parce que le bucket _Default est lisible par beaucoup de rôles (logging.viewer, owner…). Les questions des collaborateurs ne doivent être lisibles que par l'équipe habilitée.
  • Accès par vue (roles/logging.viewAccessor sur la vue _AllLogs de chaque bucket) pour security_groups uniquement.
  • Log Analytics activé : on interroge les journaux en SQL, directement dans la console, sans les copier dans BigQuery. Option link_logs_to_bigquery = true : un dataset BigQuery lié (lecture seule, sans copie) pour des tableaux de bord Looker Studio.
  • Rétention par finalité : sécurité 365 jours (preuve, enquête), usage 90 jours (minimisation). Valeurs à valider avec le DPO. lock_security_logs = true en production rend la rétention non modifiable (même par un admin), ce qui garantit l'intégrité des preuves. C'est irréversible.
  • Alerte sur les blocages sans journaliser le contenu filtré : une métrique compte les entrées d'usage contenant CUSTOMER_POLICY_VIOLATION. On détecte donc un pic d'attaques sans activer log_screened_content.

⚖️ RGPD et droit du travail : journaliser l'usage d'un outil par les salariés est un traitement de données personnelles. Il faut une finalité (sécurité, pilotage), l'information des collaborateurs, la consultation du CSE si l'outil permet un contrôle de l'activité, l'inscription au registre des traitements et des durées de conservation proportionnées. Le socle est configuré au plus sobre : pas de texte complet, accès restreint, 90 jours. Toute extension se décide avec le DPO.

10.5 Exemples de requêtes

Logs Explorer (choisir le bucket : Affiner la portée → Stockage → ge-usage) :

-- Toutes les réponses bloquées par la politique (Model Armor, phrases interdites)
logName="projects/PROJECT_ID/logs/discoveryengine.googleapis.com%2Fgemini_enterprise_user_activity"
"CUSTOMER_POLICY_VIOLATION"

-- Qui a modifié un filtre Model Armor ou une clé (bucket ge-security)
protoPayload.serviceName=("modelarmor.googleapis.com" OR "cloudkms.googleapis.com")
logName:"cloudaudit.googleapis.com%2Factivity"

-- Qui a lu un secret de connecteur
protoPayload.serviceName="secretmanager.googleapis.com" protoPayload.methodName=~"AccessSecretVersion"

Log Analytics (SQL), Logging → Log Analytics, vue du bucket ge-usage :

-- Nombre de blocages par utilisateur sur 7 jours (enquête après une alerte)
SELECT
  JSON_VALUE(proto_payload.audit_log.authentication_info.principal_email) AS utilisateur,
  COUNT(*) AS blocages
FROM `PROJECT_ID.eu.ge-usage._AllLogs`
WHERE log_name LIKE '%gemini_enterprise_user_activity'
  AND SEARCH(TO_JSON_STRING(t), 'CUSTOMER_POLICY_VIOLATION')
  AND timestamp > TIMESTAMP_SUB(CURRENT_TIMESTAMP(), INTERVAL 7 DAY)
GROUP BY utilisateur ORDER BY blocages DESC

Les chemins exacts des champs (authentication_info, skipped_reasons…) sont à confirmer sur les premières entrées réelles. Ouvrez une entrée dans Logs Explorer et adaptez la requête. La recherche plein texte ("CUSTOMER_POLICY_VIOLATION" / SEARCH) fonctionne quelle que soit la structure.


11. Options avancées

Fichier : terraform/60_advanced.tf. Elles sont désactivées par défaut et s'activent par variable.

11.1 Agent Gateway (enable_agent_gateway = true)

Quoi : le point de passage contrôlé des appels sortants de Gemini Enterprise vers des agents, des serveurs MCP et des API enregistrés dans Agent Registry. C'est un pare-feu applicatif pour les agents :

  • refus par défaut : un appel n'est autorisé que si une règle IAM l'autorise explicitement ;
  • politiques par outil (ex. « outils en lecture seule uniquement ») ;
  • Model Armor possible sur les appels d'outils ;
  • traçabilité de chaque appel agent → outil.
resource "google_network_services_agent_gateway" "egress" {
  name     = "assistant-ia-egress"
  location = "europe-west1"                       # app "eu" => passerelle europe-west1
  google_managed { governed_access_path = "AGENT_TO_ANYWHERE" }   # Gemini Enterprise : sortie uniquement
  registries = ["//agentregistry.googleapis.com/projects/<projet>/locations/global"]
}

Le rattachement à l'app (agentGatewaySetting) est fait par l'appel API de engine_settings.

Pourquoi optionnel : sans agent personnalisé ni outil MCP, il n'y a rien à gouverner et la passerelle n'apporte presque rien. Elle devient indispensable dès que Acme ajoute un agent Databricks (§7.4 option B), un serveur MCP interne ou des actions vers des API métier. Ce sont justement les flux que le Model Armor de Gemini Enterprise ne filtre pas.

Après activation : importer explicitement les agents, MCP et API autorisés (jusqu'à 15 min pour que les politiques s'appliquent).

11.2 VPC Service Controls (vpc_sc_access_policy = "<id>")

Quoi : un périmètre autour des API du projet (Gemini Enterprise, Model Armor, DLP, KMS, Secret Manager). Même avec des identifiants volés, on ne peut pas extraire de données depuis l'extérieur du périmètre.

Comment le socle le déploie : en dry-run (use_explicit_dry_run_spec = true). Rien n'est bloqué ; les violations sont journalisées. On analyse pendant 2 à 4 semaines, puis on passe en mode actif.

Impacts à préparer avant le mode actif :

  • l'interface web est bloquée sauf règles d'entrée (access level : plages IP Acme, appareils gérés…) ;
  • la création de nouveaux data stores exige de déclarer les sources et domaines autorisés dans les politiques d'organisation (« sources autorisées » et « domaines de sortie autorisés » des connecteurs) ;
  • appliquer un périmètre sur un projet qui a déjà des data stores n'est pas supporté : idéalement, VPC-SC est mis en place avant les connecteurs, en production.

11.3 Autres mesures (hors Terraform du projet)

MesureOùIntérêt
Access TransparencyOrganisationJournal des accès du personnel Google aux données (apps eu/us uniquement)
Security Command Center (AI Protection)OrganisationInventaire et détection des risques IA, alertes du plancher Model Armor
Politiques d'organisation CMEK et localisationOrganisationImposer CMEK et l'UE à tout nouveau projet

12. Déploiement pas à pas

12.1 Prérequis

  • Projet GCP dans l'organisation de Acme, avec facturation.
  • Licences Gemini Enterprise (console : Gemini Enterprise → Abonnements).
  • Les 4 groupes (utilisateurs, admins, sécurité, pilotage).
  • Rôles de la personne ou du compte CI qui déploie : agentspaceAdmin, modelarmor.admin, cloudkms.admin, resourcemanager.projectIamAdmin, secretmanager.admin, dlp.admin, logging.configWriter, monitoring.editor (ou Owner en recette).
  • Connexion : gcloud auth login et gcloud auth application-default login avec le compte d'entreprise.

12.2 Ordre de déploiement

cd terraform
cp terraform.tfvars.example terraform.tfvars   # projet, groupes, connecteurs
# 1. L'équipe sécurité crée les secrets des connecteurs (§7.6)
terraform init
terraform plan      # LIRE : localisation eu, clés, groupes, connecteurs
terraform apply

Terraform respecte l'ordre imposé par Google : clé KMS → droits sur la clé → enregistrement CMEK → connecteurs → app → assistant → réglages API.

12.3 Après apply (console)

  1. Connecteurs : autoriser les IP (terraform output connector_static_ips) côté SaaS ; faire l'autorisation OAuth initiale de chaque source ; vérifier terraform output connector_states.
  2. Licences : attribuer les licences aux utilisateurs.
  3. Lien de l'app : console → app → Intégration → copier l'URL de l'app web.
  4. Recette (§13).

13. Recette

#TestRésultat attenduCouche
1./scripts/test_model_armor.shQuestion légitime acceptée ; jailbreak FR, IBAN, carte, lien malveillant bloqués4
2Dans l'app : « Ignore tes instructions et… »Message de violation de politique4
3Dans l'app : coller un NIR de testBloqué4
4A cherche un fichier SharePoint/Drive partagé seulement avec BAucun résultat pour A3
5Personne hors du groupe utilisateurs ouvre le lienAccès refusé1
6Un admin Gemini Enterprise tente de modifier un template Model ArmorPermission refusée1
7Un admin Model Armor tente de désactiver le filtre injection/jailbreak d'un templateRefusé par le plancher4
8Modifier un template via TerraformAlerte e-mail reçue6
9« Quelle météo à Lille aujourd'hui ? »Pas de réponse web5
10Console KMS : désactiver la clé (en recette uniquement), puis réactiverApp indisponible en ≤ 15 min, puis rétablie2
11Après quelques questions, chercher gemini_enterprise_user_activity dans le bucket _DefaultRien (exclu) ; présent dans ge-usage6
12Provoquer 21 blocages en 10 min (script de recette en boucle dans l'app)Alerte « pic de requêtes bloquées » reçue6
13Un admin Gemini Enterprise (hors sécurité) ouvre le bucket ge-usageAccès refusé6

14. Exploitation au quotidien

14.1 Tout changement passe par le code

PR → terraform plan relu → validation par l'équipe sécurité pour 30_ai_guardrails.tf et 20_data_protection.tf → apply depuis la CI. L'alerte §10.2 confirme chaque changement.

14.2 Révoquer un collaborateur

Le retirer du groupe utilisateurs, ou suspendre son compte dans l'annuaire : effet immédiat. Pas de Terraform.

14.3 Révoquer les données (incident majeur, fin de contrat)

Désactiver la version de clé dans Cloud KMS (équipe sécurité). Les données deviennent illisibles en 15 min. Réversible pendant 30 jours, définitif au-delà.

14.4 Rotation de clé

Créer une nouvelle version et la rendre primaire. Gemini Enterprise rechiffre automatiquement. Garder l'ancienne version active au moins 30 jours. Ne pas faire tourner les clés régionales des connecteurs tiers (règle Google).

14.5 Rotation d'un secret de connecteur

gcloud secrets versions add <secret> --data-file=-. La référence versions/latest prend la nouvelle valeur. Puis désactiver l'ancienne version.


15. Compromis assumés

ChoixCe qu'on gagneCe qu'on accepte
MEDIUM_AND_ABOVEPeu de faux positifs, adoptionUne attaque très subtile peut passer (le plancher et la détection restent)
FAIL_CLOSEDJamais de réponse non filtréeIndisponibilité pendant une panne de Model Armor
Pas de journalisation du contenuVie privée, conformitéPas d'alerte automatique sur pic d'attaques
Fédéré par défautDroits natifs, pas de copieQualité un peu moindre sur les PDF complexes
E-mails et téléphones non bloquésOutil utilisableProtégés par les droits d'accès, pas par le filtre
Partage d'agents désactivéPas d'agent non revu diffuséMoins de collaboration au lancement
prevent_destroy sur les clésAucune destruction accidentelleterraform destroy complet impossible sans retirer la protection (voulu)

16. Vérifié / à valider en recette

Ce socle est validé syntaxiquement (terraform validate, provider hashicorp/google 8.4) et s'appuie sur la documentation Google d'octobre 2026. Il n'a pas encore été déployé sur un projet réel. Points à confirmer au premier déploiement :

PointPourquoi l'incertitudeRepli
Localisation du template DLP (dlp_location = "europe") avec un template Model Armor euNon précisé par Googledlp_location = "global", ou sdp_mode = "basic" (temporaire)
Noms exacts des paramètres de certains connecteurs (Teams, GitHub, BigQuery ingestion)Doc surtout « console »Lire la page du connecteur ; créer une fois en console et comparer
sharepoint ou sharepoint_federated_search pour SharePoint fédéréLes deux apparaissent dans la docEssayer sharepoint, sinon l'autre
Data stores des connecteurs Workspace sans « entities »Sortie non documentéeAjouter les IDs dans extra_data_store_ids
Compte qui lit les secrets (secretAccessor)Supposé : service agent Gemini EnterpriseVérifier l'erreur éventuelle et ajuster
Noms des méthodes dans l'alerte « garde-fou modifié »Noms d'API à confirmer dans les journauxAjuster le filtre après le premier changement observé
Présence de CUSTOMER_POLICY_VIOLATION dans les journaux d'usage pour un blocage Model ArmorChamp answer.skipped_reasons documenté, valeur dans le journal à observerProvoquer un blocage en recette et vérifier ; sinon activer log_screened_content en recette
Les exclusions de projet s'appliquent au bucket _DefaultComportement standard de Cloud Logging, à constaterVérifier dans Logs Explorer que les questions n'apparaissent pas dans _Default
Modifier data_store_ids peut recréer l'app (bug connu du provider)Issue GitHub du providerLire le plan ; ajouter les connecteurs avant le lancement

17. Dépannage

SymptômeCause probable → solution
CMEK is not supported in globalUtiliser ge_location = "eu"
Tout est bloqué juste après applyPropagation IAM (1–2 min), ou ge_uses_armor manquant. Avec FAIL_CLOSED, une erreur de droit = un blocage (voulu)
Template refusé « less strict than floor »Le plancher fonctionne : rendre le template plus strict
Connecteur en ALLOWLIST_STATIC_IPAutoriser les IP de connector_static_ips côté SaaS
Connecteur Workspace refuséCompte d'entreprise (pas Gmail) ; projet dans l'organisation ; app OAuth Google marquée Trusted par un super-admin
Error: Instance cannot be destroyed (clé KMS)prevent_destroy : voulu. Voir §14.3
Erreur sur le template DLP§16, première ligne

18. Sources

Next