deploy: harden nginx/coturn/livekit configs (TLS on 443, templates, required secrets)
Переносит рабочие правки продакшен-сервера в репозиторий воспроизводимо: - nginx: HTTPS-блок на 443 (TLS1.2/1.3, http->https redirect, ACME webroot, X-Forwarded-Proto) добавлен в nginx.conf.template. Список доменов и имя каталога сертификата — параметры (NGINX_SERVER_NAMES, NGINX_CERT_NAME), не хардкод. Рендерится штатным entrypoint'ом образа nginx (envsubst-on-templates). - Самоподписанный dev-сертификат генерируется на старте контейнера (docker-entrypoint-certs.sh), если реальный Let's Encrypt не смонтирован — иначе `docker compose up` без профиля ломался бы локально без реальных сертификатов. - healthcheck nginx переключён на /healthz (без TLS-редиректа), иначе http://127.0.0.1:80/ после добавления 443-редиректа всегда unhealthy. - coturn/turnserver.conf и livekit/livekit.yaml переведены в *.template — оба образа не читают env для статических полей (static-auth-secret, use_external_ip/node_ip, webhook.api_key), поэтому реальные файлы генерируются перед стартом стека скриптом deploy/render-templates.sh (вызывается install.sh). Секреты/IP — только в .env, не в git. - docker-compose.yml: LIVEKIT_API_KEY/SECRET везде (backend, livekit, egress) через ${VAR:?} без небезопасных дефолтов; порт 443 + монтирование /etc/letsencrypt (ro) и certbot-webroot у nginx; build-args VITE_LIVEKIT_URL/NEXT_PUBLIC_LIVEKIT_URL для frontend (сейчас не используются кодом, оставлены про запас).
This commit is contained in:
@@ -51,8 +51,8 @@ services:
|
||||
DATABASE_URL: ${DATABASE_URL:-postgresql+asyncpg://vidconf:vidconf@postgres:5432/vidconf}
|
||||
REDIS_URL: ${REDIS_URL:-redis://redis:6379/0}
|
||||
PLUGINS_CONFIG_PATH: ${PLUGINS_CONFIG_PATH:-config/plugins.yaml}
|
||||
LIVEKIT_API_KEY: ${LIVEKIT_API_KEY:-devkey}
|
||||
LIVEKIT_API_SECRET: ${LIVEKIT_API_SECRET:-change-me-livekit-secret}
|
||||
LIVEKIT_API_KEY: ${LIVEKIT_API_KEY:?LIVEKIT_API_KEY не задан в .env}
|
||||
LIVEKIT_API_SECRET: ${LIVEKIT_API_SECRET:?LIVEKIT_API_SECRET не задан в .env}
|
||||
LIVEKIT_PUBLIC_URL: ${LIVEKIT_PUBLIC_URL:-ws://localhost:7880}
|
||||
# Внутренний server-to-server адрес LiveKit (RoomService/Egress API).
|
||||
# Жёстко перекрывает значение из .env: там LIVEKIT_URL=ws://localhost:7880
|
||||
@@ -299,25 +299,52 @@ services:
|
||||
# Собственный образ: nginx со вкомпилированной статикой фронтенд-SPA
|
||||
# (frontend/Dockerfile, multi-stage: Vite-сборка → nginx). Раздаётся во
|
||||
# ВСЕХ пресетах (сервис без profiles), поэтому http://localhost/ отдаёт
|
||||
# рабочий фронт, а не заглушку. Конфиг реверс-прокси по-прежнему
|
||||
# монтируется томом ниже — правки прокси не требуют пересборки фронта.
|
||||
# рабочий фронт, а не заглушку.
|
||||
build:
|
||||
context: ../frontend
|
||||
dockerfile: Dockerfile
|
||||
args:
|
||||
# Инлайнится Vite на этапе сборки (см. frontend/Dockerfile) — сейчас
|
||||
# фронтенд получает LiveKit URL в рантайме от backend (join.livekit_url,
|
||||
# см. backend/services/conference_access.py: LIVEKIT_PUBLIC_URL), эти
|
||||
# build-args пока ни на что не влияют (см. комментарий в
|
||||
# frontend/Dockerfile), переданы про запас на будущее.
|
||||
VITE_LIVEKIT_URL: ${VITE_LIVEKIT_URL:-}
|
||||
NEXT_PUBLIC_LIVEKIT_URL: ${NEXT_PUBLIC_LIVEKIT_URL:-}
|
||||
image: vidconf-nginx:latest
|
||||
restart: unless-stopped
|
||||
environment:
|
||||
# Подставляются штатным entrypoint'ом образа nginx (envsubst-on-templates,
|
||||
# см. deploy/nginx/nginx.conf.template) — список доменов и имя каталога
|
||||
# сертификата вынесены в параметры, а не хардкод.
|
||||
NGINX_SERVER_NAMES: ${NGINX_SERVER_NAMES:?NGINX_SERVER_NAMES не задан в .env (список доменов через пробел)}
|
||||
NGINX_CERT_NAME: ${NGINX_CERT_NAME:?NGINX_CERT_NAME не задан в .env (каталог в /etc/letsencrypt/live/)}
|
||||
volumes:
|
||||
- ./nginx/nginx.conf:/etc/nginx/conf.d/default.conf:ro
|
||||
- ./nginx/nginx.conf.template:/etc/nginx/templates/default.conf.template:ro
|
||||
# Готовит /etc/nginx/certs/{fullchain,privkey}.pem до старта nginx: либо
|
||||
# копирует боевой сертификат из /etc/letsencrypt, либо (если его нет —
|
||||
# локальная разработка) генерирует самоподписанный. См. сам скрипт.
|
||||
- ./nginx/docker-entrypoint-certs.sh:/docker-entrypoint.d/15-vidconf-certs.sh:ro
|
||||
# Аватары: тот же volume, что у backend — nginx раздаёт файлы
|
||||
# напрямую (`location /media/`, alias), в обход backend-процесса.
|
||||
- media:/media:ro
|
||||
# Реальные сертификаты Let's Encrypt — с хоста, только на чтение (в dev
|
||||
# без ./certbot-webroot Docker создаст /etc/letsencrypt пустым — скрипт
|
||||
# выше в этом случае сгенерирует самоподписанный сертификат).
|
||||
- /etc/letsencrypt:/etc/letsencrypt:ro
|
||||
# Webroot для ACME http-01 challenge (certbot renew).
|
||||
- ./certbot-webroot:/var/www/certbot:rw
|
||||
ports:
|
||||
- "80:80"
|
||||
- "443:443"
|
||||
depends_on:
|
||||
backend:
|
||||
condition: service_healthy
|
||||
healthcheck:
|
||||
test: ["CMD", "wget", "-q", "-O", "-", "http://127.0.0.1:80/"]
|
||||
# /healthz (не /) — после добавления HTTP→HTTPS редиректа (443) `/`
|
||||
# на порту 80 отдаёт 301 вместо 200, что уронило бы healthcheck;
|
||||
# /healthz отвечает 200 без редиректа.
|
||||
test: ["CMD", "wget", "-q", "-O", "-", "http://127.0.0.1:80/healthz"]
|
||||
interval: 10s
|
||||
timeout: 5s
|
||||
retries: 5
|
||||
@@ -329,6 +356,10 @@ services:
|
||||
image: livekit/livekit-server:latest
|
||||
restart: unless-stopped
|
||||
command: --config /etc/livekit.yaml
|
||||
# Реальный livekit.yaml генерируется из шаблона перед стартом
|
||||
# (deploy/render-templates.sh, вызывается install.sh) — use_external_ip/
|
||||
# node_ip/webhook.api_key статичны и не читают env напрямую, в отличие
|
||||
# от LIVEKIT_KEYS ниже (см. комментарий в livekit.yaml.template).
|
||||
volumes:
|
||||
- ./livekit/livekit.yaml:/etc/livekit.yaml:ro
|
||||
environment:
|
||||
@@ -336,7 +367,7 @@ services:
|
||||
# иначе LiveKit падает в crash-loop с "Could not parse keys". Значение
|
||||
# обязательно в кавычках: YAML запрещает ": " внутри неквотированного
|
||||
# plain-скаляра.
|
||||
LIVEKIT_KEYS: "${LIVEKIT_API_KEY:-devkey}: ${LIVEKIT_API_SECRET:-change-me-livekit-secret}"
|
||||
LIVEKIT_KEYS: "${LIVEKIT_API_KEY:?LIVEKIT_API_KEY не задан в .env}: ${LIVEKIT_API_SECRET:?LIVEKIT_API_SECRET не задан в .env}"
|
||||
ports:
|
||||
- "7880:7880" # HTTP/WebSocket signaling
|
||||
- "7881:7881" # RTC TCP fallback
|
||||
@@ -361,11 +392,14 @@ services:
|
||||
image: coturn/coturn:latest
|
||||
restart: unless-stopped
|
||||
command: -c /etc/coturn/turnserver.conf
|
||||
# Секреты (static-auth-secret/realm/external-ip) НЕ передаются через
|
||||
# environment — coturn читает их ТОЛЬКО из файла конфигурации, запущенного
|
||||
# через `-c` (при CLI-флагах он умеет $(VAR), при файле — нет, см.
|
||||
# комментарий в deploy/coturn/turnserver.conf.template). Реальный
|
||||
# turnserver.conf генерируется из шаблона перед стартом
|
||||
# (deploy/render-templates.sh, вызывается install.sh).
|
||||
volumes:
|
||||
- ./coturn/turnserver.conf:/etc/coturn/turnserver.conf:ro
|
||||
environment:
|
||||
TURN_REALM: ${TURN_REALM:-vidconf.local}
|
||||
TURN_STATIC_AUTH_SECRET: ${TURN_STATIC_AUTH_SECRET:-change-me-turn-secret}
|
||||
network_mode: host
|
||||
# Образ coturn/coturn — минимальный (debian-slim), в нём нет pgrep/ps/nc/
|
||||
# curl/wget, поэтому проверка процесса по имени не работает
|
||||
@@ -414,8 +448,8 @@ services:
|
||||
# api_key/api_secret/ws_url — обязательные переменные окружения
|
||||
# (см. deploy/egress/egress.yaml); ws_url — внутренний адрес сервиса
|
||||
# livekit в docker-сети, НЕ LIVEKIT_PUBLIC_URL для браузера.
|
||||
LIVEKIT_API_KEY: ${LIVEKIT_API_KEY:-devkey}
|
||||
LIVEKIT_API_SECRET: ${LIVEKIT_API_SECRET:-change-me-livekit-secret}
|
||||
LIVEKIT_API_KEY: ${LIVEKIT_API_KEY:?LIVEKIT_API_KEY не задан в .env}
|
||||
LIVEKIT_API_SECRET: ${LIVEKIT_API_SECRET:?LIVEKIT_API_SECRET не задан в .env}
|
||||
LIVEKIT_WS_URL: ${LIVEKIT_WS_URL:-ws://livekit:7880}
|
||||
depends_on:
|
||||
# required: false — livekit живёт в профиле `media`, а не
|
||||
|
||||
Reference in New Issue
Block a user