Set-OPS-Public/roles/serveur_dns_public/tasks/main.yml
Daniel Allaire 788c5073dc DNS public, phase 1 : le locataire ecrit, le site sert
Role serveur_dns_public (secondaire public, transfert signe TSIG, aucune zone interne) ;
serveur_powerdns exerce enfin autorite primaire-cache dans une instance pdns@public a part,
pour que le site ne puisse jamais interroger la zone .internal. Relations derivees des plans
des locataires, mots de flux dns_public_site et primaires_dns_locataires. Eprouve avant
d ecrire : allow-axfr-ips et TSIG sont alternatifs, le primaire notifie aussi ses NS, le
serial fige aurait gele le secondaire. P82 refuse l exposition sans DNSSEC.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-16 18:08:20 -04:00

163 lines
5.7 KiB
YAML

---
# --- LES REFUS, AVANT DE TOUCHER A QUOI QUE CE SOIT -----------------------------------
# UNE ZONE INTERNE N'ARRIVE JAMAIS ICI. Ce serveur repond a Internet : une zone `.internal`
# servie ici serait la carte des machines d'un locataire offerte au monde. La derivation ne
# retient que `primaire-cache` ; ce refus garde le cas ou elle se tromperait.
- name: Refuser une zone interne sur le serveur public
ansible.builtin.assert:
that:
- >-
(serveur_dns_public_relations | map(attribute='zones') | flatten
| select('search', '\.internal\.?$') | list | length) == 0
fail_msg: >-
Une zone `.internal` est destinee au serveur DNS PUBLIC. Elle publierait la carte
des machines d'un locataire. Seules les zones `autorite: primaire-cache` y ont leur
place.
quiet: true
# UNE RELATION SANS SECRET N'EST PAS UNE RELATION NON SIGNEE : C'EST UN REFUS. Un
# secondaire qui tirerait sans cle ne recevrait rien (le primaire n'autorise aucune
# adresse) — ou pire, recevrait tout le jour ou quelqu'un ajouterait son adresse a
# `allow-axfr-ips`, et TSIG deviendrait decoratif sans le moindre message.
- name: Exiger un secret TSIG pour chaque relation
ansible.builtin.assert:
that:
- lookup('vars', item.secret_var, default='') | length > 0
fail_msg: >-
{{ item.ecosysteme }} : `{{ item.secret_var }}` est absent de la voute du site. Sans
lui, la zone de ce locataire ne se transfere pas — et aucun repli non signe n'est
prevu, par construction.
quiet: true
loop: "{{ serveur_dns_public_relations }}"
loop_control:
label: "{{ item.ecosysteme }}"
# --- L'INSTALLATION -------------------------------------------------------------------
- name: Installer PowerDNS Authoritative
ansible.builtin.apt:
name: "{{ serveur_dns_public_paquets }}"
state: present
update_cache: true
cache_valid_time: 3600
# Le secondaire ECRIT les zones qu'il recoit : le repertoire appartient a `pdns`.
- name: Créer le répertoire des zones secondaires
ansible.builtin.file:
path: "{{ serveur_dns_public_zones_dir }}"
state: directory
owner: pdns
group: pdns
mode: "0750"
- name: Déployer la configuration du serveur public
ansible.builtin.template:
src: setops-dns-public.conf.j2
dest: /etc/powerdns/pdns.d/setops-dns-public.conf
owner: root
group: pdns
mode: "0640"
notify: Redemarrer le DNS public
- name: Déployer l'index des zones secondaires
ansible.builtin.template:
src: bindbackend.conf.j2
dest: "{{ serveur_dns_public_bind_config }}"
owner: root
group: pdns
mode: "0640"
notify: Redemarrer le DNS public
- name: Créer la base des clés TSIG
ansible.builtin.command:
cmd: "pdnsutil create-bind-db {{ serveur_dns_public_dnssec_db }}"
creates: "{{ serveur_dns_public_dnssec_db }}"
when: not ansible_check_mode
- name: Confier la base des clés à pdns
ansible.builtin.file:
path: "{{ serveur_dns_public_dnssec_db }}"
owner: pdns
group: pdns
mode: "0640"
when: not ansible_check_mode
# --- LES CLES TSIG, UNE PAR RELATION --------------------------------------------------
#
# On COMPARE avant d'ecrire, et c'est ce qui rend la rotation possible : un secret change
# dans la voute doit remplacer l'ancien. Importer seulement quand le nom manque laisserait
# la vieille cle en place pour toujours, et le transfert echouerait sans rien dire.
- name: Relever les clés TSIG en place
ansible.builtin.command:
cmd: pdnsutil list-tsig-keys
register: serveur_dns_public_cles
changed_when: false
no_log: true
when: not ansible_check_mode
- name: Poser ou remplacer la clé TSIG de chaque relation
ansible.builtin.command:
argv:
- pdnsutil
- import-tsig-key
- "{{ item.cle_tsig }}"
- hmac-sha256
- "{{ lookup('vars', item.secret_var) }}"
loop: "{{ serveur_dns_public_relations }}"
loop_control:
label: "{{ item.ecosysteme }} → {{ item.cle_tsig }}"
when:
- not ansible_check_mode
- (item.cle_tsig ~ '. hmac-sha256. ' ~ lookup('vars', item.secret_var))
not in (serveur_dns_public_cles.stdout | default(''))
changed_when: true # n'arrive ici que si la cle manque ou differe
no_log: true
notify: Redemarrer le DNS public
- name: Relever les métadonnées de transfert
ansible.builtin.command:
argv: [pdnsutil, get-meta, "{{ item.1 }}", AXFR-MASTER-TSIG]
loop: "{{ serveur_dns_public_relations | subelements('zones') }}"
loop_control:
label: "{{ item.1 }}"
register: serveur_dns_public_meta
changed_when: false
failed_when: false
when: not ansible_check_mode
- name: Exiger la signature TSIG sur chaque transfert entrant
ansible.builtin.command:
argv: [pdnsutil, set-meta, "{{ item.item.1 }}", AXFR-MASTER-TSIG, "{{ item.item.0.cle_tsig }}"]
loop: "{{ serveur_dns_public_meta.results | default([]) }}"
loop_control:
label: "{{ item.item.1 }} ← {{ item.item.0.cle_tsig }}"
when:
- not ansible_check_mode
- ("= " ~ item.item.0.cle_tsig) not in (item.stdout | default(''))
changed_when: true # n'arrive ici que si la metadonnee manque ou differe
notify: Redemarrer le DNS public
- name: Activer et démarrer PowerDNS
ansible.builtin.systemd:
name: pdns
enabled: true
state: started
when: not ansible_check_mode
# --- SONDE (docs/supervision-conception.md) -------------------------------------------
- name: Assurer le répertoire des sondes de supervision
ansible.builtin.file:
path: /usr/local/lib/setops/sondes
state: directory
owner: root
group: root
mode: "0755"
- name: Déposer la sonde « zones-publiques »
ansible.builtin.template:
src: sonde-zones-publiques.sh.j2
dest: /usr/local/lib/setops/sondes/zones-publiques.sh
owner: root
group: root
mode: "0750"