From 4da68c7ecd75bf9b7678b80f0248ccd1cf7ae55f Mon Sep 17 00:00:00 2001 From: Daniel Allaire Date: Tue, 8 Sep 2026 12:47:26 -0400 Subject: [PATCH] Publication du wiki depuis le depot (source: c9d31e9) --- Autorisation-et-RBAC.md | 15 +- Bases-de-données.md | 5 +- Courriel.md | 9 +- DNS-et-résolution.md | 40 +-- Filiation-signatures-et-témoins.md | 139 +++++++++++ Glossaire.md | 382 ++++++++++++++++++++++++++--- Home.md | 2 +- Identité-et-SSO.md | 18 +- La-preuve.md | 47 +++- Le-plan-et-l-adressage-dérivé.md | 4 +- Le-réseau-des-tenants.md | 165 +++++++++++++ Liaisons-bindings.md | 20 +- Multi-instance-et-fédération.md | 47 ++-- Métriques-et-journaux.md | 20 +- PKI-et-confiance.md | 20 +- Reprendre-l-écosystème.md | 23 +- Reverse-proxy-et-TLS.md | 10 +- Sauvegardes.md | 28 ++- Supervision-et-impact.md | 2 +- Sécurité-et-durcissement.md | 22 +- Virtualisation-et-clonage.md | 7 +- Vérifier-le-déployé.md | 19 +- _Sidebar.md | 2 + 23 files changed, 923 insertions(+), 123 deletions(-) create mode 100644 Filiation-signatures-et-témoins.md create mode 100644 Le-réseau-des-tenants.md diff --git a/Autorisation-et-RBAC.md b/Autorisation-et-RBAC.md index c628008..432a4ae 100644 --- a/Autorisation-et-RBAC.md +++ b/Autorisation-et-RBAC.md @@ -41,9 +41,18 @@ Déclaratif dans Set-OPS : `serveur_keycloak_realm_roles`, `_role_mapper_clients **zéro coupure SSO**. Une identité (`testmail`), et c'est **le rôle** — pas la connexion — qui décide d'Explore. -> **Raffinement souverain** : ici le rôle est assigné *explicitement* à testmail. L'idéal est de le -> piloter par un **groupe d'annuaire** (LDAP → Keycloak → claim), pour que *l'appartenance* gouverne -> l'autorisation. C'est le vrai « l'annuaire gouverne l'accès ». +> **Ce « raffinement » est construit depuis le 2026-08-08.** Cette unité le présentait comme +> un idéal à atteindre ; c'est le mécanisme en vigueur. Les **groupes LDAP** sont projetés +> dans Keycloak et émis en claim (`roles/serveur_keycloak/tasks/groupes-ldap.yml` et +> `claim-groupes.yml`), et chaque service déclare le groupe qu'il reconnaît dans son +> `meta/acces.yml` — `sysadmin ⇒ Admin`, `personnel ⇒ Viewer` chez Grafana. C'est donc bien +> **l'appartenance** qui gouverne, pas une assignation nominative. +> +> La règle qui va avec : **un service nomme un groupe, jamais une personne**. Nommer +> quelqu'un créerait une dette qu'on découvre le jour du départ, service par service — et il +> faudrait un déploiement pour révoquer. L'assignation explicite à `testmail` de l'exemple +> ci-dessus reste utile pour *comprendre* la mécanique du claim ; ce n'est pas la façon dont +> on donne un accès. Voir `docs/autorisation.md` §5. --- diff --git a/Bases-de-données.md b/Bases-de-données.md index be9d10c..d4ab762 100644 --- a/Bases-de-données.md +++ b/Bases-de-données.md @@ -32,7 +32,10 @@ Registre ──> PostgreSQL crée la base + le compte propriétaire Le **secret** vit dans la **voûte** (Ansible Vault), déréférencé **au déploiement**, jamais en clair dans le plan. C'est une **liaison** *app → base*, de modalité **requise** (Keycloak sans sa base ne -démarre pas). Consommateurs prouvés : Keycloak, Forgejo, IcingaDB. +démarre pas). Le registre porte aujourd'hui **quatre bases** — `keycloak`, `forgejo`, +`icingadb`, `nextcloud` — et **cinq rôles** incluent `resoudre_base` pour bâtir leur +connexion : `serveur_keycloak`, `serveur_forgejo`, `serveur_icinga`, `serveur_icingaweb2`, +`serveur_nextcloud`. --- diff --git a/Courriel.md b/Courriel.md index 6e3da48..e09efb2 100644 --- a/Courriel.md +++ b/Courriel.md @@ -54,10 +54,13 @@ Tu as appris **MTA/MDA/MUA, SMTP vs soumission, IMAP, la déliverabilité** — 1. **Envoie via la soumission `:587`** (client authentifié) : ```bash - swaks --server edge-mta-01.lab.chezlepro.internal:587 --tls \ - --auth LOGIN --auth-user testmail@lab.chezlepro.internal --auth-password MotDePasseTest123 \ - --from testmail@lab.chezlepro.internal --to testmail@lab.chezlepro.internal --header 'Subject: essai' + swaks --server edge-mta-01.chezlepro.internal:587 --tls \ + --auth LOGIN --auth-user testmail@chezlepro.internal --auth-password "$MDP_ESSAI" \ + --from testmail@chezlepro.internal --to testmail@chezlepro.internal --header 'Subject: essai' ``` + *(`MDP_ESSAI` se saisit à la main — `read -rs MDP_ESSAI`. Un mot de passe écrit ici + partirait dans l'historique du shell et dans le dépôt public : ce document en portait un + en clair jusqu'au 2026-09-06.)* `235 Authentication successful` puis `250 queued` = ① en action. 2. **Lis la boîte** (le MDA) : `doveadm search -u testmail mailbox INBOX all | wc -l` sur infra-mail-01. 3. **Casse & répare.** Coupe l'annuaire (arrête OpenLDAP), renvoie un courriel : Postfix **rejette le diff --git a/DNS-et-résolution.md b/DNS-et-résolution.md index 33f6577..273f32f 100644 --- a/DNS-et-résolution.md +++ b/DNS-et-résolution.md @@ -7,12 +7,12 @@ ## ① Le concept *(générique)* Les machines se parlent par **adresses IP** ; les humains (et les configs) utilisent des **noms**. -La **résolution de noms** fait le pont : `keycloak.lab…` → `192.168.15.21`. +La **résolution de noms** fait le pont : `keycloak.chezlepro.internal` → `10.17.17.11`. Plusieurs couches, de la plus locale à la plus globale : - **Fichier hosts** (`/etc/hosts`) : une table statique, locale, **consultée en premier**, **sans aucun réseau**. Increvable, mais manuelle. -- **DNS autoritatif** : le serveur qui **détient la vérité** d'une zone (ex. `lab.chezlepro.internal`) +- **DNS autoritatif** : le serveur qui **détient la vérité** d'une zone (ex. `chezlepro.internal`) et répond pour ses noms (enregistrements **A**, **SOA**…). - **DNS récursif (résolveur)** : celui que tes machines interrogent ; il **cherche pour toi** (cache local, puis interne, puis Internet). @@ -28,14 +28,21 @@ chercher* la réponse pour toi »). |---|---|---| | **1. Le plancher** | `hosts_statiques` (socle) → `/etc/hosts` sur **chaque** nœud | résout **même DNS éteint**, dès le bootstrap. Le filet en dessous de tout. | | **2. Autoritatif** | `serveur_powerdns` (PowerDNS) | la zone interne, les enregistrements **A**. | -| **3. Récursif local** | `client_unbound` (**opt-in**) | résolveur local : stub-zone → PowerDNS pour l'interne, récursion pour le reste. | +| **3. Récursif** | `serveur_resolveur` — **un** Unbound pour tout le tenant | récurse depuis la racine, délègue la zone souveraine à PowerDNS. | +| *(l'intégration)* | `client_resolveur` — **universelle**, sur chaque nœud | n'installe rien : écrit `/etc/resolv.conf` pour désigner le résolveur ci-dessus. | + +> **Le récursif n'est plus « local », et il n'est plus optionnel.** Avant le 2026-08-24, +> `client_resolveur` posait un Unbound sur *chaque* VM — N démons identiques pour un service +> unique. Il n'installe plus rien, et son intégration est **universelle, sans aucune +> exemption** : pas même l'hôte qui porte le résolveur, qui se sert lui-même. Le **plancher** est le cœur pédagogique : parce que chaque nœud connaît *tout l'écosystème* par `/etc/hosts`, **rien ne dépend du DNS pour démarrer** — PowerDNS devient une *commodité*, pas un point de défaillance unique. On construit la robustesse **de bas en haut**. -C'est aussi ce que **tu** utilises depuis ta machine : mettre `192.168.15.21 keycloak.lab…` dans -ton `/etc/hosts`, c'est exactement le même « plancher ». +C'est aussi ce que **tu** utilises depuis ta machine : mettre +`10.17.16.11 keycloak.chezlepro.internal` dans ton `/etc/hosts`, c'est exactement le même +« plancher ». --- @@ -56,23 +63,28 @@ Tu as appris **la hiérarchie de résolution** (hosts → récursif → autorita 1. **Le plancher, sans DNS.** Sur un nœud : ```bash - getent hosts keycloak.lab.chezlepro.internal # répond via /etc/hosts, zéro DNS + getent hosts keycloak.chezlepro.internal # répond via /etc/hosts, zéro DNS grep chezlepro /etc/hosts | head ``` 2. **Interroge l'autoritatif.** Demande à PowerDNS directement : ```bash - dig @infra-dns-01.lab.chezlepro.internal keycloak.lab.chezlepro.internal A +short - dig @infra-dns-01.lab.chezlepro.internal lab.chezlepro.internal SOA +short + dig @infra-dns-01.chezlepro.internal keycloak.chezlepro.internal A +short + dig @infra-dns-01.chezlepro.internal chezlepro.internal SOA +short ``` 3. **Vois les couches.** Compare `getent hosts` (plancher) et `dig` (DNS) : **deux chemins**, même IP. -4. **Casse & répare.** Sur un nœud **sans** `client_unbound`, vide `/etc/hosts` de ses entrées - `chezlepro` (garde une sauvegarde !) et coupe l'accès au DNS : la résolution interne **échoue**. - Restaure `/etc/hosts` : ça remarche **sans DNS**. Tu viens de *sentir* pourquoi le plancher est le - filet de sécurité. +4. **Casse & répare.** Vide `/etc/hosts` de ses entrées `chezlepro` (garde une sauvegarde !) + **et** pointe `/etc/resolv.conf` ailleurs : la résolution interne **échoue**. Restaure + `/etc/hosts` seul : ça remarche **sans DNS**. Tu viens de *sentir* pourquoi le plancher + est le filet de sécurité. +5. **Le piège du récursif.** Demande un nom qui n'existe pas sous `internal.`, puis + redemande un nom qui existe. Si le résolveur répond NXDOMAIN aux deux, tu viens de + reproduire la panne de deux jours du 2026-09-02 : la racine étant signée, elle *prouve* + que `internal.` n'existe pas, et Unbound étend ce « non » à tout ce qui est dessous — + sans jamais interroger la stub-zone. Remède : `harden-below-nxdomain: no`. --- ## Pour aller plus loin *(dépôt)* -- Rôles : `roles/hosts_statiques` (le plancher), `roles/serveur_powerdns`, `roles/client_unbound`. +- Rôles : `roles/hosts_statiques` (le plancher), `roles/serveur_powerdns`, `roles/client_resolveur`. - Conception des 3 couches + frontière publique : `docs/dns-interne.md`. -- Note : `client_unbound` est une **liaison optionnelle** (opt-in) — voir l'unité *Liaisons*. +- Note : `client_resolveur` est une **intégration universelle** (elle suit l'existence de `serveur_resolveur`) — voir l'unité *Liaisons* et `docs/integrations-vm.md`. diff --git a/Filiation-signatures-et-témoins.md b/Filiation-signatures-et-témoins.md new file mode 100644 index 0000000..f2c9619 --- /dev/null +++ b/Filiation-signatures-et-témoins.md @@ -0,0 +1,139 @@ +# Filiation, signatures et témoins + +> **Unité d'apprentissage.** Moule : ① concept → ② Set-OPS → ③ transférable → ④ à toi de jouer. + +Un écosystème Set-OPS peut en **engendrer** un autre. Dès qu'il y a une descendance, trois +questions apparaissent, et aucune n'est technique au départ : + +- **de qui** cet écosystème descend-il ? +- **qui** a le droit de modifier ce dont il descend ? +- comment le savoir **sans faire confiance** à celui qui héberge le code ? + +--- + +## ① Le concept + +### Git est déjà une chaîne de hachage + +C'est la chose la plus utile à comprendre, et la moins connue. Chaque commit contient +l'**empreinte** de son parent. Changer une ligne d'il y a trois mois change l'empreinte de +ce commit — donc celle du suivant, et de tous les autres jusqu'à aujourd'hui. + +``` +A ←── B ←── C ←── D modifier B casse C et D +``` + +C'est un **arbre de Merkle** : la même structure de données qu'une blockchain. L'intégrité +est déjà là, gratuitement, depuis 2005. + +### Ce que la chaîne ne dit pas + +**Qui a écrit.** `user.name` est purement déclaratif : n'importe qui peut signer « Daniel +Allaire » et pousser. Rien ne lie ce nom à une personne. + +C'est ce que corrigent les **signatures**. Avec une clé — la même clé SSH que pour se +connecter — on scelle une étiquette ou un commit. Qui possède la **clé publique** peut +vérifier ; qui ne possède pas la **clé privée** ne peut pas contrefaire. + +Reste alors une question : *quelles clés ont le droit de signer ?* La réponse doit vivre +**avec le code**, pas chez l'hébergeur — sinon on remplace la confiance en une personne par +la confiance en une plateforme. + +### Les témoins, ou pourquoi la fractale est le registre + +Une signature prouve l'auteur, pas que l'histoire n'a pas été **remplacée**. Celui qui tient +la forge peut réécrire et re-signer. + +Contre ça, un seul remède : la **multiplicité**. Si chaque écosystème enfant porte une copie +du code dont il descend, réécrire l'histoire suppose de convaincre **tous les descendants**. +C'est exactement ce qu'une blockchain achète au prix d'une machinerie considérable — et +qu'une lignée produit comme **effet secondaire de sa forme**. + +> **Pourquoi pas une blockchain, alors ?** Elle résout un problème précis : *qui écrit +> ensuite, quand personne ne fait confiance à personne et qu'il y a de l'argent en jeu.* +> Ici : les membres sont connus, il n'y a pas d'argent dans la boucle, et une ancre de +> confiance existe déjà. Une chaîne publique ajouterait une dépendance à un réseau +> extérieur ; une chaîne privée, une base de données distribuée exigeant plusieurs +> opérateurs — l'inverse de « un humain doit pouvoir la faire tourner ». + +--- + +## ② Dans Set-OPS + +### Les quatre dépôts du génome + +Un écosystème ne se reproduit pas depuis ses machines. Il se reproduit depuis **quatre +dépôts** — et `make genome` les nomme : + +| Rôle | Ce qu'il porte | +|---|---| +| **moteur** | Set-OPS : les rôles, les preuves, le Makefile | +| **instance** | le plan du tenant, sa voûte chiffrée | +| **hébergeur** | l'`underlay.yml` du site : sa fabric physique | +| **modèles** | les offres dont les instances sont tirées | + +Perdre les VM coûte du temps. Perdre ces quatre-là coûte l'écosystème. + +### Le registre des signataires + +`.git-allowed-signers`, **versionné dans le dépôt** : + +``` +allaire.dan@chezlepro.ca ssh-ed25519 AAAAC3NzaC1lZDI1... +``` + +Qui clone peut vérifier sans rien demander à la forge. Retirer une ligne **révoque** le +signataire pour la suite ; ce qui est déjà signé reste vérifiable — on ne réécrit pas le +passé, on cesse d'accepter l'avenir. + +### La parenté, inscrite + +`make genome-inscrire` écrit `parente.yml` dans l'instance : de quel moteur elle descend, +à quel **commit**, sous quelle **étiquette signée**. + +```yaml +moteur: + remote: ssh://…/Set-OPS-Public.git + commit: 742bcbf4b0e1322bfa5e0dee14c94bcad6898c53 + etiquette: v2026.08.21 +``` + +Sans ce fichier, un écosystème fabriqué aujourd'hui ne sait plus, dans un an, d'où il +vient. Cinq enfants sans registre font cinq moteurs différents que plus personne ne sait +rapprocher. **C'est la mutation sans mémoire, et c'est le seul vrai risque d'une lignée.** + +La preuve **P40** vérifie que la parenté est inscrite, que chaque dépôt nommé se retrouve, +que chaque commit inscrit **existe encore** — une histoire réécrite se voit là — et que +chacun porte un *remote* : un dépôt qui n'existe qu'ici n'est pas une lignée, c'est un +point unique de défaillance. + +--- + +## ③ Transférable + +Rien ici n'appartient à Set-OPS : + +- **signature par clé SSH** : git 2.34+, partout, sans infrastructure ; +- **`allowed_signers`** : le mécanisme natif d'OpenSSH ; +- **journal de transparence** (Certificate Transparency, Sigstore) : la réponse mûre quand + plusieurs organisations doivent constater les mêmes faits sans se faire confiance ; +- **horodatage RFC 3161** : prouver qu'un document existait à une date. + +Le jour où l'Alliance certifiera des écosystèmes, c'est un **journal de transparence** +qu'il faudra — un registre append-only, auditable par un tiers — et non une blockchain. + +--- + +## ④ À toi de jouer + +1. `make genome` — quatre dépôts ? Lequel n'a pas de *remote* ? Celui-là n'existe qu'ici. +2. `git verify-tag v2026.08.21` — que se passe-t-il si tu retires ta ligne de + `.git-allowed-signers` ? (remets-la ensuite) +3. `make genome-inscrire`, puis ouvre `parente.yml`. Dans un an, qu'est-ce que ce fichier + te dira que ta mémoire ne dira plus ? +4. Demande-toi où sont les **témoins** aujourd'hui. Combien de copies vivantes du moteur + existent, sur combien de machines distinctes ? C'est la vraie mesure de la résistance de + la lignée — pas la longueur des clés. + +**Voir aussi** : [Multi-instance & fédération](Multi-instance-et-fédération), +[La preuve](La-preuve), [Sauvegardes](Sauvegardes). diff --git a/Glossaire.md b/Glossaire.md index 15d1fda..a307f2d 100644 --- a/Glossaire.md +++ b/Glossaire.md @@ -1,64 +1,366 @@ # Glossaire -Les concepts-clés de Set-OPS, en une phrase chacun. Les mots *en italique* renvoient à une autre -entrée. Le détail vit dans les **unités d'apprentissage** et le dépôt (`docs/`). +Tout mot que Set-OPS te met sous les yeux se trouve ici. Chaque entrée dit **ce que c'est**, +et pourquoi ce dépôt s'en sert — pas seulement sa définition. + +Les mots *en italique* renvoient à une autre entrée ; les liens mènent à l'**unité +d'apprentissage** qui développe la notion. + +> **Règle de ce glossaire.** Un terme employé par le dépôt et absent d'ici est un défaut. +> La preuve **P39** le vérifie. Ce qu'elle ne peut pas voir : un mot que personne n'a +> pensé à déclarer — elle garde une liste, elle ne devine pas. --- -**Adressage dérivé** — Les IP, VLAN, VMID, sous-réseaux ne sont pas saisis : ils se **calculent** -depuis le *seed*. Cf. [Le plan & l'adressage dérivé](Le-plan-et-l-adressage-dérivé). +## Le plan, et ce qui en dérive -**Binding (liaison)** — Relation déclarative entre entités du plan (app→app, app→base) résolue par -le moteur, sans coder de variables à la main. Cf. [Liaisons (bindings)](Liaisons-bindings). +**Plan** — La source unique de vérité (`instance/plan/` : serveurs, applications, bases, +domaines, nomenclature). On l'édite ; l'*inventaire* en est **généré**, jamais l'inverse. -**DIFF VIDE** — État sain où le *plan* reproduit **exactement** l'inventaire généré : la source de -vérité et l'artefact concordent. Vérifié par `make instancier`. +**Index (seed)** — Le **seul** intrant d'adressage d'une *instance*. Tout en descend : +supernet `10..0.0/16`, *VLAN* `1000+index×10+zone`, *VMID*, *VNet*. Unique dans la +*fédération*. Cf. [Le plan & l'adressage dérivé](Le-plan-et-l-adressage-dérivé). -**Fédération** — Ensemble des *instances* qui cohabitent sur une infrastructure partagée, chacune -avec son *index* unique. Cf. [Multi-instance & fédération](Multi-instance-et-fédération). +**Adressage dérivé** — Les adresses ne sont pas saisies, elles se **calculent**. Deux +valeurs ne peuvent donc pas se contredire : il n'y en a qu'une, et le reste en découle. -**`federe: false`** — Drapeau marquant un *bac à sable* local, **exclu** du réseau convergé (il ne -provisionne pas ses VLAN sur les switches de production). +**Nomenclature** — Le registre du modèle réseau : *zones*, placement des fonctions, et +l'*index*. Ne contient **aucune** adresse (preuve P20). -**Hôte fantôme** — Erreur de plan : une application posée sur un hôte **non déclaré** dans les -serveurs. Refusé par la preuve **P06** et par la création de modèle. +**Zone** — Un domaine de sécurité : un `/24` et un *VLAN*. Frontière, Identité, Données, +Services-infra, Observabilité, Applications. Un pare-feu les sépare. -**Idempotence** — Rejouer la même description N fois donne le même résultat (`changed=0` après la -1ʳᵉ fois). Cf. [Infra as Code & idempotence](Infra-as-Code-et-idempotence). +**Supernet** — Le grand bloc d'adresses d'un tenant (`10.29.0.0/16`), dont ses *zones* +découpent des tranches. -**Index (seed)** — Le **seul** intrant d'adressage d'une instance. Détermine supernet -(`10.(10+index)`), VLAN (`1000+index×10+zone`), VMID. Unique par instance fédérée. +**CIDR** — La notation `10.29.16.0/24` : une adresse, puis le nombre de bits **figés**. +`/24` fige les trois premiers nombres — 254 machines possibles. -**Instance** — Un écosystème réel (un *tenant*) : `../OPS-`, avec ses valeurs concrètes -(domaine, *voûte*, index). L'active est celle que pointe le symlink `instance/`. +**DIFF VIDE** — État sain : le *plan* reproduit **exactement** l'*inventaire* appliqué. +Vérifié par `make instancier`. -**ip-miroir** — Schéma de VMID à 9 chiffres où le numéro **contient** l'IP et le tenant -(`VLAN·octet-hôte·séquence`) — lisible d'un coup d'œil. +**Instance** — Un écosystème réel (`../OPS-`) : son domaine, son *index*, sa *voûte*. +L'active est celle que pointe le lien `instance/`. -**Modèle** — Un *plan* **générique** réutilisable (valeurs *placeholder*, sans *voûte*), dans le -dépôt privé `Set-OPS-Modeles`. Le socle public en est la preuve libre. On en crée une instance -(`instance-creer`) ; on en fabrique un (`model-creer`). +**Tenant** — Le même objet, vu depuis la *fédération* : un écosystème isolé parmi d'autres. -**Nomenclature** — Le registre du **modèle réseau** : zones, placement des fonctions, et le *seed* -`index`. Ne contient **aucun** adressage (il en dérive — preuve P20). +**Fédération** — L'ensemble des *instances* qui cohabitent sur une infrastructure +partagée, chacune avec son *index*. Cf. [Multi-instance & fédération](Multi-instance-et-fédération). -**Le plancher** — La première couche de résolution de noms : `/etc/hosts` posé par le socle, avant -même que le DNS soit debout. Cf. [DNS & résolution](DNS-et-résolution). +**`federe: false`** — Drapeau qui **exclut** une instance des devis du site : ni *VLAN*, ni +règles de frontière, ni *VNet* ne lui sont réservés. Pour un bac à sable local, ou pour un +écosystème encore à l'état de plan. -**Plan** — La source unique de vérité (`instance/plan/` : serveurs, applications, bases, domaines, -nomenclature). On l'édite ; l'inventaire en est **généré**, jamais l'inverse. +**Modèle** — Un *plan* générique réutilisable, sans *voûte*. Le dépôt public en fournit +**un** (`exemples/modeles/socle`) ; les modèles assemblés par offre — `forge`, `identite`, +`collaboration`… — vivent dans le dépôt privé `Set-OPS-modeles`. -**Preuve (Pxx)** — Une vérification rejouable du harnais `make prouver` qui garde une classe +**Socle** — Le modèle minimal public, **quatre VM** : DNS interne (PowerDNS), PKI +(step-ca), edge TLS (nginx) et **magasin courriel** (Dovecot). C'est un magasin de boîtes, +pas un relais : le MTA s'ajoute ensuite. + +**Liaison (binding)** — Relation déclarée entre entités du plan (app→base, app→app), +résolue par le moteur. Cf. [Liaisons (bindings)](Liaisons-bindings). + +**Hôte fantôme** — Erreur de plan : une application posée sur un hôte non déclaré. Refusée +par la preuve P06. + +--- + +## Les machines + +**VM** — Machine virtuelle : un ordinateur complet simulé par un *hyperviseur*. + +**Hyperviseur** — La machine physique qui fait tourner les *VM*. Ici : Proxmox. + +**Gabarit (template)** — Une VM **figée** qui sert de moule. Chaque machine de la flotte +en est un clone. Le gabarit doré de Set-OPS s'appelle `modeleSetOPS`. + +**Clone** — La copie du *gabarit* qui devient une machine réelle. Deux minutes, contre une +installation complète. + +**VMID** — Le numéro d'une VM sur le cluster. Set-OPS le **dérive** (schéma *ip-miroir*) au +lieu de le tirer au hasard. + +**ip-miroir** — Schéma où le *VMID* **contient** l'adresse IP et le tenant : le numéro se +lit d'un coup d'œil. + +**cloud-init** — Le mécanisme qui configure une VM **à sa toute première seconde** : +adresse IP, nom, clé SSH. Sans lui, un clone naîtrait identique à son moule, jumeau de +tous les autres. + +**Snapshot** — Photo d'une VM à un instant. Utile pour revenir en arrière ; ce n'est +**pas** une sauvegarde (elle vit sur le même stockage). + +**systemd** — Le chef d'orchestre des services d'une machine Linux : il démarre, arrête et +surveille. « Un service » veut dire « une unité systemd ». + +--- + +## Ansible : décrire au lieu d'exécuter + +**Ansible** — L'outil qui applique une **description** à des machines, par SSH. Cf. +[Infra as Code & idempotence](Infra-as-Code-et-idempotence). + +**Playbook** — Un fichier qui dit **quoi appliquer, à qui**. Ici : un par groupe +(`playbooks/groupes/.yml`). + +**Rôle** — Le paquet qui sait installer et configurer **une** chose (`serveur_nginx`, +`client_backup`). C'est l'unité réutilisable. + +**Inventaire** — La liste des machines et de leurs groupes (`hosts.yml`). **Généré** depuis +le plan : on ne l'édite jamais à la main. + +**group_vars** — Les valeurs d'un groupe de machines, rangées par fichier. C'est là que +vivent les *intrants* non secrets. + +**Handler** — Une action qui ne se déclenche **que si** quelque chose a changé — typiquement +recharger un service quand sa configuration a bougé. + +**Idempotence** — Rejouer la même description dix fois donne le même résultat. La deuxième +exécution ne change rien (`changed=0`). C'est ce qui rend une reconstruction sûre. + +**Voûte (vault)** — Le fichier **chiffré** des secrets d'une instance. Jamais dans un +modèle, jamais en clair. Seul le gabarit `vault.yml.example` — des **noms**, pas des +valeurs — est versionné. + +--- + +## Le réseau + +→ Unité complète : [Le réseau des tenants](Le-réseau-des-tenants) + +**Underlay** — Le réseau **physique** : câbles, commutateurs, adresses des hyperviseurs. Il +ne connaît aucun tenant. + +**Overlay** — Les réseaux **virtuels** des tenants, transportés par-dessus l'*underlay*. + +**VLAN** — Une étiquette posée sur chaque trame pour séparer des réseaux qui partagent un +câble. Limite : 4094 étiquettes, et chaque commutateur doit les connaître. + +**VXLAN** — L'encapsulation : la trame d'un tenant voyage **dans une enveloppe**. Le réseau +physique ne voit que des colis. L'enveloppe coûte **50 octets** — d'où le *MTU* 1450. + +**EVPN** — Le mécanisme par lequel les hyperviseurs s'annoncent les machines qu'ils +hébergent. Sans lui, la table des destinations serait tenue à la main. + +**SDN** — *Software-Defined Networking* : le réseau se **décrit** et se pose par API, au +lieu de se câbler à la main. + +**VNet** — Le réseau virtuel d'**une zone** d'un tenant (`t29fron`) : le point où la carte +d'une VM se branche. + +**VRF** — Une table de routage **étanche**. Dans celle du tenant A, les réseaux du tenant B +n'existent pas. Ce n'est pas une interdiction, c'est une ignorance. + +**Strophe (FRR)** — Le bloc de configuration qu'on pose par tenant dans +`/etc/frr/frr.conf.local`, sur chaque hyperviseur : sa **sortie** et son **puits**. + +**FRR** — Le démon de routage des hyperviseurs (*FRRouting*). + +**`nexthop-vrf`** — Emprunter **une seule** adresse à une autre table de routage, au lieu +d'importer celle-ci en entier. La différence entre une porte et un mur abattu. + +**Blackhole (puits)** — Une route qui **absorbe** le trafic vers les adresses non +attribuées d'un tenant, pour qu'il ne parte pas errer ailleurs. + +**MTU** — La plus grosse trame qu'un lien accepte. 1500 par défaut ; 1450 dans l'*overlay* +(l'enveloppe VXLAN prend 50). Se tromper suspend les grosses réponses sans casser les +petites — la panne la plus déroutante du domaine. + +**SVI** — L'adresse de passerelle portée par un **commutateur** pour un VLAN. Set-OPS ne +s'en sert plus : le routage des tenants vit sur l'hyperviseur (*VRF*). + +**Pont (bridge)** — Le commutateur **virtuel** d'un hyperviseur (`vmbr1`). Les VM de la +flotte se branchent sur leur *VNet*, pas sur un pont nu. + +**IPAM** — Le registre qui attribue les adresses. Ici, c'est la **dérivation** : personne +ne tient de liste. + +**Spanning-tree (STP)** — Le protocole qui empêche une boucle de commutateurs de saturer le +réseau. Il élit une racine ; c'est ce que désigne `routeur` dans l'*underlay*. + +**MLAG** — Deux commutateurs qui se font passer pour un seul. Set-OPS n'en a pas — d'où +les chemins doublés ailleurs (MPIO côté stockage, `active-backup` côté flotte). *(Cette +entrée concluait « d'où un seul commutateur qui route » : plus aucun ne route. Le routage +des tenants vit sur l'hyperviseur, cf. l'entrée* SVI *ci-dessus.)* + +**nftables** — Le pare-feu **de chaque machine** Linux. Set-OPS le dérive du registre des +flux. + +**Policy drop** — Politique par défaut : *tout ce qui n'est pas autorisé est refusé*. Sans +règles chargées, elle mure la machine — d'où l'importance de `make flux`. + +**Frontière** — Le pare-feu de bordure (OPNsense), entre l'écosystème et l'extérieur. Hors +flotte Ansible : piloté par API. + +**FQDN** — Le nom complet d'une machine, `forge.genese.internal` — pas seulement `forge`. + +--- + +## Les noms + +→ Unité complète : [DNS & résolution](DNS-et-résolution) + +**DNS** — L'annuaire qui traduit un nom en adresse. + +**Le plancher** — La première couche de résolution : `/etc/hosts`, posé par le socle +**avant** que le DNS existe. Sans lui, rien ne peut s'amorcer. + +**Résolveur** — Le service qui pose les questions au DNS pour une machine (ici Unbound, +local à chaque hôte). + +**Reverse proxy** — Le portier : il reçoit toutes les requêtes web et les distribue au bon +service derrière. Cf. [Reverse-proxy & TLS](Reverse-proxy-et-TLS). + +**vhost** — La configuration d'**un site** dans le *reverse proxy* : quel nom, vers quel +service. + +**WAF** — Filtre qui inspecte les requêtes web et bloque les attaques connues. + +**Edge** — La machine de bordure qui publie les services web (ici nginx). + +--- + +## La confiance + +→ Unité complète : [PKI & confiance](PKI-et-confiance) + +**PKI** — L'ensemble qui fabrique et gère les certificats. + +**CA (autorité de certification)** — Celle qui **signe** les certificats. Ici, `step-ca`, +interne : l'écosystème est sa propre autorité. + +**Certificat** — Une pièce d'identité pour une machine : ce nom, cette clé, signé par la +*CA*. + +**ACME** — Le protocole qui **automatise** la demande et le renouvellement d'un certificat. +Personne ne le fait à la main. + +**SAN** — Les noms qu'un certificat couvre. Set-OPS les **dérive** du plan. + +**TLS** — Le chiffrement d'une connexion (le « s » de https). + +**mTLS** — TLS **des deux côtés** : le client prouve aussi son identité. C'est le +zéro-confiance entre serveurs. + +--- + +## L'identité + +→ Unité complète : [Identité & SSO](Identité-et-SSO) + +**LDAP** — L'annuaire des personnes et des groupes : la source de vérité des identités. + +**LDAPS** — LDAP chiffré. + +**SSO** — *Single Sign-On* : une seule authentification pour toutes les applications. + +**OIDC** — Le protocole moderne du *SSO* sur le web. C'est lui derrière « Se connecter +avec… ». + +**SAML** — L'ancêtre d'*OIDC*, encore répandu en entreprise. + +**Realm** — Le « royaume » d'un serveur d'identité : un espace de comptes, de groupes et de +règles. Un par écosystème. + +**RBAC** — Donner des droits à des **rôles**, pas à des personnes. Cf. +[Autorisation & RBAC](Autorisation-et-RBAC). + +--- + +## Le courriel + +→ Unité complète : [Courriel (SMTP/IMAP)](Courriel) + +**SMTP** — Le protocole qui **transporte** un courriel d'un serveur à l'autre. + +**MTA** — Le serveur qui fait ce transport (ici Postfix). + +**IMAP** — Le protocole par lequel **tu lis** ta boîte, depuis ton téléphone ou ton client. + +**LMTP** — Le dernier mètre : le MTA remet le message au serveur qui **détient** les boîtes +(ici Dovecot). + +**DKIM** — La signature qui prouve qu'un courriel vient bien de ton domaine. + +--- + +## L'état, et sa preuve + +**SQLite** — Une base de données qui tient dans **un fichier**, sans serveur ni compte. +Suffisante pour une petite forge — c'est ce que portait patient 0, dont les machines +n'existent plus. À l'inverse de +*PostgreSQL*, qui est un service à part entière — un serveur, une zone, un secret. + +**restic** — L'outil de sauvegarde chiffrée et dédupliquée. Cf. [Sauvegardes](Sauvegardes). + +**Sauvegarde vs snapshot** — L'infrastructure se **reconstruit** depuis le code ; seul +l'**état** (annuaire, bases, courriel, forge) se sauvegarde. Cf. [Sauvegardes](Sauvegardes). + +**Devis** — Une commande qui **interroge le système réel** et montre l'écart avec le plan, +sans rien écrire (`make sdn-plan`, `make placement-plan`). + +**Preuve (Pxx)** — Une vérification rejouable de `make prouver` qui garde une classe d'erreur. Cf. [La preuve](La-preuve). -**Socle** — L'infrastructure de base souveraine (DNS, PKI, edge TLS, relais courriel) — le modèle -minimal public, extensible. +**Source unique** — Le principe qui traverse tout le dépôt : une information vit à **un** +endroit, et tout le reste en dérive. Neuf copies d'une même règle ne vieillissent pas +ensemble — et la divergence ne se voit jamais de l'intérieur d'une copie. -**Tenant** — Synonyme d'*instance* dans le contexte de la *fédération* : un écosystème isolé parmi -d'autres. +**Résolution d'instance** — Comment un script sait **quel écosystème** il regarde : la +variable `SETOPS_INSTANCE`, sinon le lien `instance`. Une seule fonction y répond pour tout +le moteur (`inventory_rules.instance_courante`) ; la preuve P41 refuse qu'on en réécrive +une deuxième. -**Voûte (vault)** — Le fichier chiffré des secrets d'une instance (`vault.yml`, Ansible Vault). -**Jamais** dans un *modèle* ni versionné ; seul le gabarit `vault.yml.example` l'est. +**Harnais** — L'ensemble des preuves. Un `make prouver` vert veut dire : aucune des classes +d'erreur connues n'est présente. -**Zone** — Un domaine de sécurité (un `/24` + un VLAN) : Frontière, Identité, Données, -Services-infra, Observabilité, Applications. Un pare-feu les sépare. +**Chèque vert sur un périmètre vide** — Le piège central de ce dépôt : une vérification qui +réussit **sans rien avoir mesuré**. Une sauvegarde de zéro fichier, un devis qui lit un +intrant périmé, une preuve qui ne peut pas échouer. À chaque « ✅ », se demander **sur +quoi** il a porté. + +**GUI** — La console web d'exploitation (`make inventaire-ui`). Cf. +[Le GUI](Le-GUI-console-d-exploitation). + +**CI** — La vérification automatique à chaque poussée (`make ci` la rejoue à l'identique). + +--- + +## La filiation + +→ Unité complète : [Filiation, signatures & témoins](Filiation-signatures-et-témoins) + +**Génome** — Les **quatre dépôts** sans lesquels un écosystème ne peut pas être refabriqué : +le *moteur*, l'*instance*, le dépôt de l'hébergeur, les *modèles*. `make genome` les nomme. + +**Parenté** — Le fichier `parente.yml` d'une instance : de quel moteur elle descend, à quel +*commit*, sous quelle *étiquette signée*. Sans lui, un écosystème ne sait plus d'où il vient. + +**Commit** — Un instantané du dépôt, qui porte l'**empreinte** du précédent. Modifier le +passé change toutes les empreintes suivantes : c'est ce qui rend une histoire vérifiable. + +**Empreinte (hachage)** — Une courte signature calculée d'un contenu. Deux contenus +différents n'ont pas la même — c'est ce qui permet de détecter une modification. + +**Arbre de Merkle** — La structure où chaque élément porte l'empreinte du précédent. Git en +est un ; une blockchain aussi. L'intégrité vient de là, pas d'une chaîne de blocs. + +**Étiquette (tag)** — Un nom posé sur un état précis du dépôt (`v2026.08.21`). Signée, elle +devient un point de repère **vérifiable** : « c'est de cet état-là que je descends ». + +**Signature** — Le scellé posé avec une clé privée. Qui a la clé **publique** peut vérifier ; +qui n'a pas la privée ne peut pas contrefaire. Sans elle, un nom d'auteur est déclaratif. + +**`.git-allowed-signers`** — Le registre, **versionné**, des clés autorisées à signer cette +lignée. Retirer une ligne révoque pour la suite ; le passé signé reste vérifiable. + +**Témoin** — Une copie vivante et indépendante du *génome*. Réécrire l'histoire suppose de +convaincre tous les témoins : c'est la lignée elle-même qui protège la lignée. + +**Journal de transparence** — Un registre append-only, auditable par un tiers (Certificate +Transparency, Sigstore). La réponse mûre quand plusieurs organisations doivent constater les +mêmes faits sans se faire confiance — et l'alternative sobre à une blockchain. + +**Horodatage (RFC 3161)** — Prouver qu'un document **existait** à une date, par un tiers. diff --git a/Home.md b/Home.md index 0499ca7..6002a2e 100644 --- a/Home.md +++ b/Home.md @@ -24,7 +24,7 @@ des **méthodes 100 % génériques**. | Section | Contenu | |---|---| | **Unités d'apprentissage** | Un fondamental TIC par page, toujours selon le même moule (ci-dessous). Des *services* (identité, PKI, courriel…) **et** de la *méthode* (le plan, le multi-instance, la preuve). | -| **Opérations (runbooks)** | Procédures : ajouter un service, déployer un nœud, restaurer une sauvegarde… | +| **Opérations (runbooks)** | Les procédures ne vivent **pas** ici : elles sont dans le dépôt, `docs/runbooks-exploitation.md`. Le wiki y pointe (même règle que la référence technique, ci-dessous). | | **[Glossaire](Glossaire)** | Les concepts-clés en une phrase (seed, bindings, le plancher, hôte fantôme, voûte…). | | **Référence technique** | Le *détail du « comment »* vit **dans le dépôt** (`docs/`, README des rôles) — ce wiki y **pointe**, ne le **recopie pas** (pour éviter la dérive). | diff --git a/Identité-et-SSO.md b/Identité-et-SSO.md index 2bb144e..8d93854 100644 --- a/Identité-et-SSO.md +++ b/Identité-et-SSO.md @@ -33,7 +33,7 @@ Trois pièces, un rôle chacun : | Pièce | Rôle | Concept incarné | |---|---|---| | **OpenLDAP** (`serveur_openldap`) | l'**annuaire** — la source de vérité. `testmail` y vit. | annuaire | -| **Keycloak** (`serveur_keycloak`) | le **fournisseur d'identité** (IdP) SSO. Il **fédère** OpenLDAP (lit les comptes en LDAP, lecture seule). | SSO, OIDC, fédération | +| **Keycloak** (`serveur_keycloak`) | le **fournisseur d'identité** (IdP) SSO. Il **fédère** OpenLDAP en mode **`WRITABLE`** : il écrit *à travers*, vers l'annuaire. | SSO, OIDC, fédération | | **oauth2-proxy** (`serveur_oauth2_proxy`) | une **passerelle** OIDC pour les apps **sans** OIDC natif. | motif *proxy d'authentification* | Les applications se branchent de deux façons : @@ -52,6 +52,16 @@ Grafana ──> crée la session : "tu es testmail" ✔ Résultat : **une identité** (`testmail`, un seul mot de passe LDAP) ouvre **le courriel, Grafana, Forgejo et Icinga**. Change le mot de passe une fois, il change partout. +> **Pourquoi `WRITABLE` et pas « lecture seule ».** Cette unité a écrit « lecture seule » +> jusqu'au 2026-09-06 ; ce serait plus prudent en apparence, et c'est un cul-de-sac. En +> `READ_ONLY`, un changement de mot de passe échoue sur *« Federated storage is not +> writable »* — or l'annuaire *exige* ce changement à la première connexion : la reprise du +> sysadmin butait dessus (constaté le 2026-08-07). Et `UNSYNCED` serait pire : Keycloak +> écrirait dans **sa** base, Dovecot et Postfix continueraient de valider l'ancien depuis +> LDAP — une identité, deux mots de passe, exactement ce que la doctrine interdit. +> `WRITABLE` écrit *à travers* : l'annuaire reste la source unique, Keycloak n'en est qu'un +> client. + --- ## ③ Pourquoi c'est transférable @@ -74,14 +84,14 @@ Change les produits, le **schéma reste**. C'est ça, un savoir générique. > Prérequis : accès au lab (VPN), `/etc/hosts` pointant les services sur l'edge. -1. **Vis le SSO.** Ouvre `https://grafana.lab.chezlepro.internal` → « *Se connecter avec +1. **Vis le SSO.** Ouvre `https://grafana.chezlepro.internal` → « *Se connecter avec Chezlepro* » → `testmail`. Puis ouvre Forgejo, puis Icinga : **tu n'es reconnecté nulle part**. 2. **Observe le flux.** Rouvre Grafana en navigation privée, ouvre les outils dév (F12 → Réseau) : repère la **redirection vers Keycloak**, puis le **retour avec un `code=`**. C'est ① en action. 3. **Interroge l'annuaire (la source de vérité).** Sur un nœud avec `ldap-utils` : ```bash - ldapsearch -x -H ldaps://id-ldap-01.lab.chezlepro.internal \ - -b ou=people,dc=lab,dc=chezlepro,dc=internal '(uid=testmail)' + ldapsearch -x -H ldaps://idm-01.chezlepro.internal \ + -b ou=people,dc=chezlepro,dc=internal '(uid=testmail)' ``` Tu vois l'entrée que Keycloak **fédère** — il ne l'a pas recopiée. 4. **Casse & répare (la fédération).** Dans la console admin Keycloak → *User Federation* → diff --git a/La-preuve.md b/La-preuve.md index 049a818..f52e176 100644 --- a/La-preuve.md +++ b/La-preuve.md @@ -26,7 +26,7 @@ Trois idées la portent : - **Le registre** : `docs/audit/affirmations.md` — chaque affirmation du dépôt (README, docs, aide `make`, GUI) reliée à une preuve et un statut (✅/🟡/❌/⚪). -- **Le harnais** : `make prouver` rejoue les preuves automatisables (**P01–P34**) et écrit +- **Le harnais** : `make prouver` rejoue les preuves automatisables (**P01–P60**, sans trou dans la série) et écrit `docs/audit/preuve-.md`. `make verifier` les inclut : il **échoue** si une preuve échoue. - **Chaque preuve garde une classe d'erreur.** Extrait : @@ -42,6 +42,7 @@ Trois idées la portent : | P31 | une capacité du dépôt **non expliquée** (script muet, cible sans aide, rôle sans README) | | P32 | un intrant qu'un rôle **exige** et que l'instance ne fournit pas | | P33 | deux rôles co-localisés qui **revendiquent le même port** | +| P35 | une application dont le rôle **exige une base** sans entrée au plan — sinon l'écart n'apparaît qu'après quarante minutes de déploiement | | P34 | un document qui ne **déclare pas son lecteur** — il finirait rangé par sujet, donc introuvable | > **Ce que ces preuves ne font pas, et il faut le savoir avant de leur faire confiance.** Elles @@ -86,6 +87,50 @@ harnais de Set-OPS ». --- +## Le défaut le plus dangereux n'est pas l'erreur, c'est la **copie** + +Set-OPS pilote plusieurs écosystèmes. Avant d'agir, chaque script doit donc savoir +**lequel il regarde** — par le lien `instance`, ou par la variable `SETOPS_INSTANCE`. + +En août 2026, **neuf scripts avaient chacun écrit leur propre réponse** à cette question. +Trois lignes chacun. Aucune n'était fausse en soi. + +Le problème n'est pas l'erreur : c'est que **neuf copies ne vieillissent pas ensemble**. +Quand on améliore l'une, les huit autres ne le savent pas. Et personne ne peut le voir, +parce que de l'intérieur d'un fichier, la copie locale a toujours l'air correcte. + +### Ce que ça donnait + +`make placement-plan` visait patient 0 et répondait : + +``` +Devis du placement — tenant « instance » + noeud asgard · stockage TrueNAS · gabarit 99998 → CONFORME +``` + +C'était vrai — **sur l'autre écosystème**. Sa copie lisait le lien au lieu de la variable. +Comme les deux tenants portaient les mêmes valeurs de placement, le verdict semblait +juste. C'est très exactement la circonstance où une erreur ne se voit pas. + +Cinq défauts de cette famille sont sortis en cinq jours. Tous dans des outils qui +**constatent** — preuves et devis — jamais dans ceux qui agissent. C'est moins grave et +plus insidieux : un outil qui agit mal, on le voit ; un outil qui mesure mal dit +« conforme », et on passe à la suite. + +### La réponse + +Une **source unique** : une fonction, dans un fichier, que tous appellent. Si elle est +fausse, elle l'est partout d'un coup — donc visible, donc corrigée une fois. + +Et une preuve, **P41**, qui refuse la prochaine copie. Elle en a trouvé une dixième le +jour de son écriture, dans le fichier des preuves lui-même. + +> **C'est la même leçon que `proxmox-hebergeur.yml`** : les nœuds et stockages du cluster +> recopiés chez chaque tenant avaient divergé. Une source, pas N copies — pour les données +> comme pour le code. + +--- + ## Pour aller plus loin *(dépôt)* - Le mode d'emploi : `docs/audit/README.md`. - Le registre : `docs/audit/affirmations.md` ; le harnais : `scripts/prouver.py`. diff --git a/Le-plan-et-l-adressage-dérivé.md b/Le-plan-et-l-adressage-dérivé.md index bbba9ef..729230e 100644 --- a/Le-plan-et-l-adressage-dérivé.md +++ b/Le-plan-et-l-adressage-dérivé.md @@ -37,8 +37,8 @@ Le **plan** (`instance/plan/`) est la source unique. Cinq registres : | Élément | Formule (modèle 6 zones) | |---|---| -| Supernet | `10.(10+index).0.0/16` | -| Sous-réseau de zone | `10.(10+index).(15+zone).0/24` | +| Supernet | `10..0.0/16` | +| Sous-réseau de zone | `10..(15+zone).0/24` | | Passerelle | `…(15+zone).1` | | VLAN sur le trunk | `1000 + index×10 + zone` | | VMID | `VLAN·octet-hôte·séquence` (9 chiffres) | diff --git a/Le-réseau-des-tenants.md b/Le-réseau-des-tenants.md new file mode 100644 index 0000000..20e6f7e --- /dev/null +++ b/Le-réseau-des-tenants.md @@ -0,0 +1,165 @@ +# Le réseau des tenants — du câble au VRF + +> **Unité d'apprentissage.** Moule : ① concept → ② Set-OPS → ③ transférable → ④ à toi de jouer. + +C'est la partie de Set-OPS qui emploie le plus de mots obscurs — *underlay*, *VXLAN*, +*VNet*, *VRF*, *strophe FRR*. Aucun n'est là par goût du jargon : chacun répond à un +problème précis, et on peut les rencontrer dans l'ordre où ils sont apparus. + +--- + +## ① Le concept + +### Le problème de départ : deux clients sur le même fil + +Deux organisations hébergées sur le même matériel ne doivent pas se voir. Or leurs +machines partagent des câbles, des commutateurs, des hyperviseurs. Il faut donc +**séparer ce qui est physiquement mélangé**. + +### Première réponse : le VLAN + +Un **VLAN** colle une étiquette (un nombre) sur chaque trame. Deux machines n'échangent +que si leurs étiquettes correspondent. C'est simple, ça marche, et c'est vieux de trente +ans. + +Deux limites : + +- l'étiquette tient sur 12 bits — **4094 VLAN**, pas un de plus ; +- chaque commutateur du chemin doit connaître chaque VLAN. Ajouter un client, c'est + toucher à la configuration de tout le monde. + +### Deuxième réponse : l'encapsulation + +**VXLAN** prend la trame d'un tenant, la met **dans une enveloppe** et l'expédie comme +un colis ordinaire d'un hyperviseur à l'autre. Le réseau physique ne voit passer que des +colis — il n'a plus besoin de connaître les clients. + +Deux vocabulaires en découlent, et c'est la distinction la plus utile de cette page : + +| | | +|---|---| +| **underlay** | le réseau **physique** : les câbles, les commutateurs, les adresses des hyperviseurs. Il ne connaît aucun tenant. | +| **overlay** | les réseaux **virtuels** des tenants, transportés dans des enveloppes par-dessus l'underlay. | + +L'enveloppe coûte **50 octets**. C'est toute l'explication du `mtu_overlay: 1450` : +1450 + 50 = 1500, la taille standard d'une trame. Se tromper là ne casse rien +franchement — les petites requêtes passent, les grosses réponses restent suspendues. +C'est la panne la plus déroutante du domaine. + +### Qui distribue les enveloppes : EVPN + +Pour expédier un colis, il faut savoir **où** l'envoyer. **EVPN** est le mécanisme par +lequel les hyperviseurs s'annoncent mutuellement les machines qu'ils hébergent. Sans +lui, il faudrait tenir cette table à la main. + +### Le VRF : une table de routage étanche + +Un routeur ordinaire a **une** table de routage. Un **VRF** lui en donne plusieurs, +hermétiques : dans la table du tenant A, les réseaux du tenant B **n'existent pas**. Ce +n'est pas une règle de pare-feu qu'on pourrait oublier — c'est une ignorance +structurelle. + +C'est la différence entre *« je t'interdis d'y aller »* et *« la route n'existe pas »*. + +--- + +## ② Dans Set-OPS + +### Ce que tu écris, et ce qui se calcule + +Tu écris **un nombre** : `index`. Tout le reste en découle. + +``` +index: 29 + ↓ +supernet 10.29.0.0/16 +VLAN 1000 + 29×10 + zone → 1291, 1292, 1293… +zone SDN t29 +VNet t29fron, t29serv, t29donn, t29appl +sous-réseau 10.29.16.0/24, 10.29.17.0/24 … +``` + +Un **VNet** est le réseau virtuel d'**une zone** d'un tenant : le point où les cartes +réseau des VM se branchent. Une VM de la zone Frontière du tenant 29 se branche sur +`t29fron`, et nulle part ailleurs. + +### Trois commandes, trois questions + +| Commande | Question à laquelle elle répond | +|---|---| +| `make underlay` | mon réseau **physique** est-il cohérent, et n'empiète-t-il pas sur les tenants ? | +| `make sdn-plan` | ce que le cluster porte **diffère-t-il** de ce que le plan décrit ? (aucune écriture) | +| `make sdn-appliquer` | pose la différence — et **retire ce qui est périmé** | + +`sdn-plan` avant `sdn-appliquer`, toujours. La seconde écrit sur le cluster et sur les +hyperviseurs ; la première ne fait que regarder. + +### La strophe FRR + +**FRR** est le démon de routage des hyperviseurs. Set-OPS lui pose un bloc par tenant — +ce que le dépôt appelle une **strophe** — dans `/etc/frr/frr.conf.local`, sur chaque +nœud de sortie : + +``` +vrf vrf_t29 + ip route 0.0.0.0/0 10.0.4.1 nexthop-vrf default + ip route 10.29.0.0/16 blackhole +exit-vrf +``` + +**La première ligne, c'est la sortie.** Tout ce qui ne concerne pas le tenant part vers +la frontière. `nexthop-vrf default` **emprunte une seule adresse** à la table principale +au lieu de l'importer en entier : importer aurait fait entrer dans le VRF le transport +VXLAN, le plan de gestion et les VLAN hérités — et permis de contourner la frontière. + +**La deuxième ligne, c'est un puits.** Elle attrape les adresses **non attribuées** du +tenant. Elle est moins précise que les `/24` des VNets, donc le trafic légitime ne la +voit jamais. + +Sans ce puits (mesuré le 2026-08-09) : une adresse inexistante ne trouvait aucune route +locale, sortait par le défaut, revenait de la frontière vers l'hyperviseur, atterrissait +dans la table **principale** — et repartait vers la passerelle du réseau +d'**administration**. Deux conséquences : le trafic d'un tenant pouvait atteindre le +plan de gestion **par une faute de frappe**, et `connect()` réussissait vers n'importe +quelle adresse inexistante. Ce qu'on avait longtemps pris pour une protection de la +frontière n'était qu'une route manquante ici. + +> **Le fichier ne se sauvegarde pas : il se régénère.** Il porte son avertissement en +> tête — *« NE PAS ÉDITER À LA MAIN »*. La source de vérité est le plan. + +--- + +## ③ Transférable + +Rien de tout ça n'appartient à Set-OPS. Ce sont les briques de n'importe quel réseau +multi-locataire : + +- **underlay / overlay** : le vocabulaire de tous les centres de données depuis 2015 ; +- **VXLAN + EVPN** : la même paire chez tous les hébergeurs, du garage à AWS ; +- **VRF** : présent sur tout routeur professionnel, et sur Linux depuis 2016 ; +- **le puits (`blackhole`)** : une pratique standard d'anti-fuite. + +Ce que Set-OPS ajoute n'est pas de la technologie, c'est une **dérivation** : ailleurs, +ces objets se saisissent à la main, un par un, dans quatre interfaces différentes. Ici, +ils descendent tous d'un seul nombre — donc ils ne peuvent pas se contredire. + +--- + +## ④ À toi de jouer + +1. **Lis ton réseau physique** : `make underlay`. Repère les VLAN sous 1000 (l'underlay) + et les MTU. Pourquoi le transport doit-il être à 1500 quand l'overlay est à 1450 ? +2. **Regarde sans écrire** : `make sdn-plan`. Si le cluster dit déjà ce que le plan dit, + la sortie tient en une ligne. +3. **Trouve la sortie d'un tenant** : dans `make devis-sdn`, repère la strophe FRR et + l'adresse du prochain saut. À quel équipement appartient-elle ? +4. **Change `index` dans un modèle** (jamais en production) et régénère : combien de + valeurs ont bougé ? C'est la mesure exacte de ce que la dérivation t'épargne. + +**Pour aller plus loin** : `docs/sdn-evpn.md` dans le dépôt (référence technique), +[Le plan & l'adressage dérivé](Le-plan-et-l-adressage-dérivé), +[Multi-instance & fédération](Multi-instance-et-fédération). + +> *Le lien vers `docs/` était relatif (`../docs/…`) : il fonctionne dans le dépôt et +> **casse une fois le wiki publié**, la forge ne recevant que le contenu de `wiki/`. C'est +> pourquoi le corpus cite `docs/` en **texte**, jamais en lien — une seule page dérogeait.* diff --git a/Liaisons-bindings.md b/Liaisons-bindings.md index 35d86a3..4e01fba 100644 --- a/Liaisons-bindings.md +++ b/Liaisons-bindings.md @@ -38,8 +38,16 @@ Les **résolveurs partagés** font le pont : `resoudre_base` (app→base) et `re **dans le rôle**, jamais en clair. La **modalité** structure la robustesse : une liaison **requise** absente → le moteur **refuse -d'instancier** (ex. une base sans serveur SQL). Une **optionnelle** absente → silence (ex. un nœud -sans `client_journal` marche très bien). +d'instancier** (ex. une base sans serveur SQL). Une **optionnelle** absente → silence (ex. un +nœud sans `client_backup` : il ne détient pas d'état, il n'a rien à sauvegarder). + +> **Attention à une troisième catégorie, qui n'est ni l'une ni l'autre : l'universelle.** +> `client_pki`, `client_metrique`, `client_journal`, `client_resolveur` et +> `client_artefacts` ne se déclarent **pas** — tout hôte les reçoit par dérivation, et +> **P26** refuse qu'un hôte y échappe. Cette unité citait `client_journal` en exemple +> d'optionnelle jusqu'au 2026-09-06 : c'est l'inverse. Le plan portait 28 lignes qui +> disaient « oui » à quelque chose de vrai pour tous ; elles n'existaient que pour être +> oubliées, et quatre l'avaient été. C'est **le** concept de Set-OPS : *tu déclares les liaisons, le moteur câble.* @@ -64,9 +72,11 @@ produit. C'est un **modèle mental** qui vaut de NetScaler à Kubernetes. *app→domaine*), et `serveurs.yml` : un nœud avec `integrations:` (liaison *nœud→service*). 2. **Vois-la se résoudre.** Après `make instancier`, regarde l'inventaire généré : la cible est devenue une **valeur concrète** (FQDN, groupe) — le moteur a câblé. -3. **Requise vs optionnelle.** Compare : retirer `client_journal` d'un nœud → aucun problème - (optionnelle). Déclarer une base **sans** serveur → `make instancier`/la validation **échoue** - (requise). Tu *sens* la différence de modalité. +3. **Requise, optionnelle, universelle.** Compare les trois : retirer `client_backup` d'un + nœud sans état → aucun problème (**optionnelle**). Déclarer une base **sans** serveur → + `make instancier` **échoue** (**requise**). Essayer de recopier `client_metrique` dans + `serveurs.yml` → le plan **refuse** (**universelle** : elle est dérivée, la recopier + créerait une seconde source qui finirait par diverger). 4. **Casse & répare.** Casse une liaison requise (ex. réfère une base à un serveur inexistant), relance l'instanciation : **échec clair** *avant* tout déploiement. Corrige : ça passe. Le moteur attrape le câblage manquant à ta place. diff --git a/Multi-instance-et-fédération.md b/Multi-instance-et-fédération.md index ecce929..e0698a3 100644 --- a/Multi-instance-et-fédération.md +++ b/Multi-instance-et-fédération.md @@ -30,7 +30,10 @@ rien à inscrire nulle part. Déposer une instance à côté des autres suffit. **Le seed garantit l'unicité.** Chaque tenant a un `index` distinct → adressage dérivé sans chevauchement possible : VLAN `1000+index×10+zone` (les VLAN de deux index différents ne peuvent -*mathématiquement* pas se croiser). Plafond : **245** écosystèmes fédérés (borne IPv4). +*mathématiquement* pas se croiser). Plafond : le **2ᵉ octet IPv4**, soit un index de **0 à +255** — en évitant 0, où vivent les réseaux de service du site. *(Cette unité annonçait 245, +un reste de l'époque où l'adressage valait `10.<10+index>` ; ce décalage a été retiré le +2026-08-12, justement pour qu'on lise l'index directement dans l'adresse.)* **Gérer la flotte :** - `make instances` — la vue d'ensemble : qui existe, l'active (★), index, VLAN, **collision** ; @@ -91,15 +94,23 @@ d'aucun `index`. On la décrit dans `underlay.yml`, qui vit dans le dépôt de * 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. Gabarit : `underlay.yml.example`. -| Underlay | VLAN | Sous-réseau | Fabric | -|---|---|---|---| -| management (commutateurs, Proxmox, OOB) | 10 | `10.0.0.0/24` | principale | -| **transit** vers la frontière | 40 | `10.0.4.0/29` | principale | -| stockage / iSCSI | 20 | `10.0.1.0/24` | stockage | -| ceph-public | 30 | `10.0.2.0/24` | stockage | -| ceph-cluster | 31 | `10.0.3.0/24` | stockage | +> **Ne pas recopier de table ici.** Cette unité en portait une, figée, et **aucune de ses +> cinq lignes n'était encore vraie** après la bascule d'adressage de la fabric (D-77/D-78, +> terminée le 2026-08-22). Un underlay n'est pas un modèle : c'est la description d'un +> matériel particulier, propre à un hébergeur. On le **demande** : +> +> ```bash +> make underlay # affiche l'underlay monté, et le valide +> ``` +> +> Ce qu'on y trouve chez l'hébergeur de référence, à titre d'illustration seulement : un +> plan d'**administration** sans VLAN (segment physique, `10.17.0.0/24`), le **contrôle de +> la grappe** à part (`vmbr0`, `192.168.11.0/24`), un **transit** vers la frontière, un +> réseau de **transport VXLAN**, trois réseaux de **stockage** sur leur propre fabric en +> MTU 9000, et les **zones du site lui-même** — l'hébergeur est aussi un exploitant, ses +> machines vivent là. -Deux choses à retenir de cette table. +Deux choses à retenir, qui ne dépendent d'aucune table. **Les fabrics.** Tous les réseaux ne partagent pas les mêmes câbles. Le stockage jumbo peut vivre sur ses propres commutateurs ; le devis de l'une ne déclare alors rien de l'autre, et le @@ -116,13 +127,19 @@ route vers tous les tenants par ce même saut, il ne peut donc dériver d'aucun > elle n'est joignable. On cherche une règle de pare-feu ; c'est une route manquante à l'autre > bout. -En mode `sdn`, ces réseaux transportent du VXLAN : leur **MTU doit atteindre 1550** au minimum, -sinon le ping passe et les transferts échouent. `make underlay` le refuse. +En mode `sdn`, le transport porte du VXLAN : le **MTU des réseaux de la fabric du routeur** +doit valoir l'overlay déclaré **plus 50 octets** d'encapsulation. `make underlay` le +**refuse** en dessous, et le dit — sous ce seuil, le ping passe et les transferts échouent, +la panne la plus coûteuse à diagnostiquer de cette couche. -`make underlay` l'affiche et le **valide** : VLAN < 1000 et sous-réseaux hors des supernets -tenant (`10.(10+index).0.0/16`) — **aucune collision possible** avec les overlays. `make -devis-reseau` en émet la config (section 0) et l'ajoute au trunk. La **preuve P23** garde la -règle ; elle est *sautée* si aucun `underlay.yml` n'est défini. +`make underlay` l'affiche et le **valide** : VLAN < 1000, et pas de chevauchement +**accidentel** avec un supernet de tenant. « Accidentel », parce qu'il en existe un +**voulu** : le plan d'administration de l'hébergeur occupe la **bande basse** du `/16` d'un +tenant (D-77). Les zones d'un tenant commencent au 3ᵉ octet 16 ; la bande 0-15 lui est +libre. Ce n'est pas une collision, mais il faut le **déclarer** (`bande_basse_de:`) — sinon +le validateur ne peut pas faire la différence, et il refuse. `make devis-reseau` en émet la +config (section 0) et l'ajoute au trunk. La **preuve P23** garde la règle ; elle est +*sautée* si aucun `underlay.yml` n'est défini. --- diff --git a/Métriques-et-journaux.md b/Métriques-et-journaux.md index 95773c2..a677310 100644 --- a/Métriques-et-journaux.md +++ b/Métriques-et-journaux.md @@ -28,9 +28,19 @@ ou expédie des logs (*shipper*). Le serveur central agrège ; un tableau de bor | **Loki** (`serveur_loki`) | **PUSH** | reçoit les journaux `journald` expédiés par `client_journal`. | | **Grafana** (`serveur_grafana`) | — | tableaux de bord au-dessus des **deux** (au SSO). | -Le point-clé prouvé cette session : un serveur d'observabilité **sans agents** ne voit que -lui-même. En déployant `client_metrique`/`client_journal` (des **liaisons nœud, optionnelles**), -Grafana voit **toute la flotte**. *Serveur ≠ agent* : les deux sont nécessaires. +Le point-clé : un serveur d'observabilité **sans agents** ne voit que lui-même. Avec +`client_metrique`/`client_journal` sur les nœuds, Grafana voit **toute la flotte**. +*Serveur ≠ agent* : les deux sont nécessaires. + +> **Ces deux liaisons ne sont PAS optionnelles**, contrairement à ce que cette unité a dit +> jusqu'au 2026-09-06. Elles portent `universelle: true` dans leur `meta/integration.yml` : +> tout hôte les reçoit **par dérivation**, sans qu'on écrive une ligne au plan, et **P26** +> refuse qu'un hôte y échappe. C'est exactement la leçon du paragraphe ci-dessus, tirée à +> son terme : le plan portait 28 lignes qui disaient « oui » à quelque chose de vrai pour +> tous — elles n'existaient que pour être oubliées, et quatre l'avaient été. Deux serveurs +> n'étaient alors ni supervisés ni journalisés, et **une machine non supervisée ne +> proteste pas**. Aujourd'hui on ne peut plus oublier : il faut **exempter**, et dire +> pourquoi. --- @@ -55,7 +65,7 @@ Tu as appris **métriques vs logs, pull vs push, le motif agent** — pas « Pro ``` 2. **Vois les journaux** : `curl -s http://localhost:3100/loki/api/v1/label/host/values` → les nœuds qui expédient leurs logs. -3. **Ouvre Grafana** (`https://grafana.lab.chezlepro.internal`) — métriques *et* logs au même endroit. +3. **Ouvre Grafana** (`https://grafana.chezlepro.internal`) — métriques *et* logs au même endroit. 4. **Casse & répare.** Arrête `prometheus-node-exporter` sur un nœud : dans Prometheus, sa cible passe **`up=0`** (DOWN). Redémarre : elle repasse UP. Tu *sens* que c'est **l'agent** qui nourrit le serveur (modèle pull). @@ -65,4 +75,4 @@ Tu as appris **métriques vs logs, pull vs push, le motif agent** — pas « Pro ## Pour aller plus loin *(dépôt)* - Rôles : `roles/serveur_prometheus`, `roles/serveur_loki`, `roles/serveur_grafana`. - Agents : `roles/client_metrique` (node_exporter), `roles/client_journal` (→ Loki). -- Ces agents sont des **liaisons optionnelles** : unité **Liaisons**. +- Ces agents sont des **intégrations universelles** (posées par dérivation, gardées par P26) : unités **Liaisons** et `docs/integrations-vm.md`. diff --git a/PKI-et-confiance.md b/PKI-et-confiance.md index 1fde741..2305b9f 100644 --- a/PKI-et-confiance.md +++ b/PKI-et-confiance.md @@ -9,7 +9,7 @@ **Chiffrement asymétrique** : une **paire de clés** — une **privée** (secrète) et une **publique** (partageable). Ce que l'une chiffre, l'autre le déchiffre. La privée **signe**, la publique **vérifie**. -**Un certificat** = une clé publique + une identité (« ce serveur est `id-ldap-01` »), le tout +**Un certificat** = une clé publique + une identité (« ce serveur est `idm-01` »), le tout **signé** par une autorité. Il répond à : *« à qui est-ce que je parle, vraiment ? »* **L'autorité de certification (AC / CA)** signe les certificats. On lui fait confiance, donc on fait @@ -29,7 +29,7 @@ confiance à toute la chaîne. | Pièce | Rôle | Concept incarné | |---|---|---| | **step-ca** (`serveur_step_ca`) | l'**autorité de certification** interne (racine + intermédiaire, base des émissions). | AC, chaîne de confiance | -| **client_pki** (`client_pki`) | intégration : un nœud **obtient un certificat** de l'AC (via ACME) et **fait confiance à la racine**. | émission ACME, magasin de confiance | +| **client_pki** (`client_pki`) | intégration **universelle** : tout nœud **obtient un certificat** de l'AC (via ACME) et **fait confiance à la racine**. Elle ne se déclare pas — elle est dérivée, et **P26** refuse qu'un hôte y échappe. Seule exemption, dérivée : l'hôte qui *est* l'AC, qui ne s'enrôle pas auprès d'elle-même. | émission ACME, magasin de confiance | Le motif : chaque service qui doit prouver son identité (LDAPS d'OpenLDAP, HTTPS de l'edge…) reçoit un **certificat d'hôte** signé par step-ca ; la **racine** est déposée dans le **magasin de @@ -68,7 +68,7 @@ Encrypt, Vault, une AC d'entreprise : le schéma est **identique**. `OK` = la racine valide bien le certificat du serveur. C'est ① en action. 3. **Vois-le servir en vrai.** Le LDAPS d'OpenLDAP utilise ce certificat : ```bash - openssl s_client -connect id-ldap-01.lab.chezlepro.internal:636 \ + openssl s_client -connect idm-01.chezlepro.internal:636 \ -CAfile /etc/step/certs/root_ca.crt /dev/null | grep -E 'Verify return code' ``` `0 (ok)` = confiance vérifiée. @@ -92,13 +92,25 @@ fiable. Et attention à un piège vécu en vrai : Diagnostic-réflexe : **comparer le cert servi au cert fichier**. ```bash -echo | openssl s_client -connect EDGE:443 -servername keycloak.lab… 2>/dev/null | openssl x509 -noout -enddate # SERVI +echo | openssl s_client -connect EDGE:443 -servername keycloak.chezlepro.internal 2>/dev/null | openssl x509 -noout -enddate # SERVI openssl x509 -in /etc/step/certs/EDGE.crt -noout -enddate # FICHIER ``` Dans Set-OPS, `client_pki_reload_services` (par nœud) fait recharger les **vrais** consommateurs après chaque renouvellement — edge→nginx, mail→postfix/dovecot, annuaire→slapd. Fix d'urgence si ça arrive : `systemctl reload nginx` sur l'edge. +Ce diagnostic est **outillé** depuis le 2026-08-08 : `make certificats-plan` compare, pour +toute la flotte, ce que le disque porte à ce que la mémoire sert. Il a trouvé quelque chose +à sa première exécution — sur `infra-pki-01`, **l'autorité elle-même**, le certificat était +expiré depuis plus de huit heures et le renouvellement échouait toutes les quatorze minutes +sur `'step ca renew' requires the '--ca-url' flag`. Rien ne le signalait, et le harnais +était entièrement vert. + +> **Ne compare pas les empreintes, regarde l'échéance de ce qui est SERVI.** Avec des certs +> de 24 h renouvelés toutes les ~14 minutes, une empreinte servie *différente* de celle sur +> disque est l'état **normal** : un contrôle par empreinte crierait en permanence, et on +> apprendrait à l'ignorer. + **Casse & répare** : sur l'edge, arrête le timer `cert-renewer@…`, laisse le cert expirer (ou force une horloge), observe le login SSO casser (échec TLS de l'échange OIDC), puis recharge nginx → tout revient. Tu *sens* que la PKI ne vit que si le renouvellement **et** le rechargement tournent. diff --git a/Reprendre-l-écosystème.md b/Reprendre-l-écosystème.md index c5b2622..92afc4d 100644 --- a/Reprendre-l-écosystème.md +++ b/Reprendre-l-écosystème.md @@ -47,11 +47,24 @@ Détail de ce que chaque devis vérifie — et de ce qu'il ne vérifie pas : `do Un prérequis et trois pièges, tous à la première connexion. Le runbook complet est dans `docs/autorisation.md` **§6** ; voici l'ordre et la raison de chaque geste. -**1. La clé de voûte.** Le mot de passe de la voûte est lu depuis -`ANSIBLE_VAULT_PASSWORD_FILE` (`~/.config/setops-vault-pass` par défaut). **Sans ce fichier, -rien n'est possible** — ni les devis ci-dessus, ni un déploiement. C'est la première chose à -sauvegarder hors de la machine, avec les `vault.yml` de chaque instance, qui ne sont pas -versionnés. +**1. La clé de voûte — une voûte, une clé.** Chaque dépôt a **sa** clé, nommée d'après lui : +`~/.config/setops-vault-`. Il n'y a **rien à exporter** — le `Makefile` +les rassemble seul (`scripts/voutes.py` → `ANSIBLE_VAULT_IDENTITY_LIST`). Pour voir ce que +cette machine peut ouvrir : + +``` +python3 scripts/voutes.py etat +``` + +**Sans la clé de ton écosystème, rien n'est possible** — ni les devis ci-dessus, ni un +déploiement. C'est la première chose à sortir de la machine (`make cles-exporter`, cf. +`docs/sortir-les-cles-du-poste.md`), avec les `vault.yml` de chaque instance, qui ne sont +pas versionnés. + +> Ce paragraphe a désigné un `ANSIBLE_VAULT_PASSWORD_FILE` unique jusqu'au 2026-09-06. Ce +> n'est plus le mécanisme, et la raison compte : un seul mot de passe ouvrait alors *toutes* +> les voûtes de la flotte, celle de l'hébergeur comprise. Compromettre le plus petit +> locataire, c'était obtenir les secrets de tous. **2. Le mot de passe d'amorçage, à changer avant tout le reste** (§6.1). L'annuaire refuse toute opération tant qu'il n'est pas changé, sauf le changement lui-même. Ce n'est pas diff --git a/Reverse-proxy-et-TLS.md b/Reverse-proxy-et-TLS.md index bfcbef4..71fec56 100644 --- a/Reverse-proxy-et-TLS.md +++ b/Reverse-proxy-et-TLS.md @@ -19,7 +19,7 @@ Un seul endroit gère les certificats → simple et cohérent. ## ② Comment Set-OPS le fait `serveur_nginx` déployé sur l'**edge** (`infra-edge`) est le reverse-proxy. Le point élégant : -l'**exposition est auto-dérivée**. Déclarer `expose: [icinga.lab.chezlepro.internal]` sur une app +l'**exposition est auto-dérivée**. Déclarer `expose: [icinga.chezlepro.internal]` sur une app génère **tout** : ``` @@ -49,14 +49,14 @@ Tu as appris **le reverse-proxy, le routage par nom, la terminaison TLS** — pa ## ④ À toi de jouer -1. **Route par nom.** Deux noms, un seul edge (`192.168.15.21`) : +1. **Route par nom.** Deux noms, un seul edge (`10.17.16.11`) : ```bash - curl -sI --resolve grafana.lab.chezlepro.internal:443:192.168.15.21 \ - --cacert /etc/step/certs/root_ca.crt https://grafana.lab.chezlepro.internal/ | head -1 + curl -sI --resolve grafana.chezlepro.internal:443:10.17.16.11 \ + --cacert /etc/step/certs/root_ca.crt https://grafana.chezlepro.internal/ | head -1 ``` Change `grafana` en `forge` : même IP, **backend différent**. C'est le routage par SNI. 2. **Vois la terminaison TLS.** Le certificat présenté est celui de l'**edge** (avec les SAN des - exposés) : `openssl s_client -connect 192.168.15.21:443 -servername grafana.lab… | openssl x509 -noout -text | grep -A1 'Subject Alternative'`. + exposés) : `openssl s_client -connect 10.17.16.11:443 -servername grafana.chezlepro.internal | openssl x509 -noout -text | grep -A1 'Subject Alternative'`. 3. **Casse & répare.** Arrête le backend (ex. `systemctl stop grafana-server` sur obs-01) et rouvre Grafana : l'edge répond **502 Bad Gateway** (le proxy est là, le service non). Redémarre : ça remarche. Tu distingues **le proxy** de **ce qu'il sert**. diff --git a/Sauvegardes.md b/Sauvegardes.md index cd83d99..ba7bed8 100644 --- a/Sauvegardes.md +++ b/Sauvegardes.md @@ -32,8 +32,16 @@ Choix fondateur : **sauvegarder la donnée** (l'infra est reconstructible par le | Pièce | Rôle | |---|---| | **restic** | l'outil : chiffrement côté client, déduplication, rétention. | -| **`serveur_backup`** | la **cible** hors-nœud (un dépôt restic par nœud). | -| **`client_backup`** | intégration par nœud : **jobs déclaratifs** (dump + chemins), timer quotidien, rétention. | +| **`serveur_backup`** | la **cible** hors-nœud (un dépôt restic par nœud) — depuis le 2026-09-01, chez l'**hébergeur** (`site-backup-01`), un compte Unix par écosystème. | +| **`client_backup`** | intégration par nœud : **jobs déclaratifs** (dump + chemins), timer quotidien, rétention — et, depuis le 2026-09-02, **vérification de son propre dépôt distant**. | + +> **Qui vérifie a changé.** Le dépôt était le seul à voir ce qui arrivait vraiment : un +> nœud sait qu'il a *lancé* sa sauvegarde, pas qu'elle a *abouti*. C'était juste tant que +> le dépôt vivait dans l'écosystème. Depuis que les écosystèmes déposent chez leur +> **hébergeur** — qui héberge du chiffré côté client et ne peut rien juger — la +> vérification revient au seul qui détient la clé : **le nœud lui-même**. Il interroge son +> dépôt *distant*, et non le fait d'avoir lancé un timer. Une unité verte sur un dépôt vide +> est exactement ce qui a menti pendant un mois. Ce qu'on protège, par **tiers** : - **Tier 0 (vital)** : les **clés de la CA** step-ca (`/etc/step-ca`) — l'ancre de confiance, irremplaçable. @@ -64,12 +72,15 @@ Tu as appris **quoi sauvegarder, la règle 3-2-1, logique vs image, et surtout r ```bash /usr/local/sbin/setops-sauvegarder.sh ``` -2. **Liste les instantanés** (le dépôt vit hors-nœud) : +2. **Liste les instantanés** (le dépôt vit hors-nœud, et hors de l'écosystème) : ```bash - export RESTIC_REPOSITORY=sftp:restic@backup-01.lab.chezlepro.internal:$(hostname) - export RESTIC_PASSWORD_FILE=/etc/setops/restic.pass + # NE PAS RECOPIER UNE ADRESSE ICI : la cible est une valeur du plan, et elle a + # deja change. Le noeud la porte deja, gravee dans son propre script. + eval "$(grep -E '^export RESTIC_' /usr/local/sbin/setops-sauvegarder.sh)" restic snapshots ``` + *Le même dépôt est interrogé chaque nuit par `setops-verifier-mon-depot.sh`, qui + rapporte à Icinga : c'est le nœud, seul détenteur de la clé, qui juge.* 3. **Restaure — le vrai test.** Restaure dans un dossier temporaire et **compare** : ```bash restic restore latest --target /tmp/rst @@ -85,4 +96,9 @@ Tu as appris **quoi sauvegarder, la règle 3-2-1, logique vs image, et surtout r ## Pour aller plus loin *(dépôt)* - Rôles : `roles/serveur_backup`, `roles/client_backup`. - Philosophie « donnée, pas VM » + les tiers : voir le CHANGELOG (entrée sauvegardes) et les jobs en host_vars. -- Complément 3-2-1 (offsite) : reste à faire — c'est l'**Étape B** (frontière publique). +- Le **1 hors-site** de la règle 3-2-1 : `make depot-hors-site VERS=` + (`playbooks/maintenance/depot-hors-site.yml`). Il emporte le dépôt du site — celui de + l'hébergeur **et** ceux des locataires, puisque c'est l'hébergeur qui a pris cette + promesse. La copie est opaque, et l'empreinte est prise **à la source** puis recalculée + sur la copie : sans cela on rentre chez soi avec un répertoire, pas avec une sauvegarde. + Manœuvre exécutée le 2026-09-05 — 233 fichiers, 95,7 Mo, empreintes identiques une à une. diff --git a/Supervision-et-impact.md b/Supervision-et-impact.md index a712294..33d4e3c 100644 --- a/Supervision-et-impact.md +++ b/Supervision-et-impact.md @@ -49,7 +49,7 @@ Tu as appris **supervision active vs observabilité passive, l'état, l'alerting ## ④ À toi de jouer -1. **Ouvre Icinga Web 2** (`https://icinga.lab.chezlepro.internal`, via le SSO) : la liste des hôtes +1. **Ouvre Icinga Web 2** (`https://icinga.chezlepro.internal`, via le SSO) : la liste des hôtes et services **supervisés**, avec leur **état** (vert/jaune/rouge). 2. **Vois l'impact.** Menu *Business Processes* → « **Supervision Chezlepro** » : un processus qui **agrège** des checks (load, procs, ping…) en un état roulé. C'est l'impact, pas une case. diff --git a/Sécurité-et-durcissement.md b/Sécurité-et-durcissement.md index 8abba36..df0bc14 100644 --- a/Sécurité-et-durcissement.md +++ b/Sécurité-et-durcissement.md @@ -31,14 +31,28 @@ pas en option : | Audit | `auditd` | | Anti-force-brute SSH | `fail2ban_ssh` | | Durcissement noyau | `sysctl_hardening` | -| Pare-feu | `nftables_baseline` (*installé et préparé, désactivé par défaut*) | +| Pare-feu | `nftables_baseline` — **règles dérivées du registre des flux**, pas écrites à la main | | Mises à jour | `unattended_upgrades` | +| Journaux | `journald` (rétention, persistance) | +| Vidages mémoire | `core_dumps` (désactivés — un *core* peut contenir des secrets) | +| Paquets | `hardening_packages` | | SSH | `ssh_baseline` + `ssh_hardening` (clé d'abord, `PermitRootLogin no`…) | | Moindre privilège | compte technique `ansible` + `sudo_ansible` (pas de root direct) | -Nuance importante : le **pare-feu** est *préparé mais pas activé* dans le template — on l'active sur -un **clone/serveur final** avec des règles adaptées à son rôle (couper l'accès à l'aveugle = un -risque). Prudence par conception. +Le groupe `serveur_durci` compose **dix** de ces rôles en un seul geste ; `ssh_baseline` et +`sudo_ansible` viennent du socle `serveur_debian`, en amont. + +**Le pare-feu : préparé dans le gabarit, armé sur la flotte.** `nftables_baseline` vaut +`false` par défaut *dans le rôle* — couper l'accès à l'aveugle pendant la construction d'un +gabarit serait un risque gratuit. Mais l'instance le met à `true` pour `hotes_actifs` : sur +une machine réelle, **le pare-feu tourne**. + +Et ses règles ne s'écrivent pas à la main. Chaque rôle déclare dans son `meta/flux.yml` ce +qu'il écoute et ce à quoi il se connecte ; `scripts/resoudre_flux.py` en dérive le jeu de +règles, en `policy drop` par défaut. Le **même** registre alimente le pare-feu de +l'hyperviseur et la frontière OPNsense : trois couches qui ne *peuvent pas* se contredire, +parce qu'elles descendent d'une seule décision. C'est la défense en profondeur du §① prise +au mot — sans le coût habituel, qui est de tenir trois politiques cohérentes à la main. --- diff --git a/Virtualisation-et-clonage.md b/Virtualisation-et-clonage.md index a0a6121..005f173 100644 --- a/Virtualisation-et-clonage.md +++ b/Virtualisation-et-clonage.md @@ -23,8 +23,11 @@ Idée-force : la VM devient **jetable/reconstructible**. Ce qui est précieux, c ## ② Comment Set-OPS le fait - **Proxmox** = l'hyperviseur (cluster). -- Un **golden template** (`basiqueChezlepro`) : Debian minimal, durci, avec le compte technique - `ansible`, qemu-guest-agent, cloud-init… — préparé **une fois**. +- Un **golden template** (`modeleSetOPS`) : Debian minimal, durci, avec le compte technique + `ansible`, qemu-guest-agent, cloud-init… — préparé **une fois**. *(Cette unité l'appelait + `basiqueChezlepro` jusqu'au 2026-09-06 : c'est l'ancien nom. Le nom en vigueur est celui + qu'enregistre `make config` sous `proxmox_clone_source_nom`, et il doit correspondre au + nom réel du template dans Proxmox — sinon le clonage ne trouve pas sa source.)* - Chaque nœud = un **clone** du template. `cloud-init` pose l'identité (hostname, IP dérivée de la nomenclature, clé SSH). - **Puis** Set-OPS/Ansible fait la **vraie** configuration (les rôles). diff --git a/Vérifier-le-déployé.md b/Vérifier-le-déployé.md index 542864a..67404f3 100644 --- a/Vérifier-le-déployé.md +++ b/Vérifier-le-déployé.md @@ -45,6 +45,20 @@ make postgresql-plan le chiffrement est-il imposé, et à quels réseaux make courriel-plan Postfix → LDAP → LMTP → Dovecot → IMAP, file d'attente comprise ``` +Le même patron a ensuite été porté **sous** les services, au monde physique — mêmes pièces, +même refus d'écrire : + +``` +make frontiere-plan les règles de la frontière OPNsense contre leur devis +make proxmox-fw-plan le pare-feu est-ouest de l'hyperviseur contre le registre des flux +make sdn-plan la zone EVPN, ses VNets et la sortie des VRF +make underlay-plan l'underlay déclaré contre ce que le cluster porte vraiment +make placement-plan chaque VM est-elle là où le plan la met +``` + +*(Deux mesures voisines ne comparent à rien et ne portent donc pas le suffixe `-plan` : +`make mtu-mesurer` et `make versions-mesurer` relèvent un état, sans devis en face.)* + | Pièce | Rôle | |---|---| | un **playbook** (`playbooks/maintenance/devis-*.yml`) | **relève** le déclaré et le réel, dépose un JSON | @@ -86,8 +100,9 @@ l'éprouver dans les deux sens, en cassant volontairement ce qu'il surveille. ## ④ À toi de jouer -1. Lance les cinq devis sur ta flotte. Note le temps que ça prend : quelques minutes pour ce qui - demandait une journée d'enquête à la main. +1. Lance les **dix** devis sur ta flotte — les cinq de service, puis les cinq + d'infrastructure. Note le temps que ça prend : quelques minutes pour ce qui demandait une + journée d'enquête à la main. 2. **Casse quelque chose exprès** — arrête un service publié, change un port — et relance le devis concerné. S'il ne dit rien, c'est *lui* qu'il faut réparer, pas le service. 3. Cherche, dans ton propre outillage, une vérification qui n'a **jamais** échoué. Demande-toi si diff --git a/_Sidebar.md b/_Sidebar.md index 1cecc82..64a376d 100644 --- a/_Sidebar.md +++ b/_Sidebar.md @@ -13,6 +13,7 @@ - [DNS & résolution de noms](DNS-et-résolution) *Communication* +- [Le réseau des tenants](Le-réseau-des-tenants) - [Reverse-proxy & TLS](Reverse-proxy-et-TLS) - [Courriel (SMTP/IMAP)](Courriel) @@ -36,6 +37,7 @@ *Flotte & preuve* - [Multi-instance & fédération](Multi-instance-et-fédération) - [La preuve](La-preuve) +- [Filiation, signatures & témoins](Filiation-signatures-et-témoins) - [Vérifier le déployé](Vérifier-le-déployé) **Opérations**