Давайте сразу вопрос на засыпку: может ли быть так, что клиент подключается, ну, например, к серверу www.python.org (самому настоящему, тому, к которому обращаются еще миллионы клиентов со всего мира), а потом использует его как прокси и гоняет через это подключение трафик до своего VPS для доступа в неподцензурный интернет? Если вы не уверены в ответе на этот вопрос или почему-то ответили "нет", то добро пожаловать в статью.
Я уже не раз рассказывал здесь о технологии XTLS-Reality (1, 2, 3), суть которой в том, что ваш прокси-сервер VPS может очень достоверно маскироваться под какой-нибудь популярный веб-сайт — принимать подключения, которые будут выглядеть точно так же, как обращения к настоящему сайту, отвечать на них полностью аутентичным TLS-сертификатом, и в целом вести себя как тот настоящий сайт. Сервер проверяет специальную магию в запросе при установлении TLS-соединения (которую невозможно выявить, точно не зная что она там есть и не зная секретные ключи), и если проверка прошла успешно, то работает как прокси, а если нет — то передает запросы на настоящий сервер, а потом передает ответы от него. Единственная проблема — сам IP-адрес. Немного подозрительно, когда к какому-нибудь якобы www.google.com постоянно обращается только один пользователь, а IP-адрес этого сервера на самом деле даже не относится к автономной сети Google.
Еще я рассказывал о разных вариантах проксировать трафик посредством вебсокетов и простых HTTP-туннелей через различные CDN
, такие как Cloudflare и Gcore. Вероятность того, что под блокировку попадет вся CDN гораздо ниже, чем что забанят какой-то один сервер или диапазон хостера, но та схема требовала регистрацию своего домена для работы через CDN.
И наверняка многим в голову приходила идея, а нельзя ли как-нибудь совместить эти два механизма? Проксироваться через CDN, но при этом "прикрываясь" каким-нибудь чужим доменом? Ответ: да, можно, и сейчас мы посмотрим, как именно.
Итак, что такое "domain fronting"?
Что такое CDN, вы, наверное, хорошо знаете: это сети из тысяч серверов, которыми оперируют компании типа Cloudflare, Google, Amazon, и т.д, нередко с выделеными каналами связи между ними, которые используются для доставки интернет-трафика клиентам. Благодаря оптимальным маршрутам следования трафика и кэшированию данных поближе к пользователям, они позволяют раздавать контент быстрее и эффективнее и гибко масштабировать нагрузку.
А теперь разберем, что именно происходит, когда вы подключаетесь браузером к какому-нибудь сайту по HTTPS. Первый шаг - установление зашифрованного соединения по протоколу TLS. В самом начале запроса клиент, среди других данных, передает на сервер строку, называемую SNI - server name identification, в которой он указывает, к какому именно домену он хочет подключиться (потому что один веб-сервер может обслуживать сразу много доменов). Эта информация передается в нешифрованном виде, ее могут прочитать цензоры и заблокировать ваше подключение, если им не понравился домен интересующего вас сайта. Второй шаг — когда шифрованное подключение уже установлено, начинает работать протокол HTTP, и там, в заголовках запросов, есть поле "Host", в котором снова указывается домен сайта, к которому вы обращаетесь. На этом этапе данные передаются уже в зашифрованном виде, и подслушать со стороны их нельзя. Когда вы серфите сайтом в браузере, эти два значения (SNI в TLS и Host в HTTP) всегда одинаковые.
И когда-то очень давно, какой-то сообразительный хакер подумал: CDN может обслуживать сотни и тысячи разных сайтов, а что если мы передадим в SNI домен одного сайта, а в Host - домен другого? Он (или она, кто ж знает) попробовал и с удивлением обнаружил, что все работает и так - сервер демонстрирует TLS-сертификат домена, который был запрошен в SNI, но при этом отдает содержимое веб-сайта для другого домена, который был запрошен в HTTP Host. Так и появился domain fronting - когда обращаясь к веб-серверу мы "снаружи" демонстрируем один домен (приличный, разрешенный, незаблокированный), а на самом деле нас интересует другой.
Я, пожалуй, разделил бы domain fronting на два типа.
Первый тип - это когда мы прикрываемся каким-нибудь популярным доменом на той же CDN. Понятное дело, что для этого надо, чтобы CDN такое позволяла, а таких CDN все меньше и меньше - потому что кроме нас таких хороших подобной техникой еще пользуются всякие жулики для вредоносного ПО.
Второй тип - это когда CDN не разрешает прикрываться чужим доменом, но при этом для целей раздачи контента выдает свой технический домен типа cdn-abg456sf1.bignetwork.com
, и мы можем работать через него, что тоже неплохо - во-первых не нужно иметь свой домен, а во-вторых, подобные домены все равно очень популярны, используются на тысячах сайтов, и ведут в правильный IP-диапазон, принадлежащий этой CDN. А если вдруг домен попал под блокировку, перепрыгнуть на другой можно за пару минут.
Осталось только узнать, какие именно CDN такое разрешают. Обладая эти знанием, мы можем гонять трафик до прокси-сервера через CDN, прикрываясь каким-нибудь чужим популярным доменом, например, через вебсокеты средствами XRay или GOST, или даже без веб-сокетов (если CDN их не поддерживает или хочет за них дополнительных денег) через plain HTTP tunnel. Такую же механику использовал популярный мессенджер Signal, пока их не выгнали с Google Cloud и с Amazon, такую же механику до сих пор использует Tor с транспортом meek.
Собственно, в wiki Tor есть очень и очень интересный документ: https://gitlab.torproject.org/legacy/trac/-/wikis/doc/meek. Там разбирается механика domain fronting, и приведен анализ возможностей использования его на разных популярных CDN. Одно НО — документ датируется 2018-м годом, и с тех пор очень многое поменялось. Я советую все равно изучить его для вдохновения и идей, а пока поделюсь тем, что успел нарыть сам.
Серверную часть можно настраивать так, как я описывал в статье "Особенности проксирования через CDN/Websocket/gRPC для обхода блокировок", разве что в некоторых случаях вместо TLS (HTTPS) на сервере понадобится обычный HTTP (без шифрования и сертификатов). Также возможно использовать на сервере GOST и его родной клиент.
Fastly
Самая любимая на сегодняшний день CDN для фронтеров, потому что разрешает прикрываться чужими доменами. Но, кажется это золотое время уже проходит, потому что недавно они объявили, что в следущем году такую лафу запретят, однако пока еще есть немного времени чтобы поразвлекаться.
При регистрации дарят 50 долларов на аккаунт. Никаких паспортных данных предоставлять не надо, банковскую карту привязывать тоже, поэтому, в теории, ни что не мешает создавать новый аккаунт каждый раз, когда закончилась халява на старом (если что, осуждаем). Fastly может проксировать вебсокеты, они там идут по отдельному тарифу, но есть бесплатный 30-дневный пробный период.
Чтобы создать ресурс, нужно зарегистрироваться, потом пойти в раздел "Deliver", нажать "Create delivery service". Если у вас нет своего домена или вы не планируете его использовать, то в "Domains" можно написать все что угодно:
Далее идем в Origins -> Hosts и добавляем IP-адрес вашего сервера:
После добавления Origin Host можно нажать на редактирование и посмотреть, что там еще есть, а именно, настройки TLS:
Вот тут аккуратно. С одной стороны, хорошо бы, чтобы трафик между CDN и вашим сервером бегал зашифрованным. С другой стороны, если у вас нет своего домена, то вы сможете использовать только самоподписанный сертификат - в этом случае можно заполнить нужные поля и подсунуть в Fastly ваш сертификат, чтобы она знала, что сервер, с которым она коммуницирует, действительно тот, за кого себя выдает.
Но тут есть важный нюанс, в документации Fastly про проксирование вебсокетов написано, что вебсокеты не будут работать с самоподписанными сертификатами. Что там говорить, у меня они даже с настоящим сертификатом от LetsEncrypt не заработали, пока я не отключил TLS вообще. Поэтому можно оставить "No, do not enable TLS" и CDN будет подключаться к вашему серверу на простой 80 порт без шифрования.
В принципе, если вы хотите использовать plain HTTP tunnel, то это достаточно - можно сохранять параметры, нажимать "Activate", подождать 5-10 минут, пока информация о вашем ресурсе разлетится по серверам сети Fastly, и можно начинать пользоваться.
Если же вы хотите использовать вебсокеты, то все чуть сложнее - идем слева в меню в Settings -> Webosckets, скроллим вниз, активируем триальный период для вебсокетов и сами вебсокеты:
Думаете все? Ха, хрен там. Нужно сделать еще одну очень неочевидную вещь, которая запрятана глубоко в документации: https://developer.fastly.com/learning/concepts/real-time-messaging/websockets-tunnel/
Идите слева в меню в VCL snippets, нажмите Create your first snippet:
Там задаете какое-нибудь имя, выбираете средний тип "within subroutine", и саму сабрутину recv (vcl_recv), и вставляете там такой код:
if (req.http.Upgrade) {
return (upgrade);
}
Сохраняете, и наконец-то делаете Activate для вашего ресурса. Ждете 5-10 минут (раньше бесполезно - не заработает) и можно пробовать.
Настраивайте свой прокси-клиент с вебсокетами или plain HTTP tunnel так, чтобы он стучался на адрес какой-нибудь популярного сайта, который испольщует Fastly, например, www.python.org или www.spotify.com, пусть в SNI передается тоже www.python.org или www.spotify.com, но при этом в поле "Host" HTTP-запроса должен быть ваш домен (возможно даже в реальности не существующий), который вы указали при создании ресурса в Fastly.
Пример настройки вебсокетов в Nekoray через Fastly и domain fronting, при условии, что у вас на сервере XRay или Sing-box с настроенными вебсокетами
Amazon Cloudfront
Здесь у нас будет возможен фронтинг типа 2 - Amazon выдает "технический домен" для раздачи контента.
В первую очередь надо зарегистрироваться в облаке Amazon. Хоть мы и будем использовать Free Tier, придется привязать банковскую карточку, и да, карты российских банков не принимаются.
у меня вообще была совершенно фееричная история с регистрацией
Я зарегистрировался в Amazon Cloud, но при попытке создать новый ресурс в Cloudfront вылетало сообщение типа "Неизвестная ошибка, обратитесь в техподдержку" (да, для тех кто делает такие сообщения об ошибках в аду зарезервирован отдельный котел). Я начал переписку с техподдержкой (на первой линии у них, кажется, отвечает нейросеть), и в итоге выяснилось, что им "требуется дополнительная верификация моего аккаунта". Впрочем, это не удивительно - у меня давным-давно был аккаунт со страной Russia, теперь у меня новый аккаунт с телефоном и адресом страны, где я теперь живу, но с тем же ФИО, при этом IP-адреса моего провайдера почему-то во всех GeoIP-база определяются не как страна где я живу, а как Германия, а карточку я привязал банка Revolut, которая по коду платежной системы вообще относится к какому-то литовскому банку. Короче, очень подозрительный аккаунт получился :)
В процессе переписки с саппортом мне вообще закрыли доступ к аккаунту. Оказалось, что Amazon несколько раз мне присылал емайлы, типа, "нам нужно уточнить ваши данные, пожалуйста, загрузите вот по этой ссылке вот такие документы в течении 3-х дней", но при этом GMail почему-то такие письма от Amazon'а отмечал как опасные и фишинговые (!) и отправлял сразу в спам, где я их, естественно, не видел.
В итоге они потребовали отправить им какие-нибудь счета за коммунальные услуги, где видно мое имя и адрес, а так же выписку из банка для карты, которую я привязал. Тут я немного подофигел, потому что жилье я арендую и счета приходят не на мое имя - в итоге сошлись на том, что я пришлю им ежемесячную квитанцию от моего мобильного оператора (там есть мое имя и адрес), и выписку со счета Revolut'а. После загрузки документов они присылали мне ответ, что мои документы "неразборчивые", не указывая, что именно там черт возьми неразборчивое, я загружал их снова, в итоге на 4-ую или 5-ую итерацию они все-таки согласились, что да, документы разборчивые :) Возможно помогло приложенное сопроводительное письмо, мол "вот это и вот это - вот это, и обратите внимание, dear sir or madam, выписка из банка - по счету, но если вы присмотритесь, то в деталях транзакции увидите так желаемый вами номер карты". И да, еще важно, чтобы выписка была именно по тому счету, с которого Амазон списал и вернул 1 евро для верификации карты.
В итоге аккаунт мне разблокировали, я попробовал снова создать ресурс в Cloudfront, и... снова получил ту же самую ошибку. Я переоткрыл тикет в поддержке, написал что проблема решена и изложил все что думаю по поводу их саппорта и сервиса. В итоге в субботу утром (!) мне поступил звонок на телефон с нью-йоркского номера, голос на другой стороне на хорошем английском представился саппортом Амазона, попросил продиктовать код верификации, который они отправили на емайл (там в емайле прямо так и пишется, "for Your Recent Support Request" и "please provide the following One-Time-Password (OTP) over the phone"), после чего сообщил, что они моё обращение разобрали, что-то там поправили у себя в моем аккаунте, и попросили сразу проверить, что теперь все работает. Я проверил - действительно заработало.
Итак, создали аккаунт, выбираем Free Tier - он включает в себя бесплатно 1 терабайт передаваемых данных в месяц и 10 миллионов запросов. С запросами аккуратнее, при постоянном использовании Plain HTTP tunnel их можно высадить и раньше чем за месяц, но мы будем использовать вебсокеты, Amazon их поддерживает даже на бесплатном тарифе.
Идем в консоли в Cloudfront, нажимаем Create distirbution:
И начинаем заполнять. Придется иметь свой домен, хоть какой-нибудь, пусть даже это будет бесплатный DynDNS - только лишь IP-адрес система использовать не дает. Вставляем его в Origin domain.
Протокол я для простоты выбрал HTTP only, чтобы не заморачиваться с сертификатом на сервере.
Далее привожу настройки, которые сработали у меня:
Origin shield = No
Viewer protocol policy = HTTP and HTTPS
Allowed HTTP methods - не особо важно, но на всякий случай выберем все
А вот теперь важное. Cache policy = caching disabled
, Origin request policy = AllViewer
- это нужно чтобы CDN передавала все заголовки от клиента на ваш сервер, иначе не заработают вебсокеты.
Web Application Firewall - я отключил.
Готово, нажимаем "Create distribution", и через некоторое время Cloudfront покажет вам ваш новый ресурс и технический домен для него:
Подождите, пока в поле Last modified появится нормальная дата, а не сообщение о том, что данные распространяются на сервера Cloudfront - до этого момента домен возможно уже будет работать, но вебсокеты не заведутся. Не лишним будет еще подождать 5-10 минут для надежности.
Все, а теперь вы можете использовать ваш технический домен *.cloudfront.net из поля "Distribution domain name" слева для подключения к своему прокси через вебсокеты. Все три параметра (адрес сервера, SNI и поле host) должны указывать на этот ваш домен.
пример настройки вебсокетов в Nekoray с Amazon Cloudfront, при условии, что у вас на сервере XRay или Sing-box с настроенными вебсокетами
Что еще?
А остальное - это ваше домашнее задание :) Существует еще много разных CDN - Microsoft Azure, Akamai, CDNetworks, Edgio, Stackpath, Level3 (его реселлеры VPS.NET и CloudVPS), Melbicom, CDN77, EdgeCast, и другие.
Допускают ли они domain fronting или предоставляют ли хотя бы технические домены - нам еще предстоит проверить. Я предлагаю поучаствовать в этом и вам по мере возможностей (да, могут потребоваться небольшие денежные вложения) и делиться результатами в комментариях к этому посту или на форуме ntc.party.
Как найти CDN, разрешающие domain fronting и поддерживающие вебсокеты?
Сначала нужно найти несколько IP-адресов, принадлежащих балансерам этой сети. Можно просто пингануть домен морды CDN или использовать какой-нибудь веб-сервис, но лучше все-таки применять nslookup, на случай, если у домена есть несколько IP-адресов.
Далее, для каждого из этих IP-адресов нужно определить, какие домены сайтов в интернете указывают на этот IP-адрес, для этого можно использовать например сервис 2IP.ru или любой аналогичный.
Еще вариант - можно посмотреть сайт CDN-сервиса, часто они указывают, кто являются их крупными клиентами, запомнить их домены и отрезолвить их IP-адреса.
А дальше - проверить сам фронтинг. Допустим, мы изучаем CDN77.com, и мы нашли, что их услугами пользуется сайт phpmyadmin.net.
Делаем запрос через CURL:
curl https://www.cdn77.com/ --header 'Host: www.phpmyadmin.net' -v
И если вместо сообщений об ошибке или содержимого cdn77.com вы видите содержимое сайта phpmyadmin.net - поздравляю, вероятно вы можете использовать фронтинг первого типа на этой CDN. Последним шагом будет создание аккаунта (многие CDN предоставляют free trial) и проверка этого вживую - может оказаться так, что фронтинг разрешен только для старых аккаунтов, а для новых запрещен.
Это было про полноценный фронтинг (то что мы назвали фронтингом первого типа). Даже если он запрещен, то возможно все-таки с этой CDN можно будет использовать технический домен (так называемый фронтинг второго типа), что не так круто, но все-таки лучше, чем ничего.
Теперь про вебсокеты. Когда CDN поддерживает вебсокеты (а в идеале - вообще gRPC, но такое умеет только Cloudflare), то для нас все гораздо лучше - можно проксировать более эффективно, с меньшим времени отклика и меньшим количеством запросов. Если нет - то не все потеряно, можно использовать Plain HTTP Tunnel (см. мою статью про GOST). Как узнать, поддерживает ли конкретная CDN вебсокеты - внимательно изучить сайт и документацию самой CDN по части поддержимаемых фич, ну и погуглить :) А еще может помочь вот эта табличка сравнения с CDNPlanet: https://www.cdnplanet.com/guides/websockets/
Ну а еще что?
Транспорт meek от Tor имеет интересную возможность работать через так называемые "рефлекторы" - когда для того, чтобы подключиться к вашему прокси, используется не CDN, а простой скрипт на PHP или Python, который просто 1-в-1 передает пришедшие на него запросы на ваш прокси и обратно. Такой скрипт можно разместить даже на дешевом тупом shared-хостинге (не VPS), и гонять трафик через него. Зачем? Есть теория, что некоторые shared-хостинги тоже допускают domain fronting - например, судя по всему, что-то такое умеет HostGator, а возможно и многие другие.
Meek является pluggable transport'ом Tor, поэтому его непросто заставить работать отдельно (возможно поможет ptadapter - если кто-то разберется, как заставить его работать с meek, напишите пожалуйста статью или хотя бы мне в ЛС), однако, судя по всему, довольно легко адаптировать его PHP-рефлектор для работы с plain-HTTP-tunnel из GOST, а может быть он заработает даже без переделки. Что открывает очередной простор для экспериментов :)
На этом всё.
Удачи, и да прибудет с вами сила.
Если вы хотите сказать спасибо автору — сделайте пожертвование в один из благотворительных фондов: "Подари жизнь", "Дом с маяком", "Антон тут рядом".