Le realm portait une politique d'acces complete et aucun moyen d'ecrire a qui que ce soit. Tout oubli remontait donc a l'exploitant, qui n'avait d'autre choix que de manipuler le mot de passe d'autrui. - serveur_keycloak/tasks/courriel-realm.yml : reconcilie smtpServer et resetPasswordAllowed ; hote du relais DERIVE de applications.postfix.hote, et refus explicite si le plan ne declare pas de MTA. - passe par l'API d'administration : kcadm.sh accepte les deux formes -s sur une map, sort en succes et n'ecrit rien (smtpServer reste vide). - amorcage_acces_courriel redevient a declarer : cette adresse designe une personne, hors du systeme qu'on amorce ; une boite interne serait illisible tant qu'on n'a pas l'acces qu'on cherche justement a recuperer. - autorisation.md §6.6 : le mecanisme, ses deux conditions, et l'ecart d'adresse laisse par l'ancien mode READ_ONLY de la federation. Preuve : banniere SMTP lue depuis idm-01, RCPT TO accepte, execute-actions-email declenche, MTA en starttls -> relay=mx.chezlepro.ca status=sent (250). Second deploiement changed=0. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
144 lines
7.9 KiB
YAML
144 lines
7.9 KiB
YAML
---
|
|
# 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
|