Ce projet Ansible déploie et configure un serveur DNS récursif Unbound avec les fonctionnalités suivantes :
- DNSSEC : Validation complète avec trust anchor automatique (RFC5011)
- Rate-limiting : Protection contre les attaques DDoS et DNS amplification
- Contrôle d'accès : Limitation de la récursion à des subnets spécifiques
- Blacklisting : Blocage de domaines malveillants/publicitaires avec NXDOMAIN
- Protection DNS rebinding : Filtrage des adresses privées
- Statistiques étendues : Métriques détaillées pour analyse
- Prometheus exporter : Export des métriques pour Grafana
- Logging structuré : Logs via journald avec niveaux configurables
- Tests DNSSEC automatiques : Validation post-installation
- Blacklists externes : Téléchargement et mise à jour quotidiens
- Subnets externes : Support pour listes d'IPs autorisées dynamiques
- Root hints : Mise à jour mensuelle automatique
- Protection des données : Conservation du cache en cas d'échec de téléchargement
- Forward-zone : Redirection vers DNS upstream configurables
- Cache optimisé : Multi-thread avec caches dimensionnés
- Aggressive NSEC : Réduction des requêtes DNS (RFC 8198)
- Prefetch : Anticipation des renouvellements de cache
.
├── install-unbound.yml # Playbook principal
├── inventory.ini # Inventaire des serveurs
├── templates/
│ ├── resolv.conf.j2 # Configuration /etc/resolv.conf
│ ├── dnssec.conf.j2 # Configuration DNSSEC
│ ├── access-control.conf.j2 # Contrôle d'accès et interfaces
│ ├── forward-zone.conf.j2 # DNS upstream (si liste d'upstream non vide)
│ ├── blacklist-manual.conf.j2 # Blacklist manuelle
│ ├── monitoring-and-security.conf.j2 # Stats et rate-limiting
│ └── update-unbound-lists.sh.j2 # Script de mise à jour
└── README.md # Ce fichier
- Ubuntu 24.04 LTS
- testé mais les OS familles Debian avec Unbound en dépôt fonctionne normalement
- Ansible 2.9+
- Accès root/sudo sur les serveurs cibles
- Connectivité Internet pour télécharger les paquets
git clone https://github.com/vignemail1/ansible-unbound.git
cd ansible-unboundCopie tous les fichiers fournis dans la structure ci-dessus :
- Le playbook
install-unbound.ymlà la racine - Tous les templates
.j2dans le répertoiretemplates/
Édite inventory.ini avec tes serveurs :
[dns_servers]
dns1.example.com ansible_host=192.168.1.10
dns2.example.com ansible_host=192.168.1.11
[dns_servers:vars]
ansible_user=root
ansible_python_interpreter=/usr/bin/python3Édite group_vars/dns_servers/main.yml et modifie les variables selon tes besoins :
# DNS système (pour l'OS lui-même)
system_dns_servers:
- "1.1.1.1"
- "8.8.8.8"
# DNS upstream (pour les clients d'Unbound)
unbound_upstream_dns:
- "1.1.1.1"
- "8.8.8.8"
# Subnets autorisés
allowed_subnets_manual:
- "192.168.1.0/24"
- "10.0.0.0/8"
# Blacklist manuelle
blackhole_domains_manual:
- "ads.example.com"
- "tracker.example.net"
# Rate-limiting
ratelimit_global: 1000
ip_ratelimit: 100
# Prometheus (optionnel)
enable_prometheus_exporter: yes# Test de connectivité
ansible -i inventory.ini dns_servers -m ping
# Déploiement (dry-run)
ansible-playbook -i inventory.ini install-unbound.yml --check
# Déploiement réel
ansible-playbook -i inventory.ini install-unbound.ymlOption 1 : Liste manuelle
Édite la variable blackhole_domains_manual dans group_vars/dns_servers/main.yml.
Option 2 : Liste externe supplémentaire
Modifie blackhole_external_url pour pointer vers ta propre liste.
Pour autoriser dynamiquement des subnets depuis une URL :
allowed_subnets_external_url: "https://example.com/allowed-subnets.txt"Format du fichier (un subnet par ligne) :
192.168.100.0/24
172.16.0.0/16
2001:db8::/48Pour qu'Unbound interroge directement les serveurs racine :
- définit
unbound_upstream_dnsavec une liste vide dansgroup_vars/dns_servers/main.yml - relance le playbook
- retire le fichier
15-forward-zone.confdans le dossier de configuration d'Unbound - vérifier la configuration et redémarre Unbound
unbound-checkconf && systemctl restart unbound
# Vérifier le statut d'Unbound
sudo systemctl status unbound
# Vérifier que le port 53 est utilisé par Unbound
sudo ss -tulpn 'sport == 53'
# Tester la résolution DNS
dig @localhost google.com
# Tester DNSSEC (doit retourner une IP avec flag 'ad')
dig @localhost dnssec.works +dnssec
# Tester le blocage DNSSEC (doit retourner SERVFAIL)
dig @localhost fail01.dnssec.works
# Vérifier les statistiques
sudo unbound-control stats_noreset
# Consulter les logs
sudo journalctl -u unbound -f# Liste des timers actifs
sudo systemctl list-timers | grep unbound
# Statut du timer de mise à jour des listes
sudo systemctl status unbound-update-lists.timer
# Logs de la dernière mise à jour
sudo journalctl -u unbound-update-lists.service# Domaine avec DNSSEC valide
dig @localhost dnssec.works +dnssec +multi
# Résultat attendu : flag 'ad' (Authenticated Data), RRSIG présents
# Domaine avec DNSSEC cassé
dig @localhost fail01.dnssec.works
# Résultat attendu : SERVFAIL
# Domaine sans DNSSEC
dig @localhost example.com
# Résultat attendu : Résolution normale sans flag 'ad'Si enable_prometheus_exporter: true :
# Vérifier que l'exporter est actif
sudo systemctl status unbound_exporter
# Consulter les métriques
curl http://localhost:9167/metricsAjoute à prometheus.yml :
scrape_configs:
- job_name: 'unbound'
static_configs:
- targets: ['dns1.example.com:9167', 'dns2.example.com:9167']- Dashboard ID 18077 : Complet avec logs (Prometheus + Loki)
- Dashboard ID 9604 : Statistiques classiques
- Dashboard ID 18053 : Style Pi-hole
Importe-les depuis https://grafana.com/grafana/dashboards/
# Forcer la mise à jour immédiate
sudo systemctl start unbound-update-lists.service
# Voir les logs de mise à jour
sudo journalctl -u unbound-update-lists.service -f# Recharger sans perdre le cache
sudo unbound-control reload_keep_cache
# Vérifier la configuration avant rechargement
sudo unbound-checkconfsudo systemctl start unbound-root-hints-update.service# Vérifier la configuration
sudo unbound-checkconf
# Vérifier les logs d'erreur
sudo journalctl -u unbound -xe
# Vérifier que systemd-resolved est bien désactivé
sudo systemctl status systemd-resolved# Vérifier le fichier root.key
ls -lh /var/lib/unbound/root.key
# Régénérer la trust anchor
sudo -u unbound unbound-anchor -a /var/lib/unbound/root.key
# Activer les logs DNSSEC détaillés
# Éditer /etc/unbound/unbound.conf.d/05-dnssec.conf
# val-log-level: 2
sudo systemctl reload unbound
sudo journalctl -u unbound -f | grep -i dnssec# Vérifier les logs du service de mise à jour
sudo journalctl -u unbound-update-lists.service -n 50
# Tester le téléchargement manuellement
curl -I https://raw.githubusercontent.com/StevenBlack/hosts/master/hosts
# Vérifier les fichiers en cache
ls -lh /var/cache/unbound/# Identifier le processus
sudo ss -tulpn 'sport == 53'
# Si systemd-resolved est toujours actif
sudo systemctl stop systemd-resolved
sudo systemctl mask systemd-resolvedDans templates/access-control.conf.j2 :
# Pour serveur avec 4GB RAM
rrset-cache-size: 256m
msg-cache-size: 128m
# Pour serveur avec 8GB+ RAM
rrset-cache-size: 512m
msg-cache-size: 256mSelon la charge attendue :
# Réseau domestique
ratelimit_global: 200-500
# PME
ratelimit_global: 500-1000
# Datacenter
ratelimit_global: 2000-5000- Limiter l'accès réseau : Configure un firewall pour n'autoriser que les subnets de confiance
- Monitoring actif : Surveille les métriques
unwanted_queriesetip_ratelimited - Mises à jour système : Maintiens Ubuntu et Unbound à jour
- Logs : Archive et analyse régulièrement les logs
# Autoriser uniquement ton réseau local
sudo ufw allow from 192.168.1.0/24 to any port 53
# Autoriser Prometheus exporter (si nécessaire)
sudo ufw allow from <prometheus_ip> to any port 9167
# Activer le firewall
sudo ufw enableCe projet est fourni "tel quel" sans garantie. Libre d'utilisation et modification.
Pour toute question ou problème :
- Vérifie les logs :
sudo journalctl -u unbound -f - Valide la config :
sudo unbound-checkconf - Consulte la documentation officielle : https://unbound.docs.nlnetlabs.nl/