Различни аспекти на експлоатацията на DNS вече многократно са били разглеждани от автора в серия публикации в рамките на блога. При това, основният акцент винаги е бил поставен на повишаване на сигурността на тази ключова услуга за целия интернет.

До последно време, въпреки очевидността на уязвимостта на DNS трафика, който все още в по-голямата си част се предава в открит вид, за злонамерени действия от страна на доставчици, стремящи се да увеличат доходите си чрез вграждане на реклама в съдържанието, държавни органи и цензура, както и просто от престъпници, процесът , въпреки наличието на различни технологии, като DNSSEC/DANE, DNScrypt, DNS-over-TLS и DNS-over-HTTPS, буксуваше. И ако сървърните решения, а някои от тях съществуват вече доста дълго време, са широко известни и достъпни, то поддръжката от страна на клиентски софтуер оставя много да се желае.
За щастие, ситуацията се променя. В частност, разработчиците на популярния браузър Firefox планират включването по подразбиране на режима (DoH) в близко бъдеще. Това трябва да помогне за защита на DNS трафика на потребителите от гореспоменатите заплахи, но потенциално може да предизвика нови.
1. Проблеми с DNS-over-HTTPS
На пръв поглед, започващото масово внедряване на DNS-over-HTTPS в интернет софтуер предизвиква само положителна реакция. Обаче, дяволът, както се казва, е в детайлите.
Първият проблем, който ограничава обхвата на масовото приложение на DoH, е неговата ориентация единствено към уеб трафика. Наистина, протоколът HTTP и неговата актуална версия HTTP/2, върху която се базира DoH, са основата на WWW. Но интернет не е само уеб. Съществуват множество популярни услуги, като електронна поща, различни мессенджъри, системи за предаване на файлове, стрийминг на мултимедия и др., които не използват HTTP. Така че, въпреки че много хора възприемат DoH като панацея, той се оказва неприложим без допълнителни (и непотребни) усилия, освен за браузърни технологии. За тези цели, DNS-over-TLS изглежда като много по-достоен кандидат, тъй като реализира инкапсулация на стандартния DNS трафик в защитен стандартен протокол TLS.
Втората проблема, която потенциално е много по-значима от първата, е фактическият отказ от inherentната децентрализация на DNS по дизайн в полза на използването на единствен DoH сървър, зададен в настройките на браузъра. В частност, Mozilla предлага да се използва услугата на Cloudflare. Такава услуга пуснаха също и други значими фигури в Интернет, включително Google. Получава се, че внедряването на DNS-over-HTTPS в настоящия му вид, само увеличава зависимостта на крайните потребители от най-големите услуги. Не е тайна, че информацията, която може да предостави анализът на DNS запитванията, е способна да събира още повече данни за тях, както и да подобри тяхната точност и актуалност.
В тази връзка, авторът беше и остава привърженик на масовото внедряване не на DNS-over-HTTPS, а на DNS-over-TLS съвместно с DNSSEC/DANE като универсално, безопасно и ненасърчаващо по-нататъшната централизация в Интернет средство за осигуряване на безопасността на DNS трафика. За съжаление, не можем да очакваме бързо внедряване на масова поддръжка на алтернативи на DoH в клиентския софтуер по разбираеми причини, и засега те остават в ръцете на ентусиасти по безопасни технологии.
Но, след като вече получаваме DoH, защо да не го използваме, преди да се откажем от потенциалния надзор от страна на корпорациите чрез техните сървъри, и да преминем на собствен DNS-over-HTTPS сървър?
2. Протокол DNS-over-HTTPS
Ако погледнете стандарта описыващ протокол DNS-over-HTTPS, ще видите, че той по същество представлява уеб API, позволяващ инкапсулирането на стандартния DNS пакет в протокол HTTP/2. Това се реализира чрез специални HTTP заглавия, а също и чрез конверсия на бинарния формат на предаваните DNS данни (виж и следващите документи) в форма, позволяваща предаване и получаване на данни, а също и работа с необходимите метаданни.
Според стандарта се поддържа само HTTP/2 и защитена TLS връзка.
Изпращането на DNS запитване може да се извърши чрез стандартните методи GET и POST. В първия случай запитването се трансформира в base64URL-encoded низ, а във втория — чрез тялото на POST запитването в бинарна форма. При това, при запитването и при отговора на DNS се използва специален MIME тип данни application/dns-message.
root@eprove:~ # curl -H 'accept: application/dns-message' 'https://my.domaint/dns-query?dns=q80BAAABAAAAAAAAB2V4YW1wbGUDY29tAAABAAE' -v
* Опитвам 2001:100:200:300::400:443...
* TCP_NODELAY зададено
* Свързан с eprove.net (2001:100:200:300::400) порт 443 (#0)
* ALPN, предлагам h2
* ALPN, предлагам http/1.1
* успешно зададени местоположения за проверка на сертификати:
* CAfile: /usr/local/share/certs/ca-root-nss.crt
CApath: none
* TLSv1.3 (OUT), TLS ръчна връзка, Клиент приветствие (1):
* TLSv1.3 (IN), TLS ръчна връзка, Сървър приветствие (2):
* TLSv1.3 (IN), TLS ръчна връзка, Криптирани Разширения (8):
* TLSv1.3 (IN), TLS ръчна връзка, Сертификат (11):
* TLSv1.3 (IN), TLS ръчна връзка, CERT проверка (15):
* TLSv1.3 (IN), TLS ръчна връзка, Завърши (20):
* TLSv1.3 (OUT), TLS смяна на шифър, Смяна на спецификация на шифрите (1):
* TLSv1.3 (OUT), TLS ръчна връзка, Завърши (20):
* SSL връзка, използваща TLSv1.3 / TLS_AES_256_GCM_SHA384
* ALPN, сървърът прие да използва h2
* Сертификат на сървъра:
* субект: CN=my.domain
* дата на започване: Jul 22 00:07:13 2019 GMT
* дата на изтичане: Oct 20 00:07:13 2019 GMT
* subjectAltName: хост "my.domain" съвпада с сертификат "my.domain"
* издател: C=US; O=Let's Encrypt; CN=Let's Encrypt Authority X3
* Проверка на SSL сертификат: успешно.
* Използване на HTTP2, сървърът поддържа много използване
* Състоянието на връзката се промени (HTTP/2 потвърден)
* Копиране на HTTP/2 данни в буфера на връзката след обновяване: len=0
* Използване на Стрим ID: 1 (лесно обработване 0x801441000)
> GET /dns-query?dns=q80BAAABAAAAAAAAB2V4YW1wbGUDY29tAAABAAE HTTP/2
> Хост: eprove.net
> User-Agent: curl/7.65.3
> accept: application/dns-message
>
* TLSv1.3 (IN), TLS ръчна връзка, Нов билет за сесия (4):
* Състоянието на връзката се промени (MAX_CONCURRENT_STREAMS == 100)!
< HTTP/2 200
< сървър: h2o/2.3.0-beta2
< тип-съдържание: application/dns-message
< контрол на кеша: max-age=86274
< дата: Thu, 12 Sep 2019 13:07:25 GMT
< стриктна-транспортна-сигурност: max-age=15768000; includeSubDomains; preload
< дължина-съдържание: 45
<
Внимание: Двоичният изход може да развали вашия терминал. Използвайте "--output -", за да уведомите
Внимание: curl да го изведе на вашия терминал все пак, или обмислете "--output
Внимание: " да запазите в файл.
* Неуспешен запис на тяло (0 != 45)
* прекратено е паузата на стрийм!
* Връзка #0 с хост eprove.net остана непокътнатаОбърнете внимание на заглавката контрол на кеша: в отговора от уеб сървъра. В параметъра max-age се съдържа стойността TTL за връщаната DNS запис (или минималната стойност, ако се връща набор от тях).
Следователно, функционирането на DoH сървъра се състои от няколко етапа.
- Получаване на HTTP заявка. Ако е GET, декодирайте пакета от base64URL кодировката.
- Изпратете този пакет на DNS сървъра.
- Получаване на отговора от DNS сървъра
- Намиране на минималната стойност на TTL в получения запис.
- Връщане на отговора на клиента по HTTP.
3. Свой собствен DNS-over-HTTPS сървър
Най-простият, бърз и ефективен начин да стартирате свой собствен DNS-over-HTTPS сървър е да използвате HTTP/2 уеб сървър , за който авторът вече направи кратък преглед (вж. "«).
В полза на този избор играе факта, че целият код на собствения DoH сървър може да бъде напълно реализиран чрез средствата на вграден в H2O интерпретатор . В допълнение към стандартните библиотеки, за обмен на данни с DNS сървър е необходима библиотека (mrbgem) Socket, която, за щастие, вече е включена в текущата разработваща версия H2O 2.3.0-beta2 в портовете на FreeBSD. Както и да е, не е трудно да я добавите и в която и да е предишна версия, клонирайки репозитория в директория /deps преди компилиране.
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
===> Лиценз MIT BSD2CLAUSE приета от потребителя
===> h2o-2.2.6 зависи от файл: /usr/local/sbin/pkg - намерен
===> Изтегляне на всички дистрибуционни файлове, необходими на h2o-2.2.6 за строеж
===> Извличане за h2o-2.2.6.
=> SHA256 Контролна сума OK за h2o-h2o-v2.2.6_GH0.tar.gz.
===> h2o-2.2.6 зависи от файл: /usr/local/bin/ruby26 - намерен
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: Изброяване на обекти: 385, завършено.
remote: Общо 385 (delta 0), повторно използвани 0 (delta 0), пакет повторно използвани 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
...Конфигурацията на уеб сървъра като цяло е стандартна.
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 от Макс Костиков 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-AЕС128-SHA:DHE-RSA-AЕС256-SHA256:DHE-RSA-AЕС256-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 уеб сървър като услуга DNS-over-HTTP
# v.20190908 (c)2018-2019 Макс Костиков https://kostikov.co имейл: 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") < 192
shift = str.index("x00", shift) + 5
if ttl == 0 # пропускане на секцията с въпроси
next
end
end
shift += 6
curttl = str[shift, 4].unpack1('N')
shift += str[shift + 4, 2].unpack1('n') + 6 # размер на отговорните данни
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' }, [ "Неподдържан медиен тип" ]]
end
}Обърнете внимание, че за обработката на DNS пакети отговаря локалния кеширащ сървър, в този случай от стандартната поставка на FreeBSD. От гледна точка на сигурността това е оптимално решение. Все пак, нищо не пречи да замените localhost с адреса на друг DNS, който планирате да използвате.
root@beta:/usr/local/etc/h2o # local-unbound версион
използване: local-unbound [опции]
стартирайте демона unbound DNS резолвер.
-h тази помощ
-c файл конфигурационен файл за четене вместо /var/unbound/unbound.conf
файл форматът е описан в unbound.conf(5).
-d не произвеждайте нов фон.
-p не създавайте pidfile.
-v подробен (повече пъти за увеличаване на подробността)
Версия 1.8.1
свързани библиотеки: mini-event вътрешен (използва select), OpenSSL 1.1.1a-freebsd 20 ноември 2018
свързани модули: dns64 respip валидатор итератор
BSD лиценз, вижте ЛИЦЕНЗА в изходния пакет за детайли.
Докладвайте грешки на 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
Спиране на h2o.
Изчакване на PIDS: 69871.
Стартиране на h2o.
start_server (pid:70532) започва сега...4. Тестване
Така че, нека проверим резултатите, като изпратим отново тестово запитване и наблюдаваме мрежовия трафик с помощта на утилита tcpdump.
root@beta/usr/local/etc/h2o # curl -H 'accept: application/dns-message' 'https://my.domain/dns-query?dns=q80BAAABAAAAAAAAB2V4YW1wbGUDY29tAAABAAE'
Предупреждение: Бинарният изход може да развали терминала ви. Използвайте "--output -", за да кажете
Предупреждение: на curl да изведе информацията в терминала, или помислете за "--output
Предупреждение: " за запис в файл.
...
root@beta:~ # tcpdump -n -i lo0 udp port 53 -xx -XX -vv
tcpdump: слуша на lo0, тип връзка NULL (BSD loopback), размер на улавяне 262144 байта
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: [лош 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: [лош 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 пакета уловени
23 пакета получени от филтъра
0 пакета пропуснати от ядроВ изхода се вижда как запитването за разрешаване на адреса example.com беше получено и успешно обработено от DNS сървъра.
Сега остава да активираме нашия сървър в браузъра Firefox. За целта на страницата с конфигурация трябва да променим няколко настройки about:config.

Първо, това е адресът на нашия API, по който браузърът ще запитва информация в DNS в network.trr.uri. Препоръчва се също да посочите IP адреса на домейна от този URL за безопасно разрешаване в IP средства на самия браузър без да се обръща към DNS в network.trr.bootstrapAddress. И накрая, самият параметър network.trr.mode включващ използването на DoH. Настройката на стойността на "3" ще накара браузъра да използва изцяло DNS-over-HTTPS за разрешаване на имена, докато по-надеждното и безопасно "2" ще отдаде приоритет на DoH, оставяйки стандартното запитване към DNS като резервен вариант.
5. ПЕЧАЛБА!
Статията беше полезна? Тогава моля не се колебайте и подкрепете ни финансово чрез формата за дарение (по-долу).
Източник: habr.com
