Merge origin/dev into feature/telegram-flood-hardening

This commit is contained in:
3252a8
2026-06-10 16:49:53 +03:00
139 changed files with 6227 additions and 535 deletions
+12 -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
Обычно эти значения не требуют правки.
@@ -66,6 +75,7 @@
| `ADMIN_PANEL_STATS_CACHE_TTL_SECONDS` | TTL статистики Remnawave в админке. |
| `ADMIN_DB_STATS_CACHE_TTL_SECONDS` | TTL дорогих DB-агрегатов админки. |
| `ADMIN_USERS_LIST_CACHE_TTL_SECONDS` | TTL списка пользователей админки. |
| `ADMIN_BROADCAST_AUDIENCE_COUNTS_CACHE_TTL_SECONDS` | TTL счетчиков целевых групп рассылки в админке. |
| `PROFILE_SYNC_CACHE_TTL_SECONDS` | Минимальная пауза между sync Telegram-профиля пользователя. |
| `PANEL_SYNC_LIFETIME_TRAFFIC_MIN_INTERVAL_SECONDS` | Минимальная пауза записи lifetime-трафика. |
| `PANEL_SYNC_LIFETIME_TRAFFIC_MIN_DELTA_BYTES` | Дельта lifetime-трафика для более ранней записи. |
@@ -212,6 +222,7 @@
| `PAYMENT_METHODS_ORDER` | Порядок кнопок оплаты: `severpay,wata,freekassa,platega,yookassa,stars,cryptopay,heleket,paykilla`. |
| `SUBSCRIPTION_PURCHASE_DESCRIPTION_ENABLED` | Показывать описание подписки перед выбором срока. |
| `SUBSCRIPTION_PURCHASE_DESCRIPTION_RU` / `SUBSCRIPTION_PURCHASE_DESCRIPTION_EN` | Локализованное описание подписки. |
| `PAYMENT_REQUEST_TIMEOUT_SECONDS` | Общий таймаут одного API-запроса к платёжному провайдеру, в секундах. По умолчанию `20`. |
| `PAYMENT_<METHOD>_WEBAPP_LABEL_RU` / `PAYMENT_<METHOD>_WEBAPP_LABEL_EN` | Текст кнопки провайдера в Web App. |
| `PAYMENT_<METHOD>_WEBAPP_ICON` | Lucide-иконка кнопки в Web App. |
| `PAYMENT_<METHOD>_TELEGRAM_LABEL_RU` / `PAYMENT_<METHOD>_TELEGRAM_LABEL_EN` | Текст кнопки в Telegram. |
+8
View File
@@ -55,6 +55,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 на всех публичных доменах.
+3 -1
View File
@@ -11,6 +11,8 @@
| `installation_id` | `f47ac10b-...` (UUIDv4) | Случайный идентификатор установки. Генерируется один раз, хранится в БД. Не выводится из токена, домена или ID администраторов. |
| `app_version` | `v3.4.6+gabc1234` | Полная версия сборки. |
| `app_version_tag` | `v3.4.6` | Релизный тег для разбивки по версиям. |
| `build_provenance` | `official` / `custom` | Маркер происхождения образа. `official` ставится только официальной сборкой проекта; локальные сборки и форки считаются `custom`. |
| `image_modified` | `false` / `true` | Удобный булевый флаг для разбивки: `false` только у официальных образов, `true` у локальных/форк-сборок. |
| `os` / `arch` | `linux` / `x86_64` | Платформа. |
| `python_version` | `3.12.7` | Версия рантайма. |
| `locale` | `ru` | Язык по умолчанию. |
@@ -22,7 +24,7 @@
## Чего там нет
Никогда не отправляются: токен бота, домены, URL вебхуков, ключи платёжных систем и Remnawave, ID или данные пользователей, точное число пользователей, какой-либо контент.
Никогда не отправляются: токен бота, домены, URL вебхуков, ключи платёжных систем и Remnawave, ID или данные пользователей, точное число пользователей, какой-либо контент, registry образа или URL форка.
## Как выключить
+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`. |
+2
View File
@@ -7,6 +7,8 @@
JSON-каталог может содержать несколько тарифов разных моделей: подписки на срок, пакеты трафика без срока действия, разные наборы Internal Squads, лимиты устройств и пакеты докупки. Пример формата: [data/tariffs.example.json](https://github.com/3252a8/remnawave-minishop/blob/main/data/tariffs.example.json).
Важно для Docker: `TARIFFS_CONFIG_PATH` обычно указывает на файл внутри `/app/data`, и этот каталог должен быть доступен `migrate`, `backend` и `worker`. Мигратор тоже читает каталог тарифов, когда привязывает существующие активные подписки без `tariff_key` к тарифу по умолчанию и заполняет тарифные лимиты. В штатных compose-файлах это уже настроено: корневой compose использует `shop-data:/app/data`, а production-примеры - `./data:/app/data` для всех трех сервисов.
Коротко по моделям:
- `period` - подписка на срок с месячным лимитом трафика и опциональной докупкой GB поверх месячного лимита;
+3
View File
@@ -84,6 +84,8 @@ openssl rand -hex 32
В compose-примерах данные монтируются из локальной папки `./data` рядом с выбранным `docker-compose.yml`. Внутри нее лежат тарифы, темы, логотипы и прочие файловые данные приложения.
Папка `./data` монтируется в `/app/data` для `migrate`, `backend` и `worker`. Если вы меняете compose-файл вручную, не убирайте этот mount у `migrate`: иначе `docker compose run --rm migrate` не увидит `data/tariffs.json` из `TARIFFS_CONFIG_PATH`.
Перед первым запуском создайте каталоги и отдайте их пользователю контейнера:
```bash
@@ -91,6 +93,7 @@ mkdir -p data/themes data/webapp-logo data/tariffs
touch data/locales-overrides.json
chown -R 10001:10001 data
chmod -R u+rwX data
docker compose run --rm migrate
docker compose up -d --force-recreate backend worker
```
+22 -2
View File
@@ -22,6 +22,13 @@ curl -fsSL https://raw.githubusercontent.com/3252a8/remnawave-minishop/main/scri
sh install.sh
```
Та же ссылка на install-скрипт в GitLab:
```bash
curl -fsSL https://gitlab.com/3252a8/remnawave-minishop/-/raw/main/scripts/install.sh -o install.sh
sh install.sh
```
Wizard работает через меню с цифрами и подтверждениями `y/n`. Он умеет:
- скачать выбранный compose-профиль (`Caddy`, `Nginx`, `Pangolin/Newt` или `no-proxy`);
@@ -196,7 +203,7 @@ curl http://127.0.0.1:8082/health
docker compose logs -f backend worker frontend
```
Корневой `docker-compose.yml` оставлен для локальной сборки из исходников. Примеры в `deploy/examples` используют готовые GHCR-образы и не требуют указывать `-f`.
Корневой `docker-compose.yml` оставлен для локальной сборки из исходников. Примеры в `deploy/examples` используют готовые Docker Hub-образы и не требуют указывать `-f`.
## Миграции
@@ -310,7 +317,7 @@ powershell -ExecutionPolicy Bypass -File .\scripts\docker-build-push-images.ps1
Если нужен только один registry или другой namespace, переопределите переменные:
```bash
IMAGE_REGISTRIES=ghcr.io IMAGE_TAG=v3.4.3 bash scripts/docker-build-push-images.sh
IMAGE_REGISTRIES=docker.io IMAGE_TAG=v3.4.3 bash scripts/docker-build-push-images.sh
IMAGE_REGISTRIES="ghcr.io docker.io" IMAGE_NAMESPACE=other IMAGE_TAG=v3.4.3 bash scripts/docker-build-push-images.sh
```
@@ -374,6 +381,8 @@ distributed lock; код подготовлен к нескольким репл
Файлы приложения монтируются из локальной папки `./data` рядом с выбранным `docker-compose.yml` в
`/app/data`; внутри нее лежат тарифы, темы, логотипы и прочие файловые данные приложения.
Тот же `/app/data` должен быть смонтирован в `migrate`, `backend` и `worker`. Это важно для `data/tariffs.json`: `docker compose run --rm migrate` читает тот же каталог тарифов, что и приложение. В текущих compose-файлах этот mount уже есть у всех трех сервисов.
Перед первым запуском на сервере заранее дайте права пользователю контейнера `10001`:
```bash
@@ -381,6 +390,7 @@ mkdir -p data/themes data/webapp-logo data/tariffs
touch data/locales-overrides.json
chown -R 10001:10001 data
chmod -R u+rwX data
docker compose run --rm migrate
docker compose up -d --force-recreate backend worker
```
@@ -423,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
+7
View File
@@ -8,6 +8,13 @@ curl -fsSL https://raw.githubusercontent.com/3252a8/remnawave-minishop/main/scri
sh install.sh
```
Та же ссылка на install-скрипт в GitLab:
```bash
curl -fsSL https://gitlab.com/3252a8/remnawave-minishop/-/raw/main/scripts/install.sh -o install.sh
sh install.sh
```
В меню выберите `Install new stack and run migration` для нового сервера
или `Run migration only`, если compose-папка и `.env` уже готовы.
+7
View File
@@ -7,6 +7,13 @@ curl -fsSL https://raw.githubusercontent.com/3252a8/remnawave-minishop/main/scri
sh install.sh
```
Та же ссылка на install-скрипт в GitLab:
```bash
curl -fsSL https://gitlab.com/3252a8/remnawave-minishop/-/raw/main/scripts/install.sh -o install.sh
sh install.sh
```
В меню выберите `Install new stack and run migration` для нового
сервера или `Run migration only`, если compose-папка уже готова. Затем
выберите источник `Old remnawave-tg-shop`.