Введение
![](https://habrastorage.org/webt/bn/0p/mv/bn0pmvrdjnyzw-hr4glj5lekpmk.png)
Современные корпоративные системы фильтрации контента, от таких именитых производителей как Cisco, BlueCoat, FireEye имеют довольно много общего с более мощными их собратьями — DPI системами, которые усиленно внедряются на национальном уровне. Суть работы и тех и других в том, чтобы производить досмотр входящего и исходящего интернет трафика и, на основании черных/белых списков, принимать решение о запрете интернет-соединения. А так как и те, и другие в основах своей работы полагаются на схожие принципы, то и способы их обхода также будут иметь много общего.
Одной из технологий, позволяющей достаточно эффективно обходить как DPI, так и корпоративные системы, является технология домен-фронтинга. Ее суть состоит в том, что мы идем на заблокированный ресурс, прикрываясь другим, публичным доменом, с хорошей репутацией, который заведомо не будет блокирован ни одной системой, например google.com.
О данной технологии было написано уже достаточно много статей и приведено много примеров. Однако популярные и обсуждаемые в последнее время технологии DNS-over-HTTPS и encrypted-SNI, а также новая версия протокола TLS 1.3 дают возможность рассмотреть еще один вариант домен-фронтинга.
Разбираемся с технологией
Сначала немного определимся с основными понятиями, чтобы у всех было понимание who is who и зачем все это нужно. Мы упомянули механизм eSNI, работа которого будет рассмотрена дальше. Механизм eSNI (encrypted Server Name Indication) – защищенный вариант SNI, доступный только для протокола TLS 1.3. Основная суть – шифровать в том числе информацию о том, к какому домену отправляется запрос.
А теперь давайте рассмотрим работу механизма eSNI на практике.
Допустим, у нас есть интернет-ресурс, который блокируется современным DPI решением (возьмем к примеру, знаменитый торрент-трекер — rutracker.nl). При попытке захода на сайт торрент-трекера — мы видим стандартную заглушку провайдера о том, что ресурс блокируется:
![](https://habrastorage.org/webt/hj/rt/kd/hjrtkdnlahnohjgs-tq-jzfctnu.png)
На сайте РКН это домен действительно числится в стоп-листах:
![](https://habrastorage.org/webt/4g/2m/k0/4g2mk0d4ks6xzbvmvv0za-vt16y.png)
При запросе whois — видно, что сам домен «спрятан» за облачным провайдером Cloudflare.
![](https://habrastorage.org/webt/my/wa/ki/mywakijkqem9a32jtmwyb0tcwfs.png)
Но в отличие от «специалистов» из РКН, более технически подкованные сотрудники из билайна (или наученные горьким опытом нашего знаменитого регулятора) не стали тупо банить сайт по IP -адресу, а внесли в стоп-лист именно доменное имя. В этом легко убедиться, если посмотреть, какие еще домены прячутся за этим же IP-адресом, посетить один из них и увидеть, что доступ не заблокирован:
![](https://habrastorage.org/webt/dk/j2/gk/dkj2gkveuqcbwkbumlrebfznt8u.png)
А как же так получается? Каким образом провайдерский DPI узнает, на какой из доменов идет мой браузер, ведь все коммуникации происходят по протоколу https, а подмены сертификатов https от билайна мы вроде пока не замечали? Уж не ясновидящий ли он или за мной идет слежка?
Попробуем ответить на это вопрос, взглянув на трафик через wireshark
![](https://habrastorage.org/webt/c1/k2/9e/c1k29ehqnytwfvw8udk-m6mkv6q.png)
На скриншоте видно, что сперва браузер получает IP-адрес сервера через DNS, потом происходит стандартное TCP-рукопожатие с сервером назначения, а затем браузер пытается установить ssl-соединение с сервером. Для этого он передает пакет SSL Client Hello, в котором присутствует имя исходного домена в открытом виде. Это поле необходимо фронтенд-серверу cloudflare для того, чтобы правильно маршрутизировать соединение. Вот тут-то нас и ловит провайдерский DPI, разрывая наше соединение. При этом мы не получаем никакой заглушки от провайдера, и видим стандартную ошибку браузера как будто сайт отключен или просто не работает:
![](https://habrastorage.org/webt/tn/8h/fa/tn8hfavie40wl2qqiogrdqrxptw.png)
Теперь давайте включим механизм eSNI в браузере, как это написано в инструкции для Firefox :
Для этого мы открываем страницу конфигурации Firefox about:config и активируем следующие настройки:
network.trr.mode = 2;
network.trr.uri = https://mozilla.cloudflare-dns.com/dns-query
network.security.esni.enabled = true
После этого мы проверим корректность работы настроек на сайте cloudflare по ссылке и попробуем фокус с нашим торрент-трекером еще раз.
![](https://habrastorage.org/webt/p6/o3/o-/p6o3o-zt1k8yqi4ht_rcrxugyfg.png)
Вуаля. Наш любимый трекер открылся, без каких-либо VPN и прокси-серверов. Давайте теперь посмотрим на дамп трафика в wireshark, что же произошло.
![](https://habrastorage.org/webt/ll/az/gj/llazgjig0xbha6ndnlvj0nqvziw.png)
На сей раз пакет ssl client hello не содержит в явном виде домен назначения, а вместо этого в составе пакета появилось новое поле — encrypted_server_name — именно там и содержится значение rutracker.nl, и расшифровать это поле может только фронтенд сервер cloudflare. А раз так, то провайдерскому DPI не остается ничего кроме как умыть руки и разрешить такой трафик. А других вариантов с шифрованием и нет.
Итак, как работает технология в браузере — мы посмотрели. Теперь давайте попробуем применить ее для более специфичных и интересных вещей. И для начала мы научим тот же curl использовать eSNI для работы с TLS 1.3, а заодно посмотрим, как работает сам домен-фронтинг на основе eSNI.
Домен-фронтинг с eSNI
Ввиду того, что curl для подключения по протоколу https использует стандартную библиотеку openssl, прежде всего нам необходимо обеспечить поддержку eSNI именно там. В master-ветках openssl поддержки eSNI пока что нет, поэтому нам необходимо скачать специальную ветку openssl, скомпилировать и установить ее.
Клонируем репозиторий с гитхаба и компилируем как обычно:
$ git clone https://github.com/sftcd/openssl
$ cd openssl
$ ./config
$ make
$ cd esnistuff
$ make
Далее — клонируем репозиторий с curl и конфигурируем его компиляцию с использованием нашей собранной openssl библиотеки:
$ cd $HOME/code
$ git clone https://github.com/niallor/curl.git curl-esni
$ cd curl-esni
$ export LD_LIBRARY_PATH=/opt/openssl
$ ./buildconf
$ LDFLAGS="-L/opt/openssl" ./configure --with-ssl=/opt/openssl --enable-esni --enable-debug
Здесь важно правильно указать все каталоги, где находится openssl (в нашем случае — это /opt/openssl/) и проследить, чтобы процесс конфигурации прошел без ошибок.
В случае успешной конфигурации — мы увидим строку:
WARNING: esni ESNI enabled but marked EXPERIMENTAL. Use with caution!
$ make
После успешной сборки пакета мы воспользуемся специальным bash-файлом из состава openssl для настройки и запуска curl. Скопируем его в каталог с curl для удобства:
cp /opt/openssl/esnistuff/curl-esni
и выполним тестовый https-запрос на сервер cloudflare, одновременно записав DNS и TLS пакеты в Wireshark.
$ ESNI_COVER="www.hello-rkn.ru" ./curl-esni https://cloudflare.com/
В ответе сервера помимо множества отладочной-информации от openssl и curl мы получим HTTP-ответ с кодом 301 от cloudflare.
HTTP/1.1 301 Moved Permanently
< Date: Sun, 03 Nov 2019 13:12:55 GMT
< Transfer-Encoding: chunked
< Connection: keep-alive
< Cache-Control: max-age=3600
< Expires: Sun, 03 Nov 2019 14:12:55 GMT
< Location: https://www.cloudflare.com/
что свидетельствует о том, что наш запрос был успешно доставлен до сервера назначения, услышан и обработан.
Теперь давайте посмотрим на дамп трафика в wireshark, т.е. что увидел в данном случае провайдерский DPI.
![](https://habrastorage.org/webt/sp/va/-o/spva-ogjvtwlfyiea9vg4n8qbla.png)
Видно, что сначала curl обратился к DNS серверу за публичными eSNI ключом для сервера cloudflare — TXT DNS запрос на _esni.cloudflare.com (пакет №13). Затем, используя openssl-библиотеку, curl отправил TLS 1.3 запрос на сервер cloudflare в котором поле SNI было зашифровано публичным ключом, полученным на предыдущем этапе (пакет №22). Но, помимо поля eSNI, в составе SSL-hello пакета было вставлено еще и поле с обычным — открытым SNI, которое мы можем указать в произвольном порядке (в данном случае — www.hello-rkn.ru).
Данное поле открытого SNI никак не учитывалось при обработке серверами cloudflare и лишь являлось маскировочным для провайдерского DPI. Сервер cloudflare принял наш ssl-hello пакет, расшифровал eSNI, извлек оттуда оригинальный SNI и обработал его как ни в чем не бывало (сделал все именно так, как планировалось при разработке eSNI).
Единственное, за что в данном случае можно зацепиться с точки зрения DPI — первичный DNS запрос на _esni.cloudflare.com. Но мы сделали DNS запрос открытым лишь для того, чтобы показать, как данный механизм работает изнутри.
Чтобы окончательно выбить почву из-под ног DPI мы используем уже упомянутый механизм DNS-over-HTTPS. Небольшое пояснение – DOH – протокол, который позволяет защититься от атаки «человек посередине» за счет отправки DNS-запроса по протоколу HTTPS.
Выполним запрос повторно, но в это раз публичные eSNI ключи мы получим по протоколу https, а не DNS:
ESNI_COVER="www.hello-rkn.ru" DOH_URL=https://mozilla.cloudflare-dns.com/dns-query ./curl-esni https://cloudflare.com/
Дамп трафика запроса представлен на скриншоте ниже:
![](https://habrastorage.org/webt/qz/0m/ky/qz0mky5bwnmguhwleqennudeupc.png)
Видно, что сперва curl обращается к серверу mozilla.cloudflare-dns.com по протоколу DoH (https соединение на сервер 104.16.249.249), чтобы от них получить значения public ключей для шифрования SNI, а затем уже к серверу назначения, прикрываясь при этом доменом www.hello-rkn.ru.
Помимо указанного выше DoH резолвера mozilla.cloudflare-dns.com мы можем использовать и другие популярные сервисы DoH, например, от знаменитой корпорации зла.
Выполним такой запрос:
ESNI_COVER="www.kremlin.ru" DOH_URL=https://dns.google/dns-query ./curl-esni https://rutracker.nl/
И получим ответ:
< HTTP/1.1 301 Moved Permanently
< Date: Sun, 03 Nov 2019 14:10:22 GMT
< Content-Type: text/html
< Transfer-Encoding: chunked
< Connection: keep-alive
< Set-Cookie: __cfduid=da0144d982437e77b0b37af7d00438b1a1572790222; expires=Mon, 02-Nov-20 14:10:22 GMT; path=/; domain=.rutracker.nl; HttpOnly; Secure
< Location: https://rutracker.nl/forum/index.php
< CF-Cache-Status: DYNAMIC
< Expect-CT: max-age=604800, report-uri="https://report-uri.cloudflare.com/cdn-cgi/beacon/expect-ct"
< Server: cloudflare
< CF-RAY: 52feee696f42d891-CPH
![](https://habrastorage.org/webt/dk/xk/b-/dkxkb-cudflzhgaxaebb1xhd9bw.png)
В данном случае мы обратились на заблокированный сервер rutracker.nl, использовав при этом DoH-резолвер dns.google (тут нет опечатки, теперь у знаменитой корпорации появился свой домен первого уровня) и прикрылись уже другим доменом, блокировать который строго-настрого запрещено всем DPI под страхом смертной казни. По полученному ответу можно понять, что наш запрос был удачно обработан.
В качестве дополнительной проверки того, что провайдерский DPI реагирует на открытый SNI, который мы передаем в качестве прикрытия — мы можем выполнить запрос к rutracker.nl прикрывшись каким-нибудь другим запрещенным ресурсом, например другим «хорошим» торрент-трекером:
$ ESNI_COVER="rutor.info" DOH_URL=https://dns.google/dns-query ./curl-esni https://rutracker.nl/
Ответа от сервера мы не получим, т.к. наш запрос будет заблокирован DPI- системой.
Небольшое заключение к первой части
Итак, нам удалось показать работоспособность eSNI с помощью openssl и curl и проверить работу домен-фронтинга, основанного на eSNI. Таким же образом мы можем адаптировать наши любимые инструменты, использующие библиотеку openssl, для работы «под прикрытием» других доменов. Подробнее об этом — в наших следующих статьях.