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

Builtin TURN Service #1398

Draft
wants to merge 8 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all 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 @@ -4,7 +4,7 @@ JITSI_BUILD ?= unstable
JITSI_REPO ?= jitsi
NATIVE_ARCH ?= $(shell uname -m)

JITSI_SERVICES := base base-java web prosody jicofo jvb jigasi jibri
JITSI_SERVICES := base base-java web prosody jicofo jvb jigasi jibri eturnal

ifeq ($(NATIVE_ARCH),x86_64)
TARGETPLATFORM := linux/amd64
Expand Down
28 changes: 28 additions & 0 deletions docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ services:
ports:
- '${HTTP_PORT}:80'
- '${HTTPS_PORT}:443'
- '${TURN_PORT}:3478'
volumes:
- ${CONFIG}/web:/config:Z
- ${CONFIG}/web/crontabs:/var/spool/cron/crontabs:Z
Expand Down Expand Up @@ -126,6 +127,8 @@ services:
- TESTING_OCTO_PROBABILITY
- TOKEN_AUTH_URL
- TOOLBAR_BUTTONS
- TURN_HOST
- TURNS_HOST
- TZ
- VIDEOQUALITY_BITRATE_H264_LOW
- VIDEOQUALITY_BITRATE_H264_STANDARD
Expand Down Expand Up @@ -338,6 +341,31 @@ services:
networks:
meet.jitsi:

# turn service
eturnal:
image: jitsi/eturnal:${JITSI_IMAGE_VERSION:-unstable}
restart: ${RESTART_POLICY:-unless-stopped}
ports:
- 443:443/udp
- '${TURN_RELAY_MIN_PORT:-50000}-${TURN_RELAY_MAX_PORT:-50500}:${TURN_RELAY_MIN_PORT:-50000}-${TURN_RELAY_MAX_PORT:-50500}/udp'
security_opt:
- no-new-privileges:true
volumes:
- ${CONFIG}/eturnal:/opt/eturnal/etc:Z
- ${CONFIG}/web/acme-certs:/etc/eturnal/tls:Z
environment:
- TURN_CREDENTIALS
- TURN_RELAY_MIN_PORT
- TURN_RELAY_MAX_PORT
- TURNS_HOST
- DOCKER_HOST_ADDRESS
- JVB_DISABLE_STUN
depends_on:
- prosody
- web
networks:
meet.jitsi:

# Custom network so all services can communicate using a FQDN
networks:
meet.jitsi:
28 changes: 26 additions & 2 deletions env.example
Original file line number Diff line number Diff line change
Expand Up @@ -178,7 +178,6 @@ ETHERPAD_SKIN_VARIANTS="super-light-toolbar super-light-editor light-background
# Wether to use starttls, implies LDAPv3 and requires ldap:// instead of ldaps://
# LDAP_START_TLS=1


#
# Security
#
Expand All @@ -204,6 +203,31 @@ JIBRI_RECORDER_PASSWORD=
# XMPP password for Jibri client connections
JIBRI_XMPP_PASSWORD=

# defines the shared authentication secret used to derive the passwords for ephemeral TURN user names
# TURN_CREDENTIALS=


#
# eturnal TURN Server configuration
#

# currently hard coded to 'turn.$LETSENCRYPT_DOMAIN' (in web container)
# TURN_HOST=turn.example.com

# currently hard coded (in web container)
# TURN_PORT=3478

# currently hard coded to 'turn.$LETSENCRYPT_DOMAIN' (in web container)
# TURNS_HOST=turn.example.com

# currently hard coded (in web container)
# TURNS_PORT=443

# TURN relay port range
# TURN_RELAY_MIN_PORT=50000
# TURN_RELAY_MAX_PORT=50500


#
# Docker Compose options
#
Expand All @@ -212,4 +236,4 @@ JIBRI_XMPP_PASSWORD=
#RESTART_POLICY=unless-stopped

# Jitsi image version (useful for local development)
#JITSI_IMAGE_VERSION=latest
#JITSI_IMAGE_VERSION=latest
71 changes: 71 additions & 0 deletions eturnal/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
FROM debian:bullseye-slim as prepare
# BUILD: define image build arguments
ARG ETURNAL_VERSION=1.10.1
ARG TINI_VERSION=0.19.0
ARG S6_VERSION=v3.1.2.1

RUN set -x \
&& apt-get update && apt-get install apt-transport-https apt-utils ca-certificates curl wget xz-utils libcap2-bin gcc -y \
# RUNTIME: install eturnal binary with installer
&& ARCH=$(uname -m | sed -e 's/x86_64/x64/;s/aarch64/arm64/;s/armv7l/arm/;s/s390x/s390x/;s/ppc64le/ppc64le/') \
&& curl -O https://eturnal.net/download/linux/installer/eturnal-$ETURNAL_VERSION-linux-$ARCH.run \
&& chmod +x eturnal-*.run \
&& ./eturnal-*.run \
&& rm -rf eturnal-*.run /opt/eturnal/etc/* /etc/eturnal.yml \
# RUNTIME: create rootfs with eturnal
&& mkdir -p /rootfs/opt/eturnal/log /rootfs/opt/eturnal/run /rootfs/opt/eturnal/tls \
&& cp -r /opt/eturnal /rootfs/opt \
# RUNTIME: install runtime init
&& ARCH=$(uname -m) \
&& wget -qO - https://github.com/just-containers/s6-overlay/releases/download/$S6_VERSION/s6-overlay-noarch.tar.xz | tar xfJ - -C /rootfs \
&& wget -qO - https://github.com/just-containers/s6-overlay/releases/download/$S6_VERSION/s6-overlay-$ARCH.tar.xz | tar xfJ - -C /rootfs \
# RUNTIME: copy libcap binaries and libraries
&& gccMultiarch="$(gcc -print-multiarch)" \
&& mkdir -p /rootfs/sbin /rootfs/lib/$gccMultiarch/ \
&& cp -r /sbin/capsh /sbin/getcap /sbin/getpcaps /sbin/setcap /rootfs/sbin \
&& cp -r /lib/$gccMultiarch/libcap*.so.* /rootfs/lib/$gccMultiarch/
# RUNTIME: copy s6 scripts
COPY rootfs /rootfs

FROM gcr.io/distroless/base-debian11 AS eturnal
# BUILD: copy eturnal
COPY --from=prepare --chown=9000:9000 /rootfs /
COPY --from=busybox:stable-glibc /bin /bin
ARG HOME=/opt/eturnal

RUN set -x \
# RUNTIME: add runtime group and user
&& echo "eturnal:x:9000:eturnal" >> /etc/group \
&& echo "eturnal:x:9000:9000:Linux User,,,:/opt/eturnal:/bin/sh" >> /etc/passwd \
# RUNTIME: create symbolic links, entrypoint script and minimal configuration file
&& ln -s $HOME/bin/eturnalctl /usr/sbin/eturnalctl \
&& ln -s $HOME/bin/stun /usr/sbin/stun \
&& chmod +x /etc/cont-init.d/* /etc/services.d/*/run \
&& setcap 'cap_net_bind_service=+ep' $(find $HOME -name beam.smp)

# remove libssl and openssl from distroless image
# as they are statically built within eturnal binary
RUN find -type f -name 'libcrypt*' -exec rm -rv {} + \
&& find -type d -name 'libcrypt*' -exec rm -rv {} + \
&& find -type f -name 'libssl*' -exec rm -rv {} + \
&& find -type d -name 'libssl*' -exec rm -rv {} + \
&& find -type f -name 'openssl*' -exec rm -rv {} + \
&& find -type d -name 'openssl*' -exec rm -rv {} +

FROM scratch AS runtime
# BUILD: copy eturnal
COPY --from=eturnal / /
ARG HOME=/opt/eturnal
# RUNTIME: define environment variables
ENV S6_BEHAVIOUR_IF_STAGE2_FAILS=2 \
ERL_DIST_PORT=3470 \
PIPE_DIR=$HOME/run/pipe/ \
STUN_SERVICE="stun.conversations.im 3478"

# RUNTIME: define container runtime parameters
WORKDIR $HOME
#USER eturnal
VOLUME ["$HOME"]
EXPOSE 3478 3478/udp
ENTRYPOINT ["/init"]
#CMD ["eturnalctl", "foreground"]
62 changes: 62 additions & 0 deletions eturnal/rootfs/etc/cont-init.d/10-config
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
#!/command/with-contenv /bin/sh
# eturnal config
#DOCKER_INTERNAL_IP=$(cat /etc/hosts | grep $(hostname) | awk '{ print $1 }')
cat > /opt/eturnal/etc/eturnal.yml <<EOF
eturnal:
listen:
-
ip: "::"
port: 443
transport: udp
-
ip: "::"
port: 3478
transport: auto
proxy_protocol: true

## TLS certificate/key files (must be readable by 'eturnal' user!):
#tls_crt_file: /opt/eturnal/tls/fullchain.pem
#tls_key_file: /opt/eturnal/tls/key.pem

## Reject TURN relaying from/to the following addresses/networks:
blacklist: # This is the default blacklist.
- "127.0.0.0/8" # IPv4 loopback.
- "::1" # IPv6 loopback.
- recommended # Expands to a number of networks recommended to be
# blocked, but includes private networks. Those
# would have to be 'whitelist'ed if eturnal serves
# local clients/peers within such networks.

## Logging configuration:
log_level: info # critical | error | warning | notice | info | debug
log_dir: stdout # Enable for logging to the terminal/journal.

## See: https://eturnal.net/documentation/#Module_Configuration
modules:
mod_log_stun: {} # Log STUN queries (in addition to TURN sessions).
#mod_stats_influx: {} # Log STUN/TURN events into InfluxDB.
#mod_stats_prometheus: # Expose STUN/TURN and VM metrics to Prometheus.
# ip: any # This is the default: Listen on all interfaces.
# port: 8081 # This is the default.
# tls: false # This is the default.
# vm_metrics: true # This is the default.
EOF

# tls certificates
TLS_CERT_FILE=$(find /etc/ -name fullchain.pem)
TLS_KEY_FILE=$(find /etc/ -name key.pem)
if [ ! -z $TLS_CERT_FILE ] || [ ! -z $TLS_KEY_FILE ]; then
if [ ! -z $TLS_CERT_FILE ]; then
cp -p $TLS_CERT_FILE /opt/eturnal/tls
sed -i -e "s|#tls_crt_file:|tls_crt_file:|g" /opt/eturnal/etc/eturnal.yml
fi
if [ ! -z $TLS_KEY_FILE ]; then
cp -p $TLS_KEY_FILE /opt/eturnal/tls
sed -i -e "s|#tls_key_file:|tls_key_file:|g" /opt/eturnal/etc/eturnal.yml
fi
chown 9000:9000 /opt/eturnal/tls/*
fi

# change file permissions
chown -R 9000:9000 /opt/eturnal/etc
chmod 640 /opt/eturnal/etc/eturnal.yml
47 changes: 47 additions & 0 deletions eturnal/rootfs/etc/services.d/cron/run
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
#!/command/with-contenv /bin/sh

# only run the script if TURNS is enabled (and if acme.sh is used in web container)
if [ ! -z $TURNS_HOST ] || ([ ! -z $TURNS_HOST ] && [ $ENABLE_LETSENCRYPT -eq 1 ]); then

while true; do
# sleep a sufficient time to give the web container's acme.sh script a chance to obtain certificates
sleep 60s

# mounted certs from web container
TLS_CERT_FILE=$(find /etc/ -name fullchain.pem)
TLS_KEY_FILE=$(find /etc/ -name key.pem)

# check if files have changed
if [ ! -z $TLS_CERT_FILE ] || [ ! -z $TLS_KEY_FILE ]; then
current=$(md5sum /opt/eturnal/tls/fullchain.pem | awk '{ print $1 }')
last_modified=$(md5sum $TLS_CERT_FILE | awk '{ print $1 }')

# copy certs to eturnal, adjust configuration file
if [ "$current" != "$last_modified" ]; then
echo " $(date) [Info] TLS certificates have been renewed, copy certs to eturnal and reload"

if [ ! -z $TLS_CERT_FILE ]; then
cp -p $TLS_CERT_FILE /opt/eturnal/tls
sed -i -e "s|#tls_crt_file:|tls_crt_file:|g" /opt/eturnal/etc/eturnal.yml
fi

if [ ! -z $TLS_KEY_FILE ]; then
cp -p $TLS_KEY_FILE /opt/eturnal/tls
sed -i -e "s|#tls_key_file:|tls_key_file:|g" /opt/eturnal/etc/eturnal.yml
fi

# fix ownership and reload the service (reloading eturnal does not stop/break any active sessions)
chown 9000:9000 /opt/eturnal/tls/*
eturnalctl reload

else
echo " $(date) [Info] CronJob: TLS certificates have not been renewed, check again in 5 minutes"
fi
sleep 240s
fi
done

# don't repeatedly run the cron job, if eturnal does not serve TURNS
else
s6-svc -O /var/run/s6/legacy-services/cron
fi
43 changes: 43 additions & 0 deletions eturnal/rootfs/etc/services.d/eturnal/run
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
#!/command/with-contenv /bin/sh

# TURN credentials
if [ ! -z $TURN_CREDENTIALS ]
then
export ETURNAL_SECRET=$TURN_CREDENTIALS
fi

# TURN relay port range
if [ ! -z $TURN_RELAY_MIN_PORT ] || [ ! -z $TURN_RELAY_MAX_PORT ]
then
if [ ${TURN_RELAY_MIN_PORT-50000} \< ${TURN_RELAY_MAX_PORT-50500} ]
then
export ETURNAL_RELAY_MIN_PORT=${TURN_RELAY_MIN_PORT-50000}
export ETURNAL_RELAY_MAX_PORT=${TURN_RELAY_MAX_PORT-50500}
else
echo ""
echo " $(date) [INFO] Configuration check:"
echo ""
echo " $(date) [WARNING] Defined TURN range minimum port -> ${TURN_RELAY_MIN_PORT-50000} is greater or equal than maximum port -> ${TURN_RELAY_MAX_PORT-50500}"
echo " $(date) [INFO] Starting eturnal with relay port range 50000 - 50500"
echo ""
export ETURNAL_RELAY_MIN_PORT=50000
export ETURNAL_RELAY_MAX_PORT=50500
fi
else
export ETURNAL_RELAY_MIN_PORT=50000
export ETURNAL_RELAY_MAX_PORT=50500
fi

# discover public IP addresses
if [ ! -z $DOCKER_HOST_ADDRESS ]
then
export ETURNAL_RELAY_IPV4_ADDR=$DOCKER_HOST_ADDRESS
else
if [ -z "$JVB_DISABLE_STUN" ]
then
export ETURNAL_RELAY_IPV4_ADDR=${ETURNAL_RELAY_IPV4_ADDR-$(stun -4 $STUN_SERVICE)}
export ETURNAL_RELAY_IPV6_ADDR=${ETURNAL_RELAY_IPV6_ADDR-$(stun -6 $STUN_SERVICE)}
fi
fi

exec eturnalctl foreground
2 changes: 2 additions & 0 deletions gen-passwords.sh
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,13 @@ JVB_AUTH_PASSWORD=$(generatePassword)
JIGASI_XMPP_PASSWORD=$(generatePassword)
JIBRI_RECORDER_PASSWORD=$(generatePassword)
JIBRI_XMPP_PASSWORD=$(generatePassword)
TURN_CREDENTIALS=$(generatePassword)

sed -i.bak \
-e "s#JICOFO_AUTH_PASSWORD=.*#JICOFO_AUTH_PASSWORD=${JICOFO_AUTH_PASSWORD}#g" \
-e "s#JVB_AUTH_PASSWORD=.*#JVB_AUTH_PASSWORD=${JVB_AUTH_PASSWORD}#g" \
-e "s#JIGASI_XMPP_PASSWORD=.*#JIGASI_XMPP_PASSWORD=${JIGASI_XMPP_PASSWORD}#g" \
-e "s#JIBRI_RECORDER_PASSWORD=.*#JIBRI_RECORDER_PASSWORD=${JIBRI_RECORDER_PASSWORD}#g" \
-e "s#JIBRI_XMPP_PASSWORD=.*#JIBRI_XMPP_PASSWORD=${JIBRI_XMPP_PASSWORD}#g" \
-e "s#TURN_CREDENTIALS=.*#TURN_CREDENTIALS=${TURN_CREDENTIALS}#g" \
"$(dirname "$0")/.env"
23 changes: 20 additions & 3 deletions web/rootfs/defaults/default
Original file line number Diff line number Diff line change
Expand Up @@ -12,15 +12,32 @@ server {
{{ end }}
}

{{ if not (.Env.DISABLE_HTTPS | default "0" | toBool) }}
{{ if .Env.TURNS_HOST }}
{{ if not (.Env.DISABLE_HTTPS | default "0" | toBool) }}
server {
listen 443 ssl http2;
listen 8443 ssl http2 proxy_protocol;

{{ if .Env.ENABLE_IPV6 | default "1" | toBool }}
listen [::]:443 ssl http2;
listen [::]:8443 ssl http2;
{{ end }}

include /config/nginx/ssl.conf;
include /config/nginx/meet.conf;
}
{{ end }}
{{ end }}

{{ if not .Env.TURNS_HOST }}
{{ if not (.Env.DISABLE_HTTPS | default "0" | toBool) }}
server {
listen 443 ssl http2;

{{ if .Env.ENABLE_IPV6 | default "1" | toBool }}
listen [::]:443 ssl http2;
{{ end }}

include /config/nginx/ssl.conf;
include /config/nginx/meet.conf;
}
{{ end }}
{{ end }}
3 changes: 3 additions & 0 deletions web/rootfs/defaults/nginx.conf
Original file line number Diff line number Diff line change
Expand Up @@ -67,5 +67,8 @@ http {
include /config/nginx/site-confs/*;
}

stream {
include /config/nginx/stream/*;
}

daemon off;
Loading