Blog & Astuces

Créer un service DNS over TLS et DNS over HTTPS avec Pi-Hole et Nginx

Créer un service DNS over TLS et DNS over HTTPS avec Pi-Hole et Nginx

Tuto

Ce tutoriel fait suite au tutoriel sur la mise en place de Pi-Hole que vous pouvez retrouver ici.

Dans ce tutoriel, je vais montrer comment mettre un place un service DNS over TLS et DNS over HTTPS avec Pi-Hole et Nginx.

Pré-requis

  • Un serveur Linux
  • Avoir installé et configuré Docker et Pi-Hole grâce au précédent tutoriel
  • Avoir un nom de domaine avec un certificat TLS valide (si ce n'est pas le cas, j'indique dans le tutoriel comment en générer un avec Let's Encrypt)

Introduction

DNS over TLS et DNS over HTTPS sont des protocoles sécurisés qui permettent aux clients d'effectuer des requêtes DNS. Il est plus sécurisé que le protocole DNS standard (port 53) car le traffic DNS est chiffré de bout-en-bout entre le serveur et le client.

Mettre en place votre propre service DNS over TLS et DNS over HTTPS permettra à vos appareils d'utiliser votre instance de Pi-Hole pour effectuer les requêtes DNS, même lorsque vous n'êtes pas chez vous. Vous pouvez ainsi résoudre le problème du blocage des publicités non fonctionnel sur votre smarpthone en 4G/5G par exemple.

La différence entre les deux services, c'est que DNS over TLS fonctionne sur TCP directement, alors que DNS over HTTPS fonctionne grâce aux protocoles web standard (comme son nom l'indique).

Au niveau de vos appareils, sous Android par exmple le DoT est activable de manière globale dans les paramètres du système, là où le DoH n'est activable que dans les navigateurs web. Sur Windows et Linux, le DoH est aussi activable dans Chrome et Firefox par exemple.

Nous allons mettre en place ces services grâce au serveur nginx, qui permet de le faire de manière assez facile et fiable. Nous allons mettre en place une instance de nginx par service (DoT et DoH) en mode Docker.

Ce tutoriel sera séparé en deux : une partie traitera du DNS over TLS et l'autre du DNS over HTTPS.

Avant de pouvoir mettre en place le service, il faudra avoir généré un certificat TLS valide pour votre nom de domaine.

Générer un certificat TLS pour votre nom de domaine

Cette partie est commune au DoT et au DoH.

Vous pouvez générer un certificat gratuitement grâce à Let's Encrypt pour votre domaine, si vous n'en avez pas déjà un disponible.

Vous pouvez le faire grâce à certbot, plus d'informations ici.

Installez certbot, par exemple sous Ubuntu vous pouvez le faire comme suit :

sudo snap install --classic certbot
sudo ln -s /snap/bin/certbot /usr/bin/certbot

Puis générer un certificat avec cette commande (ceci est un exemple de commande) :

sudo certbot certonly --agree-tos --email [VOTRE_ADRESSE_EMAIL] --cert-name [VOTRE_NOM_DE_DOMAINE] -d [VOTRE_NOM_DE_DOMAINE] --preferred-challenges [METHODE_DE_VERIFICATION] --preferred-chain="ISRG Root X1"

Remplacez les valeurs par ce qui va bien, mais gardez toujours le --preferred-chain="ISRG Root X1". En effet, à cause d'un bug d'Android, le DNS over TLS ne fonctionne pas avec la chaîne de certification par défaut de Let's Encrypt. Cela permet d'éviter le problème.

Quant aux challenges, plus d'infos ici : https://eff-certbot.readthedocs.io/en/latest/using.html#plugins

Si votre serveur est derrière un Cloudflare, vous pouvez mettre en place un challenge DNS avec les clés d'API de votre compte Cloudflare.

Créez un fichier de configuration comme suit :

dns_cloudflare_api_token = [TOKEN_API]

La clé API peut être générée depuis votre compte Cloudflare comme suit : https://certbot-dns-cloudflare.readthedocs.io/en/stable/#credentials

Ensuite, lancez la commande :

sudo certbot certonly --agree-tos --email [VOTRE_ADRESSE_EMAIL] -a dns-cloudflare --cert-name [VOTRE_NOM_DE_DOMAINE] --dns-cloudflare-credentials [CHEMIN_VERS_LE_FICHIER_DE_CONFIGURATION_CLOUDFLARE] -d [VOTRE_NOM_DE_DOMAINE] --preferred-challenges dns-01 --preferred-chain="ISRG Root X1"

Si tout s'est bien passé, un certificat sera généré dans le dossier /etc/letsencrypt/live/[VOTRE_NOM_DE_DOMAINE]/. Nous utiliserons ce certificat par la suite.

Le certificat sera ensuite régénéré automatiquement par certbot avant expiration. En effet, les certificats Let's Encrypt durent 3 mois.

Mise en place de DNS over TLS

Mise en place de l'instance de nginx

Créez un fichier docker-compose.yml, qui va permettre de mettre en place l'instance de nginx que nous allons utiliser pour créer le service DoT :

version: "3"
services:
  nginx:
    image: nginx:stable-alpine
    container_name: nginx_reverse_proxy_dot
    network_mode: host
    restart: unless-stopped
    ports:
      - 853:853
    volumes:
      - ./nginx.conf:/etc/nginx/nginx.conf
      - ./error.log:/var/log/nginx/error.log
      - ./access.log:/var/log/nginx/access.log
      - /etc/letsencrypt/live/[VOTRE_NOM_DE_DOMAINE]/fullchain.pem:/etc/letsencrypt/live/[VOTRE_NOM_DE_DOMAINE]/fullchain.pem
      - /etc/letsencrypt/live/[VOTRE_NOM_DE_DOMAINE]/privkey.pem:/etc/letsencrypt/live/[VOTRE_NOM_DE_DOMAINE]/privkey.pem

Configuration de nginx

Ensuite, créez le fichier de configuration de nginx. Créez le fichier nginx.conf dans le même dossier que le fichier docker-compose.yml, puis entrez les valeurs suivantes :

events {
   worker_connections 1024;
}

worker_processes [NOMBRE_DE_COEURS_DE_PROCESSEUR];
worker_cpu_affinity auto;

http {
   server_tokens off;
}

stream {
   log_format basic '$remote_addr [$time_local] '
                 '$protocol $status sent=$bytes_sent received=$bytes_received '
                 '$session_time';
   access_log /dev/stdout basic buffer=16k;
   error_log /var/log/nginx/error.log error;

   limit_conn_zone $binary_remote_addr zone=addr:10M;

   upstream dns {
       zone dns 64k;
       server 127.0.0.1:53;
   }

   # DoT server for decryption
   server {
        listen 853 ssl;
        ssl_protocols TLSv1.2 TLSv1.3;
        ssl_handshake_timeout 30s;
        ssl_session_timeout 4h;
        ssl_session_cache shared:DNS:10m;
        ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384;
        ssl_certificate /etc/letsencrypt/live/[VOTRE_NOM_DE_DOMAINE]/fullchain.pem;
        ssl_certificate_key /etc/letsencrypt/live/[VOTRE_NOM_DE_DOMAINE]/privkey.pem;
        proxy_pass dns;
        limit_conn addr 18;
        limit_conn_log_level info;
    }
}

Les éléments suivants de la configuration sont à modifier :

  • worker_processes : Doit correspondre au nombre de coeurs de votre processeur
  • server sous upstream dns : Correspond à l'adresse IP du serveur DNS. Ici serveur local (127.0.0.1) écoutant sur le port 53. Doit correspondre à votre serveur Pi-Hole
  • ssl_certificate et ssl_certificate_key : Chemin vers votre certificat TLS (la fullchain et la private key)
  • limit_conn : active du rate limiting à 18 connexions en même temps pour une IP. Vous devrez probablement ajuster cette valeur. Vous pouvez aussi la retirer mais la sécurité de votre instance sera moins bonne.

Lancement du service

Ensuite, lancez le service avec la commande docker-compose up -d. Vérifiez dans les logs si tout est OK : docker container logs nginx_reverse_proxy_dot.

Paramétrage du pare-feu

Vous devrez ensuite autoriser en entrée l'accès au port 853 de votre serveur. Si vous utilisez ufw, vous pouvez le faire de cette manière :

sudo ufw allow 853/tcp
sudo ufw reload

Ensuite, autorisez via le pare-feu de votre routeur le port 853/tcp en entrée. Avec le NAT, pointez ensuite vers le port 853 de votre serveur en entrée.

Votre service devrait être fonctionnel. Pour tester, vous pouvez le mettre en place sur votre appareil Android ou Apple.

Pour Android, voici la marche à suivre : https://support.google.com/android/answer/9654714?hl=fr#zippy=%2Cdns-priv%C3%A9 Vous devrez sélectionner "Nom d'hôte du fournisseur DNS privé" puis entrer votre nom de domaine.

Mise en place de DNS over HTTPS

Mise en place de l'instance de nginx

De la même manière que le DoT, créer un fichier docker-compose.yml comme suit :

version: "3"
services:
  nginx:
    image: nginx:stable-alpine
    container_name: nginx_reverse_proxy_doh
    network_mode: host
    restart: unless-stopped
    ports:
      - 443:443
    volumes:
      - ./nginx.conf:/etc/nginx/nginx.conf
      - ./error.log:/var/log/nginx/error.log
      - ./access.log:/var/log/nginx/access.log
      - /etc/letsencrypt/live/[VOTRE_NOM_DE_DOMAINE]/fullchain.pem:/etc/letsencrypt/live/[VOTRE_NOM_DE_DOMAINE]/fullchain.pem
      - /etc/letsencrypt/live/[VOTRE_NOM_DE_DOMAINE]/privkey.pem:/etc/letsencrypt/live/[VOTRE_NOM_DE_DOMAINE]/privkey.pem
      - ./njs.d/:/etc/nginx/njs.d/

Téléchargement du module DoH pour nginx

Téléchargez ensuite le module nginx qui permettra de mettre en place le DoH ici : https://github.com/TuxInvader/nginx-dns

Téléchargez l'archive du dépôt, puis copier le dossier "njs.d" dans le même dossier que le fichier docker-compose.yml que nous avons créé auparavant.

Configuration de nginx

Créez ensuite le fichier de configuration nginx.conf comme suit :

load_module modules/ngx_stream_js_module.so;

events {
    worker_connections 1024;
}

worker_processes [NOMBRE_DE_COEURS_DE_PROCESSEUR];
worker_cpu_affinity auto;

http {
    upstream dohloop {
        zone dohloop 64k;
        server 127.0.0.1:8053;
    }

    server {
        listen         443 ssl;
        listen         [::]:443 ssl;
        server_name    [VOTRE_NOM_DE_DOMAINE];
        ssl_certificate /etc/letsencrypt/live/[VOTRE_NOM_DE_DOMAINE]/fullchain.pem;
        ssl_certificate_key /etc/letsencrypt/live/[VOTRE_NOM_DE_DOMAINE]/privkey.pem;

        http2 on;

        location /dns-query {
           proxy_http_version 1.1;
           proxy_set_header Connection "";
           proxy_pass http://dohloop;
        }
    }
}

stream {
   js_import /etc/nginx/njs.d/dns/dns.js;
   limit_conn_zone $binary_remote_addr zone=addr:10M;

   # DNS upstream pool
   upstream dns {
       zone dns 64k;
       server 127.0.0.1:53;
   }

   # DoH server for decryption
   server {
      listen 127.0.0.1:8053;
      js_filter dns.filter_doh_request;
      proxy_pass dns;
   }
}

Les éléments suivants de la configuration sont à modifier :

  • worker_processes : Doit correspondre au nombre de coeurs de votre processeur
  • server sous upstream dns : Correspond à l'adresse IP du serveur DNS. Ici serveur local (127.0.0.1) écoutant sur le port 53. Doit correspondre à votre serveur Pi-Hole
  • ssl_certificate et ssl_certificate_key : Chemin vers votre certificat TLS (la fullchain et la private key)
  • server_name : Votre nom de domaine

Lancement du service

Ensuite, lancez le service avec la commande docker-compose up -d. Vérifiez dans les logs si tout est OK : docker container logs nginx_reverse_proxy_doh.

Paramétrage du pare-feu

Vous devrez ensuite autoriser en entrée l'accès au port 443 de votre serveur. Si vous utilisez ufw, vous pouvez le faire de cette manière :

sudo ufw allow 443
sudo ufw reload

Ensuite, autorisez via le pare-feu de votre routeur le port 443 en entrée. Avec le NAT, pointez ensuite vers le port 443 de votre serveur en entrée.

Votre service devrait être fonctionnel. Pour tester, vous pouvez le mettre en place sur votre navigateur web (Chrome ou Firefox supportent le DNS over HTTPS).

Partie optionnelle : sécurisation via GeoIP

Il est possible de mettre en place une sécurisation via GeoIP pour améliorer la sécurité de vos services DoT et DoH. Pour cela, nous allons devoir :

  • Télécharger une base de données GeoIP (celle de Maxmind)
  • Mettre en place une mise à jour automatique des bases de données
  • Modifier la configuration de nginx

Cette partie est traitée dans l'article : Empêcher l'accès à un service nginx depuis certains pays

Conclusion

Dans cet article, nous avons vu comment mettre en place les services DNS over TLS et DNS over HTTPS avec Pi-Hole et Nginx. Ces services fonctionnent avec des protocoles offrant une protection avancée contre la surveillance et le détournement des requêtes DNS, tout en permettant à Pi-Hole de bloquer les publicités sur tous vos appareils, où que vous soyez.

Commentaires