diff --git a/README.md b/README.md index 2c56d84..d6d3a4e 100644 --- a/README.md +++ b/README.md @@ -24,56 +24,85 @@ Colibri-websocket у цій версії Jitsi не використовуєть - `JICOFO_AUTH_PASSWORD` — пароль jicofo у prosody - `JVB_AUTH_PASSWORD` — пароль jvb у prosody -Необов'язкові (є значення за замовчуванням у compose): +Мережеві (задані в Coolify env, змінюються без правок у репозиторії): -- `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_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`). -Публічна адреса `46.101.119.200`, сервер `storage` — `192.168.50.73` у внутрішній мережі. +Після зміни env — Redeploy застосунку в Coolify. -| Звідки (зовні) | Куди | Протокол | Навіщо | -|------------------------------|----------------------------|----------|--------| -| `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) | +## Мережа -Також на самому `storage` має бути відкритий вхідний UDP 10000 (якщо там є ufw/iptables; -порт, опублікований Docker, зазвичай проходить повз ufw). +- `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`, вхідні порти закриті. -Перевірка після прокидання (з машини поза мережею): +Веб і XMPP (TCP 443) уже працюють. Для медіа JVB потрібен **вхідний UDP** до `192.168.50.73:$JVB_PORT`. +Без нього сторінка й кімната відкриваються, але звуку/відео в конференції через JVB не буде +(дзвінок удвох іноді працює P2P напряму між браузерами). + +Варіант треба вибрати один (або A/B + C як запасний для корпоративних мереж). + +### Варіант A — проброс на роутері Netia (найкраща якість) + +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. + +### Варіант B — UDP через VPS 46.101.119.200 і WireGuard + +Storage сам підключається до VPS (вихідне з'єднання, вхідні порти на Netia не потрібні). + +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. + +Мінуси: весь медіатрафік іде через VPS (додаткова затримка і трафік DigitalOcean). + +### Варіант 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 -# на storage: -sudo tcpdump -ni any udp port 10000 -# ззовні: -echo test | nc -u -w1 46.101.119.200 10000 +sudo tcpdump -ni any udp port 10000 # на storage +echo test | nc -u -w1 <публічна-IP> 10000 # ззовні ``` -У tcpdump має з'явитися пакет. Під час дзвінка в `chrome://webrtc-internals` вибрана -ICE-пара має бути з віддаленою адресою `46.101.119.200:10000`. - -Без UDP 10000 сторінка й кімната відкриваються, але дзвінок з 3+ учасниками (через JVB) не матиме -звуку/відео. Дзвінок удвох може працювати в режимі P2P (напряму між браузерами через STUN), -але не за суворим NAT/корпоративним файрволом — для цього потрібен TURN. - -## 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-облікові дані). +Під час дзвінка в `chrome://webrtc-internals` вибрана ICE-пара має вести на `<публічна-IP>:10000`. ## Вбудовування (iframe) у вікі / CRM diff --git a/docker-compose.yml b/docker-compose.yml index b1d7bd3..872c421 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -5,7 +5,7 @@ # https://crm-calls.tes.gd (443, Coolify Traefik) -> web:8000 (nginx) # / -> Jitsi web app # /xmpp-websocket -> prosody:5280 (proxied by web's nginx) -# UDP 10000 -> jvb (media + SCTP bridge channel), published on the host, bypasses Traefik. +# UDP JVB_PORT (10000) -> jvb (media + SCTP bridge channel), published on the host, bypasses Traefik. # # Secrets (JICOFO_AUTH_PASSWORD, JVB_AUTH_PASSWORD) live only in Coolify env vars. @@ -99,9 +99,9 @@ services: - /run:size=32M,mode=1750,exec - /tmp:size=16M,mode=1777,noexec ports: - # Media must reach JVB directly (not via Traefik). Router must forward - # public 46.101.119.200:10000/udp -> 192.168.50.73:10000/udp. - - '10000:10000/udp' + # Media must reach JVB directly (not via Traefik). How the public UDP port + # reaches storage is not decided yet (see README); port and advertised IPs come from Coolify env. + - '${JVB_PORT:-10000}:${JVB_PORT:-10000}/udp' volumes: - jitsi-jvb-config:/config environment: @@ -112,9 +112,9 @@ services: XMPP_AUTH_DOMAIN: auth.meet.jitsi XMPP_INTERNAL_MUC_DOMAIN: internal-muc.meet.jitsi JVB_AUTH_PASSWORD: '${JVB_AUTH_PASSWORD}' - JVB_PORT: '10000' - # Public IP first; LAN IP lets clients inside 192.168.50.0/24 connect without hairpin NAT. - JVB_ADVERTISE_IPS: '${JVB_ADVERTISE_IPS:-46.101.119.200,192.168.50.73}' + JVB_PORT: '${JVB_PORT:-10000}' + # Comma-separated, set in Coolify env: public IP of the chosen UDP path + LAN IP 192.168.50.73. + JVB_ADVERTISE_IPS: '${JVB_ADVERTISE_IPS:-192.168.50.73}' depends_on: - prosody