Jitsi Meet docker-compose for Coolify (stable-11248)

This commit is contained in:
crm-calls agent
2026-10-14 16:12:54 +00:00
parent 7869ccc97d
commit dda1ec27cc
2 changed files with 66 additions and 58 deletions

122
README.md
View File

@@ -1,78 +1,86 @@
# crm-calls
Сервис звонков для CRM (Tvorymo Soft): Jitsi Meet на базе
[docker-jitsi-meet](https://github.com/jitsi/docker-jitsi-meet) `stable-11248`
(образы `ghcr.io/jitsi/{web,prosody,jicofo,jvb}`), разворачивается в Coolify
(сервер storage) как docker-compose приложение.
Jitsi Meet для Tvorymo CRM: https://crm-calls.tes.gd
- URL: https://crm-calls.tes.gd
- Coolify: проект `crm-calls`, приложение `crm-calls` (uuid в `deploy.json`, он в .gitignore)
Розгортання: Coolify (сервер `storage`, 192.168.50.73), застосунок `crm-calls`, build pack `dockercompose`,
файл `docker-compose.yml`. Основа: офіційний [docker-jitsi-meet](https://github.com/jitsi/docker-jitsi-meet), образи `stable-11248`.
## Сеть
## Сервіси
| Что | Порт | Путь |
|---|---|---|
| Веб, BOSH, XMPP-websocket | TCP 443 | VPS 46.101.119.200 → storage Traefik → `web:8000` |
| Медиа JVB (звук/видео + bridge channel по SCTP) | UDP 10000 | напрямую на хост storage (192.168.50.73), мимо Traefik |
| Сервіс | Що робить | Як доступний ззовні |
|---------|-----------------------------------|---------------------|
| web | nginx + веб-застосунок Jitsi | HTTPS 443 через Traefik (Coolify) → `web:8000` |
| prosody | XMPP-сервер | лише `wss://crm-calls.tes.gd/xmpp-websocket` (проксі через web) |
| jicofo | фокус конференцій | ззовні не доступний |
| jvb | відеоміст (медіа) | **UDP 10000 напряму**, повз Traefik |
В текущих версиях Jitsi bridge channel идёт через SCTP data channel внутри
медиа-соединения, отдельный colibri-websocket не нужен — достаточно 443 + UDP 10000.
Colibri-websocket у цій версії Jitsi не використовується: канал «клієнт ↔ JVB» іде через SCTP
усередині того самого медіа-з'єднання (UDP 10000). Тому через 443 ходять лише веб і XMPP.
### Media / UDP — внешняя зависимость
## Змінні оточення (Coolify)
storage стоит за NAT Netia; снаружи сейчас доходят только TCP 80/443 через VPS.
Пока UDP 10000 не проброшен, веб открывается и в комнату можно зайти, но звук/видео
между участниками из интернета не пойдут (в офисной LAN — пойдут, через 192.168.50.73).
Секрети задаються **лише** в env застосунку в Coolify, у репозиторії їх немає:
Варианты (делает админ сети):
- `JICOFO_AUTH_PASSWORD` — пароль jicofo у prosody
- `JVB_AUTH_PASSWORD` — пароль jvb у prosody
1. **Проброс на роутере Netia** `195.128.172.198:10000/udp → 192.168.50.73:10000/udp`.
Затем `JVB_ADVERTISE_IPS=192.168.50.73,195.128.172.198`.
2. **UDP-форвард через VPS** `46.101.119.200:10000/udp → storage:10000/udp`
(по тому же туннелю, что и 80/443). Ответы должны уходить обратно через VPS
(SNAT/MASQUERADE или туннель), иначе клиенты получат пакеты с IP Netia и ICE не сойдётся.
`JVB_ADVERTISE_IPS=192.168.50.73,46.101.119.200` (значение по умолчанию).
Необов'язкові (є значення за замовчуванням у compose):
Менять: Coolify → приложение → Environment Variables → `JVB_ADVERTISE_IPS` / `JVB_PORT` → Redeploy.
При смене `JVB_PORT` проброс должен вести на тот же порт.
- `PUBLIC_URL` (`https://crm-calls.tes.gd`)
- `JVB_ADVERTISE_IPS` (`46.101.119.200,192.168.50.73`)
- `TZ` (`Europe/Kyiv`), `DEFAULT_LANGUAGE` (`uk`), `JITSI_IMAGE_VERSION` (`stable-11248`)
Проверка с внешней машины после проброса: создать комнату с двух устройств из разных
сетей; в `jvb` логах должно появиться `ICE connected`/`Selected pair`.
## Мережа: що треба пробросити
### TURN (опционально)
Публічна адреса `46.101.119.200`, сервер `storage` — `192.168.50.73` у внутрішній мережі.
В prosody уже прокинуты `TURN_HOST`, `TURN_PORT`, `TURN_TRANSPORT`, `TURNS_HOST`,
`TURNS_PORT`, `TURN_CREDENTIALS` — достаточно поднять coturn (3478 udp/tcp, 5349 tls)
и задать переменные. coturn требует тех же входящих портов, что сейчас закрыты,
поэтому без админа сети он тоже не поможет — кроме варианта TURNS на 443 через
SNI-маршрутизацию Traefik (отдельный хостнейм, TCP-роутер с TLS passthrough).
| Звідки (зовні) | Куди | Протокол | Навіщо |
|------------------------------|----------------------------|----------|--------|
| `46.101.119.200:10000` | `192.168.50.73:10000` | **UDP** | медіа JVB (обов'язково) |
| `46.101.119.200:80, 443` | `192.168.50.73:80, 443` | TCP | вже працює (Traefik) |
## Встраивание в crm-wiki / CRM (iframe)
Також на самому `storage` має бути відкритий вхідний UDP 10000 (якщо там є ufw/iptables;
порт, опублікований Docker, зазвичай проходить повз ufw).
- `CSP_HEADER` по умолчанию: `frame-ancestors 'self' https://wiki-crm.tes.gd https://*.tes.gd`
(nginx в контейнере web). X-Frame-Options не выставляется.
- На стороне родителя: `<iframe allow="camera; microphone; display-capture; autoplay; clipboard-write; fullscreen">`
или `external_api.js` (`https://crm-calls.tes.gd/external_api.js`, `new JitsiMeetExternalAPI(...)`).
Перевірка після прокидання (з машини поза мережею):
### JWT-авторизация (когда понадобится)
Переменные уже прокинуты в web/prosody/jicofo:
```
ENABLE_AUTH=1
AUTH_TYPE=jwt
JWT_APP_ID=crm-calls
JWT_APP_SECRET=<общий секрет HS256 с бэкендом CRM/wiki>
JWT_ACCEPTED_ISSUERS=crm,crm-wiki
JWT_ACCEPTED_AUDIENCES=crm-calls
ENABLE_GUESTS=0 # 1 — пускать гостей без токена в уже созданные комнаты
```sh
# на storage:
sudo tcpdump -ni any udp port 10000
# ззовні:
echo test | nc -u -w1 46.101.119.200 10000
```
Бэкенд CRM подписывает токен (`iss`, `aud`, `sub=crm-calls.tes.gd` или `*`, `room`, `context.user`)
и передаёт его в iframe (`?jwt=...` или `jwt` в `JitsiMeetExternalAPI`); токен можно
получить родителем и передать через postMessage → JS в странице хоста создаёт API с ним.
У tcpdump має з'явитися пакет. Під час дзвінка в `chrome://webrtc-internals` вибрана
ICE-пара має бути з віддаленою адресою `46.101.119.200:10000`.
## Секреты
Без UDP 10000 сторінка й кімната відкриваються, але дзвінок з 3+ учасниками (через JVB) не матиме
звуку/відео. Дзвінок удвох може працювати в режимі P2P (напряму між браузерами через STUN),
але не за суворим NAT/корпоративним файрволом — для цього потрібен TURN.
`JICOFO_AUTH_PASSWORD`, `JVB_AUTH_PASSWORD` задаются только в Coolify env vars (внутренние
XMPP-пароли компонентов). Деплой-конфиг — `deploy.json` (не в git).
## TURN (на майбутнє)
Потрібен для клієнтів, у яких UDP заблоковано (корпоративні мережі, частина мобільних операторів).
1. Додати сервіс `coturn` (наприклад `coturn/coturn`) у compose або окремим застосунком у Coolify,
`network_mode: host` або опублікувати порти.
2. Пробросити на роутері до `192.168.50.73`:
- `3478` UDP і TCP (TURN/STUN),
- `5349` TCP (TURNS/TLS) — або краще TURNS на 443 окремої IP/піддомену, бо 443 вже зайнятий Traefik,
- діапазон relay-портів, напр. `49160-49200` UDP (задається в `min-port`/`max-port` coturn).
3. coturn: `external-ip=46.101.119.200/192.168.50.73`, `realm=crm-calls.tes.gd`,
`use-auth-secret`, `static-auth-secret=<секрет у Coolify env>`, TLS-сертифікат для домену
TURN (напр. `turn.tes.gd`) для 5349.
4. Jitsi: задати в env prosody `TURN_HOST=turn.tes.gd`, `TURN_PORT=3478`, `TURNS_HOST=turn.tes.gd`,
`TURNS_PORT=5349`, `TURN_CREDENTIALS=<той самий секрет>`, `TURN_TRANSPORT=udp,tcp`
(prosody через `mod_external_services` роздає клієнтам тимчасові TURN-облікові дані).
## Вбудовування (iframe) у вікі / CRM
- Jitsi nginx не ставить `X-Frame-Options`; framing задається через
`Content-Security-Policy: frame-ancestors 'self' https://*.tes.gd https://test.units.tvorymo.com`
(змінна `CSP_HEADER` у compose, сервіс `web`). Щоб додати домен — дописати його туди.
- Вбудовувати через `https://crm-calls.tes.gd/external_api.js` (`JitsiMeetExternalAPI`), iframe має
мати `allow="camera; microphone; display-capture; autoplay; clipboard-write; fullscreen"`.
- Авторизація (postMessage → JWT) поки не налаштована: кімнати відкриті для всіх, хто знає URL.
Для CRM планується `ENABLE_AUTH=1`, `AUTH_TYPE=jwt`, `JWT_APP_ID`, `JWT_APP_SECRET` (секрет — лише в Coolify).