From 57e3bd3d017996cb337e8f5e47b441d0f9144c88 Mon Sep 17 00:00:00 2001 From: Daniel Allaire Date: Sun, 9 Aug 2026 09:04:25 -0400 Subject: [PATCH] =?UTF-8?q?client=5Fjournal=20:=20imposer=20et=20DECLARER?= =?UTF-8?q?=20le=20port=20d'Alloy=20=E2=80=94=20collision=20avec=20le=20SA?= =?UTF-8?q?SL=20de=20Dovecot?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Sixieme et dernier arret de la reconstruction from-zero, et le seul qui ne soit ni un ordre ni une course : un vrai conflit de port. infra-mail-01 : dovecot ecoute 0.0.0.0:12345 (serveur_dovecot_sasl_port, choix delibere de Set-OPS pour la soumission :587) alloy : defaut amont 12345 -> bind: address already in use Le premier demarre gagne. En exploitation courante le conflit DORMAIT : Alloy tenait le port depuis toujours et c'est l'ecoute SASL de Dovecot qui echouait, en silence. L'ordre des couches d'une reconstruction inverse les roles et le rend visible. Le vrai defaut n'est pas le numero : c'est qu'un port SUBI ne se declare nulle part, donc aucun controle ne peut voir la collision. Le port est desormais IMPOSE (--server.http.listen-addr) et DECLARE dans meta/flux.yml avec pair: localhost — une revendication de port, pas un flux entre hotes. Co-Authored-By: Claude Opus 5 --- docs/audit/preuve-2026-08-09.md | 67 ++++++++++++++++++++++++++ docs/registre-flux.md | 7 ++- roles/client_journal/defaults/main.yml | 15 ++++++ roles/client_journal/meta/flux.yml | 9 ++++ roles/client_journal/tasks/main.yml | 10 ++++ 5 files changed, 106 insertions(+), 2 deletions(-) create mode 100644 docs/audit/preuve-2026-08-09.md diff --git a/docs/audit/preuve-2026-08-09.md b/docs/audit/preuve-2026-08-09.md new file mode 100644 index 0000000..fea81cf --- /dev/null +++ b/docs/audit/preuve-2026-08-09.md @@ -0,0 +1,67 @@ +# Preuve de conformite — Set-OPS — 2026-08-09 + +> Genere par `make prouver` (`scripts/prouver.py`). **Rejouable** : relancer +> reproduit ce rapport. Chaque preuve rejoue l'outillage existant du depot ; +> aucune validation n'est reimplementee ici. Voir le mode d'emploi : +> [`docs/audit/README.md`](README.md), et le registre trace : +> [`docs/audit/affirmations.md`](affirmations.md). + +- **Instance** : `instance` — inventaire `instance/inventories/principal/hosts.yml` +- **Verdict** : ✅ CONFORME (31 OK · 0 echec · 0 saute) + +## Preuves + +| # | Preuve | Affirmations | Statut | Detail | +|---|---|---|---|---| +| P01 | Lint (ansible-lint) | AFF-006 | ✅ OK |  | +| P02 | Tests unitaires (inventory_host) | — | ✅ OK | >>> le verrou tient : aucune VM n'aurait ete touchee | +| P03 | Diff-vide du plan (inventaire genere) | AFF-001, AFF-004, AFF-030, AFF-031, AFF-032 | ✅ OK | DIFF VIDE : le plan reproduit exactement l'inventaire actuel. Bascule possible. | +| P04 | Groupes <-> playbooks homonymes | AFF-008 | ✅ OK | | +| P05 | Dependances causales de groupes | AFF-009, AFF-084 | ✅ OK | | +| P06 | Validateurs de registres (serveurs/apps/bases/domaines) | AFF-003 | ✅ OK | Registre des domaines valide. | +| P07 | GUI (node --check) | AFF-033 | ✅ OK | JS du GUI : syntaxe valide (node --check). | +| P08 | Orchestration (couches + graphe) | AFF-070 | ✅ OK | Orchestration coherente : 30 groupes classes, aucun cycle, aucune arete en arriere. | +| P09 | Flux reseau (schema + matrice) | AFF-071 | ✅ OK | Flux coherents : 29 rôles, 75 flux, schéma + matrice OK. | +| P10 | Handlers <-> notify | AFF-034, AFF-035 | ✅ OK | Tout notify pointe vers un handler du meme role (49 roles). | +| P11 | Syntaxe des playbooks (--syntax-check) | AFF-083 | ✅ OK | playbook: playbooks/proxmox/cloner_vm_debian.yml | +| P12 | Existence des runbooks cites | AFF-010, AFF-011, AFF-012, AFF-083 | ✅ OK | 17/17 runbooks/registres cites presents. | +| P13 | Invariants structurels/doctrinaux | AFF-015, AFF-022, AFF-037, AFF-038, AFF-062 | ✅ OK | LICENSE, socle dossier, pas de couches paralleles, SSH clef-only, nftables off : OK. | +| P14 | Pas de chemin lab/ code en dur | AFF-097 | ✅ OK | Aucun chemin instance/inventories/lab/group_vars code en dur. | +| P15 | Modele public socle valide | AFF-022, AFF-099 | ✅ OK | Modele public socle : domaines/serveurs/applications/bases valides. | +| P16 | Inventaire Ansible complet (--list) | AFF-030 | ✅ OK | 14 hotes, 31 groupes (inventaire dechiffre et parse). | +| P17 | Tous les modeles valident (registres + underlay) | AFF-022, AFF-099 | ✅ OK | Les 1 modele(s) decouvert(s) valident. | +| P18 | Gabarit de voute complet | AFF-026 | ✅ OK | Gabarit de voute complet : 25 secret(s) exige(s), tous presents. Voute reelle : 28 cle(s), aucun manque. | +| P19 | Le GUI couvre le schema du plan | AFF-002, AFF-095 | ✅ OK | GUI : les 28 champ(s) des plans reels sont editables (2 plan(s) inspecte(s)), registres toleres : nomenclature. | +| P20 | Adressage 100% derive du seed (aucun stocke) | AFF-001, AFF-003 | ✅ OK | 2 nomenclature(s) : adressage 100% derive du seed index. | +| P21 | Federation : aucun index en collision | AFF-102 | ✅ OK | Federation coherente : 2 instance(s) federee(s), aucun index en collision. | +| P22 | Plan de recette a jour (genere du wiki) | AFF-002 | ✅ OK | Plan de recette à jour (19 sections). | +| P23 | Underlay sans collision avec la plage tenant | AFF-103 | ✅ OK | Underlay conforme : 6 reseau(x), aucune collision avec la plage tenant. | +| P24 | Frontiere nord/sud : acces d'administration declare | AFF-104 | ✅ OK | CONFORME : frontiere nord/sud, 31 regles, 2 routes, admin=10.0.0.0/24,192.168.254.2/32,192.168.255.2/32. | +| P25 | Pare-feu Proxmox : est-ouest intra-tenant derive | AFF-107 | ✅ OK | CONFORME : pare-feu Proxmox, 2 tenant(s), 38 groupe(s), 64 regle(s). | +| P26 | Integrations universelles : aucun hote laisse de cote | AFF-108 | ✅ OK | 14 hote(s) x 4 integration(s) universelle(s) : aucune lacune, aucune recopie (1 exemption(s) derivee(s) du service rendu). | +| P27 | Propriete des intrants : hebergeur et tenant separes | AFF-109 | ✅ OK | 0 cle(s) de cluster chez l'hebergeur, aucune recopiee dans les group_vars du tenant. | +| P28 | Pools Proxmox : un par tenant, sans collision | AFF-110 | ✅ OK | CONFORME : 2 pool(s) Proxmox, 28 VM placee(s), aucun nom ni VMID en collision. | +| P29 | Authentification : chaque role declare sa position | AFF-111 | ✅ OK | 23 role(s) serveur declares (interne-sans-auth 2, ldap-direct 2, sans-auth-humaine 12, socle-identite 2, web-sso 5) ; 2 lacune(s) nommee(s) : serveur_loki, serv | +| P30 | SDN EVPN : zones, VNets et sous-reseaux derives | AFF-112 | ✅ OK | CONFORME : SDN EVPN, 2 zone(s), 12 VNet(s), 12 sous-reseau(x), aucune collision. | +| P31 | Documentation : tout ce que le depot FAIT est nomme | — | ✅ OK | 37 scripts expliques et atteignables, 87 cibles make documentees, 54 roles avec README. | + +## Couverture des affirmations ✅ du registre + +Chaque affirmation ✅ automatisable est couverte par la preuve indiquee ci-dessus. +Les ✅ **structurelles/doctrinales** non rejouables par une commande (ex. AFF-005 +`make`=aide, AFF-014 ciblage groupe, AFF-024 `instancier-appliquer`, AFF-051 autorite +d'AGENTS.md, AFF-073/075 gardes `make`, AFF-090 wiki) ont ete verifiees a l'audit ; +elles restent hors du harnais recurrent (rien d'executable a rejouer). + +## Declarations d'intention (⚪ invérifiables localement — assumees) + +Ces affirmations ne sont pas rejouables hors production ; elles sont **assumees** +comme declarations d'intention, non comme preuves : + +- **AFF-036** — « testables avec `--check` autant que possible » : verifiable seulement + contre une flotte vivante. +- **AFF-091** — contenu pedagogique du wiki : affirmations conceptuelles. +- **AFF-096** — « GUI 100 % francais » : revue exhaustive des libelles rendus, non automatisee. +- **AFF-007** — hote d'exemple `web-frontal-01` : placeholder assume. + +_Rapport genere le 2026-08-09._ diff --git a/docs/registre-flux.md b/docs/registre-flux.md index ca30fe9..ed49094 100644 --- a/docs/registre-flux.md +++ b/docs/registre-flux.md @@ -7,6 +7,7 @@ | Rôle (propriétaire) | Sens | Port | Proto | Pair | Chiffrement | Raison | | --- | --- | --- | --- | --- | --- | --- | | `client_backup` | egress | 22 | tcp | serveur_backup | ssh | Poussée des instantanés restic vers le dépôt hors-nœud, par SSH (clé dédiée). | +| `client_journal` | ingress | 12346 | tcp | localhost | clair | Interface de diagnostic locale d'Alloy — port declare pour rendre les collisions visibles. | | `client_journal` | egress | 3100 | tcp | serveur_loki | tls | Expédition des journaux par Alloy vers le collecteur central Loki (HTTPS, cert step-ca). | | `client_metrique` | ingress | 9100 | tcp | serveur_prometheus | tls | Scrape des métriques par Prometheus (node_exporter en HTTPS). | | `client_pki` | egress | 8443 | tcp | serveur_step_ca | tls-requis | Émission/renouvellement des certificats par ACME et récupération de la racine auprès de l'AC interne. | @@ -50,6 +51,8 @@ | `serveur_nextcloud` | egress | 9980 | tcp | serveur_collabora | tls-cible | Vérifications WOPI serveur->Collabora (édition en ligne). TLS interne = feuille de route edge->backends. | | `serveur_nginx` | ingress | 80 | tcp | externe | clair | HTTP entrant — redirection permanente vers HTTPS. | | `serveur_nginx` | ingress | 443 | tcp | externe | tls-requis | HTTPS entrant — services exposés (terminaison TLS à l'edge). | +| `serveur_nginx` | ingress | 443 | tcp | flotte | tls-requis | HTTPS depuis le tenant : les FQDN publiés vivent à l'edge (découverte OIDC, appels inter-services par nom). | +| `serveur_nginx` | ingress | 443 | tcp | admin | tls-requis | HTTPS depuis le reseau d'administration : l'exploitant administre les services par leur interface web, servie par l'edge. | | `serveur_nginx` | egress | derive | tcp | expositions | tls-cible | Proxy vers les backends exposés (host:port dérivés des expose ; TLS interne = roadmap edge→backends). | | `serveur_oauth2_proxy` | ingress | 4180 | tcp | edge | clair | Point d'entrée SSO servi via l'edge (TLS terminé à l'edge) devant l'application protégée. | | `serveur_oauth2_proxy` | egress | 443 | tcp | edge | tls-requis | Émetteur OIDC (Keycloak) via son FQDN publié à l'edge : échange de jetons. | @@ -81,10 +84,10 @@ ## Synthèse chiffrement -- **clair** : 28 flux +- **clair** : 29 flux - **n-a** : 3 flux - **ssh** : 3 flux - **starttls** : 6 flux - **tls** : 8 flux - **tls-cible** : 2 flux -- **tls-requis** : 22 flux +- **tls-requis** : 24 flux diff --git a/roles/client_journal/defaults/main.yml b/roles/client_journal/defaults/main.yml index 8f78814..7bf487d 100644 --- a/roles/client_journal/defaults/main.yml +++ b/roles/client_journal/defaults/main.yml @@ -18,3 +18,18 @@ client_journal_depot_source: "deb [signed-by=/etc/apt/keyrings/grafana.asc] http # --- TLS (zero-confiance) : Alloy pousse vers Loki en https + verifie root_ca --- client_journal_loki_tls: false client_journal_loki_ca: "/etc/step/certs/root_ca.crt" + +# Port de l'interface HTTP locale d'Alloy (diagnostic). DECLARE ici parce que son defaut +# amont — 12345 — entre en collision avec `serveur_dovecot_sasl_port`, qui vaut 12345 lui +# aussi et qui est un choix DELIBERE de Set-OPS (SASL reseau, pour la soumission :587). +# +# Sur `infra-mail-01`, les deux cohabitent : le premier demarre gagne, l'autre boucle sur +# « bind: address already in use ». Constate le 2026-08-08 lors de la premiere +# reconstruction from-zero, ou l'ordre des couches fait demarrer Dovecot bien avant Alloy. +# En exploitation courante le conflit dormait : Alloy tenait le port depuis toujours, et +# c'est l'ecoute SASL de Dovecot qui echouait — en silence. +# +# Le vrai defaut n'est pas le numero, c'est qu'un port SUBI ne se declare nulle part et +# qu'aucun controle ne peut donc voir la collision. +client_journal_http_port: 12346 +client_journal_http_adresse: "127.0.0.1" diff --git a/roles/client_journal/meta/flux.yml b/roles/client_journal/meta/flux.yml index 55655c6..4836ed6 100644 --- a/roles/client_journal/meta/flux.yml +++ b/roles/client_journal/meta/flux.yml @@ -7,3 +7,12 @@ flux: pair: serveur_loki chiffrement: tls raison: "Expédition des journaux par Alloy vers le collecteur central Loki (HTTPS, cert step-ca)." + # Interface de diagnostic d'Alloy, liee a la boucle locale. Ce n'est pas un flux entre + # hotes — c'est une REVENDICATION DE PORT, declaree pour qu'elle cesse d'etre invisible : + # son defaut amont (12345) entrait en collision avec le SASL de Dovecot sur le meme hote. + - sens: ingress + port: 12346 + protocole: tcp + pair: localhost + chiffrement: clair + raison: "Interface de diagnostic locale d'Alloy — port declare pour rendre les collisions visibles." diff --git a/roles/client_journal/tasks/main.yml b/roles/client_journal/tasks/main.yml index 241a618..58145bf 100644 --- a/roles/client_journal/tasks/main.yml +++ b/roles/client_journal/tasks/main.yml @@ -43,6 +43,16 @@ mode: "0640" notify: Redemarrer alloy +# Le port d'ecoute est IMPOSE ici plutot que subi : voir defaults/main.yml. +- name: Imposer le port de l interface locale d Alloy + ansible.builtin.lineinfile: + path: /etc/default/alloy + regexp: "^CUSTOM_ARGS=" + line: >- + CUSTOM_ARGS="--server.http.listen-addr={{ client_journal_http_adresse }}:{{ client_journal_http_port }}" + create: false + notify: Redemarrer alloy + - name: Activer et demarrer Alloy ansible.builtin.systemd: name: "{{ client_journal_service }}"