Login Logout

Howto BIND

BIND (Berkeley Internet Name Daemon) est le serveur DNS historique écrit au début des années 1980, c’est encore le plus utilisé sur Internet. Il est développé par l’ISC qui développe aussi ISC DHCP. BIND peut être à la fois serveur récursif (résoudre n’importe quel enregistrement DNS pour certaines machines) ou serveur faisant autorité (renvoyer au monde entier les enregistrements DNS d’un nom de domaine spécifique).

Installation

# apt install bind9

$ /usr/sbin/named -V
BIND 9.18.19-1~deb12u1-Debian (Extended Support Version) <id:>
running on Linux x86_64 6.1.0-13-amd64 #1 SMP PREEMPT_DYNAMIC Debian 6.1.55-1 (2023-09-29)
built by make with  '--build=x86_64-linux-gnu' '--prefix=/usr' '--includedir=${prefix}/include' '--mandir=${prefix}/share/man' '--infodir=${prefix}/share/info' '--sysconfdir=/etc' '--localstatedir=/var' '--disable-option-checking' '--disable-silent-rules' '--libdir=${prefix}/lib/x86_64-linux-gnu' '--runstatedir=/run' '--disable-maintainer-mode' '--disable-dependency-tracking' '--libdir=/usr/lib/x86_64-linux-gnu' '--sysconfdir=/etc/bind' '--with-python=python3' '--localstatedir=/' '--enable-threads' '--enable-largefile' '--with-libtool' '--enable-shared' '--disable-static' '--with-gost=no' '--with-openssl=/usr' '--with-gssapi=yes' '--with-libidn2' '--with-json-c' '--with-lmdb=/usr' '--with-gnu-ld' '--with-maxminddb' '--with-atf=no' '--enable-ipv6' '--enable-rrl' '--enable-filter-aaaa' '--disable-native-pkcs11' '--enable-dnstap' 'build_alias=x86_64-linux-gnu' 'CFLAGS=-g -O2 -ffile-prefix-map=/build/reproducible-path/bind9-9.18.19=. -fstack-protector-strong -Wformat -Werror=format-security -fno-strict-aliasing -fno-delete-null-pointer-checks -DNO_VERSION_DATE -DDIG_SIGCHASE' 'LDFLAGS=-Wl,-z,relro -Wl,-z,now' 'CPPFLAGS=-Wdate-time -D_FORTIFY_SOURCE=2'
compiled by GCC 12.2.0
compiled with OpenSSL version: OpenSSL 3.0.10 1 Aug 2023
linked to OpenSSL version: OpenSSL 3.0.11 19 Sep 2023
compiled with libuv version: 1.44.2
linked to libuv version: 1.44.2
compiled with libnghttp2 version: 1.52.0
linked to libnghttp2 version: 1.52.0
compiled with libxml2 version: 2.9.14
linked to libxml2 version: 20914
compiled with json-c version: 0.16
linked to json-c version: 0.16
compiled with zlib version: 1.2.13
linked to zlib version: 1.2.13
linked to maxminddb version: 1.7.1
compiled with protobuf-c version: 1.4.1
linked to protobuf-c version: 1.4.1
threads support is enabled
DNSSEC algorithms: RSASHA1 NSEC3RSASHA1 RSASHA256 RSASHA512 ECDSAP256SHA256 ECDSAP384SHA384 ED25519 ED448
DS algorithms: SHA-1 SHA-256 SHA-384
HMAC algorithms: HMAC-MD5 HMAC-SHA1 HMAC-SHA224 HMAC-SHA256 HMAC-SHA384 HMAC-SHA512
TKEY mode 2 support (Diffie-Hellman): yes
TKEY mode 3 support (GSS-API): yes

default paths:
  named configuration:  /etc/bind/named.conf
  rndc configuration:   /etc/bind/rndc.conf
  DNSSEC root key:      /etc/bind/bind.keys
  nsupdate session key: //run/named/session.key
  named PID file:       //run/named/named.pid
  named lock file:      //run/named/named.lock
  geoip-directory:      /usr/share/GeoIP

Sous Debian 8, l’unité systemd ne gère pas les options dans /etc/default/bind9, il faut corriger l’unité en copiant :

# cp -a /lib/systemd/system/bind9.service /etc/systemd/system/

et ajuster la section [Service] :

EnvironmentFile=-/etc/default/bind9
ExecStart=/usr/sbin/named -f $OPTIONS

puis :

# systemctl daemon-reload

Configuration

Les options de BIND sont dans /etc/default/named (/etc/default/bind9 avant Bullseye).

Fichiers de configuration :

/etc/bind
├── bind.keys
├── db.0
├── db.127
├── db.255
├── db.empty
├── db.local
├── named.conf
├── named.conf.default-zones
├── named.conf.local
├── named.conf.options
├── rndc.key
└── zones.rfc1918

serveur DNS récursif

Lorsque BIND est utilisé en serveur DNS récursif pour une ou plusieurs machines, on conseille d’activer la résolution DNS inverse pour les zones RFC1918 dans le fichier /etc/bind/named.conf.local (c’est important car de nombreux outils vérifient les reverses DNS) :

include "/etc/bind/zones.rfc1918";

Puis ajuster les options via /etc/bind/named.conf.options :

options {
    directory "/var/cache/bind";
    version "Bingo";
    auth-nxdomain no;
    listen-on-v6 { ::1; };
    listen-on { 127.0.0.1; };
    allow-recursion { ::1; 127.0.0.1; };
};

logging {
        category default { default_file; };
        channel default_file {
                file "/var/log/bind.log";
                severity info;
        };
};

Note : dans l’exemple, BIND n’écoute que pour la machine locale. Si l’on veut l’interroger avec d’autres machines, on ajustera les options listen-* et allow-recursion (on pourra notamment utiliser l’ACL localnets

serveur DNS faisant autorité

Lorsque BIND est utilisé en serveur DNS faisant autorité, on conseille de le sécuriser en l’enfermant dans un chroot (c’est notamment indispensable pour la configuration ci-dessous qui va rotater /var/log/bind_queries.log et qui échouera donc si Bind n’est chrooté comme conseillé).

On va ensuite configurer via le fichier /etc/bind/named.conf.options :

acl "foo" {
    ::ffff:192.0.2.21; 192.0.2.21;
    2001:db8::21;
};

options {
      directory "/var/cache/bind";
      version "Bingo";
      auth-nxdomain no;
      masterfile-format text;
      statistics-file "/var/run/named.stats";

      listen-on-v6 { any; };
      listen-on { any; };

      allow-query { localhost; };
      allow-recursion { localhost; };
      allow-transfer { localhost; };
};

logging {
    category default { default_file; };
    category queries { query_logging; };

    channel default_file {
        file "/var/log/bind.log";
        severity info;
    };
    channel query_logging {
        file "/var/log/bind_queries.log" versions 2 size 128M;
        print-category yes;
        print-severity yes;
        print-time yes;
    };
};

On peut ensuite ajouter ses domaines pour lesquels BIND fait autorité dans le fichier /etc/bind/named.conf.local, par exemple pour example.com avec un serveur replica dans l’ACL foo :

zone "example.com" { 
        type master; 
        file "/etc/bind/db.example.com";
        allow-query { any; };
        allow-transfer { "foo"; };
};

Puis enfin les fichiers de zone, exemple avec un /etc/bind/db.example.com simple :

$TTL 1800
@          IN      SOA ns1.example.net. dnsmaster.example.com. (
                   2017072107 ; serial
                   2h         ; rafraichissement replica->master
                   1h         ; en cas d'echec du refraichissement, nouvel essai
                   5w         ; expiration des enregistrements en cache par les serveurs replica
                   10m )      ; TTL negatif

           A 192.0.2.80

           NS ns1.example.net.
           NS ns2.example.org.

           MX 0    .
           TXT "v=spf1 -all"

www        CNAME @
ftp        CNAME ftp.example.net.

Note : on s’assurera que les fichiers de zone sont lisibles par l’utilisateur bind en faisant chown bind:bind /etc/bind/db.example.com

Options de configuration

  • allow-query : spécifie les adresses autorisées à faire des requêtes DNS. Par défaut, toutes les adresses sont autorisées
  • allow-transfer : spécifie les adresses des serveurs replica ou clefs TSIGs autorisées à faire des requêtes AXFR (transfert de zone). Par défaut, toutes les adresses sont autorisées, il est impératif de restreindre les autorisations.
  • allow-recursion : spécifie les adresses autorisées à faire des requêtes DNS récursives. À restreindre impérativement également.
  • allow-update : spécifie les adresses ou clefs TSIGs autorisées à mettre à jour dynamiquement des informations dans leur zone. Par défaut, aucune adresse n’est autorisée.
  • update-policy : spécifie finnement les droit de modification des entrées pour les adresses ou clefs TSIGs autorisées via une liste d’ACL, Attention : on ne peut pas définir à la fois allow-update et update-policy. voir Configuration update-policy

chroot

Pour enfermer BIND dans une prison chroot nous utilisons le script chroot-bind.sh :

# cd /root
# wget https://gitea.evolix.org/evolix/chroot-bind/raw/branch/master/chroot-bind.sh
# sh chroot-bind.sh

Puis ajoutez -t /var/chroot-bind dans la variable OPTIONS du fichier /etc/default/named (/etc/default/bind9 avant Bullseye) :

RESOLVCONF=no
OPTIONS=" -u bind -t /var/chroot-bind"

AppArmor depuis Buster (Debian 10)

AppArmor protège par défaut les chemins utilisés par la version distribuée par Debian, il faut ajouter les chemins effectivement utilisés dans le chroot.

# cat /etc/apparmor.d/local/usr.sbin.named
/var/chroot-bind/ r,
/var/chroot-bind/** r,
/var/chroot-bind/etc/bind/** rw,
/var/chroot-bind/var/** rw,
/var/chroot-bind/dev/** rw,
/var/chroot-bind/run/** rw,
/var/chroot-bind/usr/** r,

Il faut relancer apparmor et bind9 après ces ajouts.

# systemctl daemon-reload
# systemctl restart apparmor 
# systemctl restart bind9

Sinon vous aurez des erreurs open: /etc/bind/named.conf: permission denied et audit: type=1400 audit(1689581621.287:26): apparmor="DENIED" operation="open" profile="/usr/sbin/named" name="/var/chroot-bind/etc/bind/named.conf" pid=3205399 comm="named" requested_mask="r" denied_mask="r" fsuid=115 ouid=115 dans /var/log/syslog.

bind mount à partir de Bookworm (Debian 12)

Depuis Bookworm, des points de montage sont nécessaires pour un fonctionnement normal de systemd et journald (sinon l’unité systemd ne renvoie jamais de signal après un redémarrage correct).

/etc/fstab doit contenir les lignes suivantes.

/run/systemd/journal/socket /var/chroot-bind/run/systemd/journal/socket none    bind    0   0
/run/systemd/journal/stdout /var/chroot-bind/run/systemd/journal/stdout none    bind    0   0
/run/systemd/notify         /var/chroot-bind/run/systemd/notify         none    bind    0   0

Puis les points de montages doivent être créés et montés.

# mkdir -p /var/chroot-bind/run/systemd/journal
# touch /var/chroot-bind/run/systemd/{journal/s{ocket,tdout},notify}
# chown -R bind: /var/chroot-bind/run/
# systemctl daemon-reload
# mount -a

Relancer bind

Vous devez alors relancer BIND :

# systemctl restart bind9

À chaque mise à jour du paquet bind9 ou de l’une de ses dépendances (libc6, libcap2, libssl, etc.) vous devrez relancer le script chroot-bind.sh et BIND ensuite.

Problèmes de permissions avec chroot

  • Si erreur : named: chroot(): Permission denied, vérifier que /var/chroot-bind est en 750.
  • Si /var/log/bind_queries.log n’existe pas, il faut créer le lien symbolique : ln -s /var/chroot-bind/var/log/bind_queries.log /var/log/bind_queries.log

Rotation des logs

On utilise logrotate pour la rotation des logs.

Voici le fichier /etc/logrotate.d/bind9 que l’on utilise pour un serveur récursif :

/var/log/bind.log {
    weekly
    missingok
    rotate 8
    create 640 bind bind
    sharedscripts
    postrotate
        rndc reload > /dev/null
    endscript
}

Pour un serveur faisant autorité, on doit préciser le path des fichiers dans le chroot :

/var/chroot-bind/var/log/bind.log {
    weekly
    missingok
    rotate 52
    create 640 bind bind
    sharedscripts
    postrotate
        rndc reload > /dev/null
    endscript
}

Note : la rotation du fichier /var/chroot-bind/var/log/bind_queries.log est assurée par BIND sous réserve d’avoir bien précisé l’option versions dans les paramètres de logging

Il est aussi possible de faire la rotation du fichier de statistique (/var/run/named.stats ou /var/chroot-bind/var/run/named.stats avec chroot) avec logrotate:

/var/run/named/named.stats
{
        rotate 7
        daily
        missingok
        notifempty
        delaycompress
        compress
        su bind bind
}

La rotation du fichier de statistiques n’affecte ni Munin, ni BIND.

ACL

http://www.zytrax.com/books/dns/ch7/address_match_list.html

Au sein des différentes directives allow-* on peut utiliser des ACLs.

Par défaut, les ACLs suivantes sont prédéfinies :

  • none : aucune adresse IP
  • any : toutes les adresses IP
  • localhost : toutes les adresses IP de la machine (127.0.0.1 mais aussi les adresses configurées sur les différentes interfaces réseau)
  • localnets : tous les sous-réseaux dans lesquels la machine possède une adresse IP

On peut également définir ses propres ACLs :

acl "bar" {
    ::ffff:192.0.2.21; 192.0.2.21;
    ::ffff:192.0.2.42; 192.0.2.42;
    2001:db8::21; 2001:db8::42;
};

DNS primaires et secondaires

Il est possible de répliquer la configuration des zones DNS d’un serveur DNS primaire vers plusieurs secondaires.

Cela n’empêche pas les secondaires d’être primaires pour d’autres zones.

Si l’on reprend le cas de la zone suivante sur le primaire :

zone "example.com" {
        type master;
        file "/etc/bind/db.example.com";
        allow-query { any; };
        allow-transfer { <SLAVE1_IP>; <SLAVE2_IP>; ... | "<ACL1>"; "<ACL2>"; ... };
};

Sur le secondaire, on spécifiera l’adresse IP du primaire ainsi qu’un fichier pour sauvegarder la zone en local :

zone "example.com" {
        type slave;
        file "/etc/bind/bak.example.com";
        allow-query { any; };
        masters { <MASTER_IP>; };
};

Notes :

  • Attention aux IPv4 / IPv6.
  • Si on édite manuellement la configuration du master (plutôt que d’utiliser le DNS dynamique avec nsupdate), il faut absolument mettre request-ixfr no; dans la configuration de Bind sur les secondaires (dans les options générales, ou dans un bloc server).
  • Pour avoir le fichier de zone en clair sur le serveur secondaire (ce qui est pratique notamment en cas d’incident avec le serveur primaire), il faut s’assurer d’avoir l’option masterfile-format text; dans la configuration de Bind.

Debugger

Pour savoir si le primaire renvoie bien la zone DNS à jour, à partir du serveur DNS secondaire, faire une requête de transfert de zone incrémental avec dig :

dns-secondaire$ dig -b <IP_OUT> <DOMAIN> IXFR=1 @<PRIMARY_DNS_IP>

Options :

  • -b <IP_OUT> : si on a plusieurs IPs de sortie, force bind à faire sa requête à partir de IP_OUT.
  • IXFR=1 : récupère les changements de la zone depuis le serial 1.

Mise à jour dynamique

La mise à jour dynamique permet de mettre à jour une zone avec des demandes de mise à jour Dynamic Updates envoyés par le réseau au server autoritaire pour ajouter ou supprimer des champs ( voir RFC2136 )

  • Il n’y a plus besoin de se connecter au serveur autoritaire pour changer le fichier de zone
  • La syntaxe d’une demande est vérifiée avant d’être envoyé pour éviter d’ajouter des entrées incorrectes.
  • Le SOA est incrémenté automatiquement.
  • Dans une zone DNSSEC, le changement est immédiatement signé.
  • On peut facilement scripter ces mises à jours ( certbot par exemple )
  • Important : Les directives $INCLUDE et $GENERATE d’un fichier de zone ne pourront plus être utilisé. Pour conserver cette fonctionnalité, on recommande d’activer la mise à jour uniquement sur des sous domaines/zone d’une zone qui utilise ces directives (ces sous-zones seront alors incluses).

Attention : lorsqu’on active le dynamic-update sur une zone, il devient difficile d’éditer le fichier de zone à la main, voir màj manuelle.

Activer la mise à jour dynamique sur une zone

Pour faire la mise à jour dynamique, Bind génère un fichier de journal .jnl binaire du même nom que la zone, contenant l’historique des modifications apportées à la zone. L’historique est

Note : la synchronisation avec le fichier de zone n’est pas faite en temps réel, cela se fait toutes les 15 minutes en général, on peut forcer la synchronisation avec $ rndc sync [nom_zone].

Pour éviter de donner à Bind le droit d’éditer tous les fichiers dans /etc/bind, il est préférable de placer les fichiers de zones mise à jour dynamique dans un dossier séparer ou bind à l’accès en écriture, par exemple :

$ mkdir -p /etc/bind/dynamic-zones
$ chown -R bind:bind /etc/bind/dynamic-zones

Attention : la zone ne doit pas contenir de directives telles que $INCLUDE et $GENERATE … celles-ci seront perdues à la synchronisation avec le fichier journal

On peut alors mettre en place le fichier de zone dans ce dossier…

# déplacer le fichier de zone avec les bon droits
$ mv /etc/bind/db.example.com /etc/bind/dynamic-zones/db.example.com
$ chown bind:bind /etc/bind/dynamic-zones/db.example.com

Puis activer la mise à jour dynamique dans sa configuration :

zone "example.com" in {
        type                    master;
        file                    "/etc/bind/dynamic-zones/db.example.com";
       allow-update { key "custom-tsig"; localhost;};
       allow-transfer { key "secondary-tsig"; 192.0.2.42; localhost; };
//     update-policy           {
//       grant custom-tsig  name _acme-challenge.example.com. txt;
// };
//     serial-update-method ( date | increment | unixtime );
};
  • allow-update permet d’autoriser la mise à jour de toute la zone avec des IPs ou clefs TSIGs tandis que update-policy permet de contrôler finement quels champs peuvent être mis à jour, voir configuration update-policy
  • allow-transfer pour permettre de transférer la zone, utilisé pour que le client puisse aussi récupérer la zone
  • serial-update-method permet de définir le mécanisme de mise à jour du serial SOA (incrément par défaut).

Voir mise en place d’une clef tsig si besoin

On peut ensuite tester le fonctionnement en jouant nsupdate ou nsvi pour ajouter ou supprimer une entrée.

Si le nsupdate fonctionne, on peut alors vérifier avec un dig normal ou axfr ( par exemple dig -t axfr example.com @localhost sur le server dns) ou encore mieux, en controlant le journal.

Attention, il faut que AppArmor autorise l’écriture dans /etc/bind/** ou /var/chroot-bind/etc/bind/** (cf plus bas) sinon il y aura une erreur create: permission denied et AVC apparmor="DENIED" operation="mknod".

client nsupdate

nsupdate est utilitaire en ligne de commande interactif fournit le paquet de bind bind9-dnsutils sur Debian.

Celui-ci permet d’envoyer des demandes de mise à jour à un serveur dns, avec ou sans TSIGs :

# nsupdate [-y [hmac:]keyname:secret]
$ nsupdate -y hmac-sha512:happydomain:"3JtiVQEBFXtQeU/3PorKpCV7jM1bZEEzkdD1zb9emvwaBZFSgYxOXV41OdSeeHb5dcud+SvNo47jEf5yRwfSkQ=="
> server <adresse_server> [port]
> zone example.com
> update add www.example.com.      172800 IN A 192.168.254.7
> send
> 

Une réponse vide après send indique que la mise à jour à eu lieu. Sinon un code d’erreur sera affiché, par exemple update failed: SERVFAIL.

Le manuel contient plusieurs exemples.

nsdiff

Documentation : https://dotat.at/prog/nsdiff/

nsdiff est outil permettant de faciliter l’utilisation de nsupdate avec différents utilitaires:

  • nsdiff : comme son nom l’indique, permet de voir la diff entre une ancienne et une nouvelle vertsion de la zone, sa sortie peut directement etre fournie à nsupdate : nsdiff | nsupdate
  • nspatch : joue nsdiff | nsupdate et n’affiche rien s’il n’y a pas d’erreur, utile dans un CRON
  • nsvi : fait un transfert de zone, ouvre la zone dans vi (ou un autre editeur) et envoie les mise à jour avec nsdiff | nsupdate

Remarque : les enregistrements dnssec sont retirés avant de passer la zone à l’éditeur et il n’y a pas besoin de s’occuper du SOA

Exemples d’utilisations (WIP) :

# editer une zone dynamique sur le serveur dns ... (avec localhost dans allow-update)
# nsvi example.com
# editer une zone dynamique avec la clef tsig associée

Contrôler le journal d’une zone

On peut vérifier le contenu du journal

# voir les changement dans le fichier de journal .jnl
$ named-journalprint [fichier_zone_jnl]
# si il y a des changement, on peut les synchroniser et vider le journal
$ rndc sync -clean example.com

Mise à jour manuelle de la zone

Il est possible de mettre à jour le fichier de zone directement, mais ce n’est pas recommandé, car on va devoir temporairement désactiver la mise à jour dynamique “freeze”.

Avant cela, il est préférable de vérifier l’état du journal et de la vider ( voir contrôler le journal )

# désactiver la mise à jour dynamique
$ rndc freeze [nom_zone]
# Modification manuelle de la zone avec vim ou autre
$ named-checkzone <nom_zone> <fichier_de_zone>
$ rndc unfreeze [nom_zone]
$ rndc reload [nom_zone]

Comme cela est compliqué et risqué, il est préférable d’utiliser un outil comme nsdiff avec nsvi qui va nous permettre d’éditer la zone comme si on l’édite à la main, mais va envoyer les changements par demande de mise à jour, voir nsdiff.

Mise en place de clefs TSIGs

On peut utiliser nsupdate, ou n’importe quel client, pour mettre à jour la zone en s’authentifiant avec une clef TSIG. Pour mettre celle-ci en place, il faut …

Générer une clef TSIG avec tsig-keygen si disponible ou avec dnssec-keygen sinon

$ tsig-keygen -a hmac-sha512 nomclef
$ dnssec-keygen -a HMAC-SHA512 -b 512 -n HOST nomclef

Ajouter celle-ci à la configuration de bind…

$ vim /etc/bind/named.conf.tsigkeys

key "happydomain" {
  algorithm HMAC-SHA512;
  secret "3JtiVQEBFXtQeU/3PorKpCV7jM1bZEEzkdD1zb9emvwaBZFSgYxOXV41OdSeeHb5dcud+SvNo47jEf5yRwfSkQ==";
};

Ne pas oublier d’inclure named.conf.tsigkeys dans /etc/bind/named.conf

On peut maintenant recharger la configuration et vérifier que notre clef est bien présente:

$ named-checkconf /etc/bind/named.conf && rndc reload
$ rndc tsig-list

DNSSEC

Ajouter la gestion DNSSEC

Créer le répertoire /etc/bind/dnssec/db.$domain (qui servira aussi à conserver les clefs et fichiers de zones dynamiques) et y déplacer le fichier de zone (un lien symbolique depuis son emplacement précédent permet de continuer à pouvoir l’éditer comme avant). Prendre garde à ce que tous les fichiers et répertoires appartiennent bien à bind.

# sudo -u bind mkdir -p /etc/bind/dnssec/$domain
# mv /etc/bind/db.$domain /etc/bind/dnssec/$domain/
# ln -s dnssec/$domain/db.$domain /etc/bind/

Mettre en place une paire de clefs de zone (ZSK) et de signature (KSK)

# cd /etc/bind/dnssec/$domain
# sudo -u bind dnssec-keygen -a ECDSAP256SHA256 -n ZONE $domain
# sudo -u bind dnssec-keygen -a ECDSAP256SHA256 -n ZONE -f KSK $domain

Modifier le fichier de zone dans /etc/bind/named.conf.local pour ajouter les trois dernières options suivantes (et modifier le chemin vers le fichier de zone).

zone "$domain" {
        type master;
        file "/etc/bind/dnssec/$domain/db.$domain";
        allow-query { any; };
        allow-transfer { "foo"; };
        inline-signing yes;
        auto-dnssec maintain;
        key-directory "/etc/bind/dnssec/$domain";
};

Puis recharger la configuration de bind et la zone sera automatiquement signé.

# systemctl reload bind9

NSEC3 peut être ajouté à la zone signée.

# rndc signing -nsec3param 1 0 10 `cat /dev/urandom | tr -dc 'A-F0-9' | fold -w 8 | head -n 1` $domain

Modification de la zone

Attention le serial de la zone signée et du fichier original non signé peuvent différer (mise à jour dynamique).

Deux commandes pour savoir où en est le serial :

$ host -t soa $domain 127.0.0.1
# named-compilezone -j -i none -f raw -F text -o - $domain $domain.signed

Une fois le bon serial récupéré on peut modifier la zone en incrémentant le serial trouvé.

# Modifier le domaine example.org
domain=example.org
# Vérification du SOA pour le mettre à jour dans le fichier.
host -t soa $domain
# Arrêt (gel) de la mise à jour dynamique
rndc freeze $domain
# Mise à jour de la zone en pensant au SOA.
vim /etc/bind/db.$domain
# Vérification de la configuration.
named-checkzone $domain /etc/bind/db.$domain
named-checkconf
# Relance de la mise à jour dynamique
rndc thaw $domain

La zone sera automatiquement resigné.

Rotation de la ZSK

On génère une nouvelle clé comme vu plus haut.

Le temps d’inactivation de l’ancienne clé ainsi que la date de suppression doit être indiqué.

# La clé doit être retirée dans 2 jours
dnssec-settime -I +2d OLD_ZSK.key

# La clé doit être supprimée dans 6 jours
dnssec-settime -D +6d Kexample.tld.+008+25266.key

Bind fera tout seul la prébublication et le rollover.

Les 2 jours et les 6 jours sont à modifier en fonction du ttl de la zone, il faut à minima :

  • 2 ttl avant que la OLD_ZSK soit inactivé ;
  • durant ces 2 ttl, il y a 1 ttl où la OLD_ZSK est activée et la NEW_ZSK est publiée ;
  • à la fin des 2 ttl la NEW_ZSK est activée et la OLD_ZSK peut être supprimée.

Reverse DNS

Un reverse DNS est un nom de domaine associé à une adresse IP. Le nom de cet enregistrement est PTR. Exemples :

$ dig -x 31.170.8.43

;; QUESTION SECTION:
;43.8.170.31.in-addr.arpa.      IN      PTR

;; ANSWER SECTION:
43.8.170.31.in-addr.arpa. 43200 IN      PTR     hosting.evolix.net.

$ dig -x 2a01:9500::3

;; QUESTION SECTION:
;3.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.5.9.1.0.a.2.ip6.arpa. IN PTR

;; ANSWER SECTION:
3.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.5.9.1.0.a.2.ip6.arpa. 43200 IN PTR forge.evolix.net.

Cela fonctionne avec un nom de domaine “virtuel” : .in-addr.arpa en IPv4 et .ip6.arpa en IPv6 !

Il est important de configurer les reverses DNS, car ils sont souvent vérifiés par les serveurs SMTP, et certains logiciels les vérifient également (Postfix, MySQL, CUPS, SSH, etc.). Sur un réseau avec des adresses privées, il également important de renvoyer au minimum une réponse DNS NXDOMAIN ce qui peut être fait facilement grâce au fichier /etc/bind/zones.rfc1918.

À noter que les DNS de l’IANA répondent tout de même pour ces demandes… mais sans garantie de bon fonctionnement !

Monitoring

dnstop

L’outil dnstop permet de surveiller en direct l’activité DNS en analysant les paquets réseau qui circulent :

# apt install dnstop
# dnstop -l3 eth0

 s - Sources list
 d - Destinations list
 t - Query types
 o - Opcodes
 r - Rcodes
 1 - 1st level Query Names      ! - with Sources
 2 - 2nd level Query Names      @ - with Sources
 3 - 3rd level Query Names      # - with Sources
 4 - 4th level Query Names      $ - with Sources
 5 - 5th level Query Names      % - with Sources
 6 - 6th level Query Names      ^ - with Sources
 7 - 7th level Query Names      & - with Sources
 8 - 8th level Query Names      * - with Sources
 9 - 9th level Query Names      ( - with Sources
^R - Reset counters
^X - Exit

Munin

Pour activer les plugins Munin pour BIND :

# cd /etc/munin/plugins
# ln -s /usr/share/munin/plugins/bind9_rndc
# ln -s /usr/share/munin/plugins/bind9

Le plugin bind9_rndc s’appuie sur le fichier /var/run/named.stats (option statistics-file de BIND) et bind9 s’appuie un fichier de logs (channel query_logging dans les logs BIND). Il donc s’assurer d’avoir bien configuré ces deux fichiers puis on indique à Munin via /etc/munin/plugin-conf.d/bind9 :

[bind*]
user root
env.logfile /var/chroot-bind/var/log/bind_queries.log
env.querystats /var/chroot-bind/var/run/named.stats
env.MUNIN_PLUGSTATE /var/lib/munin
timeout 120

Log2mail

Pour être alerté en cas de fichiers de zone incorrect, on peut rajouter ceci dans la conf de Log2mail :

file = /var/chroot-bind/var/log/bind.log
  pattern = "not loaded due to errors"
  mailto = alert@example.com

file = /var/log/syslog
  pattern = "fatal error"
  mailto = alert@example.com

  pattern = "loading configuration: failure"
  mailto = alert@example.com

géolocalisation

BIND peut répondre une réponse différente selon l’adresse IP qui l’interroge, on peut faire cela de deux façons :

  • Depuis Buster (Debian 10) le patch geoip a été intégré a Bind. Le principe est de faire une recherche suivant un arbre binaire (binary search tree) sur la base de données de MaxMind. Cela n’impacte pas les temps de réponse mais nécessite de maintenir un Bind patché avant Buster ;
  • en se basant sur le système de vue de Bind. Le principe est de récupérer les plages d’adresses par pays auprès de MaxMind et de générer des fichiers d’ACL à l’aide de ce script. Les performances sont en théorie moins bonnes (mais cette différence n’est pas vraiment constatée dans la pratique), mais l’installation est bien plus simple à maintenir.

À l’aide de vues

  • On récupère le script GeoIP.py et on installe les dépendances nécessaires :
# apt install python-mpmath
  • exécuter le script. Il générera un fichier GeoIP.acl, avec une ACL par pays :
$ ./GeoIP.py MaxMind
  • Configurer Bind pour servir une zone différente en fonction des pays :
include "/etc/bind/GeoIP.acl";

view "europe" {
  match-clients { FR; };
  recursion no;
  zone "example555.com" {
    type master;
    file "/etc/bind/db.example555-europe.db";
    allow-query { any; };
  };
};

view "north_america" {
  match-clients { US; CA; MX; };
  recursion no;
  zone "example555.com" {
    type master;
    file "/etc/bind/db.example555-north-america.db";
    allow-query { any; };
  };
};

view "other" {
  match-clients { any; };
  recursion no;
  zone "example555.com" {
    type master;
    file "/etc/bind/db.example555-other.db";
    allow-query { any; };
  };
};

Le script peut être mis en cron pour conserver des ACL à jour.

FAQ

journal out of sync

Si vous avez une erreur du type :

journal out of sync with zone

Lancer la commande :

# named -g

Voir http://www.thedumbterminal.co.uk/?action=showArticle&articleId=168

ran out of space

Si vous avez une erreur du type (par exemple pour des enregistrements TXT ou SPF) :

ran out of space

Vous devez spliter vos champs, voir http://www.jeoffrey54.com/article144/dns-sous-bind-ran-out-of-space

received control channel command ‘stop’

En lançant votre démon, celui-ci est stoppé immédiatement avec un simple message d’arrêt :

received control channel command 'stop'

…vérifiez si vous n’avez pas oublié l’option -f tout en utilisant systemd.

Zone replica avec Bind 9.9

À partir de Bind 9.9, le stockage des zones répliquées se fait en binaire. Avec une ancienne configuration vous aurez des erreurs :

zone example.com/IN: loading from master file /etc/bind/bak.example.com failed: not implemented
zone example.com/IN: unable to load from '/etc/bind/bak.example.com'; renaming file to '/etc/bind/db-5GoiCpdc' for failure analysis and retransferring.

Pour conserver l’ancien comportement il faut ajouter dans la configuration :

masterfile-format text;

Voir http://geekdom.wesmo.com/2014/06/05/bind9-dns-slave-file-format/

Vérifier la configuration

  • Globalement :
# named-checkconf /etc/bind/named.conf
  • Pour une zone en particulier :
# named-checkzone example.com /etc/bind/db.example.com
  • Pour un reverse :
# named-checkzone $reversed_IP.in-addr.arpa. /etc/bind/db.reverse.ripe$subnet

Forcer un transfer sur le replica

Sur le replica :

# rndc reload example.com
zone refresh queued

Pas d’IPv6 ou souci d’IPv6

Si malheureusement vous n’avez pas d’IPv6 sur votre serveur ou alors si la qualité de votre connexion IPv6 est insuffisante, vous devez forcer Bind à ne pas écouter en IPv6 ainsi :

listen-on-v6 { none; };

Et via le fichier /etc/default/named (/etc/default/bind9 avant Bullseye) vous allez le forcer à n’effectuer des requêtes qu’en IPv4 :

OPTIONS="-u bind -4 -t /var/chroot-bind"

master “caché”

L’enregistrement SOA ns-hidden-master.example.com ... doit bien pointer vers le master caché, dans ce cas ns-hidden-master.example.com sinon les NOTIFY ne fonctionneront pas correctement.

refresh: timeout retrying without EDNS master

Cela signifie que les requêtes EDNS ne fonctionnent pas vers le master, cela peut notamment être causé par un firewall qui tronque les paquets EDNS qui sont volumineux. On peut notamment contourner cela en désactivant EDNS vers le serveur concerné via named.conf.options :

options {
...
}
logging {
...
}
server 192.0.2.53 { edns no; };

délai de transfert de zones

Voir https://kb.isc.org/docs/aa-00726

Mise a jour du serial

Vous devez repérer un pattern unique sur la ligne du serial, par exemple ; serial

On peut ainsi mettre à jour le serial d’une zone :

$ serial=$(date "+%Y%m%d%H")
$ sed -i "s/^\([ \t]*\)[0-9]\{10\}\([ \t]*; serial\)/\1$serial\2/" db.example.com

Si l’on doit automatiser, on utilisera les commandes suivantes pour prendre en compte plusieurs modifications le même jour :

zonefile=db.example.com
serial=$(grep -E '[ \t]*[0-9]{10}\s*; serial' $zonefile | sed "s/[ \t]*\([0-9]\{10\}\)[ \t]*; serial/\1/")
if [ `date "+%Y%m%d%H"` -gt $serial ]; then serial=$(date "+%Y%m%d%H"); else serial=$(( $serial + 1 )); fi
sed -i "s/^\([ \t]*\)[0-9]\{10\}\([ \t]*; serial\)/\1$serial\2/" $zonefile

Configuration update-policy

Dans update-policy, les régles ACLs prennent la forme ( grant | deny ) identity ruletype name types

La première règle qui match un requête sera utilisé

  • identity correspond à un fqdn ou le nom la clef TSIG
  • name le nom de l’entrée dans la zone
  • types le ou les types de l’entrée(s) dans la zone
  • ruletype est le type de règle utilisé, il en existe une vingtene de valeurs, les plus importantes sont (WIP)

Les ruletypes pricipaux :

  • name la règle match exactement avec le nom de l’entrée name
  • subdomain match avec tous le sous-domaine et le domaine de l’entrée name
  • zonesub match tous les sous-domaines de la zone contenant la configuration, il ne faut pas préciser d’entrée name
  • wildcard match toute les expansions wildcard valides de l’entrée name
  • self permet de faire correspondre identity directement avec le nom d’une entrée, il ne faut pas préciser d’entrée name ou sinon y mettre . ou la même valeur que identity. On peut aussi mettre directement * pour identity si le nom est bien le même que celui d’une entrée (cela permet de matcher automatiquement les clefs avec les noms d’entrées)
  • selfsub fonctionne comme self et permet de mettre à jour aussi les sous-domaine du nom d’une entrée
  • selfwild fonctionne comme self mais permet de mettre à jour uniquement les sous-domaine du nom d’une entrée
  • Liste complète: https://bind9.readthedocs.io/en/v9_18_2/reference.html#dynamic-update-policies

Problèmes de rafraîchissement des DNS secondaires

Si on modifie les zones du primaire à la main, les secondaires doivent désactiver les requêtes IXFR, en faveur de l’AXFR.

L’IXFR fonctionne bien seulement avec les mises-à-jour DNS dynamiques (nsupdate).

Erreurs “unable to find a DNSKEY”

Si vous obtenez des erreurs de ce type :

named[29200]:   validating @0x555adbd13380: . DNSKEY: unable to find a DNSKEY which verifies the DNSKEY RRset and also matches a trusted key for '.'
named[29200]:   validating @0x555adbd13380: . DNSKEY: please check the 'trusted-keys' for '.' in named.conf.
named[29200]: error (broken trust chain) resolving './NS/IN': 199.7.91.13#53
named[29200]: validating @0x555adc128030: . DNSKEY: unable to find a DNSKEY which verifies the DNSKEY RRset and also matches a trusted key for '.'
named[29200]: validating @0x555adc128030: . DNSKEY: please check the 'trusted-keys' for '.' in named.conf.
named[29200]: error (no valid KEY) resolving './DNSKEY/IN': 192.36.148.17#53
named[29200]: validating @0x555adc128030: . DNSKEY: unable to find a DNSKEY which verifies the DNSKEY RRset and also matches a trusted key for '.'
named[29200]: validating @0x555adc128030: . DNSKEY: please check the 'trusted-keys' for '.' in named.conf.

Cela peut empêcher Bind de fonctionner.

C’est relatif à DNSSEC, potentiellement vous devez mettre à jour des clés.

C’est déconseillé, mais une manière de contourner le problème est de désactiver dnssec-validation auto;