Skip to main content
Version: 2.7

This document covers how to set up a TURN server for BigBlueButton to allow users behind restrictive firewalls to connect.

BBB 2.6 now includes coturn for TURN server

Starting with BigBlueButton version 2.6, the default installation process includes coturn, a TURN server that allows users behind restrictive firewalls to connect. You don't need any additional configuration changes unless you want to override the default configuration.

This document covers how to set up a TURN server for previous versions of BigBlueButton.

Setup a TURN server

BigBlueButton normally requires a wide range of UDP ports to be available for WebRTC communication. In some network restricted sites or development environments, such as those behind NAT or a firewall that restricts outgoing UDP connections, users may be unable to make outgoing UDP connections to your BigBlueButton server.

The TURN protocol is designed to allow UDP-based communication flows like WebRTC to bypass NAT or firewalls by having the client connect to the TURN server, and then have the TURN server connect to the destination on their behalf.

In addition, the TURN server implements the STUN protocol as well, used to allow direct UDP connections through certain types of firewalls which otherwise might not work.

Using a TURN server under your control improves the success of connections to BigBlueButton and also improves user privacy, since they will no longer be sending IP address information to a public STUN server.

Required Hardware

The TURN protocol is not CPU or memory intensive. Additionally, since it's only used during connection setup (for STUN) and as a fallback for users who would otherwise be unable to connect, the bandwidth requirements aren't particularly high. For a moderate number of BigBlueButton servers, a single small VPS is usually sufficient.

Having multiple IP addresses may improve the results when using STUN with certain types of firewalls, but is not usually necessary.

Having the server behind NAT (for example, on Amazon EC2) is OK, but all incoming UDP and TCP connections on any port must be forwarded and not firewalled.

Required Software

We recommend using a minimal server installation of Ubuntu 20.04. The coturn software requires port 443 for its exclusive use in our recommended configuration, which means the server cannot have any dashboard software or other web applications running.

Stable versions of coturn are already available in the Ubuntu packaging repositories for version 20.04 and later, and it can be installed with apt-get:

$ sudo apt-get update
$ sudo apt-get install coturn

Note: coturn will not automatically start until configuration is applied (see below).

Required DNS Entry

You need to set up a fully qualified domain name that resolves to the external IP address of your turn server. You'll use this domain name to generate a TLS certificate using Let's Encrypt (next section).

Required Ports

On the coturn server, you need to have the following ports (in addition port 22) available for BigBlueButton clients to connect (port 3478 and 443) and for coturn to connect to your BigBlueButton server (32769 - 65535).

PortsProtocolDescription
3478TCP/UDPcoturn listening port
443TCP/UDPTLS listening port
32769-65535UDPrelay ports range

Generating TLS certificates

You can use certbot from Let's Encrypt to easily generate free TLS certificates. To setup certbot enter the following commands on your TURN server (not your BigBlueButton server).

$ sudo add-apt-repository ppa:certbot/certbot
$ sudo apt-get update
$ sudo apt-get install certbot

You can then run a certbot command like the following to generate the certificate, replacing <turn.example.com> with the domain name of your TURN server:

$ sudo certbot certonly --standalone --preferred-challenges http \
-d <turn.example.com>

Be aware that TCP 80 needs to be open temporarily to get it to work.

Current versions of the certbot command set up automatic renewal by default. To ensure that the certificates are readable by coturn, which runs as the turnserver user, add the following renewal-hook to Let's Encrypt. First, create the directory /etc/letsencrypt/renewal-hooks/deploy.

$ sudo mkdir -p /etc/letsencrypt/renewal-hooks/deploy

Next, create the file /etc/letsencrypt/renewal-hooks/deploy/coturn with the following contents. Replace <turn.example.com> with the hostname of your TURN server.

#!/bin/bash -e
for certfile in fullchain.pem privkey.pem ; do
cp -L /etc/letsencrypt/live/<turn.example.com>/"${certfile}" /etc/turnserver/"${certfile}".new
chown turnserver:turnserver /etc/turnserver/"${certfile}".new
mv /etc/turnserver/"${certfile}".new /etc/turnserver/"${certfile}"
done
systemctl kill -sUSR2 coturn.service

Make this file executable.

$ sudo chmod 0755 /etc/letsencrypt/renewal-hooks/deploy/coturn

Configure coturn

coturn configuration is stored in the file /etc/turnserver.conf. There are a lot of options available, all documented in comments in the default configuration file. We include a sample configuration below with the recommended settings (refer to the default configuration file for more information on the settings)..

Use the file below for /etc/turnserver.conf and make the following changes:

  • Replace <turn.example.com> with the hostname of your TURN server, and
  • Replace <example.com> with the realm of your TURN server, and
  • Replace <secret_value> to a random value for a shared secret (you can generate one by running openssl rand -hex 16)
  • Replace <IP> with the external IP of your TURN server

This configuration file assumes your TURN server is not behind NAT and has a public IP address.

listening-port=3478
tls-listening-port=443

listening-ip=<IP>
relay-ip=<IP>

# If the server is behind NAT, you need to specify the external IP address.
# If there is only one external address, specify it like this:
#external-ip=172.17.19.120
# If you have multiple external addresses, you have to specify which
# internal address each corresponds to, like this. The first address is the
# external ip, and the second address is the corresponding internal IP.
#external-ip=172.17.19.131/10.0.0.11
#external-ip=172.17.18.132/10.0.0.12

min-port=32769
max-port=65535
verbose

fingerprint
lt-cred-mech
use-auth-secret
static-auth-secret=<secret_value>
realm=<example.com>

cert=/etc/turnserver/fullchain.pem
pkey=/etc/turnserver/privkey.pem
# From https://ssl-config.mozilla.org/ Intermediate, openssl 1.1.0g, 2020-01
cipher-list="ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384"
dh-file=/etc/turnserver/dhp.pem

keep-address-family
no-cli
no-tlsv1
no-tlsv1_1

# Block connections to IP ranges which shouldn't be reachable
no-loopback-peers
no-multicast-peers
# CVE-2020-26262
# If running coturn version older than 4.5.2, uncomment these rules and ensure
# that you have listening-ip set to ipv4 addresses only.
#denied-peer-ip=0.0.0.0-0.255.255.255
#denied-peer-ip=127.0.0.0-127.255.255.255
#denied-peer-ip=::1
# Private (LAN) addresses
# If you are running BigBlueButton within a LAN, you might need to add an "allow" rule for your address range.
# IPv4 Private-Use
denied-peer-ip=10.0.0.0-10.255.255.255
denied-peer-ip=172.16.0.0-172.31.255.255
denied-peer-ip=192.168.0.0-192.168.255.255
# Other IPv4 Special-Purpose addresses
denied-peer-ip=100.64.0.0-100.127.255.255
denied-peer-ip=169.254.0.0-169.254.255.255
denied-peer-ip=192.0.0.0-192.0.0.255
denied-peer-ip=192.0.2.0-192.0.2.255
denied-peer-ip=198.18.0.0-198.19.255.255
denied-peer-ip=198.51.100.0-198.51.100.255
denied-peer-ip=203.0.113.0-203.0.113.255
# IPv6 Unique-Local
denied-peer-ip=fc00::-fdff:ffff:ffff:ffff:ffff:ffff:ffff:ffff
# IPv6 Link-Local Unicast
denied-peer-ip=fe80::-febf:ffff:ffff:ffff:ffff:ffff:ffff:ffff
# Other IPv6 Special-Purpose assignments
denied-peer-ip=::ffff:0:0-::ffff:ffff:ffff
denied-peer-ip=64:ff9b::-64:ff9b::ffff:ffff
denied-peer-ip=64:ff9b:1::-64:ff9b:1:ffff:ffff:ffff:ffff:ffff
denied-peer-ip=2001::-2001:1ff:ffff:ffff:ffff:ffff:ffff:ffff
denied-peer-ip=2001:db8::-2001:db8:ffff:ffff:ffff:ffff:ffff:ffff
denied-peer-ip=2002::-2002:ffff:ffff:ffff:ffff:ffff:ffff:ffff

We need to create dph.pem file,

$ sudo mkdir -p /etc/turnserver
$ sudo openssl dhparam -dsaparam -out /etc/turnserver/dhp.pem 2048

To increase the file handle limit for the TURN server and to give it the ability to bind to port 443, add the following systemd override file. First, create the directory.

$ sudo mkdir -p /etc/systemd/system/coturn.service.d

and then create /etc/systemd/system/coturn.service.d/override.conf with the following contents

[Service]
LimitNOFILE=1048576
AmbientCapabilities=CAP_NET_BIND_SERVICE
ExecStart=
ExecStart=/usr/bin/turnserver --daemon -c /etc/turnserver.conf --pidfile /run/turnserver/turnserver.pid --no-stdout-log --simple-log --log-file /var/log/turnserver/turnserver.log
Restart=always

Configure Log Rotation

To rotate the logs for coturn, install the following configuration file to /etc/logrotate.d/coturn

/var/log/turnserver/*.log
{
rotate 7
daily
missingok
notifempty
compress
postrotate
/bin/systemctl kill -s HUP coturn.service
endscript
}

And create the associated log directory

$ sudo mkdir -p /var/log/turnserver
$ sudo chown turnserver:turnserver /var/log/turnserver

Restart coturn

With the above steps completed, restart the TURN server

$ sudo /etc/letsencrypt/renewal-hooks/deploy/coturn    # Initial copy of certificates
$ sudo systemctl daemon-reload # Ensure the override file is loaded
$ sudo systemctl restart coturn # Restart

Ensure that the coturn has binded to port 443 with netstat -antp | grep 443. Also restart your TURN server and ensure that coturn is running (and binding to port 443 after restart).

Configure BigBlueButton to use your TURN server

You must configure bbb-web so that it will provide the list of turn servers to the web browser. Edit the file /etc/bigbluebutton/turn-stun-servers.xml using the contents below and make edits:

  • replace both instances of <turn.example.com> with the hostname of the TURN server, and
  • replace <secret_value> with the secret you configured in turnserver.conf.
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-2.5.xsd">

<bean id="stun0" class="org.bigbluebutton.web.services.turn.StunServer">
<constructor-arg index="0" value="stun:<turn.example.com>"/>
</bean>


<bean id="turn0" class="org.bigbluebutton.web.services.turn.TurnServer">
<constructor-arg index="0" value="<secret_value>"/>
<constructor-arg index="1" value="turns:<turn.example.com>:443?transport=tcp"/>
<constructor-arg index="2" value="86400"/>
</bean>

<bean id="turn1" class="org.bigbluebutton.web.services.turn.TurnServer">
<constructor-arg index="0" value="<secret_value>"/>
<constructor-arg index="1" value="turn:<turn.example.com>:443?transport=tcp"/>
<constructor-arg index="2" value="86400"/>
</bean>

<bean id="stunTurnService"
class="org.bigbluebutton.web.services.turn.StunTurnService">
<property name="stunServers">
<set>
<ref bean="stun0"/>
</set>
</property>
<property name="turnServers">
<set>
<ref bean="turn0"/>
<ref bean="turn1"/>
</set>
</property>
</bean>
</beans>

Restart your BigBlueButton server to apply the changes.

Going forward, when users connect behind a restrictive firewall that prevents outgoing UDP connections, the TURN server will enable BigBlueButton to connect to FreeSWITCH and Kurento via the TURN server through port 443 on their firewall.

Test your TURN server

By default, your browser will try to connect directly to Kurento or FreeSWITCH using WebRTC. If it is unable to make a direct connection, it will fall back to using the TURN server as one of the interconnectivity connectivity exchange (ICE) candidates to relay the media.

Use FireFox to test your TURN server. FireFox allows you to disable direct connections and require fallback to your TURN server. Launch FireFox, open about:config, and search for 'relay. You should see a parameter media.peerconnection.ice.relay_only. Set this value to true`.

With FireFox configured to only use a TURN server, open a new tab and join a BigBlueButton session, and share your webcam. If your webcam appears, you can verify that FireFox is using your TURN server by opening a new tab and choosing about:webrtc. Click show details and you'll see a table for ICE Stats. The successful connection, shown at the top of the table, should have (relay-tcp) in the Local Candidate column. This means the video connection was successfully relayed through your TURN server.

If, however, you received a 1020 (unable to establish connection) when sharing a webcam the browser may not be able to connect to the TURN server or the TURN server is not running or configured correctly. Check the browser console in FireFox. If you see

WebRTC: ICE failed, your TURN server appears to be broken, see about:webrtc for more details

then FireFox was unable to communicate with your TURN server, or your TURN server was not running or configured correctly.

To ensure that your firewall is not blocking UDP connections over port 443, open a new tad visit https://test.bigbluebutton.org/, launch a test session, and try sharing your webcam. the browser may not be able to connect to the TURN server or the TURN server is not running or configured correctly.

The TURN server also acts as a STUN server, so you can first check that the STUN portion is working using the stunclient. Run the following commands below and substitute <youor-turn-server-host> with the hostname of your TURN server.

sudo apt-get install -y stuntman-client
stunclient --mode full --localport 30000 <your-turn-server-host> 3478

If successful, you should see output for stunclient should be similar to the following.

Binding test: success
Local address: xxx.xxx.xxx.xxx:30000
Mapped address: xxx.xxx.xxx.xxx:30000
Behavior test: success
Nat behavior: Direct Mapping
Filtering test: success
Nat filtering: Endpoint Independent Filtering

If you get an error, check that coturn is running on the TURN server using systemctl status coturn.service. Check the logs by doing tail -f /var/log/turnserver/coturn.log. You can get verbose logs by adding verbose to /etc/turnserver.conf and restarting the TURN server systemctl restart coturn.service

You can test your TURN server using the trickle ICE page. This gives you a log of the relay candidates as they are returned from ICE gathering. To test using this page, you need to generate some test credentials. The following BASH script generates them by reading your /etc/turnserver.conf. Alternatively, set HOST=<HOSTNAME> and SECRET=<SECRET> to your TURN servers credentials.

#!/bin/bash

HOST=$(cat /etc/turnserver.conf | grep realm | sed 's/realm=//')
SECRET=$(cat /etc/turnserver.conf | grep static-auth-secret | sed 's/static-auth-secret=//')

time=$(date +%s)
expiry=8400
username=$(( $time + $expiry ))

echo
echo " https://webrtc.github.io/samples/src/content/peerconnection/trickle-ice/"
echo
echo URI : turn:$HOST:443
echo username : $username
echo password : $(echo -n $username | openssl dgst -binary -sha1 -hmac $SECRET | openssl base64)
echo

Enter the values into URI, username, and password into the trickle ICE page and click 'Gather candidates'. You should see a list of relay candidates. If you don't, again check that your TURN server is running and tail the logs TURN server logs via tail -f /var/log/turnserver/coturn.log or journalctl -f -u coturn.service.

You can get verbose logs by adding verbose to /etc/turnserver.conf and then restarting the TURN server systemctl restart coturn.service, and try testing again from FireFox or the above Tricke ICE page.