Različni aspekti uporabe DNS su već nekoliko puta spomenuti od strane autora u nizu objavljenih u okviru bloga. Pri tome se glavni naglasak uvijek stavljao na povećanje sigurnosti ove ključne usluge za cijeli Internet.

Do nedavnog vremena, unatoč očiglednoj ranjivosti DNS prometa, koji se do danas većinom prenosi u otvorenom obliku, radi zlonamjernih djelovanja s strane pružatelja usluga koji teže povećanju svojih prihoda integracijom oglašavanja u sadržaj, državnih organa i cenzure, kao i jednostavno kriminalaca, proces , unatoč postojanju različitih tehnologija poput DNSSEC/DANE, DNScrypt, DNS-over-TLS i DNS-over-HTTPS, stagnira. Iako su serverna rješenja, od kojih neka postoje već duže vrijeme, široko poznata i dostupna, podrška od strane klijentskog softvera ostavlja mnogo toga za željeti.
Na sreću, situacija se mijenja. Konkretno, programeri popularnog preglednika Firefox najavili su planove za uključivanje podrške za (DoH) kao zadane opcije u skoroj budućnosti. To bi trebalo pomoći u zaštiti DNS prometa korisnika WWW od navedenih prijetnji, međutim, potencijalno može uzrokovati nove.
1. Problemi s DNS-over-HTTPS
Na prvi pogled, masovno uvođenje DNS-over-HTTPS u softver koji radi na Internetu izaziva samo pozitivne reakcije. Međutim, 'đavo', kako se kaže, krije se u detaljima.
Prvi problem koji ograničava područje masovne primjene DoH je njegova orijentacija isključivo na web promet. Uistinu, HTTP protokol i njegova aktualna verzija HTTP/2, na kojoj se temelji DoH, osnova su WWW. No Internet nije samo web. Postoji mnogo popularnih usluga, kao što su elektronička pošta, razni messengeri, sustavi prijenosa datoteka, streaming multimedije i slično, koji ne koriste HTTP. Stoga, unatoč tome što mnogi doživljavaju DoH kao panaceju, on se pokazuje neprikladnim bez dodatnih (niti potrebnih) napora, za išta drugo osim tehnologija preglednika. Usput, za ovu ulogu, mnogo prikladniji kandidat je DNS-over-TLS, koji implementira enkapsulaciju standardnog DNS prometa u zaštićeni standardni protokol TLS.
Druga težava, ki je potencialno veliko pomembnejša od prve, je dejanski umik od zasnovane decentralizacije DNS v korist uporabe enega DoH strežnika, ki se določi v nastavitvah brskalnika. Konkretno, Mozilla ponuja storitev podjetja Cloudflare. Podobne storitve so uvedli tudi drugi znani akterji v spletu, vključno z Googlom. Tako se zdi, da uvedba DNS-over-HTTPS v sedanji obliki le povečuje odvisnost končnih uporabnikov od največjih storitev. Ni skrivnost, da informacije, ki jih lahko zagotovi analiza DNS poizvedb, lahko zberejo še več podatkov o uporabniku ter povečajo njihovo natančnost in aktualnost.
V tem smislu je avtor bil in ostaja zagovornik množične uvedbe ne DNS-over-HTTPS, temveč DNS-over-TLS skupaj z DNSSEC/DANE kot univerzalnega, varnega in neokusnega sredstva za zagotavljanje varnosti DNS prometa, ki ne spodbuja nadaljnje centralizacije interneta. Na žalost ni pričakovati hitre uvedbe množične podpore alternativnim rešitvam DoH v odjemalski programski opremi, kar je razumljivo, saj je trenutno njihova usoda v rokah navdušencev za varne tehnologije.
Toda ker zdaj že prejemamo DoH, zakaj ga ne bi uporabili, preden bi se izognili potencialnemu nadzoru korporacij prek njihovih strežnikov na svoj lastni DNS-over-HTTPS strežnik?
2. Protokol DNS-over-HTTPS
Če pogledamo v standard ki opisuje protokol DNS-over-HTTPS, lahko vidimo, da v osnovi predstavlja spletno API, ki omogoča encapsulacijo standardne DNS pakete v protokol HTTP/2. To se uresničuje preko posebnih HTTP-headerjev ter konverzije binarnega formata prenesenih DNS podatkov (glej in naslednje dokumente) v obliko, ki omogoča prenos in prejemanje teh podatkov, pa tudi delo z ustreznimi metapodatki.
Po standardu je podprta samo HTTP/2 in zaščitena povezava TLS.
Poslati DNS poizvedbo je mogoče z uporabo standardnih metod GET in POST. V prvem primeru se poizvedba preoblikuje v base64URL-encoded niz, v drugem pa preko telesa POST poizvedbe v binarni obliki. Pri tem se pri poizvedbi in odgovoru DNS uporablja poseben MIME-tip podatkov 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 intactОбратите также внимание на заголовок cache-control: в ответе со стороны веб-сервера. В параметре max-age содержится значение TTL для возвращаемой записи DNS (или минимальное значение если возвращается их набор).
Исходя из вышеизложенного, функционирование DoH сервера состоит из нескольких этапов.
- Получить HTTP запрос. Если это GET то декодировать пакет из base64URL кодировки.
- Отправить этот пакет DNS серверу.
- Получить ответ от DNS сервера
- Найти минимальное значение TTL в полученных записях.
- Вернуть клиенту ответ по HTTP.
3. Свой DNS-over-HTTPS сервер
Наиболее простым, быстрым и эффективным способом запустить свой собственный DNS-over-HTTPS сервер представляется использование HTTP/2 веб-сервера , о котором автор уже вкратце писал (см. ««).
В пользу этого выбора играет тот факт, что весь код собственного DoH сервра может быть полностью реализован средствами интегрированного в сам H2O интерпретатором . Poleg standardnih knjižnic je za izmenjavo podatkov z DNS strežnikom potrebna knjižnica (mrbgem) Socket, ki je, na srečo, že vključena v trenutno razvojno različico H2O 2.3.0-beta2. v pristaniščih FreeBSD. Vendar pa jo ni težko dodati tudi v katero koli prejšnjo različico s kloniranjem repozitorija. v imeniku /deps pred prevajanjem.
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
===> Licenca MIT BSD2CLAUSE je bila sprejeta s strani uporabnika
===> h2o-2.2.6 je odvisen od datoteke: /usr/local/sbin/pkg - najdeno
===> Pridobivanje vseh potrebnih datotek za h2o-2.2.6 za gradnjo
===> Ekstrakcija za h2o-2.2.6.
=> SHA256 Preverjanje seštevanja OK za h2o-h2o-v2.2.6_GH0.tar.gz.
===> h2o-2.2.6 je odvisen od datoteke: /usr/local/bin/ruby26 - najdeno
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
Kloniranje v „mruby-socket“…
remote: Številčenje predmetov: 385, končano.
remote: Skupaj 385 (delta 0), ponovno uporabljeno 0 (delta 0), pakirano-reuporabljeno 385
Pridobivanje predmetov: 100% (385/385), 98.02 KiB | 647.00 KiB/s, pripravljeno.
Določitev sprememb: 100% (208/208), pripravljeno.
root@beta:/usr/ports/www/h2o/work/h2o-2.2.6/deps # ll
total 181
drwxr-xr-x 9 root wheel 18 12 avg. 16:09 brotli/
drwxr-xr-x 2 root wheel 4 12 avg. 16:09 cloexec/
drwxr-xr-x 2 root wheel 5 12 avg. 16:09 golombset/
drwxr-xr-x 4 root wheel 35 12 avg. 16:09 klib/
drwxr-xr-x 2 root wheel 5 12 avg. 16:09 libgkc/
drwxr-xr-x 4 root wheel 26 12 avg. 16:09 libyrmcds/
drwxr-xr-x 13 root wheel 32 12 avg. 16:09 mruby/
drwxr-xr-x 5 root wheel 11 12 avg. 16:09 mruby-digest/
drwxr-xr-x 5 root wheel 10 12 avg. 16:09 mruby-dir/
drwxr-xr-x 5 root wheel 10 12 avg. 16:09 mruby-env/
drwxr-xr-x 4 root wheel 9 12 avg. 16:09 mruby-errno/
drwxr-xr-x 5 root wheel 14 12 avg. 16:09 mruby-file-stat/
drwxr-xr-x 5 root wheel 10 12 avg. 16:09 mruby-iijson/
drwxr-xr-x 5 root wheel 11 12 avg. 16:09 mruby-input-stream/
drwxr-xr-x 6 root wheel 11 12 avg. 16:09 mruby-io/
drwxr-xr-x 5 root wheel 10 12 avg. 16:09 mruby-onig-regexp/
drwxr-xr-x 4 root wheel 10 12 avg. 16:09 mruby-pack/
drwxr-xr-x 5 root wheel 10 12 avg. 16:09 mruby-require/
drwxr-xr-x 6 root wheel 10 12 sep. 16:10 mruby-socket/
drwxr-xr-x 2 root wheel 9 12 avg. 16:09 neverbleed/
drwxr-xr-x 2 root wheel 13 12 avg. 16:09 picohttpparser/
drwxr-xr-x 2 root wheel 4 12 avg. 16:09 picotest/
drwxr-xr-x 9 root wheel 16 12 avg. 16:09 picotls/
drwxr-xr-x 4 root wheel 8 12 avg. 16:09 ssl-conservatory/
drwxr-xr-x 8 root wheel 18 12 avg. 16:09 yaml/
drwxr-xr-x 2 root wheel 8 12 avg. 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
...Konfiguracija spletnega strežnika je na splošno standardna.
root@beta:/usr/ports/www/h2o # cd /usr/local/etc/h2o/
root@beta:/usr/local/etc/h2o # cat h2o.conf
# этот пример конфигурации позволяет вам понять, как h2o может быть использован
# и представляет высокозащищённую конфигурацию для TLS и HTTP заголовков
# см. https://h2o.examp1e.net/ для подробной документации
# и h2o --help для параметров командной строки и настроек
# 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-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.rbИсключение составляет лишь обработчик URL /dns-query за который отвечает, собственно, наш DNS-over-HTTPS сервер, написанный на mruby и вызываемый через опцию обработчика mruby.handler-file.
root@beta:/usr/local/etc/h2o # cat h2odoh.rb
# H2O HTTP/2 web сервер как служба 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=/,'')
# декодировать 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' }, [ "Неверный запрос" ]]
else
# --- запрашиваем DNS сервер
sock = UDPSocket.new
sock.connect("localhost", 53)
sock.send(req, 0)
str = sock.recv(4096)
sock.close
# --- находим минимальное время жизни (TTL) в ответе
nans = str[6, 2].unpack1('n') # количество ответов
if nans > 0 # нет ошибки DNS
shift = 12
ttl = 0
while nans > 0
# обрабатываем сжатие доменных имен
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' }, [ "Неподдерживаемый тип медиа" ]]
end
}Обратите внимание, что за обработку пакетов DNS отвечает локальный кэширующий сервер, в данном случае из стандартной поставки FreeBSD. С точки зрения безопасности это оптимальное решение. Впрочем, ничто не мешает заменить localhost на адрес другого DNS, который вы предполагаете использовать.
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 *:*Остаётся перезапустить H2O и посмотреть, что же из этого получилось.
root@beta:/usr/local/etc/h2o # service h2o restart
Stopping h2o.
Waiting for PIDS: 69871.
Starting h2o.
start_server (pid:70532) starting now...4. Testiranje
Torej, preverimo rezultate, tako da ponovno pošljemo poskusno zahtevo in si ogledamo omrežni promet s pomočjo orodja 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 kernelV izvodu je vidno, kako je bila zahteva za razrešitev naslova example.com prejeta in uspešno obdelana s strani DNS strežnika.
Zdaj je treba aktivirati naš strežnik v brskalniku Firefox. Zato bomo v stran nastavitve morali spremeniti nekaj nastavitev about:config.

Najprej je to naslov našega API-ja, na katerega bo brskalnik zahteval informacije o DNS v network.trr.uri. Priporočljivo je tudi navesti IP domene iz tega URL-ja za varno razreševanje v IP brez posredovanja DNS v network.trr.bootstrapAddress. In na koncu, lastni parameter network.trr.mode ki omogoča uporabo DoH. Nastavitev vrednosti na '3' bo prisilila brskalnik, da bo izključno uporabljal DNS-over-HTTPS za razreševanje imen, medtem ko bo bolj zanesljiva in varna '2' dala prednost DoH in pustila standardno obravnavo DNS kot rezervno možnost.
5. DOBITKI!
Je bil članek koristen? Potem prosim ne oklevajte in podprite z denarjem preko obrazca za donacije (spodaj).
Izvor: habr.com
