Детектор торрентов для Remnawave: ловим то, что пропускает штатный блокировщик

Штатный Torrent Blocker в ноде Remnawave опирается на сниффер xray, а тот видит лишь малую часть торрент-трафика: современные клиенты шифруют соединения с пирами, и опознавать в них BitTorrent не по чему. В этой статье вы поставите открытый детектор fsnt-torrent-blocker, который закрывает этот пробел двумя независимыми сигналами, сначала посмотрите его находки без банов, а потом переведёте в боевой режим.

Детектор — дополнение к штатной схеме, а не замена ей. Он только находит нарушителя и отдаёт отчёт ноде; банит, считает срок и рассылает уведомления по-прежнему плагин Torrent Blocker. Если база не настроена — сниффинг, правила маршрутизации, outbound и сам плагин, — детектору некому отдавать находки, и он ничего не сделает.

Что понадобится

  • VPS ForestsNet Cloud с доступом по SSH под root или пользователем с sudo.
  • Уже настроенная штатная блокировка по шагам из статьи Как заблокировать торренты на своём VPN или прокси: включён сниффинг, заданы правила маршрутизации, заведён outbound и включён плагин Torrent Blocker в ноде. Без этого детектор бесполезен.
  • Remnawave Node версии 2.7.0 или новее, контейнер ноды запущен с cap_add: NET_ADMIN.
  • Access-лог xray не выключен: в конфиге log.access не должен быть "none". Без лога детектору не с чем работать.
  • systemd и python3 версии 3.6 или новее. Если python3 в системе нет, установщик поставит его сам: внешних зависимостей у детектора нет, хватает стандартной библиотеки.

Почему штатного блокировщика не хватает

Документация Remnawave прямо оговаривает: сниффер xray опознаёт BitTorrent примерно в 10–30% случаев. Мимо проходят три класса трафика.

Шифрованные пиры. В qBittorrent и большинстве клиентов протокольное шифрование MSE/PE включено по умолчанию. Открытого рукопожатия BitTorrent protocol в TCP просто нет — опознавать нечего.

DHT. Распределённая таблица, через которую клиент находит пиры без трекера, живёт поверх UDP. Отдельного сниффера для неё в xray нет вовсе.

Правила по портам и доменам трекеров. Пиры ходят по голым IP на случайных высоких портах. Ни geosite:category-public-tracker, ни список портов их не ловят — туда попадают только обращения к трекерам, а это малая часть обмена.

Замер на живой ноде. Клиент с активной раздачей за 75 секунд открыл соединения к 102 адресам на 91 разном порту. Весь этот трафик ушёл в DIRECT, штатный блокировщик не сработал ни разу.

Как устроена блокировка торрентов в целом и что делать, если у вас не Remnawave, а WireGuard или OpenVPN, разобрано в той же статье — Как заблокировать торренты на своём VPN или прокси.

Как детектор ловит торренты

Сигнал 1. Веер соединений

Детектор читает access-лог xray и для каждого пользователя считает, к скольким разным адресам и на сколько разных портов тот обратился за скользящее окно — по умолчанию за 60 секунд.

Торрент-клиент открывает соединения к десяткам пиров, и у каждого пира свой порт. Такого веера не даёт ни браузер, ни мессенджер, ни видеозвонок: у обычного пользователя за минуту набирается до двух десятков адресов на высоких портах, у торрентящего — 50–150.

Соединения по доменным именам, к частным адресам и на порты веба, DNS, почты, мессенджеров и STUN/TURN в подсчёт не идут. Именно так ловятся шифрованные MSE/PE-пиры, у которых нет никакой сигнатуры.

Сигнал 2. btguard — UDP-контрплан

Вместе с детектором ставится nft-таблица btguard. Она дропает на выходе то, что сниффер xray по UDP пропускает:

  • DHT (BEP 5) — по началу bencode-словаря запроса и ответа;
  • uTP-хендшейк (BEP 29) — по полям заголовка пакета ST_SYN;
  • connect к UDP-трекеру (BEP 15) — по магическому числу протокола и нулевому коду действия.

Каждый дроп попадает в kernel-лог вместе с адресом пира. Детектор читает этот лог и восстанавливает, кто именно торрентил: тот же поток есть в access-логе как udp:<пир>:<порт> email:<клиент>, так что по паре «адрес-порт» находится клиент.

Сигнатуры здесь строгие, и это принципиально. Наивное правило на uTP вида 0x4100 ловит медиа-трафик TURN и WebRTC: в замере это 176 ложных пакетов за 90 секунд. Правило, проверяющее поля заголовка ST_SYN целиком, даёт ноль ложных. Логика сверена с форком Xray-core от Jolymmiles, где эти проверки разобраны по полям.

Что происходит после срабатывания

Детектор отправляет отчёт во внутренний вебхук ноды — тот самый, куда отчитывается и сам xray. Дальше нода работает штатно: банит IP клиента в nftables на срок blockDuration с учётом ignoreLists, рвёт его текущие соединения и передаёт отчёт в панель, а панель — в Telegram и в вебхук torrent_blocker.report.

Адрес вебхука детектор каждый раз берёт из работающего конфига xray, поэтому переживает перезапуски ноды без донастройки. Поддержаны оба поколения ноды — и с абстрактным сокетом, и с файловым.

Установка

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

curl -fsSL https://raw.githubusercontent.com/forestsnet/torrent-blocker/main/install.sh | sudo bash -s -- --dry-run
journalctl -u fsnt-torrent-blocker -f

Установщик кладёт детектор в /usr/local/sbin/fsnt-torrent-blocker, настройки — в /etc/default/fsnt-torrent-blocker, заводит юнит fsnt-torrent-blocker.service и правила btguard (/etc/nftables.d/btguard.nft плюс oneshot-юнит btguard.service). Флаг --no-btguard ставит только веерный детект по логу, без nft-правил.

Перед тем как включить сервис, установщик прогоняет selftest: находит xray, вебхук и лог, отправляет пробный отчёт без пользователя (нода проверит формат, но никого не забанит) и заведомо битый отчёт, который нода должна отвергнуть. Если что-то не сошлось — сервис не включается.

Посмотрели находки, убедились, что ложных нет, — переводите в боевой режим:

curl -fsSL https://raw.githubusercontent.com/forestsnet/torrent-blocker/main/install.sh | sudo bash -s -- --live

Повторный запуск установщика обновляет детектор и сохраняет ваши настройки.

Если узлов несколько, поставьте сразу на все со своей машины. Детектор вшит в install.sh целиком, поэтому доступ сервера к GitHub не нужен:

for h in 10.0.0.1 10.0.0.2 10.0.0.3; do ssh deploy@$h 'sudo bash -s' < install.sh; done

Настройка порогов

Пороги задаются флагами при установке и потом правятся в /etc/default/fsnt-torrent-blocker — после правки перезапустите сервис. Флаг установки и переменная конфига означают одно и то же:

  • --window N / FTB_WINDOW, по умолчанию 60 — окно наблюдения в секундах.
  • --min-hosts N / FTB_MIN_HOSTS, по умолчанию 25 — порог по числу разных адресов назначения за окно.
  • --min-ports N / FTB_MIN_PORTS, по умолчанию 20 — порог по числу разных портов назначения за окно. Сработает, только если выполнены оба условия сразу.
  • --cooldown N / FTB_COOLDOWN, по умолчанию 300 — не отправлять повторный отчёт на тот же IP чаще, чем раз в столько секунд.
  • --max-reports N / FTB_MAX_REPORTS_PER_MIN, по умолчанию 10 — предохранитель: если отчётов в минуту больше, отправка встаёт на паузу на 10 минут.
  • --ignore-ports L / FTB_IGNORE_PORTS, по умолчанию пусто — дополнительные порты назначения через запятую, которые не считать веером.
  • --no-btguard / FTB_BTGUARD, по умолчанию auto — auto включает корреляцию с btguard, если nft-таблица есть; off оставляет только веер; on форсирует.
  • --dry-run и --live / FTB_DRY_RUN, по умолчанию 0 — режим наблюдения против боевого.

Пороги подобраны по замерам на реальном трафике, и между легальным трафиком и торрентом здесь широкий зазор: самые «веерные» легальные сервисы — P2P-раздача обновлений, WebRTC-звонки, игры — дают за окно до пятнадцати разных портов, торрент-клиент даёт 140–250. Порог в 20 портов сидит ровно в этом зазоре.

Главную роль играет условие «и адреса, и порты». Сервис, который бьёт по десяткам адресов, но на узком наборе портов, под детектор не попадает: так ведут себя и раздача обновлений Windows, и голосовой звонок.

Срок бана и исключения задаются не здесь. blockDuration и ignoreLists живут в панели, в конфиге плагина Torrent Blocker. Детектор их не дублирует: нода применяет их к его отчётам ровно так же, как к своим.

Какие порты не считаются веером

Обращения ко многим адресам на этих портах — нормальное поведение, поэтому детектор их пропускает. Список собран по типовому легальному трафику, по каждому порту смотрели реальные адреса назначения и rDNS:

  • 22, 53, 80, 443, 8080, 8443 — SSH, DNS, HTTP и HTTPS, в том числе поверх QUIC.
  • 123 и 2002 — синхронизация времени: NTP и Roughtime.
  • 465, 587, 993, 995, 853 — почта и DNS-over-TLS.
  • 554 и 1935 — потоковое видео: RTSP с камер и RTMP.
  • 3478–3497, 5349, 16384–16403, 19302–19309 — STUN, TURN и RTP: установка и медиа p2p-звонков в WebRTC, мессенджерах и Google Meet.
  • 5222, 5223, 5228 — XMPP и push-уведомления, в том числе Google FCM.
  • 7680 — раздача обновлений Windows между машинами, десятки жилых адресов на один порт.

Порты, веер по которым как раз интересно видеть, в список осознанно не попали — например 3333 и другие порты майнинг-пулов.

Если какой-то легальный сервис на вашем узле всё же светит много разных портов и цепляется под детектор, добавьте его порты в FTB_IGNORE_PORTS — общий список ради одного узла править не нужно.

Как отличить срабатывание детектора от штатного

Отчёты детектора приходят по тому же пути, что и родные, поэтому отличать их стоит по метке протокола:

  • В Telegram. У штатного — Protocol: bittorrent (tcp), у детектора — Protocol: fsnt-torrent-blocker (tcp).
  • В панели, в отчётах Torrent Blocker — фильтр Protocol со значением bittorrent против fsnt-torrent-blocker.
  • В вебхуке torrent_blocker.report — у штатного xrayReport.outboundTag равен RW_TB_OUTBOUND_BLOCK, у детектора и protocol, и outboundTag равны fsnt-torrent-blocker.

Цифры самой находки лежат в xrayReport.routeTarget и выглядят так: fanout 73 hosts / 58 ports / 86 conns in 60s (tcp 85, udp 1). В поле destination — один из пиров.

Проверка

sudo fsnt-torrent-blocker --selftest
systemctl status fsnt-torrent-blocker
journalctl -u fsnt-torrent-blocker -f

Исправный selftest выглядит так:

ok    xray pid 1464055, API ноды — файловый сокет
ok    вебхук Torrent Blocker найден
ok    лог /proc/18868/root/var/log/remnanode/access.log
ok    вебхук принимает отчёты (HTTP 200)
ok    обработчик ноды разбирает формат детектора
итог: OK

А находка в журнале — так:

2026-09-23 10:45:55Z user=1042 ip=203.0.113.7 fanout 73 hosts / 58 ports / 86 conns in 60s (tcp 85, udp 1) -> принят нодой

Ограничения

О них лучше знать до установки, а не после первой жалобы.

Бан ставится по IP клиента — так же, как у штатного плагина. За адресом мобильного оператора с CGNAT может оказаться несколько абонентов.

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

Детектор ходит во внутренний API ноды, а не в публичный. После крупных обновлений Remnawave Node сначала прогоняйте --selftest.

Трафик мимо access-лога детектор не увидит. При log.access равном "none" работать не с чем.

Удаление

curl -fsSL https://raw.githubusercontent.com/forestsnet/torrent-blocker/main/install.sh | sudo bash -s -- --uninstall

Настройки при этом остаются — чтобы снести и их, добавьте --purge.

Проверьте, что обычный трафик не пострадал. После перевода в боевой режим откройте через свой VPN несколько сайтов, сделайте видеозвонок и скачайте крупный файл. Если что-то из легального цепляется под детектор, вы увидите это в журнале как находку с знакомым вам IP — и закроете портом в FTB_IGNORE_PORTS.

Источники