BIND9 Hardened : DNS autoritatif FROM scratch

Rôle dans l'architecture

BIND9 est le serveur DNS central du homelab. Il gère à la fois les zones internes (.home.arpa) et la zone publique, avec un système de vues (split-horizon) qui adapte les réponses selon le réseau source du client.

flowchart LR
    subgraph Internes
        C["Clients internes"]
    end
    subgraph Externes
        E["Requetes externes"]
    end
    subgraph BIND9
        VS["Vue per-subnet<br/>recursion autorisee"]
        VL["Vue local<br/>zones completes"]
        VI["Vue internet<br/>autoritaire uniquement"]
    end
    C -->|"DNAT rule"| VS
    C -->|"DNAT rule"| VL
    E -->|"WAN DNAT"| VI

Tout le DNS interne est intercepté par une règle DNAT sur le routeur : aucun client ne sort en DNS directement vers Internet.

Stratégie de hardening

L'image bind9-hardened est compilée from source (ISC BIND 9.20.24) avec les flags de hardening complets :

Protection Flag
Full RELRO -Wl,-z,relro,-z,now
PIE (ASLR complet) -fPIE -pie
Stack Protector -fstack-protector-strong
Stack Clash Protection -fstack-clash-protection
FORTIFY_SOURCE -D_FORTIFY_SOURCE=2
Non-executable stack -Wl,-z,noexecstack

L'image finale est FROM scratch : aucun shell, aucun outil système. Seuls 3 binaires sont présents :

  • named : le serveur DNS (550 KB strippé)
  • named-checkconf : validation de configuration
  • init : binaire Go statique (entrypoint + healthcheck)

Configuration clé

# named.conf.options (extrait)
options {
    directory "/var/cache/bind";

    # Hardening : masquer les informations
    version "not disclosed";
    hostname "not disclosed";
    server-id "not disclosed";
    minimal-responses yes;

    # Forwarders
    forwarders { 1.1.1.1; 9.9.9.9; 8.8.8.8; };
    forward only;

    # Restriction récursion aux clients de confiance
    allow-recursion { trusted; };
    allow-query-cache { trusted; };

    # Rate limiting (protection DDoS)
    rate-limit {
        responses-per-second 10;
        window 5;
        exempt-clients { trusted; };
    };
};

Healthcheck DNS (Go)

Le healthcheck est une requête DNS UDP réelle envoyée à 127.0.0.1:53. Contrairement à un simple TCP connect, cette approche vérifie le pipeline DNS complet :

// Requête CH TXT version.bind
query := []byte{
    0xBE, 0x9D, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00,
    0x07, 'v', 'e', 'r', 's', 'i', 'o', 'n',
    0x04, 'b', 'i', 'n', 'd', 0x00,
    0x00, 0x10, 0x00, 0x03, // QTYPE=TXT, QCLASS=CH
}
// Accepte ANY réponse valide (QR=1), même REFUSED

Métriques

Métrique Avant (Ubuntu) Après (hardened)
Taille image ~150 MB 19 MB (-87%)
Binaires présents ~200+ 3
Shell bash, sh, dash Aucun
User runtime root → drop privs UID 5300 natif
Version BIND 9.20.0 (Ubuntu pkg) 9.20.24 (source)
Healthcheck Aucun DNS query UDP

Points notables

  • Split-horizon : 18 vues configurées, une par subnet.
  • TSIG/ACME : clé HMAC-SHA512 pour les updates DNS Let's Encrypt. Policy restreinte aux enregistrements _acme-challenge TXT.
  • Autoconf (pas meson) : BIND 9.20.x utilise encore autoconf. Le switch meson est prévu pour 9.21+.
  • DoH désactivé : --disable-doh à la compilation (pas utilisé en interne).
  • setcap : cap_net_bind_service+ep sur le binaire named pour bind port 53 sans root.

Liens

Articles connexes


Commentaires

Laisser un commentaire

Votre adresse e-mail ne sera pas publiée. Les champs obligatoires sont indiqués avec *

Ce site utilise Akismet pour réduire les indésirables. En savoir plus sur la façon dont les données de vos commentaires sont traitées.