Как DNS работает через TLS: DNS-over-TLS на практике

от автора

Данные в запросах и ответах классической DNS никак не защищены, передаются в открытом виде. DNS-over-TLS (DoT, RFC 7858) предоставляет один из инструментов защиты информации, а именно: защиту DNS-запросов и DNS-ответов от прослушивания на промежуточных узлах.

DoT использует TLS для зашифрования DNS-транзакций, передаваемых между узлами, но не защищает сами DNS-данные. Под «DNS-данными» тут подразумевается состав ответов и запросов DNS: имена и записи. То есть, если ваш локальный компьютер использует DoT для работы с DNS-сервером, то передаваемые DNS-данные не видны «на транзите» (как объясняется ниже, сам факт обмена DNS-данными обычно всё равно виден) и нельзя простым способом узнать, для какого узла и какие DNS-запросы направлялись. Однако, если DNS-сервер, к которому запросы отправлялись с защитой DoT, возвращает неверные, искажённые DNS-данные, — например, подставляет собственный IP-адрес для, условно, habr.com, — то DoT ничего с этим поделать не может — тут уже нужно использовать DNSSEC.

Логика DoT и пакеты

Логика DoT и пакеты

На всякий случай замечу: DoT — это технология, которая никак не связана с DNS-over-HTTPS (DoH). Архитектура DoT довольно логичная: TLS здесь используется в качестве инструмента создания защищённого «сокета» между DNS-клиентом и DNS-сервером. Через «сокет» передаются те же DNS-запросы/DNS-ответы, как они передавались бы на уровне обычного UDP или TCP. То есть, «сокет» с TLS тут нужно считать туннелем, работа которого прозрачна для уровня DNS. Естественно, чтобы использовать DoT в таком прозрачном режиме, нужна поддержка TLS и на клиенте, и не сервере. Однако, поскольку спецификация отводит TLS обособленный уровень (в отличие от DoH), ничто не мешает поднять TLS-соединение между узлами какими-то другим способом, установив тем самым туннель, а запросы/ответы от неподдерживающих TLS клиентов/серверов перенаправлять без изменений через этот туннель.

Вспомним самые базовые свойства DNS как сервиса поиска данных. Эти свойства важны для понимания места DoT в уже построенной инфраструктуре. DNS — сложная система, работающая по сложным и, что называется, «развесистым» протоколам (простой эта система только кажется; в чём, надеюсь, можно убедиться даже по результатам чтения данной небольшой статьи). Внедрение TLS в DNS заметно усложняет архитектуру, но конкретно DoT позволяет внести изменения почти оптимальным образом. Типовой сценарий подразумевает так называемый рекурсивный опрос, в котором специальный DNS-сервер (резолвер) производит обход других DNS-серверов (которые называются «авторитативными»), следуя по веткам дерева делегирования с целью поиска данных, соответствующих некоторому ключу. Хрестоматийный пример: в качестве ключа выступает имя хоста (example.com), а целевыми данными является IP-адрес (значение адресной A-записи), то есть, «хотим найти IP-адрес для имени сервера».

Используется много запросов, каждый отрезок может быть защищён DoT.

Рекурсивный DNS-резолвер, если подходящий ответ отсутствует в кеше, обращается к разным авторитативным серверам Интернета по некоторому довольно сложному алгоритму и, при штатной работе системы, либо получает от сервера нужный ответ о целевом имени, либо получает так называемый делегирующий ответ, который содержит имена других авторитативных серверов и позволяет резолверу продолжить поиск, обращаясь уже к каким-то из этих серверов (откуда и появляется «рекурсия» в названии процесса). При этом рекурсивный резолвер обычно находится за пределами «локальной машины» — в качестве рекурсивного резолвера может выступать сервер провайдера интернет-доступа или публичный сервис типа Google Public DNS 8.8.8.8. На локальной машине DNS-запросы обрабатывает более простая программа — stub-резолвер, который только перенаправляет запросы рекурсивному резолверу и принимает от него ответы. DoT может использоваться на любом участке работы DNS, а не только на «последней миле», то есть на отрезке от локального stub-резолвера к рекурсивному резолверу. (Кстати, эта «последняя миля» в современных браузерах как раз часто защищена при помощи DNS-over-HTTPS.)

Итак, общая логика работы DoT следующая:

  1. произвольный DNS-клиент, если он поддерживает TLS, устанавливает TLS-соединение, подключившись по TCP к DNS-серверу (для UDP есть отдельный вариант — DNS-over-DTLS, здесь можно считать, что он работает так же, как и DoT);

  2. DNS-клиент может провести аутентификацию сервера различными способами (см. ниже);

  3. если TLS-соединение установлено, то DNS-клиент переходит к отправке DNS-запроса обычным способом, но уже через TLS-соединение;

  4. ответ DNS-сервера доставляется в рамках того же TLS-соединения, но формат ответа и прочие свойства — не изменяются, если сравнивать с DNS, работающей по UDP или TCP (см. ниже);

  5. TLS-соединение закрывается, если клиент не планирует использовать его далее.

Для DoT выделен номер порта 853 (классический DNS — номер порта 53). Поэтому клиенты могут сразу пробовать использовать DoT, подключаясь по TCP на номере порта 853. Так как DoT — универсальная спецификация, то такое подключение можно устанавливать и к авторитативным серверам. Но сейчас поддержка DoT на авторитативных серверах является большой редкостью. Тем не менее, DoT поддерживают, например, серверы DNS-зон facebook.com и wikimedia.org.

Практика

Воспользуемся утилитой dig из пакета BIND и посмотрим, как DoT работает вживую на авторитативных серверах. В качестве источника примеров используем зону wikimedia.org, авторитативные серверы имён которой поддерживают DoT.

Утилита dig — это один из типовых инструментов из области DNS. В более или менее современной версии dig умеет в DoT сразу из коробки: нужно просто указывать опцию +tls при вызове. Я здесь использую версию 9.18.33-1 под Raspberry Pi OS (Debian 12).

Сначала определим, на какие серверы делегирована зона wikimedia.org (пока что без DoT), это делается запросом NS-записей:

$ dig -t NS +short wikimedia.org ns1.wikimedia.org. ns2.wikimedia.org. ns0.wikimedia.org.

(+short здесь — это краткий формат вывода.)

Наша цель — отправить запрос AAAA-записи (IPv6-адрес) через DoT и получить ответ, посмотрев, с помощью tshark, что происходит в трафике. Кроме того, мы извлечём серверный TLS-сертификат с авторитативного сервера и глянем, что там, в сертификате, написано. Будем использовать авторитативный сервер ns0.wikimedia.org. Однако сначала определим его IPv4-адрес, чтобы DNS-запросы отправлять непосредственно серверу.

$ dig -t A +short ns0.wikimedia.org. 208.80.154.238

Запросили A-запись — получили IPv4-адрес в ответ. Теперь включаем DoT (и тут вообще будет опций побольше, но все они объяснены ниже):

$  dig -t AAAA @208.80.153.231 +tls +nocookie +norec wikimedia.org  ; <<>> DiG 9.18.33-1~deb12u2-Debian <<>> -t AAAA @208.80.153.231 +tls +nocookie +norec wikimedia.org ; (1 server found) ;; global options: +cmd ;; Got answer: ;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 44438 ;; flags: qr aa; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 1  ;; OPT PSEUDOSECTION: ; EDNS: version: 0, flags:; udp: 1232 ; TCP KEEPALIVE: 157.0 secs ; PAD: (388 bytes) ;; QUESTION SECTION: ;wikimedia.org.INAAAA  ;; ANSWER SECTION: wikimedia.org.300INAAAA2a02:ec80:300:ed1a::1  ;; Query time: 300 msec ;; SERVER: 208.80.153.231#853(208.80.153.231) (TLS) ;; WHEN: Sun Mar 16 18:18:01 MSK 2025 ;; MSG SIZE  rcvd: 468

(Опции: +tls — используем DoT, +nocookie — без куки-меток, чтобы не перегружать вывод (DNS-куки не относятся к DoT и здесь не рассматриваются, про них можно отдельную статью опубликовать), +norec — не устанавливаем флаг рекурсии.)

Здесь уже в нижнем информационном блоке видно, что запрос и ответ передавались с использованием TLS, через TCP-соединение на номере порта 853. То есть, это DoT.
Дополнительно подтвердить, что трафик ходил через TLS, можно при помощи tshark, посмотрев в дамп, записанный tcpdump.

tshark -r pcap-dns.pcap -o tls.keylog_file:keylog.log -O tls,dns -S "-----PACKET-----" -x

(Здесь -o tls.keylog_file:keylog.log — это импорт сессионных ключей TLS, они потребуются для раскрытия DNS-трафика. Про способ экспорта кратко рассказано ниже.)

В дампе находим примерно следующее:

Internet Protocol Version 4, Src: 192.168.1.13, Dst: 208.80.153.231 Transmission Control Protocol, Src Port: 36461, Dst Port: 853, Seq: 1, Ack: 1, Len: 303 Transport Layer Security     TLSv1 Record Layer: Handshake Protocol: Client Hello         Content Type: Handshake (22)         Version: TLS 1.0 (0x0301)         Length: 298         Handshake Protocol: Client Hello             Handshake Type: Client Hello (1)             Length: 294             Version: TLS 1.2 (0x0303)

Это часть сообщения ClientHello, направленного утилитой dig авторитативному DNS-серверу. Соответствующее серверное сообщение ServerHello подтверждает, что узлы установили соединение TLS 1.3. Внутри TLS-соединения передан DNS-запрос и получен DNS-ответ. Чтобы посмотреть трафик внутри TLS, нужно экспортировать сессионные ключи. Конкретный способ экспорта зависит от параметров сборки dig и системного окружения, но обычно достаточно установить переменную окружения SSLKEYLOGFILE, чтобы dig, при использовании TLS, выводила ключи в файл или в STDERR (откуда их можно точно так же скопировать). Механика экспорта TLS-ключей не относится к теме данной статьи, поэтому детали остаются за скобками. А вот выдачу tshark для расшифрованного DNS-трафика — посмотрим. Запрос:

Transport Layer Security     TLSv1.3 Record Layer: Application Data Protocol: Domain Name System         Opaque Type: Application Data (23)         Version: TLS 1.2 (0x0303)         Length: 61         [Content Type: Application Data (23)]         Encrypted Application Data: [...]         [Application Data Protocol: Domain Name System] Domain Name System (query)     Length: 42     Transaction ID: 0xad96     Flags: 0x0020 Standard query         0... .... .... .... = Response: Message is a query         .000 0... .... .... = Opcode: Standard query (0)         .... ..0. .... .... = Truncated: Message is not truncated         .... ...0 .... .... = Recursion desired: Don't do query recursively         .... .... .0.. .... = Z: reserved (0)         .... .... ..1. .... = AD bit: Set         .... .... ...0 .... = Non-authenticated data: Unacceptable     Questions: 1     Answer RRs: 0     Authority RRs: 0     Additional RRs: 1     Queries         wikimedia.org: type AAAA, class IN             Name: wikimedia.org             [Name Length: 13]             [Label Count: 2]             Type: AAAA (IPv6 Address) (28)             Class: IN (0x0001) [...]

Здесь и далее часть данных (опции EDNS и пр.) удалена, чтобы не слишком растягивать распечатку. В блоке Flags можно видеть параметры запроса, как его сформировала утилита dig. А в блоке Queries (сам запрос) — имя wikimedia.org. и состав запроса IN AAAA. Обратите внимание на поле [Application Data Protocol: Domain Name System]. Вообще, то, что TLS-сессия используется для DoT, видно не только по номеру порта, но и по идентификатору протокола уровня приложений, который dig передаёт в составе начального TLS-сообщения ClientHello. Выглядит это вот так:

 Extension: application_layer_protocol_negotiation (len=6)   Type: application_layer_protocol_negotiation (16)    Length: 6     ALPN Extension Length: 4     ALPN Protocol      ALPN string length: 3      ALPN Next Protocol: dot

ALPN — это Application Layer Protocol Negotiation, расширение ClientHello, которое позволяет сразу сообщить серверу, какой именно прикладной протокол будет использован поверх этого соединения. Для DoT зарезервирован идентификатор 0x646F74 (то есть, «dot» в ASCII). Расширение передаётся в открытом виде, поэтому системе, инспектирующей трафик, нетрудно классифицировать TLS-сессию как сессию DNS: номер порта и идентификатор протокола — этого вполне достаточно. Понятно, что ни TLS, ни DoT и не ставят своей целью сокрытие факта соединения.

Просматривая выдачу tshark далее, находим DNS-ответ:

Transport Layer Security     TLSv1.3 Record Layer: Application Data Protocol: Domain Name System         Opaque Type: Application Data (23)         Version: TLS 1.2 (0x0303)         Length: 487         [Content Type: Application Data (23)]         Encrypted Application Data: [...]         [Application Data Protocol: Domain Name System] Domain Name System (response)     Length: 468     Transaction ID: 0xad96     Flags: 0x8400 Standard query response, No error         1... .... .... .... = Response: Message is a response         .000 0... .... .... = Opcode: Standard query (0)         .... .1.. .... .... = Authoritative: Server is an authority for domain         .... ..0. .... .... = Truncated: Message is not truncated         .... ...0 .... .... = Recursion desired: Don't do query recursively         .... .... 0... .... = Recursion available: Server can't do recursive queries         .... .... .0.. .... = Z: reserved (0)         .... .... ..0. .... = Answer authenticated: Answer/authority portion was not authenticated by the server         .... .... ...0 .... = Non-authenticated data: Unacceptable         .... .... .... 0000 = Reply code: No error (0)     Questions: 1     Answer RRs: 1     Authority RRs: 0     Additional RRs: 1     Queries         wikimedia.org: type AAAA, class IN             Name: wikimedia.org             [Name Length: 13]             [Label Count: 2]             Type: AAAA (IPv6 Address) (28)             Class: IN (0x0001)     Answers         wikimedia.org: type AAAA, class IN, addr 2a02:ec80:300:ed1a::1             Name: wikimedia.org             Type: AAAA (IPv6 Address) (28)             Class: IN (0x0001)             Time to live: 300 (5 minutes)             Data length: 16             AAAA Address: 2a02:ec80:300:ed1a::1     Additional records         <Root>: type OPT             Name: <Root>             Type: OPT (41) [...]             Option: EDNS TCP Keepalive                 Option Code: EDNS TCP Keepalive (11)                 Option Length: 2                 Option Data: 0622                 Timeout: 1570             Option: PADDING                 Option Code: PADDING (12)                 Option Length: 388                 Option Data: 00…                 Padding: 00… 

Это исходник DNS-ответа, который был отображён dig (см. распечатку выше). Обратите внимание на секцию «опций», вот её копия из выдачи dig:

;; OPT PSEUDOSECTION: ; EDNS: version: 0, flags:; udp: 1232 ; TCP KEEPALIVE: 157.0 secs ; PAD: (388 bytes)

Здесь мы, например, без труда видим, что параметр TCP Keepalive имеет значение 157 секунд, как и в распечатке tshark, а дополнение (padding) совпадает по длине. Как нетрудно догадаться по названию, TCP Keepalive содержит указание на интервал времени, в течение которого сервер (в данном случае) готов поддерживать контекст TCP-соединения, чтобы клиент мог реализовать конвейеризацию запросов. К DoT это относится весьма косвенно, но зато служит прекрасным примером реальной сложности современных DNS-протоколов (про Keepalive в DNS тоже можно написать отдельную статью, как ни странно).

А вот дополнение (padding) к DoT относится в большей степени, чем Keepalive, хоть и так же находится за пределами конкретно DoT. Идея использования дополнения здесь в том, чтобы можно было изменять длину блоков данных, содержащих DNS-транзакции, не влияя на сами транзакции. Если длину блоков выравнивать, то это позволяет сделать семантически разные блоки неразличимыми по длине. Что, кстати, особенно важно, если блоки зашифрованы. Проще говоря, наблюдая трафик, состоящий из разных по длине блоков данных, где длина определяется форматом, можно выстроить корреляцию с конкретными DNS-запросами и ответами по порядку их следования. Ведь параметры известны: длина доменного имени, длина «флагов» и заголовков, количество DNS-записей в ответах и т.д. Это позволит сделать предположения о составе наблюдаемого трафика, даже без раскрытия самого его содержания. Если же вы наблюдаете только ровный конвейер блоков одинаковой длины, в стиле «запрос-ответ, всё в одинаковых коробках», то извлечь дополнительную информацию гораздо сложнее. Естественно, можно при помощи дополнения варьировать длину и так, чтобы, напротив, блоки не были одинаковыми по количеству байтов. Заметьте, что аналогичный механизм выравнивания длины записей с помощью дополнения есть и в TLS 1.3.

TLS-сессия использует серверный сертификат. Для авторитативного сервера ns0.wikimedia.org. TLS-сертификат можно получить из дампа трафика (но, так как там TLS 1.3, придётся расшифровать отдельно), а можно воспользоваться утилитой s_client из OpenSSL: TLS в DoT точно такой же, как и в других случаях, так что s_client сработает прекрасно, нужно лишь подключиться по номеру порта 853, вот так:

openssl s_client -connect 208.80.153.231:853

Cертификаты в TLS нужны для аутентификации узлов, то есть для установления их подлинности. Однако в DoT с сертификатами связаны свои особенности. Во-первых, спецификация не указывает методов аутентификации, которые обязательно должны использоваться. Сам сертификат далеко не всегда полезен для DNS: например, можно ли требовать, чтобы имя авторитативного сервера соответствовало имени в сертификате (как это делается для веба)? В DNS сложно строго сопоставить имена серверов: конкретный контекст DNS-запроса никакого имени сервера не предусматривает — запрос отправляется по IP-адресу. Да, можно наследовать имя из предыдущих запросов и ответов, но получится не самый строгий результат. Поэтому имя из сертификата можно использовать, а можно и игнорировать. Зато TLS-сертификаты для IP-адресов подошли бы неплохо. Во-вторых, в DoT можно применять дополнительные методы аутентификации, которые не связаны с привычной по вебу инфраструктурой УЦ: например, проверять отпечаток серверного ключа подписи, а не сверять подписи в сертификатах. В-третьих, DoT использует «приспособительный» подход и к аутентификации, и к TLS в целом. Это то, что в англоязычной традиции называется Opportunistic Security — «если удалось, то обязательно будем аутентифицировать и зашифровывать, а если нет — тогда ладно».

Может показаться, что если серверный сертификат не проверять, то от TLS для DNS нет никакого толку: промежуточный узел может перехватить соединение и подставить свой, произвольный сертификат, подходящий по формату. Однако в случае DoT схема, как минимум, всегда защищает от пассивного прослушивания трафика. Пассивное прослушивание реализовать гораздо проще, чем активный перехват, а защита от утечек через «пассивные» каналы и является основной для DoT. Тем более, если речь идёт о работе с авторитативными DNS-серверами. Защита же от активной атаки может быть реализована на «последней миле», где клиенту, обычно, заранее известны некоторые данные о сервере: имя, ключ и так далее.

Кстати, посмотрим на имена и на интервал валидности сертификата, который вернул сервер ns0.wikimedia.org.:

        Serial Number:             05:f7:71:aa:08:8e:32:88:0a:71:9c:2d:f3:98:17:e1:d6:aa         Signature Algorithm: ecdsa-with-SHA384         Issuer: C = US, O = Let's Encrypt, CN = E5         Validity             Not Before: Mar 16 05:02:56 2025 GMT             Not After : Jun 14 05:02:55 2025 GMT         Subject: CN = ns0.wikimedia.org  [...]              X509v3 Subject Alternative Name:                  DNS:ns0.wikimedia.org, DNS:ns1.wikimedia.org, DNS:ns2.wikimedia.org

Это сертификат, выпущенный Let’s Encrypt. Subject пропускаем, смотрим в Subject Alternative Name, где указаны все три имени для трёх авторитативных серверов зоны wikimedia.org., которые мы нашли в самом начале этого экскурса в DoT силами dig.

DoT уже поддерживается основными программными пакетами авторитативных серверов и рекурсивных резолверов. Например, BIND и Unbound. Так что протокол можно внедрять не только на «последней миле», но и на авторитативных серверах, защищая трафик резолверов к этим серверам (и обратно).

Подведём итог. DoT, используя TLS, защищает данные от пассивного прослушивания третьей стороной. Если клиент применяет тот или иной метод аутентификации сервера, то DoT может защитить от активного перехвата и подмены данных. Но всё это работает только на том отрезке, где применяется DoT, а защита TLS не распространяется на сам состав DNS-данных. Чтобы защитить содержательную часть DNS, необходимо использовать DNSSEC, а эту технологию тоже можно изучать при помощи dig на практических DMS-зонах.


ссылка на оригинал статьи https://habr.com/ru/articles/891374/