crm-calls
Jitsi Meet для Tvorymo CRM: https://crm-calls.tes.gd
Розгортання: Coolify (сервер storage, 192.168.50.73), застосунок crm-calls, build pack dockercompose,
файл docker-compose.yml. Основа: офіційний docker-jitsi-meet, образи stable-11248.
Сервіси
| Сервіс | Що робить | Як доступний ззовні |
|---|---|---|
| web | nginx + веб-застосунок Jitsi | HTTPS 443 через Traefik (Coolify) → web:8000 |
| prosody | XMPP-сервер | лише wss://crm-calls.tes.gd/xmpp-websocket (проксі через web) |
| jicofo | фокус конференцій | ззовні не доступний |
| jvb | відеоміст (медіа) | UDP 10000 напряму, повз Traefik |
Colibri-websocket у цій версії Jitsi не використовується: канал «клієнт ↔ JVB» іде через SCTP усередині того самого медіа-з'єднання (UDP 10000). Тому через 443 ходять лише веб і XMPP.
Змінні оточення (Coolify)
Секрети задаються лише в env застосунку в Coolify, у репозиторії їх немає:
JICOFO_AUTH_PASSWORD— пароль jicofo у prosodyJVB_AUTH_PASSWORD— пароль jvb у prosody
Мережеві (задані в Coolify env, змінюються без правок у репозиторії):
JVB_PORT— UDP-порт JVB, публікується на хості storage (зараз10000)JVB_ADVERTISE_IPS— адреси, які JVB повідомляє клієнтам, через кому (зараз192.168.50.73, тобто лише LAN — публічний UDP-шлях ще не вибрано)
Інші: PUBLIC_URL (https://crm-calls.tes.gd), TZ (Europe/Kyiv), DEFAULT_LANGUAGE (uk),
JITSI_IMAGE_VERSION (stable-11248).
Після зміни env — Redeploy застосунку в Coolify.
Мережа
46.101.119.200— VPS у DigitalOcean (туди веде*.tes.gd), відкриті лише TCP 22/80/443, TCP 443 прозоро передається на Traefik на storage (сертифікати Let's Encrypt видає Traefik на storage).storage—192.168.50.73, вихід в інтернет через Netia195.128.172.198, вхідні порти закриті.
Веб і XMPP (TCP 443) уже працюють. Для медіа JVB потрібен вхідний UDP до 192.168.50.73:$JVB_PORT.
Без нього сторінка й кімната відкриваються, але звуку/відео в конференції через JVB не буде
(дзвінок удвох іноді працює P2P напряму між браузерами).
Варіант треба вибрати один (або A/B + C як запасний для корпоративних мереж).
Варіант A — проброс на роутері Netia (найкраща якість)
- Переконатися, що
195.128.172.198— статична «біла» адреса саме на WAN роутера (не CGNAT): WAN IP у панелі роутера має збігатися з195.128.172.198. Якщо ні — замовити в Netia статичну публічну IP. - На роутері: port forwarding UDP 10000 з WAN
195.128.172.198:10000→192.168.50.73:10000. - Якщо на storage є ufw/iptables — дозволити вхідний UDP 10000.
- У Coolify env:
JVB_ADVERTISE_IPS=195.128.172.198,192.168.50.73→ Redeploy.
Варіант B — UDP через VPS 46.101.119.200 і WireGuard
Storage сам підключається до VPS (вихідне з'єднання, вхідні порти на Netia не потрібні).
- DigitalOcean Cloud Firewall + ufw на VPS: відкрити вхідні UDP 10000 і UDP 51820 (WireGuard).
- WireGuard: VPS
10.88.0.1/24(ListenPort 51820), storage10.88.0.2/24, у storageEndpoint = 46.101.119.200:51820,PersistentKeepalive = 25,AllowedIPs = 10.88.0.1/32. - На VPS:
(MASQUERADE потрібен, щоб відповіді JVB поверталися тим самим тунелем, а не через Netia.)
sysctl -w net.ipv4.ip_forward=1 # і в /etc/sysctl.conf iptables -t nat -A PREROUTING -i eth0 -p udp --dport 10000 -j DNAT --to-destination 10.88.0.2:10000 iptables -t nat -A POSTROUTING -o wg0 -p udp -d 10.88.0.2 --dport 10000 -j MASQUERADE iptables -A FORWARD -i eth0 -o wg0 -p udp -d 10.88.0.2 --dport 10000 -j ACCEPT iptables -A FORWARD -i wg0 -o eth0 -m state --state ESTABLISHED,RELATED -j ACCEPT - Storage: дозволити вхідний UDP 10000 з
wg0. - У Coolify env:
JVB_ADVERTISE_IPS=46.101.119.200,192.168.50.73→ Redeploy.
Мінуси: весь медіатрафік іде через VPS (додаткова затримка і трафік DigitalOcean).
Варіант C — TURN на 443 (TURNS через Traefik, нових портів не треба)
Працює навіть там, де дозволено лише TCP 443 (корпоративні мережі). Якість гірша (медіа по TCP/TLS).
- DNS:
turn.tes.gdуже веде на46.101.119.200(wildcard) — нічого не робити. - Додати сервіс
coturn(образcoturn/coturn) у compose, слухає TCP 3478 лише у внутрішній мережі. Конфіг:use-auth-secret,static-auth-secret=$TURN_CREDENTIALS,realm=crm-calls.tes.gd,no-tls,no-udpна слухачі (клієнт ↔ coturn — TCP),allowed-peer-ip=192.168.50.73,denied-peer-ipдля інших приватних діапазонів. - Traefik (Coolify): TCP-роутер
HostSNI(turn.tes.gd)на entrypointhttpsз TLS (certresolver letsencrypt) →coturn:3478. Тобто Traefik знімає TLS і передає TURN-потік у coturn. - У Coolify env (секрет лише там):
TURN_CREDENTIALS=<випадковий рядок>,TURNS_HOST=turn.tes.gd,TURNS_PORT=443,TURN_TRANSPORT=tcp→ Redeploy. Prosody (mod_external_services) роздає клієнтам тимчасові TURN-облікові дані; coturn ретранслює медіа на JVB192.168.50.73:10000всередині LAN. - На роутері/VPS нічого відкривати не потрібно.
Пізніше, за наявності вхідного UDP (A або B), можна додати класичний TURN: 3478 UDP/TCP, 5349 TLS
і діапазон relay-портів (напр. 49160–49200 UDP) з external-ip = вибрана публічна адреса.
Перевірка UDP після A або B
sudo tcpdump -ni any udp port 10000 # на storage
echo test | nc -u -w1 <публічна-IP> 10000 # ззовні
Під час дзвінка в chrome://webrtc-internals вибрана ICE-пара має вести на <публічна-IP>:10000.
Вбудовування (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).