Set-OPS-Public/docs/decisions-architecture.md
Daniel Allaire 3b1d9b6660 SDN : make devis-sdn — zone, VNets et sous-réseaux dérivés du seed (D-43/44, P30)
Ajouter un tenant implique 1 zone EVPN + 6 VNets + 6 sous-réseaux sur le cluster.
Aucun générateur ne les produisait : dernière lacune dans un dépôt où tout dérive.
26 objets pour les deux tenants, VNI = 1000 + index×10 + zone, sous-réseau et
passerelle par les mêmes fonctions que l'inventaire.

Nommage, en deux temps. D'abord VRF0017 / v1174, alignés sur ce que le cluster
portait — réflexe inverse du bon : cette convention venait d'une création à la
main et ne disait pas de quel tenant il s'agissait. Forme retenue : t<index> pour
la zone, t<index><zone abrégée> pour le VNet (t17, t17serv). C'est le préfixe que
le pare-feu Proxmox utilisait déjà (t17-cli-metrique), donc un seul schéma dans
tout le dépôt. Abréviation = 4 premières lettres du libellé, accents retirés.

Pas de tiret entre index et zone, contrairement aux IPSets : t245-serv ferait 9
caractères, t245serv en fait 8 — la forme reste uniforme jusqu'au dernier index.

Contrainte cadrante : zones ET VNets sont limités à 8 caractères par Proxmox.
sdn-evpn.md annonçait chez17-services-infra (21) : il aurait été refusé à
l'application. P30 refuse tout dépassement et toute collision de nom, de VNI ou
de sous-réseau. Éprouvé aux bornes et par sabotage.

Vérification la plus forte : avant renommage, la dérivation reproduisait à
l'identique les deux zones créées à la main — nom, VNI de VRF, MTU, contrôleur.

voute.py saisir : le pendant de la génération. On génère un secret dont le dépôt
est la source, on saisit celui dont un tiers est la source — inventer une clé
d'API OPNsense donnerait une valeur refusée à la première requête, avec P18 au
vert sur une voûte inutilisable. Sans écho, double confirmation, rien sur la
ligne de commande.

Reste ouvert : aucun nœud de sortie déclaré. Le devis émet un marqueur, pas une
valeur plausible. Deux points à trancher — le nœud de sortie route selon sa
propre table (défaut actuel : 192.168.11.254, pas la frontière), et l'entrée
n'est pas redondante puisqu'elle dépend d'une route statique d'OPNsense vers un
seul nœud.

D-45 : l'affinité de VM attend Proxmox 9 (cluster en 8.4.19), tenue à la main.

30 preuves OK.

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

13 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-03 Le /29 de transit : frontières en bas (.1,.2), SVI en haut (.6), .3 réservée deux pare-feux cohabitent pendant une transition ; .3 attend une IP virtuelle CARP frontiere-opnsense.md §6 P23
D-04 Un point de routage porte le même dernier octet sur tous ses sous-réseaux on retient une adresse, pas treize frontiere-opnsense.md §4 P23
D-05 Un seul commutateur route ; les autres restent en L2 pur sans MLAG, dupliquer les SVI créerait autant de conflits d'adresses que de zones underlay.yml.example P23
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-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

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.