deploy: require redis auth (--requirepass) across all consumers

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.
This commit is contained in:
2026-07-25 22:59:34 +03:00
parent d593add0f3
commit 5e42f6d11b
9 changed files with 77 additions and 15 deletions

View File

@@ -32,6 +32,13 @@ services:
redis:
image: redis:7
restart: unless-stopped
# requirepass обязателен: сам по себе loopback-биндинг порта (ниже) не
# защищает от взлома через скомпрометированный/зловредный контейнер в
# той же compose-сети — только внешнюю экспозицию. Инцидент безопасности
# (см. .forcc/deploy/SESSION2-FINDINGS.md) начался именно с redis без
# пароля. `${REDIS_PASSWORD:?...}` — без секрета в .env контейнер не
# стартует, а не тихо поднимается без аутентификации.
command: ["redis-server", "--requirepass", "${REDIS_PASSWORD:?REDIS_PASSWORD не задан в .env}"]
# Публикуем ТОЛЬКО на loopback: redis используется исключительно внутри
# compose-сети (backend/worker/livekit/egress по имени `redis:6379`).
# Публикация на 0.0.0.0 без пароля = открытый redis в интернет — вектор
@@ -41,7 +48,7 @@ services:
volumes:
- redis_data:/data
healthcheck:
test: ["CMD", "redis-cli", "ping"]
test: ["CMD", "redis-cli", "-a", "${REDIS_PASSWORD:?REDIS_PASSWORD не задан в .env}", "--no-auth-warning", "ping"]
interval: 5s
timeout: 5s
retries: 10
@@ -56,7 +63,7 @@ services:
- ../.env
environment:
DATABASE_URL: ${DATABASE_URL:-postgresql+asyncpg://vidconf:vidconf@postgres:5432/vidconf}
REDIS_URL: ${REDIS_URL:-redis://redis:6379/0}
REDIS_URL: redis://:${REDIS_PASSWORD:?REDIS_PASSWORD не задан в .env}@redis:6379/0
PLUGINS_CONFIG_PATH: ${PLUGINS_CONFIG_PATH:-config/plugins.yaml}
LIVEKIT_API_KEY: ${LIVEKIT_API_KEY:?LIVEKIT_API_KEY не задан в .env}
LIVEKIT_API_SECRET: ${LIVEKIT_API_SECRET:?LIVEKIT_API_SECRET не задан в .env}
@@ -119,7 +126,7 @@ services:
- ../.env
environment:
DATABASE_URL: ${DATABASE_URL:-postgresql+asyncpg://vidconf:vidconf@postgres:5432/vidconf}
REDIS_URL: ${REDIS_URL:-redis://redis:6379/0}
REDIS_URL: redis://:${REDIS_PASSWORD:?REDIS_PASSWORD не задан в .env}@redis:6379/0
PLUGINS_CONFIG_PATH: ${PLUGINS_CONFIG_PATH:-config/plugins.yaml}
PYTHONPATH: /app
volumes:
@@ -216,7 +223,7 @@ services:
- ../.env
environment:
DATABASE_URL: ${DATABASE_URL:-postgresql+asyncpg://vidconf:vidconf@postgres:5432/vidconf}
REDIS_URL: ${REDIS_URL:-redis://redis:6379/0}
REDIS_URL: redis://:${REDIS_PASSWORD:?REDIS_PASSWORD не задан в .env}@redis:6379/0
PLUGINS_CONFIG_PATH: ${PLUGINS_CONFIG_PATH:-config/plugins.yaml}
RECORDINGS_DIR: ${RECORDINGS_DIR:-/recordings}
PYTHONPATH: /app
@@ -272,7 +279,7 @@ services:
- ../.env
environment:
DATABASE_URL: ${DATABASE_URL:-postgresql+asyncpg://vidconf:vidconf@postgres:5432/vidconf}
REDIS_URL: ${REDIS_URL:-redis://redis:6379/0}
REDIS_URL: redis://:${REDIS_PASSWORD:?REDIS_PASSWORD не задан в .env}@redis:6379/0
PLUGINS_CONFIG_PATH: ${PLUGINS_CONFIG_PATH:-config/plugins.yaml}
RECORDINGS_DIR: ${RECORDINGS_DIR:-/recordings}
PYTHONPATH: /app
@@ -684,6 +691,7 @@ services:
restart: unless-stopped
environment:
REDIS_ADDR: "redis://redis:6379"
REDIS_PASSWORD: ${REDIS_PASSWORD:?REDIS_PASSWORD не задан в .env}
depends_on:
redis:
condition: service_healthy

View File

@@ -13,9 +13,12 @@
log_level: info
# Обязателен для egress (координация запущенных записей, см. комментарий
# в deploy/livekit/livekit.yaml).
# в deploy/livekit/livekit.yaml). password подставляется рендером
# (deploy/render-templates.sh) из REDIS_PASSWORD в .env — redis в
# docker-compose.yml запускается с --requirepass.
redis:
address: redis:6379
password: ${REDIS_PASSWORD}
# HTTP-эндпоинт /healthz для healthcheck контейнера.
health_port: 8081

View File

@@ -38,8 +38,13 @@ rtc:
# (проверено по официальной документации livekit/egress, раздел "Running
# locally"). LiveKit сам по себе тоже использует redis для координации
# между узлами кластера (здесь один узел, но сервис оставлен включённым).
# password подставляется рендером (deploy/render-templates.sh) из
# REDIS_PASSWORD в .env — redis в docker-compose.yml запускается с
# --requirepass, без пароля LiveKit не подключится (см.
# .forcc/deploy/SESSION2-FINDINGS.md про redis без пароля).
redis:
address: redis:6379
password: ${REDIS_PASSWORD}
# TURN обслуживает отдельный сервис coturn (профиль `media`, deploy/coturn).
# При публичной экспозиции LiveKit разместите coturn/TURN-TLS на 443 и

View File

@@ -33,6 +33,7 @@ TURN_EXTERNAL_IP="$(env_var TURN_EXTERNAL_IP)"
LIVEKIT_API_KEY="$(env_var LIVEKIT_API_KEY)"
LIVEKIT_NODE_IP="$(env_var LIVEKIT_NODE_IP)"
LIVEKIT_USE_EXTERNAL_IP="$(env_var LIVEKIT_USE_EXTERNAL_IP)"
REDIS_PASSWORD="$(env_var REDIS_PASSWORD)"
: "${TURN_STATIC_AUTH_SECRET:?TURN_STATIC_AUTH_SECRET не задан в .env}"
: "${TURN_REALM:?TURN_REALM не задан в .env}"
@@ -40,13 +41,18 @@ LIVEKIT_USE_EXTERNAL_IP="$(env_var LIVEKIT_USE_EXTERNAL_IP)"
: "${LIVEKIT_API_KEY:?LIVEKIT_API_KEY не задан в .env}"
: "${LIVEKIT_NODE_IP:?LIVEKIT_NODE_IP не задан в .env (внешний IP/домен для ICE)}"
: "${LIVEKIT_USE_EXTERNAL_IP:?LIVEKIT_USE_EXTERNAL_IP не задан в .env (true/false)}"
: "${REDIS_PASSWORD:?REDIS_PASSWORD не задан в .env (redis запускается с --requirepass, см. docker-compose.yml)}"
export TURN_STATIC_AUTH_SECRET TURN_REALM TURN_EXTERNAL_IP LIVEKIT_API_KEY LIVEKIT_NODE_IP LIVEKIT_USE_EXTERNAL_IP
export TURN_STATIC_AUTH_SECRET TURN_REALM TURN_EXTERNAL_IP LIVEKIT_API_KEY LIVEKIT_NODE_IP LIVEKIT_USE_EXTERNAL_IP REDIS_PASSWORD
envsubst '${TURN_STATIC_AUTH_SECRET} ${TURN_REALM} ${TURN_EXTERNAL_IP}' \
< "$SCRIPT_DIR/coturn/turnserver.conf.template" > "$SCRIPT_DIR/coturn/turnserver.conf"
echo "[render] deploy/coturn/turnserver.conf готов"
envsubst '${LIVEKIT_USE_EXTERNAL_IP} ${LIVEKIT_NODE_IP} ${LIVEKIT_API_KEY}' \
envsubst '${LIVEKIT_USE_EXTERNAL_IP} ${LIVEKIT_NODE_IP} ${LIVEKIT_API_KEY} ${REDIS_PASSWORD}' \
< "$SCRIPT_DIR/livekit/livekit.yaml.template" > "$SCRIPT_DIR/livekit/livekit.yaml"
echo "[render] deploy/livekit/livekit.yaml готов"
envsubst '${REDIS_PASSWORD}' \
< "$SCRIPT_DIR/egress/egress.yaml.template" > "$SCRIPT_DIR/egress/egress.yaml"
echo "[render] deploy/egress/egress.yaml готов"