Skip to content

💡 [REQUEST] - Торрент-блокер: блокировка IP не работает за reverse proxy с PROXY protocol #389

@Joliz1337

Description

@Joliz1337

Reference Issues

Торрент-блокер добавляет реальный IP пользователя в nftables и убивает сокет через sockdestroy. Но если нода за HAProxy (или любым reverse proxy) с PROXY protocol — TCP-пакеты приходят с IP прокси, а не пользователя.

  • nftables drop не срабатывает для VPN-трафика (правило матчит по source IP, а source = IP HAProxy)
  • killSockets не находит сокеты (в таблице сокетов ядра peer = IP прокси, а не пользователя)
  • Xray webhook срабатывает, отчёты приходят в панель, но соединение пользователя не разрывается и торренты продолжают качаться
Пользователь (реальный IP) → HAProxy (PROXY protocol) → Xray (нода)
                                                           ↓
                                              nftables блокирует реальный IP,
                                              но TCP source — IP HAProxy
                                                           ↓
                                                   Блокировка не работает

Summary

Добавить поддержку блокировки через HAProxy Runtime API. HAProxy поддерживает stick-tables с автоматическим expire — аналог nftables set с timeout. Нода при обнаружении торрента отправляет заблокированный IP на HAProxy, который блокирует его на входе — там где виден реальный IP клиента.

Нода держит persistent TCP-соединение к каждому HAProxy (с автоматическим реконнектом при обрыве). Торрент-блокер может тригериться часто — подключаться заново на каждый блок неэффективно.

Если haproxy не указан или пуст — поведение не меняется (обратная совместимость).

Basic Example

Что делает админ

1. Открывает TCP stats socket на HAProxy

HAProxy Runtime API не поддерживает аутентификацию по паролю. Безопасность — только через файрвол или VPN.

global
    # Локальный Unix socket 
    stats socket /var/run/haproxy.sock mode 660 level admin expose-fd listeners
    # TCP socket для удалённого доступа нод
    stats socket ipv4@0.0.0.0:9999 level admin

Порт 9999 обязательно закрыть файрволом для всех, кроме IP нод:

ufw allow from <node_ip_1> to any port 9999
ufw allow from <node_ip_2> to any port 9999
ufw deny 9999

Или использовать VPN/WireGuard — stats socket слушает на VPN-интерфейсе.

2. Добавляет stick-table и правила reject

Один общий backend со stick-table — все frontend'ы ссылаются на него. Это рекомендуемый паттерн для шаринга таблицы между несколькими frontend'ами:

# Общая таблица заблокированных IP (одна на весь конфиг)
backend torrent_blocklist
    stick-table type ip size 100k expire 1h store gpc0

# В каждый frontend — одна строка
frontend tcp_example
    bind *:8443
    mode tcp
    tcp-request connection reject if { src,table_gpc0(torrent_blocklist) gt 0 }
    default_backend backend_tcp_example

3. Настраивает плагин

"torrentBlocker": {
    "enabled": true,
    "blockDuration": 3600,
    "haproxy": [
        {
            "address": "tcp://1.2.3.4:9999",
            "tableName": "torrent_blocklist"
        }
    ]
}

Массив — потому что HAProxy-серверов может быть несколько.

Что делает нода

При обнаружении торрента:

  1. Текущая логика (nftables + killSockets) — без изменений
  2. Дополнительно для каждого HAProxy из массива отправляет команду по persistent TCP-соединению:
    set table torrent_blocklist key 85.140.85.185 data.gpc0 1
    

Что происходит на HAProxy

  • IP добавляется в stick-table → HAProxy сразу дропает соединение этого клиента
  • Новые подключения с этого IP отклоняются (reject)
  • Через expire (1h) запись автоматически удаляется — разбан без участия ноды

Drawbacks

  • HAProxy Runtime API не поддерживает TLS и аутентификацию — безопасность только через файрвол (ограничить порт IP нодами) или VPN
  • Требует от пользователя ручной настройки HAProxy (stick-table + строка reject в каждом frontend)
  • Работает только для HAProxy, не универсально для других прокси (nginx, Caddy)

Unresolved questions

  • Безопасность API — Runtime API без TLS и без пароля. Файрвол (ограничить порт только IP нод) или VPN
  • IPv6 — stick-table type ip только IPv4. Если нужен IPv6, потребуется type ipv6 и вторая таблица

Metadata

Metadata

Assignees

No one assigned

    Labels

    questionFurther information is requested

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions