Set-OPS-Public/roles/serveur_powerdns/templates/zone.db.j2
Daniel Allaire 1bd211d62b resolution_interne : entre machines du site, chaque nom mene a son service
L edge expose les interfaces web aux personnes ; les VM du site vont directement au
service. Declare par domaine (service | edge, defaut edge) ; plancher, zone et certificat
du service suivent. Pose au site : 35 planchers sur 35 conformes ; locataires inchanges.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-29 13:25:37 -04:00

83 lines
4.5 KiB
Django/Jinja

$ORIGIN {{ serveur_powerdns_zone }}.
$TTL {{ serveur_powerdns_ttl }}
@ IN SOA {{ serveur_powerdns_nameserver }}.{{ serveur_powerdns_zone }}. {{ serveur_powerdns_contact }}. (
{{ serveur_powerdns_serial }}
{{ serveur_powerdns_soa_refresh }}
{{ serveur_powerdns_soa_retry }}
{{ serveur_powerdns_soa_expire }}
{{ serveur_powerdns_soa_minimum }}
)
@ IN NS {{ serveur_powerdns_nameserver }}.{{ serveur_powerdns_zone }}.
{{ serveur_powerdns_nameserver }} IN A {{ hostvars[groups['serveur_powerdns'][0]].ansible_host | default('127.0.0.1') }}
{# LE PLAN L'EMPORTE SUR LE DEFAUT DU ROLE (2026-08-25).
`serveur_powerdns_records` porte un defaut de commodite : `dns IN CNAME ns1`. Le plan
du site, lui, expose `dns.genese.internal` pour `serveur_resolveur` — ce qui produit un
`dns IN A`. Un nom ne peut pas porter a la fois un CNAME et un A : `named-checkzone` a
refuse la zone (« CNAME and other data »), et PowerDNS ne l'a pas servie du tout.
La garde a bien fonctionne — elle a arrete une zone cassee avant qu'elle soit servie.
Mais elle laissait le DNS du site sans zone tant que la collision durait.
On ecarte donc le record statique quand le plan expose le meme nom. C'est le sens de
marche du depot : ce qui est DECLARE AU PLAN prime sur ce qu'un role suppose par
defaut. Les deux pointaient de toute facon vers la meme machine. #}
{% set _exposes = [] %}
{% if serveur_powerdns_publier_expositions | default(false) %}
{% for expo in serveur_powerdns_expositions | default([]) if expo.domaine == serveur_powerdns_zone %}
{% set _ = _exposes.append(expo.fqdn[:-(serveur_powerdns_zone | length + 1)]) %}
{% endfor %}
{% endif %}
{% for record in serveur_powerdns_records if record.name not in _exposes %}
{{ record.name }} IN {{ record.type }} {{ record.value }}
{% endfor %}
{% set emitted = [] %}
{% for group_name in serveur_powerdns_inventory_groups %}
{% for host_name in groups.get(group_name, []) | sort %}
{% if hostvars[host_name].ansible_host is defined and host_name not in emitted %}
{{ host_name }} IN A {{ hostvars[host_name].ansible_host }}
{% set _ = emitted.append(host_name) %}
{% endif %}
{% endfor %}
{% endfor %}
{% if serveur_powerdns_publier_expositions | default(false) %}
{% for expo in serveur_powerdns_expositions | default([]) if expo.domaine == serveur_powerdns_zone %}
{% set cibles = [expo.hote] if expo.resout_vers | default('edge') == 'service' else groups.get(expo.edge, []) %}
{% if cibles and cibles[0] in hostvars and hostvars[cibles[0]].ansible_host is defined %}
{{ expo.fqdn[:-(serveur_powerdns_zone | length + 1)] }} IN A {{ hostvars[cibles[0]].ansible_host }}
{% endif %}
{% endfor %}
{% endif %}
{# L'EMPREINTE DU PLANCHER (2026-09-29). Le plancher `/etc/hosts` de chaque machine et
cette zone derivent du meme plan, mais pas au meme moment : chacun suit le plan quand son
role est rejoue. Le 2026-09-29, la console manquait aux planchers des deux locataires
pendant que la zone la portait. Pour que chaque machine puisse comparer SANS transfert de
zone (AXFR reste ferme), la zone publie ce que le plancher DOIT contenir pour ce domaine :
le nombre de paires « nom adresse » et leur empreinte sha256.
LE MEME ENSEMBLE QUE `hosts_statiques/templates/hosts.j2`, pas celui de cette zone : la
flotte ACTIVE ET PLANIFIEE (le plancher resout un clone avant qu'il soit actif ; la zone
non), plus les expositions de ce domaine. `test_empreinte_plancher.py` rend les deux
gabarits sur un meme inventaire et exige la meme empreinte : si l'un change seul, le
test le dit. La sonde `plancher` (role `client_sante`) calcule le cote machine. #}
{% set _plancher = [] %}
{% for h in (groups.get('hotes_actifs', []) + groups.get('hotes_planifies', [])) | unique | sort %}
{% if hostvars[h].ansible_host is defined %}
{% set _ = _plancher.append(h ~ '.' ~ serveur_powerdns_zone ~ ' ' ~ hostvars[h].ansible_host) %}
{% endif %}
{% endfor %}
{% if serveur_powerdns_publier_expositions | default(false) %}
{% for expo in serveur_powerdns_expositions | default([]) if expo.domaine == serveur_powerdns_zone %}
{% set cibles = [expo.hote] if expo.resout_vers | default('edge') == 'service' else groups.get(expo.edge, []) %}
{% if cibles and cibles[0] in hostvars and hostvars[cibles[0]].ansible_host is defined %}
{% set _ = _plancher.append(expo.fqdn ~ ' ' ~ hostvars[cibles[0]].ansible_host) %}
{% endif %}
{% endfor %}
{% endif %}
{% set _paires = _plancher | unique | sort %}
_plancher IN TXT "n={{ _paires | length }} sha256={{ _paires | join('\n') | hash('sha256') }}"