README reduit au role du projet et au demarrage rapide. docs/architecture.md (schemas Mermaid), docs/setup.md, docs/usage.md (154 cibles en 13 familles), docs/README.md (index). Documents ranges en conception/, exploitation/, hebergement/, projet/ ; 598 chemins suivis. QUICKSTART.md, SOLUTION.md, architecture-set-ops.md et la carte fondus puis retires. Preuves : P48 lit l'index et conception/mecanismes.md ; les gardes parcourent docs/ sous- dossiers compris (_documents) ; usage.md suit le Makefile dans les deux sens. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
460 lines
23 KiB
Markdown
460 lines
23 KiB
Markdown
# DNS : interne et public
|
|
|
|
> **Pour qui :** le **mainteneur** du DNS, et l'**exploitant** d'un site qui publie les noms
|
|
> de ses locataires. Le nom du fichier date du temps où il ne couvrait que l'interne.
|
|
|
|
Le service DNS interne est la premiere capacite de plateforme.
|
|
|
|
> **Plancher de resolution independant du DNS.** Le socle (`hosts_statiques`, dans
|
|
> `serveur_debian`) genere `/etc/hosts` sur **chaque** VM depuis l'inventaire : tout
|
|
> l'ecosysteme se resout par nom **meme serveur DNS eteint** (et au bootstrap, avant
|
|
> que PowerDNS ne soit la). PowerDNS devient une **commodite** (zone, externe,
|
|
> dynamique), plus un point de defaillance.
|
|
|
|
## Vue d'ensemble
|
|
|
|
Deux services distincts, qui ne se parlent pas :
|
|
|
|
- **la résolution interne** — chaque écosystème résout ses propres noms (`*.internal`) et
|
|
l'Internet pour ses machines. Rien n'en sort.
|
|
- **le DNS public** — les zones publiques des locataires (`chezlepro.ca`, `technolibre.ca`),
|
|
écrites chez le locataire, servies à l'Internet par **deux sites**, sous deux noms.
|
|
|
|
```mermaid
|
|
flowchart LR
|
|
NET(("Internet"))
|
|
subgraph LOC["Les locataires — vivent au site de Chezlepro"]
|
|
direction TB
|
|
P1["infra-dns-01 · OPS-Chezlepro · 10.17.19.11<br/>pdns@public :5300<br/>primaire caché, signe DNSSEC"]
|
|
P2["infra-dns-01 · OPS-Technolibre · 10.23.19.11<br/>pdns@public :5300<br/>primaire caché, signe DNSSEC"]
|
|
end
|
|
subgraph SC["Site de Chezlepro"]
|
|
direction TB
|
|
BIF["bifrost-1<br/>WAN 69.70.26.62:53"]
|
|
S1["site-dnspub-01 · 10.37.37.21<br/>dnsdist :1053 → PowerDNS :53<br/><b>dns1.chezlepro.ca</b>"]
|
|
BIF -- "redirection :1053" --> S1
|
|
end
|
|
subgraph ST["Site de TechnoLibre"]
|
|
direction TB
|
|
REL["relais · VPS OVH<br/>51.79.45.193"]
|
|
REQ["requin<br/>opt9 RELAIS · opt10 lien"]
|
|
S2["site-dnspub-01 · 10.31.37.21<br/>dnsdist :1053 → PowerDNS :53<br/><b>dns1.technolibre.ca</b>"]
|
|
REL -- "tunnel WireGuard" --> REQ
|
|
REQ -- "redirection :1053" --> S2
|
|
end
|
|
P1 & P2 -- "NOTIFY · AXFR signé TSIG" --> S1
|
|
P1 & P2 -. "NOTIFY · AXFR signé TSIG<br/>par le lien entre les sites" .-> S2
|
|
NET -- "53" --> BIF
|
|
NET -- "53" --> REL
|
|
```
|
|
|
|
| Pièce | Rôle | Où | Section |
|
|
|---|---|---|---|
|
|
| plancher `/etc/hosts` | résout la flotte, DNS éteint | chaque VM | [Trois couches](#trois-couches-et-une-seule-est-optionnelle) |
|
|
| Unbound | LE résolveur d'un écosystème | `infra-dns-01` (locataire), `site-dns-01` (site) | idem |
|
|
| PowerDNS principal | autorité sur la zone `.internal` | boucle locale, derrière Unbound | idem |
|
|
| PowerDNS `pdns@public` | primaire caché des zones publiques | `infra-dns-01` du locataire, `:5300` | [Zones publiques](#zones-publiques) |
|
|
| PowerDNS + dnsdist | serveur de noms public du site, secondaire | `site-dnspub-01` de chaque site | [Deux sites](#deux-sites-deux-serveurs-de-noms) |
|
|
|
|
## Trois couches, et une seule est optionnelle
|
|
|
|
> **Ce paragraphe decrivait le modele d'avant le 2026-08-24** — un Unbound *sur chaque VM*,
|
|
> en opt-in. Ce n'est plus le cas : `client_resolveur` **n'installe plus rien**, et son
|
|
> integration est **universelle**, pas elective.
|
|
|
|
```text
|
|
1. hosts_statiques le PLANCHER : /etc/hosts genere sur chaque VM depuis l'inventaire
|
|
-> l'ecosysteme se resout DNS eteint. Jamais optionnel.
|
|
2. serveur_powerdns l'AUTORITATIF de la zone souveraine (un par ecosysteme)
|
|
3. serveur_resolveur le RECURSIF : UN seul Unbound pour tout le tenant, qui recurse
|
|
depuis la racine et delegue la zone souveraine a PowerDNS
|
|
client_resolveur l'integration : ecrit /etc/resolv.conf pour designer ce resolveur
|
|
```
|
|
|
|
**`client_resolveur` n'installe plus de demon** (2026-08-24). Il en posait un par VM — N
|
|
demons identiques de ~21 Mo pour quelques centaines de requetes. Il ne fait plus qu'une
|
|
chose : **ecrire `/etc/resolv.conf`**. Son integration est marquee `universelle: true`, et
|
|
elle n'a **aucune exemption, pas meme l'hote qui porte le resolveur** : il se sert
|
|
lui-meme. L'ancien nom (`client_unbound`) mentait des lors qu'il n'installait plus Unbound.
|
|
|
|
**L'integration suit l'existence du service, elle ne se declare pas.** Aucun
|
|
`serveur_resolveur` au plan rend `client_resolveur_actif` faux et le role ne touche a rien :
|
|
l'ecosysteme garde la resolution d'amorcage de cloud-init. C'est un choix valide, pas une
|
|
panne.
|
|
|
|
### Le chemin d'une requête interne
|
|
|
|
```mermaid
|
|
flowchart TB
|
|
Q["Une machine du locataire<br/>cherche un nom"] --> H{"/etc/hosts<br/>le plancher"}
|
|
H -- "machine de la flotte" --> A1["adresse — sans DNS"]
|
|
H -- "autre nom" --> U["Unbound · infra-dns-01:53<br/>serveur_resolveur"]
|
|
U -- "*.internal et zone inverse<br/>stub-zone" --> PA["PowerDNS principal<br/>127.0.0.1:5300"]
|
|
U -- "tout le reste<br/>récursion depuis la racine" --> NET(("Internet"))
|
|
Q -. "locataire neuf, sans résolveur à lui" .-> SD["site-dns-01<br/>le résolveur du site<br/>serveur_resolveur_site"]
|
|
```
|
|
|
|
- **Le résolveur est la seule porte.** Quand PowerDNS partage l'hôte d'Unbound, il écoute
|
|
sur la boucle locale (`5300`) : personne ne l'interroge directement (port `derive`,
|
|
`roles/serveur_powerdns/meta/flux.yml`).
|
|
- **Le site prête ses noms aux locataires neufs.** Tant qu'un locataire n'a pas son
|
|
résolveur, `client_resolveur` le fait interroger celui du site (`serveur_resolveur_site`,
|
|
dont la liste d'autorisation se dérive des locataires hébergés), puis le bascule chez lui.
|
|
- **Où mène un nom exposé, vu de l'intérieur** : `resolution_interne` dans
|
|
`plan/domaines.yml`. `service` (le site) : la zone mène au service lui-même — l'edge est
|
|
pour les personnes. `edge` (défaut, les locataires) : la machine passe par l'edge, parce
|
|
que certains services n'existent en HTTPS que derrière lui (Keycloak).
|
|
- **Une machine qui naît** interroge `dns_amorcage` (la patte de la frontière) tant que le
|
|
résolveur de son site n'existe pas.
|
|
|
|
## Groupes
|
|
|
|
```text
|
|
serveur_powerdns -> autoritatif interne (PowerDNS Authoritative)
|
|
serveur_resolveur -> LE recursif du tenant (Unbound), un seul
|
|
client_resolveur -> integration universelle : designe ce resolveur dans /etc/resolv.conf
|
|
```
|
|
|
|
## Zone initiale
|
|
|
|
La zone initiale est :
|
|
|
|
```text
|
|
exemple.internal
|
|
```
|
|
|
|
Elle est definie dans :
|
|
|
|
```text
|
|
instance/inventories/<inventaire>/group_vars/serveur_powerdns.yml
|
|
```
|
|
|
|
## Enregistrement automatique
|
|
|
|
Le role `serveur_powerdns` genere la zone a partir de l'inventaire.
|
|
|
|
Les hotes qui remplissent ces conditions sont ajoutes automatiquement :
|
|
|
|
```text
|
|
hote dans hotes_actifs
|
|
ansible_host defini
|
|
```
|
|
|
|
Exemple :
|
|
|
|
```text
|
|
web-frontal-01 ansible_host=10.0.2.31
|
|
-> web-frontal-01.exemple.internal A 10.0.2.31
|
|
```
|
|
|
|
Les enregistrements additionnels explicites sont dans `serveur_powerdns_records`.
|
|
|
|
## Backend PowerDNS
|
|
|
|
Le premier jalon utilise le backend BIND de PowerDNS.
|
|
|
|
Raison :
|
|
|
|
- pas de dependance prematuree a PostgreSQL ;
|
|
- zone lisible et generee par Ansible ;
|
|
- idempotence simple ;
|
|
- bon point de depart pour la supervision.
|
|
|
|
Quand `serveur_postgresql` sera stable, il sera possible de migrer vers un backend SQL si le besoin operationnel le justifie.
|
|
|
|
## La bascule de `/etc/resolv.conf` est protegee
|
|
|
|
Le role `client_resolveur` ne bascule `/etc/resolv.conf` qu'apres avoir verifie que le
|
|
resolveur repond **deja** — pour l'interne *et* pour l'Internet :
|
|
|
|
```yaml
|
|
client_resolveur_apply: true
|
|
client_resolveur_confirm: true
|
|
```
|
|
|
|
Sans ces deux variables, le role prépare Unbound mais ne modifie pas `/etc/resolv.conf`.
|
|
|
|
Cette protection est volontaire : une mauvaise configuration DNS peut couper la resolution
|
|
de noms. C'est la dependance la plus dangereuse du lot — basculer un hote sur un resolveur
|
|
qui n'est pas encore pret le rend **muet**, et le runner qui devrait reparer tombe avec les
|
|
autres. Le playbook de groupe applique donc l'hote qui *porte* le resolveur avant ceux qui
|
|
s'y adressent, et la preuve **P44** refuse tout ecart entre cette declaration et lui.
|
|
|
|
## Le piege qui a coute deux jours : la racine signee nie notre TLD
|
|
|
|
`internal.` n'est **pas delegue dans la racine**, qui est signee : elle rend donc une preuve
|
|
NXDOMAIN *validee* pour ce TLD. Or `harden-below-nxdomain` — **actif par defaut** dans
|
|
Unbound — tient ce « non » pour prouve et repond NXDOMAIN pour **tout** nom sous
|
|
`internal.` depuis son cache, **sans jamais interroger la `stub-zone`** declaree plus bas.
|
|
|
|
La delegation etait correcte. L'autoritatif repondait juste. Pas une requete ne lui
|
|
parvenait.
|
|
|
|
Ce qui declenche l'empoisonnement : n'importe quelle question sur un nom inexistant sous
|
|
`internal.` — y compris la zone d'**un autre ecosysteme**, que ce resolveur ne sert pas et
|
|
va donc chercher a la racine. Sur un resolveur partage, ca arrive en permanence.
|
|
|
|
Et la panne parait **intermittente** : au redemarrage le cache est vide, tout fonctionne, on
|
|
conclut que c'est regle. Le remede mesure (2026-09-02) est `harden-below-nxdomain: no` dans
|
|
`roles/serveur_resolveur/templates/setops.conf.j2` — **pas** `aggressive-nsec`, qui traite
|
|
un autre symptome.
|
|
|
|
## Surveillance a prevoir
|
|
|
|
Les premiers checks utiles :
|
|
|
|
- service `pdns` actif ;
|
|
- port UDP/TCP 53 disponible ;
|
|
- SOA de `exemple.internal` resoluble ;
|
|
- enregistrement `ns1.exemple.internal` resoluble ;
|
|
- enregistrements des hotes actifs presents ;
|
|
- serial de zone attendu ;
|
|
- latence de resolution.
|
|
|
|
## Zones publiques
|
|
|
|
> Ajouté le 2026-09-16. Le mode `primaire-cache` était **validé par le schéma et consommé par
|
|
> rien** depuis sa création — *une autorité qu'on s'attribue sans l'exercer est une panne
|
|
> différée*.
|
|
|
|
`plan/domaines.yml` porte un champ `autorite` par domaine. Deux valeurs sont exercées :
|
|
|
|
| `autorite` | Ce que ça veut dire | Qui sert la zone |
|
|
|---|---|---|
|
|
| `auto-heberge` | zone **interne** (`.internal`), servie à l'écosystème seul | l'instance principale de `serveur_powerdns`, sur la boucle locale |
|
|
| `primaire-cache` | zone **publique** : le locataire l'écrit, le site la sert | `pdns@public` chez le locataire (primaire caché), `serveur_dns_public` au site (secondaire public) |
|
|
|
|
`delegue` est accepté par le validateur et **n'est consommé par aucun rôle**. Il ne faut pas
|
|
le déclarer en croyant qu'il fait quelque chose.
|
|
|
|
**Le locataire écrit, le site sert, un site pair réplique.** Le primaire reste chez le
|
|
locataire : quand il part, il emporte sa zone. Le détail — et ce que l'épreuve de PowerDNS a
|
|
appris — est dans `roles/serveur_dns_public/README.md`.
|
|
|
|
Trois règles, chacune payée ou mesurée :
|
|
|
|
- **TSIG seul ouvre le transfert.** `allow-axfr-ips` et TSIG sont *alternatifs* : une adresse
|
|
listée obtient la zone sans signature. L'instance publique n'autorise que la boucle locale.
|
|
- **Une instance à part pour le public.** Faire écouter l'instance principale sur l'adresse
|
|
de l'hôte aurait permis au serveur public du site d'interroger la zone `.internal`.
|
|
- **Le serial suit le contenu.** Un serial figé ferait garder au secondaire l'ancienne zone
|
|
pour toujours.
|
|
|
|
**Rien n'est exposé à Internet en phase 1.** P82 refuse d'exposer le serveur public tant
|
|
que toutes ses zones ne sont pas signées DNSSEC.
|
|
|
|
### Ce qu'une zone publique porte
|
|
|
|
> Ajouté le 2026-09-16, phase 2. Une zone qui ne portait que ses expositions web aurait coupé
|
|
> le courriel de production à la minute où le registraire l'aurait suivie.
|
|
|
|
Chaque zone `primaire-cache` déclare ses **enregistrements** au plan (`enregistrements:` dans
|
|
`plan/domaines.yml`) : A, AAAA, CNAME, MX, TXT, CAA. `valider_domaines` refuse une adresse
|
|
privée, une cible incomplète, un CNAME à l'apex ou à côté d'un autre type, un MX sans
|
|
priorité, un TXT avec guillemets ou hors ASCII, un nom écrit en absolu (`mx.chezlepro.ca`
|
|
sous `chezlepro.ca`), et tout enregistrement dans une zone que Set-OPS n'écrit pas.
|
|
|
|
Le **SOA et les NS** ne se déclarent pas : ils désignent le serveur de noms du site, sous le
|
|
nom que **le site** déclare (`dns_public_nom`, contrat du site, reçu par chaque locataire).
|
|
Seule la zone qui contient ce nom porte son A. Le premier choix, `ns1.<zone>`, aurait déplacé
|
|
en silence un `ns1.chezlepro.ca` qui existe en production vers une autre adresse.
|
|
|
|
**Avant de basculer : `make dns-bascule-devis`.** Il compare le plan au DNS en service, nom
|
|
par nom et type par type, sur les noms déclarés et une liste de sondes usuelles. Il rend ce
|
|
qui serait **perdu**, **changé**, **ajouté**, ou **abandonné en connaissance de cause** (les
|
|
marques `heritage=external-dns`), et les préalables : le serveur de noms doit déjà résoudre
|
|
publiquement, et il en faut **deux** (exigence du registre `.ca`). Une mesure qui échoue rend
|
|
« mesure impossible », jamais « absent ». Sa limite est écrite à chaque rapport : sans
|
|
transfert de zone, un export chez le fournisseur actuel reste la seule preuve d'exhaustivité.
|
|
|
|
Le devis sonde aussi **la colle de chaque serveur désigné** — ceux du site et ceux des pairs
|
|
([Deux sites](#deux-sites-deux-serveurs-de-noms)).
|
|
|
|
Au 2026-10-14 : les deux zones sont servies par les deux sites, signées, au même serial ;
|
|
**aucune n'est basculée chez son registraire**. `chezlepro.ca` reproduit la production
|
|
(rien ne serait perdu). La décision pour `technolibre.ca` revient au responsable désigné de
|
|
TechnoLibre.
|
|
|
|
### Signature DNSSEC : le locataire signe, avec la clé de sa voûte
|
|
|
|
> Ajouté le 2026-09-16, phase 2. Éprouvé sur les machines réelles avant d'être codé.
|
|
|
|
`dnssec: true` sur une zone `primaire-cache` la fait signer **par le primaire du locataire**,
|
|
avec **une** clé CSK ECDSA P-256 tenue dans **sa** voûte (`vault_dnssec_<zone>`). Le site ne
|
|
détient aucune clé : il reçoit la zone déjà signée et la sert telle quelle (PowerDNS pose
|
|
`PRESIGNED` au transfert).
|
|
|
|
| Geste | Commande | Écrit ? |
|
|
|---|---|---|
|
|
| Créer la clé d'une zone | `python3 scripts/dnssec.py generer <zone>` | la voûte du locataire (copie de sûreté chiffrée, relue avant et après) |
|
|
| Lire le DS à remettre au registraire | `make dnssec-ds` | rien — calculé depuis la voûte, sans la machine |
|
|
| Voûte, plan et registre concordent-ils ? | `make dnssec-verifier` | rien |
|
|
|
|
Pourquoi la clé ne naît pas sur la machine : une reconstruction depuis zéro signerait avec
|
|
une **autre** clé, le DS du registraire ne correspondrait plus, et le domaine deviendrait
|
|
**BOGUS** pour tout résolveur validant, sans qu'aucune machine soit en panne.
|
|
|
|
Trois règles, chacune mesurée :
|
|
|
|
- **Changer la clé ou retirer la signature pendant qu'un DS est publié est refusé** par l'outil
|
|
de la machine (`setops-dnssec-zone`) comme par `dnssec.py`. Une mesure du DS qui échoue vaut
|
|
refus.
|
|
- **Le serial servi est l'heure (`SOA-EDIT EPOCH`).** PowerDNS renouvelle ses signatures chaque
|
|
semaine (inception au début de la semaine précédente, expiration deux semaines après le
|
|
début de la semaine courante). `INCEPTION-EPOCH`, essayé d'abord, ne dépassait notre serial
|
|
qu'au moment où les signatures du site expiraient. Avec l'heure, le site retire la zone à
|
|
chaque rafraîchissement ; les signatures servies gardent 7 à 14 jours.
|
|
- **L'état DNSSEC fait partie du corps de la zone.** Signer ne change aucun enregistrement :
|
|
sans la ligne `; DNSSEC : …`, le serial n'aurait pas bougé et le site aurait gardé la version
|
|
non signée.
|
|
|
|
La sonde `zones-publiques` du site alerte sur une zone signée servie **nue**, avertit sous
|
|
6 jours de signature restante, et alerte sous 3.
|
|
|
|
**Ordre de la mise en service** : basculer les serveurs de noms (zone signée, sans DS : le
|
|
domaine reste « non sécurisé », pas cassé), vérifier, **puis** remettre le DS au registraire.
|
|
Jamais l'inverse.
|
|
|
|
## Deux sites, deux serveurs de noms
|
|
|
|
> Ajouté le 2026-10-14. Le registre des `.ca` exige deux serveurs de noms ; deux serveurs
|
|
> sur le même réseau tomberaient ensemble. Le second est au site de TechnoLibre, tenu par une
|
|
> autre organisation, sur un autre réseau.
|
|
|
|
**Chaque site sert les zones publiques de tous les locataires qu'il connaît : les siens, et
|
|
ceux de son pair.** Les deux serveurs sont **secondaires** l'un comme l'autre, et tirent
|
|
chacun la zone **directement du primaire du locataire** — jamais l'un de l'autre
|
|
(`disable-axfr=yes` au site). Un site qui tombe n'emporte donc pas la copie de l'autre.
|
|
|
|
| Site | Serveur | Nom public | Adresse publique | Chemin depuis l'Internet |
|
|
|---|---|---|---|---|
|
|
| Chezlepro | `site-dnspub-01` · `10.37.37.21` | `dns1.chezlepro.ca` | `69.70.26.62` | WAN de `bifrost-1` → `:1053` |
|
|
| TechnoLibre | `site-dnspub-01` · `10.31.37.21` | `dns1.technolibre.ca` | `51.79.45.193` | relais (VPS) → tunnel `opt9` de `requin` → `:1053` |
|
|
|
|
### Publier une zone
|
|
|
|
```mermaid
|
|
sequenceDiagram
|
|
autonumber
|
|
participant X as Exploitant du locataire
|
|
participant P as pdns@public<br/>infra-dns-01 du locataire
|
|
participant C as site-dnspub-01<br/>site de Chezlepro
|
|
participant T as site-dnspub-01<br/>site de TechnoLibre
|
|
X->>P: rôle serveur_powerdns — la zone est rendue depuis le plan
|
|
Note over P: SOA et NS = dns1 du site + dns1 de chaque pair<br/>serial = suit le contenu · signée avec la clé de la voûte
|
|
P-)C: NOTIFY (only-notify : les seuls serveurs du contrat)
|
|
P-)T: NOTIFY, par le lien entre les sites
|
|
C->>P: SOA ? (UDP 5300)
|
|
C->>P: AXFR signé TSIG (TCP 5300)
|
|
P-->>C: zone déjà signée — servie telle quelle (PRESIGNED)
|
|
T->>P: SOA ? puis AXFR signé TSIG, par le lien
|
|
P-->>T: zone déjà signée
|
|
```
|
|
|
|
- **Le locataire écrit, les sites servent.** Les deux sites ne détiennent ni la zone en
|
|
source, ni la clé DNSSEC.
|
|
- **`only-notify` et `also-notify`** bornent les notifications aux adresses du contrat du
|
|
site (`dns_public_site` et `dns_public_pairs`). Sans eux, le primaire notifierait les
|
|
adresses publiques de ses NS, depuis l'intérieur du locataire.
|
|
- **Le transfert passe par le lien** quand le secondaire est sur l'autre site : la frontière
|
|
du site où vit le locataire laisse entrer le transfert et sortir les NOTIFY ; celle du site
|
|
qui réplique pose une route `/32` vers chaque primaire. Le devis les dérive de la fiche que
|
|
porte chaque locataire (où il vit), et P24 juge ces routes à part.
|
|
|
|
### Une requête de l'Internet
|
|
|
|
```mermaid
|
|
sequenceDiagram
|
|
participant R as Résolveur sur Internet
|
|
participant F as Frontière<br/>bifrost-1 ou requin
|
|
participant D as dnsdist :1053
|
|
participant A as PowerDNS :53
|
|
R->>F: chezlepro.ca MX ? (port 53)
|
|
Note over F: TechnoLibre : arrive par le relais et le tunnel opt9,<br/>et repart par lui (reply-to RELAIS_GW)
|
|
F->>D: redirection vers :1053
|
|
Note over D: refuse NOTIFY, UPDATE, AXFR · tronque ANY en UDP<br/>débit borné par /24
|
|
D->>A: requête admise
|
|
A-->>D: réponse signée
|
|
D-->>R: réponse
|
|
```
|
|
|
|
**Sans `reply-to`, la réponse du site de TechnoLibre se perdrait** : elle repartirait par la
|
|
route par défaut, le WAN, qui n'a qu'une adresse privée en amont (mesuré le 2026-10-13).
|
|
|
|
### Les flux
|
|
|
|
| Flux | De | Vers | Port | Chemin |
|
|
|---|---|---|---|---|
|
|
| Requête publique | Internet | dnsdist du site | 53 → 1053, UDP et TCP | redirection de la frontière ; par le relais au site de TechnoLibre |
|
|
| Requête admise | dnsdist | PowerDNS du site | 53, boucle de l'hôte | local |
|
|
| NOTIFY | `pdns@public` du locataire | PowerDNS de chaque site | 53/UDP | local ; par le lien vers l'autre site |
|
|
| SOA, puis AXFR TSIG | PowerDNS de chaque site | `pdns@public` du locataire | 5300, UDP puis TCP | local ; par le lien depuis l'autre site |
|
|
| Résolution interne | machine du locataire | Unbound de son écosystème | 53 | dans l'écosystème |
|
|
| Zone `.internal` | Unbound | PowerDNS principal | 5300, boucle locale | local |
|
|
| Récursion | Unbound | Internet | 53 | sortie de la frontière |
|
|
|
|
Ces flux sont déclarés par les rôles (`meta/flux.yml` de `serveur_dns_public`,
|
|
`serveur_powerdns`, `serveur_resolveur`) et par le lien (`opnsense_liens_sites`) ; le devis
|
|
de la frontière et le pare-feu Proxmox en dérivent leurs règles. Aucun n'est écrit à la
|
|
main sur une frontière.
|
|
|
|
### Ce qui se déclare, et où
|
|
|
|
| Clé | Fichier | Ce qu'elle décide |
|
|
|---|---|---|
|
|
| `autorite: primaire-cache`, `dnssec`, `enregistrements` | `plan/domaines.yml` du **locataire** | la zone est publique, signée, et ce qu'elle porte |
|
|
| `dns_public_nom` | `plan/10-intrants.yml` du **site** | le nom de SON serveur de noms (`dns1.<domaine de l'organisation>`) |
|
|
| `dns_public_pairs` | plan du **site**, des deux côtés | le site pair qui réplique ; `inactif` n'émet rien |
|
|
| `opnsense_liens_sites` | `opnsense.yml` du **site** | le tunnel que prennent NOTIFY et transferts |
|
|
| `opnsense_wan_ip` | `opnsense.yml` du **site** | l'adresse publique de son serveur de noms |
|
|
|
|
Le **contrat du site** (`make site-intrants`) en tire, pour chaque locataire, ce qu'il
|
|
dépose dans sa fiche (`fiche-site.yml`) : `dns_public_site`, `dns_public_pairs`,
|
|
`dns_public_nom` et `dns_public_noms_pairs` (le nom et l'adresse publique du serveur de
|
|
chaque pair, lus dans **son** plan et **sa** carte, par les mêmes fonctions que pour le site
|
|
lui-même).
|
|
|
|
### Les NS d'une zone
|
|
|
|
```mermaid
|
|
flowchart LR
|
|
PC["plan du site de Chezlepro<br/>dns_public_nom: dns1.chezlepro.ca"] --> K["contrat du site<br/>fiche du locataire"]
|
|
PT["plan du site de TechnoLibre<br/>dns_public_nom: dns1.technolibre.ca<br/>+ opnsense_wan_ip"] -- "dns_public_noms_pairs" --> K
|
|
K --> Z["zone-publique.db.j2<br/>chez le locataire"]
|
|
Z --> NS["chezlepro.ca — NS dns1.chezlepro.ca, NS dns1.technolibre.ca<br/>A de dns1.chezlepro.ca : seulement dans chezlepro.ca<br/>A de dns1.technolibre.ca : seulement dans technolibre.ca"]
|
|
```
|
|
|
|
- **Un site, un nom** : `dns1` sous le domaine de l'organisation qui tient le site. Pas
|
|
`ns1`/`ns2`, qui existent en production à d'autres adresses.
|
|
- **Seule la zone qui contient un nom en porte le A**, à l'adresse publique de **son** site.
|
|
Les autres zones ne le désignent que par son nom.
|
|
- **Sans pair, le rendu est identique octet pour octet** à celui d'un site seul.
|
|
|
|
### Les gardes
|
|
|
|
| Garde | Où | Ce qu'elle refuse |
|
|
|---|---|---|
|
|
| relation des deux côtés | P82 (`prouver._dns_paires`) | un pair déclaré d'un seul côté — le second serveur serait vide sans que personne le voie |
|
|
| un pair nomme son serveur | P82 (`prouver._dns_paires`) | un pair actif sans `dns_public_nom` — ses zones n'en désigneraient qu'un, alors qu'il les sert |
|
|
| signé avant exposé | P82 | exposer un serveur public dont une zone n'est pas signée |
|
|
| nom et adresse du pair | `serveur_powerdns` (`zones-publiques.yml`) | un nom mal formé, une adresse privée, un nom égal au nôtre, un nom redéclaré |
|
|
| contrat déposé | `make site-intrants-verifier` | une fiche de locataire qui diffère du contrat (une adresse faussée la rend NON CONFORME) |
|
|
| routes du lien | P24 | une route `/32` qu'aucune règle du lien n'utilise |
|
|
| zone servie nue | sonde `zones-publiques` | une zone signée servie sans signature ; signatures sous 6 jours (avertit), sous 3 (alerte) |
|
|
|
|
### Basculer chez un registraire
|
|
|
|
L'ordre, pour chaque domaine :
|
|
|
|
1. **`make dns-bascule-devis`** — rien ne serait perdu, et chaque serveur désigné résout déjà
|
|
publiquement.
|
|
2. **La colle** chez le registraire du domaine qui contient le nom : `dns1.chezlepro.ca`
|
|
chez celui de `chezlepro.ca`, `dns1.technolibre.ca` chez celui de `technolibre.ca`.
|
|
3. **Les deux NS** chez le registraire de la zone basculée. Zone signée, sans DS : le domaine
|
|
reste « non sécurisé », pas cassé.
|
|
4. **Vérifier** depuis l'Internet, sur les deux adresses.
|
|
5. **Puis seulement le DS** (`make dnssec-ds`). Jamais l'inverse.
|
|
|
|
**État au 2026-10-14** : les deux sites servent `chezlepro.ca` et `technolibre.ca` au même
|
|
serial, chacune avec deux NS signés. **Ni la colle ni les NS ne sont encore chez les
|
|
registraires.**
|