ZOYKU火线榜

syvlech/remnawave-limiter技术文档

Мониторинг и ограничение одновременных подключений к нодам Remnawave сверх лимита HWID.

多语言回退提示:当前仓库原作者暂未发布本地化文档,已为您无缝切换至官方英文原版(English),排版及代码实例完整保真。
极速安装
go get github.com/syvlech/remnawave-limiter

Remnawave Limiter

Централизованный контроль устройств для Remnawave

Мониторит одновременные подключения пользователей через API панели: собирает IP со всех нод, сравнивает с лимитом устройств каждого пользователя (hwidDeviceLimit) и реагирует на превышения — через Telegram-бота или автоматической блокировкой подписки.

License: GPL v3

English version · 简体中文

Возможности

  • Агрегация IP со всех нод — полная картина подключений, сравнение с индивидуальным лимитом + погрешность (tolerance)
  • Два режима: manual — алерт с inline-кнопками (сброс, бан, whitelist); auto — автоблокировка с автовосстановлением по таймеру
  • Порог нарушений — действие только после N превышений за окно (защита от ложных срабатываний)
  • Группировка IP по подсетям (/24) или по ASN-провайдерам — против CGNAT и шаринга
  • Webhook (JSON POST) о нарушениях с HMAC-подписью
  • Статистика и отчёты — команда /stats (за 24ч/неделю + топ-5 нарушителей) и ежедневный отчёт в чат
  • Рантайм-настройки через /settings — изменение параметров на лету без перезапуска (хранятся в Redis)
  • Whitelist пользователей и IP/CIDR, кулдаун, кэш, выбор часового пояса, ru/en
  • Liveness /healthz (опционально, по HEALTH_ADDR) для healthcheck в Docker/оркестраторе
  • Логи под docker compose logs -f — сводка по каждому циклу проверки, уровень меняется на лету, LOG_FORMAT=json для Loki/ELK

Архитектура

┌─────────────┐     ┌──────────────────┐     ┌───────────────┐
│  Remnawave  │◄────│  remnawave-      │────►│   Telegram    │
│  Panel API  │     │  limiter         │     │   Bot API     │
└─────────────┘     │  (Go binary)     │     └───────────────┘
                    │  ┌────────────┐  │
                    │  │   Redis    │  │
                    │  └────────────┘  │
                    └──────────────────┘

Один инстанс рядом с панелью или на любом сервере. Установка на ноды не нужна — всё через API. Docker Compose поднимает сервис + Redis.

Цикл проверки: список нод (GET /api/nodes) → параллельный сбор IP по нодам → агрегация по пользователю → фильтр по lastSeen → сравнение с лимит + погрешность → инкремент порогового счётчика → при достижении порога реакция (алерт или автоблокировка).

Сбойная нода не роняет цикл: проверка выполняется по остальным, а в лог идёт предупреждение о неполных данных. Если не ответила ни одна нода, цикл пропускается целиком — иначе отсутствие подключений было бы прочитано как «все в пределах лимита».

Требования

  • Docker и Docker Compose
  • Remnawave 3.0.0+ с API-токеном (scope connections:*, users:* и nodes:read)
  • Telegram-бот (@BotFather)
Минимальный набор scope

Если хотите выдать токену только то, что limiter реально вызывает:

Scope Эндпоинт Зачем
nodes:list GET /api/nodes список нод для опроса — без него цикл не начнётся
connections:by-node POST /api/connections/by-node/{nodeUuid} запуск сбора IP по ноде
connections:by-node-result GET /api/connections/by-node/{jobId} результат сбора
users:by-id GET /api/users/{id} лимит устройств и данные пользователя
users:disable POST /api/users/{id}/actions/disable отключение подписки
users:enable POST /api/users/{id}/actions/enable кнопка «Включить» и восстановление по таймеру
connections:drop POST /api/connections/drop только кнопка «Сбросить подключения» в режиме manual

Для нод достаточно чтения: nodes:* дал бы ещё запись (перезапуск и отключение нод, правку профилей и инбаундов), которая limiter не нужна.

Какую версию limiter выбрать

Версия панели Remnawave Версия limiter
3.0.0 и новее 4.0.0+ — текущая, тег latest
ниже 3.0.0 (ветка 2.x) 3.0.0 — последняя с поддержкой панелей 2.x

Версии совместимы только в одну сторону: в Remnawave 3.0.0 модуль ip-control переименован в connections, а пользователи идентифицируются числовым id вместо uuid — старые эндпоинты удалены. Поэтому limiter 4.0.0+ не работает с панелью 2.x, а limiter 3.0.0 — с панелью 3.x.

Если панель ещё не обновлена, зафиксируйте образ на релизе 3.0.0 в docker-compose.yml:

image: ghcr.io/syvlech/remnawave-limiter:b422f43

b422f43 — коммит релиза 3.0.0. Отдельного тега образа 3.0.0 в ghcr нет: CI собирает semver-теги только для рефов вида v*, а релиз помечен как 3.0.0 без префикса.

Установка

mkdir -p /opt/remnawave-limiter && cd /opt/remnawave-limiter

curl -O https://raw.githubusercontent.com/syvlech/remnawave-limiter/master/docker-compose.yml
curl -O https://raw.githubusercontent.com/syvlech/remnawave-limiter/master/.env.example

cp .env.example .env && nano .env

Обязательные параметры в .env:

REMNAWAVE_API_URL=https://panel.example.com
REMNAWAVE_API_TOKEN=your-api-token-here
TELEGRAM_BOT_TOKEN=123456:ABC-DEF1234ghIkl-zyx57W2v1u123ew11X
TELEGRAM_CHAT_ID=-1001234567890
TELEGRAM_ADMIN_IDS=123456789

Запуск, логи и обновление:

docker compose pull && docker compose up -d   # запуск / обновление
docker compose logs -f limiter                # проверка

Конфигурация

Все настройки — через .env или переменные окружения.

Параметр По умолчанию Описание
REMNAWAVE_API_URL обязательный Адрес панели Remnawave
REMNAWAVE_API_TOKEN обязательный API-токен (генерируется в панели)
REMNAWAVE_COOKIES — Доп. авторизация через cookie. Формат: key=value через ; (напр. cf_clearance=abc; session=xyz). Для панелей за Cloudflare/WAF
REMNAWAVE_HEADERS — Доп. HTTP-заголовки для каждого запроса к API. Формат: Name: Value через ; (напр. X-Api-Key: secret123; X-Custom-Header: value). Для панелей за прокси/Cloudflare/защитой
TELEGRAM_BOT_TOKEN обязательный Токен бота от @BotFather
TELEGRAM_CHAT_ID обязательный ID чата/канала/группы для алертов
TELEGRAM_ADMIN_IDS обязательный ID админов через запятую (только они нажимают кнопки)
TELEGRAM_THREAD_ID — ID треда/топика в супергруппе
TELEGRAM_PROXY — Прокси для Telegram API. Схемы: http, https, socks5, socks5h. Формат: scheme://[user:pass@]host:port
CHECK_INTERVAL 30 Интервал проверки (сек)
ACTIVE_IP_WINDOW 300 IP активен, если lastSeen < этого значения (сек)
TOLERANCE 0 Фиксированное допустимое превышение лимита. Лимит 3 + tolerance 1 → реакция при 5+
TOLERANCE_MULTIPLIER 0 Пропорциональный допуск: TOLERANCE + floor(limit × множитель). 0 — не используется. Допустимо 0..100
COOLDOWN 300 Кулдаун между алертами на пользователя (сек)
USER_CACHE_TTL 600 TTL кэша данных пользователя (сек)
DEFAULT_DEVICE_LIMIT 0 Лимит, если hwidDeviceLimit не задан. 0 = без ограничения
ACTION_MODE manual manual — алерт с кнопками; auto — автоотключение подписки
AUTO_DISABLE_DURATION 0 Длительность временного отключения (мин). 0 = перманентно. В manual добавляет кнопку, в auto — время автовосстановления
AUTO_NOTIFY_SOFT false Только auto. Превышение в пределах допуска (limit < устройств <= limit+TOLERANCE) даёт информационный алерт без бана. Бан — только выше limit+TOLERANCE
WEBHOOK_URL — URL для webhook при нарушениях (POST JSON). Пусто = выключен
WEBHOOK_SECRET — Секрет webhook для HMAC-SHA256 подписей X-Signature (тело) и X-Signature-V2 (<X-Timestamp>.<тело>). См. Webhook (опционально)
WEBHOOK_SECRET_HEADER true Передавать ли сам секрет в заголовке X-Webhook-Secret. Оставлено ради совместимости; если получатель проверяет подпись — выключите
WHITELIST_USER_IDS — Числовые ID пользователей для исключения из проверки (через запятую). UUID из версий до 4.0 здесь не работают
IGNORED_NODE_UUIDS — UUID нод, игнорируемых при сборе IP (не в отчётах, не в решениях). Для технических/тестовых нод
IP_WHITELIST — IP и/или CIDR-подсети через запятую, исключаемые из подсчёта. Отсеивает IP нод/мостов/релеев. IPv4/IPv6. Пример: 203.0.113.5,10.0.0.0/8,2001:db8::/32
IGNORE_DURATION 0 Время действия кнопки «Игнорировать» (мин). 0 = навсегда. > 0 = временный whitelist с TTL
VIOLATION_THRESHOLD 1 Сколько нарушений нужно для действия. 1 = мгновенная реакция
VIOLATION_THRESHOLD_WINDOW 3600 Окно подсчёта нарушений (сек). Должно быть больше (VIOLATION_THRESHOLD − 1) × (COOLDOWN + CHECK_INTERVAL), иначе порог недостижим и конфиг не принимается
SUBNET_GROUPING false Группировка IPv4 по /SUBNET_PREFIX_V4 — считаются подсети вместо IP (снижает ложные срабатывания от CGNAT). IPv6 — по-штучно
SUBNET_PREFIX_V4 24 Длина IPv4-префикса (8..32). 24 — стандарт; 16 — для мобильной аудитории. При SUBNET_GROUPING=true
ASN_GROUPING false Считать уникальные ASN-провайдеры вместо IP/подсетей — самый точный сигнал против шаринга. IP без ASN — отдельная группа. Приоритетнее SUBNET_GROUPING. Требует базу MaxMind ASN
ASN_DATABASE_PATH ./geoip/GeoLite2-ASN.mmdb Путь к GeoLite2-ASN.mmdb. Директория создаётся автоматически. Менять только при нестандартной раскладке
MAXMIND_LICENSE_KEY — Ключ MaxMind. Если задан — недостающая база скачивается при старте + фоновое обновление. Получить
MAXMIND_UPDATE_INTERVAL 168h Интервал автообновления базы (мин. 1h). Только при заданном MAXMIND_LICENSE_KEY
REDIS_URL redis://redis:6379 Адрес Redis
TIMEZONE UTC Часовой пояс для timestamps в алертах (напр. Europe/Moscow)
LANGUAGE ru Язык интерфейса: ru или en
DAILY_REPORT false Ежедневный отчёт о нарушениях в чат (топ нарушителей + счётчики). Переключается на лету через /settings
DAILY_REPORT_TIME 09:00 Локальное время отправки отчёта (HH:MM, в TIMEZONE). Меняется только перезапуском
HEALTH_ADDR — Адрес HTTP liveness-эндпоинта /healthz (напр. :8080). Пусто = выключен
LOG_LEVEL info Детальность логов: trace, debug, info, warn, error. На info — по одной сводной строке на цикл проверки плюс все действия; debug добавляет разбор по IP и детали Telegram. Меняется на лету через /settings
LOG_FORMAT text text для чтения человеком, json для сборщиков логов (Loki, ELK). Меняется только перезапуском

ASN в уведомлениях. Если база MaxMind доступна, рядом с каждым IP в алерте и webhook показывается провайдер (• 91.107.96.11 - Hetzner Online GmbH (Chicago-1)), а в заголовке — счётчик уникальных ASN (Обнаружено: 4 IP (3 ASN)). На логику ограничения не влияет — решения только по количеству IP/подсетей/ASN.

Логика лимитов

hwidDeviceLimit Поведение
> 0 Используется как лимит устройств
null Берётся DEFAULT_DEVICE_LIMIT из конфига
0 Без ограничений — пользователь пропускается

Порог нарушений

При VIOLATION_THRESHOLD=1 (по умолчанию) реакция на каждое превышение. При большем значении действие выполняется только после накопления N нарушений в окне VIOLATION_THRESHOLD_WINDOW: превышение → проверка cooldown → инкремент счётчика (TTL = окно) → при достижении порога действие и сброс счётчика. Если между нарушениями проходит больше окна — счётчик обнуляется.

Cooldown ставится до инкремента, поэтому счётчик растёт не чаще раза за COOLDOWN, а следующее нарушение замечается на ближайшей проверке. Окно должно вмещать N − 1 таких шагов: VIOLATION_THRESHOLD_WINDOW > (VIOLATION_THRESHOLD − 1) × (COOLDOWN + CHECK_INTERVAL). Конфиг, где это не выполняется, отклоняется при старте и в /settings — иначе порог никогда не был бы достигнут и лимитер молча перестал бы действовать.

Пример при VIOLATION_THRESHOLD=3, VIOLATION_THRESHOLD_WINDOW=3600:

Время Счётчик Действие
12:00 1/3 Лог, без действия
12:05 2/3 Лог, без действия
12:10 3/3 Алерт/блокировка, сброс
12:15 1/3 Лог, без действия

Telegram-бот

Все сообщения бота — rich-сообщения Telegram (Bot API 10.3, sendRichMessage): заголовки, таблицы, сворачиваемые блоки, время в часовом поясе читателя (tg-time) и цветные кнопки прямо внутри сообщения. Если Telegram отклонит rich-разметку, сообщение всё равно уйдёт — обычным текстом с inline-кнопками. Для отображения нужен клиент Telegram с поддержкой rich-сообщений.

Команды

Доступны только администраторам из TELEGRAM_ADMIN_IDS. Регистрируются в меню бота автоматически при старте.

Команда Что делает
/settings Интерактивное меню рантайм-настроек. Меняет безопасные параметры на лету (без перезапуска); значения сохраняются в Redis и переживают рестарт. Подробнее — раздел «Рантайм-настройки» ниже
/stats Статистика нарушений: количество за последние 24 часа и за неделю + топ-5 нарушителей за неделю. Кнопка «🔄 Обновить» перерисовывает отчёт на месте

Рантайм-настройки (/settings)

Приоритет источников: Redis-override > .env / переменные окружения > значения по умолчанию. Изменения применяются на лету (например, CHECK_INTERVAL пересоздаёт тикер). Структурные и секретные ключи (API URL/токен, все TELEGRAM_*, REDIS_URL, TIMEZONE, LANGUAGE, WEBHOOK_*, IP_WHITELIST, IGNORED_NODE_UUIDS, MAXMIND_*, DAILY_REPORT_TIME, HEALTH_ADDR, LOG_FORMAT) меняются только через .env + перезапуск. Кнопка «Сбросить к .env» (для ключа или для всех, с подтверждением) убирает override.

Меню работает в одном сообщении, которое перерисовывается на месте:

  • Главный экран — таблица всех параметров по разделам (🎯 Обнаружение, 🛡 Действия, ⏳ Пороги и кулдауны, 🌐 Группировка, 🧰 Сервис); ♻️ отмечает значения, изменённые в боте.
  • Раздел — значения, подсказки к каждому параметру и кнопки: флаги переключаются одним нажатием (включённые — зелёные), остальные открывают экран редактирования.
  • Редактирование — текущее значение, источник (.env или бот), имя переменной, быстрые пресеты (для минут — в человекочитаемом виде, 0 = «навсегда») и ввод своего значения сообщением. Ошибка валидации показывается на том же экране, ввод можно сразу повторить.

Ежедневный отчёт (DAILY_REPORT=true)

Раз в сутки в DAILY_REPORT_TIME (локальное время в TIMEZONE) бот присылает в чат тот же отчёт, что и /stats (нарушений за 24ч/неделю + топ-5). Учитываются только реальные срабатывания после порога VIOLATION_THRESHOLD; «мягкие» предупреждения в статистику не попадают. DAILY_REPORT переключается на лету через /settings, время DAILY_REPORT_TIME — только перезапуском.

Ручной режим (ACTION_MODE=manual)

При превышении бот отправляет алерт с кнопками:

⚠️ Превышение лимита устройств
┌──────────────────────┬──────────────────────┐
│ 👤 Пользователь      │ username123          │
│ 🔗 Подписка          │ открыть              │
│ 📊 Лимит             │ 3                    │
│ 📡 Обнаружено        │ 5 IP · 2 ASN         │
│ 🚦 Превышение        │ +2                   │
│ 📈 Нарушений за 24ч  │ 3                    │
│ 🕐 Время             │ сб, 29.11 04:15      │  ← в часовом поясе читателя
└──────────────────────┴──────────────────────┘
▾ 📍 IP-адреса (5)
  IP          Провайдер      Нода
  10.0.1.10   Hetzner        node-1
  10.0.3.30   Cloudflare     node-2

[🔄 Сбросить подключения]
[🔒 Отключить навсегда] [🔒 Отключить на 10 мин]  ← вторая — если AUTO_DISABLE_DURATION > 0
[🔇 Игнорировать 15 мин]                           ← если IGNORE_DURATION > 0, иначе "🔇 Игнорировать"

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

Длительность «Отключить на N мин» и «Игнорировать N мин» запоминается в самой кнопке при отправке алерта: смена AUTO_DISABLE_DURATION/IGNORE_DURATION через /settings не меняет смысл уже отправленных кнопок. «Отключить навсегда» и «Включить подписку» снимают запланированное автовосстановление, чтобы старый таймер не включил пользователя снова.

Кнопка Действие
Сбросить подключения Сброс активных подключений через API
Отключить навсегда Перманентная деактивация подписки
Отключить на N мин Временная деактивация с автовосстановлением (при AUTO_DISABLE_DURATION > 0)
Игнорировать В whitelist: временно при IGNORE_DURATION > 0, иначе навсегда

Автоматический режим (ACTION_MODE=auto)

Подписка отключается автоматически, бот шлёт информационный алерт с кнопкой «Включить подписку». При AUTO_DISABLE_DURATION > 0 подписка восстанавливается по таймеру, и в алерте видно, когда это произойдёт.

Уведомления в пределах допуска (AUTO_NOTIFY_SOFT=true). Когда нужно банить только при заметном превышении (TOLERANCE), но знать о тех, кто уже вышел за HWID-лимит:

Кол-во устройств Действие
устройств <= limit тишина
limit < устройств <= limit+TOLERANCE 🔔 информационный алерт, без бана (с кнопками «Сбросить подключения» и «Игнорировать»)
устройств > limit+TOLERANCE 🔒 бан + алерт

«Мягкие» предупреждения используют отдельный кулдаун (cooldown:soft:), не увеличивают счётчик нарушений за 24ч, не учитываются в VIOLATION_THRESHOLD и шлют webhook с событием soft_violation_detected.

Webhook

При нарушении limiter может отправить HTTP POST на WEBHOOK_URL (работает в обоих режимах). К каждому запросу добавляется X-Timestamp (unix-время отправки). Если задан WEBHOOK_SECRET, добавляются заголовки:

Заголовок Содержимое
X-Signature-V2 sha256=<hex> — HMAC-SHA256 от строки <X-Timestamp>.<тело>. Рекомендуемая проверка: подпись покрывает и время, поэтому переигранный запрос с новым X-Timestamp её не пройдёт. Отбрасывайте запросы, у которых X-Timestamp старше нескольких минут
X-Signature sha256=<hex> — HMAC-SHA256 только от тела. Для совместимости: защищает от подмены, но не от повтора
X-Webhook-Secret Сам секрет, для простой сверки. Кто увидел один запрос, узнаёт секрет и может подписать что угодно — отключите WEBHOOK_SECRET_HEADER=false, если получатель проверяет подпись

Проверка на получателе (псевдокод): hmac_sha256(secret, ts + "." + body) == header["X-Signature-V2"] и now − ts < 300.

Отправка не блокирует цикл мониторинга. При сетевой ошибке, 5xx или 429 доставка повторяется (до 3 попыток); 4xx считается осознанным отказом получателя и не повторяется. Событие переживает остановку сервиса: при SIGTERM limiter дожидается отправки, а не теряет её.

Пример payload:

{
  "event": "violation_detected",
  "action_mode": "auto",
  "user": {
    "user_id": 123,
    "username": "john",
    "email": "[email protected]",
    "telegram_id": 123456789,
    "subscription_url": "https://panel.example.com/sub/abc"
  },
  "violation": {
    "ips": [
      { "ip": "1.2.3.4", "node_name": "DE-1", "node_uuid": "node-uuid-1", "last_seen": "2025-11-29T12:00:00Z", "asn": 24940, "asn_org": "Hetzner Online GmbH" },
      { "ip": "5.6.7.8", "node_name": "US-1", "node_uuid": "node-uuid-2", "last_seen": "2025-11-29T12:01:00Z" }
    ],
    "ip_count": 5,
    "device_limit": 3,
    "tolerance": 1,
    "effective_limit": 4,
    "violation_count_24h": 3,
    "device_group_count": 5,
    "grouping_mode": "ip"
  },
  "action": { "auto_disable_duration_min": 10 },
  "timestamp": "2025-11-29T12:05:00Z"
}
Поле Описание
event violation_detected при бане; soft_violation_detected для «мягкого» предупреждения (AUTO_NOTIFY_SOFT)
action_mode manual или auto
user Данные пользователя (user_id, username, email, telegram_id, subscription_url)
violation.ips Активные IP с нодой и временем последней активности
violation.ip_count Количество уникальных IP
violation.device_limit / tolerance / effective_limit Лимит, допуск и эффективный лимит (device_limit + tolerance)
violation.violation_count_24h Нарушений за 24 часа
violation.grouping_mode По чему считались устройства: ip, subnet или asn
violation.device_group_count Итоговое число «устройств» в выбранном режиме — именно оно сравнивается с лимитом
violation.subnet_count / asn_group_count Число подсетей / ASN-групп. Присутствуют, только если режим включён
violation.ips[].asn / asn_org Номер и название провайдера. Присутствуют, только если база MaxMind загружена и ASN определился
action.auto_disable_duration_min Длительность блокировки (0 = перманентная)
timestamp Время обнаружения (ISO 8601)

FAQ

Как узнать Telegram Chat ID? Добавьте @userinfobot и отправьте /start. Для группы/канала — @getidsbot.

Что если API панели недоступен? Сервис логирует ошибку, пропускает цикл и пробует снова через CHECK_INTERVAL. Запросы к API повторяются до 3 раз с exponential backoff и джиттером; при 429 и 408 повтор тоже выполняется, а заголовок Retry-After от панели учитывается. Цикл, в котором не удалось опросить ни одну ноду, не считается успешным — /healthz в этом случае покажет проблему.

Можно ли использовать Redis от Remnawave? Можно, но не рекомендуется — проект поднимает свой Redis. Для существующего укажите REDIS_URL.

Как изменить лимит пользователя? Через поле hwidDeviceLimit в настройках подписки в панели Remnawave.

Поддержка и лицензия

GitHub Issues · GNU GPL v3.0 — см. LICENSE

文档镜像归档自 GitHub 官方源路径 · 访问原项目 GitHub
最后同步快照:2026-11-09