Клиент TrustTunnel для OpenWrt 25.12+ с выборочным обходом блокировок по спискам доменов, настройкой и диагностикой в веб-интерфейсе LuCI.
TrustTunnel — открытый VPN-протокол, изначально разработанный AdGuard VPN. Его трафик неотличим от обычного HTTPS, что позволяет проходить сквозь DPI. Этот пакет поднимает клиент на роутере и решает главный вопрос домашнего использования: что именно гнать через туннель, а что оставить напрямую.
- Что делает пакет
- Установка
- Настройка через интерфейс
- Два режима работы
- Как это работает внутри
- Настройка без интерфейса
- Полный справочник настроек — кратко; подробно с объяснением каждого параметра в SETTINGS.ru.md
- Диагностика
- Ограничения
- Обновление
- Удаление
- Поддержать проект
- Лицензия и благодарности
-
Поднимает клиент TrustTunnel как системную службу. Клиент запускается под procd с автоперезапуском, ждёт готовности сети и синхронизации часов, логирует в системный журнал.
-
Интегрирует списки обхода сообщества. Каталог itdoginfo/allow-domains подтягивается прямо из GitHub, файлы выбираются галочками: региональные списки (Россия, Украина), категории (anime, block, geoblock, hodca, news, porn), отдельные сервисы (YouTube, Telegram, Discord, Meta, TikTok и другие) и CIDR-подсети по нескольким сервисам. Новые файлы в репозитории появятся в интерфейсе сами, без обновления пакета.
-
Принимает свои домены. Два независимых списка: «обходить» — добавляется к выбранным спискам сообщества, и «не обходить» — всегда идёт напрямую и имеет приоритет над всем остальным.
-
Выносит настройки в интерфейс. Сервер заводится импортом конфига, который выдал сервер, или заполнением полей вручную. Никакого редактирования TOML.
-
Даёт диагностику. Пинг каждого адреса сервера с цифрами, сравнение внешнего IP через туннель и напрямую, и проверка конкретного домена: вводите
youtube.com— получаете ответ, в каком списке он нашёлся, в какие адреса резолвится, попал ли в nftables-набор и пойдёт ли через туннель.
Одной командой на роутере:
sh -c "$(wget -O - https://raw.githubusercontent.com/NooBiToo/TrustTunnelOpenWrt/main/install.sh)"Что делает скрипт:
- Проверяет, что это OpenWrt версии 25.12 или новее и что доступен
apk. - Ставит зависимости:
kmod-tun,ip-full,curl,ca-bundle,ucode-mod-math. Ни один из них не входит в типовую прошивку: безkmod-tunне существует даже/dev/net/tun, а у busybox-вариантаipвовсе нет подкомандыtuntap, которой поднимается туннельное устройство. - Проверяет
dnsmasqна поддержкуnftsetи, если её нет, спрашивает подтверждение на установкуdnsmasq-full. - Скачивает
luci-app-trusttunnel-*.apkиз последнего релиза и ставит его. - Определяет архитектуру и ставит бинарник клиента официальным скриптом
TrustTunnel в
/opt/trusttunnel_client. - Перезапускает
rpcd, чтобы LuCI увидел новый бэкенд.
Скрипт идемпотентен: повторный запуск обновляет пакет и бинарник, не трогая настройки, и корректно останавливает службу перед подменой бинарника.
Служба после установки выключена. Это сделано специально: сначала настройка, потом запуск.
Проверено на живом OpenWrt 25.12.5: штатный dnsmasq собран без
поддержки nftset — dnsmasq --test на строке с nftset= отвечает
recompile with HAVE_NFTSET defined. Без dnsmasq-full режим «обход по
списку» (режим по умолчанию) не работает вовсе: dnsmasq не может класть
адреса доменов в nftables-набор, и набор обхода остаётся пустым, что бы вы ни
выбрали в списках. Поэтому установщик по умолчанию предлагает поставить
dnsmasq-full.
Установка dnsmasq-full заменяет системный пакет dnsmasq и перезапускает
DNS на роутере — на несколько секунд DNS-запросы клиентов LAN не будут
обслуживаться. Поэтому замена происходит только после вашего подтверждения.
Если отказаться, продолжит работать только режим «всё через VPN».
Сам пакет архитектурно-независим: внутри только скрипты, ucode, JS и конфиги,
поэтому один и тот же .apk встаёт на любую платформу — он помечен noarch.
Отдельных сборок под архитектуры не существует и не требуется.
Ограничение идёт от бинарника клиента, который пакет не содержит, а скачивает установщиком вендора. Готовые сборки TrustTunnel есть под:
CPU (uname -m) |
Типичные роутеры |
|---|---|
aarch64 |
почти все современные потребительские модели: Xiaomi, Keenetic, GL.iNet, Asus среднего и верхнего сегмента |
armv7l |
предыдущее поколение того же класса, часть Zyxel и Netgear |
mipsel |
массовые недорогие модели на ath79 и ramips: TP-Link, D-Link, Netis |
mips |
то же семейство с обратным порядком байт |
x86_64 |
мини-ПК, виртуальные машины, самосборные шлюзы |
Вместе это покрывает подавляющее большинство устройств, на которые вообще ставят OpenWrt.
Не поддерживаются: ARMv5 и ARMv6 (старые модели вроде некоторых
arm926ej-s и xscale), mips64, riscv64, powerpc. На них бинарника
клиента просто нет.
Проверка выполняется до установки чего бы то ни было и именно по
uname -m — тому же признаку, по которому выбирает сборку установщик вендора.
Раньше здесь сверялось имя пакетной архитектуры OpenWrt, и это давало ложный
пропуск: цели arm_* включают ARMv5 и ARMv6, где uname -m отдаёт armv5tel
или armv6l. Такой роутер проходил проверку, пакет ставился, и отказ приходил
уже от вендора — оставляя пункт в меню и службу без клиента.
Откройте Службы → TrustTunnel. Три страницы: «Состояние», «Настройки» и «Диагностика». Все настройки собраны на одной странице с вкладками, которые переключаются мгновенно, без перезагрузки.
Порядок настройки — сверху вниз по вкладкам страницы Настройки:
- Сервер — нажмите Импорт… и вставьте конфиг, который выдал ваш сервер TrustTunnel, либо заполните адрес, имя хоста, пользователя и пароль вручную.
- Списки — отметьте нужные списки сообщества и, если нужно, добавьте свои ссылки.
- Свои домены — при необходимости добавьте домены, которые нужно обходить или, наоборот, никогда не обходить.
- Общее — включите Запускать при загрузке, нажмите Сохранить и применить, затем Запустить на странице «Состояние».
Вкладка Сеть нужна редко: там MTU и внутренние параметры маршрутизации.
Принимаются оба вида, которые выдаёт сервер: текст файла конфигурации и
ссылка tt://. Они различаются автоматически, потому что setup_wizard
принимает их разными, взаимоисключающими флагами — подсунутая как файл ссылка
не разбирается вовсе.
Быстрый путь — Импорт конфига сервера: вставьте текст конфигурации,
которую выдал ваш сервер TrustTunnel, и поля заполнятся сами. Разбором
занимается штатная утилита setup_wizard из поставки клиента, поэтому формат
поддерживается ровно тот, который умеет сам TrustTunnel. Импорт забирает все
поля сервера: адреса, имя хоста, доступы, сертификат, custom_sni,
client_random, DNS-серверы, транспорт и флаги anti-DPI и проверки
сертификата.
Вручную нужно заполнить:
- Имя хоста — используется для TLS-сессии, а не для маршрутизации.
- Адреса —
хост:портили[ipv6]:порт. Можно указать несколько: клиент пингует их и сам выбирает самый быстрый. - Пользователь и Пароль.
Остальное имеет разумные значения по умолчанию. Менять стоит осознанно:
- Транспорт — HTTP/2 или HTTP/3 (QUIC). QUIC часто быстрее, но в некоторых сетях UDP режут.
- Обход DPI — включает противодействие глубокой инспекции пакетов.
- Постквантовый обмен ключами — включён по умолчанию.
- Не проверять сертификат — принимает любой сертификат. Только для самоподписанной установки, которой вы управляете.
- Закреплённый сертификат (PEM) — вставьте сюда сертификат сервера, если он самоподписанный. Тогда системное хранилище доверия не нужно.
Галочки по файлам allow-domains, сгруппированные по разделам, с размером каждого файла. Каталог берётся из GitHub API и кэшируется на сутки; кнопка Обновить каталог сбрасывает кэш.
Ниже — поле Свои URL списков: любой адрес, отдающий по домену на строку. Скачивается вместе со списками сообщества.
Скачать списки сейчас выполняет загрузку и перегенерацию правил немедленно. В остальное время списки обновляет cron ежедневно в 04:17. Галочка Обновлять автоматически отключает только расписание — кнопка продолжает работать всегда.
Два поля.
Обходить — эти домены пойдут через туннель дополнительно к выбранным
спискам. Поддомены сопоставляются автоматически: запись example.com
покрывает и www.example.com — так работает сопоставление доменов в
dnsmasq, и по той же причине запись для голого TLD (например, ua)
покрывает всю зону целиком.
Не обходить — эти всегда идут напрямую и имеют приоритет над всем
остальным. Работает даже когда домен есть в выбранном списке сообщества, и
даже когда его IP-адрес общий с обходным доменом (типичная ситуация для CDN):
исключение делает сам клиент по SNI, уже после того как ядро промаркировало
пакет. Принимает домен, *.домен, IP-адрес или CIDR-диапазон.
Домены с не-ASCII символами отбрасываются на этапе нормализации — используйте
punycode (xn--…). Число отброшенных записей видно в интерфейсе.
Показывает состояние службы, устройства туннеля, маршрутизации и nft-таблицы, размер набора обхода, число доменов в списках и время последнего обновления.
Три инструмента диагностики:
- Пинг сервера — по каждому адресу: потери и min/avg/max.
- Сравнить внешний IP — показывает внешний адрес через туннель и напрямую рядом. Если они одинаковые, трафик через туннель не идёт.
- Проверить домен — главный инструмент, когда «не работает». Вводите домен и получаете: нормализованное имя, в каких выбранных списках он найден, в какие адреса резолвится, попали ли они в набор обхода, и вердикт с объяснением причины. Учтите: проверка резолвит домен через роутерный dnsmasq, то есть попутно наполняет набор его адресами. Домен из списка, которого никто ещё не спрашивал, после проверки начнёт уходить в туннель — сама проверка этому и поспособствовала, а не обнаружила уже готовое состояние. Вердикт пойдёт в туннель после резолва означает ровно это.
Внизу — хвост журнала клиента.
Отдельная вкладка, которая прогоняет всю цепочку за один раз и выдаёт список: что проверено, каков вердикт и — если что-то не так — что с этим делать. Проверка запускается сама при открытии страницы; кнопка «Проверить снова» нужна для повторного прогона после исправлений. Проверки идут в том порядке, в котором работает пакет, поэтому читать список нужно сверху вниз и искать первое место, где всё разошлось.
| Группа | Что проверяется |
|---|---|
| Настройки | адрес сервера, доступы, имя хоста для TLS, режим, выбранные списки |
| Предпосылки | бинарник клиента, поддержка nftset в dnsmasq, устройство /dev/net/tun |
| Служба | включена в автозапуске, работает ли сейчас |
| Состояние ядра | устройство и его MTU, носитель туннеля, правило и таблица маршрутизации, таблица nftables, зона фаервола, размер набора обхода |
| Списки | число доменов, время обновления, опубликован ли конфиг для dnsmasq |
| Сеть | доступность сервера, идёт ли трафик через туннель |
Вердикты различают три состояния, и это различение важнее, чем кажется:
- в порядке — проверено и работает;
- проверьте — работает, но есть замечание; например туннель настроен, а носителя нет, потому что клиент ещё не установил соединение;
- проблема — не работает, и рядом написано, что сделать;
- пропущено — проверять нечего, потому что предыдущее звено не готово. Пропуск специально не показывается как отказ: при выключенной службе проверки ядра не должны краснеть.
Отдельно стоит понимать разницу между устройством и носителем туннеля. Устройство создаёт клиент, и если оно есть с нужным MTU, а маршрут к нему привязан — наша часть выполнена. Носитель появляется только когда клиент присоединился и туннель поднялся. Если устройство в порядке, а носителя нет, искать причину надо в журнале клиента, а не в маршрутизации.
Внизу страницы состояния — версия установленного пакета и версия клиента TrustTunnel, и у каждой своя проверка обновлений: пакет сверяется с релизами этого репозитория, клиент — с релизами TrustTunnelClient, потому что клиент выходит у вендора отдельно от пакета и новая версия одного не означает новой версии другого. Результат проверки кэшируется на шесть часов: у GitHub лимит 60 неавторизованных запросов в час на адрес, а страница опрашивается регулярно. Кнопка «Проверить сейчас» спрашивает GitHub немедленно, не дожидаясь конца кэша.
Проверка различает «обновлений нет» и «проверить не удалось» — второе
показывается отдельной строкой. Если сеть недоступна, показывается последний
сохранённый результат с пометкой. Отдельно назван и третий случай: установленная
версия новее последнего релиза (сборка из main либо кэш, который не удалось
обновить) — «актуальной версией» это не считается.
Обновиться — запустить install.sh снова: он останавливает службу, заменяет
пакет, ставит клиента и поднимает службу обратно, если она работала.
Настройки при этом сохраняются, потому что /etc/config/trusttunnel объявлен
в conffiles.
Режим переключается на вкладке «Настройки» и меняет принцип отбора трафика.
dnsmasq на роутере, отвечая на DNS-запрос по домену из выбранных списков, записывает полученные адреса в набор nftables. Правило в ядре помечает пакеты к этим адресам, и только они уходят в туннель. Остальной трафик остаётся в быстром пути ядра и идёт с полной скоростью канала.
Выбирайте этот режим, если нужен обход блокировок без потери скорости на
остальном трафике. Это основной сценарий, и он требует dnsmasq-full.
Помечается весь форвардный трафик из LAN. Списки в этом режиме не используются: исключения делает сам клиент по SNI на основе списка «не обходить» (плюс, если включена галочка Отправлять выбранные списки напрямую, домены выбранных списков сообщества передаются клиенту как исключения).
Выбирайте, если нужно скрыть весь трафик, а не обойти отдельные сайты.
| Обход по списку | Всё через VPN | |
|---|---|---|
| Скорость необойдённого трафика | полная, ядерный fast-path | ограничена userspace-обработкой |
| Выбранные списки | определяют, что идёт в туннель | не используются (кроме full_exclude_lists) |
| «Не обходить» | работает | работает |
Нужен dnsmasq с nftset |
да | нет |
| Зависит от того, чей DNS у клиентов LAN | да | нет |
Трафик самого роутера по умолчанию идёт напрямую в обоих режимах. Это
сделано намеренно: иначе клиент замкнулся бы на себя, а при сломанном туннеле
роутер стало бы нечем починить. Включается галочкой Маршрутизировать также трафик самого роутера
(include_router_traffic) отдельно.
Tun-устройство создаёт КЛИЕНТ, а не пакет, и имя выбирает сам (обычно tun0).
Задать его нельзя: в схеме конфигурации клиента такого параметра не
существует. Первоначально пакет создавал устройство сам и рассчитывал, что
клиент к нему присоединится, — это оказалось неверным допущением: клиент
молча создавал своё, а созданное нами оставалось без носителя, и помеченный
трафик уходил в мёртвый интерфейс.
Поэтому маршрут по умолчанию привязывается к устройству клиента после того, как оно появилось. Основной механизм — hotplug-скрипт: он срабатывает ровно тогда, когда устройство создано, и покрывает как первый запуск, так и пересоздание устройства после перезапуска клиента силами procd.
Дальше всё держится на трёх вещах:
- таблица маршрутизации
880с маршрутом по умолчанию через устройство клиента; - правило
ip rule fwmark 0x9527 lookup 880; - своя nft-таблица
inet trusttunnel, которая ставит эту метку.
Своя таблица, а не врезка в fw4, выбрана намеренно: её не смывает
fw4 reload, и она не привязывает пакет к внутренностям firewall4. Ruleset
для обоих режимов проверен реальным nft -c -f - на живом роутере.
Цепочка всегда пропускает без метки: трафик не из LAN, трафик к адресам самого сервера (иначе соединение клиента ушло бы в туннель, который он же и устанавливает) и трафик к приватным адресам.
Поток данных в режиме «обход по списку»:
allow-domains (выбранные файлы) ─┐
свои домены «обходить» ─┼─→ gen-lists ─→ /var/etc/trusttunnel/dnsmasq.conf
свои URL списков ─┘ nftset=/домен/4#inet#trusttunnel#tt_bypass4
nftset=/домен/6#inet#trusttunnel#tt_bypass6
server=/домен/<list_resolver>
Subnets/IPv4|IPv6/*.lst ────→ gen-lists ─→ /var/etc/trusttunnel/elements.nft
UCI /etc/config/trusttunnel ────→ gen-config ─→ /var/etc/trusttunnel/client.toml
свои домены «не обходить» ────→ exclusions = [...]
В режиме «всё через VPN» gen-lists не вызывается вовсе: dnsmasq.conf и
elements.nft не создаются. В client.toml попадают exclusions из списка
«не обходить» и, если включён full_exclude_lists, домены выбранных списков.
Механизм проверен на живом роутере целиком: при подготовленном конфиге запрос обходного домена с клиента LAN приводил к тому, что dnsmasq сам добавлял резолвнутые адреса в nftables-набор.
В таблице 880 рядом с маршрутом через устройство клиента лежит blackhole с большой
метрикой. Пока устройство живо, работает основной маршрут. Как только
устройство исчезает, ядро само убирает его маршрут, и помеченный трафик
попадает в blackhole — то есть отбрасывается, а не утекает к провайдеру.
Встроенный killswitch клиента при этом выключен, чтобы двое не управляли
фаерволом одновременно.
Для каждого домена из списков генерируется не только строка nftset=, но и
server=/домен/<резолвер>. Кто именно резолвит и чем защищён запрос,
определяет параметр list_dns с тремя значениями.
plain (по умолчанию) — обычный DNS на адрес из list_resolver. Адрес
резолвера статически добавляется в набор обхода, поэтому запросы именно по
обходным доменам уходят внутрь туннеля: провайдер их не видит и не может
подменить ответ. Если бы мог — в набор попали бы подставные адреса, и обход
перестал бы работать. Остальные запросы идут обычным путём, без лишней
задержки.
doh — шифрованный DNS. Служба поднимает на роутере локальный
https-dns-proxy отдельным экземпляром procd и направляет dnsmasq на него.
Запрос уходит шифрованным, но напрямую, а не через туннель — провайдер
видит факт обращения к резолверу, но не содержимое. Адрес резолвера в набор
обхода при этом не добавляется: шифрованному запросу туннель не нужен.
Этот режим нужен, когда важна фильтрация по своему профилю — например
NextDNS: https://dns.nextdns.io/<ваш-id>. По умолчанию он обслуживает только
домены из списков; как распространить его на всю сеть — ниже. Пакет
https-dns-proxy в
зависимостях не объявлен намеренно: шифрованный DNS нужен не всем, а лишние
400 КБ на роутере с 8 МБ флеша заметны. Если его нет, строки server= не
выдаются вовсе — домены резолвит провайдер, обход продолжает работать, а
диагностика говорит, что поставить.
Важно после
apk add https-dns-proxy. У пакета есть собственный init-сервис, и при установке он сам стартует и переписывает DNS всей сети на себя — ставитnoresolvи направляет dnsmasq на свои экземпляры127.0.0.1#5053/5054. Это рабочая конфигурация, и оставить её как есть правильно: сеть получает шифрованный DNS целиком, а наш экземпляр на 5460 продолжает обслуживать домены из списков. Проверить стоит одно — отвечают ли его резолверы:nslookup -po=5053 example.com 127.0.0.1 nslookup -po=5054 example.com 127.0.0.1Если один из них молчит, перестаёт открываться всё, кроме доменов из списков: с
noresolvдругих upstream у dnsmasq нет. Лечится это правкой его конфига — сменитеresolver_urlнеотвечающего экземпляра или удалите сам экземпляр:uci delete https-dns-proxy.@https-dns-proxy[1] uci commit https-dns-proxy && /etc/init.d/https-dns-proxy restartВыключать сервис целиком не надо. Прежде здесь стоял именно такой совет —
stop,disableи чистка его записей из конфига dhcp, — и он оставлял сеть в худшем положении, чем находил. Вместе с записями уходитnoresolv, dnsmasq откатывается на DNS провайдера из PPPoE, а тот подменяет ответы: проверено на живом роутере,instagram.comприходил как188.186.154.88вместо адреса Meta. Картина «работают только сайты из списков» при этом никуда не уходит — наш экземпляр отвечает ровно за списочные домены, и всё остальное достаётся провайдеру.Вкладка «Диагностика» проверяет это сама: она спрашивает каждый прописанный экземпляр и жалуется только на тот, который молчит.
Если включена галочка «применять этот резолвер ко всей сети»,
resolver_urlэтих экземпляров принадлежит нашей службе: правка руками переживёт только до следующего её старта. Менять адрес в этом режиме надо в настройках пакета.
По умолчанию заданный здесь резолвер обслуживает только домены из списков:
для каждого выдаётся строка server=/домен/127.0.0.1#5460, и больше ничего.
Весь остальной DNS сети идёт туда, куда его направил штатный
https-dns-proxy — обычно в Cloudflare. Отсюда самая частая претензия к этой
настройке: указали NextDNS, а проверялка показывает Cloudflare. Проверялка
права: test.nextdns.io в списках обхода не лежит.
Галочка «Применять этот резолвер ко всей сети» (doh_network) закрывает
разрыв. Пока служба работает, она переводит на ваш URL штатный
https-dns-proxy — все его экземпляры сразу, — а при остановке возвращает его
настройки. Конфиг dnsmasq при этом не трогаем вовсе: noresolv и
server=127.0.0.1#5053 прописывает его собственный init, у которого для этого
есть готовый механизм с бэкапом и откатом.
Переводятся именно все экземпляры, а не первый: их init не знает опции
disabled и поднимает каждую секцию, а dnsmasq держит все бездоменные
server= в одном пуле и раскидывает запросы по ним. Один забытый экземпляр
тихо отдавал бы часть запросов чужому резолверу.
Своим снипетом в conf-dir общий DNS забрать нельзя, и это проверено на живом
роутере отдельным экземпляром dnsmasq: wildcard-домен /#/ приоритета над
бездоменными server= не имеет и попадает с ними в тот же пул. При
server=127.0.0.1#5053 рядом с server=/#/127.0.0.1#5460 отвечал Cloudflare.
То есть наш резолвер обслуживал бы «иногда» — хуже честного «никогда».
Что стоит знать до включения:
- при старте и остановке службы перезапускается dnsmasq, поэтому сеть на долю секунды остаётся без DNS;
- если резолвер перестанет отвечать, без DNS останется вся сеть, а не
только домены из списков: у dnsmasq стоит
noresolv, других upstream нет; - свой экземпляр на
list_doh_portв этом режиме не поднимается, и строкиserver=для доменов из списков не выдаются — они указывали бы на тот же резолвер, что и общий upstream; - если вы правили
resolver_urlруками, пока галочка стояла, служба это заметит при остановке и НЕ станет затирать правку своим бэкапом — но и вернуть прежние значения тогда будет нечем; - по умолчанию галочка снята, и обновление пакета её не включает.
provider — своего резолвера не задаём, домены резолвит то, что уже
настроено на роутере.
Почему для plain невозможен DoT или DoH напрямую: значение уходит в
директиву dnsmasq server=, а она принимает только обычный адрес. Именно
поэтому шифрование сделано через локальный прокси, а не настройкой поля.
Ядро отбирает трафик по адресу, а адреса в набор кладёт dnsmasq — отвечая на запрос по домену из списка. Причём только на запрос, дошедший до upstream: ответ, выданный из собственного кэша dnsmasq, в набор не попадает. Отсюда два следствия, которые стоит знать.
Первое: применение настроек пересоздаёт таблицу nftables с нуля, поэтому накопленные адреса каждый раз пропадают. Служба вместе с этим сбрасывает кэш dnsmasq сигналом HUP — иначе следующий запрос клиента получил бы ответ из кэша, набор остался бы пустым, и обход молча не работал бы до истечения TTL.
Второе: пока домен никто не спросил через роутер, его адресов в наборе нет. Для доменов списков сообщества это нормально — их больше тысячи, набор наполняется по ходу дела. Свои домены служба резолвит сама сразу после применения настроек: человек только что ввёл домен руками и ждёт, что тот заработает, а его устройство обычно уже держит адрес в собственном кэше DNS и роутер не спрашивает вовсе.
Устройство со своим DoH- или DoT-резолвером не проходит через dnsmasq
роутера, поэтому его запросы никогда не попадают в этот механизм и
сопоставление по спискам для него не работает — если только не включена
галочка Перехватывать DNS клиентов (intercept_dns), которая
перенаправляет UDP/TCP 53 на роутер и блокирует 853.
Клиент читает свой конфиг один раз, при запуске, поэтому раньше любая правка настроек означала перезапуск: туннель рвался на несколько секунд. Хуже, что вместе с клиентом на это время снималась и nft-таблица — маркировать трафик становилось нечем, и помеченный трафик LAN уходил напрямую. То есть обычное сохранение настроек само открывало утечку, от которой пакет и защищает.
Теперь служба сравнивает применённое состояние (settings.tsv) с тем, что
выгружается из UCI, и делает ровно то, что нужно изменившимся ключам:
| Что изменилось | Что происходит | Туннель |
|---|---|---|
| Расписание автообновления списков | ничего: его читает cron во время работы | не рвётся |
| Выбранные списки, свои домены, адрес открытого резолвера, LAN-интерфейсы, killswitch, перехват DNS | правила пересобираются и перезаливаются в ядро, маршрут перепривязывается к живому устройству | не рвётся |
| Адрес и данные сервера, сертификат, MTU, режим работы, уровень журнала, тип резолвера для списков | клиент перезапускается, маршрутизация при этом сохраняется — трафик держит killswitch, а не утекает напрямую | рвётся |
| Номер таблицы, метка, включение и выключение службы | полный перезапуск с разбором маршрутизации: прежнюю таблицу и прежнее правило иначе нечем снять | рвётся |
Разбор устроен как список разрешённых ключей: любой ключ, которого в нём нет, получает полный перезапуск. Лишний перезапуск стоит нескольких секунд, а пропущенное применение выглядело бы как «интерфейс показывает новое значение, а работает старое» — такой отказ нечем диагностировать. Полнота списка против схемы настроек проверяется тестом, поэтому новый параметр нельзя добавить, забыв решить, как он применяется.
В полном режиме (full) правка списков тоже перезапускает клиента: при
включённом «Список исключений из выбранных списков» они попадают в конфиг
клиента, а не в правила dnsmasq.
| Путь | Содержимое |
|---|---|
/etc/config/trusttunnel |
Ваши настройки. Переживает обновление пакета |
/usr/share/trusttunnel/lists/ |
Скачанные списки. Переживают перезагрузку, не попадают в бэкап конфигурации |
/var/etc/trusttunnel/client.toml |
Сгенерированный конфиг клиента, права 600 |
/var/etc/trusttunnel/settings.tsv |
Настройки в плоском виде для генераторов, права 600 |
/var/etc/trusttunnel/endpoint.pem |
Закреплённый сертификат, если задан, права 600 |
/var/etc/trusttunnel/dnsmasq.conf |
Сгенерированные правила для dnsmasq |
/var/etc/trusttunnel/elements.nft |
Статические элементы наборов nftables |
/var/etc/trusttunnel/own.domains |
Свои домены после нормализации — их служба резолвит сама, чтобы наполнить набор, не дожидаясь запроса клиента |
/tmp/dnsmasq.<секция>.d/trusttunnel.conf |
Копия (не ссылка) правил, которую читает dnsmasq; путь зависит от имени секции dnsmasq в UCI |
/opt/trusttunnel_client/ |
Бинарники trusttunnel_client и setup_wizard |
Файл в conf-dir dnsmasq — обычная копия, а не символическая ссылка. Это
принципиально: сам dnsmasq на OpenWrt работает внутри ujail, куда
/var/etc/trusttunnel не смонтирован, и по ссылке пройти не может.
Проверено на живом роутере: с симлинком в журнале — cannot access …/trusttunnel.conf: No such file or directory, FAILED to start up, и
procd уводит dnsmasq в crash loop, то есть установка убивает DNS всей сети.
С обычным файлом-копией dnsmasq поднимается штатно.
Ядро работает независимо от LuCI, поэтому настроить его можно и через UCI.
# Сервер
uci set trusttunnel.endpoint.hostname='vpn.example.com'
uci add_list trusttunnel.endpoint.address='203.0.113.10:443'
uci set trusttunnel.endpoint.username='alice'
uci set trusttunnel.endpoint.password='секрет'
# Режим
uci set trusttunnel.main.mode='selective'
# Списки
uci add_list trusttunnel.lists.source='Russia/inside-raw.lst'
uci add_list trusttunnel.lists.source='Services/youtube.lst'
uci add_list trusttunnel.lists.subnet='Subnets/IPv4/telegram.lst'
# Свои домены
uci add_list trusttunnel.domains.bypass='example.com'
uci add_list trusttunnel.domains.direct='bank.example'
# Включить и запустить
uci set trusttunnel.main.enabled='1'
uci commit trusttunnel
/etc/init.d/trusttunnel enable
/etc/init.d/trusttunnel startСкачать списки и применить правила без перезапуска туннеля:
/etc/init.d/trusttunnel update_listsПосмотреть, что получилось:
cat /var/etc/trusttunnel/lists.summary
/usr/libexec/trusttunnel/routing status /var/etc/trusttunnel/settings.tsvКонфигурация в /etc/config/trusttunnel. Генерируемые из неё файлы клиента
лежат в /var/etc/trusttunnel/ и правке не подлежат — они
перезаписываются при каждом запуске.
| Опция | По умолчанию | Описание |
|---|---|---|
enabled |
0 |
Запускать службу при загрузке |
mode |
selective |
selective — обход по списку, full — всё через VPN |
full_exclude_lists |
0 |
Только для full: отдать домены выбранных списков клиенту как исключения. Тысячи записей увеличивают расход памяти |
log_level |
info |
info, debug, trace |
| Опция | По умолчанию | Описание |
|---|---|---|
hostname |
— | Имя хоста для TLS-сессии, обязательно |
address |
— | Список хост:порт, обязательно хотя бы один |
username, password |
— | Авторизация, обязательны |
protocol |
http2 |
http2 или http3 |
anti_dpi |
0 |
Противодействие DPI |
post_quantum |
1 |
Постквантовый обмен ключами |
skip_verification |
0 |
Принимать любой сертификат |
certificate |
— | Закреплённый сертификат в формате PEM |
has_ipv6 |
1 |
Сервер умеет IPv6 |
dns_upstream |
— | Список DNS-серверов для запросов внутри туннеля. Пусто — AdGuard DNS без фильтрации |
custom_sni |
— | Имя в TLS-рукопожатии вместо hostname, если сервер так настроен |
client_random |
— | Префикс client random для защиты сервера от сканеров, hex[/маска]; без него такой сервер не пускает |
| Опция | По умолчанию | Описание |
|---|---|---|
mtu |
1350 |
MTU на устройстве |
early_ack |
1 |
Читать SNI до того, как соединение установлено: рекомендация вендора при внешнем DNS и wildcard-исключениях. Клиент 1.1.5+ |
scannable_ports |
443,80,8080,8008,853 |
Порты, на которых читается SNI; диапазон — 8080:8090 |
preresolve |
1 |
Резолвить домены-исключения заранее, в фоне |
preresolve_max |
50 |
Сколько исключений резолвить за проход |
tcp_recv_buf, tcp_send_buf |
0 |
Буферы TCP-окна на соединение внутри туннеля, байт; 0 — 256 КБ клиента. Клиент 1.0.63+ |
table |
880 |
Таблица маршрутизации |
fwmark |
0x9527 |
Метка фаервола |
lan_devices |
— | Интерфейсы, чей форвардный трафик рассматривается. Пусто — взять устройство сети lan. Нужно задать вручную, если есть гостевая сеть |
blackhole_on_down |
1 |
Отбрасывать помеченный трафик при упавшем туннеле вместо утечки к провайдеру |
include_router_traffic |
0 |
Гнать в туннель и трафик самого роутера |
list_dns |
plain |
Кто резолвит домены из списков: plain — обычный DNS через туннель, doh — шифрованный через локальный прокси, provider — то, что настроено на роутере. Только для selective |
list_resolver |
1.1.1.1 |
Адрес резолвера для режима plain. Принимает адрес#порт |
list_doh_url |
пусто | Адрес DoH-резолвера для режима doh, например https://dns.nextdns.io/<id> |
list_doh_port |
5460 |
Локальный порт, на котором слушает поднимаемый нами https-dns-proxy. Не используется при doh_network |
doh_network |
0 |
Сделать резолвер из list_doh_url DNS всей сети, а не только доменов из списков: служба переводит на него штатный https-dns-proxy, пока работает, и возвращает его настройки при остановке. Только для doh |
intercept_dns |
0 |
Перехватывать DNS клиентов: редирект порта 53 на роутер и блокировка 853. Только для selective |
| Опция | По умолчанию | Описание |
|---|---|---|
auto_update |
1 |
Обновлять списки по расписанию |
update_interval |
daily |
Зарезервировано; расписание задаётся строкой cron |
source |
— | Список файлов allow-domains, например Russia/inside-raw.lst |
subnet |
— | Список файлов подсетей, например Subnets/IPv4/telegram.lst |
url |
— | Список произвольных URL со своими списками |
| Опция | Описание |
|---|---|
bypass |
Домены, которые гнать через туннель дополнительно к спискам |
direct |
Домены, IP или CIDR, которые всегда идут напрямую. Приоритет над всем остальным |
logread -e trusttunnel # всё от пакета и клиента
logread -f -e trusttunnel # следить в реальном времениЖурнал клиента идёт в logd, то есть в кольцевой буфер в памяти: без записи
во флеш и без ротации.
# Что видит сам пакет
/usr/libexec/trusttunnel/routing status /var/etc/trusttunnel/settings.tsv
# Устройство, правило, таблица
ip link show "$(cat /var/etc/trusttunnel/device)" # устройство клиента
ip rule show | grep 880
ip route show table 880
# Наборы: что реально попало под обход
nft list set inet trusttunnel tt_bypass4
nft list set inet trusttunnel tt_endpoint4Проще всего — вкладка Состояние и проверка → Сравнить внешний IP. Вручную:
curl --interface "$(cat /var/etc/trusttunnel/device)" https://api.ipify.org # внешний IP через туннель
curl https://api.ipify.org # внешний IP напрямуюРазные адреса — туннель работает.
Служба не запускается, в журнале про CA-бандл. Не установлен ca-bundle.
Поставьте его, либо закрепите сертификат сервера в настройках, либо отключите
проверку сертификата.
В журнале жалобы на сертификат сразу после включения роутера. Часы не синхронизировались. Проверьте, что NTP работает.
Обход не работает для конкретного сайта. Проверьте домен инструментом Проверить домен. Частые причины: домена нет ни в одном выбранном списке; устройство пользуется своим DoH-резолвером и запрос не проходит через dnsmasq роутера; домен попал в список «не обходить».
Обход перестал работать после смены режима. В режиме «всё через VPN» списки не используются. Это ожидаемо.
Скорость упала на всём трафике. Вероятно, включён режим «всё через VPN». Для обхода блокировок нужен режим «обход по списку» — он не трогает остальной трафик.
Набор tt_bypass4 пустой. Значит dnsmasq ещё не отвечал на запросы по
обходным доменам. Наборы наполняются по факту резолва, а не заранее.
Обратитесь с клиента LAN к любому домену из списка и проверьте снова.
Это честный список того, чего пакет не умеет — и что с этим делать.
- Устройство со своим DoH или DoT обходит сопоставление по спискам. Его
DNS-запросы не проходят через dnsmasq роутера, поэтому адреса не попадают
в набор обхода. Средство — галочка «перехватывать DNS клиентов»
(
intercept_dns), но она ломает сценарии, где свой резолвер нужен намеренно, поэтому выключена по умолчанию. - Запись списка вида
example.comсопоставляется вместе со всеми её поддоменами. Так работает сопоставление доменов в dnsmasq. Из этого же следует, что запись для голого TLD (например,ua) покрывает всю зону целиком — при выборе таких списков это стоит иметь в виду. - Домены с не-ASCII символами отбрасываются. Используйте punycode
(
xn--…). Число отброшенных записей видно в интерфейсе. - Трафик самого роутера по умолчанию идёт напрямую. Включается отдельной
галочкой «Маршрутизировать также трафик самого роутера»
(
include_router_traffic). - Домены списков сообщества попадают в набор по первому запросу, а не
сразу. Адрес появляется в наборе, когда dnsmasq ответит на запрос по этому
домену; своих доменов это не касается — их служба прогревает сама, а для
тысячи с лишним доменов списков столько запросов при каждом применении это
совсем другая цена. Устройство, у которого адрес уже лежит в собственном
кэше DNS, роутер не спросит: если сайт продолжает идти напрямую, сбросьте
кэш на устройстве (
ipconfig /flushdns, перезапуск браузера). - Общий IP-адрес у CDN может протащить в туннель посторонний домен. Ядро отбирает трафик по адресу, а не по имени, поэтому один IP на несколько имён приводит к общей маркировке. Точное исключение — список «не обходить»: клиент сверяет его по SNI, уже после отбора по адресу, а не по IP.
- Гостевые сети не подхватываются автоматически. Укажите их интерфейсы
в
lan_devices. - Поддерживается только OpenWrt 25.12 и новее. Пакет рассчитан на
apk, пришедший на сменуopkg; поддержки более старых версий не будет. - Несколько профилей серверов и автопереключение между ними не реализованы. Один сервер; несколько адресов одного сервера — можно.
sh -c "$(wget -O - https://raw.githubusercontent.com/NooBiToo/TrustTunnelOpenWrt/main/install.sh)"Повторный запуск установщика обновляет и пакет, и бинарник клиента.
Настройки в /etc/config/trusttunnel не затрагиваются.
# Остановить и выключить службу
/etc/init.d/trusttunnel stop
/etc/init.d/trusttunnel disable
# Удалить пакеты. Языковой пакет обязательно в том же вызове: он зависит от
# основного, и `apk del luci-app-trusttunnel` в одиночку молча ничего не
# сделает — сообщит "not removed due to" и выйдет с нулевым кодом.
apk del luci-i18n-trusttunnel-ru luci-app-trusttunnel
# Убрать бинарники клиента и скачанные списки
rm -rf /opt/trusttunnel_client /usr/share/trusttunnel
# Убрать задание cron
sed -i '/trusttunnel update_lists/d' /etc/crontabs/root
/etc/init.d/cron restartЗона фаервола trusttunnel и правило переброса из lan остаются в
/etc/config/firewall — удалите их вручную, если больше не нужны:
uci show firewall | grep trusttunnel # найти секции зоны и forwarding
uci delete firewall.<секция_зоны>
uci delete firewall.<секция_forwarding>
uci commit firewall
/etc/init.d/firewall restartПроверить, что ничего не осталось в ядре:
nft list table inet trusttunnel # должно ответить, что таблицы нет
ip rule show | grep 880 # должно быть пусто
ip route show table 880 # маршрутов не должно бытьНастройки в /etc/config/trusttunnel остаются после удаления пакета — так
переустановка не теряет конфигурацию. Удалите файл вручную, если это не
нужно.
Пакет бесплатный. Если он вам помог, можно поддержать разработку криптовалютой — адреса ниже.
TON
UQD_JkHxRPrnkVPV560LT5zshYhe4ErkH-KALsKaNgPkJRmx
Tron (TRC-20, например USDT)
TJCjmqQu5p8g9DbVPY7FdC59LvtZCcjFVn
Ethereum / Polygon (EVM) — один адрес на обе сети:
0xd886FFA25b8816dDe1b7339D1ae2Ea4Ac9624b45
Solana
3XZGBNf2FuJ1ZKFrtWZXxLtfFKqxEVfW2JnmUuGtdqMx
OpenWrt, LuCI, TrustTunnel, VPN для роутера, VPN-клиент, обход блокировок, обход DPI, выборочный обход сайтов, split tunneling, selective routing, списки доменов, allow-domains, itdoginfo, podkop, dnsmasq, nftables, fwmark, killswitch, TUN, procd, apk, прошивка роутера, YouTube на роутере, Telegram, Discord, обход цензуры, установка одной командой
Пакет распространяется по лицензии GPL-2.0. Полный текст — в файле LICENSE.
- TrustTunnel — протокол, сервер и клиент, Apache 2.0.
- itdoginfo/allow-domains — списки доменов сообщества.
- itdoginfo/podkop — эталонная реализация обхода по спискам на OpenWrt, GPL-2.0. Подход к отбору трафика через dnsmasq и nftables сверялся с ней.