- client_pki : root_ca.crt en 0644 (un cert racine est PUBLIC ; requis pour
que les clients TLS non-root — keycloak, forgejo… — puissent vérifier).
- serveur_keycloak : db-url-properties sslmode=verify-full + sslrootcert.
Incident maîtrisé : 1er essai, keycloak (user keycloak) ne pouvait pas lire
root_ca 0600 → SSO down → restauré en <1 min → corrigé (0644) → verify-full OK.
Prouvé : realm 200, sslmode=verify-full, aucune erreur SSL/DB.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Zéro-confiance flux PG, côté serveur : cert-sync (owned postgres, motif .path
comme Dovecot/Postfix) + ssl_cert/key/ca_file pointant sur le cert step-ca au
lieu du snakeoil. Garde-fou : ssl non activé si le cert n'est pas en place.
Reload robuste (instance postgresql@NN-main, pas le wrapper).
Prouvé sur data-sql-01 : cert servi = Set-OPS Internal CA (vérif root_ca ok),
keycloak/forgejo toujours 200 (TLS reste optionnel, rien de cassé).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Fermeture des dettes de doc :
- nouvelle unité wiki « Autorisation & RBAC » (authZ, exemple Grafana) ;
- section « le renouvellement est un système » dans l'unité PKI ;
- docs/runbooks-exploitation.md (cert expiré, RBAC Grafana, branding).
15 unités wiki.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Bug latent de flotte : cert-renewer rechargeait un service nommé d'après le
cert (%i = FQDN, inexistant) au lieu du consommateur réel → nginx/postfix/
dovecot/slapd servaient l'ancien cert jusqu'à expiration. Symptôme : cert
edge périmé en mémoire → échec TLS OIDC → login SSO cassé.
Correctif : client_pki_reload_services (vrais consommateurs), par groupe.
Fix immédiat de l'incident : reload nginx sur l'edge.
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>
Branding via le dossier custom/ de Forgejo (mécanisme officiel, résistant
aux MAJ) : accent aurore par variables CSS, logo/favicon étoile, page
d'accueil brandée (home.tmpl), thème sombre, nom + méta. Codifié dans
serveur_forgejo (branding/app_name/theme). Prouvé sur forge-01.
+ CHANGELOG : entrées wiki pédagogique (14 unités) et GUI (boutons cohérents).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
EOF
Complète les 14 unités d'apprentissage au moule à 4 temps : Reverse-proxy,
Courriel, Bases de données, Cache, Métriques & journaux, Supervision & impact,
Virtualisation, Sécurité & durcissement, Infra as Code & idempotence,
Liaisons (bindings, avec l'analogie NetScaler). Navigation complétée.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
3 unités au moule à 4 temps : PKI & confiance (step-ca, chaîne, ACME, mTLS),
DNS & résolution (les 3 couches, le plancher), Sauvegardes (3-2-1,
restaurer-pour-prouver). Fondations = Identité + PKI + DNS complètes.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Set-OPS comme compagnon pédagogique : chaque service = une lentille sur un
fondamental TIC, méthodes génériques (on apprend OIDC, pas Keycloak).
Socle (source versionnée dans wiki/, à publier dans le wiki Forgejo) :
- Home : cadrage (moteur souverain ET outil d'apprentissage) ;
- _Sidebar : navigation (~15 unités, pilote active, reste à venir) ;
- Identité-et-SSO : unité-pilote, moule à 4 temps (concept → Set-OPS →
transférable → à toi de jouer) avec exercices casse-répare.
Le wiki pointe vers docs/ (anti-dérive), ne le recopie pas.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Audit des boutons : « Pousser » était surchargé — servait à cloner la VM
(🖥 Pousser = creer-vm) ET à déployer (= make deployer, identique à
« Déployer »). Corrigé :
- 🖥 Pousser -> « 🖥 Créer la VM » (il clone) ;
- tous les Pousser/Déployer -> « Déployer » (une action = un mot) ;
- dry-run obligatoire AUSSI sur le déploiement des pieds (évite un
déploiement à l'aveugle ; cohérent avec un seul « Déployer ») ;
- titres/messages/console : Poussée -> Déploiement.
Chaque bouton fait désormais ce qu'il dit. GUI déjà entièrement francisée.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
client_metrique/journal/smtp déployés sur 4 nœuds et prouvés : Prometheus
scrape les 4 cibles (UP), Loki reçoit leurs logs, un nœud relaie un courriel
système livré. Comble le trou serveur-vs-flotte de l'observabilité.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
§11 : liaison = concept-chapeau. Axe niveau (liens app / intégrations nœud).
Axe modalité orthogonal : requise (constitutive, doit échouer si absente) vs
optionnelle (élective, opt-in). Le requis est déjà appliqué implicitement ;
raffinement = le rendre explicite (requis: true|false, fail-fast).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
README du rôle : section Thème (vars login/account/theme_cache, fonctionnement,
piège cache 'immuable', mise en garde MAJ Keycloak — calque PatternFly/parents,
pas de réapparition de l'ancien thème mais retouche possible après MAJ majeure,
check-list). + README court dans le dossier du thème.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Remonte les variables de couleur PatternFly atténuées (v5/v6/générique :
Color--200, text--color--subtle...) + force titres/labels/aides en clair,
pour que le contenu de la console de compte soit lisible sur fond aurore.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Keycloak injecte le script dans <head> → il tournait avant que <body>
existe (insertBefore plantait) → pas de canvas. Corrigé : init différé à
DOMContentLoaded + repli taille sur window.innerWidth/Height. Dégradé aurore
posé aussi sur body (repli si le canvas échoue). Script copié côté account.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
serveur_keycloak_theme_cache (défaut true=prod). À false, ajoute
spi-theme-static-max-age=-1 + cache-themes/templates=false dans keycloak.conf
→ ressources servies en no-cache : les tweaks de thème sont visibles sans
lutter contre le cache 'immuable' du navigateur. Bac à sable = false.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Le SPA account peut être en PatternFly 4 (.pf-c-*) ou 5 (.pf-v5-*) selon la
version ; on cible les deux. Même correctif que le login : fond par défaut
effacé (transparent), ciel aurore + constellation remontés (z-index), cartes
en verre, champs et bouton en !important.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Le thème "keycloak" de KC 26 est PatternFly (body sans classe, .pf-c-*).
Corrections : fond gris par défaut sur .login-pf-page effacé (transparent),
ciel aurore remonté (z-index 0, sous le contenu z-index 1) → la constellation
s'affiche ; champs ciblés en .pf-c-form-control !important (le mot de passe
n'était plus blanc) ; carte/bouton/liens en !important.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Le JS (scripts=js/constellation.js) crée son propre ciel (canvas + aurore,
le template Keycloak n'en ayant pas) : champ d'étoiles scintillantes qui
dérivent, liens de constellation cyan, blob d'aurore ondulant. Respecte
prefers-reduced-motion. Reprend l'animation du site de l'Alliance.
Prouvé : constellation.js référencé + servi (200).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Thème login alliance-boreale (parent=keycloak + overlay CSS) reprenant
l'identité du site : ciel nocturne aurore, carte glassmorphism, logo étoile
(favicon du site), bouton dégradé aurore, police système (souveraineté).
Déployé dans themes/, appliqué au realm via kcadm loginTheme (idempotent).
Prouvé : page de login charge alliance.css (200) + logo.svg (200),
loginTheme=alliance-boreale actif.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Fin des 2 loose ends : icingaweb2 (LDAP dormant en mode SSO) résout via
resoudre_annuaire (redéploiement changed=0, SSO intact) ; client_ldap
(SSSD, dormant) ne pointe plus sur un idm-01 périmé.
Plus aucun rôle ne code en dur l'hôte d'annuaire — un seul point de vérité.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Postfix (edge-mta) sert :587 (master.cf : STARTTLS + SMTP AUTH, seuls les
authentifiés relaient) ; SASL délégué à Dovecot (infra-mail, passdb LDAP)
via un auth-listener réseau (inet_listener sasl :12345). Interne seulement.
Prouvé (swaks) : testmail s'authentifie (235), Postfix accepte (250 queued),
courriel livré dans la boîte. Le courriel souverain fait recevoir ET envoyer.
Pièges : Dovecot 2.4 exige un nom de section inet_listener ; nouveau service
master.cf → restart (pas reload).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Choix : sauvegarder la donnée d'état (non régénérable), pas les VM
(reconstructibles par le code). restic (chiffré, dédupliqué, rétention).
serveur_backup = cible SFTP (backup-01). client_backup = intégration par
nœud : restic + jobs déclaratifs + timer systemd + rétention, secrets voûte.
Prouvé de bout en bout sur le Tier 0 : infra-pki-01 → /etc/step-ca sauvegardé
hors-nœud vers backup-01, restauration byte-identique des clés CA.
Reste : jobs Tier 1 (pg_dump/slapcat/vmail/forgejo) + offsite (3-2-1).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Rôle utilitaire partagé resoudre_annuaire (comme resoudre_base) : dérive la
connexion OpenLDAP du domaine_interne + un hôte surchargeable, au lieu de
répéter ldaps://id-ldap-01... dans chaque rôle. Facts uri/port/base_dn/
users_dn/bind_dn/bind_password (secret no_log).
Migrés + prouvés (config neutre, changed=0) : keycloak (fédération, testmail
token 200), dovecot + postfix (flux courriel livré de bout en bout).
Piège : les defaults d'un rôle inclus ne persistent pas hors de son
exécution — publier via set_fact.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Supprimés : roles/{applications,backup,database,identity,monitoring,
proxmox,storage,web}/ (README seuls, taxonomie abandonnée — l'archi est
plate serveur_*/client_*) et les playbooks-échafaudages debug sans rôle
(nextcloud, collabora, client_supervision, web_frontal, web_dorsal).
Aucun host n'utilise ces groupes ; l'intention des capacités futures
reste documentée dans docs/catalogue-services.md.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
oauth2-proxy (v7.15.3) place Keycloak devant toute app sans OIDC natif
(auth external, utilisateur via en-tête). Rôle paramétrable, réutilisable.
Éprouvé devant icingaweb2 (backend=external, X-Forwarded-Preferred-Username).
Prouvé : testmail → oauth2-proxy → Keycloak → icingaweb2 /dashboard,
connecté. Ferme le gap LDAP-direct d'icingaweb2.
Réglages appris : insecure_oidc_allow_unverified_email (IdP interne) ;
reverse-proxy passe X-Forwarded-* pas X-Auth-Request-* ; handler nginx en
restart (pas reload) — un changement d'adresse d'écoute n'est pas pris par
un reload gracieux.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
serveur_icingaweb2 installe + active businessprocess, crée le répertoire
des processus (éditable UI) et sème des processus BPM en IaC
(serveur_icingaweb2_bpm_processes). Format host;service éprouvé via les
fixtures du module.
Prouvé : processus « Supervision Chezlepro » (roll-up load/procs/swap/
ping4/ssh en ET) rend un état dans l'UI, backend IcingaDB (pas d'IDO).
Rôle re-prouvé (reset → recrée le processus). Pile Icinga = moteur +
Web 2 + BPM.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
App PHP (php8.4-fpm) + nginx local, exposée par l'edge (auto-dérivé).
Config par fichiers .ini (config/resources/authentication/roles + module
icingadb), pas d'assistant. Base IcingaDB via resoudre_base. Auth LDAP
direct (client_pki sur sup-01) — pas d'OIDC natif (SSO-proxy = raffinement).
Prouvé : testmail (LDAP) se connecte (/dashboard), module IcingaDB affiche
la supervision (hôte icinga). Déploiement failed=0 (frictions dans le
simulateur curl, pas le rôle). Reste : module BPM.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Déployé sur data-sql-01 : service actif, :6379, requirepass (vault_redis)
— accès sans auth refusé (NOAUTH), SET/GET avec auth OK. 🔧→⭐
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Le bloc copié-collé dans keycloak/forgejo/icinga (charger le registre,
filtrer par consommateur, déréférencer le secret via lookup('vars'),
résoudre hôte/port) extrait dans roles/resoudre_base (facts génériques,
no_log). Les 3 rôles l'incluent + adoptent les facts. Le secret ne quitte
toujours pas le rôle.
Fait « sur la preuve » : re-déploiement keycloak + forgejo failed=0,
idempotent, testmail token Keycloak HTTP 200. Ferme le reste de la Phase 2
des bindings.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Unbound par nœud (127.0.0.1) : stub-zone vers PowerDNS (interne) + récursion
Internet (ou forward). Alternative dynamique au plancher /etc/hosts. Bascule
resolv.conf protégée (apply+confirm) ET validée avant (Unbound doit résoudre
interne + Internet, sinon pas de bascule → nœud jamais coupé). Sûr par défaut
(apply=false).
Prouvé sur data-sql-01 : dig @127.0.0.1 keycloak/id-sso-01 → PowerDNS,
deb.debian.org → récursion, apt OK. Rollout flotte = opt-in par nœud.
Note : OPNsense embarque Unbound → l'Unbound réseau pourra vivre sur
l'appliance de bordure (nœud public, Étape B) ; ce rôle reste complémentaire.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Le plancher pose <IP edge> <FQDN exposé> sur chaque nœud, dérivé de expose +
domaines.edge (même source que nginx/PowerDNS). DNS-indépendant, aucun risque.
Chargement du plan best-effort (stat delegate_to localhost + become false).
Prouvé : /etc/hosts d'obs-01 régénéré avec keycloak/grafana → edge (ligne
manuelle éliminée), SSO Grafana fonctionne via le plancher (login: testmail).
Boucle Phase 3 fermée : expose → vhost + SAN cert + A PowerDNS + alias plancher.
Bugs corrigés : serveur_loki (groupe loki manquant) déjà committé ; stat
sur cible→contrôle ; become inutile sur le contrôle.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
La zone interne inclut désormais un A pour chaque FQDN d'exposition
(expose des applications) vers l'edge qui le sert (domaines.edge), via
expositions_des_applications. Déclarer expose → vhost + SAN cert + DNS.
Prouvé : dig @infra-dns-01 grafana/keycloak.lab.chezlepro.internal → edge .21.
Limite : PowerDNS est autoritatif (pas récursif) — pour que les nœuds
l'utilisent sans casser Internet, il faut un récursif (pdns-recursor) ou
garder le plancher /etc/hosts. Ne pas repointer client_dns naïvement.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Liste déclarative serveur_keycloak_clients (clientId, redirect_uris,
web_origins, secret) → tasks/clients-oidc.yml enregistre chaque client
confidentiel via kcadm (create-si-absent, no_log). Décision côté Keycloak
(creds admin non répandus dans les rôles app) ; secret = même var de voûte
que l'app.
Prouvé : client grafana supprimé → rôle → recréé → testmail se connecte à
Grafana ; redéploiement idempotent (changed=0). Déploiement Grafana au SSO
désormais autonome.
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>
client_pki ajouté à l'edge, ses SANs incluent le FQDN d'exposition
(keycloak.lab.chezlepro.internal), serveur_nginx_certificat pointe sur le
cert client_pki. Prouvé : HTTP 200, ssl_verify_result=0, émetteur step_ca.
Sans nouveau code (client_pki + group_var).
Gaps notés : recharger nginx au renouvellement du cert ; auto-dériver les
SANs d'exposition depuis le plan.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Sans changement de code : la machinerie d'exposition existait déjà
(serveur_nginx_publier_expositions lit expose + domaines.edge, génère le
vhost avec X-Forwarded-*). Prouvé : keycloak.lab.chezlepro.internal →
http://192.168.15.81:8080, issuer OIDC en https par nom via l'edge.
Limite connue : cert edge = snakeoil (avertissement). Raffinement
recommandé : FQDN d'exposition dans client_pki_sans de l'edge + pointer
serveur_nginx_certificat sur le cert client_pki.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
set -euo pipefail + executable /bin/bash sur la tâche kcadm. Sûr ici (les
grep vides n'alimentent que des assignations, tolérées par set -e).
Vérifié : lint production 0 échec, redéploiement idempotent (changed=0),
testmail token HTTP 200.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
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>
PostgreSQL (data-sql-01) : écoute réseau + pg_hba VLAN, provisionne la
base keycloak depuis le registre bases-donnees.yml → binding app→base
prouvé en réel (base + rôle créés, mdp vault_bd_keycloak).
Keycloak 26.0.7 (id-sso-01) : mode prod, connecté à PostgreSQL (87 tables
du realm master écrites), token admin obtenu (auth adossée à la BD).
Maturité rafraîchie. Lacunes connues : fédération LDAP + edge nginx pas
encore câblés. Reste : DRY du bloc de résolution BD.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Après audit du dépôt : docs/carte-set-ops.md = point d'entrée « à lire
d'abord » (index du corpus) + catalogue des mécanismes transverses (2
directions de binding, pont de cert, résolution BD par registre,
socle-first, check-mode, voûte) avec où ils vivent. But : ne plus
re-découvrir l'existant.
Constat : la cruft était déjà inventoriée dans catalogue-services.md
(rôles-catégories inertes, échafaudages) — référencée, pas dupliquée.
catalogue-services « État d'implémentation » rafraîchi (rôles éprouvés
sur VM réelles : socle, PKI, LDAP, DNS, nginx, pile courriel).
Pointeur ajouté depuis architecture-set-ops.md.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Constat : le binding app→base existe déjà, côté base (consommateur/portee
dans bases-donnees.yml), résolu dans le rôle au déploiement (include_vars +
lookup('vars', secret)), sur 4 rôles. Délibérément conservé (le secret ne
quitte jamais le rôle) — ne pas dupliquer en app-side/instancier.
Note corrigée : §3.1 raffinée (app→app côté app, app→base côté base — deux
directions assumées) ; §5 réécrite ; §9 mise à jour. Reste (session Keycloak) :
factoriser le bloc de résolution copié-collé en include partagé (DRY).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Une application déclare ses liens ([{vers, role}]) dans applications.yml ;
chaque rôle décrit les liens acceptés dans meta/liens.yml. instancier
résout la cible (FQDN interne dérivé de la nomenclature) et injecte les
variables en host_vars du consommateur.
Migration prouvée : les liens mail Postfix→Dovecot (mailstore) et
Postfix→rspamd (milter) passent de group_vars codés en dur à des liens
déclaratifs — make instancier donne DIFF VIDE (mêmes vars générées).
Cf. docs/bindings-conception.md.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>