Птица Феникс. Как разблокировать прокси с помощью «зеркала»

от автора

Схема

Схема

TL;DR Начнём с создания точки входа на заблокированный сайт/REALITY/XHTTP/VLESS и закончим примером с модификацией трафика через Zapret2.

В самом простом виде задача выглядит так. Есть сайт в зоне без цензуры и есть несчастный юзер в зоне, где IP-адрес этого сайта заблокирован. Конечно, в 2026 году никого не интересует блокировка одного сайта, но интересует обход блокировки прокси-сервера, который использует VLESS-REALITY, VLESS-TLS-XHTTPS, Trojan или ещё какой-нибудь протокол, всеми силами маскирующийся под HTTPS/TLS — то есть под тот самый обычный вебсайт.

Небольшое теоретическое отступление

Прежде чем вебсервер отдаст браузеру счастливого юзера HTML-страничку сайта, происходит много всего. Сначала — TCP-хендшейк (обмен специальными пакетами SYN, SYN-ACK, ACK), после которого браузер юзера посылает на вебсервер TLS Client Hello. Как это выглядит «в битах», можно детально изучить на прекрасном сайте за авторством Michael Driscoll, нас же будут интересовать всего две вещи. Первая — это адрес (далее Address), с которым в конечном итоге связывается пользователь. Тот адрес, до которого — пользователь хочет — должны дойти его пакеты; конечная точка. Вторая — это SNI, то есть Server Name Indication, поле из Client Hello, означающее доменное имя сайта, который пользователь предполагает найти на этом вебсервере. Address и SNI в обычной жизни совпадают, но нашу жизнь разве можно назвать обычной? SNI имеет очень важное значение. Именно к этому имени привязан сертификат, который используется в TLS-шифровании. «Замочек» в браузере означает, что сертификат именно для этого SNI подписан и проверен.

VPN-протоколы (ладно-ладно, прокси-протоколы), которые хотят казаться соединением с сайтом для наблюдающего со стороны цензора, обычно поступают по принципу «быть, а не казаться». То есть устанавливают настоящее TLS-соединение. На сайте Trojan написано кратко и хорошо, и, например, VLESS-TLS делает так же. По уже установленному зашифрованному каналу дальше можно посылать всё, что угодно. Цензор в лице системы глубокого анализа пакетов (deep package inspection, DPI), конечно, может применить статистические и временнЫе методы анализа трафика, но на деле не так страшен. Буква D в DPI относится скорее к черенку, чем к анализу.

Важно, что «суровость» DPI разная в разных сетях. Логика, по-видимому, такая. На мобильных сетях люди больше развлекаются, чем работают (🤷‍♂️), поэтому доступный диапазон IP-адресов сужен, а непонятный трафик чаще блокируется. На «проводном» интернете разрешенный диапазон IP больше, и с разными протоколами жить легче. На интернете в российских дата-центрах DPI ещё более «вялый». И может оказаться, что ваш заблокированный в мобильных сетях сервер с Xray, Remnawave или трих…м 3x-ui вполне доступен из российского дата-центра! А сервера в российском дата-центре окажутся доступны из любых сетей.

Дальше для определённости я буду говорить о заграничном «виртуальном» сервере (virtual private server, VPS) и российском сервере (RU-VPS), хотя они могут быть настоящими bare metal и располагаться совсем в других странах.

Зеркало

Зеркало на картине Берты Моризо, 1876. https://commons.wikimedia.org/w/index.php?curid=11147892

Зеркало на картине Берты Моризо, 1876. https://commons.wikimedia.org/w/index.php?curid=11147892

Итак, нам нужно только одно. Пакеты, прилетающие на RU-VPS, и имеющие SNI, который использует заграничный сервер, на этот самый сервер и перенаправить. А ответ от VPS, который придёт на RU-VPS, направить назад пользователю в рамках того же соединения с ним. Для такой простой задачи достаточно одной таблетки Nginx с модулем Stream. В Ubuntu понадобится установить nginx-extras и libnginx-mod-stream, в Rocky Linux — nginx и nginx-mod-stream.

На зеркале (RU-VPS) в настройках Nginx (/etc/nginx/nginx.conf) нужно убрать блоки server {...}, включая те, что есть в include /etc/nginx/conf.d/*.conf, а после блока http {...} добавить stream {...}. Пусть VPS располагается по адресу Address = vless.example.com, и использует SNI = shop.example.com. Тогда модуль stream в конфиге Nginx будет выглядеть примерно так:

stream {    upstream backend_a {        server vless.example.com:443;    }    upstream blackhole_404 {        server 127.0.0.1:1;    }    map $ssl_preread_server_name $target_backend {        default blackhole_404;        shop.example.com backend_a;    }    server {        listen 0.0.0.0:443;        ssl_preread on;        proxy_bind 0.0.0.0;        proxy_pass $target_backend;        proxy_timeout 10s;        proxy_connect_timeout 5s;    }}

Здесь Nginx слушает порт 443 на всех ip4 интерфейсах (0.0.0.0) и анализирует трафик: ssl_preread on — это предпросмотр SNI без терминирования TLS. Если Nginx видит TLS соединение с полем SNI shop.example.com, он отправляет весь трафик этого соединения на vless.example.com (map $ssl_preread_server_name $target_backend). Кстати, Address (конечная точка, vless.example.com) может быть задан как IP, домен использовать необязательно.

Слушать можно не все интерфейсы, а только один, listen IP1:443, а пересылать пакеты — можно с другого, proxy_bind IP2, если у вашего сервера два IP адреса. Запросы с другими SNI перенаправятся на 127.0.0.1:1, и поскольку порт 1 заререзвирован для редкого протокола tcpmux и почти никогда не используется активными сервисами, в ответ получаем TCP RST (Reset), и соединение будет моментально разорвано.

Если вы используете REALITY и при этом, например, выставили SNI www.google.com, то мост на (российском!) RU-VPS вам не подойдёт. DPI увидит и заблокирует это чудо — сервер Google, который как будто располагается в России. Не стоит также использовать vk.com или домен назойливого мессенджера, который ловит своих пользователей даже на парковке. DPI может хорошо знать диапазон адресов близких к государству компаний, и блокировать сервера с такими SNI на других адресах. Так что, если хотите использовать зеркало для VPS с REALITY, используйте малоизвестные сайты в качестве «сайта-заглушки». Если же вы используете свой домен в связке с VLESS-TLS-XHTTP, то домен совсем не обязательно должен быть в зоне .ru.

Очевидно, можно попробовать на одном RU-VPS сделать зеркало для двух сайтов (добавив backend_b), а можно вообще сделать зеркало для любого трафика (hysteria2, MTProto), слегка поменяв конфигурацию stream. Такие конфигурации мы просто не тестировали — объять всё невозможно.

Тестирование

Итак, у нас есть зеркало для «сайта», который использует SNI shop.example.com и расположен на VPS по адресу vless.example.com. Само зеркало пускай расположено по адресу mirror.example.com. Чтобы проверить, что всё работает, как должно, достаточно выполнить что-то такое:

curl --resolve shop.example.com:443:192.0.2.1 https://shop.example.com

Здесь curl получает страничку сайта (или сайта-заглушки вашего VPN), не используя DNS, а сразу обращаясь на 192.0.2.1 (на самом деле здесь должен стоять IP-адрес вашего зеркала, то есть результат команды dig +short mirror.example.com). Если всё настроено правильно, curl ... вернёт HTML искомой страницы.

Кстати, если бы мы реально настраивали зеркало для сайта, то можно было бы прописать в /etc/hosts строчку вида 192.0.2.1 shop.example.com и открывать shop.example.com в браузере.

Zapret2

Если грузовик не проедет, воспользуйтесь дирижаблем

Если грузовик не проедет, воспользуйтесь дирижаблем

Что делать, если в российском дата-центре DPI не даёт установить стабильное соединение между VPS и RU-VPS? Лучшее решение — реверс-прокси, но это сложно и вообще тема отдельного разговора. Решение попроще — запутать систему DPI, чтобы она распознала соединение RU-VPS с VPS, как допустимое. Запутать можно с помощью Zapret2, установленным на RU-VPS, который модифицирует пакеты согласно особым правилам, прописанным в его конфиге. Как я уже говорил, в российских дата-центрах обычно стоит старое и не очень эффективное оборудование DPI, поэтому даже простой фрагментации TLS Client Hello может оказаться достаточно.

Чтобы вам не пришлось самостоятельно разбираться с (не всегда простой) установкой и настройкой Zapret2, мы написали скрипт, который всё упрощает — для RHEL-based дистрибутивов (Rocky, Alma, CentOS). Не стоит думать, что Zapret2 — это очень сложно. После установки его можно запустить или приостановить через systemctl start zapret2 и т. п., а правило модификации пакетов просто записано в переменной NFQWS2_OPT в файле /opt/zapret2/config и может выглядеть так:

NFQWS2_OPT="\--filter-tcp=443 --filter-l7=tls --payload=tls_client_hello --lua-desync=multidisorder:pos=1,midsld\"

Значение опций можно найти в мануале: например, --lua-desync здесь просто вызывает функцию multidisorder с параметром pos, равным 1,midsld. В этом случае пакет (TLS client hello от RU-VPS к VPS) фрагментируется разрезами после первого байта и по середине домена 2 уровня в имени хоста. Веб-сервер или Xray соберут фрагментированный пакет обратно, а вот системе DPI с ограниченными ресурсами это может оказаться не под силу, и она пропустит «непонятный» трафик.

Я не буду давать рекомендаций по настройке Zapret. У кого-то сработают настройки по-умолчанию, кто-то захочет поэкспериментировать и написать свои правила. И это правильный путь, потому что отсутствие одного и того же паттерна у всех пользователей не позволит научить DPI обнаруживать этот паттерн.

И не забывайте, что DPI анализирует не только соединения между VPS и RU-VPS, но и между пользователем и RU-VPS. Здесь может помочь Mux.Cool для VLESS-REALITY или XMUX для VLESS-XHTTP: старайтесь переиспользовать TCP-соединения. DPI очень не любит, когда их много.

Заключение

Самый быстрый способ настройки зеркала на RU-VPS — использовать наш скрипт для автоматизации процесса, который делает всё, что описано выше:

curl -L https://git.sr.ht/~atom4peace/portal/blob/trunk/install-mirror.sh -o install-mirror.shchmod +x install-mirror.shsudo ./install-mirror.sh

Финальные замечания. Для адреса зеркала мы рекомендуем использовать домен, а не IP: он будет частью ключа (uri, ссылки) для подключения к VPN, и если это IP, при его изменении придётся оповещать всех пользователей. Если задать адрес как домен (mirror.example.com), тогда то, на какой IP он указывает, можно свободно менять, ничего не меняя на стороне пользователей. Это важно, поскольку DPI часто блокирует IP адреса, но очень редко блокирует сами домены — оставляя таким образом возможность лёгкого «переезда».

Помимо обхода DPI/ТСПУ, зеркало добавляет к анонимности. Ведь трафик не расшифровывается на стороне RU-VPS (который находится в уязвимой юрисдикции), а конечный сервер вместо IP пользователя видит IP RU-VPS. Так что, если вы — Сноуден, и анонимно купили ключи какого-то VPN, то и в этом случае зеркало может вам пригодиться.

Кстати, о ключах. Если вы используете VLESS, в ключе (он же ссылка или URI) нужно всего лишь поменять адрес на адрес зеркала RU-VPS (не на Address конечной точки или SNI!): vless://1234...@mirror.example.com:443?.... Всё! Ну ладно, не всё. Если вы используете XHTTP, то в downloadSettings тоже адрес поменять не забудьте.

Ссылки

Скрипт быстрой установки и настройки Nginx в качестве «зеркала» для Rocky, Ubuntu — Readme.

Скрипт-помощник установки Zapret2 на «зеркало» (RHEL only: Rocky, Alma, CentOS) — Readme.

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