--- title: "IAM au moindre privilège et comptes de service" --- # IAM au moindre privilège et comptes de service > [!NOTE] > Dans cette leçon, vous allez donner à l'assistant une identité propre avec quatre rôles minimaux, comprendre pourquoi les clés de comptes de service sont à bannir, et lancer un contrôle de posture IAM en lecture seule sur un projet réel. ## Les trois mots de l'IAM **IAM** (Identity and Access Management) répond à « qui peut faire quoi sur quelle ressource ». Une **liaison** (binding) associe : - un **membre** : `user:`, `group:`, `serviceAccount:` (identité d'un programme) ; - un **rôle** : un paquet de permissions, par exemple `roles/aiplatform.user` qui permet d'appeler les modèles Vertex AI ; - une **ressource** : projet, bucket, clé KMS, service Cloud Run… Le **moindre privilège** consiste à donner le plus petit rôle, sur la plus petite ressource, au plus petit groupe. ```mermaid flowchart LR subgraph Mauvais["À éviter"] A1["Compute par défaut"] -->|"roles/editor sur le projet"| R1["Tout le projet"] end subgraph Bon["Le module"] A2["lab-archi-ia-app (dédié)"] -->|"roles/aiplatform.user (projet)"| R2["Appeler Gemini"] A2 -->|"roles/storage.objectViewer (1 bucket)"| R3["Lire les documents"] A2 -->|"roles/modelarmor.user"| R4["Filtrer prompts et réponses"] end ``` | Rôle | Pourquoi l'assistant en a besoin | Ce qu'il ne permet pas | | --- | --- | --- | | `roles/aiplatform.user` | appeler Gemini sur Vertex AI | supprimer des ressources Vertex, gérer IAM | | `roles/storage.objectViewer` **sur le bucket** | lire les documents de connaissances | lire les autres buckets, écrire | | `roles/modelarmor.user` | appeler Model Armor | modifier les modèles Model Armor | | `roles/logging.logWriter`, `roles/monitoring.metricWriter` | écrire ses journaux et métriques | lire les journaux des autres | ```hcl title="files/apis_iam.tf (extrait)" resource "google_service_account" "app" { account_id = "${var.prefix}-app" display_name = "Assistant interne (Cloud Run)" } resource "google_project_iam_member" "app_roles" { for_each = toset([ "roles/aiplatform.user", "roles/logging.logWriter", "roles/monitoring.metricWriter", "roles/modelarmor.user", ]) project = var.project_id role = each.value member = google_service_account.app.member } # Lecture du seul bucket de connaissances, pas de tous les buckets du projet. resource "google_storage_bucket_iam_member" "app_reads_kb" { bucket = google_storage_bucket.kb.name role = "roles/storage.objectViewer" member = google_service_account.app.member } ``` > [!TIP] > On utilise `google_project_iam_member` (ajoute **une** liaison) et non `google_project_iam_policy` ou `_binding` (qui **remplacent** la politique ou la liste des membres d'un rôle) : un module applicatif ne doit jamais effacer les droits posés par d'autres. ## Pas de clés de comptes de service Une **clé de compte de service** est un fichier JSON qui permet à n'importe qui le possédant d'agir comme ce compte, sans expiration par défaut. C'est la première cause de fuite d'identifiants cloud. Les alternatives : | Où tourne le code | Identité sans clé | | --- | --- | | Cloud Run, Cloud Functions, GKE | le compte de service attaché (ici `template.service_account`) | | Poste d'un développeur | `gcloud auth application-default login` (ses propres droits) | | CI GitHub / GitLab | Workload Identity Federation (jeton OIDC échangé contre un jeton Google) | La contrainte d'organisation `iam.disableServiceAccountKeyCreation` interdit d'en créer. ```quiz question: "Le compte Compute par défaut (PROJECT_NUMBER-compute@developer.gserviceaccount.com) a souvent roles/editor. Pourquoi est-ce dangereux pour une plateforme IA ?" options: - "Parce qu'il ne peut pas appeler Vertex AI" - "Parce que tout service qui l'utilise par défaut (Cloud Run, VM) hérite d'un droit d'écriture sur presque tout le projet : une injection de prompt qui fait exécuter du code devient une prise de contrôle du projet" - "Parce qu'il coûte plus cher qu'un compte dédié" answer: 1 explanation: "roles/editor donne l'écriture sur la quasi-totalité des ressources. Tout service lancé sans compte explicite l'utilise. Un compte dédié à rôles minimaux limite les dégâts d'une compromission. Les comptes de service ne sont pas facturés." ``` ## Contrôler la posture en lecture seule Le script [check_posture.py](../files/check_posture.py) appelle trois API **en lecture** (Service Usage, Resource Manager `getIamPolicy`, IAM) puis passe le tout à une fonction pure, `evaluate`, testable sans réseau. ```python title="files/check_posture.py (extrait)" for b in bindings: for m in b.get("members", []): if m in PUBLIC: # allUsers, allAuthenticatedUsers public.append(f"{m} -> {b['role']}") if m == default_sa: default_sa_roles.append(b["role"]) if b["role"] in PRIMITIVE: # roles/owner, roles/editor (sa_primitive if m.startswith("serviceAccount:") else humans_primitive).append(m) ``` Le rapport **masque les adresses** (`user:al***@exemple.fr`) : un rapport circule, il ne doit pas devenir une fuite de données personnelles. ```bash pip install -r requirements.txt pytest -q # 4 tests, sans réseau gcloud auth application-default login python check_posture.py --project mon-projet # code de sortie 1 s'il y a un FAIL ``` > [!WARNING] > Le script n'a besoin que de rôles de lecture : `roles/iam.securityReviewer` et `roles/serviceusage.serviceUsageViewer`. Ne le lancez pas avec un compte owner « parce que c'est plus simple » : un outil d'audit doit lui-même respecter le moindre privilège. ```quiz question: "Quelle ressource Terraform utiliser pour ajouter roles/aiplatform.user à un groupe sans risquer d'effacer les autres membres de ce rôle ?" options: - "google_project_iam_policy" - "google_project_iam_binding" - "google_project_iam_member" answer: 2 explanation: "_member ajoute une seule liaison membre-rôle. _binding fait autorité sur la liste complète des membres d'un rôle, et _policy sur toute la politique du projet : les deux peuvent retirer des accès posés ailleurs." ``` ## Exercice Ajoutez au script un contrôle : « aucun **utilisateur** individuel (`user:`) ne doit avoir `roles/aiplatform.admin` ; seuls des groupes le peuvent ». Écrivez aussi le test correspondant.
Voir la solution Dans `evaluate`, après la boucle : ```python admins = [m for b in bindings if b["role"] == "roles/aiplatform.admin" for m in b.get("members", []) if m.startswith("user:")] out.append(("WARN" if admins else "OK", "Admins Vertex AI nominatifs", ", ".join(mask(m) for m in admins) or "aucun (groupes uniquement)")) ``` Et dans `test_check_posture.py` : ```python def test_admin_vertex_nominatif(): policy = {"bindings": [{"role": "roles/aiplatform.admin", "members": ["user:bob@exemple.fr"]}]} r = levels(evaluate(N, set(EXPECTED_APIS), policy, {})) assert r["Admins Vertex AI nominatifs"] == "WARN" ```
## Résultat obtenu Exécution réelle le 2026-10-15 sur un projet de test (identifiant masqué), en lecture seule : ```text $ pytest -q .... [100%] 4 passed in 0.01s $ python check_posture.py --project [WARN] API attendues manquantes : cloudkms.googleapis.com, iap.googleapis.com, modelarmor.googleapis.com, accesscontextmanager.googleapis.com [OK ] Accès public (allUsers) aucun [FAIL] Compte Compute par défaut a roles/editor [OK ] Comptes de service owner/editor aucun [OK ] Humains owner/editor 1 (cible : 3 au plus, idéalement via un groupe) [OK ] Clés de comptes de service aucune clé utilisateur [WARN] Audit accès aux données Vertex AI manque DATA_READ, DATA_WRITE 7 contrôles : 1 FAIL, 2 WARN ``` Lecture : le projet de test a le défaut classique (compte Compute par défaut en `roles/editor`), et les journaux d'accès aux données de Vertex AI ne sont pas activés. Le module Terraform corrige le second point (leçon 3.2) ; le premier se corrige en retirant le rôle après avoir donné un compte dédié à chaque service. ## Je sais faire - [ ] Choisir des rôles prédéfinis minimaux et les poser à la bonne granularité (projet ou ressource) - [ ] Expliquer pourquoi `_member` plutôt que `_binding` ou `_policy` - [ ] Remplacer une clé de compte de service par une identité sans clé - [ ] Lancer un contrôle de posture IAM en lecture seule et interpréter le rapport