Move to Volcanic VPS: domain crm-calls.volcanic.tes.gd, advertise 164.90.233.156, README rewrite

This commit is contained in:
crm-calls agent
2026-10-14 18:17:04 +00:00
parent 22444286bd
commit 117f0736be
2 changed files with 48 additions and 76 deletions

106
README.md
View File

@@ -1,8 +1,9 @@
# crm-calls
Jitsi Meet для Tvorymo CRM: https://crm-calls.tes.gd
Jitsi Meet для Tvorymo CRM: https://crm-calls.volcanic.tes.gd
Розгортання: Coolify (сервер `storage`, 192.168.50.73), застосунок `crm-calls`, build pack `dockercompose`,
Розгортання: Coolify Вулканика (`http://coolify.volcanic.tes.gd:8000`), VPS `164.90.233.156`
(DigitalOcean, `*.volcanic.tes.gd`), проєкт і застосунок `crm-calls`, build pack `dockercompose`,
файл `docker-compose.yml`. Основа: офіційний [docker-jitsi-meet](https://github.com/jitsi/docker-jitsi-meet), образи `stable-11248`.
## Сервіси
@@ -10,7 +11,7 @@ Jitsi Meet для Tvorymo CRM: https://crm-calls.tes.gd
| Сервіс | Що робить | Як доступний ззовні |
|---------|-----------------------------------|---------------------|
| web | nginx + веб-застосунок Jitsi | HTTPS 443 через Traefik (Coolify) → `web:8000` |
| prosody | XMPP-сервер | лише `wss://crm-calls.tes.gd/xmpp-websocket` (проксі через web) |
| prosody | XMPP-сервер | лише `wss://crm-calls.volcanic.tes.gd/xmpp-websocket` (проксі через web) |
| jicofo | фокус конференцій | ззовні не доступний |
| jvb | відеоміст (медіа) | **UDP 10000 напряму**, повз Traefik |
@@ -24,92 +25,63 @@ Colibri-websocket у цій версії Jitsi не використовуєть
- `JICOFO_AUTH_PASSWORD` — пароль jicofo у prosody
- `JVB_AUTH_PASSWORD` — пароль jvb у prosody
Мережеві (задані в Coolify env, змінюються без правок у репозиторії):
Мережеві (змінюються в env Coolify без правок у репозиторії, після зміни — Redeploy):
- `JVB_PORT` — UDP-порт JVB, публікується на хості storage (зараз `10000`)
- `JVB_ADVERTISE_IPS` — адреси, які JVB повідомляє клієнтам, через кому (зараз `192.168.50.73`,
тобто лише LAN — публічний UDP-шлях ще не вибрано)
- `JVB_PORT` — UDP-порт JVB, публікується на хості (`10000`)
- `JVB_ADVERTISE_IPS` — публічні адреси, які JVB повідомляє клієнтам, через кому (`164.90.233.156`)
Інші: `PUBLIC_URL` (`https://crm-calls.tes.gd`), `TZ` (`Europe/Kyiv`), `DEFAULT_LANGUAGE` (`uk`),
Інші: `PUBLIC_URL` (`https://crm-calls.volcanic.tes.gd`), `TZ` (`Europe/Kyiv`), `DEFAULT_LANGUAGE` (`uk`),
`JITSI_IMAGE_VERSION` (`stable-11248`).
Після зміни env — Redeploy застосунку в Coolify.
Домен у Coolify (`docker_compose_domains`): `web` → `https://crm-calls.volcanic.tes.gd:8000`.
`:8000` тут — порт контейнера `web`, куди Traefik веде трафік; публічна адреса — звичайний HTTPS 443.
## Мережа
- `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`, вихід в інтернет через Netia `195.128.172.198`, вхідні порти закриті.
VPS має публічну адресу `164.90.233.156` безпосередньо на інтерфейсі (без NAT), тому
`JVB_ADVERTISE_IPS=164.90.233.156`.
Веб і XMPP (TCP 443) уже працюють. Для медіа JVB потрібен **вхідний UDP** до `192.168.50.73:$JVB_PORT`.
Без нього сторінка й кімната відкриваються, але звуку/відео в конференції через JVB не буде
(дзвінок удвох іноді працює P2P напряму між браузерами).
| Порт | Протокол | Куди | Навіщо |
|--------------|----------|--------------------------|--------|
| 443 (і 80) | TCP | Traefik → `web:8000` | веб, `/xmpp-websocket` |
| 10000 | **UDP** | `jvb` (опублікований на хості) | медіа і SCTP-канал до JVB |
Варіант треба вибрати один (або A/B + C як запасний для корпоративних мереж).
UDP 10000 має бути дозволений і в ufw/iptables на VPS, і в DigitalOcean Cloud Firewall (якщо він
прив'язаний до droplet). Порт, опублікований Docker, зазвичай проходить повз ufw, але не повз Cloud Firewall.
### Варіант A — проброс на роутері Netia (найкраща якість)
Без UDP 10000 сторінка й кімната відкриваються, але аудіо/відео через JVB не буде.
1. Переконатися, що `195.128.172.198` — статична «біла» адреса саме на WAN роутера
(не CGNAT): WAN IP у панелі роутера має збігатися з `195.128.172.198`. Якщо ні — замовити в Netia статичну публічну IP.
2. На роутері: port forwarding **UDP 10000** з WAN `195.128.172.198:10000` → `192.168.50.73:10000`.
3. Якщо на storage є ufw/iptables — дозволити вхідний UDP 10000.
4. У Coolify env: `JVB_ADVERTISE_IPS=195.128.172.198,192.168.50.73` → Redeploy.
Перевірка: під час дзвінка в `chrome://webrtc-internals` вибрана ICE-пара має вести на
`164.90.233.156:10000` (udp). Або на VPS: `sudo tcpdump -ni any udp port 10000` і ззовні
`echo test | nc -u -w1 164.90.233.156 10000`.
### Варіант B — UDP через VPS 46.101.119.200 і WireGuard
## TURN (на майбутнє, `turn.volcanic.tes.gd`)
Storage сам підключається до VPS (вихідне з'єднання, вхідні порти на Netia не потрібні).
Потрібен клієнтам, у яких заблокований UDP (корпоративні мережі, частина мобільних операторів).
DNS `turn.volcanic.tes.gd` уже веде на `164.90.233.156` (wildcard).
1. DigitalOcean Cloud Firewall + ufw на VPS: відкрити вхідні **UDP 10000** і **UDP 51820** (WireGuard).
2. WireGuard: VPS `10.88.0.1/24` (ListenPort 51820), storage `10.88.0.2/24`, у storage
`Endpoint = 46.101.119.200:51820`, `PersistentKeepalive = 25`, `AllowedIPs = 10.88.0.1/32`.
3. На VPS:
```sh
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
```
(MASQUERADE потрібен, щоб відповіді JVB поверталися тим самим тунелем, а не через Netia.)
4. Storage: дозволити вхідний UDP 10000 з `wg0`.
5. У Coolify env: `JVB_ADVERTISE_IPS=46.101.119.200,192.168.50.73` → Redeploy.
**Варіант 1 — класичний coturn (найкраща якість).** Відкрити у файрволі VPS / DO:
`3478` UDP+TCP, `5349` TCP (TURNS), relay-діапазон напр. `49160-49200` UDP.
coturn: `use-auth-secret`, `static-auth-secret=<секрет у Coolify env>`, `realm=crm-calls.volcanic.tes.gd`,
`external-ip=164.90.233.156`, `min-port`/`max-port` = relay-діапазон, TLS-сертифікат для
`turn.volcanic.tes.gd` (5349), `denied-peer-ip` для приватних мереж.
Мінуси: весь медіатрафік іде через VPS (додаткова затримка і трафік DigitalOcean).
**Варіант 2 — TURNS на 443 через Traefik (нових портів не треба).** coturn слухає TCP 3478 лише у
внутрішній мережі; TCP-роутер Traefik `HostSNI(`turn.volcanic.tes.gd`)` на entrypoint `https` з TLS
(letsencrypt) → `coturn:3478`. coturn ретранслює медіа на JVB усередині хоста. Працює там, де дозволений
лише TCP 443; якість нижча (медіа по TCP).
### Варіант C — TURN на 443 (TURNS через Traefik, нових портів не треба)
Працює навіть там, де дозволено лише TCP 443 (корпоративні мережі). Якість гірша (медіа по TCP/TLS).
1. DNS: `turn.tes.gd` уже веде на `46.101.119.200` (wildcard) — нічого не робити.
2. Додати сервіс `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` для інших приватних діапазонів.
3. Traefik (Coolify): TCP-роутер `HostSNI(`turn.tes.gd`)` на entrypoint `https` з TLS (certresolver
letsencrypt) → `coturn:3478`. Тобто Traefik знімає TLS і передає TURN-потік у coturn.
4. У Coolify env (секрет лише там): `TURN_CREDENTIALS=<випадковий рядок>`, `TURNS_HOST=turn.tes.gd`,
`TURNS_PORT=443`, `TURN_TRANSPORT=tcp` → Redeploy. Prosody (`mod_external_services`) роздає клієнтам
тимчасові TURN-облікові дані; coturn ретранслює медіа на JVB `192.168.50.73:10000` всередині LAN.
5. На роутері/VPS нічого відкривати не потрібно.
Пізніше, за наявності вхідного UDP (A або B), можна додати класичний TURN: 3478 UDP/TCP, 5349 TLS
і діапазон relay-портів (напр. 49160–49200 UDP) з `external-ip` = вибрана публічна адреса.
### Перевірка UDP після A або B
```sh
sudo tcpdump -ni any udp port 10000 # на storage
echo test | nc -u -w1 <публічна-IP> 10000 # ззовні
```
Під час дзвінка в `chrome://webrtc-internals` вибрана ICE-пара має вести на `<публічна-IP>:10000`.
В обох варіантах Jitsi (env prosody, через Coolify): `TURN_CREDENTIALS=<той самий секрет>`,
`TURNS_HOST=turn.volcanic.tes.gd`, `TURNS_PORT=5349` або `443`, для варіанту 1 також
`TURN_HOST=turn.volcanic.tes.gd`, `TURN_PORT=3478`, `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 має
- Вбудовувати через `https://crm-calls.volcanic.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).