jitsi / jitsi-meet

Jitsi Meet - Secure, Simple and Scalable Video Conferences that you use as a standalone app or embed in your web application.
https://jitsi.org/meet
Apache License 2.0
23.27k stars 6.76k forks source link

Problem running Jitsi Meet so that incoming server ports 443 and 80 suffice, for firewalled guests (no port UDP 10000+ etc. possible) #15293

Closed ned64 closed 1 week ago

ned64 commented 1 week ago

What happened?

I have been trying to get a JM server to run so that clients can connect which are behind a firewall blocking everything except ports 80 and 443.

I have set up DNS so that video3.example.org and turn3.example.org both point to my only IPv4 address.

I have used https://jitsi.github.io/handbook/docs/devops-guide/turn/ to set up the system. nginx runs as a reverse proxy for both Jitsi Videe Bridge and the Web server, config files below.

Testing: PC 1, Firefox 1: log in and start session PC 1, Chromium: join, stream music and share window Smartphone, Jitsi Meet Android: join. It receieves the music and shows Chromium's screen :-) PC 2: Block everything outgoing to my server IP except 80,443 then start Firefox 2, join

Problem: PC 2 cannot send or receive audio or video, everyone looks muted.

### /etc/nginx/nginx.conf
user nginx;
worker_processes auto;

pid /run/nginx.pid;
include /etc/nginx/modules-enabled/*.conf;

events {
    worker_connections 768;
    # multi_accept on;
}

http {
    ##
    # Basic Settings
    ##

    sendfile on;
    tcp_nopush on;
    tcp_nodelay on;
    keepalive_timeout 65s;
    types_hash_max_size 4096;
    server_tokens off;

    server_names_hash_bucket_size 64;
    # server_name_in_redirect off;

    include /etc/nginx/mime.types;
        types {
        # add support for wasm MIME type, that is required by specification and it is not part of default mime.types file
        application/wasm wasm;
    }
    default_type application/octet-stream;
    charset utf-8;

    # Settings for the non-TLS server.
     server {
        listen      80 default_server;
    listen      [::]:80 default_server;
        server_name  _;  # match all
    root        /srv/nginx/video3.example.org;

    # force TLS/https
    return 301 https://$host$request_uri;

    } # end server port 80

    ##
    # Virtual Host Configs
    ##

    include /etc/nginx/conf.d/*.conf;
    include /etc/nginx/sites-enabled/*;

}
### /etc/nginx/sites-enabled/video3.example.org
server {
    listen 80;
    listen [::]:80;
    server_name video3.example.org;

    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 444 ssl;
    listen [::]:444 ssl;
    server_name video3.example.org;

    ssl_protocols TLSv1.2 TLSv1.3;
    ssl_prefer_server_ciphers on;
    ssl_ciphers "EECDH+ECDSA+AESGCM:EECDH+aRSA+AESGCM:EECDH+ECDSA+SHA256:EECDH+aRSA+SHA256:EECDH+ECDSA+SHA384:EECDH+ECDSA+SHA256:EECDH+aRSA+SHA384:EDH+aRSA+AESGCM:EDH+aRSA+SHA256:EDH+aRSA:EECDH:!aNULL:!eNULL:!MEDIUM:!LOW:!3DES:!MD5:!EXP:!PSK:!SRP:!DSS:!RC4:!SEED";

    add_header Strict-Transport-Security "max-age=31536000";

    ssl_certificate /etc/pki/nginx/video3-server-chain.crt;
    ssl_certificate_key /etc/pki/nginx/private/video3-server.key;

    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/video3.example.org-config.js;
    }

    # is this used or even possible???
    location = interface_config.js {
        alias /etc/jitsi/meet/video3.example.org-interface_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;
    }

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

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

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

       alias /etc/jitsi/meet/video3.example.org-config.js;
    }

    # 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;
    }

    #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;
    }
}

Coturn:

### /etc/nginx/modules-enabled/coturn-multiplex.conf
stream {
    map $ssl_preread_server_name $name {
        video3.example.org web_backend;
        turn3.example.org turn_backend;
    }

    upstream web_backend {
        server 127.0.0.1:444;
    }

    upstream turn_backend {
        ## server 1.2.3.4:5349;
        server 1.2.3.4:5348;
        ### server 127.0.0.1:5348;
        ### server 127.0.0.1:5349;
    }

    server {
        listen 443;
        listen [::]:443;

        # since 1.11.5
        ssl_preread on;

        proxy_pass $name;

        # Increase buffer to serve video
        proxy_buffer_size 20m;
    }
}

Prodosy:

### /etc/prosody/prosody.cfg.lua

-- Prosody Example Configuration File
--
-- Information on configuring Prosody can be found on our
-- website at https://prosody.im/doc/configure
--
-- Tip: You can check that the syntax of this file is correct
-- when you have finished by running this command:
--     prosodyctl check config
-- If there are any errors, it will let you know what and where
-- they are, otherwise it will keep quiet.
--
-- The only thing left to do is rename this file to remove the .dist ending, and fill in the
-- blanks. Good luck, and happy Jabbering!

---------- Server-wide settings ----------
-- Settings in this section apply to the whole server and are the default settings
-- for any virtual hosts

-- This is a (by default, empty) list of accounts that are admins
-- for the server. Note that you must create the accounts separately
-- (see https://prosody.im/doc/creating_accounts for info)
-- Example: admins = { "user1@example.com", "user2@example.net" }
admins = { }

-- Enable use of libevent for better performance under high load
-- For more information see: https://prosody.im/doc/libevent
--use_libevent = true

-- Prosody will always look in its source directory for modules, but
-- this option allows you to specify additional locations where Prosody
-- will look for modules first. For community modules, see https://modules.prosody.im/
-- For a local administrator it's common to place local modifications
-- under /usr/local/ hierarchy:
plugin_paths = { "/usr/local/lib/prosody/modules" }

-- This is the list of modules Prosody will load on startup.
-- It looks for mod_modulename.lua in the plugins folder, so make sure that exists too.
-- Documentation for bundled modules can be found at: https://prosody.im/doc/modules
modules_enabled = {

    -- Generally required
        "roster"; -- Allow users to have a roster. Recommended ;)
        "saslauth"; -- Authentication for clients and servers. Recommended if you want to log in.
        "tls"; -- Add support for secure TLS on c2s/s2s connections
        "dialback"; -- s2s dialback support
        "disco"; -- Service discovery

    -- Not essential, but recommended
        "carbons"; -- Keep multiple clients in sync
        "pep"; -- Enables users to publish their avatar, mood, activity, playing music and more
        "private"; -- Private XML storage (for room bookmarks, etc.)
        "blocklist"; -- Allow users to block communications with other users
        "vcard4"; -- User profiles (stored in PEP)
        "vcard_legacy"; -- Conversion between legacy vCard and PEP Avatar, vcard
        "limits"; -- Enable bandwidth limiting for XMPP connections

    -- Nice to have
-- -- --        "version"; -- Replies to server version requests
-- -- --        "uptime"; -- Report how long server has been running
        "time"; -- Let others know the time here on this server
        "ping"; -- Replies to XMPP pings with pongs
        "register"; -- Allow users to register on this server using a client and change passwords
        --"mam"; -- Store messages in an archive and allow users to access it
        --"csi_simple"; -- Simple Mobile optimizations

    -- Admin interfaces
        "admin_adhoc"; -- Allows administration via an XMPP client that supports ad-hoc commands
        --"admin_telnet"; -- Opens telnet console interface on localhost port 5582

    -- HTTP modules
-- -- --        --"bosh"; -- Enable BOSH clients, aka "Jabber over HTTP"
        "bosh"; -- Enable BOSH clients, aka "Jabber over HTTP"
        --"websocket"; -- XMPP over WebSockets
        --"http_files"; -- Serve static files from a directory over HTTP

    -- Other specific functionality
        "posix"; -- POSIX functionality, sends server to background, enables syslog, etc.
        --"groups"; -- Shared roster support
        --"server_contact_info"; -- Publish contact information for this service
        --"announce"; -- Send announcement to all online users
        --"welcome"; -- Welcome users who register accounts
        --"watchregistrations"; -- Alert admins of registrations
        --"motd"; -- Send a message to users when they log in
        --"legacyauth"; -- Legacy authentication. Only used by some old clients and bots.
        --"proxy65"; -- Enables a file transfer proxy service which clients behind NAT can use
}

-- These modules are auto-loaded, but should you want
-- to disable them then uncomment them here:
modules_disabled = {
    -- "offline"; -- Store offline messages
    -- "c2s"; -- Handle client connections
    -- "s2s"; -- Handle server-to-server connections
}

-- Disable account creation by default, for security
-- For more information see https://prosody.im/doc/creating_accounts
allow_registration = false

-- Debian:
--   Do not send the server to background, either systemd or start-stop-daemon take care of that.
--
daemonize = false;

-- Debian:
--   Please, don't change this option since /run/prosody/
--   is one of the few directories Prosody is allowed to write to
--
pidfile = "/run/prosody/prosody.pid";

-- Force clients to use encrypted connections? This option will
-- prevent clients from authenticating unless they are using encryption.

c2s_require_encryption = true

-- Force servers to use encrypted connections? This option will
-- prevent servers from authenticating unless they are using encryption.

s2s_require_encryption = true

-- Force certificate authentication for server-to-server connections?

s2s_secure_auth = false

-- Some servers have invalid or self-signed certificates. You can list
-- remote domains here that will not be required to authenticate using
-- certificates. They will be authenticated using DNS instead, even
-- when s2s_secure_auth is enabled.

--s2s_insecure_domains = { "insecure.example" }

-- Even if you disable s2s_secure_auth, you can still require valid
-- certificates for some domains by specifying a list here.

--s2s_secure_domains = { "jabber.org" }

-- Enable rate limits for incoming client and server connections

limits = {
  c2s = {
    rate = "10kb/s";
  };
  s2sin = {
    rate = "30kb/s";
  };
}

-- Select the authentication backend to use. The 'internal' providers
-- use Prosody's configured data storage to store the authentication data.

authentication = "internal_hashed"

-- Select the storage backend to use. By default Prosody uses flat files
-- in its configured data directory, but it also supports more backends
-- through modules. An "sql" backend is included by default, but requires
-- additional dependencies. See https://prosody.im/doc/storage for more info.

--storage = "sql" -- Default is "internal" (Debian: "sql" requires one of the
-- lua-dbi-sqlite3, lua-dbi-mysql or lua-dbi-postgresql packages to work)

-- For the "sql" backend, you can uncomment *one* of the below to configure:
--sql = { driver = "SQLite3", database = "prosody.sqlite" } -- Default. 'database' is the filename.
--sql = { driver = "MySQL", database = "prosody", username = "prosody", password = "secret", host = "localhost" }
--sql = { driver = "PostgreSQL", database = "prosody", username = "prosody", password = "secret", host = "localhost" }

-- Archiving configuration
-- If mod_mam is enabled, Prosody will store a copy of every message. This
-- is used to synchronize conversations between multiple clients, even if
-- they are offline. This setting controls how long Prosody will keep
-- messages in the archive before removing them.

archive_expires_after = "1w" -- Remove archived messages after 1 week

-- You can also configure messages to be stored in-memory only. For more
-- archiving options, see https://prosody.im/doc/modules/mod_mam

-- Logging configuration
-- For advanced logging see https://prosody.im/doc/logging
--
-- Debian:
--  Logs info and higher to /var/log
--  Logs errors to syslog also
log = {
    -- Log files (change 'info' to 'debug' for debug logs):
    info = "/var/log/prosody/prosody.log";
    error = "/var/log/prosody/prosody.err";
    -- Syslog:
    { levels = { "error" }; to = "syslog";  };
}

-- Uncomment to enable statistics
-- For more info see https://prosody.im/doc/statistics
-- statistics = "internal"

-- Certificates
-- Every virtual host and component needs a certificate so that clients and
-- servers can securely verify its identity. Prosody will automatically load
-- certificates/keys from the directory specified here.
-- For more information, including how to use 'prosodyctl' to auto-import certificates
-- (from e.g. Let's Encrypt) see https://prosody.im/doc/certificates

-- Location of directory to find certificates in (relative to main config file):
certificates = "certs"

-- HTTPS currently only supports a single certificate, specify it here:
--https_certificate = "/etc/prosody/certs/localhost.crt"

----------- Virtual hosts -----------
-- You need to add a VirtualHost entry for each domain you wish Prosody to serve.
-- Settings under each VirtualHost entry apply *only* to that host.
-- It's customary to maintain VirtualHost entries in separate config files
-- under /etc/prosody/conf.d/ directory. Examples of such config files can
-- be found in /etc/prosody/conf.avail/ directory.

------ Additional config files ------
-- For organizational purposes you may prefer to add VirtualHost and
-- Component definitions in their own config files. This line includes
-- all config files in /etc/prosody/conf.d/

-- -- -- VirtualHost "localhost"
VirtualHost "video3.example.org"

--VirtualHost "example.com"
--  certificate = "/path/to/example.crt"

------ Components ------
-- You can specify components to add hosts that provide special services,
-- like multi-user conferences, and transports.
-- For more information on components, see https://prosody.im/doc/components

---Set up a MUC (multi-user chat) room server on conference.example.com:
--Component "conference.example.com" "muc"
--- Store MUC messages in an archive and allow users to access it
--modules_enabled = { "muc_mam" }

---Set up an external component (default component port is 5347)
--
-- External components allow adding various services, such as gateways/
-- transports to other networks like ICQ, MSN and Yahoo. For more info
-- see: https://prosody.im/doc/components#adding_an_external_component
--
--Component "gateway.example.com"
--  component_secret = "password"
Include "conf.d/*.cfg.lua"

I enabled bosh here because of nginx/error_log entries complaining about non-existing upstream to port 5280.

Included is the file /etc/prosody/conf.d/video3.example.org which you can see here:

### /etc/prosody/conf.d/video3.example.org
unlimited_jids = { "focus@auth.video3.example.org", "jvb@auth.video3.example.org" }
-- -- -- gives update error plugin_paths = { "/srv/nginx/video3.example.org/prosody-plugins/" }
plugin_paths = { "/usr/share/jitsi-meet/prosody-plugins/" }

-- domain mapper options, must at least have domain base set to use the mapper
muc_mapper_domain_base = "video3.example.org";

turncredentials_secret = "XXXXXXXXXY";

turncredentials = {
-- -- --   { type = "stun", host = "video3.example.org", port = "4446" },
  { type = "stun", host = "meet-jit-si-turnrelay.jitsi.net", port = "443" },
--  { type = "turn", host = "video3.example.org", port = "4446", transport = "udp" },
-- -- --   { type = "turns", host = "video3.example.org", port = "443", transport = "tcp" }
  { type = "turns", host = "video3.example.org", port = "443", transport = "tcp" }
};

cross_domain_bosh = false;
consider_bosh_secure = true;
-- https_ports = { }; -- Remove this line to prevent listening on port 5284
-- https://github.com/jitsi/jitsi-meet/issues/6473
-- comment this line to avoid error "portmanager: Error binding encrypted port for https: No certificate present in SSL/TLS configuration for https port"
-- -- -- https_ports = { 5281 };

VirtualHost "video3.example.org"
        -- enabled = false -- Remove this line to enable this host
        authentication = "internal_plain"
        -- Properties below are modified by jitsi-meet-tokens package config
        -- and authentication above is switched to "token"
        --app_id="example_app_id"
        --app_secret="example_app_secret"
        -- Assign this host a certificate for TLS, otherwise it would use the one
        -- set in the global section (if any).
        -- Note that old-style SSL on port 5223 only supports one certificate, and will always
        -- use the global one.
        ssl = {
                key = "/etc/prosody/certs/video3.example.org.key";
                certificate = "/etc/prosody/certs/video3.example.org.crt";
        }
        speakerstats_component = "speakerstats.video3.example.org"
        conference_duration_component = "conferenceduration.video3.example.org"
        -- we need bosh
        modules_enabled = {
            "bosh";
            "pubsub";
            "ping"; -- Enable mod_ping
            "speakerstats";
            "turncredentials";
            "conference_duration";
        }
        -- -- -- c2s_require_encryption = false

VirtualHost "guest.video3.example.org"
        -- enabled = false -- Remove this line to enable this host
        enabled = true
        authentication = "anonymous"
        -- Properties below are modified by jitsi-meet-tokens package config
        -- and authentication above is switched to "token"
        --app_id="example_app_id"
        --app_secret="example_app_secret"
        -- Assign this host a certificate for TLS, otherwise it would use the one
        -- set in the global section (if any).
        -- Note that old-style SSL on port 5223 only supports one certificate, and will always
        -- use the global one.
        ssl = {
                key = "/etc/prosody/certs/video3.example.org.key";
                certificate = "/etc/prosody/certs/video3.example.org.crt";
        }
        speakerstats_component = "speakerstats.video3.example.org"
        conference_duration_component = "conferenceduration.video3.example.org"
        -- we need bosh
        modules_enabled = {
            "bosh";
            "pubsub";
            "ping"; -- Enable mod_ping
            "speakerstats";
            "turncredentials";
            "conference_duration";
        }
        c2s_require_encryption = false

Component "conference.video3.example.org" "muc"
    restrict_room_creation = true
    storage = "memory"
    modules_enabled = {
        "muc_meeting_id";
        "muc_domain_mapper";
        -- "token_verification";
    }
    admins = { "focus@auth.video3.example.org" }
    muc_room_locking = false
    muc_room_default_public_jids = true

-- internal muc component
Component "internal.auth.video3.example.org" "muc"
    storage = "memory"
    modules_enabled = {
      "ping";
    }
    admins = { "focus@auth.video3.example.org", "jvb@auth.video3.example.org" }
    muc_room_locking = false
    muc_room_default_public_jids = true

VirtualHost "auth.video3.example.org"
    modules_enabled = { "limits_exception"; }
    ssl = {
        key = "/etc/prosody/certs/auth.video3.example.org.key";
        certificate = "/etc/prosody/certs/auth.video3.example.org.crt";
    }
    authentication = "internal_plain"

Component "focus.video3.example.org" "client_proxy"
    target_address = "focus@auth.video3.example.org"
    component_secret = "XXXXXXXXXX"

Component "speakerstats.video3.example.org" "speakerstats_component"
    muc_component = "conference.video3.example.org"

Component "conferenceduration.video3.example.org" "conference_duration_component"
    muc_component = "conference.video3.example.org"

Platform

Browser / app / sdk version

All current, Chromium 130, Firefox 131.

Relevant log output

No response

Reproducibility

More details?

No response

damencho commented 1 week ago

PC 2: Block everything outgoing to my server IP except 80,443 then start Firefox 2, join

How are you blocking it? Are you able from the turn3.example.org machine access UDP port 10000 on video3.example.org? If that is the same machine, are you able from that machine send and receive udp packets to the public IP address of the bridge?

Also, make sure that you have valid certificates for turn3.example.org.

ned64 commented 1 week ago

Thanks for the fast response. On PC2 I block like this (copied from commandline history, IP address anonymised):

   998  19:23   iptables -A INPUT -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT
   999  19:23   iptables -A INPUT -m conntrack --ctstate INVALID -j DROP
  1000  19:23   iptables -A OUTPUT -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT
  1001  19:23   iptables -A OUTPUT -d 1.2.3.4 -p tcp ! --dport 443 -j DROP
  1002  19:24   iptables -A OUTPUT -d 1.2.3.4 -p udp ! --dport 443 -j DROP
  1003  19:24   ip6tables -A OUTPUT -d 1:2:3:4::0  -j DROP

The turn3 server cert was indeed not renewed automatically because my port 80 listening section in nginx (copied from the documentation) re-directs 301 to ssl. Will need to check and fix this.

damencho commented 1 week ago

Which is this PC2 where everything is running or the client PC where you access the deployment?

ned64 commented 1 week ago

The Jitsi Meet Server is in a data centre and has a fixed IP address. The PC1, PC2 and Android clients are separate machines elsewhere. I blocked the non-{80,443}-ports locally on PC2 only which means that one is the test machine with only {80,443}.

I have changed nginx config files to pass .well-known for turn3 and then renewed coturn's certificates.

Here is coturns config. The certificate is accepted by coturn. journalctl -u coturn has now stopped showing TLS errors.

### /etc/turnserver.conf
# jitsi-meet coturn config. Do not modify this line
use-auth-secret
keep-address-family
static-auth-secret=XXXXXXXXXY
realm=video3.example.org
cert=/etc/pki/coturn/turn3-server-chain.crt
pkey=/etc/pki/coturn/private/turn3-server.key
no-multicast-peers
no-cli
no-loopback-peers
no-tcp-relay
no-tcp
# listening-port=3478
listening-port=5348
#the following option is ignored, tls port is previous port + 1
tls-listening-port=5349
no-tlsv1
no-tlsv1_1
# https://ssl-config.mozilla.org/#server=haproxy&version=2.1&config=intermediate&openssl=1.1.0g&guideline=5.4
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
# jitsi-meet coturn relay disable config. Do not modify this line
denied-peer-ip=0.0.0.0-0.255.255.255
denied-peer-ip=10.0.0.0-10.255.255.255
denied-peer-ip=100.64.0.0-100.127.255.255
denied-peer-ip=127.0.0.0-127.255.255.255
denied-peer-ip=169.254.0.0-169.254.255.255
denied-peer-ip=127.0.0.0-127.255.255.255
denied-peer-ip=172.16.0.0-172.31.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=192.88.99.0-192.88.99.255
denied-peer-ip=192.168.0.0-192.168.255.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
denied-peer-ip=240.0.0.0-255.255.255.255
denied-peer-ip=::1
denied-peer-ip=64:ff9b::-64:ff9b::ffff:ffff
denied-peer-ip=::ffff:0.0.0.0-::ffff:255.255.255.255
denied-peer-ip=100::-100::ffff:ffff:ffff:ffff
denied-peer-ip=2001::-2001:1ff:ffff:ffff:ffff:ffff:ffff:ffff
denied-peer-ip=2002::-2002:ffff:ffff:ffff:ffff:ffff:ffff:ffff
denied-peer-ip=fc00::-fdff:ffff:ffff:ffff:ffff:ffff:ffff:ffff
denied-peer-ip=fe80::-febf:ffff:ffff:ffff:ffff:ffff:ffff:ffff
syslog

Is the realm correctly set in coturn? video3... not turn3...?

The problem persists, though. I have a few errors on offer from logs, like these ones from /var/log/nginx/error.log:

2024/11/13 00:03:08 [error] 14463#14463: *731 connect() failed (111: Connection refused) while connecting to upstream, client: 20:30:40:50::2, server: [::]:443, upstream: "1.2.3.4:5349", bytes from/to client:0/0, bytes from/to upstream:0/0
(...)
2024/11/13 00:12:03 [error] 14463#14463: *958 recv() failed (104: Connection reset by peer) while proxying and reading from upstream, client: 20:30:40:50::2, server: [::]:443, upstream: "1.2.3.4:5349", bytes from/to client:609/4210, bytes from/to upstream:4210/1288

This (20:30:etc) is the IPv6 address of the Firefox 1 on PC1 (which also has trouble with media but it is not connected with mic/cam).

# ss -tulpan | grep 5349     (interesting lines only)
udp   UNCONN    0      0     1.2.3.4:5349      0.0.0.0:*     users:(("turnserver",pid=14780,fd=30))                                               
udp   UNCONN    0      0     1.2.3.4:5349      0.0.0.0:*     users:(("turnserver",pid=14780,fd=29))                                               
udp   UNCONN    0      0     1.2.3.4:5349      0.0.0.0:*     users:(("turnserver",pid=14780,fd=28))                                               
udp   UNCONN    0      0     1.2.3.4:5349      0.0.0.0:*     users:(("turnserver",pid=14780,fd=27))                                               
(...)
tcp   LISTEN    0      1024  1.2.3.4:5349      0.0.0.0:*     users:(("turnserver",pid=14780,fd=83))                                               
tcp   LISTEN    0      1024  127.0.0.1:5349    0.0.0.0:*     users:(("turnserver",pid=14780,fd=75))                                               
tcp   LISTEN    0      1024  [::1]:5349        [::]:*        users:(("turnserver",pid=14780,fd=71))                                               
tcp   LISTEN    0      1024  1:2:3:4::0:5349   [::]:*        users:(("turnserver",pid=14780,fd=91))

So coturn is listening on all interfaces and IPv* addresses. Why the upstream error to coturn?

damencho commented 1 week ago

Is the realm correctly set in coturn? video3... not turn3...?

yes. That should be the meet deployment main virtual host. https://github.com/jitsi/jitsi-meet/blob/86d1bd3c7804aa894ecf111a0ae68143fe227281/doc/debian/jitsi-meet-turn/turnserver.conf#L5

{ type = "turns", host = "video3.example.org", port = "443", transport = "tcp" }

Your prosody config is wrong, that should be turn3.example.org.

ned64 commented 1 week ago

Whoa, that did it! Here are the corrected config files for prosperity (future me perhaps).

### /etc/turnserver.conf
# jitsi-meet coturn config. Do not modify this line
use-auth-secret
keep-address-family
static-auth-secret=XXXXXXXXXY
realm=video3.example.org
cert=/etc/pki/coturn/turn3-server-chain.crt
pkey=/etc/pki/coturn/private/turn3-server.key
no-multicast-peers
no-cli
no-loopback-peers
no-tcp-relay
no-tcp
# listening-port=3478
listening-port=5348
#the following option is ignored, tls port is previous port + 1
tls-listening-port=5349
no-tlsv1
no-tlsv1_1
# https://ssl-config.mozilla.org/#server=haproxy&version=2.1&config=intermediate&openssl=1.1.0g&guideline=5.4
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
# jitsi-meet coturn relay disable config. Do not modify this line
denied-peer-ip=0.0.0.0-0.255.255.255
denied-peer-ip=10.0.0.0-10.255.255.255
denied-peer-ip=100.64.0.0-100.127.255.255
denied-peer-ip=127.0.0.0-127.255.255.255
denied-peer-ip=169.254.0.0-169.254.255.255
denied-peer-ip=127.0.0.0-127.255.255.255
denied-peer-ip=172.16.0.0-172.31.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=192.88.99.0-192.88.99.255
denied-peer-ip=192.168.0.0-192.168.255.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
denied-peer-ip=240.0.0.0-255.255.255.255
denied-peer-ip=::1
denied-peer-ip=64:ff9b::-64:ff9b::ffff:ffff
denied-peer-ip=::ffff:0.0.0.0-::ffff:255.255.255.255
denied-peer-ip=100::-100::ffff:ffff:ffff:ffff
denied-peer-ip=2001::-2001:1ff:ffff:ffff:ffff:ffff:ffff:ffff
denied-peer-ip=2002::-2002:ffff:ffff:ffff:ffff:ffff:ffff:ffff
denied-peer-ip=fc00::-fdff:ffff:ffff:ffff:ffff:ffff:ffff:ffff
denied-peer-ip=fe80::-febf:ffff:ffff:ffff:ffff:ffff:ffff:ffff
syslog

Nginx main config - not much, most in the other two files:

/etc/nginx/nginx.conf
#user www-data;
user nginx;
worker_processes auto;

pid /run/nginx.pid;
include /etc/nginx/modules-enabled/*.conf;

events {
    worker_connections 768;
    # multi_accept on;
}

http {
    ##
    # Basic Settings
    ##

    sendfile on;
    tcp_nopush on;
    tcp_nodelay on;
    keepalive_timeout 65s;
    types_hash_max_size 4096;
    server_tokens off;

    server_names_hash_bucket_size 64;
    # server_name_in_redirect off;

    include /etc/nginx/mime.types;
        types {
        # add support for wasm MIME type, that is required by specification and it is not part of default mime.types file
        application/wasm wasm;
    }
    default_type application/octet-stream;
    charset utf-8;

    # Settings for the non-TLS server.
    server {
        listen      80 default_server;
    listen      [::]:80 default_server;
        server_name  _;  # match all
    root        /srv/nginx/video3.example.org;

    # ensure .well-known content is available without redirect (for getssl)
    location ~ ^/.well-known/(.*)$
    {
        add_header 'Access-Control-Allow-Origin' '*';
        alias /srv/nginx/video3.example.org/$1/$2;
    }

    # force TLS/https
    return 301 https://$host$request_uri;

    } # end server port 80

    ##
    # Virtual Host Configs
    ##

    include /etc/nginx/conf.d/*.conf;
    include /etc/nginx/sites-enabled/*;
}

Nginx my web site/jm welcome server:

### /etc/nginx/sites-enabled/video3.example.org
server {
    listen 80;
    listen [::]:80;
    server_name video3.example.org turn3.example.org;

    location ^~ /.well-known/acme-challenge/ {
       default_type "text/plain";
       root         /srv/nginx/video3.example.org;
    }
    location = /.well-known/acme-challenge/ {
       return 404;
    }
    location / {
       return 301 https://$host$request_uri;
    }
}
server {
    listen 444 ssl;
    listen [::]:444 ssl;
    server_name video3.example.org;

    ssl_protocols TLSv1.2 TLSv1.3;
    ssl_prefer_server_ciphers on;
    ssl_ciphers "EECDH+ECDSA+AESGCM:EECDH+aRSA+AESGCM:EECDH+ECDSA+SHA256:EECDH+aRSA+SHA256:EECDH+ECDSA+SHA384:EECDH+ECDSA+SHA256:EECDH+aRSA+SHA384:EDH+aRSA+AESGCM:EDH+aRSA+SHA256:EDH+aRSA:EECDH:!aNULL:!eNULL:!MEDIUM:!LOW:!3DES:!MD5:!EXP:!PSK:!SRP:!DSS:!RC4:!SEED";

    add_header Strict-Transport-Security "max-age=31536000";

    ssl_certificate /etc/pki/nginx/video3-server-chain.crt;
    ssl_certificate_key /etc/pki/nginx/private/video3-server.key;

    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/video3.example.org-config.js;
    }

    # is this used???
    location = interface_config.js {
        alias /etc/jitsi/meet/video3.example.org-interface_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;
    }

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

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

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

       alias /etc/jitsi/meet/video3.example.org-config.js;
    }

    # 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;
    }

    #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;
    }
}

Nginx coturn multiplex for turnserver (split traffic to jm webserver and UDP streaming for firewalled guests):

/etc/nginx/modules-enabled/coturn-multiplex.conf
stream {
    map $ssl_preread_server_name $name {
        video3.example.org web_backend;
        turn3.example.org turn_backend;
    }

    upstream web_backend {
        server 127.0.0.1:444;
    }

    upstream turn_backend {
        server 1.2.3.4:5349;
        ### server 1.2.3.4:5348;
        ### server 127.0.0.1:5348;
        ### server 127.0.0.1:5349;
    }

    server {
        listen 443;
        listen [::]:443;

        # since 1.11.5
        ssl_preread on;

        proxy_pass $name;

        # Increase buffer to serve video
        proxy_buffer_size 20m;
    }
}
damencho commented 1 week ago

Please, when you have questions or problems use the community forum before opening new issues, thank you.

ned64 commented 1 week ago

OK, I apologise! Thanks again for the help!