Блог Clearway

Как построить VPN-сервис, устойчивый к блокировкам оператора

Коротко

Под блокировками и локальными шатдаунами мобильные операторы РФ переводят сеть в режим белого списка: доступны только whitelisted-ресурсы (часть CDN, госсайты), а VPN, идущий напрямую к своей ноде, отваливается — трафик до дата-центра просто не маршрутизируется. Устойчивое решение — спрятать вход VPN за whitelisted CDN-edge: оператор видит обращение к «белому» CDN, а CDN проксирует трафик на вашу ноду. Рабочий транспорт для такой схемы — XHTTP в режиме packet-up через CDN; Reality и Hysteria2 напрямую под троттлингом обычно не проходят. Ключевой неочевидный момент: uplink-данные должны идти в теле запроса (uplinkDataPlacement: "body"), а не в кастомном заголовке — иначе CDN-edge не донесёт их до origin и туннель порвётся с «unexpected EOF».

Почему прямой VPN отваливается под блокировками оператора

Симптом знаком любому оператору: клиент жалуется, что vpn не работает под блокировкой — на домашнем Wi-Fi туннель поднимается, а на мобильном интернете в зоне ограничений молчит. Причина при этом не в протоколе шифрования и не в клиентском приложении.

При блокировках и локальных шатдаунах мобильные операторы РФ переводят сеть в режим *белого списка*: абоненту доступны только whitelisted-ресурсы — часть CDN, госсайты, платёжные шлюзы. Всё остальное, включая произвольные IP дата-центров, куда обращается ваша нода, просто не маршрутизируется. Это не выборочная фильтрация протокола, а отсечение по адресу назначения.

Для VPN это значит, что первый хоп трафика — от клиента к ноде — физически не доходит. Reality-инбаунд на прямом IP не отвечает, TLS-handshake не завершается; Hysteria2 по UDP не находит адресата. Никакая маскировка протокола тут не спасает: пакет не доходит до сервера в принципе.

Отсюда следует главный архитектурный вывод для устойчивого VPN под блокировками: чинить нужно не шифрование, а транспорт первого хопа. Точка входа сервиса должна физически находиться на ресурсе, который уже в белом списке оператора. Всё остальное в статье — про то, как это сделать инженерно.

Whitelist привязан к подсетям /24, а не к вашему сервису

Белый список оператора — это не «домены вообще» и не «CDN как класс», а конкретные IP-диапазоны, как правило на уровне подсетей /24. Практический вывод неприятный: у одного хостера часть /24 может быть whitelisted, а соседняя /24 того же хостера — нет. Нельзя рассуждать категориями «этот провайдер белый».

По наблюдениям на реальных SIM в зоне ограничений: подсети Selectel часто оказываются whitelisted, а cloud.ru / SberCloud — часто нет. Но это *не* гарантия и не константа — статус подсетей меняется, диапазоны добавляют и убирают. Известны случаи, когда ранее рабочая подсеть (в том числе у Yandex) переставала проходить. Поэтому единственно верный подход — проверять каждую /24 отдельно и актуально, без ставки на «этот IP белый навсегда».

ХостерЧастый статус whitelistЗамечание
SelectelЧасто whitelistedПроверять конкретную /24, не хостер целиком
cloud.ru / SberCloudЧасто НЕ whitelistedСтавить сюда origin рискованно
Yandex CloudПеременчивоЧасть диапазонов отваливалась — мониторить

Методика проверки простая: держите тестовые endpoint'ы в разных /24 и регулярно гоняйте доступность с реальных мобильных SIM в зоне ограничений. Автоматизируйте это — статус подсети сегодня и через месяц может отличаться. Строить инфраструктуру VPN-сервиса на предположении о «вечно белом» IP — прямой путь к аварии в самый неподходящий момент.

Архитектура: вход за whitelisted CDN-edge

Раз попытка сделать белой собственную ноду ненадёжна (см. выше про изменчивость /24), устойчивая схема иная: не тащить ноду в белый список, а поставить перед ней слой, который уже там находится, — CDN-edge (например, Yandex CDN). Оператор видит обращение к «белому» CDN-домену, CDN принимает трафик на своём whitelisted edge и проксирует его на ваш origin — ноду.

Логическая цепочка выглядит так:

клиент → CDN edge (whitelisted /24) → origin-нода (XHTTP inbound)

Что требуется от CDN-слоя, чтобы схема работала:

  • edge-подсети реально находятся в белых списках операторов (это опять же вопрос конкретных /24, а не бренда CDN);
  • поддержка проксирования на произвольный origin (вашу ноду);
  • корректный проброс HTTP-семантики, включая тело POST-запросов (это критично, разберём отдельно).

CDN-поддомен при такой архитектуре становится публичной точкой входа сервиса: именно он прописывается в клиентском конфиге как Address / SNI / Host, а реальный IP ноды наружу не светится.

Собирать этот слой можно самому — арендуя CDN и настраивая проксирование под свой транспорт, — либо брать whitelisted-вход как готовый сервис. Например, Clearway продаёт VPN-операторам именно «белый» вход перед их нодами (без продажи серверов), с edge-доменом *.whitechannel-x1-cdn.ru; оператор оставляет у себя ноды и панель, а на вход ставит уже настроенный whitelisted-слой. Оба пути валидны — выбор зависит от того, готова ли команда сама вести мониторинг подсетей и тюнинг транспорта.

Транспорт: почему XHTTP, а не Reality или Hysteria2

CDN-edge — это по сути HTTP(S)-прокси. Он пропускает то, что укладывается в веб-семантику, и не пропускает произвольные протоколы. Это сразу отсекает часть привычных решений.

XHTTP в режиме packet-up через CDN — рабочий подход именно потому, что укладывается в обычные HTTP-запросы: для edge это похоже на веб-трафик к «белому» ресурсу, и он проходит.

Reality и Hysteria2 напрямую под троттлингом обычно НЕ проходят. Hysteria2 работает поверх QUIC/UDP — CDN-edge такой транспорт до origin не проксирует. Reality использует собственный TLS-хендшейк, который не является валидным HTTP-обращением к edge, поэтому под режимом белого списка он не доходит. Это не значит, что Reality/Hysteria2 «плохие» — на прямом канале без ограничений они отличны; но под задачу устойчивости за CDN они не подходят.

Поэтому базовый выбор транспорта для сервиса, который должен пережить перевод оператора в белый список, — XHTTP поверх whitelisted CDN. Дальше всё внимание уходит на корректную раскладку данных запроса, потому что именно здесь ломается большинство первых попыток.

Критичный нюанс: uplink в body, а не в заголовке

Это самая частая и самая неочевидная ошибка при заведении XHTTP через CDN. XHTTP умеет размещать uplink-данные в разных частях запроса, и здесь легко выбрать нерабочий вариант.

Если положить полезную нагрузку в кастомный заголовок (например, X-Payload), CDN-edge этот заголовок до origin *не донесёт* — промежуточный edge режет или не форвардит нестандартные заголовки. В результате туннель рвётся, и в логах вы видите классическое unexpected EOF. Причём на прямом соединении (в обход CDN) та же конфигурация работает — что сбивает с толку при отладке.

Правильная раскладка для прохождения через CDN:

  • uplinkDataPlacement: "body" — полезные данные идут в теле запроса (обычное тело POST, которое CDN обязан пробросить на origin);
  • session / seq — в cookie;
  • padding — в query-параметрах.

Тогда всё, что несёт нагрузку, доходит до ноды как штатное тело запроса, а служебные метаданные едут в тех местах, которые CDN сохраняет. Держать uplink в body полезно и по второй причине — экономической: крупные POST снижают число мелких запросов (об этом ниже). Запомните правило: увидели unexpected EOF на XHTTP-через-CDN — первым делом проверяйте, что uplink идёт в body, а не в заголовке.

Настройка ноды и клиента: панели, порты, совпадение path

На стороне инфраструктуры за CDN живёт обычная нода под управлением одной из панелей — Remnawave, 3x-ui, Marzban или Hiddify. Нода держит XHTTP-инбаунд на выделенном порту (например, 25454), а CDN настроен проксировать входящий трафик на origin-IP:порт.

Клиентский конфиг при этом указывает не на IP ноды, а на CDN-поддомен: он выставляется как Address / SNI / Host. И здесь второй источник «не поднимается туннель» после истории с body: path и extra на клиенте и на ноде должны совпадать точь-в-точь. Любое расхождение — лишний слэш, другой регистр, несовпадающий параметр — и туннель не встанет, часто без внятной ошибки.

Чек-лист параметров, которые обязаны совпадать по обе стороны:

  • path;
  • host (CDN-поддомен);
  • режим размещения uplink (body) и раскладка cookie/query;
  • любые дополнительные поля extra.

Практический порядок настройки вынесен в пошаговую инструкцию ниже. Главное на этом этапе — не менять параметры транспорта в одном месте, забыв про другое: клиент и нода должны быть зеркальны.

Безопасность origin: файрвол и секрет-заголовок

Как только вы спрятали ноду за CDN, критично, чтобы origin не принимал прямые обращения мимо CDN. Иначе реальный IP ноды рано или поздно засветится (сканеры, утечка из конфига), и тогда весь смысл whitelisted-входа теряется — ноду можно заблокировать или атаковать напрямую.

Два уровня защиты, которые нужно поставить сразу:

  1. Файрвол на вход. На XHTTP-порт origin пускать трафик только с IP gateway/CDN-edge. Все остальные источники — drop. Это отсекает и сканеры, и прямые попытки достучаться до ноды.
  2. Секрет-заголовок X-Cdn-Auth. CDN добавляет к каждому проксируемому запросу секретный заголовок, а origin проверяет его наличие и значение; запросы без корректного X-Cdn-Auth отбрасываются на уровне ноды. Это защищает даже в случае, если кто-то узнал IP, но не знает секрета.

Сочетание «файрвол по IP + секрет-заголовок» даёт эшелонированную защиту: первый уровень отсекает по адресу, второй — по знанию секрета. Для инфраструктуры VPN-сервиса это не опция, а обязательный минимум — открытый origin обесценивает всю работу по whitelisted-входу.

Экономика трафика и учёт в панели

У whitelisted-CDN схемы есть своя себестоимость, и её надо считать заранее. Номинальная цена за гигабайт мало что говорит: у провайдеров с тарификацией запросов XHTTP-профиль добавляет к счёту отдельную статью, и реальная ставка получается заметно выше прайсовой. Реальная удельная стоимость выводится только из собственного биллинга: она зависит от профиля трафика абонентов и от того, тарифицирует ли провайдер запросы. Отсюда практический рычаг: крупные POST в теле запроса (тот самый body-uplink) снижают число запросов и, соответственно, стоимость. То есть выбор в пользу body — одновременно и про надёжность, и про экономику.

На стороне учёта потребления помогает панель. В Remnawave есть множитель потребления ноды и per-user лимиты. Множитель определяет, с каким коэффициентом трафик через конкретную ноду списывается против лимита пользователя:

Множитель нодыЭффект
1Трафик считается 1:1 против лимита юзера
0Трафик через ноду не списывается с лимита юзера (нода при этом работает)

Множитель 0 удобен, когда CDN-нода — это аварийный fallback на время ограничений: пользователь не должен «сгорать» по лимиту из-за вынужденного дорогого маршрута. Так вы отделяете тарифную политику от факта, что клиент временно идёт через whitelisted-вход.

Если не хочется самим держать CDN-слой, мониторить подсети и оптимизировать стоимость запросов, whitelisted-вход можно взять как сервис — например, у Clearway, где эта часть уже настроена и тарифицируется прозрачно, а оператор оставляет у себя ноды, панель и биллинг.

Границы применимости: что whitelist-CDN НЕ решает

Важно не переоценивать инструмент и не смешивать две разные задачи. «Пробить троттлинг» и «разблокировать стриминг» — это не одно и то же.

Whitelisted-CDN решает задачу *доступности*: трафик доходит до ноды даже под режимом белого списка. Но он никак не меняет *репутацию* IP, с которого нода выходит в интернет.

Сервисы вроде Kinopoisk и другого российского стриминга банят дата-центровые IP по репутации — им неважно, как трафик дошёл до вашей ноды, важно, что финальный выход идёт с адреса дата-центра. Для доступа к такому контенту нужен резидентный (residential) IP, и whitelist-CDN этого не даёт — это отдельная инженерная задача (residential-exit, отдельные каналы выхода).

Поэтому при проектировании сервиса разделяйте два слоя: устойчивость входа (пережить перевод оператора в белый список — решается whitelisted CDN + XHTTP) и репутация выхода (доступ к контенту, чувствительному к типу IP — решается резидентными адресами). Смешивать их в одном обещании клиенту — значит гарантировать то, что архитектура не покрывает.

Частые вопросы

Почему VPN работает на Wi-Fi, но не работает на мобильном интернете под блокировкой?

Под блокировками и шатдаунами мобильный оператор переводит сеть в режим белого списка: доступны только whitelisted-подсети (часть CDN, госсайты), а произвольные IP дата-центров не маршрутизируются. Ваша нода на прямом IP оказывается недостижима, поэтому туннель не поднимается именно на мобильном. Проблема в транспорте первого хопа, а не в шифровании или клиенте. Лечится тем, что вход VPN прячут за whitelisted CDN-edge.

У меня работают Reality и Hysteria2 — зачем переходить на XHTTP?

На прямом канале без ограничений они действительно отличны, но под троттлингом за CDN они обычно не проходят. Hysteria2 — это QUIC/UDP, который CDN-edge не проксирует; Reality использует свой TLS-хендшейк, не являющийся валидным HTTP-обращением к edge. CDN пропускает то, что похоже на веб-трафик, поэтому под задачу устойчивости берут XHTTP в режиме packet-up. Reality/Hysteria2 можно оставить как маршруты для сетей без режима белого списка.

Как понять, whitelisted ли подсеть моего хостера?

Белый список привязан к конкретным /24, а не к хостеру целиком, и статус меняется со временем. Держите тестовые endpoint'ы в разных подсетях и регулярно проверяйте доступность с реальных мобильных SIM в зоне ограничений. По практике Selectel часто whitelisted, cloud.ru/SberCloud — часто нет, Yandex переменчив, но это ориентир, а не гарантия. Проверяйте каждую /24 актуально и не закладывайтесь на «вечно белый» IP.

Туннель рвётся с ошибкой «unexpected EOF» — в чём причина?

Почти всегда это uplink-данные, положенные в кастомный заголовок (например, X-Payload): CDN-edge такие заголовки до origin не доносит, и туннель рвётся. Поставьте uplinkDataPlacement: "body" — полезная нагрузка должна идти в теле запроса, session/seq в cookie, padding в query. Дополнительно проверьте, что path и extra на клиенте и ноде совпадают точь-в-точь. На прямом соединении такая конфигурация может работать, что и путает при отладке.

Whitelist-CDN разблокирует Кинопоиск и российский стриминг?

Нет — это разные задачи. Whitelist-CDN решает доступность (трафик доходит до ноды под режимом белого списка), но не меняет репутацию IP выхода. Стриминг вроде Kinopoisk банит дата-центровые адреса по репутации, поэтому для такого контента нужен резидентный IP — отдельное инженерное решение. Не смешивайте «пробить троттлинг» и «разблокировать контент» в одном обещании клиенту.

Сколько стоит CDN-трафик и как снизить стоимость?

Считать по прайсу за гигабайт нельзя: CDN тарифицирует ещё и количество запросов, а XHTTP плодит много мелких обращений — итоговый счёт расходится с номиналом ₽/ГБ тем сильнее, чем интерактивнее трафик абонентов. Реальная удельная стоимость выводится только из собственного биллинга: она зависит от профиля трафика абонентов и от того, тарифицирует ли провайдер запросы. Снижается это за счёт крупных POST в теле запроса — они уменьшают число запросов. То есть раскладка uplink в body полезна и для надёжности, и для экономики.

Как защитить origin-ноду от прямых обращений мимо CDN?

Двумя уровнями. Первый — файрвол: на XHTTP-порт пускать только IP gateway/CDN-edge, всё остальное drop. Второй — секрет-заголовок X-Cdn-Auth: CDN добавляет его к каждому запросу, а origin отбрасывает запросы без корректного значения. Вместе это защищает и по адресу, и по знанию секрета, чтобы засветившийся IP нельзя было использовать напрямую.

Whitelisted-вход для вашего VPN-сервиса

Clearway даёт «белый» CDN-вход перед вашей нодой — устойчивый к троттлингу операторов. Первые 10 ГБ бесплатно, без карты.

Попробовать →