Skip to content

Instantly share code, notes, and snippets.

@maxidorius
Last active June 20, 2024 17:48
Show Gist options
  • Save maxidorius/2b0acc2e707ae9a2d6d0267026a1024f to your computer and use it in GitHub Desktop.
Save maxidorius/2b0acc2e707ae9a2d6d0267026a1024f to your computer and use it in GitHub Desktop.
Working config for VoIP in Matrix: synapse + coturn

This configuration is provided AS-IS and as an example/reference for those who do not find a working configuration for themselves. It is not always kept up to date and no support is provided.

Assuming:

  • Your Matrix domain: example.org
  • Your TURN domain (arbitrary): turn.example.org
  • Your Public IP: 1.2.3.4
  • Your Private IP for the box hosing the services: 10.11.12.13
  • A shared secret between synapse and coturn: ThisIsASharedSecret-ChangeMe
  • You want Firefox compatiblity (TURNS only is not supported)

synapse

homeserver.yaml:

## Turn ##

# The public URIs of the TURN server to give to clients
turn_uris:
  - "turns:turn.example.org?transport=udp"
  - "turns:turn.example.org?transport=tcp"
  - "turn:turn.example.org?transport=udp"
  - "turn:turn.example.org?transport=tcp"

# The shared secret used to compute passwords for the TURN server
turn_shared_secret: "ThisIsASharedSecret-ChangeMe"

# How long generated TURN credentials last
turn_user_lifetime: "1h"

coturn

turnserver.conf:

syslog

lt-cred-mech
use-auth-secret
static-auth-secret=ThisIsASharedSecret-ChangeMe
realm=example.org

cert=/etc/letsencrypt/live/turn.example.org/fullchain.pem
pkey=/etc/letsencrypt/live/turn.example.org/privkey.pem

no-udp
external-ip=1.2.3.4
min-port=64000
max-port=65535

Firewall

Allow ports:

  • TCP 3478
  • UDP 3478
  • TCP 3479
  • UDP 3479
  • TCP 5349
  • UDP 5349
  • UDP 64000 to 65535
@Serphentas
Copy link

For anyone wondering, the important bit for users behind NAT is the external-ip key

@maxidorius
Copy link
Author

maxidorius commented Apr 6, 2020

Firefox has a bug that prevents it from using only TURNS when this gist was originally written, and last I tried some weeks ago, the bug was still in there, so for full client compatibility, TURN is still mandatory. That means that some data is not encrypted sadly. I've made a point about it in the gist, thank you.

Also fixed the typo, thank you for reminding me about it, seems like I forgot to fix it at the time of the first comment.

As for 3479, it is a RFC requirement to have the +1 port usable, so following the RFC on that. Is there an updated RFC on that front I missed maybe?

@Serphentas
Copy link

Are we talking about RFC5766 ? I can't find any reference to a +1 port. But yeah, without it it works just fine, so YMMV.

Have you tried to see how much is sent on TURN before TURNS ? I'm gonna look with Wireshark out of curiosity.

@maxidorius
Copy link
Author

I would have to find the good RFC again, it's been a while... I'll try :)

As for TURN, it's not the voice data, but the exchange of peers and the like (handshake, channel creation, etc.) so MitM is possible and would be the typical vector of attack. If you try to only use TURNS URI with Chrome it works fine per example.

@mowtheair
Copy link

Are these the required settings to get riot.im voice calls to work from outside the firewall? Internally I'm good, but externally it is not working. Just trying to see if this is the right path... thanks!

@Serphentas
Copy link

@mowtheair I suppose you run a Synapse server behind your firewall ? If so, my configuration that works:

  • dstnat HTTPS for Synapse
  • dstnat TURN (TCP+UDP), 3478 & 5349, UDP range as per settings

@mowtheair
Copy link

mowtheair commented Apr 17, 2020 via email

@maxidorius
Copy link
Author

maxidorius commented Apr 17, 2020

Jitsi is not related to this. This is for direct voice (irrelevant of the server or the client) based on TURN. Please make your own gist if you want to discuss specifics about jitsi.

@AnonymousWebHacker
Copy link

install matrix-synapse and by default it allows me to make calls and video calls on me, but it gives me an error when calling the ip's that are by NAT, how can I fix that? I did what was explained in this post, and it didn't work.

@whowantsmybigdata
Copy link

thanks for the post! Unfortunately I cant get it to work and have no idea why.
Even with setting "fallback-assistant" to switch to turn.matrix.org if nescessary in Riot (F-Droid) any calls/videocalls are stuck in "establishing connection".
When typing: https://myserver.com:8448, I get the: "It works! Synapse is running.". When just typing https://myserver.com, I only get "Welcome to nginx". Chatting with text and media is possible, though!
The only errors i found where in nginx error.log but after setting a .well-known-location as mentioned here: matrix-org/synapse#7077

i still get this:

2020/06/08 01:11:25 [error] 23515#23515: *7 open() "/usr/share/nginx/html/.well-known/matrix/client" failed (2: No such file or directory), client: [IP-removed], server: [domain-removed], request: "GET /.well-known/matrix/client HTTP/2.0", host: "[domain-removed]" 2020/06/08 01:11:45 [error] 23515#23515: *30 open() "/usr/share/nginx/html/.well-known/matrix/client" failed (2: No such file or directory), client: [IP-removed], server: [domain-removed], request: "GET /.well-known/matrix/client HTTP/2.0", host: "[domain-removed]" 2020/06/08 01:11:45 [error] 23515#23515: *47 open() "/usr/share/nginx/html/.well-known/matrix/client" failed (2: No such file or directory), client: [IP-removed], server: [domain-removed], request: "GET /.well-known/matrix/client HTTP/2.0", host: "[domain-removed]" 2020/06/08 01:35:02 [error] 23887#23887: *1 upstream prematurely closed connection while reading response header from upstream, client: [IP-removed], server: [domain-removed], request: "GET /_matrix/client/r0/sync?filter=0&timeout=30000&since=s631_945_0_469_11_20_664_98_1 HTTP/2.0", upstream: "http://[::1]:8008/_matrix/client/r0/sync?filter=0&timeout=30000&since=s631_945_0_469_11_20_664_98_1", host: "[domain-removed]" 2020/06/08 01:35:02 [error] 23887#23887: *70 upstream prematurely closed connection while reading response header from upstream, client: [IP-removed], server: [domain-removed], request: "GET /_matrix/client/r0/sync?filter=2&set_presence=offline&timeout=30000&since=s631_945_0_469_11_20_664_98_1 HTTP/2.0", upstream: "http://127.0.0.1:8008/_matrix/client/r0/sync?filter=2&set_presence=offline&timeout=30000&since=s631_945_0_469_11_20_664_98_1", host: "[domain-removed]" 2020/06/08 01:35:02 [error] 23887#23887: *70 no live upstreams while connecting to upstream, client: [IP-removed], server: [domain-removed], request: "GET /_matrix/client/r0/sync?filter=2&set_presence=offline&timeout=30000&since=s631_945_0_469_11_20_664_98_1 HTTP/2.0", upstream: "http://localhost/_matrix/client/r0/sync?filter=2&set_presence=offline&timeout=30000&since=s631_945_0_469_11_20_664_98_1", host: "[domain-removed]" 2020/06/08 01:35:02 [error] 23887#23887: *14 upstream prematurely closed connection while reading response header from upstream, client: [IP-removed], server: [domain-removed], request: "GET /_matrix/client/r0/sync?filter=0&timeout=30000&since=s631_945_0_469_11_20_664_98_1 HTTP/2.0", upstream: "http://[::1]:8008/_matrix/client/r0/sync?filter=0&timeout=30000&since=s631_945_0_469_11_20_664_98_1", host: "[domain-removed]" 2020/06/08 01:35:02 [error] 23887#23887: *14 no live upstreams while connecting to upstream, client: [IP-removed], server: [domain-removed], request: "GET /_matrix/client/r0/sync?filter=0&timeout=30000&since=s631_945_0_469_11_20_664_98_1 HTTP/2.0", upstream: "http://localhost/_matrix/client/r0/sync?filter=0&timeout=30000&since=s631_945_0_469_11_20_664_98_1", host: "[domain-removed]" 2020/06/08 01:35:02 [error] 23887#23887: *1 recv() failed (104: Connection reset by peer) while reading response header from upstream, client: [IP-removed], server: [domain-removed], request: "GET /_matrix/client/r0/sync?filter=0&timeout=30000&since=s631_945_0_469_11_20_664_98_1 HTTP/2.0", upstream: "http://127.0.0.1:8008/_matrix/client/r0/sync?filter=0&timeout=30000&since=s631_945_0_469_11_20_664_98_1", host: "[domain-removed]" 2020/06/08 01:35:02 [error] 23887#23887: *1 no live upstreams while connecting to upstream, client: [IP-removed], server: [domain-removed], request: "OPTIONS /_matrix/client/r0/joined_groups HTTP/2.0", upstream: "http://localhost/_matrix/client/r0/joined_groups", host: "[domain-removed]" 2020/06/08 01:35:08 [error] 23887#23887: *1 no live upstreams while connecting to upstream, client: [IP-removed], server: [domain-removed], request: "GET /_matrix/client/versions HTTP/2.0", upstream: "http://localhost/_matrix/client/versions", host: "[domain-removed]" 2020/06/08 01:35:12 [error] 23887#23887: *245 no live upstreams while connecting to upstream, client: [IP-removed], server: [domain-removed], request: "POST /_matrix/client/r0/rooms/[room-removed]:[domain-removed]/read_markers HTTP/2.0", upstream: "http://localhost/_matrix/client/r0/rooms/[room-removed]:[domain-removed]/read_markers", host: "[domain-removed]" 2020/06/08 01:35:12 [error] 23887#23887: *14 no live upstreams while connecting to upstream, client: [IP-removed], server: [domain-removed], request: "GET /_matrix/client/r0/sync?filter=0&timeout=30000&since=s631_945_0_469_11_20_664_98_1 HTTP/2.0", upstream: "http://localhost/_matrix/client/r0/sync?filter=0&timeout=30000&since=s631_945_0_469_11_20_664_98_1", host: "[domain-removed]" 2020/06/08 01:42:01 [alert] 23887#23887: *1 open socket #3 left in connection 9 2020/06/08 01:42:01 [alert] 23887#23887: *14 open socket #24 left in connection 14 2020/06/08 01:42:01 [alert] 23887#23887: *418 open socket #23 left in connection 40 2020/06/08 01:42:01 [alert] 23887#23887: *416 open socket #21 left in connection 41 2020/06/08 01:42:01 [alert] 23887#23887: aborting

Any ideas?! Thanks

@maxidorius
Copy link
Author

These settings are just provided as-is, without any kind of warranty or support. It's just what works for us and for most setups.

@AnonymousWebHacker
Copy link

AnonymousWebHacker commented Jun 10, 2020

This solution for me
##############################################################################
allowed-peer-ip=0.0.0.0-255.255.255.255 (For all, but no resolv) :(
###########[ Rang My Network ]##########
allowed-peer-ip=144.100.1.0-144.100.4.0
allowed-peer-ip=144.100.1.0-144.100.1.255
allowed-peer-ip=144.100.1.194
###########[ Share Nat ]###############
allowed-peer-ip=192.168.90.0-192.168.200.200

;) turnserver.conf

@whowantsmybigdata
Copy link

whowantsmybigdata commented Jun 10, 2020

This solution for me
##############################################################################
allowed-peer-ip=0.0.0.0-255.255.255.255 (For all, but no resolv) :(
###########[ Rang My Network ]##########
allowed-peer-ip=144.100.1.0-144.100.4.0
allowed-peer-ip=144.100.1.0-144.100.1.255
allowed-peer-ip=144.100.1.194
###########[ Share Nat ]###############
allowed-peer-ip=192.168.90.0-192.168.200.200

;) turnserver.conf

Thanks for sharing this! I would like to try it out on my config but I don't get how to adapt the "Range my network"-part. What kind of IPs are those?
additonally: isnt the first part allowed-peer-ip=0.0.0.0-255.255.255.255 allowing everything anyway? so why do you have to allow those 144.100... IPs again? The "share nat" is your local IP-range if I get it right?

I'm also having issues to understand how I'm supposed to put the external-IP of the server running coturn in the config. As its not a static IP its changing from time to time. Could I put a dyndns-address instead?!

thanks

@hartmans
Copy link

In your turn config you have
no-udp

Does that turn off udp? If so, why do you want that.

In the comments you talk about MITM possibilities if the turn conversation is compromised because of not using turns. I actually think that webrtc is not vulnerable to such an attack because SDP is going to include fingerprints of the expected DTLS certs. I think the worst you can do by compromising the webrtc server is to DOS the connection. And an attacker who can MITM you can do that in various ways even with TLS.

@maxidorius
Copy link
Author

no-udp turns off unencrypted UDP traffic that may or may not happen depending on the client's behaviour. This ensure it will never trigger any kind of reply from the server even if the client makes an attempt. This is just an as-is configuration that we feel is appropriate and reflects our approach to only enable what is needed and disable the rest proactively.

@AnonymousWebHacker
Copy link

@kescherCode
Copy link

kescherCode commented Jan 3, 2021

Using both lt-cred-mech and use-auth-secret makes coturn spit this out:

CONFIGURATION ALERT: You specified --lt-cred-mech and --use-auth-secret in the same time.
Be aware that you could not mix the username/password and the shared secret based auth methods.
Shared secret overrides username/password based auth method. Check your configuration!

Which of these two should be dropped?

@rht
Copy link

rht commented Jan 20, 2021

no-udp turns off unencrypted UDP traffic that may or may not happen depending on the client's behaviour.

@maxidorius yeah but as a side effect it also disables STUN.

@maxidorius
Copy link
Author

@maxidorius yeah but as a side effect it also disables STUN.

For our use case, this is perfectly fine. We only rely on TURN and STUN is irrelevant.

@rht
Copy link

rht commented Jan 21, 2021

I think you should put a caveat on a line above no-udp that say "WARN: will disable STUN" otherwise people get confused on why their STUN is not working. I discovered the hard way that no-udp was the cause.

@hartmans
Copy link

hartmans commented Jan 21, 2021 via email

@maxidorius
Copy link
Author

maxidorius commented Jan 21, 2021

@hartmans this is simply a configuration that works for us, we shared it in the hope it can be useful, not as a general configuration. I would hope "provided AS-IS" and "no support is provided" is good enough indication that you're on your own when using this. Please don't use random things from the internet if you do not understand them! Be safe!

@steadfasterX
Copy link

@maxidorius : thanks! simple and straight forward!
I have a public IP (so no NAT involved) and the following ruleset works fine for me:

image
(ofc with adjusted min and max ports within turnserver.conf)

note: RFC5766 does not mention port 3479 and is not needed imho.
also I specified only the both "turns:" addresses within "turn_uris" so left out the unencrypted ones.

other then that a great quick tut, thanks again :)

@GeoffLedak
Copy link

Thank you so much for this. After going through several different guides and getting nowhere, this worked perfectly.

@weiss
Copy link

weiss commented Jan 14, 2022

Just for the record:

As for 3479, it is a RFC requirement to have the +1 port usable, so following the RFC on that. Is there an updated RFC on that front I missed maybe?

Yes that's no longer used, the reasoning is explained in RFC 5389.

That means that some data is not encrypted sadly.

As has been mentioned above, WebRTC streams are end-to-end encrypted, so TLS doesn't offer additional security for this use case. Clients will usually prefer UDP to minimize latency. Offering TLS as a fallback (on port 443) can help clients circumvent restrictive firewalls (which try to block everything but HTTPS), though.

Also note that even in the case where your client does use a TLS connection to the TURN server, the server always talks unencrypted UDP to the peer (it works in an asymmetric manner, I maintain a TURN server myself and have a quick protocol overview that tries to explain these things on the website).

@lixxdee
Copy link

lixxdee commented Jan 21, 2022

Hi all. after a long and tedious setup of coturn, this configuration actually worked with TLS. However, sometimes these lines reason: TLS/TCP socket buffer operation error (callback) in the logs confuse me. Can you comment on them somehow? And how to fix them?
I managed to avoid this error in the logs, by adding inturnserver.conf to the configuration no-tlsv1_2, and I didn’t see error in logs anything, but i think and it seems that my connection was not secure in terms of TLS.

@weiss
Copy link

weiss commented Jan 21, 2022

a long and tedious setup of coturn

Gives me another chance for embedded marketing! I think configuration and logging are way more straightforward with eturnal 😄

sometimes these lines reason: TLS/TCP socket buffer operation error (callback) in the logs confuse me. Can you comment on them somehow?

You typically see those when the TLS connection wasn't closed cleanly. Unfortunately, this log line alone isn't enough to understand the actual problem. The full log line also mentions a session ID. You could grep the log for that ID to check for earlier messages related to the same session in order to track down at what point the connection was closed. Two possible causes are: (1) The client closed the connection during the initial handshake, e.g. because certificate verification failed or it was unhappy with TLS parameters/ciphers. (2) TURN relaying actually worked fine, and the client just didn't close the session cleanly. This can happen during normal operation, in which case it wouldn't be a problem at all.

it seems that my connection was not secure in terms of TLS.

Do you mean the client doesn't use TLS if you disable TLSv1.2, or do you mean it falls back to earlier TLS versions? Either may be true, the log output should tell you. However, as I explained above, TLS has no security advantage in this context anyway, and can add significant latency, which is the reason UDP is usually used for (end-to-end encrypted) media streaming.

@maxidorius
Copy link
Author

maxidorius commented Jan 21, 2022

However, as I explained above, I would personally suggest keeping UDP (and unencrypted TCP) enabled anyway. Using TLS has no security advantage in this context, and can add significant latency, which is the reason UDP is usually used for (end-to-end encrypted) media streaming.

@weiss and to everyone who would advise another configuration than the one of this gist, please keep in mind that the reason it was posted in the first place is that people were struggling to find a working setup.

As pointed out by @GeoffLedak:

Thank you so much for this. After going through several different guides and getting nowhere, this worked perfectly.

Or @lixxdee

Hi all. after a long and tedious setup of coturn, this configuration actually worked with TLS.

I'm always happy to consider updating this gist but only if it doesn't work anymore. That certain ports or some RFC may not be needed is good to know, but ultimately irrelevant to the point: other guides never worked for me. They didn't work for others who landed here. But this works as stated several times. There are many stacks, software and network configurations out there, everyone mileage may vary. Why is this working and others don't? I don't know. I'm a pragmatic person and I want to help others, so here is simply what works for me.

If you actually do have a working setup that is different than this one and just works out of the box (that's the magic bit), feel free to create a gist of your own!

@weiss
Copy link

weiss commented Jan 21, 2022

@maxidorius, my comments weren't meant to be a criticism of the setup you suggested above. Setting up TURN can be non-trivial indeed, and I totally appreciate your approach of publishing something that 'just works'.

I'm always happy to consider updating this gist but only if it doesn't work anymore.

I think problems such as missing STUN support or suboptimal latency can be somewhere in between "works" and "doesn't work", in the sense that things could work better (at least for some users), and admins might not be aware of that, as it's not obvious when testing the setup.

feel free to create a gist of your own!

I just figured it might be helpful for others to clarify some questions/assumptions made in comments above, e.g. about the relevance of enforcing TLS in this context. But I take it you don't find this useful, so I'll refrain from further comments. Sorry!

@maxidorius
Copy link
Author

@weiss Don't worry, I didn't think you were criticizing anything here, far from it! But to be precise here, I found in my own experience that non-working guides/howto/advices don't work because they:
a) Are not given as a whole
b) Are not given complete

While I can only be happy and humbled by the fact this gist is still alive and receiving comments after more than 2 years, I do not want to end up in a situation where any of the two points given above become true. Ideally for the comments as well.

So It's great people are giving feedback and advice on how to do it better, but if you do then please also share a configuration that is

  • Whole: All configs that relate to it, the same way I give the coturn, synapse and firewall configs
  • Complete: Give all of the relevant config, not just the changed bits

And I'm sure you can see how my advice "feel free to create a gist of your own!" fits there: you can literally copy/paste it, change whatever you feel is better, test it out on a server, then put a link here. I would be more than happy to include it into the gist itself under a new section!
Vague comments are what made me write this gist, so let's try not to fall into the same issues again :)

Finally: thanks for your input, I would love to see another working config that has your points taken into account. I might use it for myself as well, who knows!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment