Divers aspects du fonctionnement du DNS ont été abordés à plusieurs reprises par l'auteur dans un certain nombre d' articles publiés dans le cadre du blog. Dans le même temps, l'accent a toujours été mis sur l'amélioration de la sécurité de ce service clé pour l'ensemble de l'Internet.

Jusqu'à récemment, malgré l'évidence de la vulnérabilité du trafic DNS, qui, pour la plupart, est toujours transmis en clair, pour des actions malveillantes de fournisseurs cherchant à augmenter leurs revenus en intégrant de la publicité dans le contenu, les autorités répressives de l'État et la censure ainsi que des criminels, le processus d' amélioration de sa protection , malgré la présence de diverses technologies, telles que DNSSEC / DANE, DNScrypt, DNS-over-TLS et DNS-over-HTTPS, a dérapé. Et si les solutions serveur, et certaines d'entre elles existent depuis un certain temps, sont largement connues et disponibles, alors leur prise en charge par le logiciel client laisse beaucoup à désirer.
Heureusement, la situation évolue. En particulier, les développeurs du célèbre navigateur Firefox ont annoncé leur intention d'activer le mode de prise en charge par défaut du DNS sur HTTPS (DoH) dans un proche avenir. Cela devrait aider à protéger le trafic DNS de l'utilisateur WWW contre les menaces susmentionnées, mais il peut potentiellement en générer de nouvelles.
1. Problèmes DNS sur HTTPS
À première vue, le début de l'introduction massive du DNS sur HTTPS dans les logiciels fonctionnant sur Internet ne provoque qu'une réaction positive. Cependant, le diable, comme on dit, est dans les détails.
Le premier problème qui limite la portée de l'utilisation de masse du DoH est sa concentration uniquement sur le trafic Web. En effet, le protocole HTTP et sa version actuelle de HTTP / 2, sur laquelle DoH est basé, est le fondement du WWW. Mais Internet n'est pas seulement le Web. Il existe de nombreux services populaires, tels que le courrier électronique, toutes sortes de messagers, les systèmes de transfert de fichiers, le streaming multimédia, etc. qui n'utilisent pas HTTP. Ainsi, malgré la perception par de nombreux DoH comme une panacée, il s'avère inapplicable sans efforts supplémentaires (et inutiles), pour rien d'autre que la technologie du navigateur. Soit dit en passant, DNS sur TLS ressemble à un candidat beaucoup plus digne pour ce rôle, qui implémente l'encapsulation du trafic DNS standard dans un protocole TLS standard sécurisé.
Le deuxième problème, qui est potentiellement beaucoup plus important que le premier, est le rejet réel du DNS décentralisé inhérent par conception dans le but d'utiliser le serveur DoH unique spécifié dans les paramètres du navigateur. En particulier, Mozilla propose d'utiliser le service de Cloudflare. Un service similaire a également été lancé par d'autres personnalités de l'Internet, notamment Google. Il s'avère que la mise en œuvre du DNS sur HTTPS sous la forme dans laquelle il est proposé actuellement, ne fait qu'augmenter la dépendance des utilisateurs finaux vis-à-vis des services les plus importants. Ce n'est un secret pour personne que les informations que l'analyse des requêtes DNS peut fournir sont capables de collecter encore plus de données à ce sujet, ainsi que d'augmenter leur précision et leur pertinence.
À cet égard, l'auteur a été et demeure un partisan de la mise en œuvre massive non pas du DNS sur HTTPS, mais du DNS sur TLS avec DNSSEC / DANE en tant que moyen universel, sécurisé et ne favorisant pas davantage la centralisation des moyens Internet pour assurer la sécurité du trafic DNS. Malheureusement, pour s'attendre à l'introduction rapide d'un support de masse des alternatives DoH dans les logiciels clients pour des raisons évidentes, ce n'est pas nécessaire et son destin reste les amateurs de technologies sûres.
Mais puisque nous obtenons maintenant le DoH, pourquoi ne pas l'utiliser après avoir abandonné la surveillance potentielle des entreprises via leurs serveurs vers votre propre serveur DNS sur HTTPS?
2. DNS sur HTTPS
Si vous regardez la norme RFC8484 décrivant le protocole DNS sur HTTPS, vous pouvez voir qu'il s'agit en fait d'une API Web qui vous permet d'encapsuler un paquet DNS standard dans le protocole HTTP / 2. Ceci est mis en œuvre via des en-têtes HTTP spéciaux, ainsi que la conversion du format binaire des données DNS transmises (voir RFC1035 et documents ultérieurs) en un formulaire qui vous permet d'envoyer et de recevoir ces données, ainsi que de travailler avec les métadonnées nécessaires.
Par défaut, seuls HTTP / 2 et une connexion TLS sécurisée sont pris en charge.
Une requête DNS peut être envoyée à l'aide des méthodes GET et POST standard. Dans le premier cas, la demande est transformée en une chaîne codée en base64URL, et dans le second, via le corps de la demande POST sous forme binaire. Dans ce cas, lors de l'interrogation et de la réponse à DNS, une application spéciale de type de données MIME / message DNS est utilisée .
root@eprove:~ # curl -H 'accept: application/dns-message' 'https://my.domain/dns-query?dns=q80BAAABAAAAAAAAB2V4YW1wbGUDY29tAAABAAE' -v * Trying 2001:100:200:300::400:443... * TCP_NODELAY set * Connected to eprove.net (2001:100:200:300::400) port 443 (#0) * ALPN, offering h2 * ALPN, offering http/1.1 * successfully set certificate verify locations: * CAfile: /usr/local/share/certs/ca-root-nss.crt CApath: none * TLSv1.3 (OUT), TLS handshake, Client hello (1): * TLSv1.3 (IN), TLS handshake, Server hello (2): * TLSv1.3 (IN), TLS handshake, Encrypted Extensions (8): * TLSv1.3 (IN), TLS handshake, Certificate (11): * TLSv1.3 (IN), TLS handshake, CERT verify (15): * TLSv1.3 (IN), TLS handshake, Finished (20): * TLSv1.3 (OUT), TLS change cipher, Change cipher spec (1): * TLSv1.3 (OUT), TLS handshake, Finished (20): * SSL connection using TLSv1.3 / TLS_AES_256_GCM_SHA384 * ALPN, server accepted to use h2 * Server certificate: * subject: CN=my.domain * start date: Jul 22 00:07:13 2019 GMT * expire date: Oct 20 00:07:13 2019 GMT * subjectAltName: host "my.domain" matched cert's "my.domain" * issuer: C=US; O=Let's Encrypt; CN=Let's Encrypt Authority X3 * SSL certificate verify ok. * Using HTTP2, server supports multi-use * Connection state changed (HTTP/2 confirmed) * Copying HTTP/2 data in stream buffer to connection buffer after upgrade: len=0 * Using Stream ID: 1 (easy handle 0x801441000) > GET /dns-query?dns=q80BAAABAAAAAAAAB2V4YW1wbGUDY29tAAABAAE HTTP/2 > Host: eprove.net > User-Agent: curl/7.65.3 > accept: application/dns-message > * TLSv1.3 (IN), TLS handshake, Newsession Ticket (4): * Connection state changed (MAX_CONCURRENT_STREAMS == 100)! < HTTP/2 200 < server: h2o/2.3.0-beta2 < content-type: application/dns-message < cache-control: max-age=86274 < date: Thu, 12 Sep 2019 13:07:25 GMT < strict-transport-security: max-age=15768000; includeSubDomains; preload < content-length: 45 < Warning: Binary output can mess up your terminal. Use "--output -" to tell Warning: curl to output it to your terminal anyway, or consider "--output Warning: <FILE>" to save to a file. * Failed writing body (0 != 45) * stopped the pause stream! * Connection #0 to host eprove.net left intact
Notez également l'en - tête cache-control: dans la réponse du serveur Web. Le paramètre max-age contient la valeur TTL pour l'enregistrement DNS retourné (ou la valeur minimale si leur ensemble est retourné).
Sur la base de ce qui précède, le fonctionnement du serveur DoH comprend plusieurs étapes.
- Recevez une requête HTTP. Si c'est GET, décodez le paquet à partir du codage base64URL.
- Envoyez ce paquet au serveur DNS.
- Obtenez une réponse du serveur DNS
- Recherchez la valeur TTL minimale dans les enregistrements reçus.
- Renvoie la réponse HTTP au client.
3. Posséder un serveur DNS sur HTTPS
Le moyen le plus simple, le plus rapide et le plus efficace de démarrer votre propre serveur DNS sur HTTPS consiste à utiliser le serveur Web HTTP / 2 H2O , dont l'auteur a déjà brièvement parlé (voir " Serveur Web H2O hautes performances ").
En faveur de ce choix est le fait que tout le code de votre propre serveur DoH peut être entièrement implémenté en utilisant l'interpréteur mruby intégré dans le H2O lui-même . En plus des bibliothèques standard, pour communiquer avec le serveur DNS, vous avez besoin de la bibliothèque Socket (mrbgem), qui, heureusement, est déjà incluse dans la version de développement actuelle de H2O 2.3.0-beta2 présente dans les ports FreeBSD. Cependant, il n'est pas difficile de l'ajouter à une version précédente en clonant le référentiel de la bibliothèque Socket dans le répertoire / deps avant la compilation.
root@beta:~ # uname -v FreeBSD 12.0-RELEASE-p10 GENERIC root@beta:~ # cd /usr/ports/www/h2o root@beta:/usr/ports/www/h2o # make extract ===> License MIT BSD2CLAUSE accepted by the user ===> h2o-2.2.6 depends on file: /usr/local/sbin/pkg - found ===> Fetching all distfiles required by h2o-2.2.6 for building ===> Extracting for h2o-2.2.6. => SHA256 Checksum OK for h2o-h2o-v2.2.6_GH0.tar.gz. ===> h2o-2.2.6 depends on file: /usr/local/bin/ruby26 - found root@beta:/usr/ports/www/h2o # cd work/h2o-2.2.6/deps/ root@beta:/usr/ports/www/h2o/work/h2o-2.2.6/deps # git clone https://github.com/iij/mruby-socket.git «mruby-socket»… remote: Enumerating objects: 385, done. remote: Total 385 (delta 0), reused 0 (delta 0), pack-reused 385 : 100% (385/385), 98.02 KiB | 647.00 KiB/s, . : 100% (208/208), . root@beta:/usr/ports/www/h2o/work/h2o-2.2.6/deps # ll total 181 drwxr-xr-x 9 root wheel 18 12 . 16:09 brotli/ drwxr-xr-x 2 root wheel 4 12 . 16:09 cloexec/ drwxr-xr-x 2 root wheel 5 12 . 16:09 golombset/ drwxr-xr-x 4 root wheel 35 12 . 16:09 klib/ drwxr-xr-x 2 root wheel 5 12 . 16:09 libgkc/ drwxr-xr-x 4 root wheel 26 12 . 16:09 libyrmcds/ drwxr-xr-x 13 root wheel 32 12 . 16:09 mruby/ drwxr-xr-x 5 root wheel 11 12 . 16:09 mruby-digest/ drwxr-xr-x 5 root wheel 10 12 . 16:09 mruby-dir/ drwxr-xr-x 5 root wheel 10 12 . 16:09 mruby-env/ drwxr-xr-x 4 root wheel 9 12 . 16:09 mruby-errno/ drwxr-xr-x 5 root wheel 14 12 . 16:09 mruby-file-stat/ drwxr-xr-x 5 root wheel 10 12 . 16:09 mruby-iijson/ drwxr-xr-x 5 root wheel 11 12 . 16:09 mruby-input-stream/ drwxr-xr-x 6 root wheel 11 12 . 16:09 mruby-io/ drwxr-xr-x 5 root wheel 10 12 . 16:09 mruby-onig-regexp/ drwxr-xr-x 4 root wheel 10 12 . 16:09 mruby-pack/ drwxr-xr-x 5 root wheel 10 12 . 16:09 mruby-require/ drwxr-xr-x 6 root wheel 10 12 . 16:10 mruby-socket/ drwxr-xr-x 2 root wheel 9 12 . 16:09 neverbleed/ drwxr-xr-x 2 root wheel 13 12 . 16:09 picohttpparser/ drwxr-xr-x 2 root wheel 4 12 . 16:09 picotest/ drwxr-xr-x 9 root wheel 16 12 . 16:09 picotls/ drwxr-xr-x 4 root wheel 8 12 . 16:09 ssl-conservatory/ drwxr-xr-x 8 root wheel 18 12 . 16:09 yaml/ drwxr-xr-x 2 root wheel 8 12 . 16:09 yoml/ root@beta:/usr/ports/www/h2o/work/h2o-2.2.6/deps # cd ../../.. root@beta:/usr/ports/www/h2o # make install clean ...
La configuration du serveur Web est généralement standard.
root@beta:/usr/ports/www/h2o # cd /usr/local/etc/h2o/ root@beta:/usr/local/etc/h2o # cat h2o.conf # this sample config gives you a feel for how h2o can be used # and a high-security configuration for TLS and HTTP headers # see https://h2o.examp1e.net/ for detailed documentation # and h2o --help for command-line options and settings # v.20180207 (c)2018 by Max Kostikov http://kostikov.co e-mail: max@kostikov.co user: www pid-file: /var/run/h2o.pid access-log: path: /var/log/h2o/h2o-access.log format: "%h %v %l %u %t \"%r\" %s %b \"%{Referer}i\" \"%{User-agent}i\"" error-log: /var/log/h2o/h2o-error.log expires: off compress: on file.dirlisting: off file.send-compressed: on file.index: [ 'index.html', 'index.php' ] listen: port: 80 listen: port: 443 ssl: cipher-suite: 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 cipher-preference: server dh-file: /etc/ssl/dhparams.pem certificate-file: /usr/local/etc/letsencrypt/live/eprove.net/fullchain.pem key-file: /usr/local/etc/letsencrypt/live/my.domain/privkey.pem hosts: "*.my.domain": paths: &go_tls "/": redirect: status: 301 url: https://my.domain/ "my.domain:80": paths: *go_tls "my.domain:443": header.add: "Strict-Transport-Security: max-age=15768000; includeSubDomains; preload" paths: "/dns-query": mruby.handler-file: /usr/local/etc/h2o/h2odoh.rb
La seule exception est le gestionnaire d'URL / dns-query , qui est en fait responsable de notre serveur h2odoh DNS-over-HTTPS, écrit en mruby et appelé via l'option de gestionnaire de fichier mruby.handler .
root@beta:/usr/local/etc/h2o # cat h2odoh.rb # H2O HTTP/2 web server as DNS-over-HTTP service # v.20190908 (c)2018-2019 Max Kostikov https://kostikov.co e-mail: max@kostikov.co proc {|env| if env['HTTP_ACCEPT'] == "application/dns-message" case env['REQUEST_METHOD'] when "GET" req = env['QUERY_STRING'].gsub(/^dns=/,'') # base64URL decode req = req.tr("-_", "+/") if !req.end_with?("=") && req.length % 4 != 0 req = req.ljust((req.length + 3) & ~3, "=") end req = req.unpack1("m") when "POST" req = env['rack.input'].read else req = "" end if req.empty? [400, { 'content-type' => 'text/plain' }, [ "Bad Request" ]] else # --- ask DNS server sock = UDPSocket.new sock.connect("localhost", 53) sock.send(req, 0) str = sock.recv(4096) sock.close # --- find lowest TTL in response nans = str[6, 2].unpack1('n') # number of answers if nans > 0 # no DNS failure shift = 12 ttl = 0 while nans > 0 # process domain name compression if str[shift].unpack1("C") < 192 shift = str.index("\x00", shift) + 5 if ttl == 0 # skip question section next end end shift += 6 curttl = str[shift, 4].unpack1('N') shift += str[shift + 4, 2].unpack1('n') + 6 # responce data size if ttl == 0 or ttl > curttl ttl = curttl end nans -= 1 end cc = 'max-age=' + ttl.to_s else cc = 'no-cache' end [200, { 'content-type' => 'application/dns-message', 'content-length' => str.size, 'cache-control' => cc }, [ str ] ] end else [415, { 'content-type' => 'text/plain' }, [ "Unsupported Media Type" ]] end }
Veuillez noter que le serveur de mise en cache local est responsable du traitement des paquets DNS, dans ce cas non lié à la distribution standard de FreeBSD. Du point de vue de la sécurité, c'est la meilleure solution. Cependant, rien n'empêche de remplacer localhost par l'adresse d'un autre DNS que vous avez l'intention d'utiliser.
root@beta:/usr/local/etc/h2o # local-unbound verison usage: local-unbound [options] start unbound daemon DNS resolver. -h this help -c file config file to read instead of /var/unbound/unbound.conf file format is described in unbound.conf(5). -d do not fork into the background. -p do not create a pidfile. -v verbose (more times to increase verbosity) Version 1.8.1 linked libs: mini-event internal (it uses select), OpenSSL 1.1.1a-freebsd 20 Nov 2018 linked modules: dns64 respip validator iterator BSD licensed, see LICENSE in source package for details. Report bugs to unbound-bugs@nlnetlabs.nl root@eprove:/usr/local/etc/h2o # sockstat -46 | grep unbound unbound local-unbo 69749 3 udp6 ::1:53 *:* unbound local-unbo 69749 4 tcp6 ::1:53 *:* unbound local-unbo 69749 5 udp4 127.0.0.1:53 *:* unbound local-unbo 69749 6 tcp4 127.0.0.1:53 *:*
Reste à redémarrer H2O et à voir ce qui en est sorti.
root@beta:/usr/local/etc/h2o # service h2o restart Stopping h2o. Waiting for PIDS: 69871. Starting h2o. start_server (pid:70532) starting now...
4. Test
Vérifions donc les résultats en envoyant à nouveau une demande de test et en examinant le trafic réseau à l'aide de l'utilitaire tcpdump .
root@beta/usr/local/etc/h2o # curl -H 'accept: application/dns-message' 'https://my.domain/dns-query?dns=q80BAAABAAAAAAAAB2V4YW1wbGUDY29tAAABAAE' Warning: Binary output can mess up your terminal. Use "--output -" to tell Warning: curl to output it to your terminal anyway, or consider "--output Warning: <FILE>" to save to a file. ... root@beta:~ # tcpdump -n -i lo0 udp port 53 -xx -XX -vv tcpdump: listening on lo0, link-type NULL (BSD loopback), capture size 262144 bytes 16:32:40.420831 IP (tos 0x0, ttl 64, id 37575, offset 0, flags [none], proto UDP (17), length 57, bad cksum 0 (->e9ea)!) 127.0.0.1.21070 > 127.0.0.1.53: [bad udp cksum 0xfe38 -> 0x33e3!] 43981+ A? example.com. (29) 0x0000: 0200 0000 4500 0039 92c7 0000 4011 0000 ....E..9....@... 0x0010: 7f00 0001 7f00 0001 524e 0035 0025 fe38 ........RN.5.%.8 0x0020: abcd 0100 0001 0000 0000 0000 0765 7861 .............exa 0x0030: 6d70 6c65 0363 6f6d 0000 0100 01 mple.com..... 16:32:40.796507 IP (tos 0x0, ttl 64, id 37590, offset 0, flags [none], proto UDP (17), length 73, bad cksum 0 (->e9cb)!) 127.0.0.1.53 > 127.0.0.1.21070: [bad udp cksum 0xfe48 -> 0x43fa!] 43981 q: A? example.com. 1/0/0 example.com. A 93.184.216.34 (45) 0x0000: 0200 0000 4500 0049 92d6 0000 4011 0000 ....E..I....@... 0x0010: 7f00 0001 7f00 0001 0035 524e 0035 fe48 .........5RN.5.H 0x0020: abcd 8180 0001 0001 0000 0000 0765 7861 .............exa 0x0030: 6d70 6c65 0363 6f6d 0000 0100 01c0 0c00 mple.com........ 0x0040: 0100 0100 0151 8000 045d b8d8 22 .....Q...].." ^C 2 packets captured 23 packets received by filter 0 packets dropped by kernel
La sortie montre comment la demande de résolution d'adresse example.com a été reçue et traitée avec succès par le serveur DNS.
Reste maintenant à activer notre serveur dans le navigateur Firefox. Pour ce faire, vous devez en modifier plusieurs sur: les paramètres de configuration sur les pages de configuration.

Tout d'abord, c'est l'adresse de notre API à laquelle le navigateur interrogera les informations DNS dans network.trr.uri . Il est également recommandé de spécifier l'IP du domaine à partir de cette URL pour une résolution sécurisée dans IP à l'aide du navigateur lui-même sans accéder au DNS dans network.trr.bootstrapAddress . Et, enfin, le paramètre network.trr.mode lui-même, qui inclut l'utilisation de DoH. La définition de la valeur sur "3" forcera le navigateur à utiliser exclusivement DNS sur HTTPS pour résoudre les noms, et le "2" plus fiable et sécurisé donnera la priorité DoH, laissant l'accès DNS standard comme solution de rechange.
5. PROFIT!
L'article vous a-t-il été utile? Alors n'hésitez pas et soutenez l'argent via le formulaire de don (ci-dessous).