--- # Distribution Keycloak (Quarkus). Verifier/ajuster la version sur keycloak.org/downloads. serveur_keycloak_version: "26.0.7" serveur_keycloak_url: "https://github.com/keycloak/keycloak/releases/download/{{ serveur_keycloak_version }}/keycloak-{{ serveur_keycloak_version }}.tar.gz" serveur_keycloak_java_paquet: "default-jre-headless" serveur_keycloak_home: "/opt/keycloak" serveur_keycloak_utilisateur: "keycloak" serveur_keycloak_service: "keycloak" # Publication (derriere serveur_nginx, TLS termine a l'edge). # DERIVE de l'exposition que le plan declare (voir tasks/main.yml). Ce nom est celui que # Keycloak ANNONCE dans son issuer OIDC : il doit etre exactement celui que l'edge sert et # que le DNS resout, sinon tout client OIDC refuse le jeton — « issuer did not match ». # La valeur ci-dessous n'est qu'un REPLI, si l'application n'expose rien. serveur_keycloak_hostname_derive: true serveur_keycloak_hostname: "keycloak.{{ domaine_interne }}" # Base de donnees : resolue depuis le registre par le groupe consommateur. serveur_keycloak_groupe: "serveur_keycloak" # IP interne du serveur PostgreSQL (data-01, cf. nomenclature). serveur_keycloak_db_host: "" # dérivé (FQDN) par resoudre_base au déploiement # Secrets (Ansible Vault) — obligatoires. serveur_keycloak_admin_user: "admin" serveur_keycloak_admin_password: "{{ vault_keycloak_admin | default('') }}" # rempli depuis la voute (vault_keycloak_admin) # --- Fédération LDAP : OpenLDAP source de vérité (modèle d'identité A) --- # Keycloak lit l'annuaire (READ_ONLY) ; les users LDAP se connectent via le SSO. # Requiert client_pki sur ce nœud (racine step_ca dans le bundle système pour LDAPS). serveur_keycloak_ldap_federation: true serveur_keycloak_realm: "{{ identite_realm | default('chezlepro') }}" # Thèmes du realm (déployés depuis files/themes/). Vide = thème Keycloak par défaut. serveur_keycloak_login_theme: "" serveur_keycloak_account_theme: "" # Cache des ressources de thème. true = prod (perf). false = lab (voir les tweaks sans casse-cache). serveur_keycloak_theme_cache: true serveur_keycloak_ldap_nom: "openldap" # Connexion à l'annuaire (url, users_dn, bind_dn, bind_password) : fournie par le rôle # partagé resoudre_annuaire (voir tasks/main.yml « Adopter la connexion annuaire »). serveur_keycloak_ldap_username_attr: "uid" serveur_keycloak_ldap_uuid_attr: "entryUUID" serveur_keycloak_ldap_object_classes: "inetOrgPerson" # Bundle d'AC de confiance (contient la racine step_ca, posée par client_pki) pour LDAPS. serveur_keycloak_truststore_path: "/etc/ssl/certs/ca-certificates.crt" # --- Clients OIDC applicatifs (enregistres dans le realm, kcadm idempotent) --- # Chaque app qui delegue son auth a Keycloak declare ici son client confidentiel. # Le 'secret' reference la MEME variable de voute que l'app (ex. grafana : vault_grafana_oidc). # Ex. (a mettre en group_var d'instance, pas en defaut) : # serveur_keycloak_clients: # - clientId: grafana # redirect_uris: ["https://grafana.{{ domaine_interne }}/login/generic_oauth"] # web_origins: ["https://grafana.{{ domaine_interne }}"] # secret: "{{ vault_grafana_oidc }}" serveur_keycloak_clients: [] # --- RBAC OIDC (additif, idempotent) --- # Rôles de realm à garantir (ex. ['grafana-admin', 'grafana-editor']). serveur_keycloak_realm_roles: [] # Clients recevant le mapper « roles » (rôles de realm → claim 'roles' dans le jeton). serveur_keycloak_role_mapper_clients: [] # Assignations rôle→utilisateur (ex. [{user: testmail, role: grafana-editor}]). # En prod, préférer l'assignation via groupe d'annuaire ; ici, explicite pour la preuve. serveur_keycloak_role_assignments: [] # TLS vers PostgreSQL (zero-confiance). Vide = pas de verif (compat). 'verify-full' = # chiffre + verifie le cert serveur contre le root_ca step-ca (l'hote doit etre dans le SAN). serveur_keycloak_db_sslmode: "" serveur_keycloak_db_sslrootcert: "/etc/step/certs/root_ca.crt" # --- Projection des groupes LDAP (D-65) --------------------------------------- # LDAP porte les habilitations ; Keycloak les traduit en groupes de realm, puis en # roles que les services lisent dans le jeton. Sans ce mapper, la chaine s'arrete # avant le premier service et l'assignation se fait utilisateur par utilisateur — # ce que D-66 interdit. serveur_keycloak_groupes_ldap: true serveur_keycloak_groupes_mapper_nom: "groupes-ldap" # DERIVE de l'annuaire (resoudre_annuaire), jamais ecrit. serveur_keycloak_groupes_dn: "ou=groups,{{ resoudre_annuaire_base_dn | default('') }}" # Le schema REEL de l'annuaire Set-OPS : `serveur_openldap` cree `ou=groups`, et # `amorcage_acces` y pose des `groupOfNames` dont les membres sont des DN. serveur_keycloak_groupes_object_class: "groupOfNames" serveur_keycloak_groupes_membership_attr: "member" # Quel groupe LDAP porte quel role de realm. C'est ce qui REMPLACE l'assignation # nominative : ajouter quelqu'un au groupe suffit, aucun deploiement n'est requis. # Les roles doivent exister (`serveur_keycloak_realm_roles`). serveur_keycloak_groupes_roles: [] # Le claim qui porte les groupes dans le jeton, et les clients qui le recoivent. # Sans ce mapper de protocole, les groupes existent dans le realm mais n'atteignent # jamais les services : Forgejo et Nextcloud liraient un claim vide. serveur_keycloak_groupes_claim: "groups" serveur_keycloak_groupes_mapper_clients: [] # Mode d'ecriture de la federation LDAP. TROIS valeurs possibles, et une seule tient # avec la doctrine : # # READ_ONLY Keycloak lit, n'ecrit jamais. Un changement de mot de passe echoue sur # « Federated storage is not writable » — donc `pwdReset` est un cul-de-sac : # l'annuaire EXIGE le changement, et Keycloak ne peut pas le faire. # UNSYNCED Keycloak ecrit dans SA base. Le mot de passe ne serait plus dans LDAP : # Dovecot et Postfix, qui s'y lient DIRECTEMENT (D-39), continueraient de # valider l'ancien. Une identite, deux mots de passe — exactement ce que la # doctrine interdit. # WRITABLE Keycloak ecrit A TRAVERS vers LDAP. L'annuaire reste la source unique ; # Keycloak n'en est qu'un client. C'est le seul mode coherent. # # Constate le 2026-08-07 : READ_ONLY etait code en dur, et la premiere reprise du # sysadmin butait dessus. serveur_keycloak_ldap_edit_mode: "WRITABLE" # Compte d'amorcage dont le changement de mot de passe doit etre EXPLICITE dans # Keycloak. Vide = aucun alignement. Voir roles/amorcage_acces (D-67). serveur_keycloak_amorcage_uid: "{{ amorcage_acces_uid | default('sysadmin') }}" # Empreinte du mot de passe de LIAISON LDAP applique — Keycloak le masque, on ne # peut donc pas le comparer. Permet sa rotation. Voir tasks/federation-ldap.yml. serveur_keycloak_marqueur_bind: "/etc/keycloak/.setops-bind-sha256" # --- Courriel du realm : « mot de passe oublie » ------------------------------ # Sans SMTP, `resetPasswordAllowed` ne sert a rien — Keycloak n'a aucun moyen # d'envoyer le lien. Et sans « mot de passe oublie », chaque oubli passe par # l'exploitant, qui manipule alors le mot de passe de quelqu'un d'autre : ce que # « une identite, une personne » cherche precisement a eviter. # # Le relais est le MTA du tenant, DERIVE du plan (`applications.postfix.hote`) : # il accepte les hotes du supernet sans authentification (`mynetworks`), et # `idm-01` porte deja `client_smtp` — le flux existe. serveur_keycloak_smtp_actif: true serveur_keycloak_smtp_app: "postfix" serveur_keycloak_smtp_port: 25 serveur_keycloak_smtp_starttls: true serveur_keycloak_smtp_expediteur: "identite@{{ domaine_interne }}" serveur_keycloak_smtp_nom_expediteur: "{{ organisation | default('Set-OPS') }}" # Repli : utilise seulement si le plan ne declare pas d'application `postfix`. serveur_keycloak_smtp_hote: "" # « Mot de passe oublie » sur l'ecran de connexion. Exige le SMTP ci-dessus et une # adresse `mail` sur le compte — sans elle, Keycloak n'a nulle part ou ecrire. serveur_keycloak_reset_mot_de_passe: true # --- Durcissement de la federation LDAP --------------------------------------- # Forme de l'API (chaque valeur est une LISTE de chaines) : on ecrit le dialecte # reel plutot que de le fabriquer en Jinja. # # `usePasswordModifyExtendedOp` — sans lui, Keycloak ecrit l'attribut `userPassword` # a la main et l'overlay `ppolicy` de slapd ne voit rien passer. # `validatePasswordPolicy` — fait valider la politique DU REALM avant l'ecriture. # C'est le controle porteur : Keycloak se lie en rootDN, et slapd n'applique pas # ses controles de qualite au rootDN. Les deux cles ensemble, pas l'une ou l'autre. # `syncRegistrations` — sans lui, un compte cree dans Keycloak n'atteint JAMAIS # l'annuaire (mesure du 2026-08-08 : `POST users` -> 201, rien dans `ou=people`). # Il aurait un acces web, aucune boite aux lettres, et serait invisible du modele # de groupes : Postfix et Dovecot lisent LDAP, pas Keycloak. # `trustEmail` — l'adresse vient de l'annuaire, qui est la source de verite ; la # faire re-verifier par courriel demanderait a l'utilisateur de confirmer ce que # l'exploitant a deja etabli. # Les delais — un annuaire qui PEND, plutot qu'il refuse, bloque sinon les fils de # Keycloak sans limite. serveur_keycloak_ldap_durcissement: usePasswordModifyExtendedOp: ["true"] validatePasswordPolicy: ["true"] syncRegistrations: ["true"] trustEmail: ["true"] connectionTimeout: ["5000"] readTimeout: ["10000"] # Mappeurs d'attributs a reconcilier : nom du mappeur -> attribut LDAP. Voir # tasks/attributs-ldap.yml pour ce que le defaut de Keycloak faisait de travers. serveur_keycloak_ldap_attributs: first name: "givenName" last name: "sn" email: "mail" username: "uid"