Vari aspetti dell'operatività del DNS sono già stati trattati dall'autore in diversi pubblicati nel blog. In questo contesto, l'accento è sempre stato posto sul miglioramento della sicurezza di questo servizio fondamentale per tutta Internet.

Fino a poco tempo fa, nonostante l'evidente vulnerabilità del traffico DNS, che è ancora per lo più trasmesso in chiaro, per azioni malevole da parte dei provider, che cercano di aumentare i loro guadagni integrando pubblicità nei contenuti, dalle autorità governative e dalla censura, così come da parte di semplici criminali, il processo , nonostante la presenza di diverse tecnologie come DNSSEC/DANE, DNScrypt, DNS-over-TLS e DNS-over-HTTPS, è stato lento. E sebbene le soluzioni server, alcune delle quali esistono da molto tempo, siano ampiamente conosciute e disponibili, il supporto da parte del software client lascia molto a desiderare.
Fortunatamente, la situazione è in cambiamento. In particolare, gli sviluppatori del popolare browser Firefox piani per l'inclusione per default della modalità (DoH) a breve. Questo dovrebbe contribuire a proteggere il traffico DNS degli utenti WWW dalle minacce sopra menzionate, ma potrebbe potenzialmente causare nuovi problemi.
1. Problemi con DNS-over-HTTPS
A prima vista, l'adozione massiccia di DNS-over-HTTPS nel software che opera in Internet suscita solo reazioni positive. Tuttavia, come si suol dire, il diavolo è nei dettagli.
Il primo problema che limita l'adozione su larga scala di DoH è la sua focalizzazione esclusiva sul traffico web. Infatti, il protocollo HTTP e la sua versione attuale HTTP/2, su cui si basa DoH, costituiscono la base del WWW. Tuttavia, Internet non è solo web. Ci sono molti servizi popolari, come l'email, vari messaggeri, sistemi di trasferimento file, streaming multimediale e altro, che non utilizzano HTTP. Pertanto, nonostante che molti considerino DoH una panacea, esso risulta inapplicabile senza sforzi aggiuntivi (e non necessari) per niente di diverso dalle tecnologie browser. A proposito, DNS-over-TLS appare come un candidato molto più valido per questo ruolo, poiché implementa l'incapsulamento del traffico DNS standard all'interno del protocollo standard TLS protetto.
Un'altra problematica che è potenzialmente molto più significativa della prima è il reale abbandono della decentralizzazione intrinseca del DNS per favorire l'utilizzo di un unico server DoH configurato nelle impostazioni del browser. In particolare, Mozilla consiglia di utilizzare il servizio di Cloudflare. Anche altre importanti realtà del web, come Google, hanno lanciato servizi simili. Di conseguenza, l'implementazione del DNS-over-HTTPS come proposta attualmente accresce la dipendenza degli utenti finali dai maggiori fornitori di servizi. Non è un segreto che l'analisi delle richieste DNS possa raccogliere ulteriori dati su di loro e migliorarne la precisione e la rilevanza.
In questo contesto, l'autore è stato e rimane un sostenitore dell'adozione massiccia di DNS-over-TLS insieme a DNSSEC/DANE come soluzione universale, sicura e che non promuove la centralizzazione di Internet per garantire la sicurezza del traffico DNS. Sfortunatamente, non ci si può aspettare un rapido implemento di un supporto diffuso per alternative a DoH nel software client, per motivi comprensibili, e attualmente spetta solo agli appassionati delle tecnologie sicure.
Ma, dato che ora stiamo ottenendo DoH, perché non utilizzarlo, allontanandoci dal potenziale monitoraggio da parte delle corporazioni tramite i loro server verso il proprio server DNS-over-HTTPS?
2. Protocollo DNS-over-HTTPS
Se si guarda allo standard che descrive il protocollo DNS-over-HTTPS, si può vedere che esso rappresenta, fondamentalmente, un'API web che consente di incapsulare un pacchetto DNS standard nel protocollo HTTP/2. Questo viene realizzato tramite intestazioni HTTP speciali e convertendo il formato binario dei dati DNS trasmessi (vedi e documenti successivi) in una forma che consente di trasmetterli e riceverli, nonché di lavorare con i metadati necessari.
Il supporto standard è solo per HTTP/2 e connessioni sicure TLS.
L'invio di una richiesta DNS può avvenire tramite i metodi standard GET e POST. Nel primo caso, la richiesta viene trasformata in una stringa codificata base64URL, mentre nel secondo — attraverso il corpo della richiesta POST in forma binaria. In questo caso, durante la richiesta e la risposta DNS viene utilizzato un tipo MIME speciale. application/dns-message.
root@eprove:~ # curl -H 'accept: application/dns-message' 'https://my.domaint/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: " to save to a file.
* Failed writing body (0 != 45)
* stopped the pause stream!
* Connection #0 to host eprove.net left intactSi prega di notare anche il titolo cache-control: nella risposta del server web. Nel parametro max-age si trova il valore TTL per il record DNS restituito (o il valore minimo se viene restituito un insieme di essi).
Di conseguenza, il funzionamento del server DoH si compone di diverse fasi.
- Ricevere la richiesta HTTP. Se si tratta di un GET, decodificare il pacchetto dalla codifica base64URL.
- Inviare questo pacchetto al server DNS.
- Ricevere la risposta dal server DNS.
- Trovare il valore TTL minimo nei record ricevuti.
- Restituire al cliente la risposta via HTTP.
3. Il tuo server DNS-over-HTTPS
Il modo più semplice, veloce ed efficace per avviare il proprio server DNS-over-HTTPS è utilizzare un server web HTTP/2 , di cui l'autore ha già scritto brevemente (vedi “«).
A supportare questa scelta c'è il fatto che tutto il codice del proprio server DoH può essere completamente implementato utilizzando l'interprete integrato in H2O . Oltre alle librerie standard, per lo scambio di dati con il server DNS è necessaria la libreria (mrbgem) Socket, che, fortunatamente, è già inclusa nella versione attuale per sviluppatori H2O 2.3.0-beta2. in FreeBSD ports. Tuttavia, non è difficile aggiungerla anche in qualsiasi versione precedente clonando il repository nella directory /deps prima della compilazione.
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 accettata dall'utente
=== h2o-2.2.6 dipende dal file: /usr/local/sbin/pkg - trovato
=== Recupero tutti i file necessari per h2o-2.2.6 per la compilazione
=== Estrazione di h2o-2.2.6.
=> Controllo SHA256 OK per h2o-h2o-v2.2.6_GH0.tar.gz.
=== h2o-2.2.6 dipende dal file: /usr/local/bin/ruby26 - trovato
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
Clonazione in "mruby-socket"…
remote: Enumerando oggetti: 385, fatto.
remote: Totale 385 (delta 0), riutilizzati 0 (delta 0), pack-reused 385
Ricezione oggetti: 100% (385/385), 98.02 KiB | 647.00 KiB/s, pronto.
Determinazione delle modifiche: 100% (208/208), pronto.
root@beta:/usr/ports/www/h2o/work/h2o-2.2.6/deps # ll
total 181
drwxr-xr-x 9 root wheel 18 12 ago. 16:09 brotli/
drwxr-xr-x 2 root wheel 4 12 ago. 16:09 cloexec/
drwxr-xr-x 2 root wheel 5 12 ago. 16:09 golombset/
drwxr-xr-x 4 root wheel 35 12 ago. 16:09 klib/
drwxr-xr-x 2 root wheel 5 12 ago. 16:09 libgkc/
drwxr-xr-x 4 root wheel 26 12 ago. 16:09 libyrmcds/
drwxr-xr-x 13 root wheel 32 12 ago. 16:09 mruby/
drwxr-xr-x 5 root wheel 11 12 ago. 16:09 mruby-digest/
drwxr-xr-x 5 root wheel 10 12 ago. 16:09 mruby-dir/
drwxr-xr-x 5 root wheel 10 12 ago. 16:09 mruby-env/
drwxr-xr-x 4 root wheel 9 12 ago. 16:09 mruby-errno/
drwxr-xr-x 5 root wheel 14 12 ago. 16:09 mruby-file-stat/
drwxr-xr-x 5 root wheel 10 12 ago. 16:09 mruby-iijson/
drwxr-xr-x 5 root wheel 11 12 ago. 16:09 mruby-input-stream/
drwxr-xr-x 6 root wheel 11 12 ago. 16:09 mruby-io/
drwxr-xr-x 5 root wheel 10 12 ago. 16:09 mruby-onig-regexp/
drwxr-xr-x 4 root wheel 10 12 ago. 16:09 mruby-pack/
drwxr-xr-x 5 root wheel 10 12 ago. 16:09 mruby-require/
drwxr-xr-x 6 root wheel 10 12 set. 16:10 mruby-socket/
drwxr-xr-x 2 root wheel 9 12 ago. 16:09 neverbleed/
drwxr-xr-x 2 root wheel 13 12 ago. 16:09 picohttpparser/
drwxr-xr-x 2 root wheel 4 12 ago. 16:09 picotest/
drwxr-xr-x 9 root wheel 16 12 ago. 16:09 picotls/
drwxr-xr-x 4 root wheel 8 12 ago. 16:09 ssl-conservatory/
drwxr-xr-x 8 root wheel 18 12 ago. 16:09 yaml/
drwxr-xr-x 2 root wheel 8 12 ago. 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 configurazione del server web è, in generale, standard.
root@beta:/usr/ports/www/h2o # cd /usr/local/etc/h2o/
root@beta:/usr/local/etc/h2o # cat h2o.conf
# questo esempio di configurazione ti dà un'idea di come può essere utilizzato h2o
# e di una configurazione ad alta sicurezza per TLS e intestazioni HTTP
# vedi https://h2o.examp1e.net/ per documentazione dettagliata
# e h2o --help per le opzioni e impostazioni da riga di comando
# v.20180207 (c)2018 di 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-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES256-SHA384:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA:ECDHE-RSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES128-SHA:DHE-RSA-AES256-SHA256:DHE-RSA-AES256-SHA:ECDHE-ECDSA-DES-CBC3-SHA:ECDHE-RSA-DES-CBC3-SHA:EDH-RSA-DES-CBC3-SHA:AES128-GCM-SHA256:AES256-GCM-SHA384:AES128-SHA256:AES256-SHA256:AES128-SHA:AES256-SHA:DES-CBC3-SHA:!DSS
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.rbL'unica eccezione è il gestore URL /dns-query di cui è responsabile il nostro server DNS-over-HTTPS, scritto in mruby e richiamato tramite l'opzione del gestore mruby.handler-file.
root@beta:/usr/local/etc/h2o # cat h2odoh.rb
# H2O HTTP/2 server web come servizio DNS-over-HTTP
# 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=/,'')
# decodifica base64URL
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' }, [ "Richiesta Non Valida" ]]
else
# --- chiedi al server DNS
sock = UDPSocket.new
sock.connect("localhost", 53)
sock.send(req, 0)
str = sock.recv(4096)
sock.close
# --- trova il TTL più basso nella risposta
nans = str[6, 2].unpack1('n') # numero di risposte
if nans > 0 # nessun errore DNS
shift = 12
ttl = 0
while nans > 0
# processa la compressione del nome di dominio
if str[shift].unpack1("C") 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' }, [ "Tipo di Media Non Supportato" ]]
end
}Si prega di notare che il trattamento dei pacchetti DNS è gestito dal server di caching locale, in questo caso dalla fornitura standard di FreeBSD. In termini di sicurezza, è una soluzione ottimale. Tuttavia, nulla impedisce di sostituire localhost con l'indirizzo di un altro DNS che si prevede di utilizzare.
root@beta:/usr/local/etc/h2o # local-unbound version
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 *:*Rimane da riavviare H2O e vedere cosa è successo.
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
Quindi, verifichiamo i risultati inviando nuovamente una richiesta di prova e osservando il traffico di rete utilizzando l'utility 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: " 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 kernelNell'output si può vedere come la richiesta per la risoluzione dell'indirizzo example.com sia stata ricevuta e elaborata con successo dal server DNS.
Ora è rimasto solo da attivare il nostro server nel browser Firefox. Per fare ciò, è necessario modificare alcune impostazioni nella pagina di configurazione. about:config.

Innanzitutto, questo è l'indirizzo della nostra API da cui il browser richiederà le informazioni DNS a network.trr.uri. È consigliabile anche specificare l'IP del dominio di questo URL per una risoluzione sicura in IP direttamente dal browser, senza fare riferimento al DNS in network.trr.bootstrapAddress. Infine, il parametro stesso network.trr.mode attiva l'uso di DoH. Impostare il valore a "3" forzerà il browser a utilizzare esclusivamente DNS-over-HTTPS per la risoluzione dei nomi, mentre un'opzione più sicura e affidabile "2" darà priorità a DoH, mantenendo la tradizionale interrogazione DNS come opzione di riserva.
5. PROFITTO!
L'articolo è stato utile? Non esitate a supportarci con una donazione tramite il modulo qui sotto.
Fonte: habr.com
