Tutorial : How to make matrix-synapse's coturn (audio & video calls) work behind a VPN

Hi,

By trying to make the audio / video calls work on my matrix-synapse Yunohost server, I realized that there is a lot of misunderstanding about over the different documentations and tutorials that I found online.

A lot of those claim that it is not possible to run a coturn server behind a NAT / VPN, including the official matrix-synapse documentation :

But surprisingly, the coturn official webpage starts with this description :

The TURN Server is a VoIP media traffic NAT traversal server and gateway. It can be used as a general-purpose network traffic TURN server and gateway, too.

NAT traversal ! ! ! !
And, indeed, here is what can be found in the manual of coturn (man coturn in a terminal) :

-X, --external-ip
              TURN Server public/private address mapping, **if the server is behind NAT** [...]

I assume that :

  1. You have matrix-synapse installed on your Yunohost
  2. Your Yunohost is running behind a VPN hosted on a VPS with a public IP adress

If yes, we are going to :

  1. Activate this option on our Yunohost
  2. Forward the right ports on our VPS

1 - On your Yunohost :

To activate the option on your Yunohost, you should edit the conf file /etc/matrix-synapse/coturn.conf

And add or edit this line :

external-ip=XXX.XXX.XXX.XXX/YYY.YYY.YYY.YYY

Where :

XXX.XXX.XXX.XXX is my public IP
YYY.YYY.YYY.YYY is the private ip of your yunohost on the VPN Subnet (Usually something like 10.8.0.x )

EDIT 15/07/2022
It somehow stopped working after I rebooted my Yunohost once.
I spent some time and increased frustration to understand what was going on… And made it work by using the domain name instead of the public IP :

mydomain.com is my domain name 
YYY.YYY.YYY.YYY is the private ip of your yunohost on the VPN Subnet (Usually something like 10.8.0.x )

Then restart the coturn-synapse service :
systemctl restart coturn-synapse.service

2 - On your VPN / VPS :

You have to forward from the public static IP network interface to the private IP of the Yunohost on the VPN network subnet the following ports :

  • 5349 and 5350 (both TCP and UDP)
  • The range of ports from 49153 to 49193 (both TCP and UDP) :

Use the following command, that you’d have to adjust assuming that :

eth0 is the interface of the static public IP of your VPS
tun0 is the interface of your VPN network on your VPS
10.8.0.1 is the private ip of your VPS on the VPN Subnet
10.8.0.2 is the private ip of your yunohost on the VPN Subnet

iptables -A FORWARD -i eth0 -o tun0 -p tcp -m tcp --dport 5349 --tcp-flags FIN,SYN,RST,ACK SYN -m conntrack --ctstate NEW -j ACCEPT
iptables -A PREROUTING -i eth0 -p tcp -m tcp --dport 5349 -j DNAT --to-destination 10.8.0.2
iptables -A POSTROUTING -d 10.8.0.2/32 -o tun0 -p tcp -m tcp --dport 5349 -j SNAT --to-source 10.8.0.1


iptables -A FORWARD -i eth0 -o tun0 -p tcp -m tcp --dport 5350 --tcp-flags FIN,SYN,RST,ACK SYN -m conntrack --ctstate NEW -j ACCEPT
iptables -A PREROUTING -i eth0 -p tcp -m tcp --dport 5350 -j DNAT --to-destination 10.8.0.2
iptables -A POSTROUTING -d 10.8.0.2/32 -o tun0 -p tcp -m tcp --dport 5350 -j SNAT --to-source 10.8.0.1
iptables -A FORWARD -i eth0 -o tun0 -p tcp -m tcp --dport 49153:49193 --tcp-flags FIN,SYN,RST,ACK SYN -m conntrack --ctstate NEW -j ACCEPT
iptables -A PREROUTING -i eth0 -p tcp -m tcp --dport 49153:49193 -j DNAT --to-destination 10.8.0.2
iptables -A PREROUTING -i eth0 -p udp -m udp --dport 49153:49193 -j DNAT --to-destination 10.8.0.2
iptables -A POSTROUTING -d 10.8.0.2/32 -o tun0 -p tcp -m tcp --dport 49153:49193 -j SNAT --to-source 10.8.0.1
iptables -A POSTROUTING -d 10.8.0.2/32 -o tun0 -p udp -m udp --dport 49153:49193 -j SNAT --to-source 10.8.0.1

It should now work.
Enjoy !

7 Likes

cool, but this means you need to install Matrix Synapse? Is there a way to make it work with the vanilla Metronome that’s default on Yunohost?

OR

How do we disable metronome to not interfere with Synapse?

Hey,

I think we are getting there. Thanks @rungeard and @charly for all you help on this!!

Im running a VPS on Hetzner.com, Yunohost server as a local VM and wireguard for connection.

Before going in to details I think there is a problem with coturn start-up sequence vs network connections (and/or wg?) that has to be solved, many reboots on YH-server fails to start/or even get this in correct order!!
I have not tested this yet, but found this - coturn/coturn.service at master ¡ coturn/coturn ¡ GitHub
Edit: I think we found a workaround for this problem, its described here: Tutorial : How to make matrix-synapse's coturn (audio & video calls) work behind a VPN - #9 by tomas

I followed @rungeard tutorial here - Homemade WireGuard VPN on a VPS server

My wg NAT settings are:

VPS settings

# PostUp.sh VPS
iptables -A FORWARD -i wg0 -j ACCEPT; 
iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE; 
ip6tables -A FORWARD -i wg0 -j ACCEPT; 
ip6tables -t nat -A POSTROUTING -o eth0 -j MASQUERADE; 

# icmp
iptables -A INPUT -p icmp -j ACCEPT;
ip6tables -A INPUT -p ipv6-icmp -j ACCEPT; 

 
# Routing TCP port 25 and 587 from Yunohost Server to internet 
for j in 25 587
do
        iptables -t nat -A POSTROUTING -s 10.6.0.2 -p tcp --dport $j -j SNAT --to 98.76.543.210; 
        iptables -A FORWARD -s 10.6.0.2 -p tcp --dport $j -j ACCEPT; 
        ip6tables -t nat -A POSTROUTING -s fd42:42:42::2 -p tcp --dport $j -j SNAT --to aaaa:bbbb:cccc:dddd::1; 
        ip6tables -A FORWARD -s fd42:42:42::2 -p tcp --dport $j -j ACCEPT; 
done

# Routing TCP port required from VPN server to Yunohost server
for i in 25 80 140 443 587 993 5222 5269 8448
do 
        iptables -t nat -A PREROUTING -i eth0 -p tcp --dport $i -j DNAT --to-destination 10.6.0.2; 
        iptables -A FORWARD -d 10.6.0.2 -p tcp --dport $i -j ACCEPT; 
        ip6tables -t nat -A PREROUTING -i eth0 -p tcp --dport $i -j DNAT --to-destination fd42:42:42::2; 
        ip6tables -A FORWARD -d fd42:42:42::2 -p tcp --dport $i -j ACCEPT; 
done


# synapse contur
for port in udp tcp
do
        for i in 5350 5349
        do 
                iptables -t nat -A PREROUTING -i eth0 -p $port --dport $i -j DNAT --to-destination 10.6.0.2;
                iptables -t nat -A POSTROUTING -s 10.6.0.2 -p $port --dport $i -j SNAT --to 98.76.543.210; 
                iptables -A FORWARD -s 10.6.0.2 -p $port --dport $i -j ACCEPT;
                iptables -A FORWARD -d 10.6.0.2 -p $port --dport $i -j ACCEPT; 

                ip6tables -t nat -A PREROUTING -i eth0 -p $port --dport $i -j DNAT --to-destination fd42:42:42::2;
                ip6tables -t nat -A POSTROUTING -s fd42:42:42::2 -p $port --dport $i -j SNAT --to aaaa:bbbb:cccc:dddd::1; 
                ip6tables -A FORWARD -s fd42:42:42::2 -p $port --dport $i -j ACCEPT;
                ip6tables -A FORWARD -d fd42:42:42::2 -p $port --dport $i -j ACCEPT; 
        done
        
        iptables -t nat -A PREROUTING -i eth0 -p $port --dport 49153:49193 -j DNAT --to-destination 10.6.0.2;
        iptables -t nat -A POSTROUTING -s 10.6.0.2 -p $port --dport 49153:49193 -j SNAT --to 98.76.543.210; 
        iptables -A FORWARD -s 10.6.0.2 -p $port --dport 49153:49193 -j ACCEPT;
        iptables -A FORWARD -d 10.6.0.2 -p $port --dport 49153:49193 -j ACCEPT;

        ip6tables -t nat -A PREROUTING -i eth0 -p $port --dport 49153:49193 -j DNAT --to-destination fd42:42:42::2;
        ip6tables -t nat -A POSTROUTING -s fd42:42:42::2 -p $port --dport 49153:49193 -j SNAT --to aaaa:bbbb:cccc:dddd::1;
        ip6tables -A FORWARD -s fd42:42:42::2 -p $port --dport 49153:49193 -j ACCEPT;
        ip6tables -A FORWARD -d fd42:42:42::2 -p $port --dport 49153:49193 -j ACCEPT;
done
# PostDown.sh VPS
iptables -D FORWARD -i wg0 -j ACCEPT; 
iptables -t nat -D POSTROUTING -o eth0 -j MASQUERADE; 
ip6tables -D FORWARD -i wg0 -j ACCEPT; 
ip6tables -t nat -D POSTROUTING -o eth0 -j MASQUERADE; 

# icmp
iptables -D INPUT -p icmp -j ACCEPT;
ip6tables -D INPUT -p ipv6-icmp -j ACCEPT; 

 
# Routing TCP port 25 and 587 from Yunohost Server to internet 
for j in 25 587 5350 5349 49153:49193
do
        iptables -t nat -D POSTROUTING -s 10.6.0.2 -p tcp --dport $j -j SNAT --to 98.76.543.210; 
        iptables -D FORWARD -s 10.6.0.2 -p tcp --dport $j -j ACCEPT; 
        ip6tables -t nat -D POSTROUTING -s fd42:42:42::2 -p tcp --dport $j -j SNAT --to aaaa:bbbb:cccc:dddd::1; 
        ip6tables -D FORWARD -s fd42:42:42::2 -p tcp --dport $j -j ACCEPT; 
done

# Routing TCP port required from VPN server to Yunohost server
for i in 25 80 140 443 587 993 5222 5269 8448
do 
        iptables -t nat -D PREROUTING -i eth0 -p tcp --dport $i -j DNAT --to-destination 10.6.0.2; 
        iptables -D FORWARD -d 10.6.0.2 -p tcp --dport $i -j ACCEPT; 
        ip6tables -t nat -D PREROUTING -i eth0 -p tcp --dport $i -j DNAT --to-destination fd42:42:42::2; 
        ip6tables -D FORWARD -d fd42:42:42::2 -p tcp --dport $i -j ACCEPT; 
done


# synapse contur
for port in udp tcp
do
        for i in 5350 5349
        do 
                iptables -D FORWARD -s 10.6.0.2 -p $port --dport $i -j ACCEPT;
                iptables -D FORWARD -d 10.6.0.2 -p $port --dport $i -j ACCEPT; 
                iptables -t nat -D PREROUTING -i eth0 -p $port --dport $i -j DNAT --to-destination 10.6.0.2;
                iptables -t nat -D POSTROUTING -s 10.6.0.2 -p $port --dport $i -j SNAT --to 98.76.543.210; 

                ip6tables -D FORWARD -s fd42:42:42::2 -p $port --dport $i -j ACCEPT;
                ip6tables -D FORWARD -d fd42:42:42::2 -p $port --dport $i -j ACCEPT; 
                ip6tables -t nat -D PREROUTING -i eth0 -p $port --dport $i -j DNAT --to-destination fd42:42:42::2;
                ip6tables -t nat -D POSTROUTING -s fd42:42:42::2 -p $port --dport $i -j SNAT --to aaaa:bbbb:cccc:dddd::1; 
        done

        iptables -D FORWARD -s 10.6.0.2 -p $port --dport 49153:49193 -j ACCEPT;
        iptables -D FORWARD -d 10.6.0.2 -p $port --dport 49153:49193 -j ACCEPT;
        iptables -t nat -D PREROUTING -i eth0 -p $port --dport 49153:49193 -j DNAT --to-destination 10.6.0.2;
        iptables -t nat -D POSTROUTING -s 10.6.0.2 -p $port --dport 49153:49193 -j SNAT --to 98.76.543.210; 

        ip6tables -D FORWARD -s fd42:42:42::2 -p $port --dport 49153:49193 -j ACCEPT;
        ip6tables -D FORWARD -d fd42:42:42::2 -p $port --dport 49153:49193 -j ACCEPT;
        ip6tables -t nat -D PREROUTING -i eth0 -p $port --dport 49153:49193 -j DNAT --to-destination fd42:42:42::2;
        ip6tables -t nat -D POSTROUTING -s fd42:42:42::2 -p $port --dport 49153:49193 -j SNAT --to aaaa:bbbb:cccc:dddd::1;
done

YH settings

# PostUp.sh Yunohost

#Begin IPV4
iptables -w -N vpnclient_in; 
iptables -w -N vpnclient_out; 
iptables -w -N vpnclient_fwd;
 
iptables -w -A vpnclient_in -p icmp -j ACCEPT; 
iptables -w -A vpnclient_in -s 10.6.0.2/24 -j ACCEPT; 

#Allowing required TCP ports
for i in 25 80 140 443 587 993 5222 5269 8448
do
        iptables -w -A vpnclient_in -p tcp --dport $i -j ACCEPT; 
done

#Allowing required Synapse Contur ports
iptables -w -A vpnclient_in --dport 5349 -j ACCEPT;
iptables -w -A vpnclient_in --dport 5350 -j ACCEPT;
iptables -w -A vpnclient_in --dport 49153:49193 -j ACCEPT;
#end synapse

iptables -w -A vpnclient_in -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT; 
iptables -w -A vpnclient_in -j DROP; 

iptables -w -A vpnclient_out -j ACCEPT; 

iptables -w -A vpnclient_fwd -j DROP; 

iptables -w -I INPUT 1 -i wg0 -j vpnclient_in; 
iptables -w -I OUTPUT 1 -o wg0 -j vpnclient_out; 
iptables -w -I FORWARD 1 -o  wg0 -j vpnclient_fwd; 
#End IPV4

#Begin IPV6
ip6tables -w -N vpnclient_in; 
ip6tables -w -N vpnclient_out; 
ip6tables -w -N vpnclient_fwd;

ip6tables -w -A vpnclient_in -p ipv6-icmp -j ACCEPT; 
ip6tables -w -A vpnclient_in -s fd42:42:42::2/64 -j ACCEPT; 

#Allowing required TCP ports
for i in 25 80 140 443 587 993 5222 5269 8448
do
        ip6tables -w -A vpnclient_in -p tcp --dport $i -j ACCEPT; 
done

#Allowing required Synapse Contur ports
ip6tables -w -A vpnclient_in --dport 5349 -j ACCEPT;
ip6tables -w -A vpnclient_in --dport 5350 -j ACCEPT;
ip6tables -w -A vpnclient_in --dport 49153:49193 -j ACCEPT;
#end synapse

ip6tables -w -A vpnclient_in -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT; 
ip6tables -w -A vpnclient_in -j DROP;
 
ip6tables -w -A vpnclient_out -j ACCEPT; 

ip6tables -w -A vpnclient_fwd -j DROP; 

ip6tables -w -I INPUT 1 -i wg0 -j vpnclient_in; 
ip6tables -w -I OUTPUT 1 -o wg0 -j vpnclient_out; 
ip6tables -w -I FORWARD 1 -o  wg0 -j vpnclient_fwd;
#End IPV6
# PostDown.sh YH
#Begin IPV4
iptables -w -F vpnclient_in; 
iptables -w -F vpnclient_out; 
iptables -w -F vpnclient_fwd; 

iptables -D INPUT -i wg0 -j vpnclient_in; 
iptables -D FORWARD -o wg0 -j vpnclient_fwd; 
iptables -D OUTPUT -o wg0 -j vpnclient_out; 

iptables -w -X vpnclient_in; 
iptables -w -X vpnclient_out; 
iptables -w -X vpnclient_fwd;
#End IPV4

#Begin IPV6
ip6tables -w -F vpnclient_in; 
ip6tables -w -F vpnclient_out; 
ip6tables -w -F vpnclient_fwd; 

ip6tables -D INPUT -i wg0 -j vpnclient_in; 
ip6tables -D FORWARD -o wg0 -j vpnclient_fwd; 
ip6tables -D OUTPUT -o wg0 -j vpnclient_out;
 
ip6tables -w -X vpnclient_in; 
ip6tables -w -X vpnclient_out; 
ip6tables -w -X vpnclient_fwd;
#End IPV6

Some port testing to see that its correct.

Testing VPS

# iptables-save | grep 5349
-A PREROUTING -i eth0 -p udp -m udp --dport 5349 -j DNAT --to-destination 10.6.0.2
-A PREROUTING -i eth0 -p tcp -m tcp --dport 5349 -j DNAT --to-destination 10.6.0.2
-A POSTROUTING -s 10.6.0.2/32 -p udp -m udp --dport 5349 -j SNAT --to-source 98.76.543.210
-A POSTROUTING -s 10.6.0.2/32 -p tcp -m tcp --dport 5349 -j SNAT --to-source 98.76.543.210
-A FORWARD -s 10.6.0.2/32 -p udp -m udp --dport 5349 -j ACCEPT
-A FORWARD -d 10.6.0.2/32 -p udp -m udp --dport 5349 -j ACCEPT
-A FORWARD -s 10.6.0.2/32 -p tcp -m tcp --dport 5349 -j ACCEPT
-A FORWARD -d 10.6.0.2/32 -p tcp -m tcp --dport 5349 -j ACCEPT

Testing YH

# iptables-save | grep 5349
-A INPUT -p tcp -m tcp --dport 5349 -j ACCEPT
-A INPUT -p udp -m udp --dport 5349 -j ACCEPT

Matrix VoIP Tester


(Im not sure why ip6 fails…?)

edit: fixed two faulty port numbers in the PostUp.sh Yunohost, changed from 5439 to 5349

2 Likes

Is that tester at https://test.voip.librepush.net/ ?

Did you create a one-off access token for this website or give it a temporary user/pass? Is it trustworthy enough to trust it with those credentials?

Yes, correct.

I did a test account and used name and password.

Thank, I just had mine tested as well!

I am not sure I did any configuration for IPv6, mine got ‘poor’, with a fail for TURN on TCP and excellent for TURN on UDP.

Nice! What is your set-up look like (VPS, config, VM or PC/“bare metal”?)?
…i guess ip4 was ok?

Container on Proxmox, at home. I think the machine is in ‘exposed host’ mode, seen from the router (all traffic is forwarded to this Yunohost).

I think I have found a workaround to this problem.

When YH is started or rebooted:

  1. stop yunohost-firewall
  2. start coturn-synapse (if not started) or restart
  3. start yunohost-firewall

It looks like this also works:

  1. start coturn-synapse (if not started) or restart
  2. stop yunohost-firewall
  3. start yunohost-firewall

Someone who has any idea why this works?

edit: restart yunohost-firewall seems not to work.

1 Like