Дисклеймер. Материал носит технический и информационный характер. Статья описывает настройку маршрутизации трафика, защищённого DNS и прокси-сервера Xray в собственной домашней сети на базе OpenWrt. Все конфигурации приведены исключительно в образовательных целях. Автор не призывает к нарушению законодательства или правил использования каких-либо сервисов.

У меня дома около пятнадцати устройств: ноутбуки, телефоны, планшет, телевизор, колонка. На каждом — свои приложения, которым нужен доступ к зарубежным сервисам. Ставить VPN-клиент на каждое устройство — мучение: телевизор вообще не умеет в VLESS, колонка — тем более, а на телефоне жены VPN «почему-то опять отключился». Хотелось одного: настроить VPN один раз на роутере — и чтобы все устройства в сети получили доступ автоматически, без установки чего-либо.

Спустя пару вечеров ковыряния в OpenWrt, nftables и Xray у меня получилась система, которая:

  • Прозрачно проксирует TCP-трафик всех устройств в сети через VLESS+Reality

  • Автоматически определяет, какой трафик гнать через VPN, а какой — напрямую (сплит-роутинг по GeoIP и доменам)

  • Шифрует DNS через AdGuard Home + DoH, попутно фильтруя рекламу и трекеры

  • Обновляет список серверов из подписки каждые 30 минут

  • Балансирует нагрузку между серверами, выбирая самый быстрый

  • Автоматически восстанавливается после сбоев (procd + watchdog + hotplug)

  • Работает стабильно неделями без перезагрузки

В этой статье — полный путь от коробочного роутера до работающей системы. С объяснениями не только «как», но и «почему». С багами, которые я нашёл в собственном конфиге. С конфигами, которые можно взять и адаптировать.


Почему VPN на роутере, а не на устройствах

Прежде чем лезть в конфиги — давайте разберёмся, зачем это вообще нужно.

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

  • Smart TV, игровые консоли, IoT-устройства не поддерживают кастомные VPN-протоколы. На телевизоре Samsung нет клиента для VLESS.

  • На каждом устройстве нужно устанавливать, настраивать и обновлять клиент. Жена/ребёнок/гость не будут разбираться с v2rayNG.

  • Клиент может отключиться, и пользователь не заметит. Трафик пойдёт напрямую.

  • На мобильных устройствах VPN-клиент жрёт батарею (постоянный туннель, обработка пакетов в userspace).

VPN на роутере решает все эти проблемы разом: любое устройство, подключённое к Wi-Fi или по кабелю, автоматически получает проксирование. Настроить нужно один раз, обновить — в одном месте. Устройства вообще не знают, что трафик проксируется — для них это обычный интернет.

Цена — более сложная настройка. Но настроить один раз роутер проще, чем поддерживать VPN на пятнадцати устройствах.


Выбор железа

Не каждый роутер подойдёт. Нужны три вещи:

  1. Поддержка OpenWrt — без неё ничего не получится. Проверяйте на openwrt.org/toh.

  2. Достаточно RAM — Xray-core написан на Go и потребляет ~100 МБ физической памяти. Плюс geodata (~85 МБ в tmpfs). Роутеры с 128 МБ RAM не потянут. Минимум — 256 МБ, комфортно — 512 МБ.

  3. Нормальный CPU — ARM Cortex-A53 или мощнее. MIPS-роутеры с 580 МГц будут задыхаться на шифровании.

Я выбрал Cudy TR3000 v1 (~4500 ₽ на момент покупки):

  • SoC: MediaTek Filogic (MT7981B), ARM Cortex-A53, 2 ядра

  • RAM: 496 МБ DDR4

  • Flash: 128 МБ NAND (overlay ~44 МБ)

  • Порты: 1× WAN 2.5GbE, 1× LAN 1GbE

  • Wi-Fi: Wi-Fi 6 (2.4 ГГц + 5 ГГц)

За свои деньги — отличное железо. Полгигабайта RAM хватает с запасом, два ядра A53 справляются с шифрованием без заметных тормозов, а 2.5GbE WAN пригодится, когда провайдер предложит тариф побыстрее.

Подводный камень с ревизиями Flash

Если покупаете Cudy TR3000 v1 — обратите внимание на серийный номер. Роутеры с серийниками от 2544 и выше (примерно с ноября 2025) идут с новым чипом NAND Flash — ESMT F50L1G41LC. Старые образы OpenWrt на них не загружаются.

Поддержка нового Flash появилась только в OpenWrt 24.10.5+. Для прошивки нужна специальная intermediate firmware от Cudy (ZIP-архив с датой 20251118 на странице загрузок), а уже потом — sysupgrade на нужную версию OpenWrt. Без промежуточной прошивки роутер уйдёт в кирпич.


Прошивка OpenWrt

Прошивка идёт в два этапа.

Этап 1: Из стоковой прошивки Cudy заходим в веб-интерфейс → «Firmware Upgrade» → загружаем bin-файл из intermediate firmware ZIP.

Этап 2: После перезагрузки появляется LuCI от intermediate OpenWrt. Через него делаем sysupgrade на финальную версию OpenWrt (в моём случае — 25.12.2).

После прошивки:

ssh root@192.168.1.1

Прошивка: OpenWrt 25.12.2 (ядро Linux 6.12.74), платформа mediatek/filogic, архитектура aarch64_cortex-a53.

Важно: начиная с OpenWrt ~25.x пакетный менеджер — apk, а не opkg. Команды установки отличаются:

apk update
apk add xray-core kmod-nft-tproxy kmod-nf-tproxy nftables-json curl

Архитектура решения

Прежде чем настраивать — поймём, что мы строим. Вот общая схема:

Устройство в сети (ноутбук, телефон, ТВ)
    │
    ├─ TCP к remote-service.example.com:443
    │   ▼
    │  Роутер: nftables (TPROXY) ──► Xray (порт 12345)
    │   перехватывает TCP                │
    │   с интерфейса br-lan              ├─ sniffing → routing
    │                                    ├─ .ru / geoip:ru → direct
    │                                    └─ остальное → VLESS+Reality
    │
    └─ DNS-запрос (UDP 53)
        ▼
       AdGuard Home (порт 53, фильтрация рекламы)
        │
        ▼
       DoH (https://1.1.1.1/dns-query) ──► HTTPS наружу
        │
        ▼
       nftables (TPROXY) ──► Xray ──► VPN-туннель

Ключевой момент: ни один plaintext DNS-запрос не покидает роутер. AdGuard Home принимает DNS от устройств, резолвит через DNS-over-HTTPS, а HTTPS-трафик к DNS-серверам проходит через TPROXY и уходит через VPN.

Почему именно эта связка

VLESS + Reality + XTLS-Vision — три технологии, работающие вместе:

VLESS — легковесный прокси-протокол. Наследник VMess из экосистемы V2Ray, но без собственного шифрования: шифрование полностью делегируется TLS. Меньше оверхед, проще протокол.

Reality — технология маскировки TLS-хендшейка. В отличие от обычного TLS, не требует реального домена и сертификата. Сервер предъявляет валидный TLS-сертификат чужого сайта (SNI), а клиент аутентифицируется через пару ключей (publicKey/shortId). Для систем DPI трафик выглядит как обычное TLS-соединение к легитимному ресурсу.

XTLS-Vision — оптимизация, при которой Xray «проваливает» внутренний TLS прямо во внешний TLS без двойного шифрования. Когда вы подключаетесь к HTTPS-сайту через VLESS+Reality, данные шифруются один раз (а не два, как при классическом прокси поверх TLS). Результат — почти нативная скорость.

Транспорт — чистый TCP (не WebSocket, не gRPC). TCP даёт минимальную задержку и не добавляет лишних заголовков.

Важно: текущая схема проксирует только TCP. UDP-трафик через TPROXY не проходит и идёт напрямую (за исключением DNS, который закрыт на уровне AdGuard Home + DoH). Это осознанное архитектурное ограничение данной версии схемы.

Почему TPROXY, а не REDIRECT

Есть два способа перехватить трафик на роутере для проксирования:

REDIRECT (DNAT) — подменяет адрес назначения на 127.0.0.1:порт. Проблема: оригинальный адрес теряется. Прокси должен восстановить его из заголовков протокола (HTTP Host, TLS SNI). Для чистого TCP без HTTP/TLS — невозможно.

TPROXY (Transparent Proxy) — передаёт пакет приложению с сохранением оригинального IP назначения. Xray видит реальный адрес, куда клиент хотел подключиться. Это критично для прозрачного проксирования: Xray должен знать destination, чтобы правильно маршрутизировать запрос.

TPROXY сложнее в настройке (нужны модули ядра, policy routing, специальные правила nftables), но он надёжнее и работает с любым TCP-трафиком.


Настройка сети: PPPoE и топология

Моя топология:

Интернет (провайдер, GPON)
    │
    ▼
Оптический терминал (режим моста / bridge)
    │ PPPoE passthrough
    ▼
Cudy TR3000 (OpenWrt) ── PPPoE клиент
    │
    ├── WAN (eth0) → pppoe-wan (MTU 1480)
    ├── LAN (eth1) → br-lan (192.168.1.0/24)
    ├── Wi-Fi 2.4 ГГц → br-lan
    └── Wi-Fi 5 ГГц → br-lan

Провайдерский терминал переведён в режим моста — PPPoE-сессию поднимает сам Cudy. Это важно: мы хотим, чтобы роутер был первым хопом и полностью контролировал трафик.

MTU = 1480 — стандарт для PPPoE (1500 − 8 байт PPP − 12 байт PPPoE). В nftables OpenWrt автоматически настраивает MSS clamping для корректной работы TCP.

DNS провайдера, полученные по PPPoE, не используются — вместо них Cloudflare (1.1.1.1) и Google (8.8.8.8).


Установка Xray и необходимых пакетов

apk update
apk add xray-core \
        kmod-nf-tproxy \
        kmod-nft-tproxy \
        kmod-nft-core \
        kmod-nft-nat \
        kmod-nft-fib \
        nftables-json \
        curl

Что зачем:

  • xray-core — собственно Xray (VLESS, Reality, маршрутизация, Observatory)

  • kmod-nf-tproxy + kmod-nft-tproxy — модули ядра для TPROXY в netfilter/nftables

  • kmod-nft-fib — FIB lookup, нужен для policy routing в nftables

  • nftables-json — утилита nft с поддержкой JSON

  • curl — для скачивания geodata и подписки


Конфиг Xray: разбор по частям

Полный конфиг состоит из нескольких секций. Разберём каждую.

Входящее соединение (Inbound)

{
  "tag": "tproxy-in",
  "port": 12345,
  "protocol": "dokodemo-door",
  "settings": {
    "network": "tcp",
    "followRedirect": true
  },
  "sniffing": {
    "enabled": true,
    "destOverride": ["http", "tls"],
    "routeOnly": true
  },
  "streamSettings": {
    "sockopt": {
      "tproxy": "tproxy"
    }
  }
}

Разберём построчно:

dokodemo-door — «дверь куда угодно». Специальный протокол Xray для приёма перенаправленного трафика. Он не требует от клиента знать о прокси — трафик приходит прозрачно через TPROXY.

followRedirect: true — использовать оригинальный адрес назначения из пакета (восстановленный TPROXY). Без этого Xray не будет знать, куда переправлять соединение.

sniffing с destOverride: ["http", "tls"] — критически важная настройка. Xray анализирует первые байты соединения: для TLS извлекает домен из SNI (Server Name Indication), для HTTP — из заголовка Host. Извлечённый домен используется для маршрутизации по доменам (geosite), а не только по IP (geoip).

routeOnly: true — ключевой нюанс. Без этого флага sniffing не только извлекает домен, но и подменяет адрес назначения в самом соединении. Это ломает некоторые сценарии: например, если сервер ожидает подключение по IP, а Xray подставляет домен. С routeOnly: true sniffing извлекает домен только для маршрутизации, а соединение устанавливается по оригинальному адресу. Проще говоря: домен используется чтобы решить «через proxy или direct», но дальше пакет идёт как есть.

Без sniffing Xray видит только IP-адрес. С ним — видит youtube.com, gosuslugi.ru, discord.com и может принимать решения на уровне доменов.

tproxy: "tproxy" — режим TPROXY (а не redirect). Сохраняет оригинальный IP назначения.

Исходящие соединения (Outbounds)

Три типа:

1. Прокси-серверы (VLESS+Reality):

{
  "tag": "proxy-1",
  "protocol": "vless",
  "settings": {
    "vnext": [{
      "address": "your-server.example.com",
      "port": 443,
      "users": [{
        "id": "ваш-uuid",
        "flow": "xtls-rprx-vision",
        "encryption": "none"
      }]
    }]
  },
  "streamSettings": {
    "network": "tcp",
    "security": "reality",
    "realitySettings": {
      "serverName": "sni-domain.example.com",
      "publicKey": "ваш-publicKey",
      "fingerprint": "chrome",
      "shortId": "1234"
    }
  }
}

Ключевые параметры:

  • address / port — адрес и порт VPN-сервера

  • id — UUID для аутентификации

  • flow: "xtls-rprx-vision" — включает XTLS-Vision (нулевое копирование для TLS-трафика)

  • serverName — SNI, который сервер будет предъявлять при TLS-хендшейке

  • publicKey / shortId — пара для Reality-аутентификации (получаете от провайдера VPN или генерируете при настройке своего сервера)

  • fingerprint: "chrome" — Xray имитирует TLS-фингерпринт Chrome, чтобы DPI не отличил его от настоящего браузера

Серверов может быть несколько. Я использую пул из 5–10 серверов для балансировки.

2. Direct — прямое соединение:

{"tag": "direct", "protocol": "freedom"}

Трафик идёт напрямую, минуя прокси. Для российских сайтов, локальных ресурсов.

3. Block — чёрная дыра:

{"tag": "block", "protocol": "blackhole"}

Дроп трафика. Можно использовать для блокировки рекламы, телеметрии и т.д.

Балансировка: Observatory + leastPing

{
  "observatory": {
    "subjectSelector": ["proxy-"],
    "probeURL": "http://cp.cloudflare.com",
    "probeInterval": "120s",
    "enableConcurrency": true
  }
}

Observatory — встроенный мониторинг здоровья серверов. Каждые 120 секунд Xray делает HTTP-запрос к cp.cloudflare.com через каждый сервер с тегом proxy-*. Мёртвые серверы исключаются из пула.

В routing настраиваем балансировщик:

{
  "balancers": [{
    "tag": "vpn-auto",
    "selector": ["proxy-"],
    "strategy": {"type": "leastPing"}
  }]
}

leastPing — выбирает сервер с наименьшей задержкой. Работает совместно с Observatory: из живых серверов берётся самый быстрый.

Нюанс с Reality и Observatory: Go HTTP-клиент, который использует Observatory для проб, не проходит Reality-хендшейк. Если ваши серверы используют SNI равный IP-адресу (без домена), пробы будут падать с ошибкой x509: cannot validate certificate for IP because it doesn't contain any IP SANs. Сервер при этом работает нормально — Xray-клиент проходит Reality-хендшейк корректно, а Observatory — нет. Если у вас есть сервер с валидным доменным SNI, он будет определяться как alive. Остальные — как dead. Балансировка фактически не работает для серверов с IP-based SNI. Это известная особенность, а не баг.

Маршрутизация (Routing) — сплит-роутинг

Вот где происходит магия:

{
  "domainStrategy": "IPIfNonMatch",
  "rules": [
    {"type": "field", "ip": ["geoip:private"], "outboundTag": "direct"},
    {"type": "field", "ip": ["geoip:ru"], "outboundTag": "direct"},
    {"type": "field", "domain": ["geosite:ru-available-only-inside"],
     "outboundTag": "direct"},
    {"type": "field",
     "domain": ["regexp:.*\\.ru$", "regexp:.*\\.su$", "regexp:.*\\.xn--p1ai$"],
     "outboundTag": "direct"},
    {"type": "field", "network": "tcp", "balancerTag": "vpn-auto"}
  ]
}

Правила применяются сверху вниз, первое совпадение побеждает:

  1. Частные IP (10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16, 127.0.0.0/8) → direct. Локальный трафик не должен уходить в прокси, иначе перестанут работать LuCI, SSH, принтеры, IoT-устройства.

  2. Российские IP (geoip:ru) → direct. Зачем проксировать трафик к Яндексу или ВК? Он и так работает. Проксирование только замедлит.

  3. Сайты, работающие только из РФ (geosite:ru-available-only-inside) → direct. Это кастомная категория из проекта runetfreedom/russia-v2ray-rules-dat: госуслуги, банки, сервисы, доступные только из РФ. Если пустить их через VPN-сервер за рубежом — они перестанут работать.

  4. Домены .ru / .su / .рфdirect. Регулярные выражения ловят все домены в этих зонах. .xn--p1ai — это punycode-кодировка .рф.

  5. Всё остальное TCPvpn-auto (балансировщик). Зарубежные сервисы (YouTube, Discord, ChatGPT, Steam, GitHub и др.) уходят через прокси-сервер.

Если какой-то сервис работает хуже через VPN (например, игровые серверы с чувствительностью к задержке), можно добавить его в direct. Пример для FaceIt:

{"type": "field",
 "domain": ["domain:faceit.com", "domain:faceitcdn.net"],
 "outboundTag": "direct"}

Правило добавляется перед финальным balancerTag: vpn-auto — порядок важен, первое совпадение побеждает.

domainStrategy: "IPIfNonMatch" — если ни одно доменное правило не сработало, Xray резолвит домен в IP и пробует сопоставить с IP-правилами. Это позволяет, например, российскому CDN с .com-доменом попасть в direct, если его IP находится в geoip:ru.

DNS: AdGuard Home + DoH

DNS — одно из самых уязвимых мест в любой VPN-конфигурации. Я прошёл через три итерации, прежде чем закрыл все утечки.

Итерация 1: dnsmasq → 1.1.1.1 (plaintext). Стандартная настройка OpenWrt. Проблема: TPROXY перехватывает только TCP, а DNS-запросы — это UDP. UDP к 1.1.1.1:53 уходил напрямую через WAN в незашифрованном виде — утечка DNS.

Итерация 2: https-dns-proxy. Пакет для OpenWrt, который принимает DNS на localhost и форвардит через DoH. Работало, но это просто прослойка без фильтрации и без удобного управления.

Итерация 3 (финальная): AdGuard Home. Полноценный DNS-сервер с фильтрацией рекламы, встроенным DoH и веб-панелью. Бонусом — блокировка рекламы и трекеров на уровне DNS для всех устройств в сети.

Установка:

# Скачиваем бинарник с GitHub
cd /tmp
curl -fsSL -o adguardhome.tar.gz \
  "https://github.com/AdguardTeam/AdGuardHome/releases/latest/download/AdGuardHome_linux_arm64.tar.gz"
tar xzf adguardhome.tar.gz
cp AdGuardHome/AdGuardHome /usr/bin/adguardhome
chmod +x /usr/bin/adguardhome
mkdir -p /etc/adguardhome

# Первоначальная настройка через веб-интерфейс
# Запустите: adguardhome -w /etc/adguardhome
# Откройте http://192.168.1.1:3000 и пройдите визард

Далее — dnsmasq нужно сдвинуть на роль чистого DHCP, чтобы он не занимал порт 53:

# dnsmasq больше не слушает DNS (только DHCP)
uci set dhcp.@dnsmasq[0].port='0'
uci commit dhcp
/etc/init.d/dnsmasq restart

Init-скрипт для AdGuard Home через procd:

#!/bin/sh /etc/rc.common
USE_PROCD=1
START=95
STOP=15

start_service() {
    procd_open_instance
    procd_set_param command /usr/bin/adguardhome -w /etc/adguardhome --no-check-update
    procd_set_param respawn 3600 5 5
    procd_set_param stdout 1
    procd_set_param stderr 1
    procd_close_instance
}

В веб-панели AdGuard Home (192.168.1.1:3000) настраиваем:

  • Upstream DNS: https://1.1.1.1/dns-query и https://8.8.8.8/dns-query

  • Чёрные списки: OISD (https://small.oisd.nl) — блокирует рекламу и трекеры

Итоговая цепочка DNS:

Устройства → AdGuard Home (порт 53, фильтрация + DoH)
                │
                ▼
            HTTPS к 1.1.1.1:443
                │
                ▼
            nftables TPROXY → Xray → VPN-туннель

Ни один plaintext DNS-запрос не покидает роутер. DNS-запросы идут только через HTTPS (DoH), защищённый транспорт. А в качестве бонуса — реклама и трекеры фильтруются на уровне DNS для всех устройств в сети, включая Smart TV и IoT.

Xray internal DNS — отдельная история. Xray использует свой DNS для внутреннего резолва при маршрутизации (domainStrategy: IPIfNonMatch):

{
  "dns": {
    "queryStrategy": "UseIPv4",
    "servers": [
      {"address": "https://1.1.1.1/dns-query", "skipFallback": false},
      {"address": "https://8.8.8.8/dns-query", "skipFallback": false}
    ]
  }
}

queryStrategy: "UseIPv4" — запрашивать только A-записи. IPv6 отключён на уровне сети (см. раздел «Известные проблемы»).


TPROXY: перехват трафика

Это ключевая часть всей системы. Разберём её детально.

Что происходит с пакетом

Вот путь пакета от устройства до Xray:

  1. Телефон отправляет TCP SYN к discord.com:443

  2. Пакет приходит на роутер через br-lan

  3. nftables (хук prerouting, priority mangle) перехватывает пакет

  4. Правило TPROXY передаёт пакет Xray на 127.0.0.1:12345, ставит fwmark 0x1

  5. Policy routing (ip rule fwmark 1 table 100) маршрутизирует помеченный пакет в локальный стек

  6. Xray получает пакет с оригинальным IP назначения

  7. Sniffing извлекает discord.com из TLS SNI

  8. Routing проверяет: discord.com не в geosite:ru, не .ru — значит, proxy

  9. Xray устанавливает VLESS+Reality соединение к VPN-серверу и передаёт данные

Модули ядра

Для работы TPROXY нужны модули ядра. Проверяем:

lsmod | grep tproxy
# Должны быть: nf_tproxy_ipv4, nft_tproxy

Если модулей нет — пакеты kmod-nf-tproxy и kmod-nft-tproxy не установлены.

Policy routing

TPROXY требует, чтобы помеченные пакеты маршрутизировались в локальный стек:

ip rule add fwmark 1 table 100
ip route add local 0.0.0.0/0 dev lo table 100

Как это работает:

  1. nftables ставит метку 0x1 (fwmark) на перехваченный пакет

  2. Ядро видит метку и ищет маршрут в таблице 100 (вместо основной таблицы)

  3. В таблице 100 единственный маршрут: local default dev loвсе адреса считаются локальными

  4. Ядро доставляет пакет в локальный стек, где его подхватывает Xray на порту 12345

Без этого помеченный пакет ушёл бы по обычному маршруту (через WAN) и не попал бы в Xray.

Важный нюанс: команда ip rule add не проверяет дубликаты. Если вызвать её несколько раз — правила накопятся. Поэтому в init-скрипте перед добавлением нужно очистить все старые правила:

while ip rule del fwmark 1 table 100 2>/dev/null; do :; done
ip rule add fwmark 1 table 100

Правила nftables

В первой версии я добавлял правила по одному через nft add rule. Это работало, но было хрупко: легко забыть правило, порядок мог перепутаться. В финальной версии я генерирую nft-файл и загружаю его атомарно через nft -f:

table ip xray {
    chain prerouting {
        type filter hook prerouting priority mangle; policy accept;

        # 1. Bypass приватных подсетей
        ip daddr { 127.0.0.0/8, 10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16 } return

        # 2. Bypass трафика самого Xray (защита от петли)
        meta mark 0xff return

        # 3. Bypass IP VPN-серверов (автоматически из конфига)
        ip daddr { 1.2.3.4, 5.6.7.8, 9.10.11.12 } return

        # 4. DHCP — не трогать
        udp dport { 67, 68 } return

        # 5. QUIC drop — блокируем UDP 443 с LAN
        iifname "br-lan" udp dport 443 drop

        # 6. TCP TPROXY — перехват и маркировка
        iifname "br-lan" meta l4proto tcp tproxy to 127.0.0.1:12345
            meta mark set 1 accept
    }
}

Разберём каждое правило:

Bypass приватных IP (правило 1) — трафик к локальным адресам не должен попадать в прокси. Без этого перестанут работать LuCI (192.168.1.1), SSH, принтеры, NAS, IoT-устройства — всё, что живёт в локальной сети.

Bypass Xray-трафика через meta mark (правило 2)ключевая защита от петли. Xray ставит SO_MARK (meta mark 0xff) на свои исходящие соединения к VPN-серверам. Это правило пропускает такие пакеты мимо TPROXY. Без него Xray попытается подключиться к серверу → пакет перехватится nftables → отправится обратно в Xray → бесконечный цикл, который съест CPU и положит сеть.

Bypass серверных IP (правило 3) — дополнительная подстраховка. IP-адреса VPN-серверов извлекаются автоматически из config.json при каждом запуске init-скрипта:

extract_server_ips() {
    grep -o '"address"[[:space:]]*:[[:space:]]*"[^"]*"' "$CONF" 2>/dev/null | \
        sed 's/.*"\([^"]*\)"$/\1/' | \
        grep -E '^[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+$' | \
        sort -u
}

Функция парсит JSON grep’ом (jq на OpenWrt нет), вытаскивает все значения "address" и фильтрует только IPv4. Результат подставляется в nft-файл. Никакого ручного хардкода — обновилась подписка, рестартнул сервис, bypass-список обновился автоматически.

Зачем два уровня защиты от петли (meta mark + bypass IP)? meta mark 0xff — основная защита, но она работает только для соединений, которые Xray уже установил. При начальном DNS-резолве адреса сервера пакет может не иметь метки. Bypass по IP — страховка на этот случай.

Дроп QUIC (правило 5) — техническое ограничение текущей схемы. QUIC (HTTP/3) — это UDP на порту 443. В текущей конфигурации Xray проксирует только TCP, поэтому QUIC не маршрутизируется через прокси. Если не дропнуть QUIC, произойдёт следующее:

  1. Браузер пытается подключиться к сервису по QUIC (UDP 443)

  2. UDP не перехватывается TPROXY (нет правила для UDP)

  3. Пакет уходит напрямую через WAN, минуя прокси-сервер

  4. Трафик уходит напрямую, минуя заданные правила маршрутизации

Дроп QUIC заставляет браузеры fallback’иться на TCP (HTTPS), который уже перехватывается TPROXY. HTTP/3 при этом недоступен, но маршрутизация трафика работает корректно.

Альтернатива — проксировать UDP тоже. Для этого нужно добавить UDP-правило в nftables и UDP inbound в Xray. Это сложнее и менее стабильно (UDP через прокси работает хуже TCP), но возможно.

TCP TPROXY (правило 6) — собственно перехват. Только с интерфейса br-lan (LAN), только TCP. Пакет передаётся Xray на 127.0.0.1:12345, получает метку 0x1 для policy routing.

iifname "br-lan" — перехватываем только трафик с LAN. Трафик самого роутера (output) и трафик с WAN не трогаем.


Geodata: откуда берутся списки

Xray использует два файла для маршрутизации:

geoip.dat — база GeoIP (привязка IP-адресов к странам). Стандартная от V2Fly. Содержит категорию geoip:ru — все IP-блоки, зарегистрированные в России.

geosite.dat — база доменов. Я использую кастомную от проекта runetfreedom/russia-v2ray-rules-dat. Она содержит категорию ru-available-only-inside — домены, которые работают только из РФ: госуслуги, банки, некоторые госсервисы. Если пустить их через зарубежный VPN — они вернут ошибку или откажут в обслуживании.

Файлы скачиваются при запуске и хранятся в /tmp/xray-assets/ (tmpfs):

ASSET_DIR="/tmp/xray-assets"
GEOIP_URL="https://cdn.jsdelivr.net/gh/v2fly/geoip@release/geoip.dat"
GEOSITE_URL="https://raw.githubusercontent.com/runetfreedom/russia-v2ray-rules-dat/release/geosite.dat"

mkdir -p "$ASSET_DIR"
curl -fsSL -o "$ASSET_DIR/geoip.dat" "$GEOIP_URL"
curl -fsSL -o "$ASSET_DIR/geosite.dat" "$GEOSITE_URL"

О потреблении памяти: geoip.dat ~20 МБ, geosite.dat ~65 МБ — суммарно ~85 МБ в tmpfs. Это значительная часть tmpfs (у меня 242 МБ). На роутерах с 256 МБ RAM это может стать проблемой. Если памяти мало — можно хранить geodata на overlay (Flash), но это увеличит время запуска из-за медленного NAND.


Init-скрипт: собираем всё вместе

Init-скрипт использует procd — систему управления процессами OpenWrt. procd мониторит Xray и автоматически перезапускает его при падении. Также скрипт следит за изменениями конфига — если config.json обновился (например, скриптом подписки), procd перезапустит Xray.

#!/bin/sh /etc/rc.common

USE_PROCD=1
START=99
STOP=10

CONF="/etc/xray/config.json"
ASSET_DIR="/tmp/xray-assets"
GEOIP_URL="https://cdn.jsdelivr.net/gh/v2fly/geoip@release/geoip.dat"
GEOSITE_URL="https://raw.githubusercontent.com/runetfreedom/russia-v2ray-rules-dat/release/geosite.dat"

download_assets() {
    mkdir -p "$ASSET_DIR"
    [ -s "$ASSET_DIR/geosite.dat" ] && [ -s "$ASSET_DIR/geoip.dat" ] && return 0
    local i=0
    while [ "$i" -lt 3 ]; do
        i=$((i + 1))
        curl -fsSL --connect-timeout 10 --max-time 120 \
            -o "$ASSET_DIR/geoip.dat" "$GEOIP_URL" && \
        curl -fsSL --connect-timeout 10 --max-time 120 \
            -o "$ASSET_DIR/geosite.dat" "$GEOSITE_URL" && \
        [ -s "$ASSET_DIR/geosite.dat" ] && [ -s "$ASSET_DIR/geoip.dat" ] && return 0
        sleep 2
    done
    return 1
}

# Автоизвлечение IP VPN-серверов из конфига для bypass в nftables
extract_server_ips() {
    grep -o '"address"[[:space:]]*:[[:space:]]*"[^"]*"' "$CONF" 2>/dev/null | \
        sed 's/.*"\([^"]*\)"$/\1/' | \
        grep -E '^[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+$' | \
        sort -u
}

setup_network() {
    # Policy routing (очистка дублей + добавление)
    while ip rule del fwmark 1 table 100 2>/dev/null; do :; done
    ip route flush table 100 2>/dev/null
    ip rule add fwmark 1 table 100
    ip route add local 0.0.0.0/0 dev lo table 100

    # Собираем bypass-список IP серверов из конфига
    local bypass_ips
    bypass_ips=$(extract_server_ips | tr '\n' ',' | sed 's/,$//')

    # Удаляем старую таблицу
    nft delete table ip xray 2>/dev/null

    # Генерируем nft-файл
    local nft_file="/tmp/xray.nft"
    cat > "$nft_file" << NFT
table ip xray {
    chain prerouting {
        type filter hook prerouting priority mangle; policy accept;
        ip daddr { 127.0.0.0/8, 10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16 } return
        meta mark 0xff return
NFT

    # Bypass IP серверов (только если есть IP-адреса в конфиге)
    [ -n "$bypass_ips" ] && \
        echo "        ip daddr { $bypass_ips } return" >> "$nft_file"

    cat >> "$nft_file" << 'NFT'
        udp dport { 67, 68 } return
        iifname "br-lan" udp dport 443 drop
        iifname "br-lan" meta l4proto tcp tproxy to 127.0.0.1:12345 meta mark set 1 accept
    }
}
NFT

    # Атомарная загрузка правил
    nft -f "$nft_file" || {
        logger -t xray-tproxy "nftables apply failed"
        rm -f "$nft_file"
        return 1
    }
    rm -f "$nft_file"
    logger -t xray-tproxy "Network ready (bypass: ${bypass_ips:-none})"
}

start_service() {
    download_assets || {
        logger -t xray-tproxy "Geo assets download failed"
        return 1
    }

    setup_network || return 1

    procd_open_instance "xray"
    procd_set_param command /usr/bin/xray run -c "$CONF"
    procd_set_param env XRAY_LOCATION_ASSET="$ASSET_DIR"
    procd_set_param stdout 1
    procd_set_param stderr 1
    procd_set_param respawn 3600 5 5
    procd_set_param limits core="unlimited"
    procd_set_param limits nofile="1000000 1000000"
    procd_set_param file "$CONF"
    procd_close_instance
}

stop_service() {
    nft delete table ip xray 2>/dev/null
    while ip rule del fwmark 1 table 100 2>/dev/null; do :; done
    ip route flush table 100 2>/dev/null
    logger -t xray-tproxy "Stopped, network cleaned"
}

service_triggers() {
    procd_add_reload_trigger "xray"
}

Разберём ключевые решения:

USE_PROCD=1 — вместо простого start()/stop() используем procd API. Это даёт автоматический перезапуск при падении, мониторинг процесса и корректную обработку сигналов.

procd_set_param respawn 3600 5 5 — если Xray упадёт, procd перезапустит его. Параметры: порог (3600 секунд), задержка (5 секунд), максимум рестартов (5). Если за час Xray упал 5 раз — procd перестанет перезапускать (значит, проблема серьёзнее).

procd_set_param file "$CONF" — procd следит за файлом конфига. Когда скрипт обновления подписки перезаписывает config.json, procd перезапустит Xray.

extract_server_ips() — парсит config.json, вытаскивает все IPv4-адреса из полей "address" и подставляет в bypass-список nftables. Никакого ручного хардкода IP — обновилась подписка, рестартнул сервис, bypass обновился.

Атомарная загрузка nftables — правила записываются в файл и загружаются одной командой nft -f. Если файл содержит ошибку — ни одно правило не применится (атомарность). При поштучном nft add rule ошибка в середине оставит таблицу в полуприменённом состоянии.

setup_network() очищает дублиwhile ip rule del ... ; do :; done удаляет все накопившиеся правила перед добавлением нового. В ранней версии скрипта я этого не делал, и за 3 дня аптайма накопилось 14 дублирующих ip rule — теперь этого не происходит.

Активируем:

chmod +x /etc/init.d/xray-tproxy
/etc/init.d/xray-tproxy enable
/etc/init.d/xray-tproxy start

Важно: если у вас стоит стоковый init-скрипт xray (из пакета xray-core) — отключите его, чтобы не было конфликта:

/etc/init.d/xray disable

Автоматическое обновление подписки

Большинство VPN-провайдеров, поддерживающих VLESS, предоставляют подписку — URL, который возвращает base64-закодированный список серверов в формате vless://.... Список серверов может меняться: добавляются новые, удаляются устаревшие. Чтобы не обновлять конфиг вручную, я написал скрипт автообновления.

У меня подписок две — от разных провайдеров. Это и отказоустойчивость (один лёг — второй работает), и больше серверов для балансировки. Скрипт обрабатывает обе, и если одна недоступна — это не фатальная ошибка.

Проблема: на OpenWrt нет base64

BusyBox на OpenWrt — минимальная среда. Нет base64, нет jq, нет python. А подписка приходит в base64. Что делать?

Писать base64-декодер на awk. Да, это возможно, и это самый хакерский кусок всей системы:

awk '
BEGIN {
  a="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"
  for(i=1;i<=length(a);i++) m[substr(a,i,1)]=i-1
}
{
  for(i=1;i<=length($0);i++){
    c=substr($0,i,1)
    if(c~/[A-Za-z0-9+\/=]/) s=s c
  }
}
END {
  for(i=1;i<=length(s);i+=4){
    c1=substr(s,i,1); c2=substr(s,i+1,1)
    c3=substr(s,i+2,1); c4=substr(s,i+3,1)
    v1=m[c1]; v2=m[c2]
    printf "%c", int(v1*4+int(v2/16))
    if(c3=="=") break
    v3=m[c3]
    printf "%c", int((v2%16)*16+int(v3/4))
    if(c4=="=") continue
    v4=m[c4]
    printf "%c", int((v3%4)*64+v4)
  }
}' input.b64 > output.txt

20 строк awk — полноценный base64-декодер, работающий на любом BusyBox.

Архитектура скрипта

Скрипт вырос из простого «скачай-распарси-подставь» в полноценный инструмент на ~200 строк ash. Общий алгоритм:

  1. Fetch — скачиваем подписки по URL (две штуки, независимо)

  2. Decode — base64 → текст (по одной VLESS-ссылке на строку)

  3. Filter — отбрасываем специализированные ноды (выделенные для отдельных сервисов или типов сетей)

  4. Parse — из каждой ссылки извлекаем параметры и генерируем Xray JSON outbound

  5. Build — собираем конфиг из шаблона, подставляя список серверов

  6. Validatexray run -test -c config.json проверяет синтаксис

  7. Apply — бэкап старого конфига → замена → перезапуск

Ключевое отличие от первой версии — ничего не захардкожено. Ни один сервер не вшит в скрипт. Оба провайдера парсятся динамически. Теги серверов — proxy-provider1-1..N и proxy-provider2-1..N — все матчатся селектором proxy-* в балансировщике.

Универсальный парсер vless_to_json()

Самая полезная часть скрипта — функция, которая принимает vless:// URL и генерирует готовый Xray JSON outbound:

vless_to_json() {
  local url="$1" tag="$2"

  # Извлекаем базовые параметры из URL
  local uuid host port
  uuid=$(echo "$url" | sed -n 's|vless://\([^@]*\)@.*|\1|p')
  host=$(echo "$url" | sed -n 's|.*@\([^:]*\):.*|\1|p')
  port=$(echo "$url" | sed -n 's|.*:\([0-9]*\).*|\1|p')

  # Извлекаем параметры из query string
  local pbk sni sid tp flow fp
  pbk=$(echo "$url" | sed -n 's|.*pbk=\([^&]*\).*|\1|p')
  sni=$(echo "$url" | sed -n 's|.*sni=\([^&]*\).*|\1|p')
  sid=$(echo "$url" | sed -n 's|.*sid=\([^&]*\).*|\1|p')
  tp=$(echo "$url"  | sed -n 's|.*type=\([^&]*\).*|\1|p')
  flow=$(echo "$url" | sed -n 's|.*flow=\([^&]*\).*|\1|p')
  fp=$(echo "$url"  | sed -n 's|.*fp=\([^&]*\).*|\1|p')

  # Значения по умолчанию
  [ -z "$sni" ] && sni="$host"
  [ -z "$tp" ] && tp="tcp"
  [ -z "$fp" ] && fp="chrome"

  # flow — только для TCP (XTLS-Vision не работает с XHTTP)
  local flow_line=""
  if [ "$tp" = "tcp" ] && [ -n "$flow" ]; then
    flow_line="\"flow\": \"$flow\","
  fi

  # shortId — опционален
  local sid_line=""
  [ -n "$sid" ] && sid_line="\"shortId\": \"$sid\","

  # Транспорт: TCP или XHTTP
  local network_block
  if [ "$tp" = "xhttp" ]; then
    local path
    path=$(echo "$url" | sed -n 's|.*path=\([^&]*\).*|\1|p' | \
           sed 's|%2F|/|g')
    network_block="\"network\": \"xhttp\",
      \"xhttpSettings\": {\"path\": \"${path:-/}\"}"
  else
    network_block="\"network\": \"tcp\""
  fi

  cat <<EOJ
{
  "tag": "$tag",
  "protocol": "vless",
  "settings": {
    "vnext": [{
      "address": "$host",
      "port": $port,
      "users": [{
        "id": "$uuid",
        $flow_line
        "encryption": "none"
      }]
    }]
  },
  "streamSettings": {
    $network_block,
    "security": "reality",
    "realitySettings": {
      "serverName": "$sni",
      "publicKey": "$pbk",
      $sid_line
      "fingerprint": "$fp"
    }
  }
}
EOJ
}

Функция автоматически определяет:

  • Транспорт — TCP или XHTTP (из параметра type). Некоторые провайдеры дают запасные конфиги на XHTTP с маскировкой SNI под известные домены — альтернативный транспорт для случаев, когда TCP недоступен или работает нестабильно.

  • flow — XTLS-Vision включается только для TCP (для XHTTP он не поддерживается и вызовет ошибку).

  • shortId — опционален, подставляется только если есть.

  • SNI — берётся из параметра sni, а если его нет — используется host.

Один парсер для любых VLESS-ссылок, независимо от провайдера.

Фильтрация нод: unicode-ловушка

Подписки содержат специализированные ноды: выделенные под отдельные сервисы, для AI-сервисов, для определённых типов мобильных сетей. Для роутера они не нужны — нам нужны только общие серверы.

Наивный подход — grep -i "youtube" — не работает. Почему? Потому что некоторые провайдеры используют юникодные small caps в именах нод: ʏᴏᴜᴛᴜʙᴇ вместо youtube, ᴀɪ вместо ai, ʀᴜ вместо ru. Обычный grep -i не ловит эти символы — они находятся в другом диапазоне Unicode и не являются uppercase/lowercase-парами латинских букв.

Решение — отдельная функция с явными проверками:

should_skip() {
  local name="$1"
  # Обычные ASCII-паттерны
  echo "$name" | grep -Eiq "youtube|russia|россия|special|mobile" && return 0
  # Unicode small caps (VPN-провайдеры маскируют имена нод)
  echo "$name" | grep -q "ʏᴏᴜᴛᴜʙᴇ" && return 0
  echo "$name" | grep -q "ᴀɪ" && return 0
  echo "$name" | grep -q "ᴩоᴄᴄия" && return 0
  echo "$name" | grep -q "ʀᴜ" && return 0
  return 1
}

Первая строка ловит стандартные паттерны. Остальные — побитовые проверки на конкретные юникодные строки. Неизящно, но надёжно.

Ещё один нюанс: провайдер может включать в имя ноды разделитель | (например, ? ?? Финляндия | HK). Ранняя версия скрипта отбрасывала все такие ноды — а это были все серверы одного из провайдеров. Баг, который тихо убивал половину серверов из пула. Сейчас | в имени не является критерием для пропуска.

Зачем вообще нужен фильтр специализированных нод? Потому что подписки содержат серверы, предназначенные для конкретных задач и типов сетей. Такие ноды могут иметь неочевидные особенности маршрутизации (например, использовать российские IP и SNI) и могут нарушить правила раздельной маршрутизации для роутера.

Двойной fetch и отказоустойчивость

Скрипт обрабатывает обе подписки независимо:

# Провайдер 1
if fetch_and_decode "$URL_PROVIDER1" "$TMP/provider1.dec"; then
  parse_nodes "$TMP/provider1.dec" "proxy-p1" "$TMP/outbounds.json"
fi

# Провайдер 2
if fetch_and_decode "$URL_PROVIDER2" "$TMP/provider2.dec"; then
  parse_nodes "$TMP/provider2.dec" "proxy-p2" "$TMP/outbounds.json"
fi

# Должен быть хотя бы один живой сервер
[ "$TOTAL_COUNT" -gt 0 ] || { log "FAIL: no nodes from any provider"; exit 1; }

Если один провайдер недоступен — его серверы не попадают в конфиг, но серверы второго провайдера всё равно подтягиваются. Фатальная ошибка — только если оба источника пусты.

Теги серверов — proxy-p1-1, proxy-p1-2, proxy-p2-1, proxy-p2-2 и т.д. Все начинаются с proxy-, что матчится селектором балансировщика proxy-*. Xray не знает и не должен знать, от какого провайдера сервер — он просто выбирает самый быстрый.

Шаблон конфига

Скрипт использует шаблон /etc/xray/config.template.json, в котором все секции статичны, кроме outbounds:

{
  "log": {"loglevel": "warning"},
  "dns": {
    "queryStrategy": "UseIPv4",
    "servers": [
      {"address": "https://1.1.1.1/dns-query", "skipFallback": false},
      {"address": "https://8.8.8.8/dns-query", "skipFallback": false}
    ]
  },
  "observatory": {
    "subjectSelector": ["proxy-"],
    "probeURL": "http://cp.cloudflare.com",
    "probeInterval": "120s",
    "enableConcurrency": true
  },
  "inbounds": [
    {
      "tag": "tproxy-in",
      "port": 12345,
      "protocol": "dokodemo-door",
      "settings": {"network": "tcp", "followRedirect": true},
      "sniffing": {
        "enabled": true,
        "destOverride": ["http", "tls"],
        "routeOnly": true
      },
      "streamSettings": {"sockopt": {"tproxy": "tproxy"}}
    }
  ],
  "outbounds": [
    __OUTBOUNDS__,
    {"tag": "direct", "protocol": "freedom"},
    {"tag": "block", "protocol": "blackhole"}
  ],
  "routing": {
    "domainStrategy": "IPIfNonMatch",
    "balancers": [
      {"tag": "vpn-auto", "selector": ["proxy-"],
       "strategy": {"type": "leastPing"}}
    ],
    "rules": [
      {"type": "field", "ip": ["geoip:private"], "outboundTag": "direct"},
      {"type": "field", "ip": ["geoip:ru"], "outboundTag": "direct"},
      {"type": "field", "domain": ["geosite:ru-available-only-inside"],
       "outboundTag": "direct"},
      {"type": "field",
       "domain": ["regexp:.*\\.ru$", "regexp:.*\\.su$",
                   "regexp:.*\\.xn--p1ai$"],
       "outboundTag": "direct"},
      {"type": "field", "network": "tcp", "balancerTag": "vpn-auto"}
    ]
  }
}

Плейсхолдер __OUTBOUNDS__ заменяется скриптом на JSON-массив серверов. Это позволяет обновлять список серверов, не трогая логику маршрутизации.

Cron

crontab -e
# Обновление подписки каждые 30 минут (лог в файл)
*/30 * * * * /usr/bin/xray-update-safe "https://your-subscription-url" >> /tmp/xray-update.log 2>&1

# Ротация лога обновлений каждые 6 часов
0 */6 * * * : > /tmp/xray-update.log

# Watchdog: если Xray мёртв — чистим nftables и перезапускаем
* * * * * pidof xray >/dev/null || { nft delete table ip xray 2>/dev/null; /etc/init.d/xray-tproxy start; logger -t xray-watchdog "Xray was dead, restarted"; }

Лог обновлений пишется в отдельный файл /tmp/xray-update.log — удобно для отладки (cat /tmp/xray-update.log), и не смешивается с основным логом Xray.

Watchdog-строка — страховка от ситуации, когда procd сдался. procd_set_param respawn 3600 5 5 перезапускает Xray максимум 5 раз за час, после чего сдаётся. Если Xray упал 6-й раз — procd молчит, а nftables-правила остаются: пакеты маркируются, уходят в TPROXY, но Xray их не принимает → чёрная дыра, интернет мёртв. Watchdog в cron проверяет каждую минуту: Xray жив? Если нет — сначала удаляет таблицу nftables (восстанавливая прямой доступ), потом перезапускает сервис.

Hotplug: восстановление после network restart

Есть неочевидная проблема: /etc/init.d/network restart сбрасывает policy routing (ip rule, ip route). При этом nftables-правила TPROXY остаются. Результат: пакеты маркируются fwmark 0x1, но маршрут для этой метки не существует — пакеты дропаются. Интернет мёртв, и без ручного xray-tproxy restart не починится.

Решение — hotplug-скрипт, который автоматически восстанавливает Xray при поднятии WAN:

cat > /etc/hotplug.d/iface/99-xray-fix << 'EOF'
#!/bin/sh
[ "$ACTION" = "ifup" ] && [ "$INTERFACE" = "wan" ] && {
    ip rule show | grep -q "fwmark 0x1" || /etc/init.d/xray-tproxy restart
}
EOF
chmod +x /etc/hotplug.d/iface/99-xray-fix

Логика: когда WAN поднимается (ifup), скрипт проверяет, есть ли ip rule для fwmark. Если нет — policy routing потерялся, нужен рестарт xray-tproxy. Если правило на месте — ничего не делаем (обычный переподключение PPPoE не трогает ip rule).


Мониторинг и отладка

Проверка что всё работает

# Xray запущен?
ps | grep xray

# TPROXY правила на месте?
nft list table ip xray

# Policy routing настроен?
ip rule show | grep fwmark
ip route show table 100

# Тест: соединение идёт через VPN?
curl -x socks5://127.0.0.1:12345 https://ifconfig.me
# Или с клиентского устройства:
# curl https://ifconfig.me — должен показать IP VPN-сервера, а не ваш

Логи Xray

Xray пишет лог в stdout/stderr, который procd перенаправляет в системный лог. Смотрим через logread:

logread -e xray

Лог обновлений подписки — отдельно:

cat /tmp/xray-update.log

Типичные сообщения:

# Сервер жив
app/observatory: probe proxy-1 succeeded

# Сервер мёртв
app/observatory: the outbound proxy-3 is dead

# Sniffing извлёк домен
transport/internet/tcp: dialing TCP to tcp:discord.com:443

# Маршрут → direct
app/router: taking detour [direct] for [tcp:yandex.ru:443]

# Маршрут → proxy
app/router: taking detour [vpn-auto] for [tcp:youtube.com:443]

Типичные проблемы

«Нет интернета после включения TPROXY» — скорее всего Xray не запущен или упал. Проверьте ps | grep xray. Без Xray пакеты уходят в TPROXY и пропадают — никто их не обрабатывает.

«Локальная сеть недоступна» — забыли bypass для приватных подсетей в nftables. Трафик к 192.168.1.1 уходит в Xray вместо локального стека.

«VPN-серверы не подключаются, петля» — IP VPN-сервера не добавлен в bypass. Трафик к серверу перехватывается → Xray пытается подключиться → перехватывается → бесконечный цикл.

«Госуслуги / Сбер не работают» — проверьте, что geosite.dat — кастомная (с ru-available-only-inside), а не стандартная. Стандартная не содержит эту категорию.

«YouTube тормозит» — возможно, браузер использует QUIC (UDP 443), который не проходит через TPROXY и идёт напрямую. Проверьте nft list table ip xray | grep "udp dport 443".


Потребление ресурсов

На моём Cudy TR3000 (2× Cortex-A53, 496 МБ RAM):

  • CPU: ~0.08 load average в простое. Xray нагружает CPU только при активном трафике, и даже при стриминге 4K-видео нагрузка не превышает 30–40% одного ядра (благодаря XTLS-Vision — нет двойного шифрования).

  • RAM: ~108 МБ используется. Xray Go runtime резервирует ~1.3 ГБ виртуальной памяти, но физически потребляет ~100 МБ.

  • tmpfs: ~165 МБ из 242 МБ (geoip.dat ~20 МБ, geosite.dat ~65 МБ — основные потребители).

  • Flash overlay: 26 МБ из 44 МБ.

  • Uptime: стабильная работа неделями без перезагрузок.


Известные проблемы и пути решения

Не буду притворяться, что система идеальна. Вот оставшиеся несовершенства:

1. Observatory не работает с Reality (IP-based SNI)

Серверы, где SNI = IP-адрес, определяются как dead. Балансировка leastPing фактически не работает для них.

Решение: использовать серверы с доменным SNI. Или смириться и использовать один надёжный сервер с доменным SNI как «якорь» балансировки.

2. Geodata в tmpfs

~85 МБ geodata загружаются в tmpfs при каждом запуске. Если роутер перезагрузится без интернета (WAN не поднялся) — geodata не скачаются, Xray не запустится.

Решение: хранить копию geodata на overlay (Flash), обновлять периодически через cron, использовать как fallback если скачать не удалось:

# В download_assets()
if ! curl ... -o "$ASSET_DIR/geoip.dat" ...; then
    cp /etc/xray/geoip.dat.bak "$ASSET_DIR/geoip.dat" 2>/dev/null
fi

Что уже починено

Система прошла через несколько итераций. Вот проблемы, которые были найдены и решены:

  • DNS-утечка (UDP) — самый коварный баг. dnsmasq форвардил DNS на 1.1.1.1 по UDP, а TPROXY перехватывает только TCP. Plaintext UDP-запросы уходили напрямую в незашифрованном виде — утечка DNS. Промежуточно решено через https-dns-proxy, финально — заменой на AdGuard Home с DoH upstream. Теперь DNS идёт только через HTTPS, который проксируется TPROXY.

  • IPv6-утечка — ULA-префикс был активен, wan6 поднят, dnsmasq возвращал AAAA-записи. Современные устройства предпочитают IPv6, и весь этот трафик шёл мимо прокси-сервера. IPv6 даёт больше нюансов и требует отдельной проверки на утечки. В текущей схеме проще отключить IPv6 полностью, чем добавлять отдельные правила перехвата для него:

uci set network.globals.ula_prefix=''
uci set network.wan6.auto='0'
uci set dhcp.@dnsmasq[0].filter_aaaa='1'
uci commit network && uci commit dhcp
/etc/init.d/network restart && /etc/init.d/dnsmasq restart
  • Потеря ip rule после network restartnetwork restart сбрасывает policy routing, но nftables остаётся → чёрная дыра. Решено hotplug-скриптом /etc/hotplug.d/iface/99-xray-fix, который восстанавливает Xray при поднятии WAN.

  • procd сдаётся после 5 паденийrespawn 3600 5 5 перестаёт перезапускать Xray, nftables остаётся, интернет мёртв. Решено watchdog в cron, который каждую минуту проверяет pidof xray и при необходимости чистит nftables + перезапускает.

  • Нет автоперезапуска — Xray запускался через & (фоновый процесс) без мониторинга. Упал — лежит. Решено переходом на procd с respawn.

  • Дубли ip rule — при каждом рестарте добавлялось новое правило, старые не удалялись. За 3 дня — 14 дублей. Решено циклом while ip rule del ... ; do :; done перед добавлением.

  • Хардкод IP серверов в bypass — при обновлении подписки нужно было вручную обновлять IP в nftables. Решено автоизвлечением IP из config.json через extract_server_ips().

  • Расхождение nftables-конфигов — файл tproxy.nft содержал правильные правила, но init-скрипт добавлял правила вручную с другой логикой. Решено переходом на единый nft-файл, генерируемый скриптом.

Заметка о безопасности: User-Agent скрипта обновления

В апреле 2026 года вышло исследование, показавшее критическую уязвимость в популярных VLESS-клиентах для Android/iOS: открытый SOCKS5 без авторизации на localhost позволяет шпионским модулям в других приложениях обнаружить выходной IP прокси, а в некоторых клиентах — дампить полные конфиги через Xray API.

Для роутерного решения эта уязвимость не актуальна: на роутере нет шпионских приложений, нет SOCKS5 на localhost для сканирования, нет других приложений вообще. Но есть нюанс: некоторые провайдеры подписок начали блокировать запросы с User-Agent уязвимых клиентов. Если ваш скрипт обновления использует User-Agent конкретного мобильного клиента — смените его на что-нибудь нейтральное (XrayRouter/1.0, curl/8.0 и т.д.), чтобы не попасть под фильтр.


Итог: что получилось

Все устройства в домашней сети — от ноутбука до телевизора — автоматически получают прозрачную маршрутизацию трафика через прокси-сервер. Российские сайты работают напрямую, без дополнительной задержки. DNS-запросы защищены через DoH и фильтруются от рекламы через AdGuard Home. IPv6 отключён для исключения утечек трафика. Серверы обновляются из двух подписок каждые 30 минут. Watchdog и hotplug-хук гарантируют, что система восстановится после любого сбоя.

На всё ушло несколько вечеров: прошивка, конфиг Xray и nftables, скрипт автообновления, затем итерации по закрытию утечек DNS и IPv6, добавление AdGuard Home, hotplug и watchdog. Каждая итерация — реакция на реальную проблему, обнаруженную в продакшене.

Самое сложное во всей настройке — не конфиги (их можно скопировать), а понимание, почему каждый элемент нужен. Без этого любая нештатная ситуация (новый сервер, смена провайдера, падение Xray) превратится в чёрную коробку. Надеюсь, эта статья объяснила не только «как», но и «почему».

Если что-то не заработало или есть вопросы по конкретным нюансам — спрашивайте в комментариях. Особенно если у вас другое железо или провайдер с DPI построже.

Комментарии (95)


  1. Last26
    10.04.2026 09:06

    Пробовал похожую тему с tproxy..правда не vless а другой протокол..в итоге были две проблемы ..перехват трафика в raw и зависающие сессии .. Потом перешёл с tproxy на tun..и стало лучше )


    1. ShyDamn Автор
      10.04.2026 09:06

      У меня схема отличается - я не использую raw вообще, у меня TPROXY через nftables в mangle/prerouting + policy routing.

      Сессии не висят: Xray стабильно принимает соединения и маршрутизирует (и direct, и proxy), это видно по логам.

      Так что, скорее всего, у тебя проблема была в конкретной реализации (raw/маршрутизация/loop), а не в TPROXY как таковом :)


      1. Last26
        10.04.2026 09:06

        Вполне возможно) реализация была адовая просто)


    1. mukca
      10.04.2026 09:06

      Да боже мой...
      Статья очередной нейрослоп.
      Сравнения, противопоставления, выводы, разрыв повествования, отсутствие структуры, ни одного скриншота.
      А куда ставиться пакеты? А по какому пути лежат конфиги? Где команды для редактирования их?
      А "Проверка что всё работает" вообще гениален. Никто и никогда в виде "кода" выкладывать набор команд, да еще с комментариями не будет.

      ps пишу под первым комментовм чтобы мой комментарий увидели побольше читающих.


      1. ShyDamn Автор
        10.04.2026 09:06

        Если бы вы дочитали статью, то увидели бы и пути, и команды: /etc/xray/config.json, /etc/init.d/xray-tproxy, /etc/hotplug.d/iface/99-xray-fix, /usr/bin/adguardhome, nft list table ip xray, ip rule show - это всё там есть.

        Скриншоты для SSH/nftables-конфига тут не обязательны: важнее рабочие команды и понятная схема. Если нужен формат «одна кнопка и всё само», то это просто не тот жанр статьи.


        1. mukca
          10.04.2026 09:06

          Ага части конфига /etc/xray/config.json размазаны рандомно по всей статьи, а путь конфига указан только в файле Init скрипта
          А по ответу мне кажется что и ИИ бот отвечает :)
          ps А как на вашем роуторе пожарить яичницу? :)


          1. ShyDamn Автор
            10.04.2026 09:06

            Путь к конфигу есть в статье, просто он не вынесен в отдельный “список путей”, а показан там, где это нужно по смыслу.

            Статья - не справочник по OpenWrt, а разбор рабочей схемы. Поэтому конфиг, init-скрипт, nft и диагностика разбросаны по соответствующим разделам, а не сведены в один блок ради галочки.

            Про “ИИ-бота” удобно писать, когда не хочется читать внимательнее.

            А яичницу на этом роутере, к сожалению, не жарю :)


            1. mukca
              10.04.2026 09:06

              Я воспользовался поиском и путь где находлится config.json указан ровно в одном месте, в инит скрипте.
              А настройки которорые в нем содержатся начинаются с самого начала статьи, с заголовка "Конфиг Xray: разбор по частям", сразу как установили пакеты, но узнаем где же этот конфиг находится мы из скрипта запуска, все очень логично


      1. Mingun
        10.04.2026 09:06

        Боже мой, какие нафиг скриншоты (ещё бы в ворд их предложили вставить)??? Текст и только текст, который легко скопировать, сохранить, переписать, а не голимый скриншот непонятно чего непонятного качества, который завтра на хостинге протухнет и ищи-свищи, чего там было «выставляем галочки как на скриншоте».


        1. mukca
          10.04.2026 09:06

          Вы настраивали на роуторе что-нибудь похожее как в статье?
          Вот возьмите статью и читая ее и пользуюсь информацией из нее попробуйте настроить.
          Не прочитать и положить на полочку, а прочитать и по статье настроить.
          Сразу поймете что с статьей чтото не так, информации нахватает, огромная часть информации о настройки, о действияфх с роутерами, с конфигами, почему это так просто отсутствует.
          Я думаю на этапе прошивки возникнут вопросы (на точно таком же роуторе), так как роуторы Cude шьются на openwrt через переходную прошивку насколько помню.

           


          1. ShyDamn Автор
            10.04.2026 09:06

            В статье есть отдельный блок про прошивку, включая переходную firmware и sysupgrade, как раз под Cudy TR3000.

            Если на этом этапе «возникают вопросы», значит дело не в отсутствии информации, а в том, как читается статья.


          1. Mingun
            10.04.2026 09:06

            Ну так и скажите конкретно, чего не хватает. А не серчайте на отсутствие скриншотов. У меня нет роутера, который бы я мог настроить по этой статье, но как появится, я ей воспользуюсь.

            Про переходную прошивку в статье сказано. И какую надо брать тоже. Вы бы сами ещё раз её внимательно перечитали, м? Но статья не про прошивку роутера, ей простительно. А то мы докатимся до того, что будем обсуждать, где: на Озоне, в DNS или на каком-нибудь Али этот роутер лучше купить.


            1. ShyDamn Автор
              10.04.2026 09:06

              Я на ВБ заказывал, если интересно :)

              Но да, сделаю вторую часть: разложу спорные моменты подробнее и учту комментарии.

              Изначально статья была про саму схему (TPROXY + Xray + routing), а не про полный гайд «с нуля до продакшена», поэтому какие-то вещи оставил за рамками.


              1. hw_store
                10.04.2026 09:06

                ...а ещё они почему-то обожают вместо запятых ставить тире. Иногда это выглядит уместно. Видимо "учитель" так делал, а теперь они друг у друга нахватались и стали все так делать, в большинстве случаев даже игнорируя официальные правила русского языка.


              1. True_form
                10.04.2026 09:06

                Вопрос по xray: сейчас проходит через dpi? Я настраивал awg тоннель до роутера, из роутера в ssh или warp на заграничный vps. Работает на всех опсосах (пока что). Но! Вот кажется, что можно сделать пошустрее, но бодаться с dpi не охото


                1. ShyDamn Автор
                  10.04.2026 09:06

                  Да, с Xray (VLESS + Reality) сейчас в целом нормально проходит через DPI, если сервер и конфиг адекватные.

                  Твоя схема с awg/ssh/warp - это несколько слоёв туннелей, поэтому она более устойчива, но добавляет задержку и оверхед. Xray более прямой вариант, обычно быстрее, но сильнее зависит от того, как именно настроен сервер и маскировка.


                1. 0ka
                  10.04.2026 09:06

                  вообще ничего непонятно, при чём тут ssh и warp? если клиент будет на роутере, то очень вряд ли будет быстрее чем awg


  1. 0ka
    10.04.2026 09:06

    REDIRECT (DNAT) — подменяет адрес назначения на 127.0.0.1:порт. Проблема: оригинальный адрес теряется. 

    Вообще-то нет. Точно не знаю как оно работает, но проходит любой TCP трафик. Ядро как-то сохраняет оригинальный адрес и передаёт xray. Для UDP трафика почему-то не работает и нужен tproxy


    1. ShyDamn Автор
      10.04.2026 09:06

      Он не «сохраняется сам».

      При REDIRECT адрес реально теряется (DNAT на 127.0.0.1), а то, что у тебя работает с TCP - это либо sniffing (SNI/Host), либо SO_ORIGINAL_DST. Это не универсально.

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


      1. 0ka
        10.04.2026 09:06

        Да нету у меня сниффинга

        iptables -t nat -A PREROUTING -p tcp -j REDIRECT --to-ports 10

        sing-box redirect inbound видит айпи адрес любого tcp протокола, xray тоже, нормальная универсальность. Если нужен только TCP то настраивается в 10 раз проще чем tproxy (можно прямо из веб интерфейса openwrt добавить правило).


        1. 0ka
          10.04.2026 09:06

          *


          1. ShyDamn Автор
            10.04.2026 09:06

            Скорее всего у тебя это через SO_ORIGINAL_DST работает, а не потому что ядро само «сохраняет адрес».

            С REDIRECT это в целом ок для TCP, но всё равно не совсем универсально и зависит от реализации. Поэтому TPROXY обычно используют, когда нужна более предсказуемая прозрачная схема.


  1. 0ka
    10.04.2026 09:06

    "network": "tcp"

    Я думаю 90% читателей будут ожидать проксирование и UDP трафика в том числе. А тут такая подстава.

    Кстати QUIC блочат даже при рабочем UDP в целях увеличения производительности vless:

    1. Xtls не делает двойное шифрование https трафика, т.е. quic трафик будет жрать ресурсы проца на шифрование

    2. Quic имеет свой congestion control, ваш прокси сервер тоже, получается два congestion control что даёт производительность ниже

    3. UDP over TCP потребляет больше ресурсов проца


    1. ShyDamn Автор
      10.04.2026 09:06

      Тут только TCP сделан специально.

      QUIC (UDP 443) я режу, чтобы весь трафик уходил в TCP и нормально проксировался. UDP в прозрачной схеме - отдельный уровень сложности.

      Так что это не «подстава», а осознанное решение :)


      1. 0ka
        10.04.2026 09:06

        я и так из статьи понял зачем вы блокируете quic. здесь речь не о вас, а о читателях.

        напишите в самом верху что весь UDP трафик пропущен напрямую.

        и вообще, вроде бы не хватает 1 строчки nft чтобы UDP проксился, но точно не уверен


        1. 0ka
          10.04.2026 09:06

          *


          1. ShyDamn Автор
            10.04.2026 09:06

            Да, хорошее замечание, можно это явнее подчеркнуть, позднее сверху помечу про UDP, спасибо :)

            Но также замечу, что там не совсем «1 строчка»: для UDP нужно ещё inbound в Xray, отдельные правила и нюансы с тем же QUIC. Я поэтому решил не усложнять и оставить только TCP.


            1. 0ka
              10.04.2026 09:06

              для UDP нужно ещё inbound в Xray

              network: tcp поменять на network: tcp,udp или вообще убрать строчку, по дефолту вроде оба слушает. quic как блокировался так пусть и блокируется (ниже писал почему)


              1. ShyDamn Автор
                10.04.2026 09:06

                Спасибо, подумаю над этим


  1. 0ka
    10.04.2026 09:06

    Транспорт — чистый TCP (не WebSocket, не gRPC). TCP даёт минимальную задержку и не добавляет лишних заголовков.

    а так же даёт потенциальную сибирскую блокировку (моя последняя статья), стоило бы добавить что лучше иметь в запасе gRPC траспорт (из-за встроенного мультиплекса).

    кстати TCP транспорт так-то не даёт минимальную задержку, тратится много времени на tcp и tls хендшейки, если впска где-то далеко в США, то это очень заметно (ещё одна причина быть фанатом протоколов с мультиплексом, хотя там есть минусы)


    1. ShyDamn Автор
      10.04.2026 09:06

      Да, тут согласен - TCP не серебряная пуля, особенно если сервер далеко, хендшейки начинают чувствоваться.

      Я скорее имел в виду, что TCP без WS/gRPC даёт минимальный оверхед в рамках самой схемы (без лишних слоёв), а не вообще минимальную задержку в любых условиях.

      Про запасной транспорт тоже хорошее замечание :)
      gRPC/WS с мультиплексом действительно могут быть полезны как fallback, особенно если начинаются блокировки или высокий RTT, спасибо, добавлю это в схему позднее


  1. ShyDamn Автор
    10.04.2026 09:06

    P.S. из практики: основная проблема, с которой столкнулся - забивающийся overlay.

    В итоге вынес Xray и AdGuard в /tmp, а при загрузке разворачиваю их из архивов через init-скрипт и запускаю. Так удалось разгрузить overlay и всё работает стабильно.


  1. 12345egor
    10.04.2026 09:06

    Простите, не могу не написать)

    телевизор вообще не умеет в VLESS

    умеет

    колонка — тем более

    Даже представить не могу в какой ситуации колонке понадобится обход блокировок, у них закрытые экосистемы сервисов.


    1. ShyDamn Автор
      10.04.2026 09:06

      У меня Яндекс Алиса с выходом на TV, которая поддерживает ютуб. Но при этом с момента блокировки ютуба она не смогла загрузить мне ни одного видео толком. Теперь может :)


      1. 12345egor
        10.04.2026 09:06

        Ааа. Тогда логично. Я подумал у вас Станция без видео и ТВ/приставка на Android TV.


  1. marus_space
    10.04.2026 09:06

    Судя по роутингу, запрос 2ip.io выдает адрес впн, и любой подключенный к роутеру телефон с максом может спокойно слить ip. Лучше роутинг "перевернуть" и geosite:ru-blocked заворачивать в впн, а по умолчанию сделать путь в директ. Хотя лучше не лениться и прописать категории нужных сайтов руками (geosite:youtube, geosite:telegram и т.д.). Вдруг список geosite:ru-blocked просто бесконечно разрастается и старые адреса никто не проверяет, и ушлый ркнщик найдет уже неиспользуемый домен из этого списка и запилит на нем ip чекер? А приложения встроят к себе обращения на него, и поймают всех с простым роутингом geosite:ru-blocked -> vpn. Он конечно может и новый домен создать и заблокировать, но вроде при добавлении в geosite:ru-blocked есть какая-то модерация сообществом? И свежий ноунейм сайт надеюсь не пропустят


    1. ShyDamn Автор
      10.04.2026 09:06

      Да, замечание справедливое :)

      У меня сейчас действительно схема proxy-by-default, так что любой домен, который не вынесен в direct, может уйти через VPN - в том числе и 2ip-подобные сервисы. Для более строгого privacy-first подхода логичнее делать default direct и отдельно перечислять нужные категории.

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


    1. Aleksei_7bc
      10.04.2026 09:06

      Отличный гайд. Но я не могу пока придумать решение против активного пробинга по разным ip со стороны МАХ и т.п. И я пока перестал маршрутизировать трафик на роутере, пока не найдется решение. Поставил ip MAX в блок, домашних научил выключать wifi, если нужен MAX


      1. orthoxerox
        10.04.2026 09:06

        У меня все телефоны, на которых стоит МАХ, имеют фиксированный IP и маршрутизируются по нему в direct. А следующее правило - блокировка МАХ, как и у вас.


        1. alex_1065
          10.04.2026 09:06

          на компьютере в браузере сайты озона-валбериса и т.д. не открываете? Там тоже может присутствовать несложный js который сделает пару запросов к сайтам и отправит оба ip в базу пользователей квн.


  1. boXed
    10.04.2026 09:06

    Мужчина, можно у вас роутер со всем этим купить? :)

    Предложение, кстати, на полном серьёзе.

    Это как-то разом можно накатить на новый роутер или каждый раз руками ставить?

    А то готовая бизнес-идея.

    Понятно - до первого не штатного случая (там разберемся).


    1. ShyDamn Автор
      10.04.2026 09:06

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


      1. everstrike
        10.04.2026 09:06

        Тоже приобрел бы готовый комплект -)


        1. ivvi
          10.04.2026 09:06

          Я за вами в очереди)


    1. iamezhik
      10.04.2026 09:06

      На Авито подобного добра навалом.


      1. boXed
        10.04.2026 09:06

        Вот не поверите - реально всю текущую неделю именно на Авито искал роутер под работу блок-сайтов.

        Пообщался, наверное, с 12-15 продавцами.

        Из них 2-ое в теме (на мой взгляд).

        Один прям достаточно глубоко в теме (но явно проигрывает автору этой статьи).

        Остальные продавцы - полное ощущение - по какому-то гайду прошивку накатили и дальше кто во что горазд.

        Глубокого понимания нет (я сам для себя тоже и WireGuard и Amnesia по "букварю" поставлю - но глубокого понимания нюансов у меня нет).

        Я к чему - предложений такого уровня (читаю и перевожу со словарем) - много и я сам примерно так и могу.

        А вот некая система с подходом как выше - такое редко вижу.

        Со своей стороны я это называю "профессионализм" и готов за это платить.

        При этом и у меня (а главное - у автора) есть понимание, что может у него и не идеальное Retail-издание, но он слушает и слышит - готов следовать дельным советам.

        Поэтому - я за такое бы платил.

        Ничем не хуже скилла "заменить смеситель", "установить розетки/УЗО", "сварить кофе вкусно".

        Точно стоит денег.


        1. alamer
          10.04.2026 09:06

          Мне иногда кажется, что мы не на техническом сайте, на котором следят специалисты в айти.

          Одни не в состоянии найти one-line аналоги, вторые городят свои нейровелосипеды.


      1. savant_a
        10.04.2026 09:06

        Конкретно за модель Cudy TR3000, которая упоминается в статье, есть даже целые видео-обзоры, к которым приложены ссылки на готовые прошивки. Прошить сам аппарат несложно, хоть и какие-то знания для этого нужны. Другое дело, насколько эти прошивки в плане ошибок обкатаны. Я списывался с человеком, который сейчас допиливает OpenWrt под этот аппарат. Недавно он мне написал, что выловил следующий баг - "при отсутствие интернета V2rayA перезапускается и пишет лог, после заполнения лога, роутер выходит в режим аварии на низком уровне, переходит в режим “кирпич”.

        По этому готовые решения не всегда подойдут. Если в них нет ошибок (а если есть?), то можно покупать готовое.

        Сам жду TR3000. Буду пробовать. Если с vless на уровне роутера сложно, то для того же СмартТВ можно прикрутить ТВ-приставку на Android и туда накатить клиенты V2ray (и много чего ещё) и раздавать по кабелю уже с неё на ТВ. Я так около года смотрел Тюбик (без рекламы, бесплатно) на СмартТВ. На ПК тоже есть свои клиенты, на свою довольно-таки старую Windows 7, да ещё с поломанным .NET Framework, смог найти клиент. Недавно купил Mesh-систему Cudy M1200, в ней протокол WG работает из коробки, СмартТВ показывает и таким образом, даже меньше кнопок нажимать перед просмотром. Приставка теперь не нужна. Решил с роутером эксперимент провести.


        1. ShyDamn Автор
          10.04.2026 09:06

          Да, готовые решения есть, но там главный вопрос - насколько они понятны и поддерживаемы.

          Я как раз хотел уйти от истории «прошил по гайду и надеюсь, что всё ок», потому что при любом нестандартном баге (как ты описал) без понимания дальше уже тяжело.

          А так да, через приставку или клиенты на устройствах тоже вполне рабочий вариант, просто менее централизованный.


    1. venanen
      10.04.2026 09:06

      Идете на озон, заказываете Routerich AX3000, он там 5к стоил. В нем уже с завода настроен WARP и Opera Turbo, стоит Zapret 2 и ZeroBlock. AWG, кстати, тоже.
      Все, можно смотреть, хоть и медленно.
      Если хотите не медленно - покупаете у той же amnesia премиум-ключ - смотрите быстро. Все в веб-интерфейсе, с подробными инструкциями и консультациями в чате в телеге.

      Если еще чуть времени потратить, купить VPS, поставить на него awg 2.0 одной командой, все.
      Хочется больше контроля - 3X-UI (одной командой в консоли), настроить vless + XTLS + Reality прямо в вебе, получить ключ, вставить в роутер и все.

      P.S. Маршрутизация из коробки в ZeroBlock (который форк podkop, как я понял) - тоже есть.


      1. boXed
        10.04.2026 09:06

        Вот спасибо за совет.

        Вот в этом и дело - вы так круто и понятно расписали, что даже ради интереса хочется купить этот роутер.

        А вот ни в описании этого продукта, ни в тысяча коментах - ничего подобного нет.

        Максимум - поддержка молодцы, смотрю теперь ютуб (что не сильно отличает этот продукт от других в глазах покупателя).

        Так что снова приходим к важности понятного продукта, который закрывает потребности/боли человека.


      1. bankir1980
        10.04.2026 09:06

        Купил я на likevps впску с 3x-ui сборкой, ни черта она не работает снаружи из РФ. Банальный ssh даже блочится, только ping и работает. Попросил знакомого из-за рубежа, он смог в ssh. Так что рабочая vps - тот ещё квест.


    1. nixtonixto
      10.04.2026 09:06

      Или как принято в виндовс-среде - скачать 1 файл инсталлятора, проставить галочки, Далее, Далее, Готово. 21-й век давно, уже даже ИИ создан, а здесь всё как в 90-е, какие-то магические заклинания в командной строке, недоступные большинству пользователей интернета.


      1. boXed
        10.04.2026 09:06

        Всё так.

        Или готовый роутер (с обновлениями) или понятный инсталятор all-in-one ))


        1. sundmoon
          10.04.2026 09:06

          Ну почему же? Вполне можно иишечке подбросить эту статью как исходный пункт для размышлений и вдохновения. В частности, оно и инсталлятор сгенерить может - но, как по мне, всё же лучше несколько скриптов с ручным контролем.


  1. Mingun
    10.04.2026 09:06

    Возникло несколько вопросов после прочтения статьи:

    1. Правильно ли я понимаю, что списки серверов VPN и базы данных geoip и geosite скачиваются не через этот самый VPN, а потому провайдер спокойно видит, что вы их качаете, и, соответственно, может понять, что сейчас его будут дурить?

    2. Вроде как IPv6 более лучший протокол (все предпочитают использовать его из статьи), но вы сознательно его выключаете. В чём с ним сложность, почему нельзя всё то же самое настроить и для IPv6?

    3. Будут ли работать торренты в этой схеме? Там же peer-to-peer соединения, всё будет заворачиваться на VPN всё равно?


    1. ShyDamn Автор
      10.04.2026 09:06

      Хорошие вопросы :)

      1. Да, всё это качается напрямую. Провайдер может это видеть, но это обычный HTTPS-трафик и редкие запросы.

      2. IPv6 можно настроить, но там сильно больше нюансов и легко словить утечку - я поэтому его просто отключил.

      3. Торренты будут работать, но пойдут через VPN и без входящих соединений раздача может быть хуже.


      1. Mingun
        10.04.2026 09:06

        По пункту 2: вот интересно было бы почитать, что это за «куча нюансов» и как «легко словить утечку». И почему через IPv4 нет этой «кучи» и «тяжело».


      1. Mingun
        10.04.2026 09:06

        Кстати, забыл ещё спросить про категории в geoip и geosite. Как и откуда вообще узнать, какие существуют? Как решить практические вопросы: в какую категорию попадает этот домен / IP (ну или хотя бы более простой: попадает ли этот домен / IP в эту категорию)? Как добавить домен / IP в категорию? Как создать свою категорию?


        1. ShyDamn Автор
          10.04.2026 09:06

          По IPv6 - да, там реально больше нюансов. В Xray при IPIfNonMatch / IPOnDemand домен может резолвиться и в IPv4, и в IPv6, а встроенный DNS работает с A и AAAA-записями, так что IPv6 нужно отдельно учитывать в DNS, firewall и маршрутизации; OpenWrt прямо отдельно предупреждает про отключение ISP prefix delegation, чтобы не ловить IPv6 leaks на VPN-клиенте. Я поэтому и упростил схему, отключив IPv6, а не потому что он “плохой” сам по себе.

          По geoip/geosite: geosite - это список доменных наборов из репозитория domain-list-community, где каждый файл в data/ превращается в правило вида geosite:filename, а внутри используются include:, domain:, full:, keyword:, regexp: и атрибуты вроде @ads / @cn; geoip - отдельный проект с месячными релизами и CLI для сборки своих баз. Практически проще всего смотреть исходные списки и plain-экспорт (dlc.dat_plain.yml, текстовый вывод geoip CLI), а свою категорию делать либо отдельным файлом в data/, либо через CLI-сборку своих geoip-файлов.


  1. zbot
    10.04.2026 09:06

    ну и после всех настроек роутера надо запустить в локальную сеть маха чтобы он разобрался куда роутер валит трафик к заблокированному контенту.

    Впрочем теперь этим будет заниматься не только мах но и валберисы с озонами и т.д. Хотя никто не мешает в js наших маркетплейсов внести небольшой код который будет пробивать внешний (зарубежный) ip и на компьютере при открытии сайта в браузере.

    те кто считает что раздельное маршрутизирование спасает могут поэкспериментировать - https://api.ipify.org или https://ipinfo.io/json


  1. activa
    10.04.2026 09:06

    Очень круто и многое уже продумано, респект :) На гитлаб или около того планируете выкладывать? У вас сейчас изложено в порядке как думали и проблемы решали - это познавательно, погружает в контекст и будет здорово итоговый вариант для развертывания ) И да, про ipv6 имхо все-таки стоит проработать, через него обходы зачастую эффективнее работают.


    1. ShyDamn Автор
      10.04.2026 09:06

      Спасибо! Да, сейчас хочу сначала довести всё до более-менее стабильного состояния, а потом уже, возможно, выложить в более удобном для развёртывания виде, пока продумываю, как будет лучше.

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


  1. electrodummy
    10.04.2026 09:06

    Насколько безопасно пропускать трафик банков и госуслуг через кастомные прошивки наподобие OpenWRT ?


    1. zbot
      10.04.2026 09:06

      скажу банальность, но самое безопасное сейчас не пользоваться вообще никакими vpn, openwrt и полностью погрузится в заботливые обьятия ркн. Все остальное в той или иной мере не безопасно.

      а так из боле-менее безопасного - что-то "xray" в режиме прокси, firefox с расширением "firefox мульти-аккаунт контейнер" и настройка проксирования для разных контейнеров. Ну а затем нужные сайты закреплять за выбранными контейнерами.


  1. Sorgarus
    10.04.2026 09:06

    Всё это красиво конечно но влес уже устаревший и его активно блочат.


    1. GeorgeII
      10.04.2026 09:06

      Что есть из современного?


  1. aab137
    10.04.2026 09:06

    Если я не ошибаюсь, все же в каком-то отношении использование локальных средств обхода выигрывает по безопасности за счет возможности настройки раздельной маршрутизации для разных приложений, конечно, при условии отсутствия уязвимостей SOCKS5 без авторизации. Проблема может возникнуть, например, если у жены / ребенка / гостя на устройстве есть вредоносное ПО, которое может пингануть ресурсы из реестра РКН, обнаружить факт использования средств обхода и скомпрометировать ваш ВПН. Хотя эту проблему можно частично решить с помощью каскадного ВПН (так, по крайней мере, впн сервер не попадет под подозрение) и польностью - с помощью белого списка устройств, трафик с которых нужно проксировать


  1. mejor-correo
    10.04.2026 09:06

    А как обстоят дела с поставщиками подписок, у которых требуется передача HWID клиента? В этом решении получится получить подписку или каким-то образом передать HWID? Понимаю, что из подписки можно выдернуть ключи для каждого сервера, а потом их добавить, но подписка все же удобнее, тк могут добавляться, убираться серверы.


    1. ShyDamn Автор
      10.04.2026 09:06

      Да, у меня подписка как раз тянется с передачей HWID - через заголовок x-hwid в скрипте обновления.

      То есть если провайдеру нужен HWID, это уже поддержано на уровне fetch-скрипта; роутерная схема тут не мешает.


  1. FrvctvL
    10.04.2026 09:06

    Насколько хорошим решением будет развернуть все это дело на NanoPi?


    1. ShyDamn Автор
      10.04.2026 09:06

      Да, если речь про R5S/R6S - это уже хороший вариант под такую схему. У R5S нормальный запас по CPU и 2/4 ГБ RAM, у R6S запас ещё больше.

      Для моего стека критична именно память: Xray + geodata + AdGuard уже ощутимо едят RAM, так что 1-2 ГБ хватит спокойно.


  1. Arhimag
    10.04.2026 09:06

    Удивительно, как у вас Filogic 820 тянет с такой низкой загрузкой tproxy + pppoe. Xiaomi AX3000T с таким же процем и активным tproxy дает 350-400 мбит в лучшем случае с маской распределения нагрузки на все ядра (правда mihomo, не xray, но суть одна). Поэтому 2.5 гбит wan с таким userspace-проксированием невозможно, да даже гигабит нереально. А под торрентами этот проц ложится даже на 300 мбитах. С таким конфигом лучше перейти на x86.


    1. ShyDamn Автор
      10.04.2026 09:06

      Да, тут всё очень зависит от конкретной схемы и профиля нагрузки. У меня не “просто tproxy”, а TCP-only стек с Xray+Reality, QUIC отдельно режется, и в статье я как раз показывал, что роутер живёт без заметной нагрузки в простое и без проблем тянет обычный домашний трафик.

      Под торренты и сотни коротких соединений картина, конечно, хуже - это уже совсем другой сценарий. Но из этого не следует, что Filogic 820 “в принципе не тянет” такую схему: в моём случае узкое место сейчас скорее не CPU, а память/overlay и обвязка.


    1. 0ka
      10.04.2026 09:06

      По моим тестам самый производительный впн на ax3000t это ядерный ipsec с шифром aes. Выдавал 900мбит и потреблял всего 40% проца если правильно помню. На втором месте openvpn с новым DCO (которого пока нет в репе openwrt), выдаёт около 600мбит. На 3 месте wireguard, выдаёт около 300мбит. (Возможно немного ошибся, точно не помню)


  1. nikulin_krd
    10.04.2026 09:06

    ТС, у меня один вопрос! А зачем так усложнять? Есть же пакет V2RayA и все можно сделать без отдельных скриптов и прочего и главное - в 10 раз легче


    1. ShyDamn Автор
      10.04.2026 09:06

      Можно и через V2RayA, если нужен простой рабочий вариант.

      Я же специально пошёл в более сложную схему: чтобы был контроль над роутингом, DNS, QUIC, автоперезапуском, обновлением подписок и чтобы было понятно, что где ломается. Для домашнего роутера это уже не только “включить прокси”, а отдельная система.


      1. nikulin_krd
        10.04.2026 09:06

        Так это все настраивается в v2RayA)


  1. andrex77
    10.04.2026 09:06

    > Flash: 128 МБ NAND (overlay ~44 МБ)

    В сети есть мануалы прошивки c u-boot, после чего доступно около 95 Мб, и хватит места для ADH, Podkop, ещё и останется с запасом.

    Кстати, этот роутер есть с флешем 256 Мб(китайская версия, либо перепаивают память), есть ещё Cudy TR30 - это то-же самое что и TR 3000 (вроде как коллаборация Cudy и Wildberries)


    1. ShyDamn Автор
      10.04.2026 09:06

      Да, вариант с 256 МБ и расширением overlay встречается, но это уже не базовая TR3000 из коробки, а отдельная ревизия/модификация. У Cudy для TR3000 официально указаны 128 МБ flash, а в OpenWrt Wiki есть отдельные techdata и для 128 МБ, и для 256 МБ версии.

      Поэтому в статье я бы оставил цифры для обычной ревизии, а про 256 МБ и U-Boot-правки упомянул бы как про отдельный вариант. TR30, кстати, у Cudy в каталоге идёт как отдельная модель, а не просто другое название TR3000.


      1. moviq
        10.04.2026 09:06

        Кстати, если уж упоминули подкоп, почему не подкоп?


        1. ShyDamn Автор
          10.04.2026 09:06

          Подкоп - это когда установил и пользуешься, а тут ты сам собираешь схему понимаешь, как всё работает и что чинить, если что.

          Ниже уже отвечал на этот вопрос


        1. andrex77
          10.04.2026 09:06

          потому что без u-boot прошивки для Podkop и AGH не хватит места на флеше


          1. 0ka
            10.04.2026 09:06

            Agh не нужен


  1. develmax
    10.04.2026 09:06

    Обращу внимание, что бывают домены, например 17.doramalive.la, контент которых работает только из рф, либо блочится некоторыми странами, поэтому полезно иметь еще список исключений доменов и ip, которые отправлять напрямую в ру-сегнент. Список доменов нужен потому, что на одном серваке могут находиться разные сайты, какие-то работают только из ру сети, несмотря на то, что находятся на сервере вне ру сети, а какие-то размещены могут быть там же, но блочить уже клиентов из ру сети.


    1. ShyDamn Автор
      10.04.2026 09:06

      Да, это хороший аргумент, поэтому я как раз и добавлял исключения по доменам, а не только по IP.

      На практике одного geoip:ru мало: сервис может жить на внешнем IP, но при этом нормально работать только из ру-сегмента, либо наоборот - блокировать клиентов из РФ. Так что доменные исключения тут реально нужны.


  1. Shvved
    10.04.2026 09:06

    Приветствую. Я конечно не эксперт. Но чем это принципиально отличается от какого нибудь подкопа, который устанавливается и настраивается минут за 10?


    1. ShyDamn Автор
      10.04.2026 09:06

      Разница в контроле.

      Подкоп - это когда установил и пользуешься, а тут ты сам собираешь схему и понимаешь, как всё работает и что чинить, если что.


      1. Zloymopga
        10.04.2026 09:06

        Простите, но у меня все еще нет ответа на вопрос комментатора выше. Потому что я уже 1.5 года сижу на podkop и все прекрасно контролирую там. Включая geosite, списки, конфиг и роутинг. В сочетании с VPS + autoXRAY - 10 минут и у тебя готовое решение.
        Выглядит пока просто как попытка написать свой собственный альтернативный способ, потому что "было интересно".


        1. ShyDamn Автор
          10.04.2026 09:06

          Если коротко, то да, Podkop удобнее, но моя схема гибче.

          Podkop - это готовое решение, и для большинства задач его более чем хватает.

          У меня же цель была собрать всё руками, чтобы полностью контролировать роутинг, DNS и понимать, что происходит под капотом. Это сложнее, но даёт больше предсказуемости и гибкости.


          1. alamer
            10.04.2026 09:06

            А какой в этом смысл если ядро у вас не поддерживает альтернативные протоколы?

            Кажется работа ради работы


  1. veneamin
    10.04.2026 09:06

    Везде у этого роутера в описании и на фото только 1 wan и 1 lan порт. совсем не сходится с

    • Порты: 1× WAN 2.5GbE, 3× LAN 1GbE

    Что-то как-то слишком скромно для его цены. вот у меня в моем два лан порта используется - это мне рядом с ним еще свич ставить?


    1. ShyDamn Автор
      10.04.2026 09:06

      Тут опечатка в тексте. Спасибо, что обратили внимание.

      У него действительно 1 WAN + 1 LAN, так как данная модель - это по сути Travel-роутер, компактный для того, чтобы брать в путешествия. Если нужно больше портов, то без свича не обойтись, да.


  1. tklim
    10.04.2026 09:06

    Как я вижу все подобные статьи:


  1. SantaClaus16
    10.04.2026 09:06

    Автор, не обижайтесь но покритикую. Без fakedns, который сейчас стандарт-дефакто, ваша схема выглядит как велосипед. Особенно с adguard, который нет-нет, да уже подставлял. Только на dot и doh полагаться нельзя, надо использовать оба, а еще лучше иметь failsafe на случай падения обоих. Убирать их схемы dnsmasq считаю неправильным, лучше понять его и полюбить, зато в случае чего у вас роутер не останется без dns. Качать чужие dat, тем более в /tmp - а если не скачается? А если битым скачается? А если авторы листов сойдут с ума и раздуют до размера не совместимого с жизнью? Не используйте чужие листы, да геморно, но составляйте свои. quic не страшный. udp тоже проксируется, нет в этом никакой проблемы. ipv6 точно так же легко добавляется в схему, для него только что надо тот же fakedns настроить. Под ipset можно написать собственные обработчики, тот же firewall позволяет вам использовать user скрипты. Ну и схема в кучей серверов - я не знаю, может у вас используется куча дохнущих серверов из подписки, но по-моему лучше использовать 1-2 своих. В идеале вообще каскад (я думаю вы сами в состоянии придумать)... Просто не рационально заводить 5-6 vps и по пингу решать какую использовать. Завтра может все заблочат, выбирать будет не из чего. Хорошо если роутер у вас один, но вы же небось друзьям и знакомым схему настроили.
    Я не знаю в каком сообществе вы состоите, может сами через документацию всю схему придумывали (хотя есть паттерны, которые говорят что точно вдохновлялись), но таки поищите в тг...
    Статья крутая для понимания xray, хотя документация на мой взгляд у xray хорошая. Для средней прокачености юзера обычно хватает подкопа. Кстати у xray есть свои неочевидные минусы, может еще столкнетесь. Желаю вам успехов.