Дисклеймер. Материал носит технический и информационный характер. Статья описывает настройку маршрутизации трафика, защищённого 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 на пятнадцати устройствах.
Выбор железа
Не каждый роутер подойдёт. Нужны три вещи:
Поддержка OpenWrt — без неё ничего не получится. Проверяйте на openwrt.org/toh.
Достаточно RAM — Xray-core написан на Go и потребляет ~100 МБ физической памяти. Плюс geodata (~85 МБ в tmpfs). Роутеры с 128 МБ RAM не потянут. Минимум — 256 МБ, комфортно — 512 МБ.
Нормальный 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/nftableskmod-nft-fib— FIB lookup, нужен для policy routing в nftablesnftables-json— утилитаnftс поддержкой JSONcurl— для скачивания 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"} ] }
Правила применяются сверху вниз, первое совпадение побеждает:
Частные 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-устройства.
Российские IP (geoip:ru) → direct. Зачем проксировать трафик к Яндексу или ВК? Он и так работает. Проксирование только замедлит.
Сайты, работающие только из РФ (geosite:ru-available-only-inside) → direct. Это кастомная категория из проекта runetfreedom/russia-v2ray-rules-dat: госуслуги, банки, сервисы, доступные только из РФ. Если пустить их через VPN-сервер за рубежом — они перестанут работать.
Домены .ru / .su / .рф → direct. Регулярные выражения ловят все домены в этих зонах.
.xn--p1ai— это punycode-кодировка.рф.Всё остальное TCP → vpn-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:
Телефон отправляет TCP SYN к
discord.com:443Пакет приходит на роутер через
br-lannftables (хук prerouting, priority mangle) перехватывает пакет
Правило TPROXY передаёт пакет Xray на 127.0.0.1:12345, ставит fwmark 0x1
Policy routing (
ip rule fwmark 1 table 100) маршрутизирует помеченный пакет в локальный стекXray получает пакет с оригинальным IP назначения
Sniffing извлекает
discord.comиз TLS SNIRouting проверяет: discord.com не в geosite:ru, не .ru — значит, proxy
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
Как это работает:
nftables ставит метку
0x1(fwmark) на перехваченный пакетЯдро видит метку и ищет маршрут в таблице 100 (вместо основной таблицы)
В таблице 100 единственный маршрут:
local default dev lo— все адреса считаются локальнымиЯдро доставляет пакет в локальный стек, где его подхватывает 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, произойдёт следующее:
Браузер пытается подключиться к сервису по QUIC (UDP 443)
UDP не перехватывается TPROXY (нет правила для UDP)
Пакет уходит напрямую через WAN, минуя прокси-сервер
Трафик уходит напрямую, минуя заданные правила маршрутизации
Дроп 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. Общий алгоритм:
Fetch — скачиваем подписки по URL (две штуки, независимо)
Decode — base64 → текст (по одной VLESS-ссылке на строку)
Filter — отбрасываем специализированные ноды (выделенные для отдельных сервисов или типов сетей)
Parse — из каждой ссылки извлекаем параметры и генерируем Xray JSON outbound
Build — собираем конфиг из шаблона, подставляя список серверов
Validate —
xray run -test -c config.jsonпроверяет синтаксис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 restart —
network 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)

0ka
10.04.2026 09:06REDIRECT (DNAT) — подменяет адрес назначения на 127.0.0.1:порт. Проблема: оригинальный адрес теряется.
Вообще-то нет. Точно не знаю как оно работает, но проходит любой TCP трафик. Ядро как-то сохраняет оригинальный адрес и передаёт xray. Для UDP трафика почему-то не работает и нужен tproxy

ShyDamn Автор
10.04.2026 09:06Он не «сохраняется сам».
При REDIRECT адрес реально теряется (DNAT на 127.0.0.1), а то, что у тебя работает с TCP - это либо sniffing (SNI/Host), либо SO_ORIGINAL_DST. Это не универсально.
TPROXY как раз и нужен, чтобы получать оригинальный адрес нативно

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 добавить правило).

0ka
10.04.2026 09:06*

ShyDamn Автор
10.04.2026 09:06Скорее всего у тебя это через SO_ORIGINAL_DST работает, а не потому что ядро само «сохраняет адрес».
С REDIRECT это в целом ок для TCP, но всё равно не совсем универсально и зависит от реализации. Поэтому TPROXY обычно используют, когда нужна более предсказуемая прозрачная схема.

0ka
10.04.2026 09:06"network": "tcp"Я думаю 90% читателей будут ожидать проксирование и UDP трафика в том числе. А тут такая подстава.
Кстати QUIC блочат даже при рабочем UDP в целях увеличения производительности vless:
Xtls не делает двойное шифрование https трафика, т.е. quic трафик будет жрать ресурсы проца на шифрование
Quic имеет свой congestion control, ваш прокси сервер тоже, получается два congestion control что даёт производительность ниже
UDP over TCP потребляет больше ресурсов проца

ShyDamn Автор
10.04.2026 09:06Тут только TCP сделан специально.
QUIC (UDP 443) я режу, чтобы весь трафик уходил в TCP и нормально проксировался. UDP в прозрачной схеме - отдельный уровень сложности.
Так что это не «подстава», а осознанное решение :)

0ka
10.04.2026 09:06я и так из статьи понял зачем вы блокируете quic. здесь речь не о вас, а о читателях.
напишите в самом верху что весь UDP трафик пропущен напрямую.
и вообще, вроде бы не хватает 1 строчки nft чтобы UDP проксился, но точно не уверен

0ka
10.04.2026 09:06*

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

0ka
10.04.2026 09:06Транспорт — чистый TCP (не WebSocket, не gRPC). TCP даёт минимальную задержку и не добавляет лишних заголовков.
а так же даёт потенциальную сибирскую блокировку (моя последняя статья), стоило бы добавить что лучше иметь в запасе gRPC траспорт (из-за встроенного мультиплекса).
кстати TCP транспорт так-то не даёт минимальную задержку, тратится много времени на tcp и tls хендшейки, если впска где-то далеко в США, то это очень заметно (ещё одна причина быть фанатом протоколов с мультиплексом, хотя там есть минусы)

ShyDamn Автор
10.04.2026 09:06Да, тут согласен - TCP не серебряная пуля, особенно если сервер далеко, хендшейки начинают чувствоваться.
Я скорее имел в виду, что TCP без WS/gRPC даёт минимальный оверхед в рамках самой схемы (без лишних слоёв), а не вообще минимальную задержку в любых условиях.
Про запасной транспорт тоже хорошее замечание :)
gRPC/WS с мультиплексом действительно могут быть полезны как fallback, особенно если начинаются блокировки или высокий RTT, спасибо, добавлю это в схему позднее

ShyDamn Автор
10.04.2026 09:06P.S. из практики: основная проблема, с которой столкнулся - забивающийся overlay.
В итоге вынес Xray и AdGuard в /tmp, а при загрузке разворачиваю их из архивов через init-скрипт и запускаю. Так удалось разгрузить overlay и всё работает стабильно.

12345egor
10.04.2026 09:06Простите, не могу не написать)
телевизор вообще не умеет в VLESS
умеет
колонка — тем более
Даже представить не могу в какой ситуации колонке понадобится обход блокировок, у них закрытые экосистемы сервисов.

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

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

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 есть какая-то модерация сообществом? И свежий ноунейм сайт надеюсь не пропустят

ShyDamn Автор
10.04.2026 09:06Да, замечание справедливое :)
У меня сейчас действительно схема proxy-by-default, так что любой домен, который не вынесен в direct, может уйти через VPN - в том числе и 2ip-подобные сервисы. Для более строгого privacy-first подхода логичнее делать default direct и отдельно перечислять нужные категории.
Про geosite тоже верно: списки комьюнити-поддерживаемые и со временем меняются, так что здесь лучше держать политику маршрутизации осознанно, а не только полагаться на один большой блок.

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

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

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

boXed
10.04.2026 09:06Мужчина, можно у вас роутер со всем этим купить? :)
Предложение, кстати, на полном серьёзе.
Это как-то разом можно накатить на новый роутер или каждый раз руками ставить?
А то готовая бизнес-идея.
Понятно - до первого не штатного случая (там разберемся).

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

iamezhik
10.04.2026 09:06На Авито подобного добра навалом.

boXed
10.04.2026 09:06Вот не поверите - реально всю текущую неделю именно на Авито искал роутер под работу блок-сайтов.
Пообщался, наверное, с 12-15 продавцами.
Из них 2-ое в теме (на мой взгляд).
Один прям достаточно глубоко в теме (но явно проигрывает автору этой статьи).
Остальные продавцы - полное ощущение - по какому-то гайду прошивку накатили и дальше кто во что горазд.
Глубокого понимания нет (я сам для себя тоже и WireGuard и Amnesia по "букварю" поставлю - но глубокого понимания нюансов у меня нет).
Я к чему - предложений такого уровня (читаю и перевожу со словарем) - много и я сам примерно так и могу.
А вот некая система с подходом как выше - такое редко вижу.
Со своей стороны я это называю "профессионализм" и готов за это платить.
При этом и у меня (а главное - у автора) есть понимание, что может у него и не идеальное Retail-издание, но он слушает и слышит - готов следовать дельным советам.
Поэтому - я за такое бы платил.
Ничем не хуже скилла "заменить смеситель", "установить розетки/УЗО", "сварить кофе вкусно".
Точно стоит денег.

alamer
10.04.2026 09:06Мне иногда кажется, что мы не на техническом сайте, на котором следят специалисты в айти.
Одни не в состоянии найти one-line аналоги, вторые городят свои нейровелосипеды.

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

ShyDamn Автор
10.04.2026 09:06Да, готовые решения есть, но там главный вопрос - насколько они понятны и поддерживаемы.
Я как раз хотел уйти от истории «прошил по гайду и надеюсь, что всё ок», потому что при любом нестандартном баге (как ты описал) без понимания дальше уже тяжело.
А так да, через приставку или клиенты на устройствах тоже вполне рабочий вариант, просто менее централизованный.

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, как я понял) - тоже есть.

boXed
10.04.2026 09:06Вот спасибо за совет.
Вот в этом и дело - вы так круто и понятно расписали, что даже ради интереса хочется купить этот роутер.
А вот ни в описании этого продукта, ни в тысяча коментах - ничего подобного нет.
Максимум - поддержка молодцы, смотрю теперь ютуб (что не сильно отличает этот продукт от других в глазах покупателя).
Так что снова приходим к важности понятного продукта, который закрывает потребности/боли человека.

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

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

boXed
10.04.2026 09:06Всё так.
Или готовый роутер (с обновлениями) или понятный инсталятор all-in-one ))

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

Mingun
10.04.2026 09:06Возникло несколько вопросов после прочтения статьи:
Правильно ли я понимаю, что списки серверов VPN и базы данных geoip и geosite скачиваются не через этот самый VPN, а потому провайдер спокойно видит, что вы их качаете, и, соответственно, может понять, что сейчас его будут дурить?
Вроде как IPv6 более лучший протокол (все предпочитают использовать его из статьи), но вы сознательно его выключаете. В чём с ним сложность, почему нельзя всё то же самое настроить и для IPv6?
Будут ли работать торренты в этой схеме? Там же peer-to-peer соединения, всё будет заворачиваться на VPN всё равно?

ShyDamn Автор
10.04.2026 09:06Хорошие вопросы :)
Да, всё это качается напрямую. Провайдер может это видеть, но это обычный HTTPS-трафик и редкие запросы.
IPv6 можно настроить, но там сильно больше нюансов и легко словить утечку - я поэтому его просто отключил.
Торренты будут работать, но пойдут через VPN и без входящих соединений раздача может быть хуже.

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

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

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-файлов.

zbot
10.04.2026 09:06ну и после всех настроек роутера надо запустить в локальную сеть маха чтобы он разобрался куда роутер валит трафик к заблокированному контенту.
Впрочем теперь этим будет заниматься не только мах но и валберисы с озонами и т.д. Хотя никто не мешает в js наших маркетплейсов внести небольшой код который будет пробивать внешний (зарубежный) ip и на компьютере при открытии сайта в браузере.
те кто считает что раздельное маршрутизирование спасает могут поэкспериментировать - https://api.ipify.org или https://ipinfo.io/json

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

ShyDamn Автор
10.04.2026 09:06Спасибо! Да, сейчас хочу сначала довести всё до более-менее стабильного состояния, а потом уже, возможно, выложить в более удобном для развёртывания виде, пока продумываю, как будет лучше.
Про IPv6 согласен, тема важная - я отдельно это ещё добью, но в текущей схеме сознательно упростил и отключил его, чтобы не ловить утечки и лишние сюрпризы.

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

zbot
10.04.2026 09:06скажу банальность, но самое безопасное сейчас не пользоваться вообще никакими vpn, openwrt и полностью погрузится в заботливые обьятия ркн. Все остальное в той или иной мере не безопасно.
а так из боле-менее безопасного - что-то "xray" в режиме прокси, firefox с расширением "firefox мульти-аккаунт контейнер" и настройка проксирования для разных контейнеров. Ну а затем нужные сайты закреплять за выбранными контейнерами.

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

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

ShyDamn Автор
10.04.2026 09:06Да, у меня подписка как раз тянется с передачей HWID - через заголовок
x-hwidв скрипте обновления.То есть если провайдеру нужен HWID, это уже поддержано на уровне fetch-скрипта; роутерная схема тут не мешает.

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

ShyDamn Автор
10.04.2026 09:06Да, если речь про R5S/R6S - это уже хороший вариант под такую схему. У R5S нормальный запас по CPU и 2/4 ГБ RAM, у R6S запас ещё больше.
Для моего стека критична именно память: Xray + geodata + AdGuard уже ощутимо едят RAM, так что 1-2 ГБ хватит спокойно.

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

ShyDamn Автор
10.04.2026 09:06Да, тут всё очень зависит от конкретной схемы и профиля нагрузки. У меня не “просто tproxy”, а TCP-only стек с Xray+Reality, QUIC отдельно режется, и в статье я как раз показывал, что роутер живёт без заметной нагрузки в простое и без проблем тянет обычный домашний трафик.
Под торренты и сотни коротких соединений картина, конечно, хуже - это уже совсем другой сценарий. Но из этого не следует, что Filogic 820 “в принципе не тянет” такую схему: в моём случае узкое место сейчас скорее не CPU, а память/overlay и обвязка.

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

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

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

andrex77
10.04.2026 09:06> Flash: 128 МБ NAND (overlay ~44 МБ)В сети есть мануалы прошивки c u-boot, после чего доступно около 95 Мб, и хватит места для ADH, Podkop, ещё и останется с запасом.
Кстати, этот роутер есть с флешем 256 Мб(китайская версия, либо перепаивают память), есть ещё Cudy TR30 - это то-же самое что и TR 3000 (вроде как коллаборация Cudy и Wildberries)

ShyDamn Автор
10.04.2026 09:06Да, вариант с 256 МБ и расширением overlay встречается, но это уже не базовая TR3000 из коробки, а отдельная ревизия/модификация. У Cudy для TR3000 официально указаны 128 МБ flash, а в OpenWrt Wiki есть отдельные techdata и для 128 МБ, и для 256 МБ версии.
Поэтому в статье я бы оставил цифры для обычной ревизии, а про 256 МБ и U-Boot-правки упомянул бы как про отдельный вариант. TR30, кстати, у Cudy в каталоге идёт как отдельная модель, а не просто другое название TR3000.

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

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

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

ShyDamn Автор
10.04.2026 09:06Разница в контроле.
Подкоп - это когда установил и пользуешься, а тут ты сам собираешь схему и понимаешь, как всё работает и что чинить, если что.

Zloymopga
10.04.2026 09:06Простите, но у меня все еще нет ответа на вопрос комментатора выше. Потому что я уже 1.5 года сижу на podkop и все прекрасно контролирую там. Включая geosite, списки, конфиг и роутинг. В сочетании с VPS + autoXRAY - 10 минут и у тебя готовое решение.
Выглядит пока просто как попытка написать свой собственный альтернативный способ, потому что "было интересно".
ShyDamn Автор
10.04.2026 09:06Если коротко, то да, Podkop удобнее, но моя схема гибче.
Podkop - это готовое решение, и для большинства задач его более чем хватает.
У меня же цель была собрать всё руками, чтобы полностью контролировать роутинг, DNS и понимать, что происходит под капотом. Это сложнее, но даёт больше предсказуемости и гибкости.

alamer
10.04.2026 09:06А какой в этом смысл если ядро у вас не поддерживает альтернативные протоколы?
Кажется работа ради работы

veneamin
10.04.2026 09:06Везде у этого роутера в описании и на фото только 1 wan и 1 lan порт. совсем не сходится с
Порты: 1× WAN 2.5GbE, 3× LAN 1GbE
Что-то как-то слишком скромно для его цены. вот у меня в моем два лан порта используется - это мне рядом с ним еще свич ставить?

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

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 есть свои неочевидные минусы, может еще столкнетесь. Желаю вам успехов.

Last26
Пробовал похожую тему с tproxy..правда не vless а другой протокол..в итоге были две проблемы ..перехват трафика в raw и зависающие сессии .. Потом перешёл с tproxy на tun..и стало лучше )
ShyDamn Автор
У меня схема отличается - я не использую raw вообще, у меня TPROXY через nftables в mangle/prerouting + policy routing.
Сессии не висят: Xray стабильно принимает соединения и маршрутизирует (и direct, и proxy), это видно по логам.
Так что, скорее всего, у тебя проблема была в конкретной реализации (raw/маршрутизация/loop), а не в TPROXY как таковом :)
Last26
Вполне возможно) реализация была адовая просто)
mukca
Да боже мой...
Статья очередной нейрослоп.
Сравнения, противопоставления, выводы, разрыв повествования, отсутствие структуры, ни одного скриншота.
А куда ставиться пакеты? А по какому пути лежат конфиги? Где команды для редактирования их?
А "Проверка что всё работает" вообще гениален. Никто и никогда в виде "кода" выкладывать набор команд, да еще с комментариями не будет.
ps пишу под первым комментовм чтобы мой комментарий увидели побольше читающих.
ShyDamn Автор
Если бы вы дочитали статью, то увидели бы и пути, и команды:
/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-конфига тут не обязательны: важнее рабочие команды и понятная схема. Если нужен формат «одна кнопка и всё само», то это просто не тот жанр статьи.
mukca
Ага части конфига /etc/xray/config.json размазаны рандомно по всей статьи, а путь конфига указан только в файле Init скрипта
А по ответу мне кажется что и ИИ бот отвечает :)
ps А как на вашем роуторе пожарить яичницу? :)
ShyDamn Автор
Путь к конфигу есть в статье, просто он не вынесен в отдельный “список путей”, а показан там, где это нужно по смыслу.
Статья - не справочник по OpenWrt, а разбор рабочей схемы. Поэтому конфиг, init-скрипт, nft и диагностика разбросаны по соответствующим разделам, а не сведены в один блок ради галочки.
Про “ИИ-бота” удобно писать, когда не хочется читать внимательнее.
А яичницу на этом роутере, к сожалению, не жарю :)
mukca
Я воспользовался поиском и путь где находлится config.json указан ровно в одном месте, в инит скрипте.
А настройки которорые в нем содержатся начинаются с самого начала статьи, с заголовка "Конфиг Xray: разбор по частям", сразу как установили пакеты, но узнаем где же этот конфиг находится мы из скрипта запуска, все очень логично
Mingun
Боже мой, какие нафиг скриншоты (ещё бы в ворд их предложили вставить)??? Текст и только текст, который легко скопировать, сохранить, переписать, а не голимый скриншот непонятно чего непонятного качества, который завтра на хостинге протухнет и ищи-свищи, чего там было «выставляем галочки как на скриншоте».
mukca
Вы настраивали на роуторе что-нибудь похожее как в статье?
Вот возьмите статью и читая ее и пользуюсь информацией из нее попробуйте настроить.
Не прочитать и положить на полочку, а прочитать и по статье настроить.
Сразу поймете что с статьей чтото не так, информации нахватает, огромная часть информации о настройки, о действияфх с роутерами, с конфигами, почему это так просто отсутствует.
Я думаю на этапе прошивки возникнут вопросы (на точно таком же роуторе), так как роуторы Cude шьются на openwrt через переходную прошивку насколько помню.
ShyDamn Автор
В статье есть отдельный блок про прошивку, включая переходную firmware и sysupgrade, как раз под Cudy TR3000.
Если на этом этапе «возникают вопросы», значит дело не в отсутствии информации, а в том, как читается статья.
Mingun
Ну так и скажите конкретно, чего не хватает. А не серчайте на отсутствие скриншотов. У меня нет роутера, который бы я мог настроить по этой статье, но как появится, я ей воспользуюсь.
Про переходную прошивку в статье сказано. И какую надо брать тоже. Вы бы сами ещё раз её внимательно перечитали, м? Но статья не про прошивку роутера, ей простительно. А то мы докатимся до того, что будем обсуждать, где: на Озоне, в DNS или на каком-нибудь Али этот роутер лучше купить.
ShyDamn Автор
Я на ВБ заказывал, если интересно :)
Но да, сделаю вторую часть: разложу спорные моменты подробнее и учту комментарии.
Изначально статья была про саму схему (TPROXY + Xray + routing), а не про полный гайд «с нуля до продакшена», поэтому какие-то вещи оставил за рамками.
hw_store
...а ещё они почему-то обожают вместо запятых ставить тире. Иногда это выглядит уместно. Видимо "учитель" так делал, а теперь они друг у друга нахватались и стали все так делать, в большинстве случаев даже игнорируя официальные правила русского языка.
True_form
Вопрос по xray: сейчас проходит через dpi? Я настраивал awg тоннель до роутера, из роутера в ssh или warp на заграничный vps. Работает на всех опсосах (пока что). Но! Вот кажется, что можно сделать пошустрее, но бодаться с dpi не охото
ShyDamn Автор
Да, с Xray (VLESS + Reality) сейчас в целом нормально проходит через DPI, если сервер и конфиг адекватные.
Твоя схема с awg/ssh/warp - это несколько слоёв туннелей, поэтому она более устойчива, но добавляет задержку и оверхед. Xray более прямой вариант, обычно быстрее, но сильнее зависит от того, как именно настроен сервер и маскировка.
0ka
вообще ничего непонятно, при чём тут ssh и warp? если клиент будет на роутере, то очень вряд ли будет быстрее чем awg