Skip to content

Торрент детекция

Xray умеет детектировать BitTorrent на уровне роутинга и отправлять webhook при срабатывании правила. Никаких внешних инструментов (nDPI, iptables, NFQUEUE) не нужно.

Два действия происходят одновременно при обнаружении BitTorrent-трафика:

  1. Трафик уходит в blackhole — соединение обрывается немедленно
  2. Xray отправляет webhook в панель — панель логирует нарушение и при необходимости банит пользователя

Конфигурация Xray на ноде

Section titled “Конфигурация Xray на ноде”
{
"routing": {
"rules": [
{
"protocol": ["bittorrent"],
"outboundTag": "block",
"webhook": {
"url": "https://panel-domain.com/webhooks/node-event",
"deduplication": 300
}
}
]
}
}

deduplication: 300 — один и тот же пользователь не будет триггерить webhook чаще чем раз в 300 секунд (5 минут). Без этого панель получала бы тысячи запросов в секунду от одного торрент-клиента.

{
"outbounds": [
{
"tag": "block",
"protocol": "blackhole",
"settings": {
"response": {
"type": "http"
}
}
}
]
}

type: "http" — возвращает HTTP 403 вместо просто обрыва TCP. Торрент-клиент быстрее понимает что соединение отклонено.


Xray отправляет POST-запрос на указанный URL при срабатывании правила. Payload содержит информацию о пользователе (email-тег из inbound) и деталях соединения.

Пример payload (на основе документации Xray):

{
"user": "abc123@astral",
"inbound": "vless-ws-tls",
"protocol": "bittorrent",
"timestamp": 1737000000
}

user — это xray email тег, который мы задаём при добавлении клиента в формате {user_id}@astral. Из него достаём user_id.


POST /webhooks/node-event

Аутентификация: Xray не поддерживает добавление заголовков в webhook из коробки, поэтому URL содержит секретный токен:

https://panel-domain.com/webhooks/node-event?token={NODE_SECRET}

Либо — настроить на ноде nginx-прокси, который добавляет Authorization заголовок перед форвардингом на панель.

func (h *WebhookHandler) HandleNodeEvent(c fiber.Ctx) error {
// Проверка токена из query param
if c.Query("token") != h.cfg.NodeWebhookSecret {
return c.SendStatus(401)
}
var event XrayWebhookEvent
c.BodyParser(&event)
// "abc123@astral" → "abc123"
userID := strings.TrimSuffix(event.User, "@astral")
violation := &Violation{
UserID: userID,
Protocol: event.Protocol,
InboundTag: event.Inbound,
DetectedAt: time.Unix(event.Timestamp, 0),
}
h.violationRepo.Create(violation)
// Считаем нарушения за последние 24 часа
count := h.violationRepo.CountRecent(userID, 24*time.Hour)
switch {
case count == 1:
h.notifier.SendWarning(userID)
case count >= 3:
h.subscriptionService.Suspend(userID, SuspendReasonTorrent)
h.worker.Enqueue(SyncUserTask{UserID: userID})
h.notifier.SendSuspended(userID)
}
return c.SendStatus(200)
}

Политика (настраивается в админке)

Section titled “Политика (настраивается в админке)”
Нарушений за 24ч Действие
1 Предупреждение (email / Telegram)
2 Повторное предупреждение
3+ Приостановка подписки + sync на ноды

Управление конфигом через агента

Section titled “Управление конфигом через агента”

Routing rule с webhook добавляется при инициализации ноды. Агент применяет его через Xray API при первом запуске или при изменении настроек из админки.

// При добавлении ноды в панели агент получает команду применить базовый конфиг
// включая torrent-blocking rule
func (a *Agent) ApplyBaseConfig(cfg *BaseConfig) error {
// Добавляем blackhole outbound
a.handlerClient.AddOutbound(...)
// Добавляем routing rule через Xray Routing API
a.routerClient.AddRule(...)
return nil
}

URL для webhook берётся из конфига агента (передаётся панелью при регистрации ноды) — так можно централизованно менять URL без правки конфигов на каждой ноде вручную.


Два новых объекта (добавить при финализации схемы):

violation_logs

  • id, user_id, inbound_tag, protocol, detected_at

user_warnings

  • id, user_id, reason, sent_at, acknowledged_at

В subscriptions: добавить torrent_suspended в enum status (или отдельное поле suspend_reason).


Частичная детекция. Xray не гарантирует 100% детекцию всех BitTorrent-соединений (зашифрованный MSE, нестандартные порты). Но этого достаточно как основание для предупреждения/бана — пользователь явно использует торрент-клиент.

deduplication. 300 секунд означает что между первым срабатыванием и блокировкой пользователь успеет скачать ~50-100 МБ. Если нужна мгновенная блокировка — логика suspend должна срабатывать уже на первом нарушении, а не на третьем.