Un flux dont le pair nomme quatre rôles donne maintenant quatre règles,
chacune renvoyant à l'IPSet de son rôle. 52 règles, toutes par IPSet, zéro
littérale.
Le gain n'est pas cosmétique : une règle porte qui elle autorise.
`-source +t17-srv-keycloak` se lit ; une liste de quatre adresses demande de
retrouver à qui chacune appartient.
La raison appartient au flux, pas à chacune de ses règles : elle est écrite
une fois au-dessus du paquet qu'elle explique plutôt que répétée quatre fois.
Vérifié : aucun renvoi orphelin, aucun IPSet inutilisé.
Preuves : 25 OK, 0 échec.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Six règles par tenant portaient quatorze adresses en dur : les mots-clés
`flotte` et `edge` n'avaient pas droit à un IPSet, seuls les rôles en
avaient. `flotte` en reçoit un, `edge` renvoie à celui de nginx. 36 des 40
règles se lisent maintenant `-source +t17-…`.
Et le devis listait 28 à 30 IPSets par tenant dont la moitié n'était
référencée nulle part : un opérateur en aurait créé 58 pour n'en utiliser
qu'une douzaine. Seuls les IPSets référencés sont émis — 6 par tenant. Un
devis crée ce qu'il liste.
Restent quatre règles en liste explicite, celles dont la source est plusieurs
rôles à la fois. Aucun IPSet unique ne les couvre et Proxmox n'accepte qu'une
référence par règle ; les éclater gonflerait le devis pour un gain
discutable.
Preuves : 25 OK, 0 échec.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Les IPSets portaient l'étiquette longue (`chez17_serveur_postgresql`), les
groupes l'index court (`t17-srv-postgresql`) : deux conventions dans un même
document. Tout porte maintenant `t<index>-` et la même forme abrégée.
La troncature reste propre à chaque objet — Proxmox est large sur les IPSets,
étroit sur les groupes. Un nom peut être entier d'un côté et abrégé de
l'autre ; chacun respecte sa contrainte, le préfixe reste commun.
Vérifié : aucune collision d'IPSet, et tout renvoi `+X` d'une règle pointe
vers un IPSet existant — 58 IPSets, 34 groupes, aucun orphelin.
Preuves : 25 OK, 0 échec.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Elle partait de `hotes_actifs` avec un repli sur « tous » quand il n'y en
avait aucun. Technolibre listait donc ses 14 VM (zéro actif, repli déclenché)
et Chezlepro une seule (un actif) — un opérateur aurait lu qu'une seule VM
avait besoin de règles.
Les IPSets et les groupes incluaient déjà les hôtes planifiés,
délibérément : un pare-feu se prépare avant que la VM existe. L'affectation
suit la même règle. 14 de chaque côté, toutes avec leur VMID.
Preuves : 25 OK, 0 échec.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
`make devis-proxmox-fw` : 34 groupes de sécurité, 40 règles, 2 tenants —
depuis les 57 flux intra-tenant que le registre connaissait déjà.
Défense en profondeur, pas remplacement : l'hyperviseur filtre puis l'hôte
destinataire filtre à nouveau. Coût de maintenance nul, les deux barrières
lisent le registre par les MÊMES fonctions — la duplication est dans
l'application, jamais dans la décision.
Un IPSet par rôle porte les membres, les groupes y renvoient : ajouter un
hôte à un rôle met à jour toutes les règles qui l'autorisent, en un endroit.
Garde ajoutée après coup : Proxmox limite un nom de groupe à 18 caractères.
Ma première version tronquait sans vérifier — deux rôles tronqués au même nom
auraient fusionné leurs règles, donnant à une VM les autorisations d'un rôle
qu'elle ne porte pas, silencieusement. Le préfixe porte maintenant l'index
plutôt que l'étiquette, et une garde échoue sur toute collision. Exercée.
Conséquence consignée : tout ce qui entre dans un tenant passant par la
frontière, le contrôleur Ansible aussi — l'OPNsense devient un prérequis de
déploiement, pas une étape parmi d'autres.
Preuves : 25 OK, 0 échec.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
En préparant le déplacement des VTEP, la lecture des interfaces a montré que
`underlay.yml` annonçait 9000 alors que vmbr3 est à 1500. La garde P23
exigeait >= 1550 et passait parce que le fichier mentait. Une garde qui
valide une déclaration plutôt qu'une réalité donne un faux confort — pire
qu'une garde absente, qui au moins n'endort personne.
Le seuil ne peut pas être fixe non plus : 1550 aurait rejeté à tort un
transport à 1500 portant un overlay à 1450, qui tient exactement. Il dérive
d'un `mtu_overlay` déclaré : transport >= overlay + 50. Exercé.
Trouvé aussi : vmbr3 n'est pas VLAN-aware. L'adresse du VTEP y est non
étiquetée et vit dans le VLAN natif du port. Déplacer le VTEP n'est donc pas
un changement d'adresse — il faut un VLAN natif 10 ou une interface étiquetée
dédiée. C'est pourquoi le déplacement n'a pas été effectué.
Preuves : 24 OK, 0 échec.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Redresser les pairs EVPN vers l'underlay suppose que les hyperviseurs
existent dans le modèle. Ils n'y étaient pas.
La reconnaissance a montré la cause : vmbr3 porte 10.27.19.{41,43,47} sur les
trois nœuds — l'adresse des VTEP est prise dans le supernet de Chezlepro. Le
modèle refuse d'exprimer cet état : déclarer 10.27.19.0/24 en underlay ferait
échouer P23. La garde détecte la faute avant qu'on ne la documente.
Ajouté un `role` sur les hôtes (switch par défaut, hyperviseur, frontiere) :
le réseau ne suffit pas à le déduire, et un hyperviseur déclaré recevait une
configuration de commutateur en partie B.
Corrigé une « source unique » qui n'en était pas une : `switches_acces()`
avait été introduite comme LA décision du « qui est un switch d'accès », mais
`partie_acces()` gardait sa copie locale du filtre et ne l'appelait jamais.
Les deux ont divergé au premier hôte non-commutateur. Écrire « source
unique » dans un commentaire ne la crée pas.
Preuves : 24 OK, 0 échec.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Décision : l'overlay EVPN plafonne à 1450. Conséquence invisible : sous 1500,
tout ce qui traverse la frontière dépend de la découverte de MTU de chemin,
donc de l'ICMP « fragmentation nécessaire ».
Or le registre ne connaissait que TCP et UDP. Ce message ne pouvait pas être
déclaré et la bordure en `block in log all` l'aurait jeté : la connexion
s'établit, les petites requêtes passent, les grosses réponses restent
suspendues — la panne la plus coûteuse à diagnostiquer, et celle qu'on impute
d'abord à l'application.
`protocole: icmp` est admis ; le champ `port` y porte le type
(`frag-needed`). Le socle déclare les deux sens. Vérifié : nftables d'hôte
inchangés, le pair `externe` reste sauté.
Reconnaissance (lecture seule) : l'EVPN est à moitié construit — contrôleur
EVPN0017 (ASN 65000), zones VRF0011 et VRF0017, un VRF par tenant avec le VNI
égal à l'index. Aucun VNet, aucun nœud de sortie.
Signalé et non corrigé : les pairs BGP sont dans 10.27.19.0/24, le
sous-réseau Services-infra de Chezlepro. Le transport du cluster dérive de
l'index d'un tenant, et une VM de cette zone partage son sous-réseau avec les
VTEP — l'isolation est percée à l'endroit que l'EVPN devait fermer.
Preuves : 24 OK, 0 échec.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Les trois dates de « décisions renversées » avaient été estimées.
L'historique les corrige : ACL et routage sur commutateur remontent au
2026-07-07 (`make devis-reseau`), pas au 29 juillet ; l'underlay gitignoré au
2026-07-24, pas au 31.
Un registre qui invente une date perd la confiance qu'on lui accorde sur le
reste. Chaque renversement cite maintenant le commit qui l'a opéré —
vérifiable en une commande.
Ajouté : qui décide. Toutes ces décisions sont celles de l'opérateur du
dépôt, plusieurs 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 qu'héritée change ce qu'on s'autorise à en faire.
Non corrigé : le renvoi de D-07. `frontiere-opnsense.md` §1 porte bien un
titre explicite « Décision (2026-08-02) : pas d'ACL sur cette fabric » — ma
réserve était infondée.
Preuves : 24 OK, 0 échec.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Les décisions étaient écrites là où elles s'appliquent et leur histoire dans
le CHANGELOG — mais « pourquoi le /29 et pas le /30 ? » demandait de relire
vingt entrées. Le registre ne répète rien : il dit quelles décisions
existent, pourquoi, où lire le détail, et ce qui les garde.
28 décisions en quatre familles : le réseau, qui possède quoi, les secrets,
la méthode. Une décision peut n'être gardée par aucune preuve — elle reste
une décision, et le registre le montre plutôt que de laisser croire à une
couverture complète.
Et une section qu'on omet d'habitude : les décisions RENVERSÉES. Trois y
figurent, et elles expliquent pourquoi le code porte encore des branches qui
semblent inutiles — `acl_inter_tenant: true` et `routage_tenants: switch`
restent les défauts parce qu'une autre fabric peut en être capable.
Aucun de ces renversements ne vient d'un changement d'avis : les trois
viennent d'un fait découvert APRÈS la décision. C'est l'argument le plus fort
pour éprouver avant de figer.
Les 30 renvois internes vérifiés : aucun document ni section introuvable.
Preuves : 24 OK, 0 échec.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
P21, P23 et P24 renvoyaient à AFF-001 — « Set-OPS est un moteur Ansible
générique » — sans rapport avec la fédération, l'underlay ni la frontière.
P17, P19 et P20 n'avaient aucune référence. Une preuve accrochée à la
mauvaise affirmation passe au vert et n'atteste de rien de ce qu'on croit.
Ajouté §10 du registre : six affirmations (AFF-101..106) pour l'architecture
réseau et la fédération. La couverture du plan par le panneau est en 🟡, avec
ses exceptions nommées — listes de tables de l'underlay, ports physiques,
nœud de sortie.
Volontairement absente : la justesse des devis. Leur syntaxe dépend d'un
matériel que le dépôt ne possède pas ; six familles ont été confrontées au
commutateur réel, deux étaient fausses, mais c'est une vérification datée et
non une preuve rejouable. Le dépôt n'affirme pas que ses devis s'appliquent,
il affirme qu'ils dérivent.
Corrigé aussi : P03, P06, P12 et P13 portent maintenant les références que la
table leur attribuait déjà — la correspondance existait en double et seul le
document la tenait. Et la table attribuait AFF-030 (« inventaire complet ») à
P15, qui valide le modèle socle ; c'est P16 qui exécute
`ansible-inventory --list`.
35 affirmations référencées, aucune référence orpheline.
Preuves : 24 OK, 0 échec.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Elle décrivait les SVI par zone, les ACL d'isolation inter-tenant et un
tableau de dialectes limité aux masques d'ACL. Rien de tout ça n'est vrai
d'une fabric en SDN — et c'est le point d'entrée pédagogique : on y
apprenait à construire le mauvais réseau, avec la conviction de suivre la
documentation.
Elle présente maintenant les deux mondes côte à côte (`routage_tenants` :
`switch` ou `sdn`), avec ce qui change et surtout ce qui ne change pas — le
`.1` d'une passerelle ne change pas d'adresse, il change de porteur.
Ajouté : le devis de la frontière, absent de la page alors qu'il dérive du
même registre des flux ; le lien de transit et le piège de la route de
retour, qui a coûté une passe de déploiement ; les fabrics et le fait qu'un
devis est une configuration qu'on applique, pas un inventaire ; le MTU
minimal en SDN.
Et la leçon des dialectes, qui vaut au-delà de Set-OPS : trois formes ont été
supposées, deux étaient fausses, et la pire ne levait aucune erreur —
`allowed vlan add` ne retranchait rien sur un port qui autorisait déjà tout.
Une commande acceptée n'est pas une commande qui fait ce qu'on croit.
Preuves : 24 OK, 0 échec.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
La vue Réseau n'affichait que le devis des commutateurs. Le devis frontière
était totalement absent de l'interface, alors qu'il porte les règles de la
bordure et ses avertissements — dont celui sur « Block private networks »,
invisible dans les règles elles-mêmes.
Ajouté `/api/devis-opnsense` et son bloc d'affichage avec bouton de copie.
Vérifié par HTTP que les deux points d'API servent exactement ce que le CLI
produit : 181 et 125 lignes, identiques au caractère près. Import paresseux
et gardé — ce module lit l'underlay et les inventaires de tous les tenants,
une erreur aurait sinon vidé la vue entière.
Aide corrigée sur trois points périmés : les VLAN tenants annoncés « uniques
sur le trunk » (faux en SDN, aucun n'y circule) ; le dialecte renvoyé à une
variable d'environnement alors que c'est un intrant de la section Fabric ; et
la route par défaut dite « à adapter » alors que la section 5 l'émet.
Elle dit maintenant ce qui reste à nommer à la main : les ports physiques et,
en SDN, le nœud de sortie EVPN.
Preuves : 24 OK, 0 échec.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Régression de la correction précédente. Le champ décrivant le CÂBLAGE du lien
de transit réutilisait la variable du PROCHAIN SAUT. Les deux étaient
identiques jusqu'à la bascule SDN ; elles ont divergé, et la section 1
annonçait `switch <NOEUD-DE-SORTIE-EVPN>`.
Sur ce lien, le commutateur est à 10.0.4.6 — le nœud de sortie n'y figure
pas, il est joignable À TRAVERS. Le champ décrit maintenant le câblage,
indépendamment du routage.
Preuves : 24 OK, 0 échec.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Le prochain saut des routes tenants pointait le SVI du commutateur. En EVPN
il ne route plus les tenants : la route arriverait sur un équipement sans
chemin vers le tenant — configuration qui s'applique sans erreur et ne
fonctionne pas. Le devis émet `<NOEUD-DE-SORTIE-EVPN>` et dit pourquoi.
`underlay.passerelle_sortie` garde son sens : adresse du pare-feu sur le lien
de transit, donc sortie de l'UNDERLAY. Deux choses distinctes.
Corrigé aussi une contradiction antérieure au SDN : la section 0 demandait de
router l'administration vers 10.0.4.6, le SVI du commutateur lui-même, alors
que `devis-reseau` émet 10.0.4.1, l'adresse du pare-feu — tout en affirmant
que l'autre devis « émet déjà ces routes ». Elles coïncident maintenant,
vérifié ligne à ligne.
Et deux commentaires qui affirmaient l'inverse de la décision se dérivent du
mode de routage.
Preuves : 24 OK, 0 échec.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Le filtre `routage_tenants` n'avait été posé que sur la partie A et les
trunks. La partie B a sa propre boucle et sortait toujours les douze VLAN
tenants : aucun trunk ne les portait, aucun SVI ne les utilisait, mais leur
présence suggérait que les switches d'accès devaient les connaître — ce qui
contredit la décision.
Vérifié dans les deux sens : zéro VLAN tenant en mode `sdn`, les vingt-quatre
déclarations de retour en mode `switch`.
Corrigé aussi l'en-tête, qui annonçait les VLAN tenants « uniques globalement
sur le trunk » — ils ne sont plus sur aucun trunk en SDN.
Preuves : 24 OK, 0 échec.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Trois décisions appliquées : une zone EVPN par tenant, routage ET filtrage
inter-zone à ce niveau, inter-tenant obligatoirement par l'OPNsense.
`underlay.routage_tenants` (`switch` par défaut, `sdn`) : en SDN le devis
cesse d'émettre VLAN tenants, SVI et ACL, et les retire des trunks. Chez
Chezlepro les trunks passent de quinze VLAN à deux — seul du VXLAN circule,
que le commutateur transporte sans le lire.
Le MTU devient une garde : `make underlay` refuse un transport sous 1550 en
mode SDN, en disant pourquoi — sous ce seuil le ping passe et les transferts
échouent.
Le partage des responsabilités est écrit dans docs/sdn-evpn.md : qui route,
qui filtre, pour chaque nature de trafic. Deux conséquences nommées —
l'inter-tenant ne peut plus être oublié (il traverse une bordure en block par
défaut), et le commutateur ne voit plus rien du trafic tenant.
Point ouvert : le registre des flux n'a aucun mot-clé pour un flux
inter-tenant. Défaut sûr, mais on ne peut pas déclarer d'exception légitime.
Preuves : 24 OK, 0 échec.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Les commutateurs ne savent pas lier une ACL à une interface de routage.
Plutôt que d'assumer indéfiniment la perte d'isolation réseau, le routage
inter-zone passe à Proxmox SDN, zones EVPN.
Une zone EVPN est un VRF — celui qu'on regrettait de ne pas avoir dans le
matériel, obtenu en logiciel. Il referme le trou signalé quelques heures plus
tôt : un tenant n'a plus de route vers l'underlay, celui-ci n'étant pas dans
sa table de routage. Le plan de gestion redevient protégé par construction.
La projection du modèle ne demande AUCUN changement de dérivation, vérifiée
sur les deux tenants : zone=tenant, VNet=zone de sécurité, tag=vlan_de(),
subnet et gateway inchangés. Le `.1` change de porteur, pas d'adresse — du
SVI du commutateur vers la passerelle anycast du VNet.
Rien n'est éprouvé, rien n'est généré. Le document fixe la cible et une
séquence de spike en cinq points, dont le MTU (premier mur de VXLAN) et la
tentative d'accès à l'underlay qui DOIT échouer.
Preuves : 24 OK, 0 échec.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Les interfaces VLAN du Binardat n'offrent aucun `access-group` : impossible
de lier une ACL à un SVI. Plutôt que d'émettre des règles jamais liées — qui
auraient l'air d'isoler sans jamais filtrer — la capacité se déclare :
`underlay.acl_inter_tenant`, `true` par défaut.
Ce n'est pas lié au dialecte de CLI mais au matériel : un autre commutateur
parlant la même CLI pourrait savoir lier des ACL.
À `false`, la section 3 ne contient plus de règles mais la raison, et surtout
ce qu'on perd : une VM émettant vers l'underlay est routée localement vers le
mgmt des switches, celui de Proxmox et l'OOB/IPMI. Les nftables des VM n'y
peuvent rien (politique `output` permissive), et l'IPMI n'est pas un hôte
géré.
Des VRF auraient donné cette isolation sans ACL — critère à retenir au
prochain renouvellement. Parade d'ici là : sortir le management de la fabric
routée des tenants, comme l'est déjà le stockage.
Preuves : 24 OK, 0 échec.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
`ip ?` sur une interface VLAN du Binardat n'offre aucun `access-group`, et la
liste complète des commandes de ce mode n'en contient pas davantage. La ligne
`ip access-group <NOM> in` posée sur les douze SVI n'existe pas sur cette
plateforme.
C'est la ligne qui rend l'isolation effective. Sans elle, les ACL de la
section 3 sont parfaitement définies et jamais liées : `show access-lists`
afficherait « used 0 time(s) », et rien d'autre ne signalerait que
l'isolation inter-tenant ne filtre rien. Même signature que le défaut du
trunk — une configuration qui a l'air juste et n'agit pas.
Le `firewall disable` aperçu dans un `show running-config` prend
rétrospectivement du sens : le filtrage semble conditionné globalement.
Aucune forme de remplacement n'est devinée. Le devis porte un avertissement à
cet endroit, en dialecte `binardat` uniquement — après trois syntaxes
supposées dont deux fausses, marquer l'incertitude vaut mieux qu'un quatrième
pari.
Preuves : 24 OK, 0 échec.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
J'ai annoncé « les six familles sont closes » sur la foi d'un
`spanning-tree ?` en mode configuration GLOBALE. Trois lignes du devis
vivent ailleurs et n'y figuraient donc pas :
- `spanning-tree portfast trunk` (interface) — `trunk` est un mot-clé
Cisco ; l'équivalent s'écrit souvent `portfast` seul, voire `edged-port` ;
- `ip access-group <NOM> in` (interface) — c'est ce qui LIE l'ACL au SVI ;
sans elle l'ACL existe et ne filtre rien ;
- `ip default-gateway <ip>` (global, absent de l'aide consultée).
`(config-if)#spanning-tree ?` et `(config-if)#ip ?` les donneraient.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
`spanning-tree ?` en mode configuration tranche la dernière inconnue, en
faveur de la forme émise : `mode` et `priority` s'acceptent au niveau global.
La priorité n'a pas besoin d'être portée par une instance, même en MSTP — la
réserve inverse, notée la veille, était infondée et le devis ne la porte
plus. Une mise en garde fausse nuit autant qu'une syntaxe fausse.
Ajouté : `spanning-tree` seul, qui garantit l'état actif. Sans lui, `mode` et
`priority` sur un boîtier où le protocole aurait été désactivé
configureraient un arbre qui ne tourne pas. Sans effet s'il est déjà actif —
même logique déclarative que pour les trunks.
Six familles vérifiées contre le matériel : VLAN, SVI, trunks, routes, ACL,
spanning-tree. Deux ont révélé un défaut réel plutôt que de confirmer
l'existant — les routes (CIDR) et les trunks, dont `add` ne retranchait rien.
Preuves : 24 OK, 0 échec.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Une ACL générée s'applique telle quelle, ses règles dans l'ordre émis —
`ip access-list extended <NOM>`, masques normaux, `any`.
Détail de lecture consigné : le boîtier affiche `any-destination` là où l'on
saisit `any`. Comparer un `show access-lists` au devis ferait apparaître une
différence qui n'en est pas une.
Cinq familles de syntaxe sur six sont maintenant vérifiées contre le
matériel : VLAN, SVI, trunks, routes, ACL. Ne reste que la forme d'entrée des
commandes de spanning-tree, que `show` ne révèle pas.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
`underlay.stp.mode` passe à `mstp` — le mode d'usine du commutateur, vérifié
par `show spanning-tree`. Sur une étoile sans lien redondant, l'instance 0 de
MSTP se comporte comme un RSTP : changer de mode aurait donné le même
résultat, au risque près de toucher à un protocole qui fonctionne déjà.
Le devis énonce son incertitude là où elle est : en MSTP la priorité se règle
souvent par instance, alors que la forme émise est globale. Il le dit plutôt
que de laisser croire à une syntaxe vérifiée — `show spanning-tree` donne
l'état du protocole, jamais la forme d'entrée des commandes.
Corrigé : le commentaire de la section 6 disait « RSTP » en dur alors que le
mode est déclaré. Il le dérive.
Preuves : 24 OK, 0 échec.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Correction d'une déduction fausse : j'avais conclu de son absence du
`show running-config` que le spanning-tree était désactivé. Il est actif —
il n'y figurait pas parce qu'il est aux valeurs d'usine. Une absence dans une
configuration ne veut pas dire une absence de fonction.
La plateforme est en MSTP (802.1s, Force Version 3) alors que
`underlay.stp.mode` déclare rstp. Sur une étoile sans lien redondant les
deux se comportent identiquement ; reste à décider si l'on aligne la
déclaration sur le matériel ou l'inverse.
La priorité de pont est déjà 32768 : la ligne émise pour les switches
d'accès est un non-opérant.
Reste non vérifiée la forme d'ENTRÉE des commandes — `show` donne l'état,
pas la syntaxe. En MSTP la priorité se règle en général par instance, ce que
la forme émise ne fait pas.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
`switchport trunk allowed vlan ?` sur le matériel confirme la syntaxe et
révèle un défaut : `add` ajoute à la liste courante, la forme sans mot-clé
la définit.
Le devis émettait `add`. Or un port trunk neuf autorise tous les VLAN — dans
une config réelle, les ports n'ont aucune ligne `allowed vlan`, ce qui
signifie exactement cela. Y ajouter la liste voulue n'en retranchait aucun :
le trunk continuait de tout transporter, et le devis donnait l'illusion de
restreindre. Le pire genre de défaut — ça a l'air juste, ça s'applique sans
erreur, et ça ne fait pas ce que ça annonce.
La forme sans mot-clé est aussi atomique : `none` puis `add` couperait le
trunk entre les deux commandes, ce qui suffit à perdre la session si on
l'applique sur le port de gestion.
Preuves : 24 OK, 0 échec.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Un `show running-config` du commutateur tranche la question restée ouverte :
la plateforme écrit ses routes en notation CIDR — `ip route 0.0.0.0/0
192.168.10.254` — et non en masque séparé comme Cisco. Le générateur
produisait du Cisco quel que soit le dialecte.
`route_statique()` suit maintenant le dialecte, comme les masques d'ACL.
Vérifié dans les deux formes.
Restent non vérifiés faute d'apparaître dans la config réelle : la syntaxe
des ACL, celle de `switchport trunk allowed vlan add`, et le spanning-tree —
totalement absent du `show running-config`, ce qui suggère qu'il est
désactivé par défaut sur cette plateforme.
Preuves : 24 OK, 0 échec.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Renvoi ambigu corrigé : « en cas de retour arrière (§6) » figurait dans
l'étape 6 — deux « 6 » pour deux choses dans une seule phrase. La section est
nommée plutôt que numérotée.
Le responsable désigné devient le §3 : il vivait sous « le transfert de nom
de domaine » alors que ce n'est pas un emprunt aux registraires mais une
décision de modèle, valable migration ou pas.
Et le §8 énumérait ce que la migration ne déplace PAS sans dire ce qu'elle
déplace. Le responsable, lui, suit le tenant — c'est l'inverse, et le dire
renforce la ligne de partage : ce qui est à l'hébergeur reste, ce qui est au
tenant part avec lui. Si quelque chose appartenant à l'organisation ne peut
pas partir, elle n'est pas vraiment souveraine ; si quelque chose
appartenant à l'hébergeur devait partir, la frontière est mal tracée.
Sections renumérotées (9 au lieu de 8), six renvois internes vérifiés.
Preuves : 24 OK, 0 échec.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Elle se perd, se compromet, ou la personne quitte l'organisation. Sans
procédure, un tenant devient inmigrable : captif non par contrat mais par
accident — exactement ce que la recette existe pour empêcher.
Deux écueils symétriques consignés. Trop lourde, la procédure n'aboutit
jamais et le tenant reste bloqué. Trop légère, elle devient le chemin de
moindre résistance pour contourner la signature : inutile de forger un
mandat si l'on peut se faire attribuer la clé. Le recouvrement doit être au
moins aussi difficile que ce qu'il protège.
Vraisemblablement le même mécanisme que le changement de responsable — dans
les deux cas quelqu'un d'extérieur à la clé atteste de l'autorité. Piste la
plus transposable des registraires : un contact de secours nommé en même
temps que le responsable, tant que personne n'est en situation d'urgence.
Preuves : 24 OK, 0 échec.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Chaque tenant a un responsable désigné — la personne qui engage
l'organisation, et dont la signature seule vaut mandat de migration. Sans
responsable nommé d'avance, la question « qui peut décider de déménager
cette organisation ? » se pose au pire moment, quand les deux hébergeurs ont
un intérêt dans la réponse.
La table des états annonçait une réversibilité « gratuite » jusqu'à
`libéré`, alors que le §6 établit qu'elle change de nature à la bascule. Pas
contradictoire, mais un lecteur pressé s'arrêtant au tableau en retirait une
fausse impression. Ce qui reste gratuit est l'adressage, pas le retour.
Deux questions de gouvernance ajoutées aux points à trancher : où le
responsable est déclaré et comment on en change — acte au moins aussi
sensible que la migration, puisqu'il décide qui pourra la mandater ensuite ;
et la rétention, convenue avec qui, consignée où, attestée par qui.
Preuves : 24 OK, 0 échec.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
La recette affirmait la réversibilité sans décrire le retour. Or il change
de nature à la bascule, et le geste évident — repointer le DNS — devient
faux à cet instant : les utilisateurs ont écrit chez l'entrant, et ces
données n'existent nulle part ailleurs. Les perdre serait silencieux.
Trois régimes écrits : avant le gel (sans conséquence), pendant le gel
(dégeler), après la bascule (migration inverse, même outillage).
Rendu explicite : le sortant reste gelé après la bascule, jusqu'à
confirmation. Le dégeler « au cas où » créerait deux copies vivantes et plus
aucune vérité ; en contrepartie il n'a pas divergé, donc le delta d'un
retour reste à sens unique.
Deux points de non-retour distingués : la bascule fait perdre le retour
gratuit, la purge fait tout perdre. D'où l'exigence ajoutée : les critères
de confirmation se fixent par écrit AVANT la première bascule.
Corrigés : le rattrapage est à l'étape 5 (non 4) ; le chemin de vérification
hors DNS public est un prérequis de l'étape 3 elle-même.
Preuves : 24 OK, 0 échec.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
La séquence, les états et les gardes — écrite avant tout code, délibérément :
figer un enchaînement qu'on n'a jamais joué serait prématuré.
Le modèle est le transfert de nom de domaine : le mandat appartient au
client, verrou par défaut, deux actes délibérés et traçables. Là où
l'analogie casse elle est remplacée, pas étirée — sans registre central pour
arbitrer, le mandat est signé par le tenant et vérifié contre une clé
publique de son plan ; un secret partagé ne prouverait rien à l'entrant.
L'ordre est commandé par une règle unique : le receveur doit être prouvé
prêt avant que quoi que ce soit ne gèle. L'entrant se construit pendant que
le sortant sert ; l'interruption se réduit au delta plus la propagation DNS.
Garde de transition, pas conseil : `gelé` est inaccessible tant que
`préparé` n'est pas prouvé.
Deux pièges consignés : le TTL s'abaisse à l'étape 1 et non à la bascule ;
l'entrant doit être vérifiable sans être public, sinon le tenant sert des
deux côtés et l'identité se dédouble.
La libération est une révocation, pas une transmission : re-clétage de la
voûte chez l'entrant, sans quoi l'ancien hébergeur garde à vie l'accès aux
secrets d'un client parti.
Preuves : 24 OK, 0 échec.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Un hébergeur sert plusieurs tenants et n'a qu'une frontière. Ses intrants
étaient lus chez le tenant actif : basculer sur un invité — Technolibre, qui
n'a pas de frontière à lui — faisait perdre au devis l'URL de gestion,
l'adresse publique et les deux interfaces. Il repartait en marqueurs, comme
si le boîtier n'existait pas. Vérifié en simulant la bascule.
Même famille que le défaut de l'underlay corrigé plus tôt ; c'est la
distinction hébergeur/tenant qui le fait apparaître.
Le devis et le panneau lisent maintenant la frontière chez l'hébergeur, qui
n'est pas déclaré pour autant : le symlink `underlay.yml` le désigne déjà.
Repli sur l'instance active sans underlay monté.
Vérifié : devis identique avec l'hébergeur actif, intrants conservés avec un
invité actif.
Preuves : 24 OK, 0 échec.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Un modèle décrivait un tenant — ses services, ses zones, ses bases. Tous les
hébergeurs n'ont pas le même matériel : l'infrastructure physique mérite le
même traitement.
Le modèle public gagne un underlay volontairement minimal (un commutateur,
pas de fabric de stockage séparée), point de départ honnête d'un petit
hébergeur. Les montages plus riches sont d'autres modèles, conformément à la
doctrine : un générique public, les étoffés en privé.
Le modèle contient désormais deux moitiés qui ne vont pas au même endroit :
`plan/` et `inventories/` chez le tenant, `underlay.yml` chez l'hébergeur.
`modeles.py verifier` le valide (P17), facultativement et sur sa cohérence
INTERNE seulement — pas contre les tenants fédérés réels, un modèle étant un
gabarit et non un site déployé. Il a fallu rendre paramétrables deux
hypothèses du validateur, qui lisait la nomenclature de l'instance active et
globait les dépôts frères ; comportement par défaut inchangé.
Cinq cas de rejet exercés : VLAN empiétant sur la plage tenant, passerelle au
mauvais dernier octet (lue dans la nomenclature du modèle), routeur inconnu,
sortie hors du lien, port déclaré deux fois.
Preuves : 24 OK, 0 échec.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Il vivait gitignoré à la racine du moteur : consommé par deux générateurs,
validé par P23, versionné nulle part. La dizaine de modifications de la
journée n'était récupérable d'aucune façon, et un clone frais repartait du
gabarit.
Il appartient à l'hébergeur — ses switches, ses câbles — pas au moteur, qui
est générique, ni à un tenant, qui n'en possède pas. Chezlepro est ici
hébergeur ET tenant, d'où la confusion initiale.
Le moteur le monte par symlink comme il monte le plan par `instance/`. Ce
lien NE SUIT PAS `make instance-utiliser` : la fabric reste celle de
l'hébergeur quel que soit le tenant actif.
Vérifié : les deux devis sortent identiques octet pour octet avant et après,
P23 verte, 24 preuves. Symlink brisé (clone sans dépôt frère) : dégrade
proprement, sections omises au lieu d'échouer. Cas exercé.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Le devis frontière annonçait trois routes de retour « déjà émises par
devis-reseau ». Le devis switch n'en émettait qu'une : il lisait
`nftables_admin_ssh` de la seule instance active, alors que la frontière
était passée multi-tenant. Les réseaux d'administration de Technolibre
n'étaient routés nulle part — et une affirmation fausse est pire qu'un
silence, elle désamorce la vérification.
`admin_tous_tenants()` vit dans devis_reseau et devis_opnsense l'importe au
lieu d'en refaire une copie : routes de retour et règles lisent les mêmes
tenants par construction. Vérifié identiques.
Ajouté : l'avertissement « Block private networks ». Le SSH d'administration
a une source RFC1918 arrivant sur une interface WAN, où ce filtre est actif
par défaut et s'applique AVANT les règles — coché, il jette le paquet sans
qu'aucune règle ne soit consultée. Un réglage d'interface est invisible dans
les règles, il fallait l'écrire à part.
Prédicat exactement RFC1918, périmètre de cette case ; `is_private` aurait
été trop large (documentation, CGNAT) et l'avertissement se serait déclenché
à tort. Trois cas exercés : RFC1918 averti, 8.8.8.8 muet, 203.0.113.7 muet.
Preuves : 24 OK, 0 échec.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Dans OPNsense une règle est toujours `in` sur l'interface d'arrivée : posée
ailleurs elle ne s'applique jamais, et le trafic est bloqué sans que la
configuration paraisse anormale. Les règles n'en portaient aucune, alors que
le champ est obligatoire dans l'API.
L'attribution se dérive du sens du flux : `ingress`/`externe` arrive par le
WAN, `egress`/`externe` par le lien de transit. Le SSH d'administration suit
la première ligne — le VPN est hébergé sur le pfSense voisin et revient par
l'adresse publique. Le montage parallèle décrit ce jour lève la dernière
inconnue.
Le rendu abandonne `pass out` pour `pass in on <interface>`, l'idiome réel
d'OPNsense et ce que le client d'API devra envoyer.
Ajouté aussi :
- `opnsense_wan_ip`, la face publique, en section Frontière du panneau ;
- invariant du dernier octet (P23) : un point de routage porte le même
dernier octet sur tous ses sous-réseaux. Le chiffre vient de
`reservations.passerelle`, pas d'une constante. Exemption des liens plus
étroits qu'un /24 — sur le /29 de transit l'adressage est dicté par les
participants. Vérifié que l'invariant tenait déjà sur les 13 sous-réseaux
routés avant d'écrire la garde.
Preuves : 24 OK, 0 échec.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Le devis était multi-tenant pour ses routes, mono-tenant pour ses règles :
il routait 10.21.0.0/16 et 10.27.0.0/16 mais ne filtrait que l'instance
active. Technolibre aurait été routé jusqu'à la bordure puis bloqué dans les
deux sens, SSH d'administration compris, sans qu'une ligne dise pourquoi.
La résolution est paramétrée par tenant : `inventaire_de()` lit le hosts.yml
de chaque instance fédérée, `cibles_par_role()` prend l'inventaire en
argument, les alias d'hôtes sont préfixés. 11 règles par tenant, 22 au total.
Cloisonnement : la première version faisait de SETOPS_ADMIN l'union des
réseaux d'administration — le plan de gestion d'un tenant serait entré chez
le voisin, la bordure rouvrant ce que les ACL de switch ferment. Corrigé
avant livraison : un alias par tenant, n'ouvrant que son propre supernet.
L'union reste pour les routes de retour et P24 : router n'est pas autoriser.
Deux omissions annoncées : tenant sans inventaire (aucune règle), tenant
sans `nftables_admin_ssh` (règle SSH omise plutôt qu'ouverte à `any`, ce qui
exposerait le SSH à Internet). Cas dégradé exercé.
Preuves : 24 OK, 0 échec.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Le devis se terminait par `block out log all` avec une seule règle sortante
(le relais SMTP). Appliqué tel quel, il coupait la flotte d'Internet : plus
d'apt, plus de NTP, plus de récursion DNS. Rien ne le signalait — la ligne
la plus lourde de conséquences du devis, posée à la suite des autres.
La sortie est déclarée dans le registre, donc dérivée :
- `serveur_debian` (socle, 14 hôtes) : 443 et 80 pour les dépôts apt, 123/udp
pour l'horloge — une dérive fait échouer step-ca et le SSO des semaines
après la cause ;
- `client_unbound` : 53 udp et tcp, la récursion depuis la racine qu'implique
`client_unbound_transitaires: []`. Le TCP est le repli obligatoire dès
qu'une réponse DNSSEC dépasse la taille UDP.
Le devis passe de 6 à 11 règles, et sa section 5 énonce le default-deny
sortant, le nombre de règles qui l'accompagnent, et où déclarer un besoin
oublié — jamais à la main dans le pare-feu.
Vérifié : les nftables d'hôte sont inchangés, octet pour octet. Le pair
`externe` reste sauté par resoudre_flux.
Non déclaré volontairement : le rôle `chrony` n'est référencé par aucun
groupe ni playbook — un flux pour lui aurait été une règle morte.
Preuves : 24 OK, 0 échec.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
`opt1`, `igb1` et `TENANTS` désignent le même port — identifiant interne,
périphérique FreeBSD, libellé affiché. L'API REST ne parle que du premier.
Les libellés des deux intrants ne le disaient pas, et la question s'est
posée en pratique.
Ils le disent maintenant, et docs/frontiere-opnsense.md explique les trois
couches et pourquoi `opt1` est le plus stable : il survit à un changement de
carte comme à un renommage.
La note « prochain_saut dérive de l'underlay » est repliée dans l'en-tête
régénéré par le panneau : une sauvegarde l'effaçait, le fichier étant
réécrit depuis le YAML analysé. Vérifié qu'une sauvegarde préserve valeurs
et références de voûte.
Corrigé au passage : après le renumérotage du /29, deux passages de la doc
annonçaient encore 10.0.4.2 comme prochain saut et contredisaient le devis.
Preuves : 24 OK, 0 échec.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Le rôle dérive l'empreinte à chaud depuis l'autorité, parce qu'un from-zero
régénère l'AC avec une empreinte neuve. Figée en voûte, elle serait périmée
dès la première reconstruction — et une empreinte périmée fait échouer le
bootstrap de chaque hôte.
Or `defaults/main.yml` portait encore un défaut lisant
`vault_step_ca_fingerprint`. Ce défaut était mort : la tâche suivante écrase
le fait sans condition, donc la valeur de la voûte n'avait aucun effet.
Le recensement de voute.py s'y laissait prendre — il cherche la chaîne
`vault_*` sans pouvoir savoir qu'un défaut n'est jamais lu. La « source
unique » avait hérité de l'erreur, et le panneau réclamait un secret
impossible à fournir avant que l'AC n'existe.
Défaut retiré : la clé disparaît du recensement (25 -> 24 exigés), du
gabarit et du panneau. `client_pki_ca_fingerprint_override` reste le moyen
d'épingler une empreinte.
`docs/intrants-communs.md` §H était une troisième copie manuelle de la liste
des secrets, avec les deux mêmes erreurs ; elle renvoie maintenant à
`voute.py lister` et à la preuve P18.
Preuves : 24 OK, 0 échec ; voute.py verifier --strict passe.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
`SECRETS_ATTENDUS` était une liste tenue à la main, en parallèle du
recensement que voute.py fait déjà depuis le plan, les rôles et les
group_vars. Deux sources pour la même vérité, et la manuelle avait divergé :
elle annonçait `vault_ldap_sssd`, que rien ne consomme — aucun rôle sssd
n'existe — et omettait `vault_step_ca_fingerprint`, réellement lu par
client_pki pour vérifier l'empreinte de l'AC racine.
Un opérateur qui suivait le panneau créait un secret inutile et en oubliait
un nécessaire.
Le rappel dérive maintenant de `voute.secrets_exiges()` — la même source que
la preuve P18 — augmentée de SECRETS_HORS_MOTIF pour les jetons Proxmox, qui
n'ont pas le préfixe `vault_`. Vérifié : 27 noms, écart nul avec le gabarit.
Dépôt public nu : liste vide plutôt qu'une erreur d'affichage.
Preuves : 24 OK, 0 échec.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Tout le modèle d'underlay bâti aujourd'hui s'éditait à la main dans un YAML,
pendant que la doctrine dit qu'un sysadmin doit exploiter l'outil sans IA.
La frontière avait eu sa section ; l'underlay, non.
La section couvre les valeurs plates : switch routeur, dialecte de CLI, mode
et topologie de spanning-tree. Les listes de tables (`reseaux`, `hotes`, donc
les ports) restent hors de portée du panneau — elles demandent une vue
dédiée, comme celle des serveurs.
Le dialecte devient un intrant déclaré : il ne vivait que dans
SETOPS_DIALECTE. C'est une propriété du matériel, donc de la fabric.
Précédence : `--dialecte` > environnement > intrant déclaré > cisco.
Écriture chirurgicale plutôt que safe_dump : underlay.yml porte 23 lignes de
commentaires qui expliquent des décisions d'architecture, et un dump les
aurait effacées comme c'est arrivé à plan/applications.yml. Vérifié : trois
valeurs modifiées, 73 lignes et 23 commentaires avant comme après. Une clef
absente du fichier est refusée plutôt qu'inventée à un endroit arbitraire.
Preuves : 24 OK, 0 échec.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Les noms de ports n'étaient modélisés nulle part — des marqueurs littéraux
dans le générateur, remplacés à la main dans la sortie et perdus à chaque
régénération. Seul endroit du devis où le travail était refait à répétition.
Ils se déclarent par équipement sous quatre clefs correspondant aux quatre
natures de lien : `hyperviseurs` et `frontiere` (terminaux, portfast),
`rayons` (côté routeur), `montante` (côté switch d'accès).
Le devis émet les vrais ports, y compris PLUSIEURS vers les hyperviseurs —
il n'en supposait qu'un, alors que le cluster en compte trois. Non déclarés,
les marqueurs reviennent, par équipement : un switch renseigné et un autre
non cohabitent.
La partie B devient par switch : gestion, montante et ports terminaux
diffèrent d'une machine à l'autre, un bloc commun n'avait plus de sens.
Gardes exercées : port déclaré deux fois, rayon vers un switch inconnu,
`rayons` sur autre chose que le routeur, `montante` sur le routeur.
Preuves : 24 OK, 0 échec.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
La partie A déclarait ses ports de bord deux fois — l'interface en section
4, son portfast en section 6 — alors que la partie B posait tout dans le
même bloc. Deux conventions pour la même chose dans un seul document.
Le portfast est posé avec son interface (sections 4 et 4b). La section 6 se
réduit au global — mode, priorité du pont racine — plus les deux
avertissements : les rayons de la 4c n'en sont volontairement pas, et
pourquoi BPDU guard n'est pas émis.
Vérifié : aucune interface déclarée deux fois dans une même partie,
3 portfast avec `stp`, 0 sans lui, 0 sur un rayon.
Preuves : 24 OK, 0 échec.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
L'ajout du spanning-tree venait de rendre dangereuse une imprécision de
titre. La section 4 s'appelait « Trunk vers Proxmox + inter-switch » et
n'émettait qu'un port, que la section 6 déclarait en bord de réseau.
Réutiliser ce placeholder pour les rayons revenait à mettre portfast sur les
liens qui portent les BPDU — désactiver la protection anti-boucle là où elle
sert.
Les rayons sont dérivés et émis à part (4c côté routeur, B3a côté accès), un
par switch d'accès, marqués comme non-bord. La section 4 ne désigne plus que
les hyperviseurs ; la partie B distingue sa montante de son trunk terminal.
`underlay.switches_acces()` devient la source unique du « qui est un switch
d'accès » — pour leur devis ET pour les rayons côté routeur.
Vérifié : 3 commandes portfast avec `stp` déclaré, 0 sans lui, 0 sur un
rayon.
Preuves : 24 OK, 0 échec.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Le devis ne disait rien du spanning-tree. Sur une fabric convergée à trois
switches, une boucle par brassage accidentel est une tempête de diffusion.
La topologie se déclare (`stp: {mode: rstp, topologie: etoile}`) et le devis
en tire la configuration. Le routeur est désigné pont racine : il est le
centre de l'étoile, tous les chemins passent déjà par lui, donc l'arbre
logique suit le câblage physique plutôt qu'une élection arbitraire. Les
switches d'accès reçoivent une priorité haute — jamais racine.
Ports terminaux déclarés en bord de réseau. BPDU guard délibérément NON
émis : un pont Linux dont le STP serait activé enverrait des BPDU et ferait
tomber le port côté hyperviseur. Le devis dit pourquoi et à quelle condition
l'ajouter.
En étoile aucun lien n'est redondant : RSTP est un filet, pas une nécessité.
Le devis le dit au lieu de laisser croire à une protection indispensable.
Validation : `mode` et `topologie` contrôlés, `stp` sans `routeur` refusé
(aucun pont racine désignable). Sans `stp`, la section signale l'absence de
protection au lieu de disparaître.
Réserve : la forme `binardat` du spanning-tree n'est pas vérifiée sur le
matériel, comme les `ip route`.
Preuves : 24 OK, 0 échec.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Les VLAN et les trunks étaient déjà filtrés par fabric ; le roster de la
section 0, non. Un équipement d'une autre fabric — les switches de stockage,
le jour où ils seront déclarés — serait apparu en commentaire dans un devis
qui ne le concerne pas. Un devis est une configuration qu'on applique, pas
un inventaire.
Vérifié en déclarant un hôte de la fabric stockage : il reste absent, et la
sortie du jour est inchangée.
Preuves : 24 OK, 0 échec.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Le stockage jumbo (iSCSI, Ceph) est porté par un réseau indépendant de deux
switches 10G, sans câble commun avec la fabric convergée des sleipnir. Le
modèle l'ignorait : le devis déclarait les VLAN 20/30/31 sur les switches
convergés et les mettait dans leurs trunks. C'était faux.
Chaque réseau porte une `fabric` (`principal` par défaut). Le devis ne
configure que celle du routeur, et énonce explicitement ce qu'il ne couvre
pas au lieu de le taire — VLAN, trunks et spanning-tree d'une autre fabric
ne le regardent pas.
Les `deny` de l'ACL couvrent en revanche toutes les fabrics : la règle porte
sur l'adresse de destination, pas sur le câblage. Si un chemin s'ouvre un
jour vers le stockage, il est déjà fermé.
Preuves : 24 OK, 0 échec.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Les frontières occupent le bas du /29, le SVI du switch le haut. `.3` reste
libre pour une future IP virtuelle CARP si les deux OPNsense passent en
haute disponibilité — ce jour-là, `passerelle_sortie` pointera sur la VIP
plutôt que sur un boîtier nommé, et ce sera le seul changement.
Plan du lien :
10.0.4.1 bifrost-1 frontière active, sortie par défaut de la flotte
10.0.4.2 bifrost-2 seconde frontière
10.0.4.3 libre réservée VIP CARP
10.0.4.6 sleipnir-01 SVI du switch routeur
Les deux devis suivent sans intervention : routes du switch vers 10.0.4.1,
routes tenants de la frontière via 10.0.4.6.
Preuves : 24 OK, 0 échec.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>