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 }}"