Directive : toute authentification web passe par Keycloak, LDAP est la source
unique des comptes, chaque service garde un accès de secours par sudo sur
l'hôte. Les trois sont indissociables — la chaîne service → Keycloak → LDAP est
en série, donc sans secours une panne exclut tout le monde, y compris pour
réparer. Portée : le web seulement ; IMAP/SMTP se lient à LDAP directement et
SSH est en clé seule.
Posture <rôle>_connexion_locale, false par défaut. Le compte local existe — il
ne peut pas dépendre de Keycloak — mais son formulaire n'est plus proposé au
repos : ouvert en permanence, il contourne la politique de mot de passe, le MFA
et surtout la révocation centrale.
Vérifié auprès de l'amont, puis par rendu réel des gabarits dans les deux
postures :
- Grafana GF_AUTH_DISABLE_LOGIN_FORM → ferme ;
- Forgejo ENABLE_INTERNAL_SIGNIN + ENABLE_BASIC_AUTHENTICATION → ferme, API
Basic comprise. N'existe que depuis la v10 (ticket amont 7476) ; le rôle
épingle 10.0.0 et un assert refuse la fermeture en deçà, car le réglage serait
ignoré sans erreur ;
- Nextcloud hide_login_form → MASQUE seulement : ?direct=1 reste le chemin de
secours documenté par l'amont. Écrit comme tel, sans prétendre à l'équivalence.
Défaut attrapé par le rendu : la condition Forgejo sans `| bool` n'émettait rien
dans aucune posture — une valeur en chaîne est vraie au sens Jinja, la connexion
locale serait restée ouverte en silence.
docs/authentification.md, décisions D-38 à D-41. ansible-lint (production) sans
échec, 28 preuves OK.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Fin des dernières poches de 'codé en dur' liées à l'instance d'origine :
- realm SSO centralisé sur l'intrant identite_realm (defaut chezlepro,
retro-compatible) ; les 4 rôles (keycloak/forgejo/grafana/oauth2_proxy)
en dérivent. Expose dans la GUI (panneau Intrants).
- vars brandees renommees generiques : chezlepro_timezone -> fuseau_horaire,
chezlepro_organisation -> organisation (chrony, openldap, GUI, docs).
Aucune reference fonctionnelle aux anciens noms. Le moteur ne porte plus le
nom d'un tenant. Technolibre a son propre realm (technolibre).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Machinerie additive/idempotente dans serveur_keycloak (rbac-oidc.yml) :
rôles de realm + mapper 'roles' sur les clients choisis + assignations
rôle→utilisateur. kcadm à chaud (zéro coupure SSO). Grafana :
role_attribute_path (grafana-admin→Admin, grafana-editor→Editor, sinon
Viewer). Prouvé sur id-sso-01 (idempotence) : testmail = grafana-editor.
Complète le dashboard logs (Viewer-friendly) : les deux volets de la
question « testmail peut-il voir les logs ? ».
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Provisioning de dashboards (provider + JSON) : un dashboard logs avec
sélecteur d'hôte + filtre regex + panneau logs + débit. Référence Loki par
une VARIABLE de datasource (ds_loki), pas un UID codé en dur.
Leçon : ajouter un uid explicite à une datasource déjà provisionnée casse
le démarrage de Grafana (« data source not found ») → variable de datasource.
Permet aux Viewers (dont testmail) de voir les logs sans accès Explore.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
serveur_grafana : config OIDC via GF_AUTH_GENERIC_OAUTH_* (client grafana,
realm chezlepro, secret vault_grafana_oidc). serveur_loki/prometheus/grafana
déployés sur obs-01. Fix serveur_loki : créer le groupe loki (paquet crée
l'user en nogroup).
Prouvé (flux authorization code headless via l'edge) : testmail (LDAP) se
connecte à Grafana par le SSO ; /api/user renvoie login/email/name fédérés
depuis LDAP. Chaîne LDAP → Keycloak → Grafana.
Gaps notés : enregistrement du client OIDC dans Keycloak fait via kcadm à la
main (à codifier) ; résolution keycloak→edge sur obs-01 = /etc/hosts manuel
(PowerDNS devrait porter les A d'exposition) ; mapping de rôles Grafana.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Les rôles à secrets déclaraient serveur_X_password: "" avec la variable
de voûte seulement en commentaire — aucun mapping réel. Remplir la voûte
ne branchait donc rien (secret vide → assertion échoue).
Les 12 secrets des 8 rôles pointent maintenant vers leur source :
serveur_X_password: "{{ vault_X | default('') }}"
Comportement inchangé si la voûte est vide ; branché dès qu'elle a le
secret. Prouvé : assertion step_ca passe avec vault_step_ca_password.
Chaque instance remplit ses vault_* ; aucun mapping par instance.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>