Set-OPS-Public/docs/decisions-architecture.md
Daniel Allaire 63aa79e710 underlay : l'invariant du dernier octet retrouve sa portée (D-04, D-52 à D-54)
Il valait partout ; il ne vaut que dans l'adressage dérivé des tenants, où
passerelle_de(index, zone) produit le même .1 dans les treize sous-réseaux d'un
tenant. C'est une propriété de la dérivation, pas une loi universelle.

Dans l'underlay il produisait deux effets pervers. Un seuil arbitraire : les
sous-réseaux plus étroits qu'un /24 étaient exemptés, donc élargir un /29
changeait la validité du fichier sans que rien d'autre bouge. Et une couture
entre propriétaires : l'octet attendu venait de la nomenclature d'un tenant,
appliquée à la fabric de l'hébergeur — la validité de l'underlay aurait dépendu
du tenant actif.

Ce qui reste est plus fort et suffit (D-52) : une passerelle doit être l'adresse
d'un hôte déclaré sur ce réseau. Elle attrape les passerelles fantômes, ce que le
comptage d'octets ne faisait pas. Vérifié : la garde mord toujours.

À noter, parce que l'ordre était mauvais : le ré-adressage de l'OPNsense en .1 a
été demandé au nom de cette règle, deux messages avant qu'elle soit recadrée. Pas
perdu — .1 est la position conventionnelle d'une passerelle — mais la portée
aurait dû être questionnée avant de faire changer une adresse en service.

D-53 : le réseau et l'underlay de l'hébergeur méritent leur propre dépôt.
underlay.yml décrit une infrastructure, le dépôt de tenant une organisation ; un
tenant peut déménager, une fabric non. Consigné, non fait.

D-54 : 10.0.0.0/24 est réservé à l'IPAM, la gestion des équipements et l'OOB,
accès sysadmin. Aucun hyperviseur, aucune VM, aucun trafic tenant. C'est la
raison d'être des VLAN 11 et 40.

30 preuves OK.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-04 14:46:06 -04:00

16 KiB

Registre des décisions d'architecture

À quoi sert ce document. Les décisions sont écrites là où elles s'appliquent — frontiere-opnsense.md, sdn-evpn.md, migration-tenant.md, underlay.yml.example — et leur histoire vit dans le CHANGELOG. Ce registre ne les répète pas : il dit quelles décisions existent, pourquoi, et où lire le détail. Sans lui, « pourquoi le /29 et pas le /30 ? » demande de relire vingt entrées de journal.

Ce n'est pas la doctrine de conduite (AGENTS.md §Principes), ni le registre des affirmations prouvables (docs/audit/affirmations.md). Une décision peut n'être gardée par aucune preuve : elle reste une décision.

Qui décide. Toutes ces décisions sont celles de l'opérateur du dépôt. Plusieurs ont été prises sur recommandation — l'assistance propose et argumente, elle ne tranche pas. La distinction compte pour la suite : une décision se renverse par celui qui l'a prise, et savoir qu'elle a été choisie plutôt que héritée change ce qu'on s'autorise à en faire.

Les dates de la §5 sont celles de l'historique git, pas du moment de la conversation : ce sont les seules vérifiables.


1. Le réseau

# Décision Pourquoi Détail Garde
D-01 OPNsense est une frontière nord/sud, pas la passerelle des zones le routage inter-zone reste au débit ligne ; la bordure ne voit pas l'est-ouest frontiere-opnsense.md §1
D-02 Le lien de transit vit dans l'underlay, pas dans un tenant la frontière route vers tous les tenants par le même saut : il ne peut dériver d'aucun index frontiere-opnsense.md §6 P23
D-04 Un point de routage porte le même dernier octet sur tous ses sous-réseaux — dans l'adressage dérivé des tenants seulement on retient une adresse, pas treize. Dans l'underlay, l'adressage est dicté par les participants d'un lien physique : la règle y imposait un seuil arbitraire et empruntait sa valeur à la nomenclature d'un tenant frontiere-opnsense.md §4 P20
D-52 Dans l'underlay, une passerelle doit être l'adresse d'un hôte déclaré sur ce réseau remplace le comptage d'octets par une règle plus forte : elle attrape les passerelles fantômes underlay.py P23
D-49 Aucun commutateur ne route. La frontière est le seul équipement L3 l'EVPN a retiré les VLAN tenants du fil, puis la fusion du lien de sortie a rendu les nœuds de sortie adjacents à la frontière : plus un seul flux à router underlay.yml P23
D-50 passerelle = la passerelle du sous-réseau, où qu'elle vive ; le SVI se dérive du rôle de son porteur la clé signifiait « adresse du SVI du switch » — une hypothèse, pas une donnée. Un même moteur sert désormais les deux postures devis_reseau.py P23
D-51 Deux commutateurs en paire, pas trois en étoile le centre d'une étoile est sur tous les chemins : point de panne unique du plan de données. Deux liens vers deux commutateurs donnent la redondance qu'une étoile ne peut pas donner underlay.yml
D-06 Les réseaux déclarent leur fabric ; un devis ne parle que de la sienne le stockage jumbo vit sur ses propres commutateurs — un devis est une configuration, pas un inventaire sdn-evpn.md §4 P23
D-07 Pas d'ACL sur cette fabric (acl_inter_tenant: false) le matériel ne sait pas lier une ACL à un SVI ; des règles jamais liées auraient l'air d'isoler frontiere-opnsense.md §1
D-43 Les objets SDN d'un tenant (zone, VNets, sous-réseaux) sont dérivés du seed et émis par make devis-sdn ajouter un tenant implique 13 objets de cluster ; les créer à la main dans un dépôt où tout dérive était la dernière lacune sdn-evpn.md §2 P30
D-44 Le nommage SDN dérive du tenant : zone <PRÉFIXE><index>, VNet <préfixe><index><zone> se lit sans table de correspondance ; les zones faites à la main (VRF0017) sont remplacées tant qu'elles sont vides. Seule contrainte conservée : 8 caractères, imposée par Proxmox sdn-evpn.md §2 P30
D-08 Le routage tenant passe au SDN EVPN — une zone par tenant une zone EVPN est un VRF : l'isolation devient structurelle, pas réglementaire sdn-evpn.md P23 (MTU)
D-09 Le filtrage inter-zone d'un tenant se fait au même niveau (EVPN) là où le routage a lieu sdn-evpn.md §3
D-31 Le filtrage est-ouest est appliqué deux fois : hyperviseur puis nftables d'hôte défense en profondeur ; les deux dérivent du même registre par les mêmes fonctions, donc ne peuvent pas diverger sdn-evpn.md §3 P25
D-32 Tout ce qui entre ou sort d'un tenant passe par la frontière un VRF n'a qu'une sortie ; conséquence : la frontière devient un prérequis de déploiement sdn-evpn.md §3
D-10 L'inter-tenant passe obligatoirement par la frontière il sort du VRF, donc traverse une bordure en block par défaut : il ne peut plus être oublié sdn-evpn.md §3
D-11 La sortie générale est déclarée dans le registre des flux block out est un vrai default-deny ; un besoin oublié ne se manifeste pas par un refus clair frontiere-opnsense.md §8 P09
D-29 L'overlay EVPN plafonne à 1450 ; le transport doit donc dépasser 1500 choix d'exploitation ; l'encapsulation VXLAN coûte 50 octets sdn-evpn.md §5 P23 (MTU du transport)
D-30 L'ICMP « fragmentation nécessaire » est déclaré, dans les deux sens à 1450, tout ce qui traverse la frontière dépend de la découverte de MTU de chemin ; une bordure en default-deny la casse en silence roles/serveur_debian/meta/flux.yml P09
D-12 Nommage : bifrost aux frontières, sleipnir à la fabric Bifröst est le pont vers l'extérieur ; Sleipnir traverse les mondes sans en sortir frontiere-opnsense.md §6

2. Qui possède quoi

# Décision Pourquoi Détail Garde
D-13 Un hébergeur sert plusieurs tenants et a son tenant par défaut Chezlepro est les deux à la fois, ce qui masquait la distinction frontiere-opnsense.md §2
D-14 underlay.yml appartient à l'hébergeur, monté par symlink ce sont ses commutateurs, ses câbles ; le moteur est générique, un tenant n'en possède pas sdn-evpn.md, underlay.yml.example
D-15 Ce symlink ne suit pas make instance-utiliser basculer le tenant actif ne change pas la fabric frontiere-opnsense.md §2
D-16 Les intrants de la frontière se lisent chez l'hébergeur un hébergeur n'a qu'une frontière pour tous ses tenants frontiere-opnsense.md §2
D-17 L'hébergeur n'est pas déclaré : le symlink le désigne une seconde déclaration ouvrirait deux valeurs contradictoires frontiere-opnsense.md §2
D-35 Le cluster Proxmox appartient à l'hébergeur ; seuls le golden template et les défauts de placement restent au tenant recopié chez chaque tenant, l'inventaire du cluster avait déjà divergé — deux listes de stockages contradictoires pour le même matériel config-proxmox.md P27
D-36 Le panneau nomme le propriétaire de chaque section d'intrants éditer une section « hébergeur » vaut pour tous ses tenants ; l'écran ne le disait pas scripts/inventory_gui.py (INTRANTS_SCHEMA)
D-37 Chaque tenant a son pool Proxmox ; les noms courts de VM restent identiques d'un tenant à l'autre 11 serveurs sur 14 sont homonymes — c'est la preuve que la nomenclature est un gabarit ; le coût est humain (la console affiche le nom), et le pool le corrige sans rien renommer devis_proxmox_pools.py P28
D-45 L'affinité de VM (garder un groupe sur le même hyperviseur) attend Proxmox 9 ; tenue à la main d'ici là les resource affinity rules n'existent qu'en 9 ; en 8.4 seuls les groupes HA épinglent à des nœuds, pas des VM entre elles. Sans ressource HA déclarée, rien ne déplace ni ne sépare les VM — le sujet ne devient réel qu'en activant la HA sdn-evpn.md
D-46 Un hébergeur porte trois catégories, pas deux : son tenant, ses opérations, le plan de contrôle Chezlepro est hébergeur ET tenant, ce qui masquait des besoins n'appartenant à aucun tenant hebergeur-exploitation.md §2
D-47 Les services d'exploitation de l'hébergeur vivent dans son dépôt, et leurs VM se rattachent à un pont VLAN, jamais un VNet un service qui observe la fabric ne peut pas dépendre d'elle : l'EVPN tombe, et la supervision tombe avec la raison de la panne hebergeur-exploitation.md §1, §4
D-48 Les hyperviseurs sont gérables par Ansible ; « hors flotte » ne vaut que pour les commutateurs et la frontière ce sont des Debian joignables en SSH ; c'est la seule façon d'y poser un exportateur de métriques hebergeur-exploitation.md §5
D-53 Le réseau et l'underlay de l'hébergeur méritent leur propre dépôt, séparé de son tenant underlay.yml et le cluster décrivent une infrastructure ; le dépôt de tenant décrit une organisation. Les mêler oblige à trancher qui possède quoi à chaque commit hebergeur-exploitation.md §7
D-54 10.0.0.0/24 est réservé à l'IPAM, la gestion des équipements et l'OOB — accès sysadmin aucun hyperviseur, aucune VM, aucun trafic tenant ; c'est la raison d'être des VLAN 11 et 40 underlay.yml
D-18 Chaque tenant a un responsable désigné sans lui, « qui peut décider de déménager cette organisation ? » se pose au pire moment migration-tenant.md §3

| D-38 | Toute authentification web passe par Keycloak ; LDAP est la source unique des comptes | une identité, un mot de passe ; aucun service ne tient son propre répertoire d'humains | authentification.md §1-2 | — | | D-39 | Les protocoles qui ne parlent pas OIDC (IMAP, SMTP) se lient directement à LDAP | le chemin varie, la source ne varie pas | authentification.md §2 | — | | D-40 | L'accès de secours passe par sudo sur l'hôte, pas par un compte web permanent | service → Keycloak → LDAP est en série : sans secours, une panne exclut tout le monde, y compris pour réparer | authentification.md §4 | — | | D-41 | Le formulaire de connexion locale n'est pas proposé au repos (<rôle>_connexion_locale: false) | il contourne la politique de mot de passe, le MFA et surtout la révocation centrale ; sudo est le mécanisme de réouverture | authentification.md §3 | — |

| D-42 | Chaque rôle déclare sa position d'authentification (meta/authentification.yml), gardée par une preuve | une directive qu'aucune garde ne vérifie finit par ne plus être vraie — c'était le cas des 28 lignes d'intégration recopiées | authentification.md §5 | P29 |

3. Les secrets

# Décision Pourquoi Détail Garde
D-19 Une seule voûte par instance (group_vars/all/vault.yml) un mot de passe, un endroit config-proxmox.md P18
D-20 La liste des secrets se recense, elle ne s'écrit pas trois copies manuelles ont existé, toutes ont divergé intrants-communs.md §H P18
D-21 L'empreinte du root CA n'est pas un secret : elle se dérive à chaud un from-zero régénère l'AC ; figée en voûte, elle serait périmée roles/client_pki/README.md
D-22 Migrer un tenant, c'est révoquer, pas transmettre sinon l'ancien hébergeur garde à vie l'accès aux secrets d'un client parti migration-tenant.md §6

4. La méthode

# Décision Pourquoi Détail Garde
D-23 Les cibles hors flotte reçoivent un devis, pas un rôle commutateurs, frontière et SDN sont des objets de cluster ; un rôle boucle sur des hôtes sdn-evpn.md §7
D-24 Un devis n'écrit rien : il se relit, puis s'applique proportionné au risque — une config ratée partitionne tout un cluster frontiere-opnsense.md §7
D-25 Le dépôt n'affirme pas que ses devis s'appliquent, il affirme qu'ils dérivent leur syntaxe dépend d'un matériel que le dépôt ne possède pas audit/affirmations.md §10
D-26 Un modèle peut porter un underlay ; tous les hébergeurs n'ont pas le même matériel générique public, étoffés en privé exemples/modeles/socle/README.md P17
D-27 Migration : le receveur est prouvé prêt avant tout gel l'interruption se réduit au delta et à la propagation DNS migration-tenant.md §4
D-28 Le mandat de migration est signé par le tenant, pas convenu entre hébergeurs il n'y a pas de registre central pour arbitrer ; une organisation n'est pas la propriété de son hébergeur migration-tenant.md §2
D-33 Une intégration universelle est déclarée par le rôle, jamais recopiée par serveur 28 des 57 lignes du plan disaient oui à ce qui vaut pour tous : elles n'existaient que pour être oubliées — et quatre l'avaient été integrations-vm.md §Politique P26
D-34 Une exemption se dérive du service rendu (sauf_role), jamais d'un nom d'hôte l'AC ne s'enrôle pas auprès d'elle-même ; l'exemption doit suivre step-ca si on le déplace roles/client_pki/meta/integration.yml P26

5. Décisions renversées

Les garder évite de refaire le chemin, et explique pourquoi le code porte encore des branches qui semblent inutiles.

Décision Tenue du … au … Renversée par Pourquoi Ce qu'il en reste
L'isolation inter-tenant est portée par des ACL de commutateur 2026-07-07 → 08-03 059d76a le matériel ne sait pas lier une ACL à une interface de routage acl_inter_tenant: true reste le défaut : une autre fabric peut en être capable
Le routage inter-zone est porté par les commutateurs L3 2026-07-07 → 08-03 e5ce2b9 sans ACL, l'isolation devenait déclarative ; EVPN la rend structurelle routage_tenants: switch reste le défaut et reste généré
L'underlay.yml vit à la racine du moteur, gitignoré 2026-07-24 → 08-02 0eae97c consommé par deux générateurs, validé par une preuve, versionné nulle part SETOPS_UNDERLAY permet toujours de le pointer ailleurs
D-05 — un seul commutateur route, les autres en L2 pur 2026-07-07 → 08-04 2026-08-04 deux décisions séparées (EVPN, puis fusion du lien de sortie) ont vidé le rôle sans qu'on regarde leur effet cumulé : les trois SVI n'avaient plus aucun consommateur le moteur sait toujours émettre des SVI — le modèle public s'en sert
D-03 — le /29 de transit, SVI en .6, .3 réservée CARP 2026-07-24 → 08-04 2026-08-04 élargi en /24 pour y loger les nœuds de sortie ; l'exemption d'invariant sous le /24 tombe alors, et le .1 revient à la passerelle le plan .1 passerelle / .2 .3 pare-feux survit, décalé d'un cran

Ce que ces renversements ont en commun. Aucun ne vient d'un changement d'avis : les trois viennent d'un fait découvert après la décision — une commande absente de l'aide du matériel, une capacité manquante, une dizaine de modifications irrécupérables. C'est l'argument le plus fort pour éprouver avant de figer.