Протокол DNS (Domain Name System Protocol) является одним из важнейших инфраструктурных протоколов для поддержки сети Интернет и первоначально он разрабатывался для максимальной производительности и возможности распределенного хранения неограниченного числа доменных зон. DNS может функционировать поверх UDP-протокола и это уменьшает накладные расходы на установку соединения и избыточный трафик в сети. Но одной из важнейших проблем стала безопасность обмена данными, поскольку клиент в первоначальном варианте протокола не может проверить достоверность информации и это может приводить к подмене ip-адресов злоумышленниками с переадресацией клиента на фишинговый сайт.

Для решения этой проблемы были введены расширения DNSSEC для генерации цифровой подписи ответа. Но сам запрос и ответ при этом не шифровались, что могло быть использовано для ограничения доступа к определенным доменам или для получения на транзитных узлах статистики доступа к хостам. Частично эту проблему решило использование инкапсуляций DNS-over-TLS (DoT, использует TLS для шифрования UDP-дэйтаграмм) и DNS-over-HTTPS (DoH, передает запросы и ответы поверх HTTPS-подключения), которые функционируют поверх TCP. В первом случае запрос более компактный (но может быть обнаружен по анализу трафика), во втором DNS-диалог неотличим от подключения к сайту или веб-сервисам, но при этом существенно увеличивает избыточной трафик (даже при использовании HTTP/2), а также вынужденно добавляет заголовки (которые могут использоваться для трекинга и перехвата cookies). Но можно ли как-то объединить преимущества UDP и DoH? Встречаем DNS-over-QUIC, который был утвержден в RFC9250 как Proposed Standard.

Сначала скажем несколько слов о протоколе QUIC (Quick UDP Internet Connections, RFC9000). Первоначально он был разработан Google в 2012 году и использует транспортный протокол UDP для передачи потоков данных с обязательным шифрованием. Протокол существенно уменьшает расходы на установку соединения, поддерживает миграцию соединения (возможность замены ip-адреса и порта при переключении сетей, благодаря наличию идентификатора потока), позволяет мультиплексировать несколько потоков данных, допускает возможность переотправки отдельных поврежденных фрагментов (поскольку шифрование выравнивается по границе фрагмента и данные могут быть расшифрованы даже при частичной потере сообщений). Протокол QUIC лежит в основе утвержденного недавно HTTP/3 (RFC9114). Все браузеры основанные на Chromium поддерживают QUIC (с установленным флагом enable-quic), также Firefox с версии 80.0 (с конфигурацией network.http.http3.enabled).

Протокол DNS-over-QUIC (далее DoQ) использует TLS 1.3 и может использоваться для отправки запросов от клиентов к DNS-серверам, взаимодействия DNS-серверов (в том числе, передачи обновлений зоны и запросов между рекурсивными и авторитативными серверами). DNS-запросы отображаются поверх потоков QUIC и ответ сервера (независимо от объема) полностью возвращается в том же потоке, что и запрос. При этом запросы могут обрабатываться асинхронно, без необходимости ожидания ответов на ранее полученные запросы. Один клиент может отправить несколько запросов через разные потоки (количество ограничивается через механизмы управления соединением со стороны сервера).

Поскольку взаимодействие через QUIC в целом идентично для DNS-запросов и HTTP-сообщений, это позволяет защитить DNS-запросы от возможной пакетной фильтрации на уровне транспортного протокола (дополнительно для этого используется заполнение QUIC-пакетов, чтобы избежать возможного анализа трафика). Также для исключения атаки DNS amplification (вызванной тем, что в UDP-дэйтаграмме может быть подменен адрес клиента на адрес жертвы, которой и будет отправлен ответ) в QUIC предусмотрен механизм валидации адресов. Поскольку в QUIC используется UDP-протокол, то отсутствует фаза установки соединения и ответ может быть получен сразу после согласования протокола (чуть медленнее, чем в случае незащищенного DNS UDP-соединения), но при этом остаются доступными все механизмы защиты и управления сессиями от QUIC (включая возможность продолжения сессии при изменении ip-адреса клиента без прерывания потоков). Сравнение производительности DoQ с другими защищенными типами транспорта можно в этой работе.

Во время установки соединения через QUIC сервер сообщает о поддержке протокола отправкой ALPN-токена doq. По умолчанию сервер должен отвечать на запросы через UDP-порт 853, но может быть настроен на любой порт (например, 443). DNS-запросы всегда отправляются с Message ID = 0, запрос и ответ не могут превышать 65535 (поскольку длина ограничивается 16-битным полем).

Поддержка DNS-over-QUIC сейчас не представлена в стабильных сборках браузеров, но можно использовать dnsproxy для реализации прокси-сервера, который может подключаться с использованием всех протоколов безопасного DNS (DoT, DoH, DoQ и DNSCrypt). Альтернативой DoQ может рассматриваться DoH3 (передача DNS-запросов и ответов поверх HTTP/3 соединения, но она медленнее чем DoQ из-за установки HTTP/3 соединения и имеет недостатки, сходные с DoH — передача заголовков и cookie). При этом нужно отметить, что телефоны на Android 11+ уже поддерживают передачу DNS-запросов через http3 с обновлением Google Play System. Для DoH3 можно использовать серверы cloudflare-dns: https://cloudflare-dns.com/dns-query.  Для настройки DoH3 в Chrome для Android можно перейти в меню, выбрать Settings → Privacy and Security → Use Secure DNS и указать адрес провайдера (например, cloudflare). Проверить используемый протокол для доступа к странице и DNS можно на тестовой странице https://cloudflare-quic.com (срабатывает после перезагрузки страницы).

Пока нет официальной поддержки DoQ, можно установить в локальной сети или на своем компьютере проксирующий DNS-сервер, который будет принимать запросы от приложений и ретранслировать их через DoQ на внешний DNS (например, RouteDNS, doq-proxy или DNSProxy). DNSProxy может быть запущен через Docker-контейнер (например, chenhw2/dnsproxy) или установлен через go install. Наиболее важными опциями для нас будут:

  • -u — указание upstream DNS-серверов (tls://ip для использования DoT, https://host/path для DoH, quic://host для DoQ), может быть перечислено несколько адресов через запятую. Обратите внимание, что при использовании DNS-имен для подключения к внешнему серверу (например, https или DoQ) для успешного начального подключения (bootstrap) необходимо связать имя хоста с ip-адресом любым доступным способом (например, указанием fallback DNS или использованием --add-host при запуске контейнера);

  • -p — порт для прослушивания локальных DNS-запросов (также можно разрешить подключения по DoH (-s), DoT (-t), DoQ (-q) и DNSCrypt (-y). В этом случае необходимо передать сертификат и приватный ключ для использования в TLS или шифровании для прикладных протоколов HTTPS/QUIC);

  • --cache — включить DNS-кэш;

  • --fastest-addr — возвращать наиболее быстрый адрес (если их несколько).

Запустим DNS-прокси. Мы будем использовать AdguardDNS (но можно установить собственный сервер, например CoreDNS, quicdog, unbound или doqd).

docker run --restart=always --name dnsproxy -p 53:53/udp -p 53:53 \
 --add-host dns.adguard.com:94.140.15.15 -d \
 -e "ARGS=-l 0.0.0.0 --cache --fastest-addr -u quic://dns.adguard.com -v" \
 chenhw2/dnsproxy

Теперь мы можем переключиться на использование локального DNS:

  • Windows — netsh interface ipv4 set dnsservers "Wi-Fi" static 127.0.0.1 primary или Set-DnsClientServerAddress -InterfaceAlias "Wi-Fi" -ServerAddresses "127.0.0.1" -Validate для PowerShell (с правами администратора, название интерфейса может быть другим, список можно получить через netsh interface show interface);

  • Linux — sudo systemd-resolve --interface eth0 --set-dns 127.0.0.1 для систем на основе SystemD, либо заменить nameservers 127.0.0.1 в /etc/resolv.conf (название интерфейса может отличаться, можно посмотреть в ip link show);

  • MacOS — sudo networksetup -setdnsservers Wi-Fi 127.0.0.1 (название интерфейса может быть другим, можно посмотреть в sudo networksetup -listallnetworkservices).

В логе контейнера (docker logs dnsproxy) мы можем увидеть запросы и ответы, поступившие от upstream dns-серверов. Попробуем сделать запрос адреса: nslookup habr.ru и обнаружим успешное обращение прокси к upstream-серверу:

2022/07/24 11:59:59 1#5561 [debug] github.com/AdguardTeam/dnsproxy/proxy.(*Proxy).udpHandlePacket(): Start handling new UDP packet from 172.17.0.1:48889
2022/07/24 11:59:59 1#5561 [debug] github.com/AdguardTeam/dnsproxy/proxy.(*Proxy).logDNSMessage(): IN: ;; opcode: QUERY, status: NOERROR, id: 2
;; flags: rd; QUERY: 1, ANSWER: 0, AUTHORITY: 0, ADDITIONAL: 0

;; QUESTION SECTION:
;habr.ru.       IN       A

2022/07/24 11:59:59 1#5562 [debug] github.com/AdguardTeam/dnsproxy/upstream.(*bootstrapper).createDialContext.func1(): Dialing to 94.140.15.15:853
2022/07/24 11:59:59 1#5562 [debug] github.com/AdguardTeam/dnsproxy/upstream.(*bootstrapper).createDialContext.func1(): dialer has successfully initialized connection to 94.140.15.15:853 in 51.3µs
2022/07/24 11:59:59 1#5561 [debug] github.com/AdguardTeam/dnsproxy/proxy.(*Proxy).replyFromUpstream(): RTT: 25.3216ms
2022/07/24 11:59:59 1#5561 [debug] github.com/AdguardTeam/dnsproxy/proxy.(*Proxy).logDNSMessage(): OUT: ;; opcode: QUERY, status: NOERROR, id: 2
;; flags: qr rd ra; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 0

;; QUESTION SECTION:
;habr.ru.       IN       A

;; ANSWER SECTION:
habr.ru.        3600    IN      A       178.248.233.33

Переход на использование DoQ в целом уменьшает время выполнения DNS-запросов, обеспечивает защиту содержания запроса от перехвата и анализа на промежуточном оборудовании и позволяет избежать возможной подмены адресов в ответе. Кроме того, DoQ (и DoH3) успешно функционируют в условиях переключения сетей (например, между различными Wi-Fi сетями или между Wi-Fi и сотовой связью), что особенно важно для мобильных клиентов.

Подробный список публичных серверов с поддержкой DoQ, DoH3, информацию о поддержке протоколов браузерами, клиентских и серверных реализаций защищенного DNS можно найти на этой странице.


Сегодня вечером в OTUS состоится открытое занятие «IS-IS. Фильтрация LSP-пакетов», на котором участники:

  • Сравнят виды и возможности фильтрации в протоколе;

  • Настроят фильтрацию как в L1, так и в L2 взаимодействии между устройствами;

  • Определят плюсы и минусы каждого из вариантов.

Регистрируйтесь по ссылке.

Комментарии (6)


  1. dartraiden
    25.07.2022 17:10
    +2

    Это здорово, но сперва хорошо бы отменить блокировку QUIC на зарубежные направления, чтобы пользователи могли использовать DNS over QUIC с зарубежными DNS-провайдерами.


  1. nickolas059
    25.07.2022 20:43

    Что любопытно, в моем Oppo android 11 адрес надо вбивать one.one.one.one , а цифрами не даёт. Почему?


    1. equeim
      26.07.2022 10:54

      Эта настройка в Андроиде поддерживает только шифрованные протоколы. Вероятно, Гугл решил что никто из днс провайдеров не будет в здравом уме использовать tls сертификаты привязанные к ip адресу.


      1. Komei
        27.07.2022 00:44

        никто из днс провайдеров не будет в здравом уме использовать tls сертификаты привязанные к ip адресу.

        А можно поинтересоваться: собственно почему? В чём минус таких сертификатов? Особенно в плане DNS? Ведь до DNS нам доступны ТОЛЬКО ip адреса.


        1. equeim
          27.07.2022 01:07

          Привязка сертификата к доменному имени позволяет менять ip адрес сервера как угодно, и соответственно переносить сервер без ограничений. Прибитие сертификата гвоздями к ip соответственно это ограничивает. Плюс из-за того что такие сертификаты используются редко повышаются шансы встречи с багами в реализациях TLS. Например, насколько я знаю, винда до 10 версии их обрабатывала неправильно.

          Хотя конечно странно что нет такой возможности в контексте DNS. systemd-resolved, например, требует указывания как ip адреса так и доменного имени для верификации сертификата (или только ip если сертификат без домена).

          Андроид, вероятно, просто резолвит адрес dns сервера через публичный dns гугла с захардкоженным в систему ip и доменом.


  1. mvv-rus
    26.07.2022 05:49
    +1

    DNSSEC для генерации цифровой подписи ответа. Но сам запрос и ответ при этом не шифровались, что могло быть использовано для ограничения доступа к определенным доменам

    Не совсем так. По крайней мере, DNSSEC не позволяет подавить запрос к существующей в зоне записи незаметно: отрицательные ответы в нем тоже подтвердаются подписью через подписанные записи NSEC3 (или NSEC) в зоне.