Блог Clearway

Безопасность origin-ноды: файрвол и секрет X-Cdn-Auth

Коротко

Origin-нода за whitelist-CDN должна быть закрыта двумя независимыми слоями. Первый — файрвол ноды VPN: политика default-deny на входящий трафик и allowlist только для IP gateway/origin-pull, чтобы порт XHTTP-инбаунда не отвечал на прямые коннекты из интернета. Второй — прикладной секрет-заголовок X-Cdn-Auth: gateway добавляет его в каждый запрос к origin, а нода отклоняет всё без корректного значения. Вместе эти слои не дают обнаружить, снять отпечаток и напрямую заблокировать ноду мимо CDN — без них смысл whitelisted-входа теряется.

Зачем защищать 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):

  1. разрешить уже установленные соединения (established/related);
  2. разрешить вход на порт инбаунда только с IP-адресов gateway / origin-pull;
  3. запретить весь остальной входящий трафик на этот порт;
  4. отдельно разрешить 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.

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

Достаточно ли одного файрвола без X-Cdn-Auth?

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

Почему X-Cdn-Auth ставит gateway, а не клиент?

Публичный CDN-edge не доносит клиентские кастомные заголовки до origin — по этой же причине uplink XHTTP кладут в тело запроса, а не в заголовок. Поэтому секрет инжектится на доверенном хопе gateway↔origin, который стоит непосредственно перед нодой. Клиент про X-Cdn-Auth ничего не знает и знать не должен.

Как валидировать заголовок, если инбаунд XHTTP сам его не проверяет?

Проверку выносят в reverse-proxy (nginx/Caddy) перед инбаундом: без корректного X-Cdn-Auth фронт отдаёт 404, с корректным — проксирует на локальный порт ноды (например 127.0.0.1:25454). Наружу открыт только фронт, а сам порт инбаунда не выставляется в интернет.

Что вернуть при неверном секрете — 403 или 404?

Предпочтительнее 404. Ответ «ничего нет по этому адресу» выглядит естественнее для случайного сканера, чем явное «доступ запрещён», и не подтверждает, что за этим хостом что-то скрыто. Всплеск 404 при этом удобно использовать как сигнал сканирования в мониторинге.

Защищает ли эта схема origin от блокировки, если IP уже засветился?

Файрвол и X-Cdn-Auth не дают обнаружить и напрямую пробить ноду мимо CDN, но не восстанавливают уже скомпрометированный IP. Если реальный адрес засветился, его меняют, а защиту настраивают на новой ноде с самого начала. Отдельно помните: whitelist привязан к /24, поэтому новый адрес проверяют на актуальный статус подсети.

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

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

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