Trying to make TURN working

Hello,

I’m following Self-Hosting Guide - Debian/Ubuntu server · Jitsi Meet Handbook and I face a first issue with ufw I guess…

Does that make sense?

root@visio:~# ufw status verbose
Status: active
Logging: on (low)
Default: deny (incoming), allow (outgoing), deny (routed)
New profiles: skip

To                         Action      From
--                         ------      ----
22/tcp                     ALLOW IN    Anywhere                  
80/tcp                     ALLOW IN    Anywhere                  
443/tcp                    ALLOW IN    Anywhere                  
4443/tcp                   ALLOW IN    Anywhere                  
3478/udp                   ALLOW IN    Anywhere                  
5349/tcp                   ALLOW IN    Anywhere                  
10000/udp                  ALLOW IN    Anywhere                  
22/tcp (v6)                ALLOW IN    Anywhere (v6)             
80/tcp (v6)                ALLOW IN    Anywhere (v6)             
443/tcp (v6)               ALLOW IN    Anywhere (v6)             
4443/tcp (v6)              ALLOW IN    Anywhere (v6)             
3478/udp (v6)              ALLOW IN    Anywhere (v6)             
5349/tcp (v6)              ALLOW IN    Anywhere (v6)             
10000/udp (v6)             ALLOW IN    Anywhere (v6)             

root@visio:~# nc -u -z -vv localhost 3478 
localhost [127.0.0.1] 3478 (?) open
 sent 0, rcvd 0
root@visio:~# nc -u -z -vv localhost 5349
localhost [127.0.0.1] 5349 (?) open
 sent 0, rcvd 0
root@visio:~# nc -z -vv localhost 5349
localhost [127.0.0.1] 5349 (?) open
 sent 0, rcvd 0
root@visio:~# nc -z -vv localhost 3478
localhost [127.0.0.1] 3478 (?) : Connection refused
 sent 0, rcvd 0
root@visio:~# nc -z -vv localhost 443 
localhost [127.0.0.1] 443 (https) open
 sent 0, rcvd 0
root@visio:~# nc -z -vv localhost 4443
localhost [127.0.0.1] 4443 (?) : Connection refused
 sent 0, rcvd 0
root@visio:~# nc -u -z -vv localhost 10000
localhost [127.0.0.1] 10000 (?) : Connection refused
 sent 0, rcvd 0

Why do I get Connection refused on open port such as 4443 or 10000 locally?

Of course, I get the same answer when I try from an other container doing
nc -u -z -vv 192.168.xxx.xxx 10000
for example

you set rules allowing only incoming. :slight_smile:

Well, I did, as explained in the handbook, Self-Hosting Guide - Debian/Ubuntu server · Jitsi Meet Handbook

sudo ufw allow 80/tcp
sudo ufw allow 443/tcp
sudo ufw allow 10000/udp
sudo ufw allow 22/tcp
sudo ufw allow 3478/udp
sudo ufw allow 5349/tcp
sudo ufw enable

and then
sudo ufw allow 4443/tcp
as required here Self-Hosting Guide - Debian/Ubuntu server · Jitsi Meet Handbook
What should be shell commands then?

something like

for i in ssh http https 4443/tcp 10000:20000/udp
do
    ufw allow $i
done
ufw enable

(adjust to your needs)

I don’t see any difference except you use a script and I launched several command but the result should be the same.
by the way, why 10000:20000/udp ?
In the handbook, it is mention only 10000/udp

Have you tried yet if your server works anyway?

Having only incoming ufw rules is ok, since the default for outgoing is allow anyway, so that’s fine.

Connection on 3478/tcp refused is to be expected, because that port is open for udp only. Add the missing -u option to that command line.

Port 4443 might not be needed at all and could be disabled in the videobridge config anyway, so no problem there either.

Port 10000 finally might be configured to listen only on your external IP addresses, so you can’t connect on localhost. That is the case on my machine, maybe try ss -tulpn | grep 10000 to see if it is on yours as well. Anyway, this shouldn’t be an issue either.

1 Like

Thank you for your answer @lpetersen

root@visio:~# ss -tulpn | grep 10000
udp   UNCONN 0      0               [::ffff:XXX.XXX.XXX.XXX]:10000            *:*    users:(("java",pid=329,fd=163))                                                                                                                                                                                       
udp   UNCONN 0      0      [2a00:c70:1:YYY:YYY:YYY:YYY:YYY]:10000         [::]:*    users:(("java",pid=329,fd=162))

with XXX.XXX.XXX.XXX the private IPv4 behind NAT
and
YYY:YYY:YYY:YYY:YYY the public IPv6

Is it OK?

Jitsi is working but TURN doesn’t seem to work as people in restricted environment I work with can’t see or hear me and vice versa.

Jitsi has a public IPv6 and a NAT private IPv4 behind an nginx reverse proxy.

org.ice4j.ice.harvest.NAT_HARVESTER_LOCAL_ADDRESS=<Local.IP.Address>
org.ice4j.ice.harvest.NAT_HARVESTER_PUBLIC_ADDRESS=<Public.IP.Address>

is OK

port 80/tcp, 443/tcp, 1000/udp, 3478/udp and 5349/tcp are open on the firewall for the public IPv6 of jitsi
port 1000/udp, 3478/udp and 5349/tcp are open (NAT redirect) to the private IPv4 of jitsi
port 80/tcp and 443tcp are open (NAT redirec) to the private IPv4 of Nginx reverse proxy

nginx conf of jitsi is

types {
# nginx's default mime.types doesn't include a mapping for wasm
    application/wasm     wasm;
}

server {
    listen 80;
    listen [::]:80;
    server_name my.domain.com;

    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;
    listen [::]:443 ssl;
    server_name my.domain.com;

    # Mozilla Guideline v5.4, nginx 1.17.7, OpenSSL 1.1.1d, intermediate configuration
    ssl_protocols TLSv1.2 TLSv1.3;
    ssl_ciphers 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;
    ssl_prefer_server_ciphers off;

    ssl_session_timeout 1d;
    ssl_session_cache shared:SSL:10m;  # about 40000 sessions
    ssl_session_tickets off;

    add_header Strict-Transport-Security "max-age=63072000" always;
    ssl_certificate /etc/letsencrypt/live/my.domain.com/fullchain.pem; # managed by Certbot
    ssl_certificate_key /etc/letsencrypt/live/my.domain.com/privkey.pem; # managed by Certbot
    ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem; # managed by Certbot

    root /usr/share/jitsi-meet;

    # ssi on with javascript for multidomain variables in config.js
    ssi on;
    ssi_types application/x-javascript application/javascript;

    index index.html index.htm;
    error_page 404 /static/404.html;

    gzip on;
    gzip_types text/plain text/css application/javascript application/json image/x-icon application/octet-stream application/wasm;
    gzip_vary on;
    gzip_proxied no-cache no-store private expired auth;
    gzip_min_length 512;

    location = /config.js {
        alias /etc/jitsi/meet/my.domain.com-config.js;
    }

    location = /external_api.js {
        alias /usr/share/jitsi-meet/libs/external_api.min.js;
    }

    # ensure all static content can always be found first
    location ~ ^/(libs|css|static|images|fonts|lang|sounds|connection_optimization|.well-known)/(.*)$
    {
        add_header 'Access-Control-Allow-Origin' '*';
        alias /usr/share/jitsi-meet/$1/$2;

        # cache all versioned files
        if ($arg_v) {
            expires 1y;
        }
    }

    # BOSH
    location = /http-bind {
        proxy_pass       http://localhost:5280/http-bind;
        proxy_set_header X-Forwarded-For $remote_addr;
        proxy_set_header Host $http_host;
    }

    # xmpp websockets
    location = /xmpp-websocket {
        proxy_pass http://127.0.0.1:5280/xmpp-websocket?prefix=$prefix&$args;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";
        proxy_set_header Host $http_host;
        tcp_nodelay on;
    }

    # colibri (JVB) websockets for jvb1
    location ~ ^/colibri-ws/default-id/(.*) {
        proxy_pass http://127.0.0.1:9090/colibri-ws/default-id/$1$is_args$args;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";
        tcp_nodelay on;
    }

    # load test minimal client, uncomment when used
    #location ~ ^/_load-test/([^/?&:'"]+)$ {
    #    rewrite ^/_load-test/(.*)$ /load-test/index.html break;
    #}
    #location ~ ^/_load-test/libs/(.*)$ {
    #    add_header 'Access-Control-Allow-Origin' '*';
    #    alias /usr/share/jitsi-meet/load-test/libs/$1;
    #}

    location ~ ^/([^/?&:'"]+)$ {
        try_files $uri @root_path;
    }

    location @root_path {
        rewrite ^/(.*)$ / break;
    }

    location ~ ^/([^/?&:'"]+)/config.js$
    {
        set $subdomain "$1.";
        set $subdir "$1/";

        alias /etc/jitsi/meet/my.domain.com-config.js;
    }

    # Anything that didn't match above, and isn't a real file, assume it's a room name and redirect to /
    location ~ ^/([^/?&:'"]+)/(.*)$ {
        set $subdomain "$1.";
        set $subdir "$1/";
        rewrite ^/([^/?&:'"]+)/(.*)$ /$2;
    }

    # BOSH for subdomains
    location ~ ^/([^/?&:'"]+)/http-bind {
        set $subdomain "$1.";
        set $subdir "$1/";
        set $prefix "$1";

        rewrite ^/(.*)$ /http-bind;
    }

    # websockets for subdomains
    location ~ ^/([^/?&:'"]+)/xmpp-websocket {
        set $subdomain "$1.";
        set $subdir "$1/";
        set $prefix "$1";

        rewrite ^/(.*)$ /xmpp-websocket;
    }

}

and the nginx conf of my nginx reverse proxy is

server {
    location / {
        include proxy_params;
        proxy_pass https://XXX.XXX.XXX.XXX;
    }
    server_name my.domain.com;

    listen 443 ssl;
    ssl_certificate /home/sftpuser_visio/letsencrypt/fullchain.pem;
    ssl_certificate_key /home/sftpuser_visio/letsencrypt/privkey.pem;
    include /home/sftpuser_visio/letsencrypt/options-ssl-nginx.conf;
    ssl_dhparam /home/sftpuser_visio/letsencrypt/ssl-dhparams.pem;

}
server {
    if ($host = my.domain.com) {
        return 301 https://$host$request_uri;
    }

    server_name my.domain.com;
    listen 80;
    return 404;
}

What could I do to make TURN working nicely and thus jitsi working for all people I’m working with?

Very interesting. This setup should not work at all

really?? Why?
Well it does but not for everybody…
Do you have a tutorial that explain what I should do?

Does this work for IPv4 clients?
Did you test only for IPv6 clients?

yes, it does work with both ipv6 and ipv4 with different clients on my dsl network (ipv4 only) and 4g network (ipv4 and ipv6)

Since you mentioned this:

org.ice4j.ice.harvest.NAT_HARVESTER_LOCAL_ADDRESS=<Local.IP.Address>
org.ice4j.ice.harvest.NAT_HARVESTER_PUBLIC_ADDRESS=<Public.IP.Address>

… do you also have a line like this pointing to your turn server:

org.ice4j.ice.harvest.STUN_MAPPING_HARVESTER_ADDRESSES=<your.turn.server:port>

To see if the problem is with your Jitsi setup or if already your turn server isn’t working, consider checking the latter with a tool like this one.

Use a bash script like this one to generate temporary credentials for your turn server:

#!/bin/bash
CONFIG="/etc/turnserver.conf"
secret=$(awk -F "=" '/static-auth-secret/ {print $2}' $CONFIG) && time=$(date +%s) && expiry=8400 && \
username=$(( $time + $expiry )) && echo username: $username && \
echo password: $(echo -n $username | openssl dgst -binary -sha1 -hmac $secret | openssl base64)

(Code isn’t mine, and I have forgotten where I found it – probably on this forum, so sorry to whomever I just plagiarized …)

Oh, and one last thing: Since you also wrote

Of course, I get the same answer when I try from an other container doing
nc -u -z -vv 192.168.xxx.xxx 10000
for example

… your turn server isn’t supposed to be reachable by the private address 192.168.xxx.xxx only, or is it? It must obviously be exposed to the internet, having a publicly routable IP address. Just to be sure …

Well, in the handbook, it is written to " comment the existing org.ice4j.ice.harvest.STUN_MAPPING_HARVESTER_ADDRESSES".
So I don’t have any line like

org.ice4j.ice.harvest.STUN_MAPPING_HARVESTER_ADDRESSES=<your.turn.server:port>

but the commented one…
I don’t have an external turn server. I thought there was a turn server wthin jitsi, I guess I read about a jitsi-meet-turnserver thing.
I might have misunderstood something.

The jitsi-meet-turnserver package depends on coturn as a TURN server and only adjusts its configuration file. So you should have coturn working. (And coturn is probably what is listening on those ports 3478 and 5349.)

To be honest, about that org.ice4j.ice.harvest.STUN_MAPPING_HARVESTER_ADDRESSES line, I’m not really sure. I have it in my config, but it’s been ages since I modified that, so maybe you’re right about commenting it out …

1 Like

This sets jvb to use a stunserver to discover its public address and this cannot be a turnserver from the same network. Default setting is jvb to use stunservers of meet.jit.si to discover its public address. This can be commented and replaced with manual setting of private and public address.

2 Likes