Re:HomeProxy на OpenWrt: обход блокировок и гибкая маршрутизация

Материал из wolfram
Версия от 11:06, 14 августа 2026; Владимир (обсуждение | вклад) (Инструкция по установке Re:HomeProxy на OpenWrt)
(разн.) ← Предыдущая версия | Текущая версия (разн.) | Следующая версия → (разн.)
Перейти к навигации Перейти к поиску

Re:HomeProxy на OpenWrt: обход блокировок и гибкая маршрутизация

Подробная пошаговая инструкция по развёртыванию Re:HomeProxy на чистом роутере с OpenWrt — от установки пакета до работающей выборочной маршрутизации. Рассчитана на пользователя, который умеет работать в терминале, но не знаком с внутренним устройством роутерных систем.

Проверено на Xiaomi Redmi Router AX6000 (aarch64_cortex-a53, mediatek/filogic) с OpenWrt 24.10.0. Подходит для любого устройства с OpenWrt 24.10 (opkg) или 25.12+ (apk).


Что мы построим

Одна панель в LuCI, которая управляет всем стеком обхода блокировок: туннелем, списками маршрутизации и обходом DPI без VPN.

Компонент Назначение
Re:HomeProxy (luci-app-re-homeproxy) Веб-панель в LuCI. Форк ImmortalWrt HomeProxy. Управляет узлами, списками маршрутизации, правилами доступа и обходом DPI. Сама по себе трафик не обрабатывает — генерирует конфигурацию для ядра и правила nftables.
sing-box-extended Ядро (движок). Форк sing-box с расширенным набором протоколов: VLESS (в т.ч. REALITY, XHTTP), Hysteria2, Trojan, Shadowsocks, ShadowTLS, AnyTLS, WireGuard, AmneziaWG, Mieru, NaïveProxy, SSH и др.
Списки маршрутизации Готовые наборы доменов и IP (Re-filter, Russia Inside, точечные по сервисам). Скачиваются ядром автоматически и обновляются раз в сутки.
Zapret 2 (nfqws2) Обход DPI без туннеля: искажает TLS-рукопожатие «на лету» через NFQUEUE. Подходит для замедленных, но не заблокированных сервисов — трафик идёт напрямую, минуя VPS.
ByeDPI (ciadpi) Альтернативный движок обхода DPI, работает как локальный SOCKS5-прокси. 47 пресетов стратегий и встроенный тестер.

Принцип работы

В основе — разделение на две независимые оси:

  • Списки решают, какой трафик особенный.
  • Узел / Zapret / ByeDPI решают, через что его пустить.

Они комбинируются: каждое правило — это пара «список → способ доставки». Например, заблокированные РКН сайты идут через туннель, а замедленный YouTube — напрямую с лечением DPI, не нагружая VPS.

Примечание: Весь трафик LAN перехватывается через nftables (redirect для TCP, tproxy для UDP) и передаётся ядру. Ядро определяет домен по SNI из TLS-рукопожатия либо по DNS-ответу, сверяет со списками и выбирает маршрут. Всё, что не попало ни в один список, идёт напрямую.


Требования

Требование Описание
Роутер OpenWrt / ImmortalWrt 24.10 и новее (менеджер пакетов opkg) либо 25.12 и новее (apk). Для 23.05 в разделе Releases есть legacy-сборка с суффиксом _all-legacy.ipk.
Свободное место Минимум 40 МБ на overlay при сжимающей файловой системе (ubifs/jffs2), либо 90 МБ на несжимающей (ext4/f2fs). Основной объём — ядро.
Оперативная память От 256 МБ. Ядро — крупное Go-приложение.
Архитектура Любая, для которой собран пакет ядра: aarch64 (cortex-a53/a72/a76, generic), x86_64, arm, mips/mipsel.
Доступ Веб-интерфейс LuCI и SSH-доступ к роутеру.
Узел (опционально) Сервер с VLESS / Hysteria2 / Trojan / Mieru / NaïveProxy и т.п. Нужен только для туннеля — обход DPI через Zapret и ByeDPI работает и без него.

Внимание — как правильно оценить свободное место. Команда df -h показывает несколько разделов, и легко перепутать. Ориентироваться нужно только на строку с точкой монтирования /overlay (или /). Раздел tmpfs, смонтированный в /tmp — это оперативная память (обычно половина от объёма ОЗУ), а не хранилище: он очищается при перезагрузке и для установки не годится, хотя его размер часто выглядит самым внушительным в выводе.

Проверка перед установкой

Подключитесь к роутеру по SSH и выполните:

# Версия и архитектура
cat /etc/openwrt_release
grep OPENWRT_ARCH /etc/os-release

# Свободное место (смотреть строку /overlay или /)
df -h

# Тип файловой системы overlay: ubifs/jffs2 сжимают, ext4/f2fs — нет
mount | awk '$3=="/overlay"{print $5}'

# Оперативная память
grep -E 'MemTotal|MemAvailable' /proc/meminfo

Уточнение про сжатие. На ubifs и jffs2 файлы сжимаются прозрачно, и крупный бинарник ядра занимает примерно в 2–3 раза меньше своего номинального размера. Проверять реальный расход нужно по df до и после установки: команда du на этих ФС показывает логический размер файла, а не занятое место.


Этап 1. Установка панели

1.1. Зависимости

Панели требуются четыре пакета: libc, firewall4, kmod-nft-tproxy, ucode-mod-digest. В типовой сборке OpenWrt 24.10 первые три уже присутствуют.

opkg update
opkg install ucode-mod-digest kmod-nft-tproxy kmod-tun

kmod-tun нужен, если планируется режим TUN; для базового режима Redirect + TProxy достаточно kmod-nft-tproxy.

1.2. Установка пакета

Пакет ставится не из штатных репозиториев, а из релизов проекта.

Для OpenWrt 24.10 и ниже (opkg / ipk):

cd /tmp
wget -O rhp.ipk "$(wget -qO- 'https://api.github.com/repos/1andrevich/homeproxy-hiddify/releases/latest' \
  | grep -o 'https://github\.com/[^"]*luci-app-re-homeproxy[^"]*_all\.ipk' | head -1)"
opkg install /tmp/rhp.ipk

Русская локализация интерфейса:

cd /tmp
wget -O rhp-ru.ipk "$(wget -qO- 'https://api.github.com/repos/1andrevich/homeproxy-hiddify/releases/latest' \
  | grep -o 'https://github\.com/[^"]*luci-i18n-homeproxy-ru[^"]*_all\.ipk' | head -1)"
opkg install /tmp/rhp-ru.ipk

Для OpenWrt 25.12+ (apk): в командах выше замените расширение .ipk на .apk, а opkg install — на apk add --allow-untrusted.

Сам пакет панели весит около 650 КБ — основное место займёт ядро на следующем этапе.

1.3. Проверка

opkg list-installed | grep re-homeproxy
ls /etc/config/homeproxy /etc/init.d/homeproxy

После установки в LuCI появится раздел Сервисы → Re:HomeProxy. Если пункт меню не виден — обновите страницу с очисткой кэша (Ctrl+F5).


Этап 2. Установка ядра

Панель без ядра не работает: на её странице будет висеть красная надпись NOT RUNNING, а в журнале — No supported core found.

2.1. Выбор ядра

Ядро Особенности
hiddify-core Вариант по умолчанию. Ставится в один клик, есть компактная (UPX) сборка для устройств с малым объёмом флеш-памяти.
sing-box-extended Самый широкий набор протоколов: AmneziaWG / WARP, Mieru, NaïveProxy, TrustTunnel, MASQUE, Snell, MTProxy. Рекомендуется, если нужны эти протоколы.

2.2. Установка в один клик

В панели: Core & Tools (в русской локализации — Ядро и службы) → выбрать ядро → Установить. Панель сама определит архитектуру, проверит свободное место, скачает и установит пакет.

Внимание — в OpenWrt-сборке sing-box-extended нет NaïveProxy. Пакет для OpenWrt собирается без флага with_naive_outbound, поэтому пункт NaïveProxy в списке типов узлов не появится, а попытка использовать такой узел даст ошибку naive outbound is not included in this build. Если NaïveProxy нужен — устанавливайте ядро вручную по инструкции ниже. Mieru в OpenWrt-сборке присутствует.

2.3. Ручная установка ядра с поддержкой NaïveProxy

Вместо пакета для OpenWrt берётся ассет linux-<арх>-musl из того же релиза. Он собран с флагами with_naive_outbound и with_musl, библиотека cronet влинкована статически, поэтому дополнительных файлов не требуется. OpenWrt тоже построен на musl, и бинарник запускается как есть.

cd /tmp
# Узнать актуальную версию и скачать musl-сборку (пример для arm64)
VER=$(wget -qO- 'https://api.github.com/repos/shtorm-7/sing-box-extended/releases/latest' \
  | grep -o '"tag_name": "[^"]*"' | cut -d'"' -f4)
wget -O sb.tar.gz "https://github.com/shtorm-7/sing-box-extended/releases/download/${VER}/sing-box-${VER#v}-linux-arm64-musl.tar.gz"

mkdir -p sb && tar xzf sb.tar.gz -C sb
BIN=$(find /tmp/sb -name 'sing-box' -type f | head -1)
chmod +x "$BIN"

# Убедиться, что нужные флаги на месте
"$BIN" version

В строке Tags: должны присутствовать with_naive_outbound (для NaïveProxy) и with_quic (для Mieru и Hysteria2). Если флаги на месте — установить бинарник:

df -h /                                   # запомнить свободное место
cp "$BIN" /usr/bin/sing-box
chmod +x /usr/bin/sing-box
sync
df -h /                                   # оценить реальный расход
/usr/bin/sing-box version                 # проверка запуска
rm -rf /tmp/sb /tmp/sb.tar.gz

Внимание — путь строго /usr/bin/sing-box. В конфигурации есть параметры custom_core_path и custom_core_type, но стартовый скрипт /etc/init.d/homeproxy их не читает — в нём жёстко задано PROG_SINGBOX="/usr/bin/sing-box". Ядро, положенное в произвольный каталог, панель в статусе покажет, но сервис с ним не запустится.

Внимание — не сжимайте ядро упаковщиком UPX. Сжатый бинарник распаковывается в оперативную память при каждом запуске процесса, а панель вызывает sing-box version при каждой отрисовке своих страниц (так она определяет доступные протоколы по флагам сборки). В результате интерфейс становится непригодным для работы: замер на aarch64 даёт 3,1 с против 0,05 с на каждый вызов — разница в шестьдесят раз. На скорость трафика это не влияет (ядро стартует один раз), страдает только отзывчивость панели. Экономия при этом невелика: на сжимающей файловой системе выигрыш составляет около 19 МБ.

Совет — как проверить ядро, ничего не устанавливая. Скачайте архив в /tmp (это ОЗУ), распакуйте и запускайте бинарник прямо оттуда: ./sing-box version, ./sing-box check -c конфиг.json. Так проверяются и флаги сборки, и совместимость конфигурации, и живой трафик — при этом флеш-память роутера не затрагивается.

2.4. Проверка, что панель видит ядро

ubus call luci.homeproxy singbox_get_features

В ответе должны быть "core_type": "singbox", версия и набор флагов. Ключевые:

Флаг Что даёт
with_naive_outbound Пункт NaïveProxy в списке типов узлов
with_quic Mieru, Hysteria, Hysteria2, TUIC
hp_has_tproxy Режим TProxy для UDP
hp_has_tun Режим TUN

Этап 3. Запуск и добавление узла

3.1. Запуск службы

/etc/init.d/homeproxy enable
/etc/init.d/homeproxy start
/etc/init.d/homeproxy status

Примечание: Статус active with no instances и запись no main_node configured, skipping config generation в журнале — это нормальное состояние до того, как задан основной узел. Ядро запускается только при выбранном узле. Пока узел не задан, трафик не перехватывается и сеть работает как обычно.

3.2. Добавление узла

Node SettingsДобавить. Поддерживаются два пути:

  • Импорт ссылки или подписки — вставить vless://, hysteria2://, naive+https://, ss:// и т.п., либо URL подписки (sing-box JSON / Hiddify, base64, обычные share-ссылки, конфигурации Xray/V2Ray).
  • Ручное заполнение — выбрать тип и заполнить поля.

Уточнение по Mieru: диапазон портов в поле Port range записывается через дефис2012-2022. Форматы 2012:2022 и одиночное значение в поле диапазона дают ошибку invalid server_ports format. Одиночный порт без диапазона задаётся в обычном поле порта.

3.3. Основные параметры

Client Settings → Routing Settings:

Параметр Рекомендация
Main node Созданный узел. Значение Отключить оставляет всю маршрутизацию прямой.
Routing mode Russia (Proxy Banned) — по умолчанию всё идёт напрямую, через туннель — только попавшее в списки.
Proxy mode Redirect TCP + TProxy UDP — базовый режим, не требует TUN.
Russia DNS server Быстрый российский резолвер для доменов, идущих напрямую.
Secure DNS server DoH/DoT-резолвер для проксируемых доменов — провайдер не видит, какие сайты запрашиваются.
Do not proxify torrents Включить: торренты пойдут напрямую и не будут нагружать туннель.

После изменений — Применить.


Этап 4. Списки маршрутизации

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

4.1. Автообновляемые списки (основной способ)

Client Settings → Proxy Rules → Добавить. Каждое правило — строка из трёх полей:

Поле Значение
Enable галочка
Source ⤵️ источник списка
Node 🔗 способ доставки: Same as main node, Separate URLTest, конкретный узел, ByeDPI или Zapret

Доступные источники:

Источник Содержимое
Re-filter Более 60000 доменов и 25000 IP — сводный перечень ресурсов, заблокированных в России
Russia Inside Свыше 1000 записей: базовый набор сервисов, которым в России нужен туннель
Точечные YouTube, Discord, Telegram, Meta, TikTok, Twitter/X, Roblox, HDRezka, Google AI, Google Play, Cloudflare, CloudFront, Hetzner, OVH, DigitalOcean, аниме-стриминги, новостные сайты и др.

Такие списки ядро скачивает само и обновляет раз в сутки (update_interval: 1d) — поддерживать их вручную не нужно.

Примечание про приоритет: порядок правил задавать не нужно, панель расставляет их сама: сначала точечные списки по сервисам, затем Russia Inside, последним — самый широкий Re-filter. Благодаря этому частное правило всегда выигрывает у общего, и, например, YouTube можно отправить через Zapret, а всё остальное из Re-filter — через туннель.

Уточнение — почему списки по сервисам важнее ручных доменов. Многие приложения подключаются к своим серверам по IP-адресам, без доменного имени — так работают мессенджеры, игровые клиенты и часть мобильных приложений. Доменные правила на такие соединения не действуют в принципе: ядру нечего сопоставлять со списком. Готовые списки по сервисам содержат, помимо доменов, ещё и IP-подсети, поэтому закрывают оба случая. Показательный пример — Telegram: список включает домены (t.me, telegra.ph, telesco.pe) и подсети дата-центров (149.154.160.0/20, 91.108.4.0/22, 91.108.56.0/22 и другие). Без подсетей веб-версия откроется, а сам мессенджер подключиться не сможет.

4.2. Ручной список доменов

Access Control → Proxy Domain List — произвольный перечень доменов, по одному в строке. Имеет смысл для того, чего нет в готовых списках.

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

4.3. Обязательный пункт: ECH

Внимание — без этой записи проксирование не работает в браузерах. Добавьте в Proxy Domain List строку:

cloudflare-ech.com

Суть проблемы. Сайты за Cloudflare публикуют DNS-запись типа HTTPS (RR 65) с параметром ech=. Получив её, современный браузер применяет ECH (Encrypted Client Hello) и шифрует имя сервера (SNI) в TLS-рукопожатии. Ядро определяет домен именно по SNI, поэтому распознать сайт не может, не находит его в списках и отправляет соединение напрямую — где его и обрывает DPI.

Характерная картина: сайт есть в списке, но в браузере выдаёт ошибку установления защищённого соединения (в Firefox — PR_END_OF_FILE_ERROR), тогда как curl с того же устройства на тот же адрес работает и корректно уходит через туннель. Расхождение объясняется просто: curl не поддерживает ECH и отправляет открытый SNI.

Почему помогает одна строка. При ECH шифруется только внутреннее имя, а внешнее остаётся видимым — и для всех сайтов за Cloudflare оно равно cloudflare-ech.com. Ядро видит его, находит в списке и отправляет соединение в туннель. Решение действует на любого клиента сети независимо от браузера и его настроек DNS — настраивать устройства пользователей не требуется.

Проверить, использует ли конкретный домен ECH, можно с любой машины:

curl -s 'https://cloudflare-dns.com/dns-query?name=example.com&type=HTTPS' -H 'accept: application/dns-json'

Наличие ech= в ответе означает, что у этого домена SNI будет скрыт.


Этап 5. Обход DPI без туннеля

Для сервисов, которые не заблокированы, а замедлены (характерный пример — видеохостинги), туннель избыточен: он тратит трафик VPS и режет скорость. Достаточно исказить рукопожатие так, чтобы DPI не смог его разобрать.

Движок Как встроен Особенности
Zapret 2 (nfqws2) Исходящее соединение помечается меткой маршрутизации, nftables отправляет первые пакеты в NFQUEUE, где nfqws2 искажает рукопожатие Работает с QUIC; трафик идёт напрямую и не шифруется
ByeDPI (ciadpi) Локальный SOCKS5-прокси на 127.0.0.1:5335, подключается как обычный узел типа socks 47 пресетов стратегий, не требует NFQUEUE

5.1. Установка

На вкладке Client Settings включите Enable Zapret или Enable ByeDPI — панель предложит установить недостающий пакет и сделает это сама. Пакеты небольшие.

5.2. Обязательный шаг: назначить трафик

Внимание — сама по себе галочка ничего не обходит. В этой панели nfqws2 запускается без собственных хостлистов и обрабатывает только то, что ему явным образом направят. Какие соединения попадут в очередь, решает ядро — по правилам из Proxy Rules. Пока ни одно правило не указывает на Zapret, демон работает вхолостую, а счётчик его цепочки nftables остаётся нулевым.

Правильный порядок: Proxy Rules → ДобавитьSource = нужный сервис (например, YouTube) → Node = Zapret. После этого — Применить и перезапуск службы.

Проверить, что трафик действительно пошёл в обработку:

nft list chain inet fw4 homeproxy_zapret_queue | grep counter

Ненулевые счётчики пакетов означают, что механизм работает.

Примечание: В очередь попадают только первые 1–12 пакетов каждого соединения, то есть само рукопожатие, а не весь поток. Нагрузка на процессор роутера при этом минимальна.

5.3. Подбор стратегии

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

Совет — что выбрать для голосовой связи. Голосовые звонки (Discord, мессенджеры) идут по UDP и плохо переносят искажение пакетов. Такие сервисы надёжнее направлять через туннель (Node = основной узел), а не через Zapret. Если всё же нужен Zapret — включите в настройках опцию проксирования голосовых портов, иначе звонки могут не устанавливаться.


Эксплуатация

Как применяются изменения

Что изменено Как применить
Настройки на вкладках (узел, режим, DNS, правила) кнопка Применить
Только списки доменов в Access Control ДиагностикаПерезапустить службу (кнопка Применить изменений не увидит)
Из командной строки /etc/init.d/homeproxy reload

Диагностика

Вкладка Диагностика показывает состояние ядра, версию, PID, запущенные движки обхода DPI, занятые порты и позволяет проверить доступность контрольных сайтов.

Из командной строки:

# Состояние службы и ядра
/etc/init.d/homeproxy status
ps w | grep -E 'sing-box|nfqws|ciadpi' | grep -v grep

# Журналы: события службы и лог ядра
tail -30 /var/run/homeproxy/homeproxy.log
tail -30 /var/run/homeproxy/hiddify-c.log

# Проверка сгенерированной конфигурации
/usr/bin/sing-box check -c /var/run/homeproxy/hiddify-c.json

Уточнение по чтению журнала ядра. Обилие строк connection refused и i/o timeout для соединений через direct-out на портах 6881, 51413 и произвольных высоких портах — это, как правило, торрент-трафик: клиент перебирает пиров, часть которых недоступна. К работе туннеля такие записи отношения не имеют.

Как определить, каким маршрутом ушло соединение

Временно повысьте подробность журнала (в панели Log levelinfo), выполните запрос с клиентского устройства и посмотрите записи вида:

inbound/redirect[redirect-in]: inbound connection to 172.67.182.196:443
outbound/vless[main-out]: outbound connection to 172.67.182.196:443

Тег в квадратных скобках показывает выбранный маршрут: main-out — через основной узел, direct-out — напрямую, zapret-out — с обработкой DPI. После диагностики верните уровень warn, иначе журнал быстро заполняется.

Проверка через клиентское устройство

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

# Какой адрес видит внешний мир (для непроксируемого ресурса — адрес провайдера)
curl -s https://api.ipify.org; echo

# Доступность конкретного сайта
curl -s -o /dev/null -m 15 -w 'HTTP %{http_code} за %{time_total}s\n' https://example.com

Уточнение: в выводе curl поле %{remote_ip} — это адрес запрошенного сайта, а не собственный внешний адрес. Чтобы понять, ушло ли соединение через туннель, используйте сервис определения IP (api.ipify.org, ifconfig.me) либо смотрите тег маршрута в журнале ядра.


Типовые неполадки

Симптом Причина и решение
Панель показывает NOT RUNNING, в журнале No supported core found Ядро не установлено или лежит не по пути /usr/bin/sing-box — см. Этап 2
Служба запущена, но статус active with no instances Не задан Main node. Это ожидаемое поведение, а не ошибка
Сайт из списка не открывается в браузере, но curl работает ECH скрывает SNI. Добавить cloudflare-ech.com в Proxy Domain List — см. раздел 4.3
Изменил список доменов — ничего не изменилось, панель пишет, что изменений нет Правка списка не перезапускает ядро. Диагностика → Перезапустить службу
Мессенджер не подключается, хотя его сайт открывается Приложение ходит на серверы по IP. Нужен готовый список по сервису, содержащий IP-подсети — см. раздел 4.1
Zapret включён, но сервис по-прежнему замедлен Нет правила с назначением Zapret. Счётчик очереди нулевой — см. раздел 5.2
Пункт NaïveProxy отсутствует в списке типов узлов В OpenWrt-сборке ядра нет соответствующего флага. Установить musl-сборку — см. раздел 2.3
Интерфейс панели открывается по несколько секунд Ядро сжато UPX. Заменить на несжатый бинарник — см. предупреждение в разделе 2.3
Страницы «висят» по 30 секунд, затем открываются Браузер уходит в QUIC (UDP/443) мимо перехвата. Заблокировать UDP/443 действием REJECT (не DROP — при DROP браузер ждёт таймаута)

Удаление

/etc/init.d/homeproxy stop
/etc/init.d/homeproxy disable
opkg remove luci-app-re-homeproxy luci-i18n-homeproxy-ru

# Ядро, если ставилось пакетом
opkg remove sing-box-extended
# Ядро, если бинарник копировался вручную
rm -f /usr/bin/sing-box

# Остатки конфигурации и списков
rm -rf /etc/homeproxy /etc/config/homeproxy

Правила nftables снимаются автоматически при остановке службы. Настройки межсетевого экрана, проброс портов и прочая конфигурация роутера не затрагиваются.


Ссылки