Commit graph

94 commits

Author SHA1 Message Date
1ad7b48483 GUI : bascule d'instance (vraiment multi-instance)
Demande explicite et repetee de l'operateur : gerer les instances DEPUIS le GUI.

- Inventaire resolu DYNAMIQUEMENT : le serveur figeait l'inventaire au demarrage
  (args.inventaire.resolve()). Il est desormais relu depuis le symlink instance/
  a chaque requete (propriete Gestionnaire.inventaire) -> la bascule prend effet
  sans redemarrer. Les FICHIER_* de plan suivaient deja le lien. SETOPS_INVENTAIRE
  force encore un inventaire fixe (CI).
- POST /api/instance-utiliser + basculer_instance(nom) : repointe le symlink avec
  les garde-fous de make instance-utiliser + validation STRICTE (nom doit etre une
  instance decouverte -> pas de traversee de chemin ; ../etc refuse, teste).
- Vue Reseau : bouton « Activer » par instance dans la table de flotte. Confirme
  (avertissement renforce si production), bascule, recharge -> toutes les vues et
  les deploiements visent la nouvelle instance.

L'edition de federe reste au CLI (rarement change) ; la bascule est CLI ET GUI.

docs/carte-set-ops.md : la ligne Multi-instance du tableau des mecanismes documente
la decouverte (glob des dossiers freres, aucun registre) et le garde-fou P21.

Valide : node --check ; bascule testee de bout en bout (repoint -> inventaire
dynamique suit -> traversee refusee -> restauration) ; make verifier rc=0
CONFORME 21/21.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-23 12:12:29 -04:00
b1460a6dde Multi-instances : make instances (vue + collision) + preuve P21
La bascule d'instance existait deja (make instance-utiliser / instance-courante,
repointage du symlink). Manquaient la vue d'ensemble et le filet de securite.

- make instances (scripts/instances.py) : liste les instances de la federation
  (depots freres avec plan/nomenclature.yml), marque l'active (*), montre index,
  plage VLAN derivee, statut federe/local et production. Lecture seule.
- Detection de collision d'index entre instances FEDEREES (memes VLAN/VMID sur le
  trunk) : le piege exact vecu (prod + labo tous deux a l'index 1) est desormais
  crie. Mode --verifier -> rc=2 sur collision.
- Preuve P21 : cable ce garde-fou dans make prouver / make verifier. No-op quand
  moins de deux instances federees sont presentes (comme P17 sans SETOPS_MODELES).

Valide : make instances liste les 3 instances (labo LOCAL, Technolibre + Chezlepro
federees, index 2 et 13) ; detection testee en synthetique (deux federees meme
index -> collision ; labo federe:false -> coherent) ; make verifier rc=0
CONFORME 21/21.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-23 11:26:01 -04:00
36a882b125 Adressage derive du seul seed index (rupture, mode compact retire)
Principe : les valeurs de configuration se derivent des intrants, elles ne se
reecrivent pas a la main. La nomenclature dupliquait ce qu'index determine deja
(supernet, sous-reseaux, passerelles, VLAN). Corrige en rupture nette.

- inventory_rules : source unique de derivation — supernet_de, base3_de,
  sous_reseau_de, passerelle_de, vlan_de. Modele 6 zones encode une fois
  (2e octet = 10+index, 3e octet zone = 15+categorie, VLAN = 1000+index*10+zone).
  deriver_nomenclature ne lit plus aucun adressage stocke ; mode compact supprime.
- devis_reseau : importe ces helpers (fin de la duplication) ; decouvre les
  tenants sur `index` present (filtre vmid_schema retire).
- GUI : `index` devient un INTRANT (section Reseau). Il vit dans la nomenclature
  (plan reseau uniforme, contrairement aux intrants des modeles heterogenes) et
  le GUI l'ecrit chirurgicalement (une ligne, sans reformater). Le miroir JS
  derive le VLAN du seed (fin de la lecture de c.vlan stocke).
- socle public : nomenclature au format maigre.

Preuve P20 (preuve_nomenclature_derivee) : aucune nomenclature ne stocke
d'adressage — garde-fou permanent, teste en negatif.

Valide : DIFF VIDE sur les 3 instances (la derivation reproduit exactement
l'adressage stocke), 7 modeles valident, devis_reseau genere les memes VLAN
(1011-1016 derives), make verifier rc=0 CONFORME 20/20, node --check du GUI OK.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-23 02:58:15 -04:00
7e190e0a57 Trois preuves qui regardent au-dela d'une seule instance + champ liens/websocket au GUI
Le harnais ne verifiait qu'UNE instance et le seul modele socle. Tout ce qui vit
a cote du moteur echappait au controle. Trois preuves ferment ces angles morts :

- P17 (scripts/modeles.py) : TOUS les modeles valident, pas seulement socle.
  SETOPS_MODELES=../Set-OPS-Modeles inclut les modeles assembles prives. A trouve
  6 modeles invalides sur 7 (corriges dans Set-OPS-Modeles).
- P18 (scripts/voute.py) : le gabarit vault.yml.example couvre EXACTEMENT les secrets
  que le plan exige (bases + roles actifs + group_vars). Ne dechiffre jamais la vraie
  voute : compare des noms.
- P19 (scripts/couverture_gui.py) : tout champ present dans un plan reel est editable
  par le GUI. A trouve applications.websocket (comble). Nomenclature toleree (trou connu).

GUI :
- champ « Liens (bindings) » dans l'inspecteur d'application : role -> cible en listes
  deroulantes, les roles proposes = ceux que le role porteur accepte (meta/liens.yml).
  Comble un manque : les bindings ne se declaraient qu'en editant le YAML a la main.
- champ « WebSocket » (Collabora).
- CHAMPS_ECRITS_PAR_GUI : declaration de ce que le GUI sait ecrire, verifiee par P19.

Garde-fou de fond : valider_applications refuse une application posee sur un hote non
declare (l'hote fantome exact qu'integral portait). Cable partout + POST du GUI.

liens_acceptes()/catalogue_liens() dans inventory_rules : source unique partagee par le
validateur, le GUI et instancier.py (dont la copie locale est retiree).

Valide : make verifier rc=0, CONFORME 19/19, ansible-lint 0 echec, 7 modeles valident,
DIFF VIDE, node --check du GUI OK. Piece justificative : docs/audit/preuve-2026-07-22.md.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-22 21:32:42 -04:00
131a30075d Aurore : opacites reduites ; etat de forge-01 elucide
Palette (promo/, les 4 pages) — les teintes rose, verte et violette etaient
trop presentes une fois etalees au tour precedent. Reduction d'environ 40 % :
rose .075 -> .044, vert .058 -> .034, violet .062 -> .036, arrets
intermediaires abaisses dans la meme proportion, opacite de la nappe .aurora
.44 -> .32. Geometrie inchangee : meme largeur, meme position, seule
l'intensite baisse. 0 conflit CSS entre les quatre pages.

forge-01 — la note precedente presentait comme une contradiction a trancher
le fait que le plan dise `etat: planifie` et que le CHANGELOG du 2026-07-03
dise le branding « prouve sur forge-01 ». Ce n'en etait pas une : l'hote a ete
cree, puis supprime. Le plan decrit l'etat COURANT, le CHANGELOG un etat PASSE
— les deux disent vrai. La preuve reste valable, simplement non rejouable tant
que l'hote n'est pas recree. roles/serveur_forgejo/README.md corrige.

Valide : equilibre accolades/parentheses du CSS, 0 conflit CSS,
prouver.py --verifier -> CONFORME 16/16.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-22 11:57:50 -04:00
f8e754c0b0 Aurore rose-mauve et vert fluo ; thème Forgejo étendu au wiki
Palette (promo/, les 4 pages) — ajoute --rose:#ff6fc4 (frange magenta) et
--vert:#5cff9d (vert fluo), uniquement dans les DÉGRADÉS FONCÉS : fond fixe du
corps, nappe .aurora dérivante, filets .rule. Opacités de 5,5 % à 8,5 %, une
teinte et non un motif. Le vert entre par la gauche et le rose sort par la
droite, comme une vraie aurore. --aurora (texte et boutons) est inchangé :
l'identité de marque ne bouge pas. Appliqué identiquement aux quatre fichiers,
0 conflit CSS après coup.

Thème Forgejo (alliance.css, 29 → 118 lignes) — la feuille étant injectée par
templates/custom/header.tmpl sur toutes les pages, le wiki est couvert sans
feuille distincte. Réorganisée en deux sections de risque explicite :
  §1 Variables — couleurs officielles + ciel nocturne. Sûr, résiste aux
     mises à jour. ÉPROUVÉ sur forge-01 (CHANGELOG 2026-07-03).
  §2 Décor — fond aurore, filet sous les titres, citations, tableaux et code
     en ligne de .markup. Fragile (classes internes). JAMAIS RENDU.
Supprimer §2 ramène au thème sobre. Le ciel nocturne ne s'applique qu'aux
thèmes sombres, pour ne pas casser le thème clair.

docs/theme-forgejo-hors-flotte.md — pose manuelle sur une instance Forgejo non
gérée par Set-OPS (la forge historique qui héberge ce dépôt et son wiki).
Forgejo n'ayant aucun réglage web pour le CSS, il faut déposer la feuille sur
le serveur. La procédure ne duplique aucun fichier : elle pointe vers ceux du
rôle. Inclut la détection du répertoire custom, un garde-fou pour ne pas
écraser un header.tmpl existant (ajout de ligne), la vérification curl et la
marche arrière.

Corrige deux affirmations fausses de ma part :
- « thème jamais rendu par un Forgejo réel » était faux pour §1, éprouvée.
  Le statut est désormais donné section par section.
- Contradiction consignée sur forge-01 : le plan la dit `etat: planifie`, le
  CHANGELOG 2026-07-03 dit le branding « prouvé sur forge-01 ». Les deux ne
  peuvent pas être vrais ; l'écart est écrit dans le README du rôle, à trancher.

Validé : équilibre accolades/parenthèses du CSS, 0 conflit CSS entre les quatre
pages, lien de doc résolu, ansible-lint 0 échec sur 485 fichiers,
prouver.py --verifier → CONFORME 16/16.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-21 17:42:52 -04:00
ea2d490e20 Protocole d'épreuve de l'opérateur indépendant (AFF-002)
AFF-002 (« Set-OPS s'exploite sans aucune IA ») était  par inspection : ses
écarts bloquants soldés et le parcours relu, mais jamais exécutée par un
opérateur qui n'est pas l'auteur. Aucune commande locale ne peut la prouver.

Ajoute le protocole de l'épreuve humaine : départ à froid depuis le modèle
public `socle`, sur la grappe Proxmox de l'opérateur. Règle du silence
(N0 journalise / N1 déblocage après 30 min, consigné comme défaut / N2 arrêt
sécurité), interdits (aucune IA, aucun dépôt privé, aucune lecture de
docs/audit/ qui divulguerait les pièges connus), critères R1→R6 fixés
d'avance, périmètre matériel, gabarit de rapport.

Le registre peut perdre : la redescente d'AFF-002 en 🟡 ou  selon le verdict
est explicitement prévue.

Le protocole ne nomme personne — il est réutilisable, et l'identité de
l'opérateur vit dans son rapport, sous réserve de consentement.

Validé : prouver.py --verifier → CONFORME 16/16. Aucun code touché.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-21 09:46:21 -04:00
5a53332f96 make verifier inclut les preuves (make prouver --verifier)
- prouver.py : nouveau mode --verifier — exécute toutes les preuves du registre
  (verdict + code de sortie) sans écrire de rapport, pour ne pas écraser la pièce
  justificative committée docs/audit/preuve-<date>.md.
- Makefile : make verifier se termine par `python3 scripts/prouver.py --verifier`
  → verifier échoue si une preuve échoue. make prouver seul écrit toujours le rapport.
- docs/audit/README.md : section « Rapport avec make verifier » mise à jour.

Vérifié : make verifier → CONFORME 16/16 (voûte), aucun churn du rapport ; make
prouver écrit toujours ; ansible-lint 0 failure.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-20 21:03:20 -04:00
835f8ab6d0 Mise en conformité prouvable : registre d'affirmations + make prouver
Le dépôt fait / explique / prouve ce qu'il affirme, vérifiable en une commande.

- Phase 1 : docs/audit/affirmations.md — 54 affirmations publiques tracées vers
  une commande de preuve et un statut (/🟡//).
- Phase 2 : CLAUDE.md réduit à un pointeur mince ; contradiction SSH levée (le
  code applique déjà PasswordAuthentication no + AuthenticationMethods publickey,
  conforme à AGENTS.md) ; section AGENTS « Codex » → « agents IA ».
- Phase 3 : parcours démarrage réparé (QUICKSTART renvoyait à un modèle absent,
  chemins de voûte faux, commandes make périmées) ; make verifier vert
  (ansible-lint 33 → 0 : site.yml généré nommé, pipefail, name[template]) ;
  voûte Proxmox unifiée lue par le clonage (all/vault.yml).
- Phase 4 : make prouver → docs/audit/preuve-<date>.md, harnais rejouable qui
  rappelle l'outillage existant (aucune validation réimplémentée).
- Phase 5 : parcours QUICKSTART prouvé hors-ligne sur le socle ; modèle socle
  rendu valide (autorite interne → auto-heberge) ; split-brain d'inventaire
  corrigé (repli sur le répertoire existant, pas principal/).

make prouver : 15 OK, 0 échec, 1 sautée (voûte). ansible-lint : 0 failure.
Écarts découverts en cours de traitement (AFF-097..100) : tous résolus.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-20 19:53:18 -04:00
0580c931a7 GUI : vue Reseau (devis switch) + bouton Copier
Nouvel onglet lecture seule affichant le devis make devis-reseau (VLANs+SVIs+ACLs)
via GET /api/devis-reseau, avec bouton Copier. node --check OK, endpoint eprouve
(2 tenants, 121 lignes). Un operateur genere et transmet la config sans CLI.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-07 15:57:47 -04:00
97670dd27c make devis-reseau : config switch derivee du plan
scripts/devis_reseau.py decouvre les instances federees (ip-miroir) et emet
VLANs + SVIs + ACLs d'isolation inter-tenant, derives des nomenclatures.
Plus jamais saisi a la main. Eprouve sur Chezlepro + Technolibre.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-07 15:50:18 -04:00
4de04ec0e1 Nomenclature ip-miroir : socle VLAN 1000 -> longueurs uniformes
VLAN = 1000 + index*10 + zone (toujours 4 chiffres) -> VMID 9 chiffres.
Longueurs uniformes et mnemotechniques.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-07 15:46:00 -04:00
e8aba1ab74 GUI : erreurs de déploiement en langage clair
Quand une action échoue, un encadré résume les tâches en erreur (étape ·
hôte · type · message clair), avec un bouton Copier pour transmettre à un
humain — au lieu d'obliger à lire le dump Ansible.

Backend _extraire_echec : parse fatal:/UNREACHABLE!, privilégie stderr sur le
générique 'non-zero return code', gère no_log (secret masqué) et injoignables.
Marqueur __RESUME__ streamé, rendu côté JS. Éprouvé sur les 4 échecs réels du
from-zero du jour. node --check OK, smoke serveur OK.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-07 13:31:02 -04:00
3cc00ad660 GUI : éditeur Domaines (6e onglet éditable)
Le registre des domaines publics — seul sans éditeur — a désormais son onglet :
ajouter/retirer une zone, autorité (sélecteur), edge, DNSSEC, mail, secondaires,
et affichage des expositions (FQDN) sous la zone. Backend : route /api/domaines,
ecrire_domaines, validation valider_domaines (rejette une autorité inconnue).

Éprouvé : round-trip backend (valide/écrit/rejette interne), node --check, smoke serveur.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-07 13:08:15 -04:00
b4ca67289a GUI : persister les journaux d'exécution
Chaque action (creer-vm/verifier/deployer) écrit un journal horodaté
<instance>/logs/<hote>-<action>-<date>.log en plus du streaming live.
Si le navigateur se déconnecte, l'exécution continue et le log est capturé
jusqu'au bout (au lieu d'être interrompue). node --check OK, serveur HTTP 200.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-07 12:49:17 -04:00
b5e2edd3de GUI : vues Flux + Couches (lecture seule)
Deux onglets qui rendent visibles dans l'interface deux registres jusque-là
en CLI seulement :
- Flux : matrice d'audit réseau (rôle/sens/port/pair/chiffrement coloré/raison,
  depuis les meta/flux.yml), + légende chiffrement dans le panneau latéral.
- Couches : ordre de reconstruction (socle -> pki -> services -> apps -> agents),
  groupes triés topologiquement par couche.

Alimentées par resoudre_flux/orchestrer via l'API inventaire. Éprouvé :
node --check OK, smoke test serveur (63 flux, 6 couches servis, boutons présents).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-07 11:34:55 -04:00
0363c3255c GUI : intégrer l'intrant nftables_admin_ssh + retirer l'empreinte obsolète
Le panneau « Intrants de base » expose désormais nftables_admin_ssh (type liste,
section Sécurité) — la garde anti-lockout du pare-feu, éditable en fichier mais
absente du GUI. Retrait de vault_step_ca_fingerprint des secrets attendus
(empreinte du root CA dérivée dynamiquement maintenant). node --check OK.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-07 11:24:56 -04:00
72a5fdbbc4 Portabilité from-zero : 4 correctifs débusqués par la reconstruction
Reconstruction complète prouvée (14 VM + sauvegardes + AC supprimées, puis
`make myDay` rebâtit tout ; `make valider` entièrement vert). Bugs corrigés :

- client_pki : empreinte du root CA dérivée dynamiquement de l'autorité
  (au lieu d'une valeur figée en Vault) — une AC régénérée a une empreinte neuve.
- serveur_keycloak : assignation de rôle tolérante aux utilisateurs absents
  (annuaire vide sur un from-zero).
- serveur_prometheus : ne scrute que les hôtes ACTIFS (client_metrique ∩ hotes_actifs).
- nftables résolu : compatible Docker — remplace la seule table setops_flux (pas de
  flush ruleset, préserve les tables Docker) + forward autorise docker0/established.
  Sans ça, forward policy drop coupait Collabora (conteneur).

Ajoute playbooks/proxmox/supprimer_vm_debian.yml (suppression par VMID, garde-fous).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-07 06:45:12 -04:00
89d0e4e821 Recette : valider — restauration de sauvegarde restic
Ajoute au make valider la preuve de RÉSILIENCE : pour chaque nœud client_backup,
restic restaure le dernier snapshot dans un dossier temporaire (lecture seule sur
le dépôt) et vérifie que des fichiers en sortent.

Éprouvé sur la flotte : 6 dépôts restaurables. La recette a débusqué un vrai trou
(collab-01/Nextcloud sans backup jobs -> service en échec, stockage fichier non
protégé) — corrigé côté instance.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-07 03:40:37 -04:00
3e7c85506c Recette : valider — courriel bout-en-bout
Ajoute au make valider la preuve fonctionnelle du service courriel souverain :
envoi via le MTA (sendmail -> postfix -> LDAP -> LMTP) puis vérification de la
remise dans le Maildir avec doveadm (compte testmail), message de test nettoyé.

Éprouvé sur la flotte vivante, sous pare-feu actif : remise confirmée.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-07 03:25:24 -04:00
77dcc72aa6 Recette d'acceptation : make valider (phase 4, v1)
Vérifie que les services FONCTIONNENT, pas juste qu'ils sont déployés — complète
les *-verifier (statiques, sur le plan) par une recette fonctionnelle vivante.

playbooks/valider.yml (lecture seule) :
- Prometheus : toutes les cibles de supervision UP (API /targets)
- Edge : les vhosts HTTPS exposés répondent (server_name réels filtrés sur
  domaine_interne, statut sain 200/301/302/401/403)

Éprouvé sur la flotte : 7 cibles UP, 8 vhosts OK (sous pare-feu actif).
À venir : courriel bout-en-bout (envoi->IMAP), restauration de sauvegarde.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-07 03:19:51 -04:00
f05f505b88 Reconstruction propre : orchestrateur ordonné, registre des flux + pare-feu
Rend l'écosystème reconstructible en une commande (create+deploy idempotent) et
ajoute la couche « accès » (nftables least-privilege) au zéro-confiance.

Orchestrateur (phase 2) :
- docs/couches-deploiement.yml : registre des couches (socle → pki → services → apps → agents)
- scripts/orchestrer.py : tri par couche + topo intra-couche (graphe) → playbooks/site.yml ordonné
- Makefile : site / deployer-tout / flotte-creer / reconstruire / myDay (+ gardes CONFIRMER)
- docs/dependances-groupes.yml : graphe complété (keycloak→openldap, dovecot, postfix, icingaweb2, nextcloud)

Audit codé-en-dur (phase 1b) : labels/slug OIDC dérivés de l'intrant `organisation`
(serveur_forgejo/grafana/nextcloud) — le moteur ne porte plus de nom de tenant.

Registre des flux réseau (phase 0) :
- meta/flux.yml pour tous les rôles (29 rôles, 63 flux ; schéma + matrice validés)
- scripts/resoudre_flux.py : matrice d'audit (docs/registre-flux.md) + rulesets nftables résolus par hôte
- roles/nftables_baseline : déploie le ruleset résolu (moindre-privilège) quand activé, sinon repli

Correctifs : détection du coffre Vault (chemin production → inventaire réellement résolu).
Outillage : make wiki-publier (publication du wiki pédagogique dans Forgejo).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-07 03:08:09 -04:00
02bbba61b3 Nomenclature : VLAN dérivé (index × 10 + zone) pour réseau convergé
Sur un trunk convergé (tenants partagent l'infra), les VLAN doivent être uniques
GLOBALEMENT. deriver_nomenclature (ip-miroir) dérive VLAN = index×10 + zone : chaque
tenant son bloc de 10, 1-9 réservés à l'infra partagée. Le VLAN contenant le tenant,
le VMID mène avec le VLAN (VLAN·octet·seq, ≤ 9 chiffres Proxmox). compact (lab)
inchangé. GUI (miroir) + validation 1-4094 : compatibles.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-05 20:38:44 -04:00
5f3af67322 CHANGELOG 2026-07-05 : zéro-confiance (métriques/logs/courriel), VMID ip-miroir, Technolibre, FQDN, découplage tenant, modèles
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-05 01:14:49 -04:00
204910edbf Zéro-confiance PG : verrou hostssl + détection de version robuste
- pg_hba hostssl (serveur_postgresql_tls_force) : refuse les connexions
  non-TLS du réseau. Posé après validation des 3 clients en verify-full.
- Fix : tls_dir sous /etc/postgresql collisionnait avec la détection de
  version (find|sort|last prenait 'tls') → filtre numérique ^[0-9]+$ +
  tls_dir déplacé sous /var/lib/postgresql/tls.

Prouvé : non-TLS rejeté (« aucun chiffrement »), TLS accepté, 3 apps 200.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-04 20:46:53 -04:00
b6952759f5 Doc à jour : unité wiki Autorisation & RBAC + leçon renouvellement + runbooks
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>
2026-07-04 17:30:13 -04:00
94c9277c3d client_pki : recharger le vrai consommateur au renouvellement de cert
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>
2026-07-04 17:18:13 -04:00
c5df8fac4a RBAC via SSO : rôle de realm Keycloak → niveau Grafana (authZ)
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>
2026-07-04 16:58:15 -04:00
e9ca943425 Forgejo : identité visuelle Alliance (custom/ officiel, léger)
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
2026-07-04 15:47:39 -04:00
bcab586845 Agents observabilité/ops éprouvés — observabilité flotte-complète
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>
2026-07-04 11:56:51 -04:00
987ba88644 Thème Keycloak : console de compte (account) à l'identité Alliance
Thème account (parent=keycloak.v3) : overlay CSS surchargeant les variables
PatternFly 5 (fond aurore, cartes en verre, accent aurore, bouton dégradé)
+ la même constellation animée. Appliqué via kcadm -s accountTheme.

Prouvé : console charge (200, keycloak.v3 intact), account.css servi (200),
constellation.js référencé.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-03 23:54:19 -04:00
53acf1e209 Thème Keycloak : constellation animée en fond du login
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>
2026-07-03 23:47:44 -04:00
dd10ae8010 Thème de connexion Keycloak à l'identité Alliance Boréale
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>
2026-07-03 23:42:57 -04:00
1f1b722a00 Binding annuaire complété : icingaweb2 + client_ldap → resoudre_annuaire
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>
2026-07-03 23:24:38 -04:00
457b89927b Soumission courriel :587 interne (authentifiée) — boucle souveraine bouclée
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>
2026-07-03 23:17:36 -04:00
0681cdd72f Sauvegardes Tier 1 généralisées (5 nœuds) — restauration prouvée
client_backup étendu (jobs host_vars) : PostgreSQL (pg_dumpall), OpenLDAP
(slapcat), mail (/var/vmail), Forgejo (/var/lib/forgejo + config). Prouvé
par restauration : dump PG contient les 3 bases, LDIF contient testmail.
5 dépôts restic hors-nœud sur backup-01, chiffrés.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-03 23:00:51 -04:00
7476a54f22 Sauvegardes applicatives (restic) : serveur_backup + client_backup — Tier 0 prouvé
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>
2026-07-03 22:49:26 -04:00
0a0563c226 Consolider : binding annuaire (resoudre_annuaire) — keycloak/dovecot/postfix
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>
2026-07-03 22:23:22 -04:00
b60c5dc963 serveur_oauth2_proxy : passerelle SSO OIDC générique + Icinga Web 2 au SSO
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>
2026-07-03 19:05:04 -04:00
2ed919e914 Module BPM éprouvé + codifié — pile Icinga complète
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>
2026-07-03 17:04:34 -04:00
b3b972fb5e serveur_icingaweb2 : Icinga Web 2 (UI + module IcingaDB) — éprouvé
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>
2026-07-03 16:57:04 -04:00
36cf62236d Cœur Icinga éprouvé (supervision active) + DRY confirmé sur icinga
serveur_icinga (icinga2 + icingadb + redis dédié) sur sup-01, base
icingadb PostgreSQL via registre. Prouvé : 3 services actifs, moteur
supervise (IcingaDB peuplée : 1 hôte, 12 services, checks persistés).
Zéro bug. Icinga Web 2 + BPM restent différés (phases dédiées).
Confirme le DRY resoudre_base sur les 3 rôles consommateurs.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-03 16:39:23 -04:00
889e9af97a DRY : rôle utilitaire partagé resoudre_base (résolution BD depuis le registre)
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>
2026-07-03 15:57:06 -04:00
e214dcf4ed Forgejo au SSO OIDC (2e app SSO) — éprouvé sur forge-01
serveur_forgejo (10.0.0) : PostgreSQL via registre, exposé par l'edge
(auto-dérivé), source OAuth2 vers Keycloak (add-oauth idempotent), client
OIDC forgejo via serveur_keycloak_clients, auto-enregistrement OIDC
(identités depuis l'annuaire seulement).

Prouvé : testmail (LDAP) → « Se connecter avec Chezlepro » → compte
auto-créé, connecté au tableau de bord.

5 bugs de 1er déploiement corrigés : dépendance sendmail→postfix ;
app.ini propriété git (persiste secrets) ; ordre admin/migrations
(flush_handlers + wait_for) ; HTTP_ADDR 0.0.0.0 (edge distant) ;
auto-enregistrement OIDC.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-03 15:52:38 -04:00
fa945de5a7 client_unbound : résolveur local (DNS dynamique) — éprouvé sur un nœud
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>
2026-07-03 14:35:00 -04:00
a870c9befe hosts_statiques : alias d'exposition dans le plancher /etc/hosts (résolution sûre)
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>
2026-07-03 14:14:34 -04:00
00ba515aad serveur_powerdns : A d'exposition auto-dérivés (DNS de la Phase 3 bindings)
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>
2026-07-03 13:47:29 -04:00
494dbb32dc serveur_keycloak : enregistrement des clients OIDC codifié (kcadm idempotent)
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>
2026-07-03 13:37:33 -04:00
67699e7841 Grafana branché au SSO OIDC (« Se connecter avec Chezlepro ») — prouvé
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>
2026-07-03 13:15:52 -04:00
bcd6371243 Cert de l'edge nginx : snakeoil → step_ca (HTTPS valide)
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>
2026-07-03 12:01:19 -04:00