GUIDE
2026
pour les Nuls
Guide pratique — La Cyber pour les Nuls
BIND DNS
sur Ubuntu
🏗️ Architecture views 🔒 Sécurisation 📋 Logs séparés 🔐 DNSSEC 🛡️ CAA 🐧 Ubuntu 22.04/24.04
🤓
La Cyber pour les Nuls — Avril 2026
🏗️

Partie A — Architecture : séparer interne et externe avec les views

Le fondement d'un DNS d'entreprise propre : un seul serveur, deux comportements.

A.1 Pourquoi séparer ?

Un serveur BIND en entreprise doit répondre à deux publics très différents :

PublicBesoinComportement
Réseau interneRésoudre les noms internes + InternetAutoritaire + récursif
InternetRésoudre uniquement vos domaines publicsAutoritaire uniquement — pas de récursion

Sans séparation, votre serveur est un open resolver — il répond aux requêtes récursives de n'importe qui sur Internet. C'est un risque de sécurité majeur (amplification DDoS, exfiltration DNS) et une non-conformité ISO 27001.

N'oubliez pas
La séparation autoritaire/récursif est le premier contrôle ANSSI pour les serveurs DNS (guide ANSSI-PA-105). Un serveur qui fait les deux pour tout le monde est un « open resolver » — c'est une vulnérabilité.

A.2 Le concept de views dans BIND

Une view (ou « vue ») est un bloc de configuration dans BIND qui définit un comportement différent selon l'IP source du client. BIND évalue les views dans l'ordre et prend la première qui matche.

┌─────────────────────┐ │ Requête DNS │ │ entrante │ └──────────┬──────────┘ │ ┌──────────▼──────────┐ │ IP source du │ │ client ? │ └──────────┬──────────┘ ┌─────────┴─────────┐ │ │ ┌────────▼────────┐ ┌────────▼────────┐ │ View INTERNAL │ │ View EXTERNAL │ │ │ │ │ │ ✅ Récursion │ │ ❌ Récursion │ │ ✅ Autoritaire │ │ ✅ Autoritaire │ │ ✅ Zones root │ │ ❌ Zones root │ │ ✅ AXFR sortant │ │ ❌ AXFR │ └─────────────────┘ └─────────────────┘

A.3 Structure des fichiers

Voici l'organisation cible des fichiers de configuration :

Arborescence
/etc/bind/
├── named.conf                     # Point d'entrée (includes)
├── named.conf.options             # ACL, options globales, logging
├── named.conf.view-internal       # View interne : récursion + zones
├── named.conf.view-external       # View externe : zones seulement
└── zones/
    ├── example.com.hosts          # Fichier de zone (source unique)
    ├── example.fr.hosts
    └── external/
        ├── example.com.hosts → ../example.com.hosts   # Liens symboliques
        └── example.fr.hosts → ../example.fr.hosts
Truc
Le répertoire external/ contient des liens symboliques vers les fichiers de zone. On édite un seul fichier, les deux views servent le même contenu. Si vous avez besoin de split-horizon (IPs différentes interne/externe), vous pouvez utiliser un fichier séparé pour la view external.

A.4 Le fichier named.conf principal

named.conf
// named.conf — Point d'entrée avec views
// Options globales, ACL, logging
include "/etc/bind/named.conf.options";

// View interne : récursion + autoritaire (réseaux internes)
include "/etc/bind/named.conf.view-internal";

// View externe : autoritaire uniquement (Internet)
include "/etc/bind/named.conf.view-external";

A.5 Les ACL (listes de contrôle d'accès)

Définir les réseaux internes dans named.conf.options :

named.conf.options — ACL
acl "internal-nets" {
    127.0.0.0/8;         // loopback
    10.0.0.0/8;          // RFC1918
    172.16.0.0/12;       // RFC1918
    192.168.0.0/16;      // RFC1918
    // Ajoutez vos plages internes spécifiques
};

acl "secondary-ns" {
    // IP de vos serveurs DNS secondaires
    // Ajoutez les IPs de vos secondaires ici
};

A.6 Configuration de la view interne

named.conf.view-internal
view "internal" {
    match-clients { "internal-nets"; "secondary-ns"; };
    recursion yes;
    allow-query { any; };
    allow-query-cache { "internal-nets"; };

    // Zones racine et locales (indispensables pour la récursion)
    zone "." { type hint; file "/usr/share/dns/root.hints"; };
    zone "localhost" { type master; file "/etc/bind/db.local"; };
    zone "127.in-addr.arpa" { type master; file "/etc/bind/db.127"; };
    zone "0.in-addr.arpa" { type master; file "/etc/bind/db.0"; };
    zone "255.in-addr.arpa" { type master; file "/etc/bind/db.255"; };

    // Vos zones master
    zone "example.com" {
        type master;
        file "example.com.hosts";
        also-notify { /* IP secondaire */ };
        allow-transfer { "secondary-ns"; };
    };
};

A.7 Configuration de la view externe

named.conf.view-external
view "external" {
    match-clients { any; };     // Tout ce qui n'est pas internal
    recursion no;               // ❌ Pas de récursion pour Internet
    allow-query { any; };
    allow-transfer { none; };   // ❌ Pas de transfert de zone

    // Vos zones master (mêmes zones, fichier via lien symbolique)
    zone "example.com" {
        type master;
        file "example.com.hosts";   // ou "external/example.com.hosts"
    };
};

A.8 Créer les liens symboliques

Bash
# Créer le répertoire external
mkdir -p /etc/bind/zones/external

# Créer un lien symbolique pour chaque zone
cd /etc/bind/zones
for f in *.hosts; do
  [ ! -e "external/$f" ] && ln -s "/etc/bind/zones/$f" "external/$f"
done

A.9 Recharger une zone après modification

Avec les views, le rechargement change. Il faut préciser la view :

Bash
# AVANT (ne fonctionne plus avec les views)
rndc reload example.com

# MAINTENANT — spécifier chaque view
rndc reload example.com IN internal
rndc reload example.com IN external

# Ou créer un script utilitaire :
cat > /usr/local/bin/dns-reload << 'EOF'
#!/bin/bash
ZONE="$1"
[ -z "$ZONE" ] && echo "Usage: dns-reload nom.de.zone" && exit 1
rndc reload "$ZONE" IN internal && echo "[internal] $ZONE rechargé"
rndc reload "$ZONE" IN external && echo "[external] $ZONE rechargé"
EOF
chmod +x /usr/local/bin/dns-reload
# Puis simplement : dns-reload example.com
Attention
Si vous oubliez de spécifier la view, BIND retourne l'erreur 'reload' failed: multiple — zone was found in multiple views. Le rndc reload sans nom de zone (reload global) fonctionne toujours.
🔒

Partie B — Sécuriser BIND

Durcir la configuration pour un serveur exposé sur Internet.

B.1 Masquer la version de BIND

Par défaut, dig @votreserveur version.bind chaos txt renvoie la version exacte de BIND. Un attaquant peut l'utiliser pour cibler des CVE spécifiques.

named.conf.options
options {
    version "none";       // Masque la version
    hostname "none";      // Masque le hostname
};

B.2 Rate Limiting (RRL)

Protège contre les attaques d'amplification DNS :

named.conf.options
options {
    rate-limit {
        responses-per-second 10;
        window 5;
        slip 2;
        errors-per-second 5;
        nxdomains-per-second 5;
    };
};

B.3 Réponses minimales

named.conf.options
options {
    minimal-responses yes;   // Réduit la taille des réponses (anti-amplification)
};

B.4 Restreindre les transferts de zone (AXFR)

named.conf.options
options {
    allow-transfer { none; };   // Par défaut : personne
};

// Dans chaque zone qui a un secondaire :
zone "example.com" {
    allow-transfer { "secondary-ns"; };
};
Interdit
Un AXFR ouvert à tous permet à n'importe qui de récupérer l'intégralité de votre zone DNS — tous vos enregistrements, sous-domaines, IPs internes. C'est l'équivalent de donner le plan de votre réseau.

B.5 Synthèse du bloc options sécurisé

named.conf.options — complet
options {
    directory "/etc/bind";
    listen-on { any; };
    listen-on-v6 { any; };

    // Sécurité
    version "none";
    hostname "none";
    allow-transfer { none; };
    minimal-responses yes;

    // Récursion (désactivée globalement, activée dans la view internal)
    recursion no;

    // Rate limiting
    rate-limit {
        responses-per-second 10;
        window 5;
        slip 2;
        errors-per-second 5;
        nxdomains-per-second 5;
    };
};
📋

Partie C — Logs séparés par catégorie

Voir ce qui se passe sans noyer l'essentiel dans le bruit.

C.1 Pourquoi séparer les logs ?

BIND mélange par défaut toutes les catégories dans syslog. Pour un audit de sécurité ou un dépannage, il faut pouvoir consulter indépendamment :

CanalCatégorieContenu
security.logsecurityRequêtes refusées, tentatives d'intrusion
queries.logqueriesToutes les requêtes DNS reçues
bind.logdefaultFonctionnement général
dnssec.logdnssecSignatures, validation, erreurs crypto
xfer.logxfer-in, xfer-outTransferts de zone (AXFR/IXFR)

C.2 Configuration complète du logging

named.conf.options — bloc logging
logging {
    // Canal sécurité
    channel security_info {
        file "/var/log/named/security.log" versions 10 size 10m;
        severity info;
        print-category yes;
        print-severity yes;
        print-time yes;
    };

    // Canal requêtes
    channel query_log {
        file "/var/log/named/queries.log" versions 5 size 50m;
        severity info;
        print-category yes;
        print-severity yes;
        print-time yes;
    };

    // Canal général
    channel bind_log {
        file "/var/log/named/bind.log" versions 20 size 5m;
        severity info;
        print-category yes;
        print-severity yes;
        print-time yes;
    };

    // Canal DNSSEC
    channel dnssec_log {
        file "/var/log/named/dnssec.log" versions 10 size 5m;
        severity debug 1;
        print-category yes;
        print-severity yes;
        print-time yes;
    };

    // Canal transferts de zone
    channel xfer_log {
        file "/var/log/named/xfer.log" versions 10 size 5m;
        severity info;
        print-category yes;
        print-severity yes;
        print-time yes;
    };

    // Routage des catégories vers les canaux
    category security  { security_info; };
    category queries   { query_log; };
    category default   { bind_log; };
    category dnssec    { dnssec_log; };
    category xfer-in   { xfer_log; };
    category xfer-out  { xfer_log; };

    // Réduire le bruit
    category lame-servers     { null; };
    category delegation-only  { null; };
};

C.3 Créer le répertoire et les permissions

Bash
# Créer le répertoire de logs
mkdir -p /var/log/named
chown bind:bind /var/log/named
chmod 750 /var/log/named

# Vérifier la config
named-checkconf /etc/bind/named.conf

# Recharger
rndc reload

# Vérifier que les fichiers apparaissent
ls -la /var/log/named/
Truc
La directive versions 10 size 5m signifie : conserver 10 fichiers rotés de 5 Mo max chacun. BIND gère sa propre rotation — pas besoin de configurer logrotate pour ces fichiers.
🔐

Partie D — DNSSEC : signer vos zones

Garantir l'authenticité de vos réponses DNS avec des signatures cryptographiques.

D.1 Pourquoi DNSSEC ?

Sans DNSSEC, un attaquant peut falsifier les réponses DNS (DNS spoofing/poisoning) et rediriger vos utilisateurs vers un serveur malveillant. DNSSEC ajoute une signature cryptographique à chaque réponse, vérifiable par toute la chaîne de confiance (root → TLD → votre domaine).

1
🔑 BIND génère une paire de clés (KSK/ZSK ou CSK) par zone
2
✍️ BIND signe automatiquement chaque enregistrement (inline-signing)
3
📤 Le CDS/CDNSKEY apparaît automatiquement dans la zone
4
🔗 Vous publiez le DS chez votre registrar (Gandi, OVH, etc.) → la chaîne de confiance est activée

D.2 Créer la politique DNSSEC

Depuis BIND 9.16+, la méthode recommandée est dnssec-policy (qui remplace auto-dnssec maintain). On utilise l'algorithme ECDSAP256SHA256 (algo 13) — moderne, performant, recommandé par l'ANSSI.

named.conf.options
dnssec-policy "standard" {
    keys {
        // CSK = Combined Signing Key (KSK + ZSK en une seule clé)
        csk key-directory lifetime unlimited algorithm ecdsap256sha256;
    };
    nsec3param iterations 0 optout no salt-length 0;
    dnskey-ttl 3600;
    publish-safety 1h;
    retire-safety 1h;
    zone-propagation-delay 300;
    max-zone-ttl 86400;
    parent-ds-ttl 3600;
    parent-propagation-delay 1h;
};
N'oubliez pas
CSK (Combined Signing Key) combine KSK et ZSK en une seule clé. Avec ECDSA P-256, c'est la recommandation actuelle — une seule clé de 256 bits suffit, les signatures sont plus petites que RSA, et la gestion est simplifiée. Le lifetime unlimited signifie que la clé ne sera pas rotée automatiquement — un rollover manuel reste possible.

D.3 Activer DNSSEC sur une zone

Pour chaque zone, ajouter 3 lignes dans les deux views :

Dans chaque bloc zone
zone "example.com" {
    type master;
    file "example.com.hosts";                 // view internal
    // file "external/example.com.hosts";     // view external
    dnssec-policy "standard";
    inline-signing yes;
    key-directory "/etc/bind/keys";
};

Créer le répertoire de clés

Bash
mkdir -p /etc/bind/keys
chown bind:bind /etc/bind/keys
chmod 750 /etc/bind/keys

# Valider et recharger
named-checkconf /etc/bind/named.conf
rndc reload

D.4 Vérifier la signature

Bash
# Vérifier que les RRSIG apparaissent
dig @localhost +dnssec example.com SOA +short

# Attendu : le SOA + une ligne RRSIG

# Vérifier l'apparition du CDS (peut prendre ~25h)
dig @localhost example.com CDS +short

# Vérifier les clés générées
ls -la /etc/bind/keys/

D.5 Publier le DS chez le registrar

Une fois le CDS apparu, récupérez le DS et publiez-le chez votre registrar.

Récupérer le DS

Bash
# Le CDS contient les informations du DS
dig @localhost example.com CDS +short
# Retourne : key-tag algo digest-type hash

# Ou récupérer le DNSKEY et calculer le DS
dig @localhost example.com DNSKEY +short
# Puis coller la DNSKEY dans l'interface registrar

Chez Gandi (exemple)

Connectez-vous sur l'interface Gandi → Domaines → votre domaine
Onglet « Glue records & DNSSEC »
Cliquer « Ajouter DNSSEC »
Saisir le key-tag, l'algorithme (13 = ECDSAP256SHA256), le digest type (2 = SHA-256) et le digest
Valider → propagation 10-60 min

D.6 Valider la chaîne de confiance

Bash
# Vérifier le DS chez le parent
dig DS example.com +short @8.8.8.8

# Vérifier la validation par un résolveur validant
dig +dnssec example.com SOA @1.1.1.1
# Le flag "ad" (Authenticated Data) doit être présent dans la réponse

# Analyse visuelle complète
# https://dnsviz.net/d/example.com/dnssec/
Attention
Un DS incorrect casse la résolution de votre domaine pour tous les résolveurs validants (Google DNS, Cloudflare, etc.). Vérifiez systématiquement que le key-tag du DS correspond au key-tag de votre DNSKEY (flags 257) avant de publier chez le registrar. En cas de doute, DNSViz est votre meilleur ami.

D.7 Automatiser DNSSEC sur toutes les zones

Pour déployer DNSSEC massivement (dizaines de zones), un script Python peut modifier les fichiers de configuration des views :

Bash
# 1. Créer les liens symboliques manquants
cd /etc/bind/zones
for f in *.hosts; do
  [ ! -e "external/$f" ] && ln -s "/etc/bind/zones/$f" "external/$f"
done

# 2. Ajouter dnssec-policy dans les views (si pas déjà présent)
# Vérifier manuellement ou avec un script qui parse les blocs zone
# et insère les 3 lignes après la ligne "file"

# 3. Valider TOUJOURS après modification
named-checkconf /etc/bind/named.conf && echo "CONFIG OK"

# 4. Recharger
rndc reload
Truc
Après un rndc reload, BIND signe toutes les zones en arrière-plan. Pour surveiller la progression : tail -f /var/log/named/dnssec.log. Les clés apparaissent dans /etc/bind/keys/ au format Kdomaine.+013+XXXXX.key (013 = algo ECDSA).
🛡️

Partie E — CAA : contrôler qui peut émettre vos certificats

Empêcher un attaquant d'obtenir un certificat TLS valide pour votre domaine.

E.1 Le problème

Sans enregistrement CAA, n'importe quelle autorité de certification (CA) peut émettre un certificat pour votre domaine. Un attaquant qui a compromis une CA peu scrupuleuse pourrait obtenir un certificat *.votredomaine.com parfaitement valide.

E.2 Le mécanisme CAA

L'enregistrement DNS CAA (RFC 8659) dit aux CA : « seules ces autorités ont le droit d'émettre des certificats pour ce domaine ». Toute CA conforme vérifie le CAA avant d'émettre.

E.3 Inventorier vos CA avant de configurer

Interdit
Ne configurez jamais un CAA sans avoir inventorié les certificats existants. Un CAA trop restrictif empêchera le renouvellement de vos certificats en production. L'erreur classique : mettre uniquement letsencrypt.org alors qu'un sous-domaine utilise DigiCert.

Inventoriez d'abord les certificats émis pour votre domaine via Certificate Transparency :

Bash
# Scanner les CA qui ont émis des certificats pour votre domaine
curl -s "https://crt.sh/?q=%.example.com&output=json" | \
  python3 -c "
import json, sys
data = json.load(sys.stdin)
issuers = set()
for cert in data:
    issuers.add(cert.get('issuer_name',''))
for i in sorted(issuers):
    print(i)
"

E.4 Configurer les enregistrements CAA

Dans votre fichier de zone :

Zone file
; CAA — Autorités de certification autorisées
@       IN      CAA     0 issue "letsencrypt.org"
@       IN      CAA     0 issue "digicert.com"
@       IN      CAA     0 issue "sectigo.com"
@       IN      CAA     0 iodef "mailto:securite@votredomaine.com"
TagSignification
issueAutorise cette CA à émettre des certificats pour le domaine et ses sous-domaines
issuewildAutorise cette CA à émettre des certificats wildcard (*.domaine)
iodefAdresse email de notification si une CA refuse une émission à cause du CAA

E.5 Héritage et sous-domaines

Les enregistrements CAA sont hérités vers le bas. Si example.com a un CAA issue "letsencrypt.org", alors mail.example.com hérite de cette restriction — sauf s'il a son propre CAA.

N'oubliez pas
Si un sous-domaine utilise une CA différente du domaine racine, vous avez deux options : soit lister toutes les CA à la racine (plus permissif, plus simple), soit ajouter un CAA spécifique sur le sous-domaine. La première option est recommandée pour les déploiements multi-CA.

E.6 Vérifier le déploiement

Bash
# Incrémenter le serial dans le fichier de zone
# Puis recharger
dns-reload example.com

# Vérifier
dig @localhost example.com CAA +short
# Attendu : 3 lignes issue + 1 ligne iodef
🐛

Partie F — Les pièges qu'on a évités pour vous

Retour d'expérience sur des déploiements réels.
❌ « zone was found in multiple views »
Cause
Après la migration vers les views, un rndc reload example.com sans préciser la view échoue.
Solution
Préciser la view : rndc reload example.com IN internal puis IN external. Ou utiliser le script dns-reload.
❌ DNSSEC : BIND sert un ancien enregistrement malgré la modification du fichier de zone
Cause
Avec inline-signing, BIND utilise un fichier .signed et un journal .jnl en mémoire. L'ancien enregistrement persiste dans le cache signé.
Solution
rndc freeze example.com IN internal puis rndc thaw example.com IN internal. Si ça ne suffit pas, supprimer .signed et .jnl puis redémarrer BIND.
❌ DNSSEC : « 'dnssec-policy' redefined »
Cause
Un script d'automatisation a ajouté les lignes DNSSEC sur une zone qui les avait déjà partiellement.
Solution
Toujours vérifier avec named-checkconf après modification. Le script doit détecter les zones déjà signées avant d'ajouter les directives.
❌ DNSSEC : DS publié mais chaîne invalide sur DNSViz
Cause
Incohérence entre les NS déclarés au registrar (délégation parent) et les NS déclarés dans la zone (RRset autoritaire). Certains NS sont « fantômes » (ne répondent plus).
Solution
Aligner la liste des NS chez le registrar avec ceux réellement déclarés dans la zone. Supprimer les NS qui ne répondent plus. Vérifier avec dig NS example.com @8.8.8.8 vs dig NS example.com @votreNS.
❌ Noms d'infrastructure interne dans les certificats CT
Cause
Des certificats Let's Encrypt ont été émis avec des noms internes (ex: esx-3.example.com, db-1.example.com) dans les SAN. Les logs Certificate Transparency sont publics et permanents.
Solution
Utiliser une PKI interne pour les noms d'infrastructure. Ne jamais émettre de certificats publics pour des noms internes. Le dégât CT est irréversible — documenter le risque résiduel.
Récapitulatif des commandes essentielles
Vérifier la config : named-checkconf /etc/bind/named.conf
Vérifier une zone : named-checkzone example.com /etc/bind/zones/example.com.hosts
Recharger une zone : dns-reload example.com
Recharger tout : rndc reload
Tester DNSSEC : dig +dnssec example.com SOA @1.1.1.1 (chercher le flag ad)
Tester CAA : dig example.com CAA +short
Visualiser DNSSEC : https://dnsviz.net/d/example.com/dnssec/
Logs DNSSEC : tail -f /var/log/named/dnssec.log
Logs sécurité : tail -f /var/log/named/security.log