Зачем защищать origin, если перед ней уже стоит CDN
Схема «whitelisted-CDN перед нодой» работает так: клиент обращается к CDN-поддомену, который для оператора связи выглядит как «белый» ресурс, а CDN-edge проксирует трафик на вашу origin-ноду. Пока клиент идёт через edge, оператор видит обращение к разрешённому CDN, а не к VPN-ноде напрямую.
Проблема в том, что у origin-ноды есть публичный IP и открытый порт XHTTP-инбаунда. Если этот порт отвечает всем подряд, вся маскировка обесценивается:
- ноду можно найти сканированием диапазонов хостера и подтвердить активным пробингом, что это транспортный узел;
- прямой коннект в обход CDN раскрывает реальный IP, который затем блокируется отдельно от «белого» домена;
- соседний трафик и отпечаток инбаунда позволяют скоррелировать origin с CDN-поддоменом.
Поэтому защита origin cdn — это не «дополнительная опция», а обязательная часть архитектуры. Задача — сделать так, чтобы origin принимала трафик исключительно от доверенного слоя (CDN-edge / gateway) и молчала для всех остальных.
Два слоя защиты и зачем нужны оба
Правильная модель — defense in depth: сетевой фильтр (L3/L4) и прикладная аутентификация (L7). Ни один слой по отдельности не закрывает задачу полностью.
| Слой | Механизм | Против чего работает | Слабое место в одиночку |
|---|---|---|---|
| Файрвол (L3/L4) | default-deny + allowlist по IP gateway/origin-pull | сканирование, прямые коннекты с любых IP | у публичного CDN диапазоны широкие/общие — соседи по рейнджу тоже проходят фильтр |
| Секрет X-Cdn-Auth (L7) | заголовок-секрет, инжектится gateway, валидируется на origin | обращения с «разрешённых» IP, но мимо CDN | не отсекает L4-сканирование и шум на порт |
Файрвол отсекает основную массу прямого трафика по IP, но если легитимный вход идёт из большого общего пула адресов CDN, одного IP-фильтра мало. Секрет-заголовок добавляет независимый фактор: даже тот, кто попал в разрешённый диапазон, без корректного значения X-Cdn-Auth получает отказ. Секрет можно ротировать, не трогая файрвол, и наоборот.
Слой 1: файрвол ноды — вход только с IP gateway
Базовый принцип файрвола ноды vpn — запретить всё входящее по умолчанию и точечно открыть только то, что нужно. Управляющий доступ (SSH) держите отдельно и только со своих административных адресов, чтобы не заблокировать себя.
Логика правил для XHTTP-инбаунда (порт задаёте свой, например 25454):
- разрешить уже установленные соединения (established/related);
- разрешить вход на порт инбаунда только с IP-адресов gateway / origin-pull;
- запретить весь остальной входящий трафик на этот порт;
- отдельно разрешить SSH со своих IP.
Пример на nftables (адаптируйте адреса и порт под себя):
table inet filter {
chain input {
type filter hook input priority 0; policy drop;
ct state established,related accept
iif lo accept
# SSH только со своих админ-адресов
ip saddr { ВАШ_ADMIN_IP } tcp dport 22 accept
# XHTTP-инбаунд только с gateway/origin-pull
ip saddr { GATEWAY_IP_1, GATEWAY_IP_2 } tcp dport 25454 accept
}
}
Важно: список IP gateway может меняться, поэтому его нужно поддерживать в актуальном состоянии (в идеале — через управляемый набор адресов, а не хардкод в одном месте). Если вы используете whitelisted-вход как сервис, актуальный перечень доверенных IP предоставляет провайдер входа.
Слой 2: секрет-заголовок X-Cdn-Auth
X-Cdn-Auth — это общий секрет между gateway и origin. Gateway (доверенный хоп, который непосредственно проксирует на ноду) добавляет заголовок с секретным значением в каждый forwarded-запрос, а origin отклоняет любой запрос, где заголовка нет или значение неверное.
Тонкий, но принципиальный момент: клиентские кастомные заголовки публичный CDN-edge до origin не доносит — именно поэтому uplink XHTTP кладут в тело запроса, а не в заголовок. X-Cdn-Auth это не нарушает, потому что заголовок ставит не клиент, а доверенный gateway на последнем хопе перед origin. Значит, инжектить и валидировать секрет надо именно на связке gateway↔origin, а не пытаться передавать его от клиента.
Валидацию удобно вынести в reverse-proxy перед инбаундом (nginx/Caddy): без корректного заголовка отдаём 404 (а не 403 — «пусто» выглядит естественнее, чем «запрещено»), с корректным — проксируем на локальный XHTTP-порт.
location /ваш-секретный-path {
if ($http_x_cdn_auth != "ДЛИННЫЙ_СЛУЧАЙНЫЙ_СЕКРЕТ") { return 404; }
proxy_pass http://127.0.0.1:25454;
proxy_http_version 1.1;
proxy_set_header Host $host;
}
Секрет — длинное случайное значение, хранится вне репозитория и конфигов в открытом виде, ротируется по расписанию или при инцидентах.
Как это ложится на панель и XHTTP-инбаунд
Подход одинаково применим к Remnawave, 3x-ui, Marzban и Hiddify: нода держит XHTTP-инбаунд (mode packet-up) на выбранном порту, а клиентский конфиг указывает CDN-поддомен как Address/SNI/Host. Ключевое правило транспорта: path и extra на клиенте и на ноде должны совпадать точь-в-точь — иначе туннель не поднимется, и это легко спутать с проблемой безопасности.
Чек-лист совместимости слоёв защиты с рабочим туннелем:
- reverse-proxy с проверкой X-Cdn-Auth стоит перед инбаундом на том же хосте и проксирует на 127.0.0.1:порт — снаружи открыт только фронт;
- файрвол пускает на публичный порт только gateway-адреса, локальный порт инбаунда наружу не выставлен вообще;
pathв правиле reverse-proxy совпадает сpathинбаунда и клиента;- заголовок X-Cdn-Auth инжектится на gateway, а не ожидается от клиента.
Отдельно: whitelist привязан к подсетям (/24) — у одних хостеров подсети в белых списках, у других нет, и статус меняется. Selectel часто whitelisted, cloud.ru/SberCloud — часто нет. Это влияет на то, где вообще держать вход, но не отменяет защиту origin — фильтровать и аутентифицировать нужно на любой ноде, независимо от статуса её /24.
Проверка, ротация и типовые ошибки
После настройки убедитесь, что оба слоя действительно работают, а не просто присутствуют в конфиге:
- прямой запрос на публичный IP:порт origin с постороннего адреса — должен обрываться на файрволе (таймаут/отсутствие ответа);
- запрос с разрешённого IP, но без заголовка X-Cdn-Auth — должен получать 404;
- легитимный клиент через CDN — туннель поднимается штатно.
Частые ошибки: локальный порт инбаунда случайно открыт наружу мимо reverse-proxy; секрет попал в git или в клиентский конфиг; файрвол-политика осталась default-accept; SSH закрыли раньше, чем добавили свой админ-IP. Включите мониторинг дропов на файрволе и счётчик 404 на фронте — всплеск отказов часто означает сканирование или устаревший список IP gateway.
Если вы не хотите самостоятельно поддерживать whitelisted-вход и актуальный перечень доверенных IP, whitelisted-edge перед вашей нодой можно взять как сервис — например, Clearway (clear-way.pro) даёт операторам «белый» вход перед их нодами, не продавая серверов; ваша задача сводится к тому, чтобы правильно закрыть origin файрволом и секретом X-Cdn-Auth.