Compare commits
2 Commits
45c997380f
...
0441f4f72b
| Author | SHA1 | Date | |
|---|---|---|---|
| 0441f4f72b | |||
| 9bc8d6174d |
10
.env.example
10
.env.example
@@ -59,6 +59,16 @@ TURN_STATIC_AUTH_SECRET=change-me-turn-secret
|
||||
# скриптом deploy/render-templates.sh (вызывается install.sh).
|
||||
TURN_EXTERNAL_IP=127.0.0.1
|
||||
|
||||
# TURN over TLS (5349) — единственный переключатель во всём проекте: пусто =
|
||||
# TLS выключен везде (dev-дефолт, как ниже), непустое значение = coturn
|
||||
# слушает TLS на 5349 (сертификат смонтирован из /etc/letsencrypt через
|
||||
# coturn-certs-init, docker-compose.yml) И LiveKit объявляет клиентам запись
|
||||
# protocol: tls. ⚠️ Обязан быть ДОМЕНОМ сертификата (например, vidconf.ru —
|
||||
# тем же, что и NGINX_CERT_NAME), а НЕ IP-адресом, в отличие от
|
||||
# TURN_EXTERNAL_IP выше: браузер проверяет TLS-сертификат TURN-сервера по
|
||||
# имени хоста, а Let's Encrypt выписывает сертификат на домен.
|
||||
TURN_TLS_HOST=
|
||||
|
||||
# --- Nginx: TLS (443) + список доменов — deploy/nginx/nginx.conf.template ---
|
||||
# Домены, которые обслуживает nginx (через пробел, все — в server_name).
|
||||
NGINX_SERVER_NAMES=example.com www.example.com
|
||||
|
||||
@@ -12,8 +12,8 @@
|
||||
# затрутся при следующем рендере.
|
||||
|
||||
listening-port=3478
|
||||
# Установить 5349 в проде для TURN over TLS, если будут смонтированы
|
||||
# реальные TLS-сертификаты (см. закомментированные cert/pkey ниже).
|
||||
# TLS-порт объявлен всегда; реально слушать TLS coturn начинает только когда
|
||||
# заданы cert/pkey ниже (блок TLS-CERT) — без них строка ничего не включает.
|
||||
tls-listening-port=5349
|
||||
|
||||
# Диапазон relay-портов для TURN-аллокаций.
|
||||
@@ -31,10 +31,15 @@ fingerprint
|
||||
# Без CLI/telnet admin-интерфейса в этой поставке.
|
||||
no-cli
|
||||
|
||||
# Раскомментировать и смонтировать реальные сертификаты, чтобы включить
|
||||
# TURN over TLS на 443:
|
||||
# cert=/etc/coturn/certs/cert.pem
|
||||
# pkey=/etc/coturn/certs/key.pem
|
||||
# Блок ниже рендерится ТОЛЬКО когда в .env задан TURN_TLS_HOST (см.
|
||||
# render-templates.sh) — тогда coturn-certs-init (docker-compose.yml) уже
|
||||
# скопировал fullchain/privkey из /etc/letsencrypt в volume coturn-certs.
|
||||
# Без TURN_TLS_HOST маркеры и всё, что между ними, вырезаются целиком —
|
||||
# в файле не остаётся ни следа cert/pkey, а не просто закомментированных строк.
|
||||
# BEGIN-TLS-CERT
|
||||
cert=/etc/coturn/certs/cert.pem
|
||||
pkey=/etc/coturn/certs/key.pem
|
||||
# END-TLS-CERT
|
||||
|
||||
log-file=stdout
|
||||
simple-log
|
||||
|
||||
@@ -432,6 +432,49 @@ services:
|
||||
profiles: ["media"]
|
||||
logging: *default-logging
|
||||
|
||||
# Образ coturn/coturn — Dockerfile прописывает `USER nobody:nogroup`, и это
|
||||
# НЕ runtime-привилегия, которую можно сбросить: Docker exec'ает entrypoint
|
||||
# сразу от этого uid, root-фазы внутри контейнера нет вовсе (в отличие от
|
||||
# официального образа nginx, который стартует entrypoint от root и только
|
||||
# nginx-воркеры позже понижают права по директиве в конфиге — см.
|
||||
# deploy/nginx/docker-entrypoint-certs.sh). Значит coturn физически не может
|
||||
# сам прочитать приватный ключ Let's Encrypt (root:root, обычно 0600) —
|
||||
# никакой volume-опцией это не обойти, не ослабляя права на ключ на хосте.
|
||||
#
|
||||
# Решение — по образцу уже существующего `recordings-init`/`llm-models-init`
|
||||
# в этом файле: отдельный init-контейнер (busybox, дефолтный root) читает
|
||||
# /etc/letsencrypt (той же ro-монтировкой, что и у nginx) и копирует
|
||||
# fullchain/privkey в СВОЙ volume под правами 644 — это копия, а не
|
||||
# оригинал, оригинальный ключ на хосте прав не меняет. Копия достаточно
|
||||
# открыта, чтобы её прочитал nobody:nogroup внутри coturn.
|
||||
#
|
||||
# Если /etc/letsencrypt/live/<домен> не существует (dev, нет реальных
|
||||
# сертификатов) — команда ниже просто ничего не копирует и завершается
|
||||
# успешно; coturn стартует как раньше, без TLS (см. TURN_TLS_HOST в
|
||||
# render-templates.sh — вторая половина того же переключателя).
|
||||
coturn-certs-init:
|
||||
image: busybox:1.36
|
||||
command: >
|
||||
sh -c '
|
||||
SRC="/etc/letsencrypt/live/$$NGINX_CERT_NAME";
|
||||
if [ -f "$$SRC/fullchain.pem" ] && [ -f "$$SRC/privkey.pem" ]; then
|
||||
cp "$$SRC/fullchain.pem" /certs/cert.pem;
|
||||
cp "$$SRC/privkey.pem" /certs/key.pem;
|
||||
chmod 644 /certs/cert.pem /certs/key.pem;
|
||||
echo "[coturn-certs-init] сертификат $$SRC скопирован в volume coturn-certs";
|
||||
else
|
||||
echo "[coturn-certs-init] $$SRC не найден — TLS для coturn не настроен (норма для dev без TURN_TLS_HOST)";
|
||||
fi
|
||||
'
|
||||
environment:
|
||||
NGINX_CERT_NAME: ${NGINX_CERT_NAME:?NGINX_CERT_NAME не задан в .env}
|
||||
volumes:
|
||||
- /etc/letsencrypt:/etc/letsencrypt:ro
|
||||
- coturn-certs:/certs
|
||||
restart: "no"
|
||||
profiles: ["media"]
|
||||
logging: *default-logging
|
||||
|
||||
coturn:
|
||||
image: coturn/coturn:latest
|
||||
restart: unless-stopped
|
||||
@@ -444,6 +487,10 @@ services:
|
||||
# (deploy/render-templates.sh, вызывается install.sh).
|
||||
volumes:
|
||||
- ./coturn/turnserver.conf:/etc/coturn/turnserver.conf:ro
|
||||
- coturn-certs:/etc/coturn/certs:ro
|
||||
depends_on:
|
||||
coturn-certs-init:
|
||||
condition: service_completed_successfully
|
||||
network_mode: host
|
||||
# Образ coturn/coturn — минимальный (debian-slim), в нём нет pgrep/ps/nc/
|
||||
# curl/wget, поэтому проверка процесса по имени не работает
|
||||
@@ -887,6 +934,11 @@ volumes:
|
||||
# Загруженные пользователями файлы (аватары) — общий том между
|
||||
# backend (запись при загрузке) и nginx (раздача статики, `location /media/`).
|
||||
media:
|
||||
# Копия fullchain/privkey Let's Encrypt под правами 644 для coturn
|
||||
# (nobody:nogroup) — источник в /etc/letsencrypt не трогаем, см.
|
||||
# coturn-certs-init выше. Обновляется при каждом перезапуске
|
||||
# coturn-certs-init (deploy-hook certbot делает это при продлении).
|
||||
coturn-certs:
|
||||
# Метрики Prometheus (профиль `monitoring`) — переживают пересоздание контейнера.
|
||||
prometheus_data:
|
||||
# Дашборды/настройки Grafana (профиль `monitoring`) — переживают пересоздание.
|
||||
|
||||
@@ -54,10 +54,24 @@ rtc:
|
||||
# оба рендерятся из одного TURN_STATIC_AUTH_SECRET (deploy/render-templates.sh).
|
||||
# Логин/пароль LiveKit генерирует сам по механизму TURN REST API.
|
||||
#
|
||||
# UDP и TCP на 3478 — оба порта уже открыты в ufw. TLS (5349) намеренно не
|
||||
# объявляем: в turnserver.conf сертификаты не смонтированы, и анонс
|
||||
# неработающего `turns:` заставил бы клиента впустую ждать таймаута,
|
||||
# прежде чем перейти к рабочему кандидату.
|
||||
# UDP и TCP на 3478 — оба порта уже открыты в ufw.
|
||||
#
|
||||
# TLS (5349) объявляется ТОЛЬКО когда в .env задан TURN_TLS_HOST (см.
|
||||
# render-templates.sh) — до тех пор блок между маркерами вырезается
|
||||
# целиком, и клиент его не увидит вовсе. Это осознанно: анонс
|
||||
# неработающего `turns:` (без смонтированных в coturn сертификатов)
|
||||
# заставил бы клиента впустую ждать TLS-таймаута, прежде чем перейти
|
||||
# к рабочему кандидату — именно так это и стояло здесь до включения TLS.
|
||||
#
|
||||
# Хост для TLS-записи обязан быть ДОМЕНОМ, а не IP (в отличие от udp/tcp
|
||||
# выше): браузер проверяет TLS-сертификат TURN-сервера по имени хоста,
|
||||
# а сертификат Let's Encrypt выписан на домен, не на IP — с IP в host
|
||||
# TLS-хендшейк упадёт на проверке имени, и это будет выглядеть как ещё
|
||||
# один вариант «coturn healthy, но relay не работает».
|
||||
#
|
||||
# TLS-запись стоит ПОСЛЕДНЕЙ: клиент перебирает кандидатов по порядку,
|
||||
# а TLS через TCP дороже прямого UDP — она должна быть фолбэком, а не
|
||||
# выбираться первой.
|
||||
turn_servers:
|
||||
- host: ${TURN_EXTERNAL_IP}
|
||||
port: 3478
|
||||
@@ -69,6 +83,13 @@ rtc:
|
||||
protocol: tcp
|
||||
secret: ${TURN_STATIC_AUTH_SECRET}
|
||||
ttl: 14400
|
||||
# BEGIN-TLS-TURN
|
||||
- host: ${TURN_TLS_HOST}
|
||||
port: 5349
|
||||
protocol: tls
|
||||
secret: ${TURN_STATIC_AUTH_SECRET}
|
||||
ttl: 14400
|
||||
# END-TLS-TURN
|
||||
|
||||
# Redis обязателен для сервиса egress (см. deploy/egress/) — он использует
|
||||
# его как pub/sub и key-value хранилище состояния запущенных записей;
|
||||
|
||||
@@ -23,13 +23,27 @@ fi
|
||||
# значения вроде `SMTP_FROM=VidConf <no-reply@vidconf.example>`, где `<` —
|
||||
# валидный литерал для docker-compose/pydantic, но невалидный bash-синтаксис
|
||||
# (интерпретируется как редирект) при попытке `source` файла целиком.
|
||||
#
|
||||
# `|| true` в конце обязателен: под `set -e -o pipefail` (см. выше) сборка
|
||||
# `"$(env_var VAR)"` для ключа, которого в файле нет ВООБЩЕ (не просто
|
||||
# пустое значение, а отсутствующая строка) иначе завершает весь скрипт
|
||||
# ошибкой grep ДО того, как сработает дружелюбная проверка `:?` ниже —
|
||||
# найдено на TURN_TLS_HOST (новый необязательный ключ, есть не во всех
|
||||
# существующих .env).
|
||||
env_var() {
|
||||
grep -E "^${1}=" "$ENV_FILE" 2>/dev/null | tail -1 | cut -d= -f2-
|
||||
grep -E "^${1}=" "$ENV_FILE" 2>/dev/null | tail -1 | cut -d= -f2- || true
|
||||
}
|
||||
|
||||
TURN_STATIC_AUTH_SECRET="$(env_var TURN_STATIC_AUTH_SECRET)"
|
||||
TURN_REALM="$(env_var TURN_REALM)"
|
||||
TURN_EXTERNAL_IP="$(env_var TURN_EXTERNAL_IP)"
|
||||
# TURN_TLS_HOST — единственный переключатель TURN over TLS (5349) во всём
|
||||
# проекте: непустой = TLS смонтирован и объявляется клиентам, пустой = TLS
|
||||
# отсутствует везде (dev по умолчанию). Поэтому НЕ обязателен (без `:?`) —
|
||||
# в отличие от TURN_EXTERNAL_IP, который должен быть IP хоста, TURN_TLS_HOST
|
||||
# обязан быть ДОМЕНОМ сертификата (иначе браузер не пройдёт TLS-валидацию
|
||||
# по имени хоста для `turns:`, см. комментарий в livekit.yaml.template).
|
||||
TURN_TLS_HOST="$(env_var TURN_TLS_HOST)"
|
||||
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)"
|
||||
@@ -43,17 +57,31 @@ REDIS_PASSWORD="$(env_var REDIS_PASSWORD)"
|
||||
: "${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 REDIS_PASSWORD
|
||||
export TURN_STATIC_AUTH_SECRET TURN_REALM TURN_EXTERNAL_IP TURN_TLS_HOST LIVEKIT_API_KEY LIVEKIT_NODE_IP LIVEKIT_USE_EXTERNAL_IP REDIS_PASSWORD
|
||||
|
||||
# Вырезает блок между парой маркеров-комментариев (не только их самих), если
|
||||
# TURN_TLS_HOST пуст — так рендер отражает реальное наличие TLS-сертификатов,
|
||||
# а не просто закомментированные "на будущее" строки.
|
||||
strip_tls_block_if_disabled() {
|
||||
local file="$1" begin_marker="$2" end_marker="$3"
|
||||
if [ -z "$TURN_TLS_HOST" ]; then
|
||||
sed -i.bak "/${begin_marker}/,/${end_marker}/d" "$file" && rm -f "$file.bak"
|
||||
else
|
||||
sed -i.bak "/${begin_marker}/d; /${end_marker}/d" "$file" && rm -f "$file.bak"
|
||||
fi
|
||||
}
|
||||
|
||||
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 готов"
|
||||
strip_tls_block_if_disabled "$SCRIPT_DIR/coturn/turnserver.conf" "# BEGIN-TLS-CERT" "# END-TLS-CERT"
|
||||
echo "[render] deploy/coturn/turnserver.conf готов$([ -n "$TURN_TLS_HOST" ] && echo " (TLS включён)" || echo " (TLS выключен — TURN_TLS_HOST пуст)")"
|
||||
|
||||
# TURN_EXTERNAL_IP и TURN_STATIC_AUTH_SECRET нужны и здесь: с 0.0.14 LiveKit
|
||||
# анонсирует клиентам внешний coturn (секция `rtc.turn_servers`), и секрет
|
||||
# обязан совпадать с `static-auth-secret` в turnserver.conf выше.
|
||||
envsubst '${LIVEKIT_USE_EXTERNAL_IP} ${LIVEKIT_NODE_IP} ${LIVEKIT_API_KEY} ${REDIS_PASSWORD} ${TURN_EXTERNAL_IP} ${TURN_STATIC_AUTH_SECRET}' \
|
||||
envsubst '${LIVEKIT_USE_EXTERNAL_IP} ${LIVEKIT_NODE_IP} ${LIVEKIT_API_KEY} ${REDIS_PASSWORD} ${TURN_EXTERNAL_IP} ${TURN_STATIC_AUTH_SECRET} ${TURN_TLS_HOST}' \
|
||||
< "$SCRIPT_DIR/livekit/livekit.yaml.template" > "$SCRIPT_DIR/livekit/livekit.yaml"
|
||||
strip_tls_block_if_disabled "$SCRIPT_DIR/livekit/livekit.yaml" "# BEGIN-TLS-TURN" "# END-TLS-TURN"
|
||||
echo "[render] deploy/livekit/livekit.yaml готов"
|
||||
|
||||
envsubst '${REDIS_PASSWORD}' \
|
||||
|
||||
@@ -237,7 +237,22 @@ docker compose -f deploy/docker-compose.yml --env-file .env restart nginx
|
||||
```bash
|
||||
cat > /etc/letsencrypt/renewal-hooks/deploy/reload-nginx.sh << 'EOF'
|
||||
#!/bin/bash
|
||||
# certbot renew копирует новый сертификат в /etc/letsencrypt/live/<домен>/,
|
||||
# но и nginx, и coturn держат СВОИ копии (docker-entrypoint-certs.sh и
|
||||
# coturn-certs-init в deploy/docker-compose.yml) — обе копируются только
|
||||
# при СТАРТЕ/пересоздании соответствующего контейнера. Reload недостаточен
|
||||
# ни для того, ни для другого — нужен restart.
|
||||
docker restart vidconf-nginx-1
|
||||
|
||||
# TURN over TLS (см. docs/deploy/DEPLOYMENT.md §8) — только если включён
|
||||
# (TURN_TLS_HOST задан в .env). Без coturn-certs-init coturn продолжит
|
||||
# держать в памяти старый сертификат ещё ~60 дней, до следующего продления,
|
||||
# и TLS-хендшейк начнёт падать с ошибкой валидации сертификата у клиентов.
|
||||
cd /opt/vidconf || exit 1
|
||||
if grep -qE '^TURN_TLS_HOST=.+' .env; then
|
||||
docker compose -f deploy/docker-compose.yml --env-file .env up -d coturn-certs-init
|
||||
docker restart vidconf-coturn-1
|
||||
fi
|
||||
EOF
|
||||
chmod +x /etc/letsencrypt/renewal-hooks/deploy/reload-nginx.sh
|
||||
|
||||
@@ -277,7 +292,8 @@ docker compose -f deploy/docker-compose.yml --env-file .env --profile monitoring
|
||||
docker compose -f deploy/docker-compose.yml --env-file .env ps
|
||||
|
||||
# 2. Наружу открыто только ожидаемое (80/443/7881 + udp 54000,
|
||||
# плюс 3478 tcp+udp, если включили TURN — раздел 8)
|
||||
# плюс 3478 tcp+udp и 49160:49200/udp, если включили TURN, плюс 5349/tcp,
|
||||
# если включили TURN over TLS — раздел 8)
|
||||
ss -ltnp
|
||||
|
||||
# 3. Redis требует пароль (НЕ должен пускать без него)
|
||||
@@ -359,14 +375,89 @@ state: connected`, но собеседник не видит видео/не с
|
||||
Ноль при живом звонке из-за NAT означает, что до coturn не дошли —
|
||||
смотрите ufw и `TURN_EXTERNAL_IP`.
|
||||
|
||||
**TURN over TLS (порт 5349 или 443) — не настроен.** Это самый надёжный
|
||||
фолбэк (проходит там, где режут UDP и нестандартные порты), но требует
|
||||
смонтировать в coturn TLS-сертификат: раскомментировать `cert`/`pkey` в
|
||||
`deploy/coturn/turnserver.conf.template`, добавить volume с
|
||||
`/etc/letsencrypt` (nginx его уже монтирует, coturn — нет), открыть порт и
|
||||
не забыть про перезапуск coturn при обновлении сертификата. Пока этого нет,
|
||||
`turns:` намеренно не анонсируется: анонс неработающего адреса заставил бы
|
||||
клиента ждать таймаута перед переходом к рабочему кандидату.
|
||||
### TURN over TLS (порт 5349)
|
||||
|
||||
Самый надёжный фолбэк: в жёстких корпоративных сетях наружу часто разрешён
|
||||
только `443/tcp`, и TLS-соединение на нестандартный порт (5349) выглядит для
|
||||
firewall как обычный HTTPS. UDP/TCP на 3478 такие сети режут целиком.
|
||||
|
||||
**443 вместо 5349 невозможен без доп. усложнений**: `443/tcp` на хосте уже
|
||||
занят nginx (Docker port-publish биндит хостовый сокет), а coturn слушает в
|
||||
`network_mode: host` — оба не могут забрать один и тот же порт без
|
||||
SNI-мультиплексора перед ними. Такой мультиплексор — отдельная, более
|
||||
сложная система; в этом проекте её нет, и заводить её только ради 443 не
|
||||
оправдано, пока 5349 проходит через те же firewall, что и 443.
|
||||
|
||||
**Включение — один флаг, `TURN_TLS_HOST` в `.env`:**
|
||||
|
||||
```bash
|
||||
# Домен сертификата (НЕ IP — см. предупреждение ниже), тот же, что в
|
||||
# NGINX_CERT_NAME:
|
||||
TURN_TLS_HOST=vidconf.ru
|
||||
```
|
||||
|
||||
Пусто (dev-дефолт) — TLS выключен полностью и без следов: `render-templates.sh`
|
||||
вырезает cert/pkey из `turnserver.conf` и запись `protocol: tls` из
|
||||
`rtc.turn_servers` в `livekit.yaml` (маркеры `BEGIN-TLS-*`/`END-TLS-*` в
|
||||
`.template`-файлах). Непустое значение включает оба сразу — TLS без анонса
|
||||
клиентам (и наоборот) не бывает, ровно один переключатель на всё.
|
||||
|
||||
⚠️ **`TURN_TLS_HOST` обязан быть ДОМЕНОМ, не IP** — в отличие от
|
||||
`TURN_EXTERNAL_IP`, который остаётся IP-адресом для udp/tcp-записей. Браузер
|
||||
проверяет TLS-сертификат TURN-сервера по имени хоста в `turns:`-URL, а
|
||||
Let's Encrypt выписывает сертификат на домен. С IP в этом поле TLS-хендшейк
|
||||
упадёт на проверке имени сертификата — внешне это будет выглядеть как ещё
|
||||
один вариант «coturn healthy, а relay не работает», только на новом порту.
|
||||
|
||||
**Права на приватный ключ.** coturn (`nobody:nogroup` внутри контейнера, без
|
||||
root-фазы в entrypoint — не то что у nginx, где `docker-entrypoint-certs.sh`
|
||||
выполняется от root) физически не может прочитать
|
||||
`/etc/letsencrypt/live/<домен>/privkey.pem` (root, обычно `0600`). Решение —
|
||||
одноразовый init-контейнер `coturn-certs-init` (busybox, дефолтный root,
|
||||
образец — уже существующий `recordings-init`/`llm-models-init` в этом же
|
||||
compose-файле): копирует `fullchain.pem`/`privkey.pem` в свой volume
|
||||
`coturn-certs` под правами `644`. Это копия, не оригинал — права на ключ на
|
||||
хосте не меняются. coturn просто монтирует `coturn-certs:/etc/coturn/certs:ro`
|
||||
и ждёт (`depends_on: condition: service_completed_successfully`), пока init
|
||||
отработает.
|
||||
|
||||
**Обновление сертификата.** certbot продлевает Let's Encrypt раз в ~60 дней
|
||||
и обновляет файлы в `/etc/letsencrypt`, но и nginx, и coturn держат свои
|
||||
копии, которые перечитываются только при (пере)старте контейнера — reload
|
||||
недостаточен ни для одного из них. Deploy-hook (см. шаг 5 выше,
|
||||
`/etc/letsencrypt/renewal-hooks/deploy/reload-nginx.sh`) после `nginx`
|
||||
дополнительно пересоздаёт `coturn-certs-init` (перекопировать свежий
|
||||
сертификат в volume) и перезапускает `coturn`. Без этого шага TLS-TURN
|
||||
тихо остановится обслуживать новые TLS-хендшейки примерно через два
|
||||
месяца — coturn будет держать в памяти сертификат, у которого истёк срок
|
||||
действия, и клиенты начнут получать ошибку валидации сертификата при
|
||||
попытке TLS-хендшейка.
|
||||
|
||||
⚠️ Перезапуск `coturn` (и hook, и ручной после включения TLS) может задеть
|
||||
активные звонки, идущие через relay, — как и с `livekit` (см. выше),
|
||||
выбирайте окно или закладывайтесь на автопродление certbot (раз в ~60 дней,
|
||||
непредсказуемое время суток).
|
||||
|
||||
Порядок включения:
|
||||
|
||||
1. `TURN_TLS_HOST=<домен>` в `.env`.
|
||||
2. `./deploy/render-templates.sh` (перерендерит `turnserver.conf` и
|
||||
`livekit.yaml` с TLS-блоками).
|
||||
3. Открыть `5349/tcp` в ufw (IPv4 и IPv6).
|
||||
4. `docker compose … up -d --force-recreate coturn-certs-init coturn` —
|
||||
пересоздать (не просто restart: новый volume/depends_on).
|
||||
5. Проверить, что TLS реально отвечает:
|
||||
`openssl s_client -connect <домен>:5349 -servername <домен>` — должен
|
||||
показать сертификат Let's Encrypt (`issuer=Let's Encrypt`), а не ошибку
|
||||
соединения.
|
||||
6. `docker compose … up -d --force-recreate livekit` — подхватить новую
|
||||
запись `rtc.turn_servers`. ⚠️ Разрывает активные конференции.
|
||||
7. Обновить deploy-hook certbot (см. шаг 5) и проверить
|
||||
`certbot renew --dry-run`.
|
||||
8. Провести звонок, принудительно загнав клиента в relay-режим (ICE
|
||||
transport policy `relay` в браузере), и убедиться, что аллокации в
|
||||
`docker logs vidconf-coturn-1` растут именно через TLS-соединение, а
|
||||
обычный TURN на 3478 продолжает работать для остальных клиентов.
|
||||
|
||||
---
|
||||
|
||||
|
||||
@@ -412,6 +412,10 @@ ensure_default NGINX_CERT_NAME "localhost"
|
||||
ensure_default LIVEKIT_USE_EXTERNAL_IP "false"
|
||||
ensure_default LIVEKIT_NODE_IP "127.0.0.1"
|
||||
ensure_default TURN_EXTERNAL_IP "127.0.0.1"
|
||||
# Пусто = TURN over TLS выключен (см. docs/deploy/DEPLOYMENT.md §8) — не
|
||||
# генерируем и не требуем здесь, только гарантируем, что ключ явно есть в
|
||||
# .env (для discoverability), а не отсутствует молча.
|
||||
ensure_default TURN_TLS_HOST ""
|
||||
|
||||
# Профили compose и модели по пресету. GPU-профили — ТОЛЬКО для пресета 5
|
||||
# (max): в текущей матрице уровней (backend/services/ai_tiers.py, ADR-004)
|
||||
|
||||
Reference in New Issue
Block a user