Кейс · Бэкенд · Rust
hook-relay: доставка вебхуков
Сервис доставки вебхуков на Rust: принимает событие, подписывает его HMAC-SHA256, отправляет получателю и не теряет. При сбое повторяет с экспоненциальной задержкой и джиттером, после лимита попыток откладывает в dead-letter, где доставку можно вернуть в очередь вручную. Адрес получателя проверяется на SSRF после DNS: все полученные адреса должны быть публичными. Идемпотентность по Idempotency-Key, состояние в SQLite.
- Rust
- axum
- tokio
- SQLite
- HMAC-SHA256
- SSRF
- proptest
- musl
- distroless
- Docker
- GitHub Actions
01 · Как выглядит
Скриншоты


02 · О проекте
Что это и зачем
Задача: доставлять вебхуки так, чтобы событие не терялось, получатель мог проверить подлинность, а сам сервис нельзя было использовать для обращения во внутреннюю сеть.
Решение: Rust (axum + tokio + SQLite). Тело подписывается HMAC-SHA256 по строке «t.тело» секретом получателя; при сбое доставка повторяется с экспоненциальной задержкой и джиттером, после лимита уходит в dead-letter. Адрес получателя разрешается через DNS, и все полученные адреса обязаны быть публичными.
Ограничения
Доставка «минимум один раз»: получатель должен быть идемпотентным (X-Relay-Delivery-Id).
Секреты получателей лежат в БД открытым текстом, в списках API не отдаются.
Тело ответа получателя не читается и не сохраняется.
Проект работает на небольшом VPS с демо-данными: без отказоустойчивости и резервных копий.
Что внутри
- SSRF: блокируются loopback, приватные, link-local, CGNAT, документационные, ULA, IPv4-mapped, NAT64 и 6to4 с вложенным приватным адресом; проверка при регистрации и при каждой доставке
- Ретраи: сетевые ошибки, 5xx, 408 и 429; остальные 4xx это окончательный отказ; ручной возврат из dead-letter
- Идемпотентность по Idempotency-Key: повтор возвращает прежнюю доставку и не отправляет событие снова
- После аварийного рестарта доставки «в полёте» возвращаются в очередь; graceful shutdown ждёт до 15 с
- 20 тестов; proptest: подпись не проходит после изменения любого байта, любой приватный IPv4 и его IPv4-mapped вид блокируются
- Результат: образ 14,4 МБ, 3,5 МиБ в покое и 5,5 МиБ после 200 событий с реальной доставкой на приёмник
03 · Как запустить
Запуск одной командой
git clone <репозиторий по запросу> hook-relay
cd hook-relay
DATA_DIR=./data cargo run --release
# http://localhost:811304 · Код
Репозиторий приватный
Исходники лежат в приватном репозитории. Напишите в Telegram, открою доступ.
Запросить код