diff --git a/Makefile b/Makefile index 3324d4bbf0..082e5d87a8 100644 --- a/Makefile +++ b/Makefile @@ -2,7 +2,7 @@ FORCE_REBUILD ?= 0 JITSI_RELEASE ?= stable JITSI_BUILD ?= latest JITSI_REPO ?= jitsi -JITSI_SERVICES ?= base base-java web prosody jicofo jvb jigasi etherpad jibri +JITSI_SERVICES ?= base base-java web prosody jicofo jvb jigasi etherpad jibri turn BUILD_ARGS := --build-arg JITSI_REPO=$(JITSI_REPO) ifeq ($(FORCE_REBUILD), 1) diff --git a/README.md b/README.md index f8881d9440..296678024b 100644 --- a/README.md +++ b/README.md @@ -47,6 +47,9 @@ If you want to use jibri too, first configure host as described in JItsi BRoadca and then run Docker Compose as follows: ``docker-compose -f docker-compose.yml -f jibri.yml up -d`` or to use jigasi too: ``docker-compose -f docker-compose.yml -f jigasi.yml -f jibri.yml up -d`` +If you want to enable TURN server, configure it and run Docker Compose as +follows: ``docker-compose -f docker-compose.yml -f turn.yml up`` + ## Architecture A Jitsi Meet installation can be broken down into the following components: @@ -64,6 +67,33 @@ The diagram shows a typical deployment in a host running Docker. This project separates each of the components above into interlinked containers. To this end, several container images are provided. +### External Ports + +The following external ports must be opened on a firweall: + +* 80/tcp for Web UI HTTP (really just to redirect, after uncommenting ENABLE_HTTP_REDIRECT=1 in .env) +* 443/tcp for Web UI HTTPS +* 4443/tcp for RTP media over TCP +* 10000/udp for RTP media over UDP +* 5349/tcp for TURN data over TCP +* 5349/udp for TURN data over UDP +* 16000-17000/udp for TURN RTP media over UDP + +Also 20000-20050/udp for jigasi, in case you choose to deploy that to facilitate SIP acces. + +E.g. on a CentOS/Fedora server this would be done like this (without SIP access): + +```shell + $ sudo firewall-cmd --permanent --add-port=80/tcp + $ sudo firewall-cmd --permanent --add-port=443/tcp + $ sudo firewall-cmd --permanent --add-port=4443/tcp + $ sudo firewall-cmd --permanent --add-port=10000/udp + $ sudo firewall-cmd --permanent --add-port=5349/tcp + $ sudo firewall-cmd --permanent --add-port=5349/udp + $ sudo firewall-cmd --permanent --add-port=16000-17000/udp + $ sudo firewall-cmd --reload +``` + ### Images * **base**: Debian stable base image with the [S6 Overlay] for process control and the @@ -76,6 +106,7 @@ several container images are provided. * **jigasi**: [Jigasi], the SIP (audio only) gateway. * **etherpad**: [Etherpad], shared document editing addon. * **jibri**: [Jibri], the brooadcasting infrastructure. +* **turn**: [Coturn], the TURN server. ### Design considerations @@ -344,6 +375,31 @@ Variable | Description | Example For setting `GOOGLE_APPLICATION_CREDENTIALS` please read https://cloud.google.com/text-to-speech/docs/quickstart-protocol section "Before you begin" from 1 to 5 paragraph. +### TURN(S) server +For enable turn server for P2P and JVB connections, please set variables below + +Variable | Description | Default value +--- | --- | --- +`TURN_ENABLE` | Use TURN for P2P and JVB (bridge mode) connections | 0 +`TURN_REALM` | Realm to be used for the users with long-term credentials mechanism or with TURN REST API | realm +`TURN_SECRET` | Secret for connect to TURN server | keepthissecret +`TURN_HOST` | Annonce FQDN/IP address of the turn server via XMPP (XEP-0215) | 192.168.1.1 +`TURN_PUBLIC_IP` | Public IP address for an instance of turn server | set dynamically +`TURN_PORT` | TLS/TCP/UDP turn port for connection | 5349 +`TURN_TRANSPORT` | transport for turn connection (tcp/udp) | tcp +`TURN_RTP_MIN` | RTP start port for turn/turns connections | 16000 +`TURN_RTP_MAX` | RTP end port for turn/turns connections | 17000 + + +For enable web-admin panel for turn, please set variables below + +Variable | Description | Default value +--- | --- | --- +`TURN_ADMIN_ENABLE` | Enable web-admin panel | 0 +`TURN_ADMIN_USER` | Username for admin panel | admin +`TURN_ADMIN_SECRET` | Password for admin panel | changeme +`TURN_ADMIN_PORT` | HTTP(s) port for acess to admin panel | 8443 + ### Advanced configuration These configuration options are already set and generally don't need to be changed. @@ -403,7 +459,6 @@ option. ## TODO * Support container replicas (where applicable). -* TURN server. [Jitsi]: https://jitsi.org/ [Jitsi Meet]: https://jitsi.org/jitsi-meet/ @@ -421,3 +476,4 @@ option. [jwt.io]: https://jwt.io/#debugger-io [Etherpad]: https://github.com/ether/etherpad-lite [Jibri]: https://github.com/jitsi/jibri +[Coturn]: https://github.com/coturn/coturn diff --git a/base/Dockerfile b/base/Dockerfile index f1a0e9e239..f00f5b6a54 100644 --- a/base/Dockerfile +++ b/base/Dockerfile @@ -12,7 +12,7 @@ RUN \ tar xfz /tmp/s6-overlay.tar.gz -C / && \ rm -f /tmp/*.tar.gz && \ apt-dpkg-wrap apt-get update && \ - apt-dpkg-wrap apt-get install -y apt-transport-https apt-utils ca-certificates gnupg && \ + apt-dpkg-wrap apt-get install -y apt-transport-https apt-utils ca-certificates gnupg curl && \ apt-key add /tmp/jitsi.key && \ rm -f /tmp/jitsi.key && \ echo "deb https://download.jitsi.org $JITSI_RELEASE/" > /etc/apt/sources.list.d/jitsi.list && \ @@ -25,7 +25,7 @@ RUN \ RUN \ [ "$JITSI_RELEASE" = "unstable" ] && \ apt-dpkg-wrap apt-get update && \ - apt-dpkg-wrap apt-get install -y jq procps curl vim iputils-ping net-tools && \ + apt-dpkg-wrap apt-get install -y jq procps vim iputils-ping net-tools && \ apt-cleanup || \ true diff --git a/docker-compose.yml b/docker-compose.yml index 589e1a59f9..1126680947 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -37,6 +37,7 @@ services: - JIBRI_RECORDER_USER - JIBRI_RECORDER_PASSWORD - ENABLE_RECORDING + - TURN_ENABLE networks: meet.jitsi: aliases: @@ -99,6 +100,11 @@ services: - JWT_TOKEN_AUTH_MODULE - LOG_LEVEL - TZ + - TURN_ENABLE + - TURN_SECRET + - TURN_HOST + - TURN_PORT + - TURN_TRANSPORT networks: meet.jitsi: aliases: diff --git a/env.example b/env.example index 69f032b419..9397d56cfc 100644 --- a/env.example +++ b/env.example @@ -294,3 +294,44 @@ JIBRI_LOGS_DIR=/config/logs # Redirects HTTP traffic to HTTPS. Only works with the standard HTTPS port (443). #ENABLE_HTTP_REDIRECT=1 + +# Use TURN for P2P and JVB (bridge mode) connections +#TURN_ENABLE=0 + +# Realm to be used for the users with long-term credentials mechanism or with TURN REST API +#TURN_REALM=realm + +# Secret for connect to TURN server +#TURN_SECRET=keepthissecret + +# Annonce FQDN/IP address of the turn server via XMPP server (XEP-0215). +# If empty or not set, variable DOCKER_HOST_ADDRESS will be used by default. +#TURN_HOST=turn.example.com + +# Public IP address for an instance of turn server. +# If empty or not set, will be detected and set dynamically when a container start. +#TURN_PUBLIC_IP=144.144.144.144 + +# TLS/TCP/UDP turn port for connection +#TURN_PORT=5349 + +# Transport for stun/turn connection. Can be tcp or udp. +#TURN_TRANSPORT=tcp + +# RTP start port for turn/turns connections +#TURN_RTP_MIN=16000 + +# RTP end port for turn/turns connections +#TURN_RTP_MAX=17000 + +# Enable admin web access. If enabled, please set variables below. +#TURN_ADMIN_ENABLE=0 + +# Username for admin panel +#TURN_ADMIN_USER=admin + +# Password for admin panel +#TURN_ADMIN_SECRET=changeme + +# HTTP(s) port for acess to admin panel +#TURN_ADMIN_PORT=8443 diff --git a/prosody/Dockerfile b/prosody/Dockerfile index dac9fc6dc5..86bcfc0d6c 100644 --- a/prosody/Dockerfile +++ b/prosody/Dockerfile @@ -38,6 +38,10 @@ RUN \ && apt-cleanup \ && rm -rf /tmp/usr /var/cache/apt +RUN \ + curl -4so /prosody-plugins/mod_turncredentials.lua \ + https://raw.githubusercontent.com/netaskd/mod_turncredentials/master/mod_turncredentials.lua + RUN \ sed -i s/hook/hook_global/g /prosody-plugins/mod_auth_token.lua \ && patch -d /usr/lib/prosody/modules/muc -p0 < /prosody-plugins/muc_owner_allow_kick.patch diff --git a/prosody/rootfs/defaults/conf.d/jitsi-meet.cfg.lua b/prosody/rootfs/defaults/conf.d/jitsi-meet.cfg.lua index ea55fc629b..99f92b85bd 100644 --- a/prosody/rootfs/defaults/conf.d/jitsi-meet.cfg.lua +++ b/prosody/rootfs/defaults/conf.d/jitsi-meet.cfg.lua @@ -2,6 +2,17 @@ admins = { "{{ .Env.JICOFO_AUTH_USER }}@{{ .Env.XMPP_AUTH_DOMAIN }}" } plugin_paths = { "/prosody-plugins/", "/prosody-plugins-custom" } http_default_host = "{{ .Env.XMPP_DOMAIN }}" +{{ if .Env.TURN_ENABLE | default "0" | toBool }} +turncredentials_secret = "{{ .Env.TURN_SECRET | default "keepthissecret" }}"; +turncredentials = { + { type = "turns", + host = "{{ .Env.TURN_HOST | default .Env.DOCKER_HOST_ADDRESS }}", + port = {{ .Env.TURN_PORT | default "3478" }}, + transport = "{{ .Env.TURN_TRANSPORT | default "tcp" }}" + } +} +{{ end }} + {{ $ENABLE_AUTH := .Env.ENABLE_AUTH | default "0" | toBool }} {{ $AUTH_TYPE := .Env.AUTH_TYPE | default "internal" }} {{ $JWT_ASAP_KEYSERVER := .Env.JWT_ASAP_KEYSERVER | default "" }} diff --git a/prosody/rootfs/etc/cont-init.d/10-config b/prosody/rootfs/etc/cont-init.d/10-config index 701efcdd93..83e9d6ecd0 100644 --- a/prosody/rootfs/etc/cont-init.d/10-config +++ b/prosody/rootfs/etc/cont-init.d/10-config @@ -31,6 +31,12 @@ fi if [[ ! -f $PROSODY_CFG ]]; then cp -r /defaults/* /config + + if [[ "${TURN_ENABLE}" == "1" || "${TURN_ENABLE}" == "true" ]]; then + [ -z "${GLOBAL_MODULES}" ] && export GLOBAL_MODULES="turncredentials" \ + || export GLOBAL_MODULES="${GLOBAL_MODULES},turncredentials" + fi + tpl /defaults/prosody.cfg.lua > $PROSODY_CFG tpl /defaults/conf.d/jitsi-meet.cfg.lua > /config/conf.d/jitsi-meet.cfg.lua diff --git a/turn.yml b/turn.yml new file mode 100644 index 0000000000..d52d44c2f9 --- /dev/null +++ b/turn.yml @@ -0,0 +1,30 @@ +version: '3' + +services: + # coturn TURN server project + turn: + image: jitsi/turn + restart: always + volumes: + - ${CONFIG}/turn:/config + ports: + - '${TURN_PORT}:${TURN_PORT}/tcp' + - '${TURN_PORT}:${TURN_PORT}/udp' + - '${TURN_RTP_MIN}-${TURN_RTP_MAX}:${TURN_RTP_MIN}-${TURN_RTP_MAX}/udp' + - '${TURN_ADMIN_PORT}:${TURN_ADMIN_PORT}/tcp' + environment: + - DOCKER_HOST_ADDRESS + - TURN_SECRET + - TURN_REALM + - TURN_HOST + - TURN_PORT + - TURN_TRANSPORT + - TURN_RTP_MIN + - TURN_RTP_MAX + - TURN_ADMIN_ENABLE + - TURN_ADMIN_USER + - TURN_ADMIN_SECRET + - TURN_ADMIN_PORT + networks: + meet.jitsi: + diff --git a/turn/Dockerfile b/turn/Dockerfile new file mode 100644 index 0000000000..da62b7148a --- /dev/null +++ b/turn/Dockerfile @@ -0,0 +1,13 @@ +ARG VERSION +FROM instrumentisto/coturn:${VERSION:-latest} + +RUN apk add --no-cache openssl + +ADD ./rootfs/defaults/docker-entrypoint.sh /docker-entrypoint.sh + +ENTRYPOINT ["/docker-entrypoint.sh"] + +VOLUME ["/config"] + +EXPOSE 5349 8443 10000:11000/udp + diff --git a/turn/Makefile b/turn/Makefile new file mode 100644 index 0000000000..7317d86e50 --- /dev/null +++ b/turn/Makefile @@ -0,0 +1,5 @@ +build: + docker build $(BUILD_ARGS) -t $(JITSI_REPO)/turn . + +.PHONY: build + diff --git a/turn/rootfs/defaults/docker-entrypoint.sh b/turn/rootfs/defaults/docker-entrypoint.sh new file mode 100755 index 0000000000..28e397fb5a --- /dev/null +++ b/turn/rootfs/defaults/docker-entrypoint.sh @@ -0,0 +1,42 @@ +#!/bin/ash + +# make certs if not exist +if [[ ! -f /config/cert.crt || ! -f /config/cert.key ]]; then + openssl req -newkey rsa:2048 -nodes -keyout /config/cert.key -x509 -days 3650 -out /config/cert.crt -subj "/C=US/ST=NY/L=NY/O=IT/CN=${TURN_HOST}" +fi + +# use non empty TURN_PUBLIC_IP variable, othervise set it dynamically. +[ -z "${TURN_PUBLIC_IP}" ] && export TURN_PUBLIC_IP=$(curl -4ks https://icanhazip.com) +[ -z "${TURN_PUBLIC_IP}" ] && echo "ERROR: variable TURN_PUBLIC_IP is not set and can not be set dynamically!" && kill 1 + +# set coturn web-admin access +if [[ "${TURN_ADMIN_ENABLE}" == "1" || "${TURN_ADMIN_ENABLE}" == "true" ]]; then + turnadmin -A -u ${TURN_ADMIN_USER:-admin} -p ${TURN_ADMIN_SECRET:-changeme} + export TURN_ADMIN_OPTIONS="--web-admin --web-admin-ip=$(hostname -i) --web-admin-port=${TURN_ADMIN_PORT:-8443}" +fi + +# run coturn server with API auth method enabled. +turnserver -n ${TURN_ADMIN_OPTIONS} \ +--verbose \ +--prod \ +--no-tlsv1 \ +--no-tlsv1_1 \ +--log-file=stdout \ +--listening-port=${TURN_PORT:-5349} \ +--tls-listening-port=${TURN_PORT:-5349} \ +--alt-listening-port=${TURN_PORT:-5349} \ +--alt-tls-listening-port=${TURN_PORT:-5349} \ +--cert=/config/cert.crt \ +--pkey=/config/cert.key \ +--min-port=${TURN_RTP_MIN:-10000} \ +--max-port=${TURN_RTP_MAX:-11000} \ +--no-stun \ +--use-auth-secret \ +--static-auth-secret=${TURN_SECRET:-keepthissecret} \ +--no-multicast-peers \ +--realm=${TURN_REALM:-realm} \ +--listening-ip=$(hostname -i) \ +--external-ip=${TURN_PUBLIC_IP} \ +--cli-password=NotReallyCliUs3d \ +--no-cli + diff --git a/web/rootfs/etc/cont-init.d/10-config b/web/rootfs/etc/cont-init.d/10-config index 5bf08a6432..93c27fd36e 100644 --- a/web/rootfs/etc/cont-init.d/10-config +++ b/web/rootfs/etc/cont-init.d/10-config @@ -92,6 +92,13 @@ if [[ ! -f /config/config.js ]]; then -e "s#// transcribingEnabled:.*#transcribingEnabled: true,#" \ /config/config.js fi + + if [[ $TURN_ENABLE -eq 1 || "$TURN_ENABLE" == "true" ]]; then + sed -i \ + -e "s#// useStunTurn:.*,#useStunTurn: true,#" \ + /config/config.js + fi + fi if [[ ! -f /config/interface_config.js ]]; then