docs/dns-interne.md couvre desormais le DNS public sur deux sites : vue d'ensemble, chemin d'une requete interne, publication d'une zone, requete de l'Internet, NS d'une zone (cinq schemas Mermaid), et les tableaux des flux, des declarations, des gardes et de la bascule. README de serveur_powerdns et de serveur_dns_public a jour ; le wiki y renvoie. 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.**
|