diff --git a/CHANGELOG.md b/CHANGELOG.md index ec29f01..c2be6dd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,34 @@ # CHANGELOG — Set-OPS +## 2026-08-02 (suite 8) — recette de migration d'un tenant entre hébergeurs + +`docs/migration-tenant.md` : 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**, qui résout depuis trente ans les mêmes +problèmes : le mandat appartient au **client** (ni l'hébergeur sortant ni l'entrant ne peut +déplacer un tenant seul), verrou par défaut, deux actes délibérés et traçables. + +Là où l'analogie casse, elle est remplacée plutôt qu'étirée : il n'y a **pas de registre** +central pour arbitrer, donc 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, il pourrait venir du +sortant. + +**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 et se prouve pendant que le sortant sert +normalement ; l'interruption se réduit au rattrapage du delta plus la propagation DNS. Ce +n'est pas un conseil mais une **garde de transition** — l'état `gelé` est inaccessible tant +que `préparé` n'est pas prouvé. + +Deux pièges consignés parce qu'ils ne vont pas de soi : le **TTL** s'abaisse à l'étape 1, pas +à la bascule, sinon tout le bénéfice de l'ordre est perdu ; et l'entrant doit être +**vérifiable sans être public**, sinon le tenant sert des deux côtés et l'identité se +dédouble. + +Enfin, la libération est une **révocation, pas une transmission** : re-clétage de la voûte et +rotation des secrets chez l'entrant. Transmettre le mot de passe laisserait à l'ancien +hébergeur un accès à vie aux secrets d'un client parti — rien ne rattrape ça après coup. + ## 2026-08-02 (suite 7) — la frontière appartient à l'hébergeur, pas au tenant actif Un hébergeur sert **plusieurs tenants** et n'a qu'**une** frontière. Or ses intrants diff --git a/docs/carte-set-ops.md b/docs/carte-set-ops.md index 1e23d44..4952c2f 100644 --- a/docs/carte-set-ops.md +++ b/docs/carte-set-ops.md @@ -22,6 +22,7 @@ code + les README de rôles). Cette page comble ces deux trous. | **Réseau / pare-feu** | `docs/flux-conception.md` (le modèle) → `docs/registre-flux.md` (**généré**, matrice d'audit) → `docs/frontiere-opnsense.md` (la bordure nord/sud) ; underlay : `underlay.yml.example` + `make underlay` | | **Ordre de déploiement** | `docs/couches-deploiement.yml` (couches) + `docs/dependances-groupes.yml` (graphe) → `playbooks/site.yml` (**généré**, `make site`) | | **Preuve / recette** | `docs/audit/affirmations.md` (registre), `make prouver` → `docs/audit/preuve-.md`, `docs/audit/plan-de-recette.md` (**généré** du wiki), `docs/audit/protocole-operateur-independant.md` | +| **Migration de tenant** | `docs/migration-tenant.md` — recette en 8 étapes, machine à états, gardes ; le receveur se construit **avant** tout gel | | **Exploitation courante** | `docs/runbooks-exploitation.md`, `docs/intrants-communs.md`, `docs/intrants-base-gui-conception.md`, `docs/theme-forgejo-hors-flotte.md` | | **Pédagogie (le wiki)** | `wiki/` — 21 unités (+ `_Sidebar`) publiées par `make wiki-publier` ; entrer par `wiki/Home.md` | | **Vision / positionnement** | `docs/ecosysteme-chezlepro.md`, `docs/positionnement.md`, `docs/pouvoirs-set-ops.md` | diff --git a/docs/migration-tenant.md b/docs/migration-tenant.md new file mode 100644 index 0000000..d021597 --- /dev/null +++ b/docs/migration-tenant.md @@ -0,0 +1,160 @@ +# Migrer un tenant d'un hébergeur à un autre + +> **Recette d'exploitation.** Elle décrit la séquence, les états et les gardes ; elle ne +> décrit pas encore un outil, parce que la séquence doit être éprouvée avant d'être figée +> dans du code. Complète `docs/multi-instances.md` (la fédération) et +> `docs/frontiere-opnsense.md` §2 (qui possède quoi). + +## 1. Pourquoi ce document existe + +Un **hébergeur** possède le matériel et sert plusieurs **tenants** ; il a en général son +propre tenant par défaut. Un tenant doit pouvoir partir chez un autre hébergeur — sans quoi +la souveraineté qu'on lui promet n'est qu'un mot : **une organisation qui ne peut pas partir +est captive, quelle que soit la licence du logiciel.** + +La migration est donc une fonctionnalité de premier plan, pas un cas limite. + +## 2. Le modèle : le transfert de nom de domaine + +La procédure reprend celle des registraires, qui résout depuis trente ans exactement les +mêmes problèmes. Trois propriétés valent d'être empruntées. + +**Le mandat appartient au client.** Le code d'autorisation est remis au titulaire, qui le +donne au registraire entrant. Transposé : ni l'hébergeur sortant ni l'entrant ne peut +déplacer un tenant de sa propre initiative. Un hébergeur ne « cède » pas un client ; un +autre ne le « réclame » pas. + +**Le verrou par défaut.** Un domaine est verrouillé ; le déverrouiller est un geste +explicite. Un tenant l'est aussi : pas de migration silencieuse. + +**Deux actes délibérés, chez deux parties**, et une trace de qui a libéré et qui a reçu. + +### Là où l'analogie casse, et par quoi on la remplace + +| Le monde des domaines | Set-OPS | Substitution | +|---|---|---| +| un **registre** central arbitre | rien de central, par construction | le mandat est **signé** par le tenant, vérifié contre une clé publique inscrite dans son plan — donc versionnée et publique | +| le domaine ne porte **aucune donnée** | boîtes, dépôts, bases | un **gel** + un instantané restic ; c'est là qu'est le vrai risque | +| fenêtre d'objection | — | fenêtre de **réversibilité** : le sortant ne détruit rien avant confirmation | + +Un secret partagé ne suffirait pas : transmis à l'hébergeur entrant, il ne lui prouve rien — +il pourrait venir du sortant. Une signature, si. C'est plus solide que l'original, et sans +tiers de confiance. + +## 3. La règle qui commande tout l'ordre + +> **Le receveur doit être prouvé prêt avant que quoi que ce soit ne gèle.** + +Construire chez l'entrant pendant que le sortant sert normalement ramène l'interruption au +seul rattrapage final. Geler d'abord et construire ensuite coûterait des heures d'arrêt pour +une opération qui n'en vaut que quelques minutes. + +Cette règle n'est pas un conseil : c'est une **garde de transition** — l'état `gelé` est +inaccessible tant que l'état `préparé` n'est pas prouvé. + +## 4. Les états d'un tenant + +| État | Ce qui est vrai | Comment on en sort | +|---|---|---| +| **verrouillé** | régime normal, aucune migration en cours | un mandat signé apparaît | +| **mandaté** | le tenant a signé ; l'entrant peut commencer | l'entrant a construit | +| **préparé** | l'entrant est déployé, restauré (données tièdes) et **prouvé** — mais **pas public** | *garde : preuves vertes chez l'entrant* | +| **gelé** | le sortant est en lecture seule ; capture du delta | delta restauré chez l'entrant | +| **basculé** | le DNS public pointe l'entrant, qui sert | l'entrant confirme le service | +| **libéré** | le sortant a **révoqué** ses accès ; horloge de rétention lancée | fin de rétention | +| **purgé** | le sortant a détruit sa copie | — | + +La réversibilité vit entre **préparé** et **libéré** : tant que le sortant n'a pas révoqué, +on rentre. Elle vient gratuitement du fait que tout l'adressage dérive d'un seul chiffre. + +## 5. La séquence + +### Étape 0 — le mandat (chez le tenant) + +L'organisation signe une déclaration nommant l'hébergeur entrant. L'entrant la vérifie contre +la clé publique du plan. **Sans mandat vérifié, rien ne commence.** + +### Étape 1 — préparer le terrain (chez le sortant, sans interruption) + +**Abaisser le TTL** des enregistrements publics du tenant. C'est le geste qu'on oublie +systématiquement, et il conditionne tout : posé à l'étape 6, la bascule traînera des heures +quoi qu'on fasse ; posé maintenant, elle prendra le temps du nouveau TTL. + +Vérifier que les sauvegardes du tenant sont saines et récentes — la migration s'appuie +entièrement dessus (`serveur_backup` / `client_backup`). + +### Étape 2 — construire chez l'entrant (sans interruption) + +1. **Choisir un index libre** dans la fédération de l'entrant (`make instances` le montre ; + la preuve **P21** refuse une collision). S'il diffère de l'ancien, *tout* l'adressage + change — IP, VLAN, VMID — et c'est **une seule ligne à changer**, puis `make instancier`. +2. **Vérifier que la fabric de l'entrant porte le tenant** : plage VLAN disponible, lien de + transit présent, zones du plan couvertes (`make underlay`, preuve **P23**). +3. **Créer et déployer** la flotte du tenant chez l'entrant. +4. **Restaurer** depuis un instantané restic *tiède* — les données auront quelques jours ; + c'est voulu, le rattrapage viendra à l'étape 4. + +Pendant tout ce temps, le sortant sert normalement. Personne ne voit rien. + +### Étape 3 — prouver l'entrant, sans le rendre public + +Faire passer la batterie de preuves et la recette **contre les adresses de l'entrant**, pas +par le DNS public. + +C'est le point délicat de toute la procédure : le tenant tourne alors **aux deux endroits**, +mais un seul doit servir. Si l'entrant répondait publiquement, le courrier arriverait des deux +côtés et l'identité se dédoublerait. Il faut donc un chemin de vérification qui ne dépende pas +du DNS public — résolution locale, `/etc/hosts` du poste de vérification, ou vues DNS +séparées. **Ce chemin doit exister avant l'étape 4.** + +### Étape 4 — geler (chez le sortant) + +*Garde : l'étape 3 est verte.* + +Le tenant passe en lecture seule. L'interruption commence **ici**, et seulement ici. + +### Étape 5 — rattraper le delta + +Instantané restic incrémental, restauré chez l'entrant. Court, parce que le gros est déjà +passé à l'étape 2. + +### Étape 6 — basculer + +Le DNS public pointe l'adresse publique de l'entrant. Le TTL abaissé à l'étape 1 fait son +office. L'interruption se termine à la propagation. + +### Étape 7 — confirmer (chez l'entrant) + +Le service répond publiquement, les preuves passent, le tenant valide. **C'est ce qui autorise +l'étape 8, rien d'autre.** + +### Étape 8 — libérer (chez le sortant) + +**Révoquer, pas transmettre.** La voûte voyage avec le dépôt, chiffrée, mais son mot de passe +est celui de l'opérateur sortant. Migrer proprement ne consiste pas à le communiquer : c'est +**re-cléer chez l'entrant** — nouveau mot de passe de voûte, rotation des secrets applicatifs, +révocation des certificats et des clés SSH émis par le sortant. + +C'est l'équivalent du re-verrouillage chez le nouveau registraire. Sans lui, l'ancien +hébergeur garde **à vie** l'accès aux secrets d'un client parti, et aucune procédure ne +rattrape ça après coup. + +Puis rétention — durée convenue — avant purge. + +## 6. Ce que la migration ne déplace pas + +- **La fabric** : l'underlay appartient à l'hébergeur. Le tenant en change, il n'en emporte + pas. +- **La frontière** : l'entrant a la sienne. Ses règles pour le tenant se **régénèrent** — + `make devis-opnsense` est déjà multi-tenant. +- **L'index** : c'est une coordonnée locale à une fédération, pas une propriété du tenant. + +## 7. Ce qui reste à trancher + +- **Le format du mandat** et l'emplacement de la clé publique du tenant dans son plan. +- **Le chemin de vérification hors DNS public** (étape 3) — le plus structurant. +- **La durée de rétention** avant purge, et qui l'atteste. +- **L'outillage** : un `devis-migration` dériverait l'index retenu, l'adressage résultant et + la compatibilité de fabric, et refuserait sur un point bloquant — dans l'idiome du dépôt : + dériver un plan, le prouver, laisser l'humain l'appliquer. À écrire **après** avoir joué la + séquence au moins une fois.