Plan d'identité et de politiques : guide d'exploitation
Ce guide couvre Keycloak (A-03), SPIFFE/SPIRE (A-04), OpenFGA (A-05), OPA et les bundles signés (A-06, A-07), la gestion des clés KMS et HSM (A-08). Les preuves sont des tests qui démarrent les vrais composants, issus des versions officielles téléchargées par tools/iam/fetch.sh (versions épinglées, empreintes sha256 vérifiées) dans /tmp/toolchains.
| Composant | Version | Licence | Test |
|---|---|---|---|
| Keycloak | 26.8.1 | Apache-2.0 | tests/test_iam_keycloak_real.py |
| SPIRE server et agent | 1.15.5 | Apache-2.0 | tests/test_iam_spire_real.py |
| OpenFGA | 1.22.1 | Apache-2.0 | tests/test_iam_openfga_real.py |
| OPA | 1.21.4 | Apache-2.0 | tests/test_iam_opa_bundles_real.py, tests/test_iam_fail_closed.py |
| SoftHSM2 (compilé depuis les sources) | 2.7.0 | BSD-2-Clause | tests/test_iam_kms.py |
Keycloak (OIDC, SAML, MFA)
- Le realm
deploy/keycloak/realm-enterprise-ai.jsonest importé tel quel (la fédération LDAP est retirée en test, faute d'annuaire). Le flux code d'autorisation avec PKCE S256 est joué en HTTP, le jeton est accepté par le Control Plane (EAI_OIDC_ISSUER, JWKS découvert), renouvelé, puis la déconnexion révoque le refresh token (invalid_grant) et la session (userinfo401). - MFA : l'administrateur plateforme porte l'action requise
CONFIGURE_TOTP; à la connexion suivante le mot de passe seul ne termine pas la connexion, un code faux est refusé, un code TOTP valide (HmacSHA1, 6 chiffres, 30 s, codes non réutilisables) passe. - SAML : le client
https://<apps-host>/saml/metadata(assertions signées RSA-SHA256, POST) est déclaré dans le realm ; le descripteur IdP/realms/enterprise-ai/protocol/saml/descriptorest validé (SSO POST, SLO, certificat RSA 2048 bits minimum). Une connexion SAML complète par navigateur n'est pas jouée par les tests. - Identité résolue :
GET /v1/identity/whoami.
SPIFFE et SPIRE
- Attestation de nœud par join token, attestation de workload
unix(chemin du binaire), une entrée d'enregistrement par workload, SVID X.509 récupérés par la Workload API. eai.domains.identity.spiffe: contexte TLS 1.3 qui exige le certificat du pair chaîné au bundle, puis autorise l'identifiant SPIFFE contre une liste blanche (refus par défaut). Un SVID valide mais hors liste, l'absence de certificat client ou un serveur inattendu sont refusés.- L'identifiant vérifié est résolu par le Control Plane (
X-SPIFFE-IDderrière le proxy maillé,EAI_AUTH_SPIFFE) vers l'identité de service enregistrée ; un SVID inconnu reçoit 401unknown_spiffe_id.
OpenFGA
- Modèle
deploy/policies/openfga/model.fga, tuples écrits par/v1/authz/tupleset par la création des ressources, contrôles par document et par action (can_read,editor,owner,can_use), tuples contextuels (contextual_tuplesdePOST /v1/authz/check, jamais stockés), isolation des tenants par préfixe d'objet. - Panne :
require_relationet/v1/authz/checkrépondent 502authz_backend_erroret l'audit enregistre un refus avec la raisonbackend_unavailable.
OPA et bundles versionnés
EAI_OPA_DELIVERY=bundle:POST /v1/policies/bundlesempaquette les politiques engine=opa (actives, et en brouillon pour les bibliothèques commeeai.lib) avec un.manifestqui porte la révision et les racines, signé RS256 commeopa build --signing-key. OPA interrogeGET /v1/policies/bundles/current/bundle.tar.gzavec une clé API auditeur et vérifie la signature avec la clé publique déclarée souskeys.- Configuration OPA type :
services.cp.url = https://<control-plane>/v1/policies/bundles,bundles.eai.resource = current/bundle.tar.gz,bundles.eai.signing.keyid = <EAI_POLICY_BUNDLE_KEY_ID>. GET /v1/policies/engineslit la révision réellement chargée dans OPA (/v1/data/system/bundles) et indiquein_sync. Un bundle signé par une autre clé est refusé par OPA, qui garde la révision précédente :in_syncpasse à false.- Décisions : refus, autorisation, obligations (
dlpmasquage,approval) remontées par le PEP/v1/policies/evaluate.
Refus par défaut et fail closed
- OPA arrêté : l'action sensible (appel d'outil) est refusée en 403
policy_deniedavec la raisonpolicy engine unavailable: <politique>, la décision est enregistrée et auditée. - OpenFGA arrêté : 502
authz_backend_error, refus audité. - Stockage d'audit indisponible : l'écriture d'audit fait partie de la transaction de la mutation, donc la délégation de droits et la publication de politique échouent sans rien laisser ; les lectures non auditées (liste des politiques) continuent de répondre.
Simulateur et historique
- Chaque création, modification et suppression ajoute une révision immuable (
GET /v1/policies/{id}/revisions,.../revisions/diff). POST /v1/policies/simulatecompare, entrée par entrée et sur les N dernières décisions enregistrées, les politiques actives et le même ensemble avec la candidate ; rien n'est modifié, l'exécution est auditée.
Séparation des rôles
Matrice testée (tests/test_iam_role_matrix.py) : seul platform_admin crée un tenant ; tenant_admin et security_officer écrivent les politiques, les relations et les identités de service ; auditor lit l'audit, les décisions et l'état des moteurs sans rien modifier ; developer simule et lit l'historique sans publier ; viewer n'a accès à rien de tout cela. Le demandeur d'une approbation ne peut jamais la décider (separation_of_duties), l'auditeur non plus.
Rôles de l'architecture de référence
Quinze rôles (eai.auth.ROLES, rôles de royaume Keycloak, migration V161). Les sept rôles métiers ajoutés :
| Rôle | Capacités | Jamais |
|---|---|---|
soc_analyst | alertes, détections, incidents, investigations, réponses permises par le garde de proportionnalité | écriture de politiques, clés, administration du tenant |
ai_governance_officer | modèles, agents, risques, évaluations, validation | infrastructure, secrets |
data_steward | sources, classification, quarantaine RAG, lignage, rétention (métadonnées) | contenu des documents dont il n'est pas lecteur |
application_owner | agents et applications dont il est propriétaire (relation owner) | ressources des autres |
ai_platform_engineer | fournisseurs, routes, déploiements, passerelle, runtime | IAM, export d'audit |
business_owner | approbation et validation des cas d'usage | administration |
support_engineer | diagnostics, santé, SLO, opérations en lecture | écritures |
Les administrateurs techniques (platform_admin, tenant_admin) n'ont aucun accès implicite au contenu métier :
documents, recherche RAG et mémoires dépendent de l'ACL de la ressource, et le refus est audité.
Preuve : services/control-plane/tests/test_iam_role_matrix.py.
KMS et HSM : ce que le client doit fournir
EAI_KMS_PROVIDER=openbao utilise OpenBao Transit. EAI_KMS_PROVIDER=pkcs11 utilise le HSM du client par python-pkcs11 (MIT). À négocier avec le client :
- La bibliothèque PKCS#11 du fabricant, montée dans le conteneur (
EAI_KMS_PKCS11_MODULE). - Le libellé du token ou de la partition (
EAI_KMS_PKCS11_TOKEN_LABEL). - Une référence au PIN utilisateur : un fichier monté depuis un secret (
EAI_KMS_PKCS11_PIN_FILE), jamais le PIN dans l'environnement. - Les clés, créées par le client, non extractibles et sensibles : une clé AES-256
<libellé>(CKM_AES_CBC_PAD), un secret générique<libellé>.mac(CKM_SHA256_HMAC, chiffrement puis MAC), une paire EC P-256 de signature (CKM_ECDSA_SHA256). - La vérification que ces mécanismes sont autorisés sur la partition (certains HSM restreignent CBC ou exigent GCM : à valider avant la mise en service).
Toute erreur du HSM lève KmsError (code kms_unavailable) : l'appelant échoue fermé. Le chemin PKCS#11 est prouvé contre un vrai token SoftHSM2.