Исходящие webhook
Remnawave Admin умеет отправлять свои события на любой HTTP-адрес. Подписка выбирает набор событий, подходящие уходят подписанным JSON-запросом.
Не путайте с входящим webhook от панели Remnawave — там события приходят к нам.
Понятия
Подписка — адрес плюс список событий плюс настройки подписи. Их может быть сколько угодно.
Доставка — одна попытка HTTP-запроса. Все попытки видны в истории доставок.
Очередь повторов — неудачные доставки повторяются автоматически, после исчерпания попыток нужен ручной повтор: у каждой доставки в истории хранится тело события, и кнопка «Повторить» отправляет его заново со свежей подписью. У доставок, записанных до 5.0.0, тела нет — их не повторить. Историю можно фильтровать по событию и результату.
Создание подписки
API и вебхуки → Вебхуки → Создать:
- Имя — метка вроде
slack-ops-channel - URL — начинается с
http://илиhttps://; приватные и локальные адреса по умолчанию отклоняются, см. ниже - Секрет — не обязателен, но нужен: он служит ключом подписи
- Версия подписи —
v2по умолчанию, с меткой времени и защитой от повторной отправки;v1оставлен для совместимости - События — одно или несколько
- Описание — по желанию
Подписка начинает работать сразу. Кнопка Отправить тест пошлёт синтетический запрос — удобно убедиться, что адрес вообще отвечает.
Формат доставки
POST <ваш адрес>
Content-Type: application/json
X-Webhook-Event: <имя события>
X-Webhook-Signature: sha256=<hex>
X-Webhook-Timestamp: <unix-время> # только v2
X-Webhook-Signature-Version: v2 # только v2{
"event": "user.created",
"data": {
"uuid": "...",
"username": "alice"
}
}Содержимое data по каждому событию — в каталоге.
Подпись
Тело запроса подписывается вашим секретом по HMAC-SHA256.
| Версия | Что подписывается |
|---|---|
v1 | HMAC(секрет, тело) |
v2 | HMAC(секрет, "<timestamp>.<тело>") |
Метка времени приходит в X-Webhook-Timestamp. Принимающей стороне стоит отклонять запросы, где она отличается от текущего времени больше чем на пять минут: это защита от повторной отправки перехваченного запроса.
Примеры приёмников на Python, Node, Go и PHP — в разделе Проверка подписи.
Повторы
Первая попытка идёт сразу, вместе с исходным событием, с таймаутом 10 секунд. Если она не удалась — сетевая ошибка или ответ 4xx/5xx — доставка ставится в очередь.
Повторов ещё два, с нарастающей паузой: минута, пять минут, двадцать пять минут. Каждая попытка попадает в историю.
Автоотключение
Когда подряд накапливается 50 неудач (WEBHOOK_AUTO_DISABLE_AFTER), подписка отключается сама, и в интерфейсе появляется отметка с причиной. Обратное включение обнуляет счётчик.
Защита от SSRF
Адрес проверяется при создании, изменении, тесте и каждой отправке:
- схема только
httpилиhttps - имя хоста резолвится, и результат сверяется с приватными диапазонами: RFC-1918,
127.0.0.0/8,169.254.0.0/16,::1,fc00::/7,fe80::/10
Адрес, который указывает внутрь, отклоняется с 400. Обойти можно только осознанно и только для разработки:
WEBHOOK_ALLOW_PRIVATE_URL=1История доставок
Иконка часов рядом с подпиской показывает последние 200 попыток: код ответа (или ERR, если до ответа не дошло), событие, время, длительность, тело ответа до 5000 символов и текст ошибки. Тестовые доставки в историю не пишутся.
Переменные
| Переменная | По умолчанию | Что задаёт |
|---|---|---|
WEBHOOK_TIMEOUT_SEC | 10 | таймаут одной попытки |
WEBHOOK_MAX_ATTEMPTS | 3 | попыток всего, включая первую |
WEBHOOK_AUTO_DISABLE_AFTER | 50 | неудач подряд до автоотключения |
WEBHOOK_RETRY_WORKER_INTERVAL | 10 | как часто просыпается фоновая отправка, секунды |
WEBHOOK_ALLOW_PRIVATE_URL | 0 | 1 отключает проверку адреса |
Меняются с перезапуском бэкенда.
Если что-то не так
Появилась отметка «отключён автоматически». Приёмник отвечал ошибками 50 раз подряд. Почините его и включите подписку обратно — счётчик обнулится.
История пустая. Либо подходящих событий ещё не было, либо до приёмника не доходит даже соединение — тогда запись не создаётся.
Подпись не сходится. Чаще всего причина одна из трёх: используется старый секрет; тело пересобрано перед проверкой (подписывается ровно то, что пришло); подпись проверяется по v1, а подписка на v2.
Тест проходит, реальные события нет. Проверьте, что нужное событие действительно отмечено в подписке.