Redis without a password was the root cause of the security incident (cron miner via unauthenticated replication RCE, see .forcc/deploy/SESSION2-FINDINGS.md). Loopback binding alone doesn't protect against a compromised container inside the same compose network, so wire REDIS_PASSWORD as a required secret everywhere redis is used: backend/worker/worker-transcriber(-gpu), redis-exporter, livekit and egress (via rendered templates). docker compose now refuses to start without it instead of silently running unauthenticated.
549 lines
18 KiB
Markdown
549 lines
18 KiB
Markdown
# Переменные окружения (.env)
|
||
|
||
Полный справочник переменных окружения VidConf. Все секреты хранятся **только** в `.env` и никогда не коммитятся в git.
|
||
|
||
## Соглашение
|
||
|
||
Значения читаются из файла `.env` (или переменных окружения) при старте приложения. Dev-шаблон см. в `.env.example`.
|
||
|
||
---
|
||
|
||
## База данных
|
||
|
||
### DATABASE_URL
|
||
**Тип:** `str` | **Default:** `postgresql+asyncpg://vidconf:vidconf@localhost:5432/vidconf`
|
||
|
||
**Описание:** Connection string для асинхронного драйвера SQLAlchemy (asyncpg).
|
||
|
||
**Пример:**
|
||
```bash
|
||
DATABASE_URL=postgresql+asyncpg://vidconf:vidconf@localhost:5432/vidconf
|
||
```
|
||
|
||
**Проде:** Используйте отдельного пользователя с минимальными привилегиями (только SELECT/INSERT/UPDATE на нужные таблицы).
|
||
|
||
### POSTGRES_USER, POSTGRES_PASSWORD, POSTGRES_DB
|
||
**Для Docker Compose:** переменные инициализации контейнера PostgreSQL.
|
||
|
||
```bash
|
||
POSTGRES_USER=vidconf
|
||
POSTGRES_PASSWORD=change-me-secure
|
||
POSTGRES_DB=vidconf
|
||
```
|
||
|
||
---
|
||
|
||
## Redis (очередь Celery)
|
||
|
||
### REDIS_PASSWORD
|
||
**Тип:** `str` | **Обязателен** (нет дефолта — `${REDIS_PASSWORD:?}` в `deploy/docker-compose.yml`)
|
||
|
||
**Описание:** Пароль Redis (`--requirepass`). Без него `docker compose up` не
|
||
стартует — намеренно: инцидент безопасности на этом проекте начался именно
|
||
с redis без пароля, опубликованного наружу (см. `.forcc/deploy/SESSION2-FINDINGS.md`
|
||
во внутренних заметках сессии деплоя). `install.sh` генерирует случайное
|
||
значение при первой установке. Используется backend/worker/livekit/egress —
|
||
все читают его через `REDIS_URL`/секцию `redis:` своих конфигов, генерируемых
|
||
из `.env` (см. `deploy/render-templates.sh`).
|
||
|
||
**Пример:**
|
||
```bash
|
||
REDIS_PASSWORD=a1b2c3...
|
||
```
|
||
|
||
### REDIS_URL
|
||
**Тип:** `str` | **Default:** `redis://localhost:6379/0`
|
||
|
||
**Описание:** URL Redis для брокера Celery (очередь задач) и кэша сеансов.
|
||
Значим только при запуске backend/worker НА ХОСТЕ вне docker-сети — внутри
|
||
контейнеров `docker-compose.yml` всегда подставляет
|
||
`redis://:${REDIS_PASSWORD}@redis:6379/0` сам, это значение игнорируя.
|
||
|
||
**Пример:**
|
||
```bash
|
||
REDIS_URL=redis://:password@localhost:6379/0
|
||
REDIS_URL=redis://:password@redis.example.com:6379/1 # с паролем, БД 1
|
||
```
|
||
|
||
**Проде:** Redis публикуется только на `127.0.0.1` (loopback) — снаружи
|
||
недоступен даже с паролем; удалённый доступ — через ssh-туннель.
|
||
|
||
---
|
||
|
||
## Конфигурация приложения
|
||
|
||
### Бутстрап настроек инстанса (первый старт backend, `BOOTSTRAP_*`)
|
||
|
||
Три переменные определяют начальное состояние настроек модулей при первом
|
||
запуске backend. Используются инсталлятором `install.sh` для синхронизации
|
||
пресетов поставки (пресеты 1–5 → матрица `BOOTSTRAP_*`).
|
||
|
||
#### BOOTSTRAP_CHAT_ENABLED
|
||
|
||
**Тип:** `bool` (строка: `true` | `false`) | **Default:** `false`
|
||
|
||
**Описание:** Включить чат в конференциях при первом старте backend.
|
||
|
||
```bash
|
||
BOOTSTRAP_CHAT_ENABLED=false # пресеты 1, 3
|
||
BOOTSTRAP_CHAT_ENABLED=true # пресеты 2, 4, 5
|
||
```
|
||
|
||
**Применение:** однократно при первом старте (lifespan backend), затем игнорируется
|
||
(настройка хранится в БД, `instance_settings.chat.enabled`). Изменение переменной
|
||
на живой инсталляции не действует — меняйте через админ-API `PUT /api/v1/admin/settings`.
|
||
|
||
---
|
||
|
||
#### BOOTSTRAP_TRANSCRIPTION_ENABLED
|
||
|
||
**Тип:** `bool` | **Default:** `false`
|
||
|
||
**Описание:** Включить транскрибацию и суммаризацию при первом старте backend.
|
||
|
||
```bash
|
||
BOOTSTRAP_TRANSCRIPTION_ENABLED=false # пресеты 1, 2
|
||
BOOTSTRAP_TRANSCRIPTION_ENABLED=true # пресеты 3, 4, 5
|
||
```
|
||
|
||
**Применение:** однократно при первом старте (lifespan backend), затем игнорируется.
|
||
Выключение транскрибации = AI-уровень не применяется (см. ниже). На живой
|
||
инсталляции меняйте через `PUT /api/v1/admin/settings?transcription_enabled=...`.
|
||
|
||
**Guard:** если транскрибация включена в настройках (`transcription_enabled=true`),
|
||
но ни один Celery-воркер `transcriber` не обслуживает очередь, в админке
|
||
отображается предупреждение (поле `transcription_queue_served` в ответе
|
||
`GET /api/v1/admin/settings`).
|
||
|
||
---
|
||
|
||
#### BOOTSTRAP_AI_LEVEL
|
||
|
||
**Тип:** `str` | **Default:** `min` | **Допустимые:** `min`, `medium`, `max`
|
||
|
||
**Описание:** Уровень качества AI-обработки при первом старте backend
|
||
(требует `BOOTSTRAP_TRANSCRIPTION_ENABLED=true`).
|
||
|
||
```bash
|
||
BOOTSTRAP_AI_LEVEL=min # пресеты 1–3
|
||
BOOTSTRAP_AI_LEVEL=medium # пресет 4
|
||
BOOTSTRAP_AI_LEVEL=max # пресет 5
|
||
```
|
||
|
||
**Матрица инсталлятора (пресет → `BOOTSTRAP_*`):**
|
||
|
||
| Пресет | `BOOTSTRAP_CHAT_ENABLED` | `BOOTSTRAP_TRANSCRIPTION_ENABLED` | `BOOTSTRAP_AI_LEVEL` |
|
||
|--------|:---:|:---:|---|
|
||
| 1 (MVP) | `false` | `false` | `min` |
|
||
| 2 (+чат) | `true` | `false` | `min` |
|
||
| 3 (+AI мин) | `true` | `true` | `min` |
|
||
| 4 (+AI средний) | `true` | `true` | `medium` |
|
||
| 5 (+AI макс) | `true` | `true` | `max` |
|
||
|
||
**Применение:** однократно при первом старте, затем игнорируется
|
||
(настройка в `instance_settings.ai_level`). На живой инсталляции меняйте
|
||
через `PUT /api/v1/admin/settings?ai_level=...` (проверяется доступность уровня,
|
||
недоступный уровень → 400).
|
||
|
||
---
|
||
|
||
### PLUGINS_CONFIG_PATH
|
||
**Тип:** `str` | **Default:** `config/plugins.yaml`
|
||
|
||
**Описание:** Путь к конфигурационному файлу плагинов (от корня проекта или абсолютный).
|
||
|
||
```bash
|
||
PLUGINS_CONFIG_PATH=config/plugins.yaml
|
||
```
|
||
|
||
Содержит профили AI: transcriber (faster-whisper small/medium/large-v3), summarizer (Qwen3.5 4B/9B/35B-A3B), chat (отключён/включён).
|
||
|
||
---
|
||
|
||
## Пользователь seed (начальный администратор)
|
||
|
||
### SEED_ADMIN_EMAIL
|
||
**Тип:** `str` | **Default:** `admin@vidconf.example`
|
||
|
||
**Описание:** Email администратора, создаваемого при инициализации БД (`uv run python -m scripts.seed`, см. [backend/README.md](../../backend/README.md)).
|
||
|
||
```bash
|
||
SEED_ADMIN_EMAIL=admin@example.com
|
||
```
|
||
|
||
### SEED_ADMIN_PASSWORD
|
||
**Тип:** `str` | **Default:** `change-me`
|
||
|
||
**Описание:** Пароль администратора. **Измените на боевом инстансе сразу после развёртывания!**
|
||
|
||
```bash
|
||
SEED_ADMIN_PASSWORD=SuperSecurePassword123!
|
||
```
|
||
|
||
---
|
||
|
||
## Аутентификация и сессии (JWT)
|
||
|
||
### JWT_SECRET
|
||
**Тип:** `str` | **Default:** `dev-only-insecure-secret-change-me`
|
||
|
||
**Описание:** Секретный ключ для подписи JWT токенов (HS256).
|
||
|
||
**Требование:** Минимум 32 символа случайных данных для проде.
|
||
|
||
```bash
|
||
# Генерировать:
|
||
# python -c "import secrets; print(secrets.token_urlsafe(32))"
|
||
JWT_SECRET=your-long-random-secret-generated-above
|
||
```
|
||
|
||
### ACCESS_TOKEN_TTL_MINUTES
|
||
**Тип:** `int` | **Default:** `15`
|
||
|
||
**Описание:** Срок действия access-токена в минутах.
|
||
|
||
```bash
|
||
ACCESS_TOKEN_TTL_MINUTES=15
|
||
```
|
||
|
||
### REFRESH_TOKEN_TTL_DAYS
|
||
**Тип:** `int` | **Default:** `14`
|
||
|
||
**Описание:** Срок действия refresh-токена в днях. При истечении пользователь должен перелогиниться.
|
||
|
||
```bash
|
||
REFRESH_TOKEN_TTL_DAYS=14
|
||
```
|
||
|
||
### EMAIL_VERIFICATION_TTL_HOURS
|
||
**Тип:** `int` | **Default:** `24`
|
||
|
||
**Описание:** Срок действия email-верификационного токена в часах (при регистрации).
|
||
|
||
```bash
|
||
EMAIL_VERIFICATION_TTL_HOURS=24
|
||
```
|
||
|
||
### AUTH_COOKIE_SECURE
|
||
**Тип:** `bool` | **Default:** `true`
|
||
|
||
**Описание:** Флаг `Secure` для cookies, содержащих refresh-токены.
|
||
|
||
- **true** (проде) — cookies отправляются только по HTTPS
|
||
- **false** (dev на localhost) — cookies отправляются и по HTTP (необходимо для Safari на localhost без HTTPS)
|
||
|
||
```bash
|
||
AUTH_COOKIE_SECURE=false # только для dev
|
||
AUTH_COOKIE_SECURE=true # проде обязательно
|
||
```
|
||
|
||
### FRONTEND_URL
|
||
**Тип:** `str` | **Default:** `http://localhost:5173`
|
||
|
||
**Описание:** URL фронтенда. Используется для формирования ссылок в email'ах подтверждения (обычно не требуется, фронтенд за nginx'ом).
|
||
|
||
```bash
|
||
FRONTEND_URL=http://localhost:5173
|
||
FRONTEND_URL=https://vidconf.example.com
|
||
```
|
||
|
||
---
|
||
|
||
## LiveKit SFU
|
||
|
||
### LIVEKIT_URL
|
||
**Тип:** `str` | **Default:** `ws://localhost:7880`
|
||
|
||
**Описание:** Внутренний server-to-server URL LiveKit (для Celery задач, вызовы RoomService). Не проксируется через Nginx.
|
||
|
||
```bash
|
||
LIVEKIT_URL=ws://localhost:7880
|
||
LIVEKIT_URL=http://livekit:7880 # docker-сеть
|
||
```
|
||
|
||
### LIVEKIT_PUBLIC_URL
|
||
**Тип:** `str` | **Default:** `ws://localhost:7880`
|
||
|
||
**Описание:** Публичный URL LiveKit (браузер клиента). Обычно за nginx'ом с TLS.
|
||
|
||
```bash
|
||
LIVEKIT_PUBLIC_URL=ws://localhost:7880 # dev
|
||
LIVEKIT_PUBLIC_URL=wss://livekit.example.com # проде (WSS)
|
||
```
|
||
|
||
### LIVEKIT_API_KEY
|
||
**Тип:** `str` | **Default:** `devkey`
|
||
|
||
**Описание:** API ключ LiveKit (для генерации токенов комнат).
|
||
|
||
```bash
|
||
LIVEKIT_API_KEY=your-api-key
|
||
```
|
||
|
||
### LIVEKIT_API_SECRET
|
||
**Тип:** `str` | **Default:** `change-me-livekit-secret`
|
||
|
||
**Описание:** API секрет LiveKit (для подписи токенов).
|
||
|
||
```bash
|
||
LIVEKIT_API_SECRET=your-secret-key
|
||
```
|
||
|
||
Оба найдите в конфигурации LiveKit сервера (`livekit.conf`).
|
||
|
||
---
|
||
|
||
## TURN (Coturn)
|
||
|
||
### TURN_REALM
|
||
**Тип:** `str` | **Default:** `vidconf.local`
|
||
|
||
**Описание:** Realm для TURN сервера (Coturn). Испльзуется в credentials для WebRTC NAT traversal.
|
||
|
||
```bash
|
||
TURN_REALM=vidconf.example.com
|
||
```
|
||
|
||
### TURN_STATIC_AUTH_SECRET
|
||
**Тип:** `str` | **Default:** `change-me-turn-secret`
|
||
|
||
**Описание:** Секрет для TURN сервера (для временных credentials).
|
||
|
||
```bash
|
||
TURN_STATIC_AUTH_SECRET=your-secret-auth-key
|
||
```
|
||
|
||
---
|
||
|
||
## Аватары
|
||
|
||
### MEDIA_ROOT
|
||
**Тип:** `str` | **Default:** `/app/media`
|
||
|
||
**Описание:** Абсолютный путь к каталогу хранения загруженных аватаров пользователей.
|
||
|
||
```bash
|
||
MEDIA_ROOT=/app/media # Docker Compose (по умолчанию)
|
||
MEDIA_ROOT=/var/lib/vidconf/media # альтернативный путь на сервере
|
||
```
|
||
|
||
**Структура:**
|
||
```
|
||
MEDIA_ROOT/
|
||
└── avatars/
|
||
├── 550e8400-e29b-41d4-a716-446655440000.jpg
|
||
├── 6ba7b811-9dad-11d1-80b4-00c04fd430c8.png
|
||
└── ...
|
||
```
|
||
|
||
**Docker Compose:** том `media` монтируется в backend и nginx. Nginx раздаёт файлы напрямую по `location /media/` в обход backend'а (для performance).
|
||
|
||
---
|
||
|
||
## Транскрибация
|
||
|
||
### RECORDINGS_DIR
|
||
**Тип:** `str` | **Default:** `/recordings`
|
||
|
||
**Описание:** Абсолютный путь к тому с записями (LiveKit Egress пишет .ogg треки сюда, Celery воркер их читает).
|
||
|
||
```bash
|
||
RECORDINGS_DIR=/recordings
|
||
RECORDINGS_DIR=/mnt/recordings # альтернативный путь
|
||
```
|
||
|
||
**Docker Compose:** том `recordings` монтируется в обе сервиса (egress, transcriber). Менять путь внутри контейнера обычно не нужно (он совпадает с точкой монтирования).
|
||
|
||
---
|
||
|
||
## Email
|
||
|
||
Все SMTP-реквизиты — **только в `.env`, никогда не попадают в БД или API**.
|
||
|
||
### EMAIL_BACKEND
|
||
**Тип:** `str` | **Default:** `console`
|
||
|
||
**Допустимые значения:**
|
||
- `console` — dev: письма только логируются в stdout, ссылки берутся из логов
|
||
- `smtp` — реальная отправка через aiosmtplib
|
||
|
||
```bash
|
||
EMAIL_BACKEND=console # dev
|
||
EMAIL_BACKEND=smtp # проде
|
||
```
|
||
|
||
### SMTP_HOST
|
||
**Тип:** `str` | **Default:** `localhost`
|
||
|
||
**Описание:** Хост SMTP сервера (для `EMAIL_BACKEND=smtp`).
|
||
|
||
```bash
|
||
SMTP_HOST=smtp.gmail.com
|
||
SMTP_HOST=mail.example.com
|
||
```
|
||
|
||
### SMTP_PORT
|
||
**Тип:** `int` | **Default:** `587`
|
||
|
||
**Описание:** Порт SMTP (обычно 587 для TLS, 465 для SSL, 25 без шифрования).
|
||
|
||
```bash
|
||
SMTP_PORT=587 # TLS (STARTTLS)
|
||
SMTP_PORT=465 # SSL
|
||
SMTP_PORT=25 # plain
|
||
```
|
||
|
||
### SMTP_USERNAME
|
||
**Тип:** `str | None` | **Default:** `None`
|
||
|
||
**Описание:** Логин SMTP (если требуется аутентификация).
|
||
|
||
```bash
|
||
SMTP_USERNAME=noreply@vidconf.example.com
|
||
SMTP_USERNAME=
|
||
```
|
||
|
||
### SMTP_PASSWORD
|
||
**Тип:** `str | None` | **Default:** `None`
|
||
|
||
**Описание:** Пароль SMTP.
|
||
|
||
```bash
|
||
SMTP_PASSWORD=your-app-password
|
||
SMTP_PASSWORD=
|
||
```
|
||
|
||
### SMTP_START_TLS
|
||
**Тип:** `bool` | **Default:** `true`
|
||
|
||
**Описание:** Использовать STARTTLS (команда TLS после SMTP HELLO). Обычно для порта 587.
|
||
|
||
```bash
|
||
SMTP_START_TLS=true # порт 587
|
||
SMTP_START_TLS=false # если уже SSL или plain
|
||
```
|
||
|
||
### SMTP_USE_TLS
|
||
**Тип:** `bool` | **Default:** `false`
|
||
|
||
**Описание:** Использовать implicit TLS (сразу шифрованное соединение). Обычно для порта 465.
|
||
|
||
```bash
|
||
SMTP_USE_TLS=false # для STARTTLS (587)
|
||
SMTP_USE_TLS=true # для implicit TLS (465)
|
||
```
|
||
|
||
### SMTP_FROM
|
||
**Тип:** `str` | **Default:** `VidConf <no-reply@vidconf.example>`
|
||
|
||
**Описание:** From адрес в письмах (имя + email).
|
||
|
||
```bash
|
||
SMTP_FROM=VidConf <no-reply@vidconf.example>
|
||
SMTP_FROM=noreply@example.com
|
||
```
|
||
|
||
### SMTP_TIMEOUT_S
|
||
**Тип:** `int` | **Default:** `30`
|
||
|
||
**Описание:** Таймаут SMTP операций в секундах.
|
||
|
||
```bash
|
||
SMTP_TIMEOUT_S=30
|
||
```
|
||
|
||
---
|
||
|
||
## Примеры конфигурации
|
||
|
||
### Dev (console backend)
|
||
```bash
|
||
# Database & Redis
|
||
DATABASE_URL=postgresql+asyncpg://vidconf:vidconf@localhost:5432/vidconf
|
||
REDIS_PASSWORD=change-me-redis-secret
|
||
REDIS_URL=redis://:change-me-redis-secret@localhost:6379/0
|
||
|
||
# App
|
||
PLUGINS_CONFIG_PATH=config/plugins.yaml
|
||
JWT_SECRET=dev-only-secret-change-me
|
||
FRONTEND_URL=http://localhost:5173
|
||
|
||
# LiveKit
|
||
LIVEKIT_URL=ws://localhost:7880
|
||
LIVEKIT_PUBLIC_URL=ws://localhost:7880
|
||
LIVEKIT_API_KEY=devkey
|
||
LIVEKIT_API_SECRET=...
|
||
|
||
# Seed
|
||
SEED_ADMIN_EMAIL=admin@vidconf.example
|
||
SEED_ADMIN_PASSWORD=change-me
|
||
|
||
# Email (только логирование)
|
||
EMAIL_BACKEND=console
|
||
|
||
# Media (аватары)
|
||
MEDIA_ROOT=/app/media
|
||
```
|
||
|
||
### Проде (SMTP, TLS)
|
||
```bash
|
||
# Database & Redis
|
||
DATABASE_URL=postgresql+asyncpg://vidconf:secure-pass@db.example.com:5432/vidconf
|
||
REDIS_PASSWORD=redis-password
|
||
REDIS_URL=redis://:redis-password@redis.example.com:6379/0
|
||
|
||
# App
|
||
PLUGINS_CONFIG_PATH=config/plugins.yaml
|
||
JWT_SECRET=<generated-long-random-key>
|
||
ACCESS_TOKEN_TTL_MINUTES=15
|
||
REFRESH_TOKEN_TTL_DAYS=14
|
||
AUTH_COOKIE_SECURE=true
|
||
FRONTEND_URL=https://vidconf.example.com
|
||
|
||
# LiveKit
|
||
LIVEKIT_URL=http://livekit:7880
|
||
LIVEKIT_PUBLIC_URL=wss://livekit.example.com
|
||
LIVEKIT_API_KEY=your-key
|
||
LIVEKIT_API_SECRET=your-secret
|
||
|
||
# Seed
|
||
SEED_ADMIN_EMAIL=admin@vidconf.example
|
||
SEED_ADMIN_PASSWORD=<strong-password>
|
||
|
||
# Email
|
||
EMAIL_BACKEND=smtp
|
||
SMTP_HOST=smtp.sendgrid.net
|
||
SMTP_PORT=587
|
||
SMTP_USERNAME=apikey
|
||
SMTP_PASSWORD=SG.xxxxx...
|
||
SMTP_START_TLS=true
|
||
SMTP_USE_TLS=false
|
||
SMTP_FROM=VidConf <noreply@vidconf.example>
|
||
SMTP_TIMEOUT_S=30
|
||
|
||
# Media (аватары)
|
||
MEDIA_ROOT=/var/lib/vidconf/media
|
||
|
||
# Recordings
|
||
RECORDINGS_DIR=/mnt/recordings
|
||
|
||
# Transcription
|
||
```
|
||
|
||
---
|
||
|
||
## Безопасность
|
||
|
||
1. **Никогда не коммитьте `.env` в git** — используйте `.gitignore`
|
||
2. **Регулярно ротируйте JWT_SECRET** (потребует переlogin'ования пользователей)
|
||
3. **Используйте окружение для production credentials** (никогда не hardcod'ьте)
|
||
4. **Проверьте SMTP_PASSWORD в логах** — их быть не должно (логирование маскирует пароли)
|
||
5. **Для SMTP используйте app passwords** (не основной пароль аккаунта)
|
||
|
||
---
|
||
|
||
## Ссылки
|
||
|
||
- [.env.example](../../.env.example) — шаблон переменных для dev
|
||
- [backend/core/config.py](../../backend/core/config.py) — парсинг Settings в коде
|