Aller au contenu

Managed Identity — le passwordless par application

Vous êtes ici : Référence — l’identité des applications elles-mêmes. Comment les apps se connectent à SQL et au Storage sans aucun secret — une identité par unité de déploiement. Lecture 8 min. Audience : devs, auditeurs sécurité. Date : 2026-07-07 | Version : 1.0 — remplace le stub (matière : code @gs/db/@gs/storage, bicep, migrations, docs/standards/AUTH.md, ADR-033/057/060/065)

Question Réponse
Un mot de passe SQL quelque part ? Non — SQL_USE_MANAGED_IDENTITY=true posé par l’infrastructure, connexion azure-active-directory-default
Qui renouvelle les jetons ? Le driver (tedious + DefaultAzureCredential) à chaque connexion physique du pool — zéro code maison
Quelle identité pour qui ? Apps web : system-assigned (une chacune) ; worker ETL : user-assigned dédiée ; Storage : même principe, SAS user-delegation
Et côté base ? CREATE USER … FROM EXTERNAL PROVIDER versionné en migrations — permissions as code
Tout est en MI ? Non — trois exceptions assumées : SOFI (système externe, compte dédié lecture seule), le CI d’initialisation BD, le dev local

Historiquement, les applications partageaient un compte SQL à privilèges élevés, mot de passe en clair dans la configuration — l’anti-pattern classique des premiers déploiements (rotation pénible, fuite possible par logs ou capture d’écran, aucune traçabilité par application). La migration Managed Identity (ADR-033, avril 2026) a remplacé ce partage : chaque application se présente à la base sous son propre nom, et les journaux SQL savent qui fait quoi.

  1. L’infrastructure (module Bicep unique des apps) crée l’App Service avec son identité (SystemAssigned) et pose SQL_USE_MANAGED_IDENTITY=true — c’est une convention d’infrastructure, pas un choix par app.
  2. Le connecteur SQL (@gs/db) ouvre le pool en mode azure-active-directory-default : le driver obtient un jeton Entra via DefaultAzureCredential à chaque connexion physique du pool, et le renouvelle tout seul.
  3. En développement local, la même config retombe sur un compte SQL classique — branche explicitement étiquetée dev only.
La chaîne passwordless — quatre étapes, rejouées à chaque connexionÀ chaque connexion physique du pool SQL, quatre étapes s’enchaînent. Étape 1 : l’application, via @gs/db en mode azure-active-directory-default, demande un jeton à DefaultAzureCredential. Étape 2 : DefaultAzureCredential présente la Managed Identity de l’application à Microsoft Entra, qui émet un jeton d’accès frais. Étape 3 : le jeton revient à l’application. Étape 4 : le jeton ouvre la connexion vers Azure SQL. À la connexion suivante, le driver recommence — aucun secret stocké, aucun jeton réutilisé après expiration.Application@gs/db · pool SQLaad-defaultDefaultAzure-Credentialprésente la MIMicrosoft Entraémet un jeton fraisAzure SQLGS_Apps1 · jeton ?2 · MI3 · jeton retourné à l’application4 · le jeton ouvre la connexion — renouvelé à la connexion suivantezéro secret stocké · zéro jeton réutilisé après expiration
La chaîne passwordless, rejouée à chaque connexion du pool : le driver demande un jeton frais (1-2-3) et l’ouverture SQL s’ensuit (4) — rien à faire tourner à la main.
Unité Identité Pourquoi
Apps web (hub, admin, ug, inv, mcp, api-portail) System-assigned, une par app Cycle de vie lié à l’app ; provisionnée par le module Bicep unique (ADR-065)
Worker ETL (gs-etl-worker) User-assigned dédiée, partagée par ses deux jobs (file + horaire) et par le déclencheur de file L’ETL a sa propre identité — jamais celle d’une app web (fin du cumul de privilèges historique de l’app admin)
Accès Storage (@gs/storage) Même identité que l’app appelante, via DefaultAzureCredential Aucune clé de compte ; URLs signées en SAS user-delegation (dérivées du jeton Entra, plafonnées à 7 jours) — ADR-057

L’identité ne suffit pas ; il faut lui accorder des droits, et ces droits sont du code (ADR-060) :

  • Chaque identité est créée en base par une migration versionnée — CREATE USER … FROM EXTERNAL PROVIDER idempotent, suivie de GRANT explicites (exemple : la Console UG reçoit SELECT, INSERT, UPDATE sur son schéma — pas DELETE, le soft delete étant la règle).
  • Un manifeste canonique décrit qui a le droit de toucher quel schéma, avec les exceptions documentées et justifiées (ex. : l’outil MCP reste lecteur global par conception — il consulte tout schéma présent et futur, borné en lecture seule par un garde SQL).
  • Un contrôle de dérive en CI compare l’état réel de la base au manifeste.

5. Ce qui n’est pas en Managed Identity (assumé)

Section intitulée « 5. Ce qui n’est pas en Managed Identity (assumé) »
Exception Pourquoi
SOFI (ERP source) Système externe hors d’Azure — compte dédié lecture seule, secrets injectés par l’infrastructure, jamais dans le dépôt
CI d’initialisation BD Le pipeline db:init utilise un compte SQL dédié (le DDL exige des privilèges qu’aucune MI runtime ne doit porter) ; la cible versionnée est un principal fédéré OIDC — pas encore provisionné
Dev local Compte SQL classique, branche explicitement dev only