resolveur du site : le troisieme service prete pendant la jeunesse d un tenant
Apres les paquets (serveur_cache_site) et le genome (serveur_forge_site), les NOMS.
Meme patron : un installateur, un marqueur.
CE QUE CA CORRIGE, mesure sur infra-pki-01, premiere machine a porter l autorite
de Chezlepro :
DNS BLOQUE un tenant resout chez lui, sa requete ne sort pas
443 sortant OK par adresse
apt via le cache OK le mandataire resout a sa place
apt s en sortait ; TOUT LE RESTE ETAIT AVEUGLE AUX NOMS. Versionner la cle de
signature Smallstep a fait passer une tache et l echec s est deplace d un cran : le
depot lui-meme devait etre joint par son nom.
POURQUOI PAS L UNBOUND DE LA FRONTIERE : il tourne sur le boitier sans etre un
service gere, et le plan du site l avait deja ecrit une fois — un processus n est
pas un service. site-dns-01 est deploye par le moteur et verifie par le harnais.
OU VIT LA DERIVATION : dans site_inventaire.py, qui connait le plan du site ET le
registre des tenants. Le role tourne sur une machine qui n a aucune raison de lire
la carte de l hebergeur — la derivation appartient a qui detient les deux sources.
PIEGE DE FILTRE, ATTRAPE EN LE BRANCHANT : _supernets_voisins() EXCLUT l instance
montee (personne n est son propre voisin). Le site sert TOUS ceux qu il heberge — y
compris celui qu on deploie. Reutilise tel quel, il privait de resolution le seul
tenant en cours de montage. On reutilise donc la DECOUVERTE partagee sans son
filtre : deux recensements divergent, deux filtres non.
Le marqueur verifie deux choses : que le resolveur ECOUTE, et que sa liste
d autorisation ADMET reellement les tenants. Sans la seconde, la panne chez le
locataire ressemblerait a un DNS mort alors que c est une ACL.
make verifier : vert. make prouver : CONFORME, 55 OK, 0 echec, 0 saute.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019f91zs9SDdwSzL2CVei6on
This commit is contained in:
parent
fc76a4e252
commit
a5c9a88f00
15 changed files with 279 additions and 13 deletions
|
|
@ -20,10 +20,10 @@
|
|||
| P05 | Dependances causales de groupes | AFF-009, AFF-084 | ✅ OK | |
|
||||
| P06 | Validateurs de registres (serveurs/apps/bases/domaines) | AFF-003 | ✅ OK | Registre des domaines valide. |
|
||||
| P07 | GUI (node --check) | AFF-033 | ✅ OK | JS du GUI : syntaxe valide (node --check). |
|
||||
| P08 | Orchestration (couches + graphe) | AFF-070 | ✅ OK | Orchestration coherente : 38 groupes classes, aucun cycle, aucune arete en arriere. |
|
||||
| P09 | Flux reseau (schema + matrice) | AFF-071 | ✅ OK | Flux coherents : 36 rôles, 95 flux, schéma + matrice OK. |
|
||||
| P08 | Orchestration (couches + graphe) | AFF-070 | ✅ OK | Orchestration coherente : 39 groupes classes, aucun cycle, aucune arete en arriere. |
|
||||
| P09 | Flux reseau (schema + matrice) | AFF-071 | ✅ OK | Flux coherents : 37 rôles, 97 flux, schéma + matrice OK. |
|
||||
| P10 | Handlers <-> notify | AFF-034, AFF-035 | ✅ OK | Tout notify pointe vers un handler du meme role (49 roles). |
|
||||
| P11 | Syntaxe des playbooks (--syntax-check) | AFF-083 | ✅ OK | playbook: playbooks/proxmox/cloner_vm_debian.yml |
|
||||
| P11 | Syntaxe des playbooks (--syntax-check) | AFF-083 | ✅ OK | serveur_resolveur_site |
|
||||
| P12 | Existence des runbooks cites | AFF-010, AFF-011, AFF-012, AFF-083 | ✅ OK | 17/17 runbooks/registres cites presents. |
|
||||
| P13 | Invariants structurels/doctrinaux | AFF-015, AFF-022, AFF-037, AFF-038, AFF-062 | ✅ OK | LICENSE, socle dossier, pas de couches paralleles, SSH clef-only, nftables off : OK. |
|
||||
| P14 | Pas de chemin lab/ code en dur | AFF-097 | ✅ OK | Aucun chemin instance/inventories/lab/group_vars code en dur. |
|
||||
|
|
@ -41,28 +41,28 @@
|
|||
| P26 | Integrations universelles : aucun hote laisse de cote | AFF-108 | ✅ OK | 15 hote(s) x 5 integration(s) universelle(s) : aucune lacune, aucune recopie (0 exemption(s) derivee(s) du service rendu). |
|
||||
| P27 | Propriete des intrants : hebergeur et tenant separes | AFF-109 | ✅ OK | 8 cle(s) de cluster chez l'hebergeur, aucune recopiee dans les group_vars du tenant. |
|
||||
| P28 | Pools Proxmox : un par tenant, sans collision | AFF-110 | ✅ OK | CONFORME : 3 pool(s) Proxmox, 35 VM placee(s), aucun nom ni VMID en collision. |
|
||||
| P29 | Authentification : chaque role declare sa position | AFF-111 | ✅ OK | 30 role(s) serveur declares (interne-sans-auth 2, ldap-direct 2, sans-auth-humaine 19, socle-identite 2, web-sso 5) ; 2 lacune(s) nommee(s) : serveur_loki, serv |
|
||||
| P29 | Authentification : chaque role declare sa position | AFF-111 | ✅ OK | 31 role(s) serveur declares (interne-sans-auth 2, ldap-direct 2, sans-auth-humaine 20, socle-identite 2, web-sso 5) ; 2 lacune(s) nommee(s) : serveur_loki, serv |
|
||||
| P30 | SDN EVPN : zones, VNets et sous-reseaux derives | AFF-112 | ✅ OK | CONFORME : SDN EVPN, 3 zone(s), 15 VNet(s), 15 sous-reseau(x), aucune collision. |
|
||||
| P31 | Documentation : tout ce que le depot FAIT est nomme | — | ✅ OK | 53 scripts expliques et atteignables, 106 cibles make documentees, 62 roles avec README. |
|
||||
| P31 | Documentation : tout ce que le depot FAIT est nomme | — | ✅ OK | 53 scripts expliques et atteignables, 106 cibles make documentees, 63 roles avec README. |
|
||||
| P32 | Intrants exiges par les roles : tous fournis | — | ✅ OK | CONFORME : 41 exigence(s) de role, toutes satisfaites (130 cle(s) declaree(s) par l'instance). |
|
||||
| P33 | Aucune collision de port entre roles co-localises | — | ✅ OK | CONFORME : 33 revendication(s) de port, aucune collision entre roles co-localises (38 groupes). |
|
||||
| P34 | Chaque document declare son lecteur | — | ✅ OK | 42 document(s) declarent leur lecteur (27 genere(s) exempte(s)). |
|
||||
| P35 | Toute application exigeant une base en a une au plan | — | ✅ OK | 5 application(s) exigeant une base l'ont toutes (4 entree(s) au registre). |
|
||||
| P36 | Tout detenteur d'etat porte une sauvegarde | — | ✅ OK | 9 hote(s) detiennent de l'etat, tous porteurs de `client_backup` (9 groupe(s) au catalogue). |
|
||||
| P37 | Le placement du tenant existe chez son hebergeur | — | ✅ OK | placement confronte a l'hebergeur monte (SITE-Chezlepro) : noeud, stockage, pont — tous offerts. |
|
||||
| P38 | Catalogue des services : la carte dit ce que le moteur fait | — | ✅ OK | Catalogue a jour : 37 role(s) serveur/client tous nommes, 38 groupe(s) cite(s) en table existent tous. |
|
||||
| P38 | Catalogue des services : la carte dit ce que le moteur fait | — | ✅ OK | Catalogue a jour : 38 role(s) serveur/client tous nommes, 39 groupe(s) cite(s) en table existent tous. |
|
||||
| P39 | Glossaire : tout mot employe est enseigne | — | ✅ OK | Glossaire complet : 81 terme(s) du jargon expliques, 15 lien(s) valides, 27 page(s) de wiki toutes atteignables. |
|
||||
| P40 | Parente : l'ecosysteme sait de quoi il descend | — | ✅ OK | Parente coherente : 4 depot(s), tous retrouves, tous porteurs d'un remote. |
|
||||
| P41 | Resolution d'instance : une seule, partagee | — | ✅ OK | Resolution unique : 49 script(s) passent par `inventory_rules`, 3 exemption(s) nommee(s). |
|
||||
| P42 | L'edge porte les noms qu'il publie | — | ✅ OK | 4 edge(s) emettent un certificat portant les noms publies (OPS-Chezlepro-lab/principal, OPS-Chezlepro/principal, OPS-Technolibre/principal, OPS-Patient0/product |
|
||||
| P43 | Frontiere : le devis voit les machines du site | — | ✅ OK | Devis de la frontiere : 5 machine(s) du plan retrouvees, 60 regle(s) du site. |
|
||||
| P43 | Frontiere : le devis voit les machines du site | — | ✅ OK | Devis de la frontiere : 5 machine(s) du plan retrouvees, 66 regle(s) du site. |
|
||||
| P44 | Integrations : le serveur avant ses clients | — | ✅ OK | 4 integration(s) appliquent leur serveur avant leurs clients. |
|
||||
| P45 | Pare-feu Proxmox : arme sur les VNet SDN, jamais ailleurs | — | ✅ OK | Le pare-feu Proxmox ne s'arme que sur un VNet SDN (4 cas evalues, dont un qui doit rendre VRAI). |
|
||||
| P46 | Plancher /etc/hosts : un seul role en decide | — | ✅ OK | Un seul maitre du plancher — roles/hosts_statiques/tasks/main.yml : manage_etc_hosts: false ; et le gabarit maitre est pose (roles/hosts_statiques/templates/hos |
|
||||
| P47 | Zones inverses : couvrir l'occupe, et rien de plus | — | ✅ OK | Les zones inverses couvrent l'occupe et rien de plus (5 cas evalues, dont un site a quatre zones et un tenant a une). |
|
||||
| P48 | La carte d'orientation designe ce qui existe, et compte juste | — | ✅ OK | La carte designe 84 chemin(s) qui existent, et ses 7 chiffres correspondent a la mesure. |
|
||||
| P49 | Registre des flux : la matrice d'audit est a jour | — | ✅ OK | Le registre des flux reproduit exactement ce que les `meta/flux.yml` declarent (113 lignes). |
|
||||
| P50 | Silences : un refus muet est declare, place en dernier, et motive | — | ✅ OK | 2 silence(s) declare(s), tous en sequence > 1 (la plus haute des 125 regles `pass`), tous non consignes et tous motives. |
|
||||
| P49 | Registre des flux : la matrice d'audit est a jour | — | ✅ OK | Le registre des flux reproduit exactement ce que les `meta/flux.yml` declarent (115 lignes). |
|
||||
| P50 | Silences : un refus muet est declare, place en dernier, et motive | — | ✅ OK | 2 silence(s) declare(s), tous en sequence > 1 (la plus haute des 131 regles `pass`), tous non consignes et tous motives. |
|
||||
| P51 | Collections : toutes declarees, toutes epinglees | — | ✅ OK | 3 collection(s) et 2 bibliotheque(s) Python declarees et epinglees : ansible.posix==1.6.2, community.general==10.3.0, community.postgresql==3.10.2 |
|
||||
| P52 | Materialiser n'exige pas d'entrer dans le tenant | — | ✅ OK | `creer-vm` confirme par l'agent invite (API des hyperviseurs, deja utilisee pour creer), sans exiger d'entrer dans le tenant. |
|
||||
| P53 | L'interne refuse a voix haute, la bordure se tait | — | ✅ OK | L'interne parle, la bordure se tait — 15 ruleset(s) nftables refusent a voix haute ; pare-feu est-ouest en REJECT, source unique ; frontiere muette (actions : b |
|
||||
|
|
|
|||
|
|
@ -23,8 +23,8 @@ README de rôles). Cette page comble ces deux trous.
|
|||
|
||||
| Ce qu'on compte | Combien | Comment on le mesure |
|
||||
|---|---|---|
|
||||
| rôles | 62 | `roles/*/` |
|
||||
| README de rôles | 62 | `roles/*/README.md` — l'écart avec la ligne au-dessus est la dette |
|
||||
| rôles | 63 | `roles/*/` |
|
||||
| README de rôles | 63 | `roles/*/README.md` — l'écart avec la ligne au-dessus est la dette |
|
||||
| documents | 38 | `docs/*.md` |
|
||||
| pièces d'audit | 30 | `docs/audit/*` |
|
||||
| unités de wiki | 27 | `wiki/*.md` |
|
||||
|
|
|
|||
|
|
@ -56,6 +56,7 @@ Ce que la reconstruction couvre, par capacité :
|
|||
| Resolution | `serveur_resolveur`, `client_resolveur` | UN resolveur recursif par tenant (Unbound), qui recurse depuis la racine et delegue la zone souveraine a PowerDNS. Remplace les N demons locaux d'avant le 2026-08-24 |
|
||||
| Cache du site | `serveur_cache_site` | designe LE cache que les ecosystemes voisins prennent comme amont : Debian telecharge une fois pour toute la fabric, et le cache ne voit que des requetes agregees. Reserve a l'ecosysteme de l'hebergeur |
|
||||
| Forge du genome du site | `serveur_forge_site` | designe LA forge dont les ecosystemes de ce site se reproduisent (D-81), et l'ouvre a eux. Marqueur : `serveur_forgejo` installe. |
|
||||
| Resolveur du site | `serveur_resolveur_site` | designe LE resolveur que les ecosystemes de ce site interrogent tant qu'ils n'ont pas le leur. Marqueur : `serveur_resolveur` installe. |
|
||||
| Agents de flotte | `client_metrique`, `client_journal`, `client_smtp` | collecte et relais sur toute la flotte |
|
||||
|
||||
*Rôles retirés (2026-07-04, supersédés ou hors conception)* : `serveur_sendmail`
|
||||
|
|
|
|||
|
|
@ -62,6 +62,8 @@ couches:
|
|||
# La forge du genome, meme nature : elle vit dans l'underlay de
|
||||
# l'hebergeur et un tenant la CONSOMME sans jamais la deployer.
|
||||
- serveur_forge_site
|
||||
# Le resolveur du site, meme nature : prete aux locataires pendant leur jeunesse.
|
||||
- serveur_resolveur_site
|
||||
- nom: apps
|
||||
raison: "Les applications métier, qui consomment les services (base, SSO, courriel, edge)."
|
||||
groupes:
|
||||
|
|
|
|||
|
|
@ -94,6 +94,8 @@
|
|||
| `serveur_resolveur` | ingress | 53 | udp | flotte | clair | Toute la flotte du tenant résout ici — et nulle part ailleurs. |
|
||||
| `serveur_resolveur` | ingress | 53 | tcp | flotte | clair | Réponses longues et bascule TCP, obligatoires en DNS. |
|
||||
| `serveur_resolveur` | egress | 53 | udp | externe | clair | Récursion depuis les serveurs racine. Aucun transitaire : l'écosystème ne confie ses questions à personne. |
|
||||
| `serveur_resolveur_site` | ingress | 53 | udp | voisins_site | clair | Les écosystèmes de ce site résolvent ici tant qu'ils n'ont pas leur propre résolveur. `client_resolveur` les bascule chez eux dès qu'il existe ; retirer cet emprunt est alors une émancipation. |
|
||||
| `serveur_resolveur_site` | ingress | 53 | tcp | voisins_site | clair | Réponses longues et bascule TCP, obligatoires en DNS. |
|
||||
| `serveur_rspamd` | ingress | 11332 | tcp | localhost | clair | Protocole milter consommé par Postfix co-localisé (analyse + signature DKIM). Local uniquement. |
|
||||
| `serveur_rspamd` | ingress | 11334 | tcp | localhost | clair | Interface de contrôle rspamd (statistiques, apprentissage) en local. |
|
||||
| `serveur_step_ca` | ingress | 8443 | tcp | flotte | tls-requis | ACME + API step-ca : chaque nœud (client_pki) émet/renouvelle ses certificats et récupère la racine. |
|
||||
|
|
@ -104,7 +106,7 @@
|
|||
|
||||
## Synthèse chiffrement
|
||||
|
||||
- **clair** : 35 flux
|
||||
- **clair** : 37 flux
|
||||
- **n-a** : 3 flux
|
||||
- **ssh** : 7 flux
|
||||
- **starttls** : 6 flux
|
||||
|
|
|
|||
15
playbooks/groupes/serveur_resolveur_site.yml
Normal file
15
playbooks/groupes/serveur_resolveur_site.yml
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
---
|
||||
- name: Appliquer le groupe serveur_resolveur_site
|
||||
hosts: serveur_resolveur_site
|
||||
become: true
|
||||
gather_facts: true
|
||||
|
||||
pre_tasks:
|
||||
- name: Vérifier que la cible est Debian
|
||||
ansible.builtin.assert:
|
||||
that:
|
||||
- ansible_facts.distribution == "Debian"
|
||||
fail_msg: "Ce playbook est prévu pour Debian."
|
||||
|
||||
roles:
|
||||
- serveur_resolveur_site
|
||||
|
|
@ -44,6 +44,8 @@
|
|||
import_playbook: groupes/serveur_cache_site.yml
|
||||
- name: Couche services — groupe serveur_forge_site
|
||||
import_playbook: groupes/serveur_forge_site.yml
|
||||
- name: Couche services — groupe serveur_resolveur_site
|
||||
import_playbook: groupes/serveur_resolveur_site.yml
|
||||
- name: Couche services — groupe serveur_resolveur
|
||||
import_playbook: groupes/serveur_resolveur.yml
|
||||
- name: Couche services — groupe serveur_dovecot
|
||||
|
|
|
|||
57
roles/serveur_resolveur_site/README.md
Normal file
57
roles/serveur_resolveur_site/README.md
Normal file
|
|
@ -0,0 +1,57 @@
|
|||
# serveur_resolveur_site
|
||||
|
||||
**Le marqueur du résolveur du site.** Il dit que *ce* résolveur est celui que les
|
||||
écosystèmes de ce site interrogent tant qu'ils n'ont pas le leur, et déclare le flux qui
|
||||
le permet. Il n'installe rien.
|
||||
|
||||
## Le troisième service prêté
|
||||
|
||||
C'est le même patron que `serveur_artefacts` + `serveur_cache_site`, et que
|
||||
`serveur_forgejo` + `serveur_forge_site` : **un installateur, un marqueur**.
|
||||
|
||||
| ce que le site prête | pendant la jeunesse du tenant |
|
||||
|---|---|
|
||||
| les paquets | `serveur_cache_site` |
|
||||
| le génome | `serveur_forge_site` |
|
||||
| **les noms** | **ce rôle** |
|
||||
|
||||
## Ce qu'il a corrigé
|
||||
|
||||
Mesuré le 2026-08-30 sur `infra-pki-01`, première machine à porter l'autorité de
|
||||
certification de Chezlepro :
|
||||
|
||||
```
|
||||
DNS BLOQUÉ un tenant résout chez lui, sa requête ne sort pas
|
||||
443 sortant OK par adresse
|
||||
apt via le cache OK le mandataire résout à sa place
|
||||
```
|
||||
|
||||
`apt` s'en sortait ; **tout le reste était aveugle aux noms** — un `get_url`, un dépôt
|
||||
tiers en HTTPS, un `git clone`. Versionner la clé de signature Smallstep a fait passer une
|
||||
tâche, et l'échec s'est déplacé d'un cran : le dépôt lui-même devait être joint par son
|
||||
nom.
|
||||
|
||||
## Pourquoi pas l'Unbound de la frontière
|
||||
|
||||
Il tourne sur le boîtier **sans être un service géré**, et le plan du site l'avait déjà
|
||||
écrit une fois : *« un processus n'est pas un service »*. `site-dns-01` est déployé par le
|
||||
moteur, vérifié par le harnais, et porte ses flux.
|
||||
|
||||
## Où vit la dérivation
|
||||
|
||||
La liste d'autorisation est calculée par `scripts/site_inventaire.py`, qui connaît le plan
|
||||
du site **et** le registre des tenants. Le rôle, lui, tourne sur une machine qui n'a
|
||||
aucune raison de lire la carte de l'hébergeur — *la dérivation appartient à qui détient
|
||||
les deux sources*.
|
||||
|
||||
Attention au filtre : `resoudre_flux._supernets_voisins()` **exclut** l'instance montée
|
||||
(personne n'est son propre voisin). Le site sert **tous** ceux qu'il héberge, y compris
|
||||
celui qu'on déploie — sans quoi le seul tenant en cours de montage serait précisément
|
||||
celui qu'on prive de résolution.
|
||||
|
||||
## Ce que ce rôle ne fait pas
|
||||
|
||||
Il n'installe rien, n'ouvre aucun port, ne sauvegarde rien. Il attribue une responsabilité
|
||||
— et vérifie deux choses : que le résolveur **écoute**, et que sa liste d'autorisation
|
||||
**admet réellement** les tenants du site. Sans la seconde, la panne chez le locataire
|
||||
ressemblerait à un DNS mort alors que c'est une ACL.
|
||||
11
roles/serveur_resolveur_site/defaults/main.yml
Normal file
11
roles/serveur_resolveur_site/defaults/main.yml
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
---
|
||||
# LE RÉSOLVEUR DU SITE — le marqueur, pas l'installateur.
|
||||
#
|
||||
# `serveur_resolveur` pose Unbound et son ACL ; ce rôle dit que CE résolveur est celui que
|
||||
# les écosystèmes du site interrogent pendant leur jeunesse, et déclare le flux qui le
|
||||
# permet.
|
||||
#
|
||||
# La liste d'autorisation se dérive dans `scripts/site_inventaire.py`, qui connaît le plan
|
||||
# du site ET le registre des tenants. On ne la redéclare pas ici : une seconde source
|
||||
# finirait par diverger de la première, et la divergence se lirait « tout va bien ».
|
||||
serveur_resolveur_site_port: "{{ serveur_resolveur_port | default(53) }}"
|
||||
13
roles/serveur_resolveur_site/meta/authentification.yml
Normal file
13
roles/serveur_resolveur_site/meta/authentification.yml
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
---
|
||||
# Position de ce role dans la directive d'authentification (D-38..D-41).
|
||||
# Voir docs/authentification.md. Gardee par la preuve P29.
|
||||
authentification:
|
||||
portee: sans-auth-humaine
|
||||
mecanisme: aucun
|
||||
formulaire_local: sans-objet
|
||||
secours: "Acces SSH a l'hote"
|
||||
raison: >-
|
||||
Un resolveur ne sert que des machines. Ce qui le protege n'est pas une
|
||||
authentification mais sa LISTE D'AUTORISATION : seuls les supernets des tenants de ce
|
||||
site l'interrogent, et la frontiere le fait respecter. Un resolveur ouvert au monde
|
||||
serait un relais d'amplification — c'est ce risque-la que l'ACL couvre, pas un secret.
|
||||
7
roles/serveur_resolveur_site/meta/empreinte.yml
Normal file
7
roles/serveur_resolveur_site/meta/empreinte.yml
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
---
|
||||
# Empreinte ressources — ce role n'installe rien. Il attribue une responsabilite a un
|
||||
# resolveur qui existe deja, et la rend lisible dans le plan.
|
||||
setops_empreinte:
|
||||
coeurs: 0
|
||||
memoire_mo: 0
|
||||
disque_go: 0
|
||||
43
roles/serveur_resolveur_site/meta/flux.yml
Normal file
43
roles/serveur_resolveur_site/meta/flux.yml
Normal file
|
|
@ -0,0 +1,43 @@
|
|||
---
|
||||
# Flux réseau du RÉSOLVEUR DU SITE. Voir docs/flux-conception.md.
|
||||
#
|
||||
# TROISIÈME SERVICE QUE LE SITE PRÊTE À SES LOCATAIRES, après les paquets
|
||||
# (`serveur_cache_site`) et le génome (`serveur_forge_site`). Même patron, même raison :
|
||||
# un écosystème neuf n'a ni cache, ni forge, ni résolveur — et c'est lui qui doit les
|
||||
# construire.
|
||||
#
|
||||
# CE QUE ÇA A COÛTÉ DE NE PAS L'AVOIR (2026-08-30). `infra-pki-01`, première machine à
|
||||
# porter l'autorité de Chezlepro, n'a pas pu récupérer une clé de signature :
|
||||
#
|
||||
# Echec temporaire dans la resolution du nom
|
||||
#
|
||||
# `apt` s'en sortait par le mandataire du cache, qui résout à sa place. Tout ce qui ne
|
||||
# passe pas par apt — un `get_url`, un `git clone`, une sonde — n'avait aucun moyen de
|
||||
# traduire un nom. Le tenant sortait en 443 par adresse et restait aveugle aux noms.
|
||||
#
|
||||
# POURQUOI PAS L'UNBOUND DE LA FRONTIÈRE. Il tourne sur le boîtier sans être un service
|
||||
# géré, et le plan du site l'a déjà écrit une fois : *un processus n'est pas un service*.
|
||||
# Celui-ci est déployé par le moteur, vérifié par le harnais, et porte ses flux.
|
||||
#
|
||||
# `voisins_site` désigne les autres tenants que CE SITE héberge. Ni `flotte` (mon
|
||||
# écosystème), ni `externe` — un résolveur ouvert au monde est un relais d'amplification.
|
||||
flux:
|
||||
- sens: ingress
|
||||
port: 53
|
||||
protocole: udp
|
||||
pair: voisins_site
|
||||
chiffrement: clair
|
||||
raison: >-
|
||||
Les écosystèmes de ce site résolvent ici tant qu'ils n'ont pas leur propre
|
||||
résolveur. `client_resolveur` les bascule chez eux dès qu'il existe ; retirer cet
|
||||
emprunt est alors une émancipation.
|
||||
# CE RÔLE N'OUVRE AUCUNE ÉCOUTE — il emprunte celle de `serveur_resolveur`, sur le
|
||||
# même hôte. Sans ce mot, P33 y voit deux rôles qui se disputent le port 53.
|
||||
partage: true
|
||||
- sens: ingress
|
||||
port: 53
|
||||
protocole: tcp
|
||||
pair: voisins_site
|
||||
chiffrement: clair
|
||||
raison: "Réponses longues et bascule TCP, obligatoires en DNS."
|
||||
partage: true
|
||||
15
roles/serveur_resolveur_site/meta/main.yml
Normal file
15
roles/serveur_resolveur_site/meta/main.yml
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
---
|
||||
# CE RÔLE EN EXIGE UN AUTRE, ET C'EST À ANSIBLE DE LE SAVOIR.
|
||||
#
|
||||
# `serveur_resolveur_site` n'installe rien : il MARQUE un résolveur comme celui que les
|
||||
# écosystèmes du site interrogent pendant leur jeunesse, et vérifie qu'il écoute vraiment.
|
||||
# Pour cela il lit les variables de `serveur_resolveur`.
|
||||
#
|
||||
# Les valeurs par défaut d'un rôle ne sont en portée que DANS LE PLAY QUI L'INCLUT. Une
|
||||
# dépendance de rôle règle les deux choses d'un coup : Ansible applique l'installateur
|
||||
# AVANT le marqueur, dans le MÊME play, donc ses variables sont en portée — et l'ordre
|
||||
# cesse de dépendre de la façon dont on lance le déploiement.
|
||||
#
|
||||
# Même raisonnement, mot pour mot, que `serveur_cache_site` et `serveur_forge_site`.
|
||||
dependencies:
|
||||
- role: serveur_resolveur
|
||||
51
roles/serveur_resolveur_site/tasks/main.yml
Normal file
51
roles/serveur_resolveur_site/tasks/main.yml
Normal file
|
|
@ -0,0 +1,51 @@
|
|||
---
|
||||
# CE RÔLE MARQUE UN RÉSOLVEUR ; IL N'EN INSTALLE PAS.
|
||||
#
|
||||
# Il présuppose `serveur_resolveur` sur le même hôte et lit ses variables. Le message
|
||||
# qu'on obtiendrait sans lui — « variable non définie » — envoie chercher une faute de
|
||||
# frappe alors que la cause est une déclaration incomplète, un cran au-dessus.
|
||||
- name: Le rôle qui installe le résolveur est-il bien là ?
|
||||
ansible.builtin.assert:
|
||||
that:
|
||||
- serveur_resolveur_reseaux_autorises is defined
|
||||
- serveur_resolveur_reseaux_autorises | length > 0
|
||||
fail_msg: >-
|
||||
`serveur_resolveur_site` marque un résolveur existant, il n'en installe aucun.
|
||||
Cet hôte doit AUSSI porter `serveur_resolveur`. Ajouter les deux rôles à sa
|
||||
déclaration, dans cet ordre.
|
||||
|
||||
# UN MARQUEUR QUI NE VÉRIFIE PAS CE QU'IL DÉSIGNE NE MARQUE RIEN (D-68).
|
||||
#
|
||||
# La liste d'autorisation vient de l'inventaire du site. Si elle n'a pas reçu les
|
||||
# supernets des tenants, ce rôle attribuerait une responsabilité que le service refuse
|
||||
# d'assumer — et la panne, chez le locataire, ressemblerait à un DNS mort alors que c'est
|
||||
# une ACL. C'est exactement ce qui est arrivé au site lui-même quand il a été découpé en
|
||||
# zones (voir `site_inventaire.py`).
|
||||
- name: Quels supernets de tenants ce résolveur doit-il servir ?
|
||||
ansible.builtin.set_fact:
|
||||
serveur_resolveur_site_attendus: >-
|
||||
{{ serveur_resolveur_site_supernets_tenants | default([]) }}
|
||||
|
||||
- name: Exiger que les tenants du site figurent dans la liste d'autorisation
|
||||
ansible.builtin.assert:
|
||||
that:
|
||||
- serveur_resolveur_site_attendus
|
||||
| difference(serveur_resolveur_reseaux_autorises) | length == 0
|
||||
fail_msg: >-
|
||||
Ce résolveur est déclaré résolveur DU SITE, mais sa liste d'autorisation n'admet pas
|
||||
{{ serveur_resolveur_site_attendus
|
||||
| difference(serveur_resolveur_reseaux_autorises) | join(', ') }}.
|
||||
Les locataires seraient refusés, et leur panne ressemblerait à un DNS mort.
|
||||
success_msg: >-
|
||||
{{ serveur_resolveur_site_attendus | length }} supernet(s) de tenant admis.
|
||||
when: serveur_resolveur_site_attendus | length > 0
|
||||
|
||||
# ÉCRIRE, PUIS RELIRE. La seule prise de ce rôle sur le réel est de vérifier que le
|
||||
# service qu'il désigne existe vraiment. Sans ça, il donnerait la résolution du site à un
|
||||
# port que personne n'écoute — et un écosystème neuf attendrait sans savoir pourquoi.
|
||||
- name: Le résolveur écoute-t-il vraiment ?
|
||||
ansible.builtin.wait_for:
|
||||
host: "127.0.0.1"
|
||||
port: "{{ serveur_resolveur_site_port }}"
|
||||
timeout: 30
|
||||
when: not ansible_check_mode
|
||||
|
|
@ -92,6 +92,36 @@ def inventaire() -> dict:
|
|||
if not serveurs:
|
||||
return {"_meta": {"hostvars": {}}}
|
||||
|
||||
# LES SUPERNETS DES TENANTS QUE CE SITE HEBERGE.
|
||||
#
|
||||
# LA MEME SOURCE QUE LES FLUX, UN FILTRE DIFFERENT — et la difference compte.
|
||||
#
|
||||
# `resoudre_flux._supernets_voisins()` EXCLUT l'instance montee : il sert a declarer
|
||||
# les flux d'un tenant vers ses VOISINS, et personne n'est son propre voisin. Le site,
|
||||
# lui, sert TOUS ceux qu'il heberge — y compris celui qu'on pilote au moment ou l'on
|
||||
# genere cet inventaire. Reutiliser la fonction telle quelle aurait prive de
|
||||
# resolution le seul tenant qu'on est en train de deployer, et la panne serait apparue
|
||||
# chez lui seul (constate en la branchant, le 2026-08-30 : Chezlepro manquait).
|
||||
#
|
||||
# On reutilise donc la DECOUVERTE partagee (`decouvrir_du_site`, lecon de P41) sans
|
||||
# son filtre : deux recensements de tenants finiraient par diverger, deux filtres non.
|
||||
#
|
||||
# Rend [] si la carte de l'hebergeur n'est pas montee : le site se decrit alors comme
|
||||
# avant, sans preter son resolveur. Degrader, jamais deviner.
|
||||
try:
|
||||
import devis_reseau
|
||||
from inventory_rules import supernet_de
|
||||
_supernets_tenants = sorted({supernet_de(int(n["index"]))
|
||||
for _nom, _p, n in devis_reseau.decouvrir_du_site()
|
||||
if n.get("index") is not None})
|
||||
except Exception:
|
||||
_supernets_tenants = []
|
||||
|
||||
# Les groupes d'une machine du site, tels que `plan/applications.yml` les declare.
|
||||
def _groupes_de(nom_hote: str) -> set:
|
||||
return {str(a.get("groupe")) for a in applications.values()
|
||||
if str(a.get("hote")) == nom_hote and a.get("groupe")}
|
||||
|
||||
par_reseau = {r.get("nom"): r for r in U.reseaux(u)}
|
||||
# Les sous-reseaux ou vivent REELLEMENT des machines du site : l'etendue de
|
||||
# l'ecosysteme, telle que le plan la dessine.
|
||||
|
|
@ -221,7 +251,24 @@ def inventaire() -> dict:
|
|||
#
|
||||
# Un resolveur ouvert au monde est un relais d'amplification ; celui-ci est
|
||||
# ouvert a son ecosysteme, et a rien d'autre.
|
||||
"serveur_resolveur_reseaux_autorises": _zones_du_site,
|
||||
# LE SITE PRETE SON RESOLVEUR PENDANT LA JEUNESSE DE SES LOCATAIRES
|
||||
# (2026-08-30). L'hote qui porte `serveur_resolveur_site` admet EN PLUS les
|
||||
# supernets des tenants que ce site heberge.
|
||||
#
|
||||
# POURQUOI ICI ET PAS DANS LE ROLE : l'inventaire connait le plan du site ET
|
||||
# le registre des tenants ; le role, lui, tourne sur une machine qui n'a
|
||||
# aucune raison de lire la carte de l'hebergeur. La derivation appartient a
|
||||
# qui detient les deux sources.
|
||||
#
|
||||
# CE QUE CA A COUTE DE NE PAS L'AVOIR : `infra-pki-01`, premiere machine a
|
||||
# porter l'autorite de Chezlepro, ne pouvait resoudre aucun nom. `apt` s'en
|
||||
# sortait par le mandataire du cache, qui resout a sa place — tout le reste
|
||||
# (`get_url`, un depot tiers en HTTPS, un `git clone`) restait aveugle.
|
||||
"serveur_resolveur_reseaux_autorises": (
|
||||
_zones_du_site + _supernets_tenants
|
||||
if "serveur_resolveur_site" in _groupes_de(nom) else _zones_du_site),
|
||||
# Ce que le marqueur verifiera : la liste ci-dessus les admet-elle vraiment ?
|
||||
"serveur_resolveur_site_supernets_tenants": _supernets_tenants,
|
||||
# Un role partage peut avoir besoin de savoir de quel cote il tourne.
|
||||
"setops_site": True,
|
||||
# Le plan du site, pour les roles qui lisent des registres.
|
||||
|
|
|
|||
Loading…
Reference in a new issue