fix: resolve webhook client IP behind proxies

This commit is contained in:
3252a8
2026-06-08 11:25:02 +03:00
parent 23ad893f69
commit c2f0ae0b8b
21 changed files with 161 additions and 25 deletions
+10 -1
View File
@@ -41,10 +41,19 @@
| `DB_POOL_RECYCLE_SECONDS` | `.env` | Период recycling DB-соединений. |
| `REDIS_URL` | Compose | Redis для FSM, кеша, rate-limit, очередей и locks. В Compose задается автоматически. |
| `REDIS_KEY_PREFIX` | `.env` | Префикс Redis-ключей. |
| `TRUSTED_PROXIES` | `.env` | IP/CIDR обратных прокси, которым доверяется `X-Forwarded-For`. |
| `TRUSTED_PROXIES` | `.env` | IP/CIDR обратных прокси, которым доверяется `X-Forwarded-For`. По умолчанию включает loopback и private ranges для Docker/LAN/Kubernetes proxy. |
| `HTTP_BIND` / `HTTPS_BIND` | Caddy Compose | Адреса публикации Caddy-варианта. |
| `NEWT_ID` / `NEWT_SECRET` | Dev Compose | Доступы Newt в dev-compose. |
`TRUSTED_PROXIES` нужен не только для логов: платежные webhook-обработчики с IP-фильтром
сравнивают allowlist провайдера с client IP после обработки `X-Forwarded-For`. Если внешний
proxy не передает этот заголовок или его IP не входит в `TRUSTED_PROXIES`, backend увидит IP
proxy/Docker gateway и может отклонить валидный webhook. Для Caddy/Nginx/Newt из
`deploy/examples` дефолта достаточно; в кастомной инфраструктуре добавьте CIDR своего proxy
или сузьте значение до конкретных proxy IP. Trust-all вариант записывается как
`0.0.0.0/0,::/0`, но он безопасен только если backend не доступен напрямую, а внешний proxy
очищает входящий `X-Forwarded-For`.
## Кеши, rate limits и worker
Обычно эти значения не требуют правки.
+8
View File
@@ -28,6 +28,14 @@ openssl rand -hex 32
- `SUBSCRIPTION_MINI_APP_URL` должен вести на frontend/Mini App-домен.
- Не добавляйте `/api`, `/auth` или webhook-пути в `SUBSCRIPTION_MINI_APP_URL`.
## IP allowlist вебхуков
- Reverse proxy для `WEBHOOK_BASE_URL` должен передавать `X-Forwarded-For` с реальным IP отправителя.
- `TRUSTED_PROXIES` должен включать IP/CIDR последнего proxy-hop до backend. Иначе платежные webhook-обработчики будут проверять allowlist по IP proxy или Docker gateway.
- В Docker Compose профилях Caddy, Nginx и Pangolin/Newt дефолт покрывает loopback и private ranges: `127.0.0.1`, `::1`, `10.0.0.0/8`, `172.16.0.0/12`, `192.168.0.0/16`, `fc00::/7`.
- Если backend находится в общей Docker-сети с недоверенными контейнерами, сузьте `TRUSTED_PROXIES` до конкретных IP ваших reverse proxy.
- Если вы сознательно хотите доверять любому proxy-hop, используйте `0.0.0.0/0,::/0`, но только когда backend не опубликован напрямую, а внешний proxy очищает входящий `X-Forwarded-For`.
## Дополнительно
- Используйте HTTPS на всех публичных доменах.
+6
View File
@@ -28,6 +28,12 @@
Все платежные webhook URL строятся от `WEBHOOK_BASE_URL` - публичного HTTPS-адреса backend/webhook-домена. Это должен быть домен, который проксируется на backend-сервер вебхуков (`backend:8080`), а не `SUBSCRIPTION_MINI_APP_URL` frontend/Mini App. Если `WEBHOOK_BASE_URL=https://bot.example.com`, то полный адрес получается как `https://bot.example.com` + путь из таблицы.
Если у провайдера включена IP-фильтрация (`FREEKASSA_TRUSTED_IPS`, `WATA_TRUSTED_IPS`,
`HELEKET_TRUSTED_IPS`, `PAYKILLA_TRUSTED_IPS` или встроенный allowlist YooKassa),
reverse proxy должен прокидывать `X-Forwarded-For`, а его IP/CIDR должен входить в
`TRUSTED_PROXIES`. Иначе backend увидит IP proxy/Docker gateway и может отклонить
валидный webhook с ошибкой `403`.
| Провайдер | Что указать в кабинете провайдера | Комментарий |
| --- | --- | --- |
| YooKassa | `WEBHOOK_BASE_URL` + `/webhook/yookassa` | Например `https://bot.example.com/webhook/yookassa`. |
+10
View File
@@ -433,6 +433,16 @@ docker compose up -d backend worker
- Mini App/frontend-домен целиком идет в `frontend:80`;
- API/auth/theme routes Mini App дальше проксируются frontend nginx в `backend:8081`.
Для платежных провайдеров с IP allowlist важно, чтобы reverse proxy передавал реальный IP
отправителя в `X-Forwarded-For`, а backend доверял IP последнего proxy-hop через
`TRUSTED_PROXIES`. Готовые профили `caddy`, `nginx` и `newt` уже доверяют loopback и
private ranges (`10.0.0.0/8`, `172.16.0.0/12`, `192.168.0.0/16`, `fc00::/7`), чтобы
Docker/LAN/Kubernetes proxy не ломал проверки `YOOKASSA`, `FREEKASSA_TRUSTED_IPS`,
`WATA_TRUSTED_IPS`, `HELEKET_TRUSTED_IPS` и `PAYKILLA_TRUSTED_IPS`. Если в вашей
Docker-сети есть недоверенные контейнеры, сузьте `TRUSTED_PROXIES` до конкретного IP
Caddy/Nginx/Newt. Trust-all режим возможен через `0.0.0.0/0,::/0`, но используйте его
только когда backend недоступен напрямую, а внешний proxy очищает входящий `X-Forwarded-For`.
Минимальная логика Caddy:
```caddyfile