Première rédaction de cet article le 30 septembre 2019
Dernière mise à jour le 28 septembre 2021
Cet article documente le fonctionnement interne du résolveur
DoH
https://doh.bortzmeyer.fr/
et du résolveur
DoT
dot.bortzmeyer.fr
. Il concerne donc
essentiellement les technicien·ne·s. Si vous vous intéressez plutôt
aux conditions d'utilisation de ce service, voyez l'article décrivant la
politique.
Pour comprendre les protocoles DoH et DoT, il faut relire les normes qui les décrivent, respectivement les RFC 8484 et RFC 7858.
C'est le même résolveur qui gère les deux protocoles. Si une version initiale de ce résolveur DoH utilisait une mise en œuvre en Python que j'avais écrite lors d'un hackathon de l'IETF, la version « de production » se sert de l'excellent logiciel dnsdist. dnsdist est un frontal pour serveurs DNS, assurant des fonctions telles que la répartition de charge et, ce qui nous intéresse surtout ici, le relais entre différents protocoles. Ici, dnsdist a été configuré pour accepter des connexions DoH et DoT (mais pas le traditionnel DNS sur UDP, ni sur TCP en clair, d'ailleurs) et pour relayer les requêtes DNS vers le « vrai » résolveur.
La machine est une Arch Linux tournant chez OVH, sur l'offre nommée « VPS ».
La configuration de dnsdist se trouve dans un fichier de
configuration, dnsdist.conf
. N'hésitez pas à
consulter la documentation très complète
de dnsdist. Voyons les points
essentiels.
On fait du DoH donc on lance un service DoH, en IPv4 et en IPv6 :
addDOHLocal("0.0.0.0:443", "/etc/dnsdist/server-doh.pem", "/etc/dnsdist/server-doh.key", {"/", "/rfc", "/about", "/policy", "/help"}, {minTLSVersion="tls1.2", customResponseHeaders={["link"]="<https://www.bortzmeyer.org/doh-bortzmeyer-fr-policy.html> rel=\"service-meta\"; type=\"text/html\""}}) addDOHLocal("[::]:443", "/etc/dnsdist/server-doh.pem", "/etc/dnsdist/server-doh.key", {"/", "/rfc", "/about", "/policy", "/help"}, {minTLSVersion="tls1.2", customResponseHeaders={["link"]="<https://www.bortzmeyer.org/doh-bortzmeyer-fr-policy.html> rel=\"service-meta\"; type=\"text/html\""}})
Pour améliorer un peu la sécurité, on exige du
TLS 1.2 au minimum. Notez qu'il y aurait
plein d'autres paramètres à configurer (refuser des algorithmes trop
faibles), pour avoir une
meilleure note sur SSLlabs. Et les trucs bizarres qui
commencent par customResponseHeaders
? Il
s'agit d'utiliser la technique du RFC 8631 pour indiquer des méta-informations sur le service,
ici, la politique suivie. Cela donne :
% curl -v https://doh.bortzmeyer.fr/help ... < HTTP/2 200 < server: h2o/dnsdist < link: <https://www.bortzmeyer.org/doh-bortzmeyer-fr-policy.html> rel="service-meta"; type="text/html" < content-length: 86 <
Pour avoir des URL « spéciaux » ne faisant pas du DoH, on ajoute aussi :
supportpagemap = { newDOHResponseMapEntry("^/rfc$", 307, "https://www.rfc-editor.org/info/rfc8484"), newDOHResponseMapEntry("^/about$", 307, "https://www.bortzmeyer.org/doh-bortzmeyer-fr-policy.html"), newDOHResponseMapEntry("^/policy$", 307, "https://www.bortzmeyer.org/doh-bortzmeyer-fr-policy.html"), newDOHResponseMapEntry("^/help$", 200, "For the server policy, see <https://www.bortzmeyer.org/doh-bortzmeyer-fr-policy.html>.") } dohFE = getDOHFrontend(0) dohFE:setResponsesMap(supportpagemap) dohFE6 = getDOHFrontend(1) dohFE6:setResponsesMap(supportpagemap)
Ces instructions disent à dnsdist de rediriger
/policy
vers l'article décrivant la
politique, et d'afficher un court message pour /help
.
Et on veut faire du DoT, pas seulement du DoH, donc on met aussi :
addTLSLocal("0.0.0.0:853", "/etc/dnsdist/server-dot.pem", "/etc/dnsdist/server-dot.key", {minTLSVersion="tls1.2"}) addTLSLocal("[::]:853", "/etc/dnsdist/server-dot.pem", "/etc/dnsdist/server-dot.key", {minTLSVersion="tls1.2"})
Le résolveur est public, donc les ACL autorisent tout le monde :
addACL('0.0.0.0/0') addACL('[::]/0')
Mais on se méfie quand même des clients trop enthousiastes donc on les limite à cent requêtes par seconde :
addAction(MaxQPSIPRule(100), DropAction())
Au passage, il faut dire un mot de addAction
car il sert souvent. dnsdist permet de définir des politiques à
appliquer lors du traitement d'une requête DNS (la documentation dit
« paquet », mais, apparemment, il s'agit bien d'une requête). La
syntaxe générale (cf. la documentation)
est addAction(critère, action)
avec de nombreux
critères possibles (ici, « a fait plus de 100 r/s ») et plein
d'actions disponibles (ici, ignorer la requête).
dnsdist est un répartiteur de charge, il
n'est pas un résolveur DNS. Il faut donc un ou plusieurs « vrais »
résolveurs derrière. Le principal résolveur, pour le service
https://doh.bortzmeyer.fr
, est un
Unbound qui tourne sur la même machine. Au
cas où quelque chose irait mal, un second résolveur, complètement
indépendant, est fourni par OVH. dnsdist
permet de choisir quel
résolveur utiliser et j'ai mis :
setServerPolicy(firstAvailable) newServer({address="127.0.0.1:53", name="Local-Unbound"}) newServer({address="213.186.33.99:53", name="OVH"})
Pourquoi firstAvailable
? Parce que je voulais
éviter autant que possible d'envoyer des requêtes à ce second résolveur que
je ne contrôle pas et dont je ne connais pas la politique en matière
de vie privée. Comme voulu, la quasi-totalité des requêtes arrivent
donc au premier résolveur :
> showServers() # Name Address State Qps Qlim Ord Wt Queries Drops Drate Lat Outstanding Pools 0 Local-Unbound 127.0.0.1:53 up 0.0 0 1 1 2509 0 0.0 70.4 0 1 OVH 213.186.33.99:53 up 0.0 0 1 1 0 0 0.0 0.0 0 All 0.0 2509 0
Pour pouvoir utiliser la console de dnsdist, comme ci-dessus, il faut l'activer dans le fichier de configuration :
controlSocket('[::1]:5199') setKey("kg...=")
(La documentation
explique quelle valeur indiquer à
setKey()
.)
L'accès à la console se fait ensuite avec dnsdist
-c
:
% dnsdist -c > dumpStats() acl-drops 0 latency0-1 5058 cache-hits 4429 latency1-10 351 cache-misses 2571 latency10-50 785 cpu-sys-msec 12929 latency100-1000 388 cpu-user-msec 47305 latency50-100 280 ...
dnsdist dispose également d'un serveur Web minimal, permettant de regarder l'état du service (ce n'est pas un site Web d'administration, c'est juste pour jeter un coup d'œil). Voici une configuration typique :
webserver("[::1]:8082", "2e...", "a5..")
Les deux derniers paramètres indiquent le mot de passe à utiliser pour les accès au site Web et la clé pour l'API. Ici, le serveur n'écoute que sur une adresse locale. Si vous voulez le rendre accessible de l'extérieur, comme il ne gère pas HTTPS, il vaut mieux le mettre derrière un relais. J'ai utilisé stunnel, avec cette configuration :
[dnsdist] accept = 8083 connect = localhost-ipv6:8082 cert = /etc/stunnel/stunnel.pem key = /etc/stunnel/stunnel.key
Cela me permet de regarder de l'extérieur :
Et pour l'API de ce serveur interne, qui renvoie des résultats en JSON :
% curl -s --header "X-API-Key: XXXX" https://doh.bortzmeyer.fr:8083/api/v1/servers/localhost/statistics | jq . [ { "name": "responses", "type": "StatisticItem", "value": 2643 }, { "name": "queries", "type": "StatisticItem", "value": 3698 }, ...
Il y a aussi quelques paramètres liés aux performances du serveur. Celui-ci active la mémoire de dnsdist, qui gardera au maximum 100 000 enregistrements DNS :
pc = newPacketCache(100000) getPool(""):setCache(pc)
Pour l'instant, cette valeur est énorme pour ce modeste serveur. Dans la console :
> getPool(""):getCache():printStats() Entries: 84/100000 Hits: 1127 Misses: 2943 ...
Cette mémoire explique pourquoi, plus haut, le serveur indiquait davantage de requêtes que de réponses (il ne compte comme réponse que ce qui a été traité par les vrais résolveurs). Autres réglages, portant sur le réseau, qu'il me reste à ajuster dès que le serveur recevra du trafic abondant :
setMaxUDPOutstanding(65535) -- Nombre maximum de requêtes en attente pour un résolveur setMaxTCPClientThreads(30) -- Nombre maximum de fils d'exécution TCP (chacun pouvant traiter plusieurs clients) setMaxTCPConnectionDuration(1800) -- Après trente minutes, on raccroche setMaxTCPQueriesPerConnection(300) -- Après trois cents requêtes, on raccroche setMaxTCPConnectionsPerClient(10) -- Dix connexions pour un seul client, c'est déjà beaucoup, mais il faut penser à des choses comme le CG-NAT
dnsdist permet d'enregistrer chaque requête DNS, par exemple dans
un fichier. Cette fonction n'est pas activée
sur https://doh.bortzmeyer.fr
car elle serait
contraire aux promesses
faites quant au respect de la vie privée. Mais c'est un bon
exemple d'utilisation de addAction
pour
toutes les requêtes (AllRule
) :
-- addAction(AllRule(), LogAction("/tmp/dnsdist.log", false, true, false))
(Attention si vous utilisez systemd, avec
l'option PrivateTmp=true
, le fichier journal
sera mis quelque part sous
/tmp/systemd-private-XXXXXXX-dnsdist.service-Ijy8yw
,
pour être plus difficile à trouver.) Les lignes journalisées sont
du genre :
Packet from [2001:db8::a36b::64]:44364 for www.ietf.org. AAAA with id 8283
Pour la même raison de vie privée, je n'utilise pas la TeeAction (qui permet de copier les requêtes ailleurs) ou dnstap.
De même qu'on ne journalise pas, on ne ment
pas. dnsdist peut le faire via
SpoofAction()
ou bien avec un script
Lua spécifique, pour faire des choses
horribles comme bloquer un type de données particulier :
luarule(dq) if (dq.qtype==dnsdist.NAPTR) then return DNSAction.Nxdomain, "" else return DNSAction.Allow, "" end end addLuaAction(AllRule(), luarule)
DoH et DoT fonctionnent tous les deux sur TLS (RFC 8446) et utilisent donc son mécanisme d'authentification via des certificats PKIX (RFC 5280). Il faut donc obtenir des certificats raisonnablement reconnus par les clients donc, comme tout le monde, j'ai utilisé Let's Encrypt. dnsdist n'inclut pas de client ACME (RFC 8555), j'ai donc utilisé certbot que je fais tourner en mode autonome (standalone). Cela marche car dnsdist n'écoute que sur le port 443 alors qu'ACME n'utilise que le 80. Pour créer le certificat initial :
certbot certonly -n --standalone --domain doh.bortzmeyer.fr
Et pour le renouveler :
certbot renew --standalone --reuse-key --deploy-hook /usr/local/sbin/restart-dnsdist
(restart-dnsdist
contient dnsdist -e 'reloadAllCertificates()'
.)
Et voici les liens symboliques configurés pour pointer vers les
répertoires utilisés par Let's Encrypt :
% ls -lt total 28 lrwxrwxrwx 1 root root 51 Sep 24 15:50 server-dot.key -> /etc/letsencrypt/live/dot.bortzmeyer.fr/privkey.pem lrwxrwxrwx 1 root root 53 Sep 24 15:50 server-dot.pem -> /etc/letsencrypt/live/dot.bortzmeyer.fr/fullchain.pem lrwxrwxrwx 1 root root 51 Sep 15 16:31 server-doh.key -> /etc/letsencrypt/live/doh.bortzmeyer.fr/privkey.pem lrwxrwxrwx 1 root root 53 Sep 15 16:31 server-doh.pem -> /etc/letsencrypt/live/doh.bortzmeyer.fr/fullchain.pem ...
Avec gnutls-cli, on peut voir le certificat utilisé :
% gnutls-cli dot.bortzmeyer.fr:853 ... - Certificate type: X.509 - Got a certificate list of 2 certificates. - Certificate[0] info: - subject `CN=dot.bortzmeyer.fr', issuer `CN=Let's Encrypt Authority X3,O=Let's Encrypt,C=US', serial 0x047aa99cbff8ac180c4c3b14935d9599b1f5, RSA key 2048 bits, signed using RSA-SHA256, activated `2019-09-24 12:46:32 UTC', expires `2019-12-23 12:46:32 UTC', key-ID `sha256:787005b3173d1c95bc425241ea40e5474b644f00fded7fd35d87350331644c56' Public Key ID: sha1:696c94f0f093a3b1037ffcb2fcd9d23859d539bc sha256:787005b3173d1c95bc425241ea40e5474b644f00fded7fd35d87350331644c56 Public key's random art: +--[ RSA 2048]----+ | o | | = o | | . . O ...| | o B + .+.| | = S . o| | = . . E | | . .= | | . o=o. | | o.oo. | +-----------------+ - Certificate[1] info: - subject `CN=Let's Encrypt Authority X3,O=Let's Encrypt,C=US', issuer `CN=DST Root CA X3,O=Digital Signature Trust Co.', serial 0x0a0141420000015385736a0b85eca708, RSA key 2048 bits, signed using RSA-SHA256, activated `2016-03-17 16:40:46 UTC', expires `2021-03-17 16:40:46 UTC', key-ID `sha256:60b87575447dcba2a36b7d11ac09fb24a9db406fee12d2cc90180517616e8a18' - Status: The certificate is trusted. - Description: (TLS1.2)-(ECDHE-RSA-SECP256R1)-(AES-256-GCM) ...
Pour pouvoir authentifier le serveur par d'autres moyens que la
chaîne de confiance PKIX (par exemple par épinglage de la clé, ou
par DANE), je demande à certbot de ne pas
changer la clé lors des renouvellements de certificats, avec
l'option --reuse-key
(cf. mon précédent article sur la question).
À propos de DANE (RFC 6698), j'ai créé les enregistrements nécessaires avec hash-slinger :
% tlsa --create --selector 1 --port 853 dot.bortzmeyer.fr Got a certificate with Subject: /CN=dot.bortzmeyer.fr _853._tcp.dot.bortzmeyer.fr. IN TLSA 3 1 1 787005b3173d1c95bc425241ea40e5474b644f00fded7fd35d87350331644c56
Puis je les mets dans le fichier de zone DNS, et on peut voir les enregistrements avec dig :
% dig TLSA _853._tcp.dot.bortzmeyer.fr ... ;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 15141 ;; flags: qr rd ra ad; QUERY: 1, ANSWER: 2, AUTHORITY: 4, ADDITIONAL: 11 ... ;; ANSWER SECTION: _853._tcp.dot.bortzmeyer.fr. 86400 IN TLSA 3 1 1 ( 787005B3173D1C95BC425241EA40E5474B644F00FDED
Et on peut les vérifier :
% tlsa --verify --resolvconf="" doh.bortzmeyer.fr SUCCESS (Usage 3 [DANE-EE]): Certificate offered by the server matches the TLSA record (193.70.85.11) SUCCESS (Usage 3 [DANE-EE]): Certificate offered by the server matches the TLSA record (2001:41d0:302:2200::180)
Et superviser le tout depuis Icinga.
On a vu qu'il y avait deux résolveurs DNS derrière dnsdist, le
principal, géré par moi, et un résolveur de secours. Le résolveur
principal utilise Unbound. Pour mieux
protéger la vie privée, il
utilise la QNAME minimisation du RFC 9156. Dans le unbound.conf
, on a :
server: qname-minimisation: yes
Vous pouvez vérifier que c'est bien activé avec le domaine de test
qnamemintest.internet.nl
et l'outil test-doh
présenté plus loin :
% test-doh https://doh.bortzmeyer.fr qnamemintest.internet.nl TXT ... a.b.qnamemin-test.internet.nl. 10 IN TXT "HOORAY - QNAME minimisation is enabled on your resolver :)!"
Alors qu'avec un autre résolveur DoH, moins soucieux de vie privée :
% test-doh https://dns.google/dns-query qnamemintest.internet.nl TXT ... a.b.qnamemin-test.internet.nl. 9 IN TXT "NO - QNAME minimisation is NOT enabled on your resolver :("
J'ai aussi mis quelques autres paramètres Unbound, typiquement pour durcir la résolution face à diverses menaces :
harden-glue: yes harden-below-nxdomain: yes harden-dnssec-stripped: yes harden-referral-path: yes aggressive-nsec: yes
Avec tout ça, je crois ne pas avoir dit comment j'avais installé dnsdist. Comme il n'existe pas de paquetage dnsdist dans Arch Linux (mais il existe dans AUR), j'ai téléchargé le source et compilé moi-même, selon les instructions. D'abord, j'ai téléchargé et installé la bibliothèque libh2o, qui permet à dnsdist de parler HTTP/2 (RFC 9113) :
cmake . make make install
Puis on peut installer dnsdist :
PKG_CONFIG_PATH=/usr/local/lib/pkgconfig ./configure --enable-dns-over-tls --enable-dns-over-https make make install
Testons maintenant, d'abord avec kdig (qui fait partie de Knot, et notez la première ligne de la réponse) :
% kdig +tls @dot.bortzmeyer.fr nextinpact.com ;; TLS session (TLS1.3)-(ECDHE-SECP256R1)-(RSA-PSS-RSAE-SHA256)-(AES-256-GCM) ;; ->>HEADER<<- opcode: QUERY; status: NOERROR; id: 56403 ;; Flags: qr rd ra; QUERY: 1; ANSWER: 2; AUTHORITY: 0; ADDITIONAL: 1 ;; EDNS PSEUDOSECTION: ;; Version: 0; flags: ; UDP size: 4096 B; ext-rcode: NOERROR ;; QUESTION SECTION: ;; nextinpact.com. IN A ;; ANSWER SECTION: nextinpact.com. 118 IN A 45.60.122.203 nextinpact.com. 118 IN A 45.60.132.203 ;; Received 75 B ;; Time 2019-10-03 17:34:29 CEST ;; From 2001:41d0:302:2200::180@853(TCP) in 14.1 ms
Puis grâce aux sondes RIPE Atlas, le service. Les Atlas savent faire du DoT. Voyons d'abord en IPv6 (le choix par défaut) :
% blaeu-resolve --dnssec --displayvalidation --displayrtt --tls --nameserver=dot.bortzmeyer.fr --nsid --sort --requested=1000 cyberstructure.fr Nameserver dot.bortzmeyer.fr [ (Authentic Data flag) 2001:4b98:dc0:41:216:3eff:fe27:3d3f] : 956 occurrences Average RTT 1572 ms [TIMEOUT] : 17 occurrences Average RTT 0 ms [TUCONNECT (may be a TLS negotiation error)] : 14 occurrences Average RTT 0 ms Test #22928943 done at 2019-09-30T09:37:15Z
Il y a quand même trop d'échecs. Ceci dit, il s'agit parfois de problèmes réseau et pas de problèmes DoT. Essayons ICMP Echo avec les mêmes sondes :
% blaeu-reach --old_measurement=22928943 --by_probe 2001:41d0:302:2200::180 493 probes reported Test #22929082 done at 2019-09-30T09:48:25Z Tests: 487 successful probes (98.8 %), 6 failed (1.2 %), average RTT: 72 ms
Donc, au moins une partie des problèmes n'est pas spécifique à DoT. Pour les autres cas d'échec, on peut aussi imaginer que certains réseaux ne laissent pas sortir les communications vers le port 853, qu'utilise DoT (c'est d'ailleurs une des principales raisons qui a motivé le développement de DoH qui, utilisant HTTPS, est plus difficile à bloquer). En attendant, essayons en IPv4 :
% blaeu-resolve --dnssec --displayvalidation --displayrtt --tls --nameserver=dot.bortzmeyer.fr --nsid --sort --requested=1000 -4 cyberstructure.fr Nameserver dot.bortzmeyer.fr [ (Authentic Data flag) 2001:4b98:dc0:41:216:3eff:fe27:3d3f] : 971 occurrences Average RTT 975 ms [TIMEOUT] : 9 occurrences Average RTT 0 ms [TUCONNECT (may be a TLS negotiation error)] : 6 occurrences Average RTT 0 ms Test #22929087 done at 2019-09-30T09:51:59Z % blaeu-reach --old_measurement=22929087 --by_probe 193.70.85.11 494 probes reported Test #22929254 done at 2019-09-30T11:35:16Z Tests: 494 successful probes (100.0 %), 0 failed (0.0 %), average RTT: 78 ms
C'est clair, on a moins d'erreurs. L'Internet est hélas moins fiable en IPv6 (surtout vu les difficultés de configuration d'IPv6 chez OVH).
J'ai utilisé plus haut le script test-doh
pour tester le serveur. Ce script est écrit en
Python et disponible en test-doh.py
. Exemple d'utilisation :
% test-doh https://doh.bortzmeyer.fr netflix.com AAAA id 0 opcode QUERY rcode NOERROR flags QR RD RA ;QUESTION netflix.com. IN AAAA ;ANSWER netflix.com. 60 IN AAAA 2a01:578:3::3431:7806 netflix.com. 60 IN AAAA 2a01:578:3::22fd:6807 netflix.com. 60 IN AAAA 2a01:578:3::36e5:444d ... ;AUTHORITY ;ADDITIONAL
On peut aussi utiliser doh-client.sh
, qui appelle curl, et
utilise deux programmes Python, dns-t2b.py
et dns-b2t.py
pour créer les
messages DNS en entrée et les lire à la sortie (DoH n'utilise pas
JSON ou XML, mais le
format binaire du DNS.)
Le service est supervisé par
Icinga. Pour DoT, je me sers d'un programme
développé lors d'un hackathon IETF. (Il y a
depuis une version plus propre dans getdns,
le programme dans le paquetage Debian
getdns-utils
se nomme
getdns_server_mon
.) Lancé à la main, il donne :
% /usr/local/lib/nagios/plugins/check_dns_with_getdns -H 2001:41d0:302:2200::180 -n nextinpact.com GETDNS OK - 16 ms, expiration date 2019-12-23, auth. None: Address 45.60.132.203 Address 45.60.122.203
Pour DoH, certains des tests sont les tests génériques des monitoring plugins, par exemple le test de l'expiration du certificat (pour ne pas être surpris si les renouvellements Let's Encrypt se sont mal passés). Cela se configure avec :
vars.http_vhosts["doh-http"] = { http_uri = "/" http_vhost = "doh.bortzmeyer.fr" http_ssl = true http_sni = true http_timeout = 15 http_certificate = "4,2" }
Ainsi, je suis prévenu s'il reste moins de quatre jours au certificat (et une alarme est levée s'il ne reste que deux jours). Autre test générique, que le serveur renvoie bien un échec si on ne lui parle pas en DoH correctement :
vars.http_vhosts["doh-raw-http"] = { http_uri = "/" http_vhost = "doh.bortzmeyer.fr" http_ssl = true http_sni = true http_timeout = 15 http_expect = 400 http_string = "Unable to parse the request" }
Mais il faut évidemment tester que le serveur répond bien aux
requêtes DoH. On utilise pour cela un script dérivé du
test-doh
cité plus haut, check_doh.py
. Utilisé à la main, ça donne :
% /usr/local/lib/nagios/plugins/check_doh -H doh.bortzmeyer.fr -n nextinpact.com https://doh.bortzmeyer.fr/ OK - No error for nextinpact.com/AAAA, 107 bytes received
Et pour l'intégrer dans Icinga, on déclare une commande dans
commands.conf
:
object CheckCommand "doh_monitor" { command = [ PluginContribDir + "/check_doh" ] arguments = { "-H" = "$address6$", "-n" = "$doh_lookup$", "-p" = "$doh_path$", "-V" = "$doh_vhost$", "-t" = "$doh_type$", "-p" = "$doh_post$", "-i" = "$doh_insecure$", "-h" = "$doh_head$" } }
Puis un service dans services.conf
:
Apply Service "doh" { import "generic-service" check_command = "doh_monitor" assign where (host.address || host.address6) && host.vars.doh }
On peut alors configurer la machine à superviser :
vars.doh = true vars.doh_vhost = "doh.bortzmeyer.fr" vars.doh_lookup = "fr.wikipedia.org" vars.doh_post = true
On supervise également le serveur Web internet :
vars.http_vhosts["doh-admin"] = { http_uri = "/api/v1/servers/localhost" http_port = 8083 http_vhost = "doh.bortzmeyer.fr" http_ssl = true http_sni = true http_timeout = 15 http_header = "X-API-Key: 23b..." http_string = "dohFrontends" } }
Ce service est également accessible via un
.onion
, le
lani4a4fr33kqqjeiy3qubhfx2jewfd3aeaepuwzxrx6zywp2mo4cjad.onion
. Testons
avec kdig et le wrapper torsocks :
% torsocks kdig +tls @lani4a4fr33kqqjeiy3qubhfx2jewfd3aeaepuwzxrx6zywp2mo4cjad.onion A toto.fr ;; TLS session (TLS1.3)-(ECDHE-SECP256R1)-(RSA-PSS-RSAE-SHA256)-(AES-256-GCM) ;; ->>HEADER<<- opcode: QUERY; status: NOERROR; id: 54471 ;; Flags: qr rd ra; QUERY: 1; ANSWER: 1; AUTHORITY: 0; ADDITIONAL: 1 ;; EDNS PSEUDOSECTION: ;; Version: 0; flags: ; UDP size: 1232 B; ext-rcode: NOERROR ;; PADDING: 412 B ;; QUESTION SECTION: ;; toto.fr. IN A ;; ANSWER SECTION: toto.fr. 83659 IN A 164.132.161.100 ;; Received 468 B ;; Time 2021-09-28 18:40:47 UTC ;; From 127.42.42.0@853(TCP) in 328.9 ms
Et en DoH :
% torsocks kdig +https=/ @lani4a4fr33kqqjeiy3qubhfx2jewfd3aeaepuwzxrx6zywp2mo4cjad.onion A toto.fr ;; TLS session (TLS1.3)-(ECDHE-SECP256R1)-(RSA-PSS-RSAE-SHA256)-(AES-256-GCM) ;; HTTP session (HTTP/2-POST)-(lani4a4fr33kqqjeiy3qubhfx2jewfd3aeaepuwzxrx6zywp2mo4cjad.onion/)-(status: 200) ;; ->>HEADER<<- opcode: QUERY; status: NOERROR; id: 0 ;; Flags: qr rd ra; QUERY: 1; ANSWER: 1; AUTHORITY: 0; ADDITIONAL: 1 ;; EDNS PSEUDOSECTION: ;; Version: 0; flags: ; UDP size: 1232 B; ext-rcode: NOERROR ;; PADDING: 412 B ;; QUESTION SECTION: ;; toto.fr. IN A ;; ANSWER SECTION: toto.fr. 83626 IN A 164.132.161.100 ;; Received 468 B ;; Time 2021-09-28 18:41:21 UTC ;; From 127.42.42.0@443(TCP) in 415.7 ms
La configuration a été triviale, il suffit de mettre dans le
torrc
:
HiddenServiceDir /var/lib/tor/dns/ # DoH HiddenServicePort 443 [2001:db8::fada]:443 # DoT HiddenServicePort 853 [2001:db8::fada]:853
Il existe d'autres résolveurs DoH gérés par des associations ou
des individus, comme celui de Shaft (avec une
documentation détaillée sur la configuration du client et sur celle
du serveur),
celui du
.cz
,
https://www.nic.cz/odvr/
ou comme celui de l'association
42l. Mais je n'en trouve pas (pour l'instant) qui ait
documenté en détail leur configuration technique.
Sinon, si vous aimez lire, il y a les très bons supports de l'exposé de Shaft à Pas Sage En Seine (plein de DoT et un peu de DoH.)
Version PDF de cette page (mais vous pouvez aussi imprimer depuis votre navigateur, il y a une feuille de style prévue pour cela)
Source XML de cette page (cette page est distribuée sous les termes de la licence GFDL)