From c73ba372022db0bb967d628b136934219d8c4fec Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=94=D0=B0=D0=BC=D1=8F=D0=BD=20=D0=9C=D0=B8=D0=BD=D0=BA?= =?UTF-8?q?=D0=BE=D0=B2?= Date: Thu, 9 Jan 2020 16:51:27 +0000 Subject: [PATCH] Introduces installing coturn as turn server for jitsi-meet (#4959) * Adds package that can configure using turnserver for jitsi-meet. Activates http2 on the nginx host and uses the alpn send with the web requests to multiplex traffic to be served as web of proxied to the turn server. It needs nginx at least v1.13.10. Adds turncredentials module from Philipp Hancke, with small modification (all int values for hosts need to be strings/tostring()) in order to be able to use the module with prosody 0.11. * Moves loading of stream after loading stream module (50-..). * Leaves DISABLE_TCP_HARVESTER to be handled by jvb. * Fixes comments. * Properly detect first time coturn install and configure it. * Handles upgrading from jetty serving web. * Does not create jvb user if already exists. * Fixes let's encrypt and adds turnserver handling. * Enables use of turn server in config.js if available. * Adds a check whether prosody config exists. There are cases where deployments can still have configured prosody in the main prosody config in /etc/prosody. --- config.js | 2 + debian/control | 6 +- debian/jitsi-meet-prosody.postinst | 18 ++- debian/jitsi-meet-prosody.templates | 5 + debian/jitsi-meet-turnserver.install | 2 + debian/jitsi-meet-turnserver.links | 1 + debian/jitsi-meet-turnserver.postinst | 127 ++++++++++++++++++ debian/jitsi-meet-turnserver.templates | 9 ++ debian/jitsi-meet-web-config.postinst | 15 +-- .../prosody.cfg.lua-jvb.example | 12 ++ doc/debian/jitsi-meet-turn/RREADME | 1 + doc/debian/jitsi-meet-turn/turnserver.conf | 13 ++ doc/debian/jitsi-meet/jitsi-meet.conf | 30 +++++ doc/debian/jitsi-meet/jitsi-meet.example | 12 +- doc/quick-install.md | 6 +- resources/install-letsencrypt-cert.sh | 9 ++ .../prosody-plugins/mod_turncredentials.lua | 80 +++++++++++ 17 files changed, 328 insertions(+), 20 deletions(-) create mode 100644 debian/jitsi-meet-turnserver.install create mode 100644 debian/jitsi-meet-turnserver.links create mode 100644 debian/jitsi-meet-turnserver.postinst create mode 100644 debian/jitsi-meet-turnserver.templates create mode 100644 doc/debian/jitsi-meet-turn/RREADME create mode 100644 doc/debian/jitsi-meet-turn/turnserver.conf create mode 100644 doc/debian/jitsi-meet/jitsi-meet.conf create mode 100644 resources/prosody-plugins/mod_turncredentials.lua diff --git a/config.js b/config.js index 3237579a6..da1c05d2b 100644 --- a/config.js +++ b/config.js @@ -329,6 +329,8 @@ var config = { // The STUN servers that will be used in the peer to peer connections stunServers: [ + + // { urls: 'stun:jitsi-meet.example.com:443' }, { urls: 'stun:stun.l.google.com:19302' }, { urls: 'stun:stun1.l.google.com:19302' }, { urls: 'stun:stun2.l.google.com:19302' } diff --git a/debian/control b/debian/control index 061a78d33..94cb4094f 100644 --- a/debian/control +++ b/debian/control @@ -21,7 +21,7 @@ Description: WebRTC JavaScript video conferences Package: jitsi-meet-web-config Architecture: all -Depends: openssl, nginx | nginx-extras | apache2 +Depends: openssl, nginx | nginx-full | nginx-extras | apache2 Description: Configuration for web serving of Jitsi Meet Jitsi Meet is a WebRTC JavaScript application that uses Jitsi Videobridge to provide high quality, scalable video conferences. @@ -54,3 +54,7 @@ Architecture: all Depends: ${misc:Depends}, prosody-trunk (>= 1nightly747) | prosody-0.11 | prosody (>= 0.11.2), libssl-dev, luarocks, jitsi-meet-prosody Description: Prosody token authentication plugin for Jitsi Meet +Package: jitsi-meet-turnserver +Architecture: all +Depends: ${misc:Depends}, nginx (>= 1.13.10) | nginx-full (>= 1.13.10) | nginx-extras (>= 1.13.10), jitsi-meet-prosody, coturn, dnsutils +Description: Configures coturn to be used with Jitsi Meet diff --git a/debian/jitsi-meet-prosody.postinst b/debian/jitsi-meet-prosody.postinst index bb7951f97..83cf23f66 100644 --- a/debian/jitsi-meet-prosody.postinst +++ b/debian/jitsi-meet-prosody.postinst @@ -80,6 +80,15 @@ case "$1" in # stores the hostname so we will reuse it later, like in purge db_set jitsi-meet-prosody/jvb-hostname "$JVB_HOSTNAME" + db_get jitsi-meet-prosody/turn-secret + if [ -z "$RET" ] ; then + # 8-chars random secret used for the turnserver + TURN_SECRET=`cat /dev/urandom | tr -dc 'a-zA-Z0-9' | fold -w 8 | head -n 1` + db_set jitsi-meet-prosody/turn-secret "$TURN_SECRET" + else + TURN_SECRET="$RET" + fi + # and we're done with debconf db_stop @@ -97,6 +106,7 @@ case "$1" in sed -i "s/jitmeet.example.com/$JVB_HOSTNAME/g" $PROSODY_HOST_CONFIG sed -i "s/focusSecret/$JICOFO_SECRET/g" $PROSODY_HOST_CONFIG sed -i "s/focusUser/$JICOFO_AUTH_USER/g" $PROSODY_HOST_CONFIG + sed -i "s/__turnSecret__/$TURN_SECRET/g" $PROSODY_HOST_CONFIG if [ ! -f /etc/prosody/conf.d/$JVB_HOSTNAME.cfg.lua ]; then ln -s $PROSODY_HOST_CONFIG /etc/prosody/conf.d/$JVB_HOSTNAME.cfg.lua fi @@ -115,12 +125,14 @@ case "$1" in PROSODY_CONFIG_PRESENT="false" fi - # we always try to create the user 'jvb' and not fail configure if user exists - prosodyctl register jvb $JICOFO_AUTH_DOMAIN $JVB_SECRET || true + USER_EXISTS_CHECK=`prosodyctl adduser jvb@$JICOFO_AUTH_DOMAIN < /dev/null || true` + if [ ! "$USER_EXISTS_CHECK" = "That user already exists" ]; then + prosodyctl register jvb $JICOFO_AUTH_DOMAIN $JVB_SECRET || true + fi # Check whether prosody config has the internal muc, if not add it, # as we are migrating configs - if ! grep -q "internal.auth.$JVB_HOSTNAME" $PROSODY_HOST_CONFIG; then + if [ -f $PROSODY_HOST_CONFIG ] && ! grep -q "internal.auth.$JVB_HOSTNAME" $PROSODY_HOST_CONFIG; then echo -e "\nComponent \"internal.auth.$JVB_HOSTNAME\" \"muc\"" >> $PROSODY_HOST_CONFIG echo -e " storage = \"null\"" >> $PROSODY_HOST_CONFIG echo -e " modules_enabled = { \"ping\"; }" >> $PROSODY_HOST_CONFIG diff --git a/debian/jitsi-meet-prosody.templates b/debian/jitsi-meet-prosody.templates index 18d634b30..fc51949d4 100644 --- a/debian/jitsi-meet-prosody.templates +++ b/debian/jitsi-meet-prosody.templates @@ -28,3 +28,8 @@ Template: jicofo/jicofosecret Type: password _Description: Jicofo Component secret: The secret used to connect to xmpp server as component + +Template: jitsi-meet-prosody/turn-secret +Type: string +_Description: The turn server secret + The secret used to connect to turnserver server. diff --git a/debian/jitsi-meet-turnserver.install b/debian/jitsi-meet-turnserver.install new file mode 100644 index 000000000..ba71b7a97 --- /dev/null +++ b/debian/jitsi-meet-turnserver.install @@ -0,0 +1,2 @@ +doc/debian/jitsi-meet-turn/turnserver.conf /usr/share/jitsi-meet-turnserver/ +doc/debian/jitsi-meet/jitsi-meet.conf /usr/share/jitsi-meet-turnserver/ diff --git a/debian/jitsi-meet-turnserver.links b/debian/jitsi-meet-turnserver.links new file mode 100644 index 000000000..9518f96ac --- /dev/null +++ b/debian/jitsi-meet-turnserver.links @@ -0,0 +1 @@ +/usr/share/jitsi-meet-turnserver/jitsi-meet.conf /etc/nginx/modules-enabled/60-jitsi-meet.conf diff --git a/debian/jitsi-meet-turnserver.postinst b/debian/jitsi-meet-turnserver.postinst new file mode 100644 index 000000000..305b1d66a --- /dev/null +++ b/debian/jitsi-meet-turnserver.postinst @@ -0,0 +1,127 @@ +#!/bin/bash +# postinst script for jitsi-meet-turnserver +# +# see: dh_installdeb(1) + +set -e + +# summary of how this script can be called: +# * `configure' +# * `abort-upgrade' +# * `abort-remove' `in-favour' +# +# * `abort-remove' +# * `abort-deconfigure' `in-favour' +# `removing' +# +# for details, see http://www.debian.org/doc/debian-policy/ or +# the debian-policy package + +case "$1" in + configure) + # loading debconf + . /usr/share/debconf/confmodule + + # try to get host from jitsi-videobridge + db_get jitsi-videobridge/jvb-hostname + if [ -z "$RET" ] ; then + # server hostname + db_set jitsi-videobridge/jvb-hostname "localhost" + db_input critical jitsi-videobridge/jvb-hostname || true + db_go + fi + JVB_HOSTNAME="$RET" + + TURN_CONFIG="/etc/turnserver.conf" + NGINX_CONFIG="/etc/nginx/sites-available/$JVB_HOSTNAME.conf" + JITSI_MEET_CONFIG="/etc/jitsi/meet/$JVB_HOSTNAME-config.js" + + # detect dpkg-reconfigure, just delete old links + db_get jitsi-meet-turnserver/jvb-hostname + JVB_HOSTNAME_OLD=$RET + if [ -n "$RET" ] && [ ! "$JVB_HOSTNAME_OLD" = "$JVB_HOSTNAME" ] ; then + rm -f $TURN_CONFIG + fi + + # this detect only old installations with no nginx + db_get jitsi-meet/jvb-serve || true + if [ ! -f $NGINX_CONFIG -o "$RET" = "true" ] ; then + # nothing to do + echo "" + echo "turnserver not configured as no nginx found to multiplex traffic" + echo "" + db_stop + exit 0 + fi + + # stores the hostname so we will reuse it later, like in purge + db_set jitsi-meet-turnserver/jvb-hostname "$JVB_HOSTNAME" + + # try to get turnserver password + db_get jitsi-meet-prosody/turn-secret + if [ -z "$RET" ] ; then + db_input critical jitsi-meet-prosody/turn-secret || true + db_go + fi + TURN_SECRET="$RET" + + if [[ -f $TURN_CONFIG ]] && ! grep -q "jitsi-meet coturn config" "$TURN_CONFIG" ; then + PUBLIC_IP=$(dig +short myip.opendns.com @resolver1.opendns.com) + cp /usr/share/jitsi-meet-turnserver/turnserver.conf $TURN_CONFIG + sed -i "s/jitsi-meet.example.com/$JVB_HOSTNAME/g" $TURN_CONFIG + sed -i "s/__turnSecret__/$TURN_SECRET/g" $TURN_CONFIG + sed -i "s/__external_ip_address__/$JVB_HOSTNAME/g" $TURN_CONFIG + + # SSL for nginx + db_get jitsi-meet/cert-choice + CERT_CHOICE="$RET" + + if [ "$CERT_CHOICE" = "I want to use my own certificate" ] ; then + db_get jitsi-meet/cert-path-key + CERT_KEY="$RET" + db_get jitsi-meet/cert-path-crt + CERT_CRT="$RET" + + # replace self-signed certificate paths with user provided ones + CERT_KEY_ESC=$(echo $CERT_KEY | sed 's/\./\\\./g') + CERT_KEY_ESC=$(echo $CERT_KEY_ESC | sed 's/\//\\\//g') + sed -i "s/pkey=\/etc\/jitsi\/meet\/.*key/pkey=$CERT_KEY_ESC/g" $TURN_CONFIG + CERT_CRT_ESC=$(echo $CERT_CRT | sed 's/\./\\\./g') + CERT_CRT_ESC=$(echo $CERT_CRT_ESC | sed 's/\//\\\//g') + sed -i "s/cert=\/etc\/jitsi\/meet\/.*crt/cert=$CERT_CRT_ESC/g" $TURN_CONFIG + fi + + sed -i "s/#TURNSERVER_ENABLED/TURNSERVER_ENABLED/g" /etc/default/coturn + invoke-rc.d coturn restart || true + + NGINX_STREAM_CONFIG="/etc/nginx/modules-enabled/60-jitsi-meet.conf" + if [ -f $NGINX_STREAM_CONFIG ] && [ -f $NGINX_CONFIG ] ; then + sed -i "s/listen 443 ssl/listen 4444 ssl http2/g" $NGINX_CONFIG + invoke-rc.d nginx reload || true + fi + + # Enable turn server in config.js + if [ -f $JITSI_MEET_CONFIG ] ; then + sed -i "s/\/\/ useStunTurn: true/useStunTurn: true/g" $JITSI_MEET_CONFIG + fi + fi + + # and we're done with debconf + db_stop + ;; + + abort-upgrade|abort-remove|abort-deconfigure) + ;; + + *) + echo "postinst called with unknown argument \`$1'" >&2 + exit 1 + ;; +esac + +# dh_installdeb will replace this with shell code automatically +# generated by other debhelper scripts. + +#DEBHELPER# + +exit 0 diff --git a/debian/jitsi-meet-turnserver.templates b/debian/jitsi-meet-turnserver.templates new file mode 100644 index 000000000..9d9539442 --- /dev/null +++ b/debian/jitsi-meet-turnserver.templates @@ -0,0 +1,9 @@ +Template: jitsi-meet-turnserver/jvb-hostname +Type: string +_Description: The hostname of the current installation: + The value for the hostname that is set in Jitsi Videobridge installation. + +Template: jitsi-videobridge/jvb-hostname +Type: string +_Description: The hostname of the current installation: + The value for the hostname that is set in Jitsi Videobridge installation. diff --git a/debian/jitsi-meet-web-config.postinst b/debian/jitsi-meet-web-config.postinst index d756858f1..4ce11e50d 100644 --- a/debian/jitsi-meet-web-config.postinst +++ b/debian/jitsi-meet-web-config.postinst @@ -53,9 +53,12 @@ case "$1" in db_set jitsi-meet/jvb-hostname $JVB_HOSTNAME NGINX_INSTALL_CHECK="$(dpkg-query -f '${Status}' -W 'nginx' 2>/dev/null | awk '{print $3}' || true)" + NGINX_FULL_INSTALL_CHECK="$(dpkg-query -f '${Status}' -W 'nginx-full' 2>/dev/null | awk '{print $3}' || true)" NGINX_EXTRAS_INSTALL_CHECK="$(dpkg-query -f '${Status}' -W 'nginx-extras' 2>/dev/null | awk '{print $3}' || true)" if [ "$NGINX_INSTALL_CHECK" = "installed" ] \ || [ "$NGINX_INSTALL_CHECK" = "unpacked" ] \ + || [ "$NGINX_FULL_INSTALL_CHECK" = "installed" ] \ + || [ "$NGINX_FULL_INSTALL_CHECK" = "unpacked" ] \ || [ "$NGINX_EXTRAS_INSTALL_CHECK" = "installed" ] \ || [ "$NGINX_EXTRAS_INSTALL_CHECK" = "unpacked" ] ; then FORCE_NGINX="true" @@ -105,8 +108,6 @@ case "$1" in sed -i "s/jitsi-meet.example.com/$JVB_HOSTNAME/g" $JITSI_MEET_CONFIG fi - JVB_CONFIG="/etc/jitsi/videobridge/sip-communicator.properties" - # this is new install let's configure jvb to serve meet # no-nginx, no-apache installed on machine, this is new install or reconfiguring old one which have jvb_serve set if [[ "$JVB_SERVE" = "true" ]] ; then @@ -121,11 +122,6 @@ case "$1" in echo "------------------------------------------------" echo "" elif [[ "$FORCE_NGINX" = "true" && ( -z "$JVB_HOSTNAME_OLD" || "$RECONFIGURING" = "true" ) ]] ; then - # disables tcp harvester to make sure jvb will not take port 443 - if [[ -f $JVB_CONFIG ]] && ! grep -q "org.jitsi.videobridge.DISABLE_TCP_HARVESTER" "$JVB_CONFIG" ;then - echo "org.jitsi.videobridge.DISABLE_TCP_HARVESTER=true" >> $JVB_CONFIG - invoke-rc.d jvb restart || true - fi # this is a reconfigure, lets just delete old links if [ "$RECONFIGURING" = "true" ] ; then @@ -156,11 +152,6 @@ case "$1" in invoke-rc.d nginx reload || true elif [[ "$FORCE_APACHE" = "true" && ( -z "$JVB_HOSTNAME_OLD" || "$RECONFIGURING" = "true" ) ]] ; then - # disables tcp harvester to make sure jvb will not take port 443 - if [[ -f $JVB_CONFIG ]] && ! grep -q "org.jitsi.videobridge.DISABLE_TCP_HARVESTER" "$JVB_CONFIG" ;then - echo "org.jitsi.videobridge.DISABLE_TCP_HARVESTER=true" >> $JVB_CONFIG - invoke-rc.d jvb restart || true - fi # this is a reconfigure, lets just delete old links if [ "$RECONFIGURING" = "true" ] ; then diff --git a/doc/debian/jitsi-meet-prosody/prosody.cfg.lua-jvb.example b/doc/debian/jitsi-meet-prosody/prosody.cfg.lua-jvb.example index e4af1c5bf..b7f0db42e 100644 --- a/doc/debian/jitsi-meet-prosody/prosody.cfg.lua-jvb.example +++ b/doc/debian/jitsi-meet-prosody/prosody.cfg.lua-jvb.example @@ -3,6 +3,17 @@ plugin_paths = { "/usr/share/jitsi-meet/prosody-plugins/" } -- domain mapper options, must at least have domain base set to use the mapper muc_mapper_domain_base = "jitmeet.example.com"; +turncredentials_secret = "__turnSecret__"; + +turncredentials = { + { type = "stun", host = "jitmeet.example.com", port = "443" }, + { type = "turn", host = "jitmeet.example.com", port = "443", transport = "udp" }, + { type = "turns", host = "jitmeet.example.com", port = "443", transport = "tcp" } +}; + +cross_domain_bosh = false; +consider_bosh_secure = true; + VirtualHost "jitmeet.example.com" -- enabled = false -- Remove this line to enable this host authentication = "anonymous" @@ -25,6 +36,7 @@ VirtualHost "jitmeet.example.com" "pubsub"; "ping"; -- Enable mod_ping "speakerstats"; + "turncredentials"; } c2s_require_encryption = false diff --git a/doc/debian/jitsi-meet-turn/RREADME b/doc/debian/jitsi-meet-turn/RREADME new file mode 100644 index 000000000..c64c0f43c --- /dev/null +++ b/doc/debian/jitsi-meet-turn/RREADME @@ -0,0 +1 @@ +Coturn configuration for Jitsi Meet diff --git a/doc/debian/jitsi-meet-turn/turnserver.conf b/doc/debian/jitsi-meet-turn/turnserver.conf new file mode 100644 index 000000000..c25dfd6f2 --- /dev/null +++ b/doc/debian/jitsi-meet-turn/turnserver.conf @@ -0,0 +1,13 @@ +# jitsi-meet coturn config. Do not modify this line +lt-cred-mech +use-auth-secret +keep-address-family +static-auth-secret=__turnSecret__ +realm=jitsi-meet.example.com +cert=/etc/jitsi/meet/jitsi-meet.example.com.crt +pkey=/etc/jitsi/meet/jitsi-meet.example.com.key + +no-tcp +listening-port=443 +tls-listening-port=4445 +external-ip=__external_ip_address__ diff --git a/doc/debian/jitsi-meet/jitsi-meet.conf b/doc/debian/jitsi-meet/jitsi-meet.conf new file mode 100644 index 000000000..5d2a589f2 --- /dev/null +++ b/doc/debian/jitsi-meet/jitsi-meet.conf @@ -0,0 +1,30 @@ +# this is jitsi-meet nginx module configuration +# this forward all http traffic to the nginx virtual host port +# and the rest to the turn server + +stream { + upstream web { + server 127.0.0.1:4444; + } + upstream turn { + server 127.0.0.1:4445; + } + # since 1.13.10 + map $ssl_preread_alpn_protocols $upstream { + "h2" web; + "http/1.1" web; + "h2,http/1.1" web; + default turn; + } + + server { + listen 443; + + # since 1.11.5 + ssl_preread on; + proxy_pass $upstream; + + # Increase buffer to serve video + proxy_buffer_size 10m; + } +} diff --git a/doc/debian/jitsi-meet/jitsi-meet.example b/doc/debian/jitsi-meet/jitsi-meet.example index bd743d588..290083407 100644 --- a/doc/debian/jitsi-meet/jitsi-meet.example +++ b/doc/debian/jitsi-meet/jitsi-meet.example @@ -3,7 +3,17 @@ server_names_hash_bucket_size 64; server { listen 80; server_name jitsi-meet.example.com; - return 301 https://$host$request_uri; + + location ^~ /.well-known/acme-challenge/ { + default_type "text/plain"; + root /usr/share/jitsi-meet; + } + location = /.well-known/acme-challenge/ { + return 404; + } + location / { + return 301 https://$host$request_uri; + } } server { listen 443 ssl; diff --git a/doc/quick-install.md b/doc/quick-install.md index 936c8e11c..ebd74e836 100644 --- a/doc/quick-install.md +++ b/doc/quick-install.md @@ -6,7 +6,7 @@ Debian Wheezy and other older systems may require additional things to be done. Also note that a recent default Ubuntu installation has only the `main` repository enabled, and Jitsi Meet needs packages from `universe`. Check your `/etc/apt/sources.list` file, and if `universe` is not present refer to [Ubuntu's documentation](https://help.ubuntu.com/community/Repositories/Ubuntu) on how to enable it. (Usually it amounts to copying the `main` lines and changing to `universe`.) -N.B.: +N.B.: a.) All commands are supposed to be run by root. If you are logged in as a regular user with sudo rights, please prepend ___sudo___ to each of the commands. @@ -46,7 +46,7 @@ During the installation, you will be asked to enter the hostname of the Jitsi Me This hostname (or IP address) will be used for virtualhost configuration inside the Jitsi Meet and also, you and your correspondents will be using it to access the web conferences. -### Generate a Let's Encrypt certificate +### Generate a Let's Encrypt certificate Simply run the following in your shell @@ -109,7 +109,7 @@ Enjoy! ## Uninstall ```sh -apt-get purge jigasi jitsi-meet jitsi-meet-web-config jitsi-meet-prosody jitsi-meet-web jicofo jitsi-videobridge +apt-get purge jigasi jitsi-meet jitsi-meet-web-config jitsi-meet-prosody jitsi-meet-turnserver jitsi-meet-web jicofo jitsi-videobridge ``` Sometimes the following packages will fail to uninstall properly: diff --git a/resources/install-letsencrypt-cert.sh b/resources/install-letsencrypt-cert.sh index 267eceff4..1163cf402 100755 --- a/resources/install-letsencrypt-cert.sh +++ b/resources/install-letsencrypt-cert.sh @@ -57,6 +57,15 @@ if [ -f /etc/nginx/sites-enabled/$DOMAIN.conf ] ; then echo "service nginx reload" >> $CRON_FILE service nginx reload + TURN_CONFIG="/etc/turnserver.conf" + if [ -f $TURN_CONFIG ] && grep -q "jitsi-meet coturn config" "$TURN_CONFIG" ; then + echo "Configuring turnserver" + sed -i "s/cert=\/etc\/jitsi\/meet\/.*crt/cert=$CERT_CRT_ESC/g" $TURN_CONFIG + sed -i "s/pkey=\/etc\/jitsi\/meet\/.*key/pkey=$CERT_KEY_ESC/g" $TURN_CONFIG + + echo "service coturn restart" >> $CRON_FILE + service coturn restart + fi elif [ -f /etc/apache2/sites-enabled/$DOMAIN.conf ] ; then ./certbot-auto certonly --noninteractive \ diff --git a/resources/prosody-plugins/mod_turncredentials.lua b/resources/prosody-plugins/mod_turncredentials.lua new file mode 100644 index 000000000..9648385b7 --- /dev/null +++ b/resources/prosody-plugins/mod_turncredentials.lua @@ -0,0 +1,80 @@ +-- XEP-0215 implementation for time-limited turn credentials +-- Copyright (C) 2012-2014 Philipp Hancke +-- This file is MIT/X11 licensed. + +--turncredentials_secret = "keepthissecret"; +--turncredentials = { +-- { type = "stun", host = "8.8.8.8" }, +-- { type = "turn", host = "8.8.8.8", port = "3478" }, +-- { type = "turn", host = "8.8.8.8", port = "80", transport = "tcp" } +--} +-- for stun servers, host is required, port defaults to 3478 +-- for turn servers, host is required, port defaults to tcp, +-- transport defaults to udp +-- hosts can be a list of server names / ips for random +-- choice loadbalancing + +local st = require "util.stanza"; +local hmac_sha1 = require "util.hashes".hmac_sha1; +local base64 = require "util.encodings".base64; +local os_time = os.time; +local secret = module:get_option_string("turncredentials_secret"); +local ttl = module:get_option_number("turncredentials_ttl", 86400); +local hosts = module:get_option("turncredentials") or {}; +if not (secret) then + module:log("error", "turncredentials not configured"); + return; +end + +module:add_feature("urn:xmpp:extdisco:1"); + +function random(arr) + local index = math.random(1, #arr); + return arr[index]; +end + + +module:hook_global("config-reloaded", function() + module:log("debug", "config-reloaded") + secret = module:get_option_string("turncredentials_secret"); + ttl = module:get_option_number("turncredentials_ttl", 86400); + hosts = module:get_option("turncredentials") or {}; +end); + +module:hook("iq-get/host/urn:xmpp:extdisco:1:services", function(event) + local origin, stanza = event.origin, event.stanza; + if origin.type ~= "c2s" then + return; + end + local now = os_time() + ttl; + local userpart = tostring(now); + local nonce = base64.encode(hmac_sha1(secret, tostring(userpart), false)); + local reply = st.reply(stanza):tag("services", {xmlns = "urn:xmpp:extdisco:1"}) + for idx, item in pairs(hosts) do + if item.type == "stun" or item.type == "stuns" then + -- stun items need host and port (defaults to 3478) + reply:tag("service", + { type = item.type, host = item.host, port = tostring(item.port) or "3478" } + ):up(); + elseif item.type == "turn" or item.type == "turns" then + local turn = {} + -- turn items need host, port (defaults to 3478), + -- transport (defaults to udp) + -- username, password, ttl + turn.type = item.type; + turn.port = tostring(item.port); + turn.transport = item.transport; + turn.username = userpart; + turn.password = nonce; + turn.ttl = tostring(ttl); + if item.hosts then + turn.host = random(item.hosts) + else + turn.host = item.host + end + reply:tag("service", turn):up(); + end + end + origin.send(reply); + return true; +end);