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 configurationinit: 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-challengeTXT. - 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+epsur le binaire named pour bind port 53 sans root.
Liens
- Code source : bind9-hardened
- Image Docker : Docker Hub
Laisser un commentaire