serveur_keycloak : fédération LDAP (modèle A) automatisée et prouvée
Le rôle configure via kcadm (idempotent) un realm applicatif + un provider de stockage LDAP READ_ONLY vers OpenLDAP (LDAPS, uid/entryUUID/inetOrgPerson). LDAPS validé par le truststore système (truststore-paths → bundle CA, racine step_ca via client_pki, désormais requis sur le nœud). Secrets par environment + no_log. tasks/federation-ldap.yml. Éprouvé avant codification, puis prouvé par le rôle : testmail (user LDAP) obtient un token via le realm chezlepro (HTTP 200) ; redéploiement idempotent (changed=0). Modèle d'identité A (OpenLDAP source → Keycloak fédéré) complet. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
parent
c33cdf6729
commit
c707b693c4
5 changed files with 92 additions and 0 deletions
|
|
@ -36,6 +36,15 @@
|
|||
connecté à PostgreSQL (**87 tables** du realm master écrites), **token admin obtenu** (auth
|
||||
adossée à la BD). Lacunes connues (documentées `catalogue-services.md`) : fédération LDAP et
|
||||
edge nginx pas encore câblés. Reste : DRY du bloc de résolution BD (keycloak/forgejo/icinga).
|
||||
- **`serveur_keycloak` — fédération LDAP (modèle d'identité A) : automatisée et prouvée.** Le rôle
|
||||
configure, via `kcadm` (idempotent), un realm applicatif (`serveur_keycloak_realm`, déf.
|
||||
`chezlepro`) et un **provider de stockage LDAP READ_ONLY** vers OpenLDAP (LDAPS, `uid`/`entryUUID`,
|
||||
`inetOrgPerson`). TLS LDAPS validé via le **truststore système** (`truststore-paths` →
|
||||
`/etc/ssl/certs/ca-certificates.crt`, racine step_ca posée par **client_pki**, désormais requis
|
||||
sur le nœud). Secrets par `environment` + `no_log`. **Éprouvé avant codification** puis prouvé
|
||||
par le rôle : un utilisateur LDAP (`testmail`) obtient un token via le realm (HTTP 200), et le
|
||||
redéploiement est **idempotent** (`changed=0`). Nouveau : `tasks/federation-ldap.yml`. Reste :
|
||||
edge nginx (accès HTTPS par nom), mappers d'attributs/groupes fins.
|
||||
|
||||
## 2026-07-02
|
||||
|
||||
|
|
|
|||
|
|
@ -19,3 +19,19 @@ serveur_keycloak_db_host: "10.0.13.11"
|
|||
# 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: "chezlepro"
|
||||
serveur_keycloak_ldap_nom: "openldap"
|
||||
serveur_keycloak_ldap_url: "ldaps://id-ldap-01.{{ domaine_interne }}:636"
|
||||
serveur_keycloak_ldap_users_dn: "ou=people,dc={{ domaine_interne.split('.') | join(',dc=') }}"
|
||||
serveur_keycloak_ldap_bind_dn: "cn=admin,dc={{ domaine_interne.split('.') | join(',dc=') }}"
|
||||
serveur_keycloak_ldap_bind_password: "{{ vault_openldap_admin | default('') }}"
|
||||
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"
|
||||
|
|
|
|||
58
roles/serveur_keycloak/tasks/federation-ldap.yml
Normal file
58
roles/serveur_keycloak/tasks/federation-ldap.yml
Normal file
|
|
@ -0,0 +1,58 @@
|
|||
---
|
||||
# Fédération LDAP via kcadm — idempotent. Crée le realm applicatif + le provider de
|
||||
# stockage LDAP (OpenLDAP, READ_ONLY). Éprouvé avant codification (testmail se connecte).
|
||||
- name: Attendre que Keycloak réponde
|
||||
ansible.builtin.wait_for:
|
||||
host: 127.0.0.1
|
||||
port: 8080
|
||||
timeout: 120
|
||||
when: not ansible_check_mode
|
||||
|
||||
- name: Configurer le realm et la fédération LDAP (kcadm, idempotent)
|
||||
ansible.builtin.shell:
|
||||
cmd: |
|
||||
set -eu
|
||||
KC={{ serveur_keycloak_home }}/bin/kcadm.sh
|
||||
CHANGED=0
|
||||
"$KC" config credentials --server http://localhost:8080 --realm master \
|
||||
--user {{ serveur_keycloak_admin_user }} --password "$KC_ADMIN_PW" >/dev/null
|
||||
if ! "$KC" get realms/{{ serveur_keycloak_realm }} >/dev/null 2>&1; then
|
||||
"$KC" create realms -s realm={{ serveur_keycloak_realm }} -s enabled=true >/dev/null
|
||||
CHANGED=1
|
||||
fi
|
||||
RID=$("$KC" get realms/{{ serveur_keycloak_realm }} --fields id 2>/dev/null \
|
||||
| grep -oiE '[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}' | head -1)
|
||||
if ! "$KC" get components -r {{ serveur_keycloak_realm }} -q name={{ serveur_keycloak_ldap_nom }} 2>/dev/null \
|
||||
| grep -q '"{{ serveur_keycloak_ldap_nom }}"'; then
|
||||
"$KC" create components -r {{ serveur_keycloak_realm }} \
|
||||
-s name={{ serveur_keycloak_ldap_nom }} -s providerId=ldap \
|
||||
-s providerType=org.keycloak.storage.UserStorageProvider -s parentId="$RID" \
|
||||
-s 'config.enabled=["true"]' \
|
||||
-s 'config.vendor=["other"]' \
|
||||
-s 'config.editMode=["READ_ONLY"]' \
|
||||
-s 'config.importEnabled=["true"]' \
|
||||
-s 'config.connectionUrl=["{{ serveur_keycloak_ldap_url }}"]' \
|
||||
-s 'config.usersDn=["{{ serveur_keycloak_ldap_users_dn }}"]' \
|
||||
-s 'config.bindDn=["{{ serveur_keycloak_ldap_bind_dn }}"]' \
|
||||
-s "config.bindCredential=[\"$LDAP_BIND_PW\"]" \
|
||||
-s 'config.authType=["simple"]' \
|
||||
-s 'config.searchScope=["2"]' \
|
||||
-s 'config.usernameLDAPAttribute=["{{ serveur_keycloak_ldap_username_attr }}"]' \
|
||||
-s 'config.rdnLDAPAttribute=["{{ serveur_keycloak_ldap_username_attr }}"]' \
|
||||
-s 'config.uuidLDAPAttribute=["{{ serveur_keycloak_ldap_uuid_attr }}"]' \
|
||||
-s 'config.userObjectClasses=["{{ serveur_keycloak_ldap_object_classes }}"]' \
|
||||
-s 'config.useTruststoreSpi=["always"]' \
|
||||
-s 'config.connectionPooling=["true"]' >/dev/null
|
||||
CHANGED=1
|
||||
fi
|
||||
[ "$CHANGED" = 1 ] && echo SETOPS_CHANGED || echo SETOPS_OK
|
||||
environment:
|
||||
KC_ADMIN_PW: "{{ serveur_keycloak_admin_password }}"
|
||||
LDAP_BIND_PW: "{{ serveur_keycloak_ldap_bind_password }}"
|
||||
register: serveur_keycloak_fed
|
||||
changed_when: "'SETOPS_CHANGED' in serveur_keycloak_fed.stdout"
|
||||
no_log: true
|
||||
retries: 5
|
||||
delay: 6
|
||||
until: serveur_keycloak_fed.rc == 0
|
||||
when: not ansible_check_mode
|
||||
|
|
@ -128,3 +128,7 @@
|
|||
enabled: true
|
||||
state: started
|
||||
daemon_reload: true
|
||||
|
||||
- name: Configurer la federation LDAP (realm + provider OpenLDAP)
|
||||
ansible.builtin.include_tasks: federation-ldap.yml
|
||||
when: serveur_keycloak_ldap_federation | bool
|
||||
|
|
|
|||
|
|
@ -8,3 +8,8 @@ db-username={{ serveur_keycloak_entree.proprietaire }}
|
|||
hostname={{ serveur_keycloak_hostname }}
|
||||
proxy-headers=xforwarded
|
||||
http-enabled=true
|
||||
{% if serveur_keycloak_ldap_federation %}
|
||||
|
||||
# Truststore (racine step_ca via le bundle systeme) pour valider LDAPS vers OpenLDAP.
|
||||
truststore-paths={{ serveur_keycloak_truststore_path }}
|
||||
{% endif %}
|
||||
|
|
|
|||
Loading…
Reference in a new issue