deploy: bind backend/livekit-signaling/monitoring/llm ports to loopback

Close host-published ports that don't need to be reachable from outside
the docker network: nginx already proxies backend/livekit by service
name, and admin UIs (prometheus/grafana) and llm servers should only be
reachable via SSH tunnel. Only 80/443/7881 + the WebRTC UDP range stay
open to the internet.
This commit is contained in:
2026-07-25 22:58:38 +03:00
parent 6dda717ad8
commit d593add0f3
3 changed files with 35 additions and 6 deletions

View File

@@ -137,6 +137,12 @@ LLM_TOKENIZER_FILE=qwen3.5-4b-instruct.tokenizer.json
LLM_TOKENIZER_URL=https://huggingface.co/Qwen/Qwen3.5-4B/resolve/main/tokenizer.json LLM_TOKENIZER_URL=https://huggingface.co/Qwen/Qwen3.5-4B/resolve/main/tokenizer.json
# --- Мониторинг (профиль compose `monitoring`) --- # --- Мониторинг (профиль compose `monitoring`) ---
# Prometheus (9090) и Grafana (3001) публикуются ТОЛЬКО на 127.0.0.1
# (deploy/docker-compose.yml) — с боевого сервера доступ через ssh-туннель,
# напр. `ssh -L 3001:127.0.0.1:3001 -L 9090:127.0.0.1:9090 <user>@<host>`,
# затем открыть http://localhost:3001 / :9090 у себя локально. То же для
# backend (8000, локальный curl-дебаг), livekit signaling (7880, снаружи
# работает только через nginx wss://) и llm/llm-gpu (8080/8081).
GRAFANA_ADMIN_USER=admin GRAFANA_ADMIN_USER=admin
GRAFANA_ADMIN_PASSWORD=change-me-grafana GRAFANA_ADMIN_PASSWORD=change-me-grafana

View File

@@ -81,8 +81,12 @@ services:
volumes: volumes:
- ../config:/app/config:ro - ../config:/app/config:ro
- media:/app/media - media:/app/media
# Публикуем ТОЛЬКО на loopback: backend нужен снаружи compose-сети
# только для локального curl-дебага на сервере — nginx проксирует
# запросы по имени `backend:8000` внутри docker-сети, публичный доступ
# идёт исключительно через nginx (80/443).
ports: ports:
- "8000:8000" - "127.0.0.1:8000:8000"
depends_on: depends_on:
postgres: postgres:
condition: service_healthy condition: service_healthy
@@ -375,8 +379,13 @@ services:
# обязательно в кавычках: YAML запрещает ": " внутри неквотированного # обязательно в кавычках: YAML запрещает ": " внутри неквотированного
# plain-скаляра. # plain-скаляра.
LIVEKIT_KEYS: "${LIVEKIT_API_KEY:?LIVEKIT_API_KEY не задан в .env}: ${LIVEKIT_API_SECRET:?LIVEKIT_API_SECRET не задан в .env}" LIVEKIT_KEYS: "${LIVEKIT_API_KEY:?LIVEKIT_API_KEY не задан в .env}: ${LIVEKIT_API_SECRET:?LIVEKIT_API_SECRET не задан в .env}"
# 7880 (signaling) — ТОЛЬКО loopback: nginx проксирует /livekit/ по имени
# `livekit:7880` внутри docker-сети (см. nginx.conf.template), браузеры
# снаружи ходят через nginx/443 (wss://), прямой доступ к 7880 им не
# нужен. 7881/tcp и UDP-диапазон ниже — реальные медиа-порты, остаются
# публичными.
ports: ports:
- "7880:7880" # HTTP/WebSocket signaling - "127.0.0.1:7880:7880" # HTTP/WebSocket signaling
- "7881:7881" # RTC TCP fallback - "7881:7881" # RTC TCP fallback
# Узкий диапазон для dev на macOS: широкий (50000-60000) почти всегда # Узкий диапазон для dev на macOS: широкий (50000-60000) почти всегда
# конфликтует с занятыми UDP-портами хоста и тормозит Docker Desktop. # конфликтует с занятыми UDP-портами хоста и тормозит Docker Desktop.
@@ -556,8 +565,9 @@ services:
LLAMA_ARG_ENDPOINT_METRICS: 1 LLAMA_ARG_ENDPOINT_METRICS: 1
volumes: volumes:
- llm-models:/models/qwen:ro - llm-models:/models/qwen:ro
# Loopback-only: admin/debug доступ по ssh-туннелю, наружу не публикуется.
ports: ports:
- "8080:8080" - "127.0.0.1:8080:8080"
depends_on: depends_on:
llm-model-init: llm-model-init:
condition: service_completed_successfully condition: service_completed_successfully
@@ -596,8 +606,9 @@ services:
LLAMA_ARG_ENDPOINT_METRICS: 1 LLAMA_ARG_ENDPOINT_METRICS: 1
volumes: volumes:
- llm-models:/models/qwen:ro - llm-models:/models/qwen:ro
# Loopback-only: admin/debug доступ по ssh-туннелю, наружу не публикуется.
ports: ports:
- "8081:8080" - "127.0.0.1:8081:8080"
# Алиас `llm` в сети compose (в дополнение к штатному `llm-gpu`) — # Алиас `llm` в сети compose (в дополнение к штатному `llm-gpu`) —
# `llm` и `llm-gpu` взаимоисключающи по профилю (install.sh включает # `llm` и `llm-gpu` взаимоисключающи по профилю (install.sh включает
# ровно один), поэтому Prometheus (deploy/monitoring/prometheus.yml, # ровно один), поэтому Prometheus (deploy/monitoring/prometheus.yml,
@@ -639,8 +650,9 @@ services:
- ./monitoring/prometheus.yml:/etc/prometheus/prometheus.yml:ro - ./monitoring/prometheus.yml:/etc/prometheus/prometheus.yml:ro
- ./monitoring/alerts.yml:/etc/prometheus/alerts.yml:ro - ./monitoring/alerts.yml:/etc/prometheus/alerts.yml:ro
- prometheus_data:/prometheus - prometheus_data:/prometheus
# Loopback-only: админ-доступ по ssh-туннелю, наружу не публикуется.
ports: ports:
- "9090:9090" - "127.0.0.1:9090:9090"
healthcheck: healthcheck:
test: ["CMD-SHELL", "wget -q -O- http://127.0.0.1:9090/-/healthy || exit 1"] test: ["CMD-SHELL", "wget -q -O- http://127.0.0.1:9090/-/healthy || exit 1"]
interval: 10s interval: 10s
@@ -697,8 +709,9 @@ services:
- ./monitoring/grafana/provisioning:/etc/grafana/provisioning:ro - ./monitoring/grafana/provisioning:/etc/grafana/provisioning:ro
- ./monitoring/grafana/dashboards:/var/lib/grafana/dashboards:ro - ./monitoring/grafana/dashboards:/var/lib/grafana/dashboards:ro
- grafana_data:/var/lib/grafana - grafana_data:/var/lib/grafana
# Loopback-only: админ-доступ по ssh-туннелю, наружу не публикуется.
ports: ports:
- "3001:3000" - "127.0.0.1:3001:3000"
depends_on: depends_on:
prometheus: prometheus:
condition: service_healthy condition: service_healthy

View File

@@ -27,6 +27,16 @@ docker compose -f deploy/docker-compose.yml --env-file .env --profile monitoring
`GRAFANA_ADMIN_USER`/`GRAFANA_ADMIN_PASSWORD`; `install.sh` генерирует `GRAFANA_ADMIN_USER`/`GRAFANA_ADMIN_PASSWORD`; `install.sh` генерирует
пароль при первой установке) пароль при первой установке)
⚠️ На боевом сервере оба порта публикуются ТОЛЬКО на `127.0.0.1`
(`deploy/docker-compose.yml`) — открытые наружу admin-панели без
дополнительной аутентификации ранее уже приводили к компрометации сервисов
на этом проекте (см. `.forcc/deploy/SESSION2-FINDINGS.md`). С другой машины
используйте ssh-туннель:
```bash
ssh -L 9090:127.0.0.1:9090 -L 3001:127.0.0.1:3001 <user>@<host>
```
и открывайте `http://localhost:9090` / `http://localhost:3001` у себя.
Дашборд «Пайплайны пост-обработки» (папка VidConf в Grafana) появляется Дашборд «Пайплайны пост-обработки» (папка VidConf в Grafana) появляется
сразу — источник данных и дашборд провижинятся из файлов, без ручной сразу — источник данных и дашборд провижинятся из файлов, без ручной
настройки. настройки.