Наблюдатель за блокировками для роутеров на OpenWrt с netshift или podkop.
Он сам находит сайты, которые у провайдера не открываются напрямую, но открываются через туннель, проверяет каждый опытом и добавляет подтверждённые в список netshift/podkop. Списки, которые вы ведёте вручную, остаются как были: blockwatch дополняет их тем, чего в них не хватает. Соединения при добавлении не рвутся — sing-box перечитывает список на лету.
Находит. Своя таблица nftables только считает признаки, ничего не блокирует и не перенаправляет:
| признак | что видно |
|---|---|
| сброс | сервер присылает RST сразу после приветствия TLS |
| нет ответа | SYN уходит, SYN-ACK не приходит |
| молчит | наши пакеты с данными уходят, обратно — ни одного |
| обрыв | соединение установлено и замерло: сервер не ответил даже на TLS; или передача встала на 12–24 КБ сразу в нескольких соединениях к адресу; или браузер переспрашивает, а ответа нет |
| замер | одно соединение: запросы ушли, ответ встал на 2–24 КБ (слабый признак, в очередь последним) |
| обход | медленный обход сайтов, которые в доме уже открывали |
Учитывается только трафик мимо туннеля: то, что уже идёт через netshift/podkop, blockwatch не касается. Кандидатов на загруженной сети бывает сотня, поэтому первыми проверяются находки «1 из 2», затем свежие обрывы — то, во что люди упираются прямо сейчас.
Проверяет. Подозрение — ещё не блокировка. Для каждого кандидата blockwatch сам делает три запроса: к сайту напрямую, к конкретному адресу напрямую и к тому же адресу через туннель. Блокировкой считается только случай, когда напрямую не отвечают ни сайт, ни адрес, а через туннель страница приходит целиком. Передача, оборванная на середине, считается отказом, даже если заголовки успели прийти с кодом 200 — именно так выглядит самая незаметная блокировка. Обрыв случается после ~16 КБ от сервера, поэтому маленький ответ (корень CDN, редирект) blockwatch запрашивает ещё раз подряд в том же соединении, пока не наберётся больше: оборванное соединение на этом замрёт. Если соединение установилось и замерло, решает тот адрес, в который упирается браузер: проверка «сайта целиком» заново спрашивает DNS и может попасть на соседний, рабочий узел CDN. Добавляется находка только после двух совпадений в разных проверках: второе — не раньше чем через confirm_minutes (по умолчанию минута). Сразу после подтверждения адреса сайта на полчаса отправляются в sing-box: он по SNI пускает в туннель только этот домен, поэтому сайт открывается, даже если браузер или система закэшировали его настоящий адрес. На практике хватает открыть сайт, подождать минуту-две и обновить страницу.
Имя сайта берётся из журнала запросов dnsmasq — то, которое спросил браузер, даже если ответ пришёл через цепочку CNAME (у сайтов на Vercel, Cloudflare и CDN адрес приходит на служебное имя хостинга). Вердикт хранится на пару «адрес + имя»: за одним адресом хостинга живут сотни сайтов. Если на подтверждении не ответил и туннель, набранное совпадение не сбрасывается (один раз) — вдруг туннель сбоил сам.
Добавляет. Подтверждённые домены дописываются в /etc/blockwatch/domains.txt (Local Domain List netshift/podkop), а адреса — без имени или общие, где живут сотни сайтов (Cloudflare, Vercel, CDN), — в /etc/blockwatch/subnets.txt (Local Subnet List). blockwatch вписывает их ещё и в уже собранные списки sing-box и в nft-множество бэкенда, поэтому сайт начинает открываться через пару секунд, без перезапуска.
Убирает. Домены из списка раз в recheck_hours открываются напрямую; открылся целиком remove_after раз подряд — домен уходит из списка. Адрес так не перепроверить (трафик к нему уже в туннеле), поэтому у адреса срок ip_ttl_days. Вручную — кнопкой «Убрать» на вкладке или blockwatch remove.
- Не видит геоблоки — «сервис недоступен в вашей стране». Для роутера такой сайт выглядит исправным: соединение и TLS проходят, отказ приходит внутри шифрованного ответа.
- Не добавляет видеоузлы Google, имена ваших собственных серверов из конфига sing-box и, как правило, российские домены (
.ru,.su,.рф): через зарубежный выход они часто отказывают. Исключение — российский домен на зарубежном хостинге, который провайдер режет обрывом (так бывает с Vercel и Cloudflare): если соединения к нему обрываются на середине, blockwatch его проверяет и добавляет, когда напрямую сайт не открывается, а через туннель открывается (zones_on_cut). - Не добавляет адреса Google и YouTube (ни по именам, ни по сетям самого Google), частные адреса и адреса ваших прокси-серверов.
- Не трогает домены и адреса, которые уже есть в каком-либо списке netshift/podkop, — ни в туннеле, ни в исключениях. Если YouTube у вас нарочно идёт мимо туннеля, так и останется.
- Не видит сайтов, если браузер спрашивает DNS мимо роутера (Secure DNS / DoH в браузере): тогда и netshift направляет их только по адресам.
- Не меняет настройки netshift/podkop и не перезапускает их сам — кроме команды
blockwatch hook, которую вы запускаете явно, и самовосстановленияauto_rehook, если вы его включите. - Смотрит только IPv4.
- OpenWrt 24.10 или новее (apk и opkg поддерживаются оба)
- netshift или podkop. Чтобы проверять, открывается ли сайт через туннель, blockwatch берёт служебный socks-вход sing-box (он есть при включённой загрузке списков через прокси), а если его нет — интерфейс секции типа vpn (например
AWG2): так netshift ставит и Zapret-Manager. Можно задать и вручную:socksилиtunnel_interfaceв «Настройках». - Zapret-Manager (Forkozz) удаляет локальные файлы-списки при каждом сохранении секции, поэтому там
blockwatch hookподключает списки как внешние по ссылке:http://127.0.0.1/blockwatch/domains.txtиsubnets.txtотдаёт веб-сервер роутера (uhttpd), и Forkozz их сохраняет. Новые домены и адреса blockwatch сразу дописывает в живые наборы sing-box, а при каждом заходе сверяет их со своими файлами — так ничего не теряется, даже если список не скачался. Режим выбирается сам (hook_mode:auto,local,remote). - dnsmasq как DNS-сервер роутера (у netshift и podkop так по умолчанию)
sh -c "$(curl -fsSL https://raw.githubusercontent.com/xDarkOne/blockwatch/main/install.sh)"Установщик сам поставит недостающие jq и curl, разложит файлы и в конце покажет результат blockwatch doctor.
Затем подключите списки к нужной секции netshift/podkop:
blockwatch hook mainКоманда подключает оба списка — доменов и адресов — и один раз перезапускает netshift/podkop, чтобы он их собрал. Если подключён только список доменов (установка до версии с адресами), повторный hook докинет список адресов. Вручную: секция → Local Domain Lists → /etc/blockwatch/domains.txt и Local Subnet Lists → /etc/blockwatch/subnets.txt → сохранить.
Смотреть, что происходит: LuCI → Службы → Blockwatch.
Ссылки и файлы списков для ручного подключения (например, в Forkozz) показывают вкладка «Обзор» и blockwatch doctor: http://127.0.0.1/blockwatch/domains.txt и http://127.0.0.1/blockwatch/subnets.txt.
| что | зачем | при удалении |
|---|---|---|
| журнал запросов dnsmasq включается | узнать имя сайта по адресу | возвращается как было |
| буфер системного журнала — не меньше 512 КБ (в ОЗУ) | чтобы имена не вытеснялись раньше, чем их прочитают | возвращается как было |
| учёт трафика conntrack (если был выключен) | распознавать обрывы | возвращается как было |
| задание cron раз в 2 минуты | заходы наблюдателя | удаляется |
таблица nftables inet blockwatch |
только счётчики | удаляется |
Рабочее состояние хранится в /tmp, чтобы не изнашивать флеш. На флеш пишутся списки доменов и адресов, журнал находок (последние 500 записей) и вердикты — раз в час или сразу после находки.
blockwatch doctor проверить установку: бэкенд, socks-вход, туннель, проблемы
blockwatch report [all] найденные блокировки (all — вместе с живыми и лежащими)
blockwatch hook [секция] подключить списки доменов и адресов к netshift/podkop
blockwatch unhook отключить оба
blockwatch export перенести подтверждённое прямо сейчас
blockwatch remove <x> убрать домен или адрес из списка
blockwatch status состояние в JSON (для вкладки LuCI)
Журнал находок: /etc/blockwatch/blockwatch.log.
/etc/config/blockwatch:
| параметр | по умолчанию | |
|---|---|---|
enabled |
1 |
выключить заходы |
backend |
auto |
netshift, podkop или автоопределение |
auto_export |
1 |
0 — только находить, ничего не добавлять |
confirm_minutes |
1 |
второе подтверждение находки — не раньше чем через столько минут |
max_per_day |
30 |
предел добавлений в сутки |
max_domains |
500 |
предел всего списка: дальше новые не добавляются, на вкладке предупреждение |
auto_remove |
1 |
убирать из списка то, что снова открывается напрямую |
recheck_hours |
24 |
как часто перепроверять каждый домен списка (после удачной проверки — через 6 часов) |
remove_after |
3 |
сколько удачных прямых проверок подряд нужно, чтобы убрать домен |
| auto_rehook | 0 | для подключения локальными файлами: вернуть списки, если они пропали из настроек бэкенда, и перезапустить его (не чаще трёх раз в час) |
| max_subnets | 200 | предел списка адресов |
| ip_ttl_days | 30 | срок адреса в туннеле |
| never_ip_suffix | Google, YouTube | адреса с такими именами не добавляются никогда |
Всё это меняется и на вкладке LuCI → Службы → Blockwatch, во вкладке «Настройки». Там же сводка, поиск по спискам, журнал и кнопка «Убрать» у каждого домена и адреса (blockwatch remove <домен|адрес> в консоли).
Адреса. На одном адресе Cloudflare или CDN живут сотни сайтов, а в соединении имени сайта нет — только адрес, и имя из журнала DNS бывает чужим. Поэтому, если адрес без имени или общий, а блокировка подтверждена по самому адресу, в туннель уходит адрес: он дописывается в /etc/blockwatch/subnets.txt (подключается как Local Subnet List), в набор sing-box и в nft-множество netshift/podkop — на лету. Проверить адрес напрямую после этого нельзя, поэтому у него срок ip_ttl_days; потом он возвращается на прямую и, если его всё ещё режут, находится заново. Адреса Google и YouTube, частные, свои прокси-серверы и адреса с именами из стоп-списка не добавляются никогда.
Перепроверка списка. Блокировки снимают так же, как ставят, поэтому blockwatch время от времени открывает каждый домен из своего списка напрямую — по настоящему адресу, мимо туннеля. Открылся целиком remove_after раз подряд — домен уходит из списка и из sing-box на лету; вернётся блокировка — найдётся заново. Домены, чей адрес уже есть в IP-списках netshift/podkop, так не проверить: трафик к ним уходит в туннель и с самого роутера. Они остаются в списке и помечены на вкладке.
Сколько места занимает: рабочее состояние в ОЗУ (/tmp/blockwatch) — около 300 КБ и больше не растёт; на флеше — список доменов (~40 байт на домен), журнал находок (последние 500 записей) и вердикты, которые сохраняются раз в час.
| never_suffix | ru su xn--p1ai googlevideo.com gvt1.com | что не добавлять никогда |
| zones_on_cut | 1 | записи без точки (ru, su, xn--p1ai) — целые зоны: домен из них всё же проверяется, если к нему видны обрывы |
| socks, tunnel_interface, dns, fakeip_range, fakeip_mark | из настроек бэкенда | ручная настройка, если автоопределение не справилось |
Всё, чем netshift и podkop отличаются друг от друга, — имена таблиц, метки, адреса — blockwatch читает из их собственного constants.sh и конфига sing-box.
sh -c "$(curl -fsSL https://raw.githubusercontent.com/xDarkOne/blockwatch/main/install.sh)" -- uninstallСписок доменов и журнал останутся в /etc/blockwatch. Чтобы убрать всё, включая подключение к netshift/podkop, добавьте --purge.
tests/run-rootfs.sh 24.10 и tests/run-rootfs.sh 25.12 прогоняют установку, распознавание netshift и podkop, подключение и удаление в корневой ФС OpenWrt соответствующей версии. То же самое выполняется в CI при каждом изменении.
GPL-2.0, как у netshift и podkop.