Aumente o servidor DNS sobre HTTPS

Vários aspectos da operação do DNS foram abordados repetidamente pelo autor em vários artigos publicados como parte do blog. Ao mesmo tempo, a ênfase principal sempre foi a melhoria da segurança desse serviço essencial para toda a Internet.


Doh


Até recentemente, apesar da obviedade da vulnerabilidade do tráfego DNS, que, na maioria das vezes, ainda é transmitida de maneira clara, por ações maliciosas de provedores que buscam aumentar sua renda incorporando publicidade no conteúdo, agências policiais e censura, além de apenas criminosos, o processo de aprimoramento de sua proteção , apesar da presença de várias tecnologias, como DNSSEC / DANE, DNScrypt, DNS sobre TLS e DNS sobre HTTPS, derrapou. E se as soluções para servidores, e algumas delas já existem há algum tempo, são amplamente conhecidas e disponíveis, então o suporte do software cliente deixa muito a desejar.


Felizmente, a situação está mudando. Em particular, os desenvolvedores do popular navegador Firefox anunciaram planos para ativar o modo de suporte padrão para DNS sobre HTTPS (DoH) em um futuro próximo. Isso deve ajudar a proteger o tráfego DNS do usuário da WWW das ameaças mencionadas, mas pode causar novas ameaças.



1. Problemas de DNS sobre HTTPS


À primeira vista, o início da introdução maciça de DNS sobre HTTPS em software em execução na Internet causa apenas uma reação positiva. No entanto, o diabo, como se costuma dizer, está nos detalhes.


O primeiro problema que limita o escopo do uso em massa de DoH é seu foco apenas no tráfego da Web. De fato, o protocolo HTTP e sua versão atual do HTTP / 2, na qual o DoH se baseia, é a base da WWW. Mas a Internet não é apenas a web. Existem muitos serviços populares, como email, todos os tipos de mensageiros, sistemas de transferência de arquivos, streaming de multimídia, etc. que não usam HTTP. Assim, apesar da percepção de muitos DoH como uma panacéia, ela se torna inaplicável sem esforços adicionais (e desnecessários), para nada além de tecnologia de navegador. A propósito, o DNS sobre TLS parece um candidato muito mais digno para essa função, que implementa o encapsulamento do tráfego DNS padrão em um protocolo TLS padrão seguro.


O segundo problema, que é potencialmente muito mais significativo que o primeiro, é a rejeição real do DNS descentralizado inerente por design, a fim de usar o único servidor DoH especificado nas configurações do navegador. Em particular, a Mozilla oferece o uso do serviço do Cloudflare. Um serviço semelhante também foi lançado por outras figuras proeminentes da Internet, em particular o Google. Acontece que a implementação do DNS sobre HTTPS na forma em que é proposta agora, apenas aumenta a dependência dos usuários finais nos maiores serviços. Não é segredo que as informações que a análise de consultas DNS pode fornecer são capazes de coletar ainda mais dados sobre ela, além de aumentar sua precisão e relevância.


Nesse sentido, o autor foi e continua sendo um defensor da implementação em massa de DNS não sobre HTTPS, mas DNS sobre TLS junto com DNSSEC / DANE como uma centralização universal, segura e que não promove ainda mais a Internet para garantir a segurança do tráfego DNS. Infelizmente, não há razão para esperar a rápida introdução de suporte em massa para alternativas de DoH no software cliente por razões óbvias, e os entusiastas de tecnologias seguras continuam sendo seu destino.


Mas como estamos recebendo o DoH agora, por que não usá-lo depois de deixar a vigilância em potencial pelas empresas através de seus servidores para o seu próprio servidor DNS sobre HTTPS?


2. DNS sobre HTTPS


Se você observar o padrão RFC8484 que descreve o protocolo DNS sobre HTTPS, poderá ver que, de fato, é uma API da web que permite encapsular um pacote DNS padrão no protocolo HTTP / 2. Isso é implementado por meio de cabeçalhos HTTP especiais, além de converter o formato binário dos dados DNS transmitidos (consulte RFC1035 e documentos subseqüentes) em um formulário que permita enviar e recebê-los, além de trabalhar com os metadados necessários.


Por padrão, apenas HTTP / 2 e uma conexão TLS segura são suportadas.


Uma consulta DNS pode ser enviada usando os métodos GET e POST padrão. No primeiro caso, a solicitação é transformada em uma cadeia codificada em base64URL e, na segunda, através do corpo da solicitação POST em formato binário. Nesse caso, ao consultar e responder ao DNS, um tipo de dados MIME especial application / dns-message é usado .


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 

Observe também o cabeçalho cache-control: na resposta do servidor da web. O parâmetro max-age contém o valor TTL para o registro DNS retornado (ou o valor mínimo se o conjunto for retornado).


Com base no exposto, o funcionamento do servidor DoH consiste em vários estágios.


  • Receba uma solicitação HTTP. Se for GET, decodifique o pacote da codificação base64URL.
  • Envie este pacote para o servidor DNS.
  • Obtenha uma resposta do servidor DNS
  • Encontre o valor TTL mínimo nos registros recebidos.
  • Retornar resposta HTTP para o cliente.

3. Próprio servidor DNS sobre HTTPS


A maneira mais fácil, rápida e eficaz de iniciar seu próprio servidor DNS sobre HTTPS é usar o servidor Web HTTP / 2 H2O , sobre o qual o autor já escreveu brevemente (consulte " Servidor Web H2O de alto desempenho ").


A favor dessa escolha está o fato de que todo o código do seu próprio servidor DoH pode ser totalmente implementado usando o interpretador mruby integrado ao próprio H2O. Além das bibliotecas padrão, para se comunicar com o servidor DNS, você precisa da biblioteca Socket (mrbgem), que, felizmente, já está incluída na versão atual de desenvolvimento do H2O 2.3.0-beta2 presente nas portas do FreeBSD. No entanto, não é difícil adicioná-lo a nenhuma versão anterior clonando o repositório da biblioteca Socket no diretório / deps antes da compilação.


 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 ... 

A configuração do servidor da web geralmente é padrão.


 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 

A única exceção é o manipulador de URL / dns-query , que é responsável, de fato, pelo nosso servidor h2odoh DNS sobre HTTPS, escrito em mruby e chamado pela opção de manipulador de arquivo 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 } 

Observe que o servidor de armazenamento em cache local é responsável pelo processamento de pacotes DNS, neste caso Não vinculado à distribuição padrão do FreeBSD. Do ponto de vista da segurança, esta é a melhor solução. No entanto, nada impede a substituição do host local pelo endereço de outro DNS que você pretende usar.


 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 *:* 

Resta reiniciar o H2O e ver o que veio dele.


 root@beta:/usr/local/etc/h2o # service h2o restart Stopping h2o. Waiting for PIDS: 69871. Starting h2o. start_server (pid:70532) starting now... 

4. Teste


Portanto, vamos verificar os resultados enviando uma solicitação de teste novamente e examinando o tráfego de rede usando o utilitário 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 

A saída mostra como a solicitação de resolução de endereço example.com foi recebida e processada com êxito pelo servidor DNS.


Agora resta ativar o servidor no navegador Firefox. Para fazer isso, você precisa alterar várias configurações de about: config nas páginas de configuração.


Configuração DNS-sobre-HTTPS do Firefox


Primeiro, este é o endereço da nossa API no qual o navegador consultará as informações de DNS em network.trr.uri . Também é recomendável que você especifique o IP do domínio nesta URL para obter uma resolução segura no IP usando o próprio navegador sem acessar o DNS em network.trr.bootstrapAddress . E, finalmente, o próprio parâmetro network.trr.mode , que inclui o uso de DoH. Definir o valor como "3" forçará o navegador a usar exclusivamente DNS sobre HTTPS para resolver nomes, e o "2" mais confiável e seguro dará prioridade ao DoH, deixando o acesso DNS padrão como substituto.


5. LUCRO!


O artigo foi útil? Então não se acanhe e apóie o dinheiro através do formulário de doação (abaixo).

Source: https://habr.com/ru/post/pt467237/


All Articles