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>
89 lines
8.6 KiB
Markdown
89 lines
8.6 KiB
Markdown
# 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-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-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-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 | — |
|
|
|
|
## 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 | — |
|
|
|
|
---
|
|
|
|
## 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.
|