Turn-Server for Nextcloud Talk
Nextcloud, my self-hosted Sync-Solution also offers conferencing services. It is currently installed on my luks-Encrypted Server (which is an old laptop), so not a scalable solution but a pretty decent one for all the Stuff I am running and the amount of storage I get from self-hosting.
Nextcloud Talk is a free conference service which is very easily installed if you already have a Nextcloud. If you don’t, you can still use Matrix’s Jitsi plugin, host your own Jitsi or BigBlueButton instance. All those solutions have in common, that for a proper setup they need a TURN-Server to operate with clients behind a NAT or a Router (which is always the case if you don’t talk with someone on your local network).
What is a TURN-Server
There is TURN and STUN but coturn provides both and for further information you can look into Wikipedia.
Installing coturn
Coturn can be installed with apt from the official repositories but as everything except for my nginx is running in docker, we will use docker again.
I am using the docker-container by instrumentisto which seems quite well-supported.
StrukturAG, the company which maintains the Nextcloud Talk Backend also provides one, but I liked the configuration of this one more.
Firewall settings / port forwarding
The ports are a little tricky. There is
- 3478 (TCP and UDP) for normal access
- 5349 (TCP and UDP) for packing the stream into another layer of TLS so that it gets through most corporate firewalls
- 49160-49200 (UDP) as media ports for streams (I configured the exact ports below)
This has to be set in the configuration, UFW and in my FritzBox! so it is important not to forget anything. The forwarding rules have to be set for TCP and UDP separately.
Configuration
So here is the configuration part of my docker-compose file.
I don’t really understand why the detect-external-ip
script is not needed, but it works as good without knowing its external IP, which is good, as it changes every day.
I tried using the port range in docker, but somehow it didn’t correctly set up the ports, but it worked when using host mode, so I just left it with the working host mode solution.
I had a problem when setting up TLS, as the symlinks are relative to the letsencrypt root. I wonder if this counts as best practice to simply link the global TLS-keys readonly into the docker, but I currently don’t know a better way for this approach.
Docker-compose:
coturn:
image: instrumentisto/coturn
restart: always
container_name: coturn
command:
- -c /etc/coturn/turnserver.conf
- --static-auth-secret=${COTURN_SECRET}
- --log-file=stdout
# - --external-ip=$$(detect-external-ip) #obviously not needed
# two $$ for docker-compose
network_mode: "host"
# port range not working 2020/03/26 - use host mode
# ports:
# - 3478:3478/tcp
# - 3478:3478/udp
# - 5349:5349/tcp
# - 5349:5349/udp
# - "49160-49200:49160-49200/udp"
volumes:
- ./conf/turnserver.conf:/etc/coturn/turnserver.conf:ro
- /etc/letsencrypt:/certs:ro
# letsencrypt root needed so that symlinks are still working
Showing my configs around will probably once backfire when I get hacked but so far I trust my configuration and most importantly OpenSSL and SSH enough to minimize the risk for such an event. But who knows.
Coturn-Config:
no-cli
listening-port=3478
tls-listening-port=5349
listening-ip=192.168.178.10
userdb=/var/lib/coturn/turndb
use-auth-secret
realm=datensch.eu
server-name=datensch.eu
# this limits the needed media ports
min-port=49160
max-port=49200
user-quota=100
total-quota=360
cert=/certs/live/datensch.eu/fullchain.pem
pkey=/certs/live/datensch.eu/privkey.pem
no-multicast-peers
pidfile="/var/run/turnserver.pid"
mobility
no-tlsv1
no-tlsv1_1 # use tlsv1_2 and up only
fingerprint
Result
After this I can now video chat with my friends on my own private solution. Even my grandfather who is older than 90 years old could do it, so this is approved ;)
My server is capable to host p2p conversations with about 3-4 persons at once, but the limiting factor is the client CPU (which has to decode 3 streams and encoding 1 stream at the same time).
Using the native Android or iOS App helps a little, but you can get the best experience with a laptop or PC.
The general approach to this limitation is to set up a backend server which helps with the decoding of all participants. This results into the loss of p2p, which means that the stream is not e2e encrypted as the backend-server can read streams too.
Jitsi and BigBlueButton are using exactly this way to serve WebRTC and Skype/Teams, Zoom and Discord are probably using a very similar functionality for their streaming backends.
Update 19th May 2020
StrukturAG open-sourced the High Performance Backend of Nextcloud. Which is the current signaling server which handles the above stated limitations.
I will try to set it up in the next weeks, but it seems to be a lot of configuration and opening ports (janus, nats and the spreed-signaling server is needed). For bigger deployments it would be probably best to have a dedicated video chat server to separate the video chat solution.