Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

turn: add TURN server #163

Open
wants to merge 9 commits into
base: dev
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
31 changes: 30 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there anything I can do to support his PR towards being merged? I'd really like to vacate Zoom with all of its problems for Jitsi Meet...

follows: ``docker-compose -f docker-compose.yml -f turn.yml up``

## Architecture

A Jitsi Meet installation can be broken down into the following components:
Expand Down Expand Up @@ -76,6 +79,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

Expand Down Expand Up @@ -344,6 +348,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 | 10000
`TURN_RTP_MAX` | RTP end port for turn/turns connections | 11000


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.
Expand Down Expand Up @@ -403,7 +432,6 @@ option.
## TODO

* Support container replicas (where applicable).
* TURN server.

[Jitsi]: https://jitsi.org/
[Jitsi Meet]: https://jitsi.org/jitsi-meet/
Expand All @@ -421,3 +449,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
6 changes: 6 additions & 0 deletions docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ services:
- JIBRI_RECORDER_USER
- JIBRI_RECORDER_PASSWORD
- ENABLE_RECORDING
- TURN_ENABLE
networks:
meet.jitsi:
aliases:
Expand Down Expand Up @@ -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:
Expand Down
41 changes: 41 additions & 0 deletions env.example
Original file line number Diff line number Diff line change
Expand Up @@ -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_PUBIC_IP=144.144.144.144
netaskd marked this conversation as resolved.
Show resolved Hide resolved

# TLS/TCP/UDP turn port for connection
#TURN_PORT=5349

# Transport for stun/turn connection. Can be tcp or udp.
#TURN_TRANSPORT=tcp
netaskd marked this conversation as resolved.
Show resolved Hide resolved

# RTP start port for turn/turns connections
#TURN_RTP_MIN=10000
netaskd marked this conversation as resolved.
Show resolved Hide resolved

# RTP end port for turn/turns connections
#TURN_RTP_MAX=11000

# 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
4 changes: 4 additions & 0 deletions prosody/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,10 @@ RUN \
&& apt-cleanup \
&& rm -rf /tmp/usr /var/cache/apt

RUN \
curl -4so /prosody-plugins/mod_turncredentials.lua \
netaskd marked this conversation as resolved.
Show resolved Hide resolved
https://raw.githubusercontent.com/netaskd/mod_turncredentials/master/mod_turncredentials.lua
saghul marked this conversation as resolved.
Show resolved Hide resolved

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
Expand Down
11 changes: 11 additions & 0 deletions prosody/rootfs/defaults/conf.d/jitsi-meet.cfg.lua
Original file line number Diff line number Diff line change
Expand Up @@ -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 }}",
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shouldn't the default be TURN_PUBLIC_IP ?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

prosody doesn't know about TURN_PUBLIC_IP in case TURN_PUBLIC_IP is not set from env file (got dynamically)

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We can detect it in the init file and re-export it, perhaps?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'll try to explain my view on it. TURN_HOST is an announced FQDN for reach cluster of turn. It has use inside prosody for provide XEP-0215.
TURN_PUBLIC_IP is a exactly external IP address of turn instance.
When we use TURN_HOST="turn.example.com", conditionally, A record can be turn.example.com TURN_PUBLIC_IP1, TURN_PUBLIC_IP2, TURN_PUBLIC_IP3 and client (that got TURN_HOST via XEP-0215) goes to this scope.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

On the other hand, if it's just a single setup. TURN_HOST can be DOCKER_HOST_ADDRESS (if not set) and TURN_PUBLIC_IP is dynamically set.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not completely sure of the latest comment.. I guess, we should remove default set for "TURN_HOST" and make it force needed, because if TURN_PUBLIC_IP and DOCKER_HOST_ADDRESS is different, setup will broken. WDYT?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not sure I follow you here. I do understand that you could set a hostname. But if you don't, the IP that should be there is the publicc IP of the TURN server, right?

On hindsight DOCKER_HOST_ADDRESS was not a great name. I'll probably rename it to JVB_PUBLIC_IP in the future, which is a better name.

So, to me, it makes sense that the default is the TURN public IP, and not anything else.

Copy link
Contributor Author

@netaskd netaskd Apr 23, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I suggest to use only TURN_HOST for spreading hostname via XEP-0215. In this case we are suppose that:
a) the turn instance only one, and we exactly know how to reach it from outside.
b) the turn instance is multiple, but behind proxy. so, proxy knows how to reach each of them.

The TURN_HOST is mandatory variable, otherwise do not enable turn functions.

@saghul WDYT?

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 "" }}
Expand Down
6 changes: 6 additions & 0 deletions prosody/rootfs/etc/cont-init.d/10-config
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
30 changes: 30 additions & 0 deletions turn.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
version: '3'

services:
netaskd marked this conversation as resolved.
Show resolved Hide resolved
# coturn TURN server project
turn:
image: jitsi/turn
saghul marked this conversation as resolved.
Show resolved Hide resolved
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:

13 changes: 13 additions & 0 deletions turn/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
ARG VERSION
FROM instrumentisto/coturn:${VERSION:-latest}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This has been deprecated: instrumentisto/coturn-docker-image@0a4a7f0

It is suggested to switch to the upstream image coturn/coturn (🔗).


RUN apk add --no-cache openssl

ADD ./rootfs/defaults/docker-entrypoint.sh /docker-entrypoint.sh

ENTRYPOINT ["/docker-entrypoint.sh"]
netaskd marked this conversation as resolved.
Show resolved Hide resolved

VOLUME ["/config"]

EXPOSE 5349 8443 10000:11000/udp

netaskd marked this conversation as resolved.
Show resolved Hide resolved
5 changes: 5 additions & 0 deletions turn/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
build:
docker build $(BUILD_ARGS) -t $(JITSI_REPO)/turn .

.PHONY: build

42 changes: 42 additions & 0 deletions turn/rootfs/defaults/docker-entrypoint.sh
Original file line number Diff line number Diff line change
@@ -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 \

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

coturn/coturn@520e172

--no-software-attribute Production mode: hide the software version.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's a good spot!

--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} \
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As per https://hub.docker.com/r/instrumentisto/coturn they recommend using the detect-external-ip tool to automatically get the external IP. I did not check how that works, but I'm not sure as well to add a dependency on icanhazip.com to get the TURN server up a good thing.

Copy link
Contributor Author

@netaskd netaskd Jul 19, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, should have checked it before. Awesome. I've been testing this and works great. My use case is on kubernetes and Im having some issues with networking, but that is another story.

Copy link
Contributor

@almereyda almereyda Feb 8, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This has been adopted for the upstream coturn image, too, despite the strategy it uses is a little different.

https://github.com/coturn/coturn/blob/master/docker/coturn/rootfs/usr/local/bin/detect-external-ip.sh

See previous comment https://github.com/jitsi/docker-jitsi-meet/pull/163/files#r802157850

--cli-password=NotReallyCliUs3d \
--no-cli

7 changes: 7 additions & 0 deletions web/rootfs/etc/cont-init.d/10-config
Original file line number Diff line number Diff line change
Expand Up @@ -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
netaskd marked this conversation as resolved.
Show resolved Hide resolved
sed -i \
-e "s#// useStunTurn:.*,#useStunTurn: true,#" \
/config/config.js
fi

fi

if [[ ! -f /config/interface_config.js ]]; then
Expand Down