Table des matières

Pi-Hole with unbound upstream server

Il n'est plus nécessaire de présenter pi-hole.net tant cet outil rends le contrôle et la réduction des trackers/publicité facile.

En migrant mon instance pi-Hole vers docker, je me suis heurté à un soucis imprévu, l'image officielle ne contient pas de serveur “upstream” intégré (c'est en discussion pour une future révision); elle est parfaitement fonctionnelle avec des DNS upstream tel que google, quad9, cloudflare, mais si l'on veut réellement être indépendant, il manque son propre référentiel.

il existe des images Pihole modifiées, mais celle-ci peuvent ne pas suivre la cadence d'update ou contenir des packages en plus que l'on ne désire pas.

Avec docker, il est très facile d'ajouter un container “unbound” et de le configurer en serveur upstream, pour ensuite faire pointer pi-Hole sur ce container.

Cahier de charges

Le stack doit être basé sur des images officielles et maintenues, pour l'image de pi-hole, c'est facile, ça sera l'image officielle, pour ce qui est unbound, il y a un peu plus de choix; j'ai choisis celle de Matthew Vance.

J'ai décidé de composer mon propre “stack” pour lier les deux containers tout en gardant une configuration un peu particulière: je veux que le serveur unbound soit sur un port différent : 5335 du serveur dns.

Les raisons sont multiples, mais la plus évidente est d'éviter que unbound soit publier par accident sur le réseau (ce DNS serveur n'est absolument pas filtré et peut donc présenter des risques importants).

pour parvenir a ce résultat, il va falloir modifier les configurations du serveur unbound, mais aussi de modifier le healthcheck de ce container.

En résumé

Architecture

tutoriaux:docker-related:dns-pihole-unbound-arch.png

cette architecture est décrite dans le fichier compose (en fin d'article), mais il est important de préciser quelques paramètres, notamment votre timezone pour les deux containers.

unbound

dans la partie service “unbound”:

   environment:
      - "TZ=Europe/Brussels"

pi-hole

dans la partie service “pi-hole”, il convient également de préciser le mot de passe de votre pi-hole, sans quoi pi-hole génèrera un mot de passe aléatoire qu'il faudra retrouver dans les logs.

   environment:
      - "TZ=Europe/Brussels"
      - "WEBPASSWORD=[MONPASSWORD]"

unbound

Le container de Matthew contient tout ce qu'il faut pour avoir un unbound serveur avec un résolveur privé et un indicateur de healthcheck.

Par défaut l'image de Matthew utilise les résolvers de cloudflare, ce qui ne me convient pas; le serveur “écoute” sur le port udp:53 (port par défaut des DNS) et docker vérifie la santé du container en testant ce même port, il conviendra donc de modifier ces paramètres en éditant les fichiers de configuration qui sont stocké dans un volume persistant:

  volumes: 
    - "unbound-etc:/opt/unbound/etc/unbound"

Nous allons donc modifier certains fichiers “par défaut” de Matthew.

Je me suis inspiré de la documentation de pi-Hole concernant l'usage de unbound comme résolveur.

on va surtout modifier les paramètres d'écoute du serveur, en remplaçant le binding localhost 127.0.0.1 par tout les interfaces 0.0.0.0 et en vérifiant le port d'écoute 53

    # Listen to for queries from clients and answer from this network interface
    # and port.
    interface: 0.0.0.0
    port: 53
    do-ip4: yes
    do-udp: yes
    do-tcp: yes
 
    # May be set to yes if you have IPv6 connectivity
    do-ip6: yes

Ces paramètres sont également a configurer dans le fichier compose, pour valider le healthcheck:

    healthcheck:
      test: drill @127.0.0.1 -p 53 cloudflare.com || exit 1
      interval: 30s
      timeout: 30s
      retries: 3
      start_period: 10s

On va également vérifier et adapter les droits d'accès a notre serveur DNS:

    ###########################################################################
    # SECURITY SETTINGS
    ###########################################################################
    # Only give access to recursion clients from LAN IPs
    access-control: 127.0.0.1/32 allow
    access-control: 192.168.0.0/16 allow
    access-control: 172.16.0.0/12 allow
    access-control: 10.0.0.0/8 allow
    # access-control: fc00::/7 allow
    # access-control: ::1/128 allow

On va retirer la référence au “forwarder” de Matthew, en commentant l'inclusion du fichier:

    ###########################################################################
    # FORWARD ZONE
    ###########################################################################
 
    #include: /opt/unbound/etc/unbound/forward-records.conf

j'invite le lecteur a aller consulter ce fichier, il contient énormément d'informations et de proposition de DNS externes assez détaillés. vous y trouverez plusieurs classifications de serveurs sources déjà filtrés (malware, adulte, etc.) ainsi que d'autres forwarder (quad9, etc.)

Pour ma facilité de test (et ne pas de voir a chaque fois redémarrer le container complétement, j'ai autorisé le contrôle, mais en production ce paramètre peut être laissé par défaut sur no:

remote-control:
    control-enable: yes

pi-hole

Pour accéder à l'interface de Pi-Hole, il suffit de se connecter via un browser (en http) sur l'ip du host docker en précisant le port (8053) et l'uri /admin (par exemple: http://192.168.1.253:8053/admin), vous serez accueilli par une page de login:

il suffit de référencé votre mot de passe, et de se connecter, et de directement aller vers les “settings”, “DNS”:

Par défaut, Pi-Hole ne réponds que aux clients situé sur le même réseau et sub-net (172.20.x.x), il transmet ensuite les requêtes aux serveurs de Google, ces paramètres ne conviennent pas, mais nous allons les changer:

Il nous suffit de configurer les clients pour utiliser ce serveur DNS Pi-Hole.

fichiers

name:stack-dns-pihole-unbound-custom.yaml
networks:
  infrastructure:
    external: true
    name: "infrastructure"

services:
  unbound:

    command:
      - "/unbound.sh"

    healthcheck:
      test: drill @127.0.0.1 cloudflare.com || exit 1
      interval: 30s
      timeout: 30s
      retries: 3
      start_period: 10s
      
    container_name: dns_unbound

    environment:
      - "PATH=/opt/unbound/sbin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"
      - "NAME=unbound"
      - "SUMMARY=is a validating, recursive, and caching DNS resolver."
      - "DESCRIPTION=is a validating, recursive, and caching DNS resolver."
      - "TZ=Europe/Brussels"

    expose:
      - "5335/tcp"
      - "5335/udp"

    hostname: "97b5d3c8a2cc"

    image: "mvance/unbound:latest"

    ipc: "private"

    labels:
      maintainer: "Matthew Vance"
      org.opencontainers.image.description: "a validating, recursive, and caching DNS resolver"
      org.opencontainers.image.licenses: "MIT"
      org.opencontainers.image.source: "https://github.com/MatthewVance/unbound-docker"
      org.opencontainers.image.title: "mvance/unbound"
      org.opencontainers.image.url: "https://github.com/MatthewVance/unbound-docker"
      org.opencontainers.image.vendor: "Matthew Vance"
      org.opencontainers.image.version: ""

    logging:
      driver: "json-file"
      options: {}

    networks:
      infrastructure:
        ipv4_address: 172.20.0.200

    restart: "unless-stopped"

    volumes:
      - "unbound-etc:/opt/unbound/etc/unbound"

    working_dir: "/opt/unbound"

  dns-pihole:

    container_name: dns_pihole

    entrypoint:
      - "/s6-init"

    environment:
      - "PATH=/opt/pihole:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"
      - "phpver=php"
      - "PHP_ERROR_LOG=/var/log/lighttpd/error-pihole.log"
      - "IPv6=True"
      - "S6_KEEP_ENV=1"
      - "S6_BEHAVIOUR_IF_STAGE2_FAILS=2"
      - "S6_CMD_WAIT_FOR_SERVICES_MAXTIME=0"
      - "FTLCONF_LOCAL_IPV4=0.0.0.0"
      - "FTL_CMD=no-daemon"
      - "DNSMASQ_USER=pihole"
      - "TZ=Europe/Brussels"
      - "WEBPASSWORD=[MONPASSWORD]"

    hostname: "pi-Hole"

    image: "pihole/pihole:latest"

    ipc: "private"

    labels:
      org.opencontainers.image.created: "2024-06-16T19:54:20.386Z"
      org.opencontainers.image.description: "Pi-hole in a docker container"
      org.opencontainers.image.licenses: "NOASSERTION"
      org.opencontainers.image.revision: "4149693092ea364c7aab6c30ba0b308e4bc45716"
      org.opencontainers.image.source: "https://github.com/pi-hole/docker-pi-hole"
      org.opencontainers.image.title: "docker-pi-hole"
      org.opencontainers.image.url: "https://github.com/pi-hole/docker-pi-hole"
      org.opencontainers.image.version: "2024.06.0"

    logging:
      driver: "json-file"
      options: {}

    networks:
      infrastructure:
        ipv4_address: 172.20.0.201

    ports:
      - "53:53/tcp"
      - "53:53/udp"
      - "8053:80/tcp"

    restart: "unless-stopped"

    volumes:
      - "pihole_config:/etc/pihole"
      - "pihole_dnsmasq:/etc/dnsmasq.d"
 

volumes:
  unbound-etc:
  pihole_config:
  pihole_dnsmasq: