La revision a commence par un balayage par motifs — chemins morts, cibles make absentes, comptes derives. Il a trouve une trentaine d ecarts et rate presque tout le reste : un motif ne voit que ce qui s exprime en motif. make hote-planifier en est l exemple. La cible EXISTE, donc le controle passait au vert. C est une cible depreciee qui refuse et sort en 2, recommandee par AGENTS.md, et qui contredit la REGLE D OR du meme fichier trois ecrans plus haut. Il fallait lire pour la voir. 74 documents lus un par un. 66 corriges, 8 exacts. CE QUI ETAIT FRANCHEMENT FAUX AGENTS.md, la source d autorite, annoncait la flotte pas encore executee contre des VM reelles. Elle a ete rasee et remontee depuis zero trois fois. ecosysteme-chezlepro.md, le document montre a un client, portait la meme phrase : il se sous-vendait gravement. courriel-conception.md s ouvrait sur aucun role n est encore ecrit, au-dessus de son propre paragraphe 1 qui les nomme. autorisation.md se terminait sur rien n est construit alors qu il rapporte des mesures datees du role en fonctionnement. hebergeur-exploitation.md disait rien n est fait d un depot qui existe. filiation-emancipation.md se contredisait a deux ecrans de distance. DES MODELES DECRITS D APRES UN MONDE ANTERIEUR Le resolveur : cinq documents decrivaient un Unbound par VM en opt-in, trois le donnaient en exemple d integration FACULTATIVE — il est universel depuis le 2026-08-24. L adressage de nomenclature-vm.md : reseau unique, VLAN 11-15, VMID a cinq chiffres. Le nommage SDN de sdn-evpn.md contre le code : c est le wiki qui avait raison. CE QUI CASSE AU PREMIER ESSAI Le nom du gabarit dore etait faux a quatre endroits, dont la procedure qui le FABRIQUE et le critere R2 de l epreuve d operateur independant. preparer-un-site-hebergeur.md avertissait qu une VM faite a la main serait detruite : raser derive du plan, il ne la detruira jamais — le risque est l inverse. Un mot de passe d essai en clair dans un depot public. DEUX PREUVES ETENDUES, ET UNE QUI SE TROMPAIT ELLE-MEME P57 couvre les groupes : elle a signale aussitot 29 groupes annonces au-dessus d un tableau qui en cite 40. P29 confronte le tableau de authentification.md aux declarations reelles : 12 annonces, 21 reels. Et P57 imposait un chiffre faux — 56 preuves alors que le depot en porte 57, la conditionnelle vivant hors de tout comptage. Un garde-fou qui fait respecter une erreur ajoute l assurance a l erreur. CE QUI RESTE, ET QU AUCUNE PREUVE NE TIENT Deux comptes trouves a la main. Et une lacune reelle : rien ne garde les meta/acces.yml — ni qu un service web-sso en porte un, ni que le groupe qu il nomme existe. P29 tient les positions d authentification, personne ne tient les habilitations. make prouver : CONFORME, 56 OK, 0 echec, 1 saute. 0 lien mort. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Crgis8CxCWkAGFA1ecBz3q
7.8 KiB
Registre des flux réseau — conception
Pour qui : le mainteneur qui déclare un flux réseau dans
roles/*/meta/flux.yml.
But
Set-OPS tient un registre des flux (qui parle à qui, sur quel port, dans quel sens, pourquoi) pour deux usages :
- Générer les règles nftables de chaque serveur — autoriser les flux déclarés, tout refuser d'autre (moindre privilège). Complète la couche chiffrement (TLS) par la couche accès.
- Preuve d'audit — la matrice d'accès réseau documentée (source → dest : port : chiffrement : raison), exigée par un audit de sécurité et le label de certification.
Principe : le rôle possède ses flux
Comme meta/empreinte.yml (dimensionnement) est la propriété du rôle, roles/<rôle>/meta/flux.yml
déclare les flux du logiciel. Le rôle sait sur quoi il écoute et à quoi il se connecte ; personne
d'autre. Le registre global se dérive de l'ensemble des flux.yml des rôles présents sur un nœud.
Schéma de meta/flux.yml
flux:
- sens: ingress # ingress = ce rôle ÉCOUTE ; egress = ce rôle SE CONNECTE
port: 5432 # entier ou liste [80, 443]
protocole: tcp # tcp | udp
pair: [serveur_keycloak, serveur_forgejo] # QUI — voir « Résolution du pair »
chiffrement: tls-requis # tls-requis | tls | starttls | ssh | tls-cible | clair | n-a (bonus audit)
# tls-requis : chiffré + pair vérifié (verify-full) | tls : chiffré | starttls : mise à niveau opportuniste
# ssh : transport SSH (chiffré, hôte vérifié) | tls-cible : TLS visé mais pas encore appliqué (feuille de route)
# clair : non chiffré (local ou terminé à l'edge) | n-a : sans objet
raison: "Connexions applicatives (verify-full)." # lisible, pour l'audit
Résolution du pair
| Valeur | Résout vers |
|---|---|
un rôle/groupe (serveur_prometheus) |
les IP des hôtes de ce groupe (via le plan) |
edge |
le(s) hôte(s) de l'edge (serveur_nginx) |
flotte |
tous les nœuds de l'instance |
externe |
hors flotte (frontière publique — géré à l'OPNsense, pas dans le nœud) |
localhost |
boucle locale — aucune règle inter-nœud (nftables autorise lo) |
expositions |
dérivé des expose: des applications (cas de l'edge → backends) |
admin |
les réseaux d'administration (intrant nftables_admin_ssh) — l'exploitant n'est ni la flotte, ni l'Internet |
voisins_site |
les autres tenants fédérés de la même fabric (leurs supernets) — le voisinage, quatrième chemin d'arrivée |
fabric |
le matériel de l'hébergeur : hyperviseurs, frontière, commutateurs |
runner_site |
le runner du site, seul autorisé à matérialiser des VM |
derive |
résolu ailleurs que par le nœud (hors périmètre de sa règle) |
La résolution pair → IP réutilise le plan (registre IP/FQDN/zones) déjà en place.
Quatre de ces mots sont nés d'un piège, et pas d'un besoin de vocabulaire.
voisins_site(2026-08-24) : sans lui, le chaînage des caches d'artefacts n'aurait pu se déclarer qu'eningress+externe— ce qui aurait publié le cache à l'Internet entier.fabric(2026-08-25) : le runner de site déclarait son API Proxmox enexterne, or « externe » se rend par « tout sauf les espaces privés » et les hyperviseurs sont en RFC 1918 — la règle avait l'air d'ouvrir le flux et l'excluait. Un flux qui a l'air ouvert et qui ne l'est pas est pire qu'un flux fermé : il ne se cherche pas.Les cinq derniers ne rendent pas des hôtes :
admin,voisins_site,fabricetrunner_siterendent des CIDR ou des sources extérieures à l'écosystème,derivene rend rien. Ils sont donc traités à part dansscripts/resoudre_flux.py.
Génération
Un résolveur (miroir de instancier) agrège, par serveur, les flux.yml de tous ses rôles
(services + intégrations), résout les pair, et produit :
nftables: ruleset par serveur (allow des flux résolus,policy droppar défaut), consommé par le rôlenftables_baseline;- le registre d'audit :
docs/registre-flux.md(généré) + une ciblemake flux.
Activation prudente
Activer nftables = action destructive (peut couper l'accès) → confirmation explicite + déploiement graduel (garder l'accès SSH/Ansible, tester par nœud).
Le pare-feu est armé sur la flotte depuis. Ce paragraphe disait « nftables reste préparé mais non activé tant que le registre n'est pas complet et validé ». Le registre est complet — P49 vérifie qu'il reproduit exactement ce que les
meta/flux.ymldéclarent — etnftables_baseline_enabledvauttruepourhotes_actifsdans le plan. Le rôle, lui, gardefalsepar défaut : le pare-feu n'est pas une propriété du rôle, c'est une décision de l'instance. Le gabarit doré reste àfalse, et c'est voulu.
Séquence — franchie
- ✅ figer le schéma (ce doc) + piloter sur postgresql / client_metrique / nginx ;
- ✅ le résolveur (
scripts/resoudre_flux.py) — agrégation → règles + registre ; - ✅ remplir tous les rôles (transcription du travail zéro-confiance) ;
- ✅ générer + registre d'audit (
docs/registre-flux.md, gardé par P49) ; puis activer nftables nœud par nœud — fait.
Ce qui a suivi la séquence n'y était pas prévu : le même registre alimente désormais aussi
le pare-feu est-ouest de l'hyperviseur (make proxmox-fw-plan, garde P45) et la
frontière nord/sud OPNsense (make frontiere-plan). Trois couches, une seule décision —
elles ne peuvent pas se contredire parce qu'elles dérivent de la même source.
poste: false — un service publié qui ne s'adresse pas à un humain
Ajouté le 2026-08-09. Ne concerne que les flux ingress dont le pair inclut externe,
c'est-à-dire les services publiés.
La frontière étend ces services au VLAN d'administration : le poste de l'exploitant y
est, et c'est de là qu'il ouvre ses consoles web ou son client de courriel. Mais tous les
services publiés ne s'adressent pas à un humain — le 25 entrant de Postfix est un flux
serveur à serveur, les MX distants.
| Situation | Exemple | poste |
|---|---|---|
| un poste de travail s'y connecte | serveur_nginx 443, serveur_dovecot 993 |
absent (défaut true) |
| flux serveur à serveur uniquement | serveur_postfix 25 entrant |
false |
Sans ce mot-clé, la frontière autorisait admin → 25 que le nftables de l'hôte refusait :
deux couches déclarant deux politiques différentes. Mesuré par make frontiere-mesurer, qui
distingue précisément ce cas d'une vraie fuite.
Le mot-clé vit dans meta/flux.yml, avec le rôle qui sait ce que son port veut dire. Le
générateur de la frontière, lui, ne connaît aucun numéro de port.
partage: — décrire une écoute plutôt que l'ouvrir
Ajouté le 2026-08-09 avec P33, qui refuse que deux rôles co-localisés revendiquent le même port.
Le registre confondait deux situations :
| Situation | Exemple | partage |
|---|---|---|
| le rôle ouvre l'écoute | serveur_dovecot lie 12345 (SASL réseau) |
absent |
| le rôle décrit celle d'un autre | serveur_backup emprunte le sshd de serveur_debian |
true |
Sans cette distinction, la seule co-location légitime de la flotte — tcp/22 sur le
dépôt de sauvegarde, site-backup-01 depuis que les écosystèmes déposent chez leur
hébergeur — serait signalée à tort. Une preuve qui crie sur un cas sain finit par être
ignorée, ce qui est pire que de ne pas l'avoir.
Corollaire à retenir : un port qu'on subit (le défaut amont d'un logiciel) doit être imposé et déclaré comme les autres. Celui d'Alloy ne l'était pas, et c'est la seule raison pour laquelle la collision a pu durer des semaines.