Le journal qui vous décrit mieux que votre historique de navigation
Avant que votre machine puisse envoyer le moindre octet chiffré à un site, elle doit demander à quelqu'un où ce site habite. Cette question voyage en clair, vers un résolveur que vous n'avez presque certainement pas choisi, et la réponse à qui la conserve décide de la quantité de votre vie qui se retrouve consignée. Un historique de navigateur est une liste de pages que vous avez choisi de garder. Un journal de résolveur, c'est tout : chaque site, chaque application qui vérifie ses mises à jour, chaque service cloud auquel votre téléphone parle pendant que vous dormez, chaque domaine dans chaque email que vous avez ouvert, dans l'ordre, avec horodatage, qu'un humain ait regardé l'écran ou non.
Lisez-en une semaine et vous pouvez reconstituer une personne. La banque qu'elle utilise, le vol qu'elle vient de réserver, la pharmacie, l'application de rencontre, le système de suivi des candidatures du recruteur, l'heure à laquelle elle se réveille et celle à laquelle elle s'arrête. Rien de tout cela ne nécessite de casser un chiffrement. Les noms seuls suffisent à le porter, et les noms sont la seule partie de la transaction qui est encore, dans la plupart des configurations, remise à un tiers par conception.
Aujourd'hui, ce tiers est celui vers qui votre bail DHCP vous a pointé — votre FAI, votre employeur, le café du coin. Les gens qui s'en rendent compte basculent en général vers un résolveur public, ce qui est une réelle amélioration en intégrité et un déplacement latéral en matière de vie privée : vous n'avez pas supprimé l'observateur, vous avez changé quelle entreprise l'incarne, échangeant un opérateur télécom réglementé contre un CDN proche de la publicité ou une association dont la politique peut changer avec son financement. Les bons publient des politiques de rétention honnêtes et certains les respectent vraiment. Mais une politique est une promesse sur un comportement, et un fichier de configuration est un énoncé sur une capacité. Vous ne pouvez pas auditer une promesse. Vous pouvez auditer vingt lignes que vous avez écrites vous-même, sur une machine que vous louez, dans un pays que vous avez choisi.
SP·02Le forwarding déplace le journal. La récursion l'éparpille.
Presque tous les tutoriels qui disent "installez votre propre serveur DNS" finissent par construire un forwarder : un petit cache sur votre réseau qui transmet tout ce qu'il ne connaît pas à 1.1.1.1 ou 9.9.9.9. C'est une chose réellement utile — c'est rapide, ça tient en cinq lignes, et ça empêche votre réseau local de vous observer. Cela laisse aussi le journal agrégé exactement là où il était. Chaque nom que vous recherchez arrive toujours chez la même entreprise, désormais commodément pré-étiqueté avec l'adresse IP unique de votre résolveur, ce qui rend le flux plus facile à attribuer plutôt que l'inverse.
Un résolveur récursif fait le travail plutôt que de le déléguer. Interrogé sur news.example.io, il demande à un serveur racine qui gère .io, puis demande aux serveurs de .io qui gère example.io, puis interroge directement cet opérateur. Trois conversations avec trois parties sans lien entre elles, dont aucune n'est dans le métier de l'agrégation, et — c'est le point qui compte — aucune d'entre elles ne voit jamais votre flux de requêtes complet. Les opérateurs racine voient un filet de requêtes de TLD. Verisign voit que quelqu'un à votre adresse a touché quelque chose sous .com. Le serveur faisant autorité pour un domaine voit le trafic qu'il allait de toute façon voir, puisque vous êtes sur le point de vous y connecter.
La minimisation QNAME affine considérablement cela, et l'Unbound moderne le fait par défaut. Plutôt que d'envoyer le nom complet à chaque serveur de la chaîne — ce que les résolveurs ont fait pendant trente ans — il n'envoie à chaque serveur que le label dont il a besoin pour répondre : io. à la racine, example.io. aux serveurs de .io, et ce n'est qu'alors le nom complet news.example.io. à l'opérateur qui y a droit. La hiérarchie cesse d'être une diffusion de vos intentions et devient ce pour quoi elle a été dessinée : une délégation.
Le compromis est réel et vaut la peine d'être énoncé clairement. Une résolution récursive à froid prend plusieurs allers-retours là où un forwarder n'en prend qu'un, donc la première visite d'un domaine inconnu est mesurablement plus lente. Vous héritez de la responsabilité d'un cache qui était auparavant le problème de quelqu'un d'autre. Et vous ne bénéficiez plus d'un cache partagé, chauffé par des millions d'autres personnes. En échange, l'enregistrement complet, ordonné et horodaté de ce que vous avez consulté cesse d'exister ailleurs que sur votre propre disque.
SP·03Ce que cela cache, et ce que cela ne cache clairement pas
Le périmètre honnête est plus étroit que ce que le marketing autour du DNS privé laisse entendre, et connaître ses limites est ce qui vous évite de prendre une mauvaise décision sur la seule force d'un bon sentiment.
Cela supprime une chose : le journal des noms agrégé détenu par un observateur unique. C'est une chose importante, parce que c'est l'agrégat qui a une valeur commerciale et qui fait l'objet de demandes en masse. Ce n'est pas tout.
Cela ne cache pas la connexion. Une fois la résolution effectuée, votre appareil ouvre quand même une session vers cette adresse, et quiconque observe votre liaison montante voit l'IP de destination. Pour un site sur une infrastructure dédiée, l'IP est l'identité. Cela ne cache pas non plus le hostname sur le fil : à moins que les deux extrémités ne prennent en charge Encrypted Client Hello, la poignée de main TLS transporte toujours le nom du serveur en clair, ce qui est la même information que votre résolveur aurait eue.
Cela ne cache pas les requêtes à votre hébergeur. C'est le point que les gens comprennent le plus souvent à l'envers, donc il mérite d'être énoncé sans détour : le trafic amont de votre résolveur — les questions qu'il pose à la racine, au TLD et aux serveurs faisant autorité — quitte le VPS sur le port UDP 53, non chiffré, et le réseau sur lequel se trouve votre serveur peut tout lire. Il n'existe pas de DoT vers la racine. Vous n'avez pas supprimé l'observateur, vous l'avez plutôt déplacé, d'un FAI grand public qui vend des données et répond aux réquisitions judiciaires dans votre propre pays, vers un réseau d'hébergement dans une juridiction que vous avez choisie délibérément, qui ne voit qu'un flux fragmenté grâce à la minimisation QNAME. C'est une amélioration réelle, et c'est un débat sur quelle loi s'applique au fil plutôt que quelque chose qu'un fichier de configuration peut trancher.
Et cela ne vous donne pas de foule. Un résolveur utilisé par un seul foyer attribue chaque requête qui le traverse à ce foyer, sans la moindre ambiguïté. Face à un adversaire passif global, un résolveur partagé et chargé est réellement la meilleure cachette. Face à votre FAI, votre employeur, les courtiers en données qui achètent la télémétrie des résolveurs, et les demandes en masse routinières qui touchent réellement les gens ordinaires, le vôtre est meilleur — à condition que le dernier saut depuis vos appareils se fasse à l'intérieur d'un tunnel. Utilisez ceci avec votre propre tunnel WireGuard, pas à sa place. Seul, un résolveur privé ne fait surtout que déplacer vos métadonnées. Derrière un tunnel, il ferme le seul canal que le tunnel laisse ouvert.
SP·04L'erreur qui transforme votre résolveur en arme pour quelqu'un d'autre
Il n'y a exactement qu'une seule façon de se tromper gravement là-dessus, c'est facile à faire par accident, et les conséquences retombent sur des inconnus avant de retomber sur vous.
Un résolveur qui répond à n'importe qui est un résolveur ouvert, et un résolveur ouvert est un amplificateur. Le DNS tourne sur UDP, les adresses source UDP se falsifient trivialement, et une petite requête peut produire une réponse plusieurs fois plus grosse. Un attaquant envoie à votre machine une question de 60 octets avec l'adresse d'une victime falsifiée comme expéditeur ; votre machine envoie consciencieusement à la victime une réponse plusieurs dizaines de fois plus grosse. Faites cela depuis quelques milliers de résolveurs ouverts à la fois, et la victime est mise hors ligne, ayant reçu une inondation qui semble — à raison, au niveau du paquet — venir de vous. Vous n'êtes pas la cible. Vous êtes l'arme, et le trafic dans le rapport d'incident est le vôtre.
Ce qui suit n'a rien de glorieux : des signalements d'abus venant de réseaux dont vous n'avez jamais entendu parler, un hébergeur qui nullroute votre adresse pour protéger son propre transit, et une conversation sur votre compte que vous préféreriez ne pas avoir. Vous finissez du côté de celui qui cause exactement l'événement décrit dans notre runbook de première heure contre une attaque DDoS, et il n'existe aucune version de cette histoire où votre disponibilité y survit.
Deux verrous indépendants l'empêchent, et vous voulez les deux, parce que chacun couvre l'échec de l'autre. Le premier est le propre access-control d'Unbound, qui doit refuse tout l'internet sur les deux familles d'IP puis explicitement allow le loopback et le sous-réseau de votre tunnel — une liste default-deny, pas une liste d'autorisation avec une queue permissive. Le second est l'endroit même où le démon écoute : liez-le à 127.0.0.1 et à l'adresse du tunnel, jamais à 0.0.0.0, et gardez le port 53 fermé sur l'interface publique au niveau du pare-feu.
Le piège, c'est de ne faire que la première chose. Une règle access-control ne fait pas disparaître le port ; une requête refusée reste un paquet reçu et un paquet envoyé, votre adresse continue d'apparaître dans les scans qui cartographient les résolveurs ouverts, et une modification ultérieure de la mauvaise section transforme un refus en réponse. La liaison et le pare-feu rendent l'erreur structurellement impossible plutôt qu'à une ligne de configuration près. Si la machine fait aussi tourner des conteneurs, relisez comment Docker publie des ports en contournant votre pare-feu avant de supposer que la règle que vous avez écrite est la règle en vigueur.
Les réglages par défaut d'Unbound sont sensés. Ils ne sont pas privés.
Unbound est livré réglé pour la correction et la stabilité, ce qui est le bon défaut pour un logiciel principalement déployé par des FAI. Une poignée de réglages le transforment en quelque chose construit pour la personne qui le fait tourner. Aucun n'est exotique ; ils sont juste désactivés, ou non tranchés, à la sortie de la boîte.
Arrêtez de répondre à des questions sur vous-même. Par défaut, un résolveur signale volontiers sa version logicielle et son hostname via la classe CHAOS — version.bind et hostname.bind — ce qui est de la reconnaissance gratuite pour quiconque décide si votre machine vaut le coup d'œil. hide-identity et hide-version ne coûtent rien et suppriment une empreinte.
Échouez fermé, pas ouvert. La validation DNSSEC est la différence entre un résolveur qui détecte une réponse falsifiée et un résolveur qui la sert. harden-dnssec-stripped refuse d'accepter une réponse non signée pour une zone censée être signée, harden-glue et harden-below-nxdomain ferment deux voies classiques d'empoisonnement de cache, et aggressive-nsec permet au résolveur de répondre aux noms inexistants directement depuis les enregistrements de refus mis en cache plutôt que de redemander. use-caps-for-id ajoute une capitalisation aléatoire comme entropie supplémentaire contre le spoofing à l'aveugle — peu coûteux, et parfois incompatible avec un serveur faisant autorité mal construit, ce qui vaut mieux savoir avant de passer une après-midi sur un domaine qui ne résout pas.
N'écrivez rien. Unbound ne journalise pas les requêtes sauf si on le lui demande, mais les réglages qui le feraient ne sont qu'à une ligne non commentée, et certains paquets de distribution livrent un défaut plus bavard. Réglez verbosity: 0 et déclarez explicitement log-queries: no, afin que l'intention soit visible dans le fichier plutôt que déduite de son absence. Souvenez-vous ensuite de la partie qui n'est pas un réglage : le cache lui-même est un enregistrement. unbound-control dump_cache sur une machine en fonctionnement affiche un historique récent de ce que cette machine a recherché, et il vit dans la mémoire d'une machine que les mains de quelqu'un d'autre peuvent atteindre. Il expire de lui-même, ce qui est précisément pourquoi des durées de vie de cache courtes et la vie privée sont en légère tension, et c'est la raison de ne pas laisser un résolveur tourner pendant des mois sur un hôte auquel vous ne faites pas confiance du tout.
Mettez en cache délibérément. Chaque réponse servie depuis le cache est une observation qui n'a jamais lieu en amont, donc un cache en bonne santé est une fonctionnalité de vie privée, pas seulement de vitesse. prefetch renouvelle les enregistrements populaires avant leur expiration, si bien que le cas courant ne touche plus jamais le réseau ; serve-expired vous garde en ligne quand un serveur faisant autorité est brièvement inatteignable. Augmenter cache-min-ttl réduit encore le bavardage en amont, mais cela outrepasse des choix délibérés des opérateurs de domaine — c'est ainsi que fonctionnent les CDN et le failover avec des TTL bas — donc un plancher d'une minute ou deux est raisonnable, et une heure finira par vous laisser bloqué sur une adresse morte.
Deux portes d'entrée : le tunnel, ou DNS-over-TLS
Vos appareils doivent bien atteindre le résolveur d'une façon ou d'une autre, et le choix entre les deux options raisonnables est surtout une question de ce que vous êtes prêt à publier.
Le tunnel est la meilleure réponse pour presque tout le monde. Si votre ordinateur portable et votre téléphone tiennent déjà une session WireGuard vers ce serveur, le résolveur peut écouter sur l'adresse du tunnel et parler du DNS ordinaire sur le port 53. Le trafic est déjà chiffré et déjà authentifié par le tunnel, donc il n'y a aucun certificat à obtenir, aucun nouveau port ouvert vers internet, aucune pile TLS exposée à des inconnus, et — la partie sous-estimée — aucun hostname nulle part. Le guide WireGuard de ce site laisse les clients pointés vers DNS = 9.9.9.9 précisément parce qu'il n'y avait encore rien de mieux à y mettre. Voici ce qui va dans cette ligne à la place : l'adresse tunnel de votre propre serveur.
DNS-over-TLS est fait pour l'appareil qui ne peut pas tenir un tunnel. Le champ Private DNS d'Android en est le meilleur argument — il s'applique à tout le système, survit aux redémarrages, et couvre des applications que vous ne pouvez pas toucher autrement. Le coût est un hostname avec un certificat valide, et un certificat signifie une entrée publique et permanente dans les journaux Certificate Transparency, liant ce nom au moment où vous l'avez créé. Le DNS passif liera ensuite le nom à l'adresse du serveur. Si tout l'intérêt de l'exercice était de garder votre nom à l'écart de l'infrastructure, utilisez un hostname qui ne mène nulle part près de vous, et déposez-le avec le soin décrit dans l'enregistrement anonyme d'un domaine — pas un sous-domaine du domaine que vous utilisez pour tout le reste, ce qui relierait les deux de façon permanente.
Il y a un second coût, plus subtil. Un point d'accès DoT que des téléphones en itinérance peuvent atteindre ne peut pas être restreint par adresse source, donc c'est, par définition, un résolveur que des inconnus peuvent utiliser s'ils apprennent le nom. Ce n'est pas un amplificateur — TLS sur TCP exige une poignée de main complète, donc la source ne peut pas être falsifiée et il n'y a rien à réfléchir — mais c'est de la capacité que vous offrez, et un service qui mérite d'être scanné. Gardez les limites de débit par IP activées, choisissez un hostname que personne ne devinera, et traitez-le comme une exception délibérée pour un ou deux appareils plutôt que comme la porte par défaut.
DNS-over-HTTPS est une troisième option, et généralement la mauvaise ici. Il faut un serveur web devant le résolveur, ce qui fait plus de pièces mobiles et une surface d'attaque plus large pour le seul bénéfice d'être indiscernable du trafic web. Ce bénéfice est décisif si vous contournez un réseau qui bloque le DoT, et sans intérêt si ce n'est pas le cas.
SP·07Le blocage est un produit différent ; décidez avant de le greffer
Tôt ou tard, quelqu'un suggère d'ajouter des blocklists, et l'argument est réellement séduisant : filtrer au niveau du résolveur couvre chaque appareil sur le tunnel, y compris la smart TV et les applications mobiles qu'aucune extension ne peut atteindre. Sur mobile en particulier, c'est le seul endroit pratique où intervenir. C'est aussi la fonctionnalité la plus susceptible de vous faire discrètement perdre confiance dans votre propre infrastructure, et cela vaut la peine de comprendre pourquoi avant que ça n'arrive plutôt que pendant.
Le premier coût, c'est que les pannes ne ressemblent pas à des pannes. Un résolveur cassé s'annonce lui-même ; un domaine bloqué se présente comme un bouton de paiement qui ne fait rien, une application bloquée sur une roue qui tourne, un email qui n'arrive jamais. Le symptôme apparaît des semaines après l'installation de la liste, sur un appareil auquel vous ne pensiez pas, et rien ne le relie à une décision DNS prise un autre mois. Chaque blocklist que vous installez est une politique écrite par un inconnu et appliquée silencieusement à votre foyer. Si vous en ajoutez une, notez que vous l'avez fait, gardez-la petite et réputée, et gardez un moyen de la désactiver en une seule commande — la première étape de dépannage pour tout ce qui est inexpliqué sur votre réseau devient "contourner le filtre et réessayer", et cette étape doit être peu coûteuse.
Le second coût, c'est que cela ne fait pas ce que les gens espèrent. Une application avec une adresse de résolveur codée en dur, ou son propre client DoH intégré, ne demande jamais rien à votre résolveur ; elle ouvre une connexion vers une IP codée en dur et c'est tout. Les navigateurs résolvent de plus en plus via leur propre DoH sauf indication contraire. Le blocage DNS est une couche d'hygiène qui supprime beaucoup de bruit de pistage et de publicité peu sophistiqué, et ce n'est pas un contrôle de sécurité, parce que tout ce qui est réellement hostile le contourne par conception.
Si vous le voulez malgré tout, préférez une petite zone locale dans Unbound à un second démon — local-zone: "tracker.example." always_nxdomain n'a besoin d'aucun logiciel supplémentaire, d'aucune interface web sur un port qu'il faudrait ensuite défendre, et d'aucun nouveau service qui peut tomber en panne et emporter votre résolution de noms avec lui. Gardez la description de poste de la machine à une seule ligne : elle résout des noms. Chaque responsabilité supplémentaire que vous lui donnez est une nouvelle façon pour que tout s'arrête d'un coup.
Un résolveur que vous faites tourner est une dépendance qui vous appartient
Quand un site que vous hébergez tombe, certaines personnes ne peuvent pas lire quelque chose. Quand votre résolveur tombe, rien ne marche — ni le navigateur, ni l'email, ni le gestionnaire de paquets, ni l'application qui allait vous dire que le serveur est en panne. C'est le service le plus critique que vous puissiez mettre sur une petite machine, et il tombe en panne de façons qui ne ressemblent pas à du DNS.
Les modes de panne réalistes valent la peine d'être connus à l'avance, car chacun a une signature différente. Le VPS redémarre et Unbound n'a jamais été activé au démarrage, donc tout fonctionne jusqu'au premier redémarrage imprévu. Une grosse blocklist pousse le cache dans le swap sur une instance de 1 Go et l'OOM killer choisit le résolveur. Une zone quelque part casse ses propres signatures DNSSEC, et votre résolveur correctement configuré refuse la réponse tandis que tout le monde sur un résolveur non validant continue de naviguer — votre machine a raison et le site vous paraît quand même cassé, ce qui fait cinq minutes de confusion si vous avez oublié que vous validez. Ou le tunnel tombe, et parce que DNS = 10.66.0.1 n'existe qu'à l'intérieur du tunnel, l'appareil n'a plus aucun résolveur du tout et signale qu'il est hors ligne.
Les mitigations sont heureusement peu coûteuses. Activez le service au démarrage et testez-le réellement avec un redémarrage plutôt que de le supposer. Donnez aux clients un résolveur secondaire pour qu'un tunnel mort dégrade le service plutôt que de l'arrêter — un résolveur public dans ce rôle est un compromis de vie privée petit et explicite qui ne s'applique que tant que le vôtre est inatteignable, et c'est généralement le bon compromis. Gardez serve-expired activé pour qu'une brève coupure en amont ne devienne pas votre propre coupure. Vérifiez le résolveur depuis un autre endroit, pas depuis la machine elle-même, ce qui est la seule façon de remarquer la différence entre "en panne" et "inatteignable".
Et gardez une copie de la configuration. Le tout ne fait que quelques dizaines de lignes qui vous ont pris une après-midi à mettre au point et que vous ne vous rappellerez pas dans un an ; elle a sa place dans vos sauvegardes chiffrées hors site, aux côtés des clés WireGuard, pour que reconstruire prenne vingt minutes plutôt qu'une seconde après-midi. C'est le résumé honnête de tout cet exercice : une petite machine qui fait un travail, pour le prix du plan le moins cher de la liste, remplaçant une promesse sur la rétention par un arrangement où l'enregistrement n'est simplement jamais créé.
SP·09Étape par étape
-
01
Partez d'une machine déjà verrouillée
Un résolveur est un service petit et discret, ce qui donne envie de l'installer sur ce qui traîne. Ne le faites pas — cette machine verra chaque nom que chaque appareil sur votre tunnel recherche, donc elle mérite le même traitement que tout ce qui détient des secrets. Déployez le plan le plus petit que vous voulez ; 1 Go de RAM est largement suffisant pour un foyer, puisque les tailles de cache ci-dessous se mesurent en dizaines de mégaoctets. Passez par la première heure après déploiement avant toute autre chose : un utilisateur nommé, un SSH par clé uniquement, un default-deny sur les deux familles d'IP, des mises à jour de sécurité automatiques.
Installez ensuite Unbound. Le paquet de la distribution fournit les root hints et l'ancre de confiance racine DNSSEC, et connecte le renouvellement automatique de cette ancre, ce qui est l'un des rares cas où la version empaquetée vous épargne réellement toute une classe de pannes futures.
sudo apt update && sudo apt install -y unbound dnsutils unbound -V | head -n 3 # the packaged trust anchor the resolver will validate against sudo ls -l /var/lib/unbound/root.key
Si
root.keyest absent, votre paquet n'a pas exécutéunbound-anchoret la validation échouera fermée sur absolument tout. Générez-la une fois avecsudo -u unbound unbound-anchor -a /var/lib/unbound/root.keyavant de continuer. -
02
Reprenez le port 53 à systemd-resolved
Sur la plupart des distributions actuelles, quelque chose détient déjà le port 53 :
systemd-resolvedfait tourner un stub à l'écoute sur127.0.0.53, et/etc/resolv.confest un lien symbolique qui y pointe. Unbound refusera de démarrer, ou démarrera sans rien lier d'utile, jusqu'à ce que ce soit réglé. Regardez avant de modifier.ss -ulpn 'sport = :53' ls -l /etc/resolv.conf
Lisez ce paragraphe avant d'exécuter le bloc suivant : entre la désactivation du stub et le démarrage d'Unbound, cette machine n'a plus aucun DNS fonctionnel. Terminez les étapes 3 et 4 dans la même session, et ne lancez pas d'opération
aptdans une autre fenêtre en attendant — elle restera bloquée sur une résolution de nom et vous la confondrez avec le résolveur que vous n'avez pas encore démarré.# stop resolved from holding the port (appends inside the [Resolve] section) printf 'DNSStubListener=no\n' | sudo tee -a /etc/systemd/resolved.conf sudo systemctl restart systemd-resolved # point the host at the resolver it is about to run sudo rm -f /etc/resolv.conf printf 'nameserver 127.0.0.1\noptions edns0 trust-ad\n' | sudo tee /etc/resolv.conf # the port should now be free ss -ulpn 'sport = :53'
-
03
Écrivez une configuration qui récurse et oublie
Laissez la configuration empaquetée tranquille et ajoutez votre propre fichier dans le répertoire drop-in, pour qu'une mise à jour de paquet n'annule jamais silencieusement vos décisions. Chaque ligne ci-dessous concerne soit le refus des inconnus, soit la récursion depuis la racine, soit l'absence de conservation de traces. Remplacez
10.66.0.1et10.66.0.0/24par l'adresse et le sous-réseau de tunnel de votre propre serveur WireGuard s'ils diffèrent.sudo tee /etc/unbound/unbound.conf.d/private-resolver.conf > /dev/null <<'EOF' server: # listen for the host itself and for the tunnel — never on 0.0.0.0 interface: 127.0.0.1 interface: 10.66.0.1 port: 53 # default-deny: refuse the internet, then allow what you trust access-control: 0.0.0.0/0 refuse access-control: ::/0 refuse access-control: 127.0.0.0/8 allow access-control: 10.66.0.0/24 allow # recurse from the root and send each server only the label it needs qname-minimisation: yes harden-dnssec-stripped: yes harden-below-nxdomain: yes harden-glue: yes aggressive-nsec: yes use-caps-for-id: yes # answer nothing about the software or the host hide-identity: yes hide-version: yes # keep no query log, and say little to the journal verbosity: 0 log-queries: no log-replies: no # a warm cache is an upstream observation that never happens cache-min-ttl: 120 cache-max-ttl: 86400 prefetch: yes prefetch-key: yes serve-expired: yes # belt and braces if a rule above is ever loosened ratelimit: 1000 ip-ratelimit: 100 # sizing for a small instance num-threads: 2 so-reuseport: yes msg-cache-size: 32m rrset-cache-size: 64m EOFRemarquez ce qui n'est pas dans le fichier : il n'y a pas de
forward-zone. Son absence est ce qui fait de ceci un résolveur récursif plutôt qu'un cache devant celui de quelqu'un d'autre. Si vous collez plus tard un extrait d'un tutoriel qui en ajoute une, vous avez silencieusement annulé tout l'intérêt de l'exercice. -
04
Démarrez-le, puis prouvez que DNSSEC valide vraiment
Vérifiez la syntaxe avant de redémarrer quoi que ce soit — sur une machine dont le propre
resolv.confpointe maintenant vers Unbound, une erreur de configuration signifie plus aucune résolution de noms pendant que vous déboguez.sudo unbound-checkconf sudo systemctl enable --now unbound systemctl --no-pager status unbound | head -n 5
Vérifiez maintenant les deux comportements qui comptent, parce qu'un résolveur qui répond n'est pas la même chose qu'un résolveur qui valide. Un nom signé doit revenir avec le drapeau
ad— authenticated data — et un nom dont les signatures sont délibérément cassées doit échouer fermé avecSERVFAILplutôt que de se résoudre.# should show: flags: qr rd ra ad dig @127.0.0.1 example.com +dnssec | grep -E '^;; flags' # should show: status: SERVFAIL (not NOERROR, not an address) dig @127.0.0.1 dnssec-failed.org | grep -E '^;; ->>HEADER' # and a normal name should simply work dig @127.0.0.1 +short cloudflare.com
Si la zone cassée se résout vers une adresse, la validation ne fonctionne pas : vérifiez que
/var/lib/unbound/root.keyexiste et est lisible par l'utilisateurunbound. Si tout est enSERVFAIL, la cause habituelle est une horloge sérieusement décalée — les signatures ont des fenêtres de validité, et une machine désynchronisée de plusieurs heures rejette la totalité d'internet. -
05
Fermez la porte publique, n'ouvrez que le tunnel
La configuration refuse déjà les inconnus. Cette étape fait en sorte qu'il n'y ait rien à atteindre pour un inconnu, tout court — le second des deux verrous, celui qui survit à une future modification du premier.
# DNS is reachable from the tunnel interface only sudo ufw allow in on wg0 to any port 53 proto udp sudo ufw allow in on wg0 to any port 53 proto tcp sudo ufw status verbose
Vérifiez-le ensuite de la seule façon qui compte, depuis une autre machine. Tester depuis le serveur lui-même ne prouve absolument rien — le loopback est autorisé exprès. Lancez ceci depuis votre ordinateur portable avec le tunnel coupé, ou depuis n'importe quel autre hôte que vous possédez.
# must time out. an answer here means you are running an open resolver. dig @SERVER_PUBLIC_IP example.com +time=3 +tries=1 # same question over IPv6, which is the half people forget dig -6 @SERVER_IPV6 example.com +time=3 +tries=1
Si l'une des deux commandes renvoie une réponse, arrêtez-vous et corrigez-le maintenant plutôt qu'après le signalement d'abus. Les causes habituelles sont un
interface: 0.0.0.0resté dans un fichier de configuration empaqueté, une règle de pare-feu autorisant le 53 globalement depuis une expérience antérieure, ou Docker qui publie un port de conteneur en contournant entièrementufw. -
06
Faites pointer vos appareils vers lui
Côté client, c'est un changement d'une seule ligne. Dans la configuration du client WireGuard, la ligne
DNSdevient l'adresse tunnel de votre serveur au lieu d'un résolveur public — c'est la modification qui retireDNS = 9.9.9.9du guide WireGuard.[Interface] PrivateKey = <paste client.key> Address = 10.66.0.2/32, fd86:ea04:1115::2/128 DNS = 10.66.0.1 [Peer] PublicKey = <paste server.pub> Endpoint = YOUR_SERVER_IP:51820 AllowedIPs = 0.0.0.0/0, ::/0 PersistentKeepalive = 25
Établissez le tunnel et vérifiez deux choses distinctes depuis le client : que les réponses viennent bien de votre résolveur, et que la récursion sort effectivement par votre VPS plutôt que par ailleurs. Le second contrôle est le plus utile —
whoami.akamai.netrenvoie l'adresse de celui qui a posé la question, donc il doit afficher l'IP publique de votre serveur et rien d'autre.# answers should come from the tunnel address dig example.com | grep -E 'SERVER:' # should print your VPS public IP — this is the leak test dig +short whoami.akamai.net # and the ad flag should still be there, end to end dig example.com +dnssec | grep -E '^;; flags'
-
07
N'ajoutez DNS-over-TLS que pour un appareil qui ne peut pas tenir le tunnel
Ignorez cette étape sauf si vous avez un appareil spécifique — généralement un téléphone Android, via son réglage Private DNS à l'échelle du système — que vous voulez couvrir sans tunnel permanent. Cela nécessite un hostname et un certificat, et ce hostname devient un enregistrement public permanent dans les journaux Certificate Transparency, donc choisissez-en un qui ne mène nulle part près de vos autres identités.
sudo apt install -y certbot sudo ufw allow 80/tcp comment 'certbot, temporarily' sudo certbot certonly --standalone -d dns.example.net sudo ufw delete allow 80/tcp # unbound must be able to read the key sudo usermod -a -G ssl-cert unbound 2>/dev/null || true sudo chgrp -R ssl-cert /etc/letsencrypt/live /etc/letsencrypt/archive sudo chmod -R g+rX /etc/letsencrypt/live /etc/letsencrypt/archive
Ajoutez l'écouteur TLS comme son propre fichier drop-in, pour pouvoir le supprimer en un geste si vous changez d'avis.
# NOTE: drop-in files are read in alphabetical order and the last # access-control line for a given prefix wins — so this file must # sort AFTER private-resolver.conf, or its refuse rule overrides # the allow below and every DoT client gets REFUSED. sudo tee /etc/unbound/unbound.conf.d/zz-dot.conf > /dev/null <<'EOF' server: interface: 0.0.0.0@853 interface: ::0@853 tls-service-key: "/etc/letsencrypt/live/dns.example.net/privkey.pem" tls-service-pem: "/etc/letsencrypt/live/dns.example.net/fullchain.pem" # roaming clients have no fixed address, so this endpoint must accept any. # safe only because port 53 stays bound to loopback + wg0 and blocked at # the firewall — TLS over TCP cannot be spoofed, so it cannot be reflected. access-control: 0.0.0.0/0 allow access-control: ::/0 allow EOF sudo ufw allow 853/tcp sudo unbound-checkconf && sudo systemctl restart unboundTestez depuis l'extérieur du tunnel avant de lui faire confiance, puis mettez le hostname dans le champ Private DNS du téléphone. Le renouvellement est la partie qui casse silencieusement trois mois plus tard — certbot remplace le certificat mais Unbound garde l'ancien en mémoire, donc ajoutez un hook de déploiement qui le recharge.
kdig -d @dns.example.net +tls-ca +tls-host=dns.example.net example.com echo 'systemctl reload unbound' | sudo tee /etc/letsencrypt/renewal-hooks/deploy/unbound.sh sudo chmod +x /etc/letsencrypt/renewal-hooks/deploy/unbound.sh


