I can’t start videoconference on my Jitsi server with JWT enabled (which Is connected with Moodle). There are no logs in Prosody or Jicofo that would give me any clue. The best error I got was from developer console:
VM336 lib-jitsi-meet.min.js:2 2022-04-11T07:54:21.748Z [JitsiMeetJS.js] <Object.getGlobalOnErrorHandler>: UnhandledError: Focus error, retry after 1000 Script: null Line: null Column: null StackTrace: Error: Focus error, retry after 1000 VM336 lib-jitsi-meet.min.js:2
at rr._allocateConferenceFocusError (https://meet.vc.example.com/libs/lib-jitsi-meet.min.js?v=5913:2:273076)
at https://meet.vc.example.com/libs/lib-jitsi-meet.min.js?v=5913:2:272117
at P.Handler.handler (https://meet.vc.example.com/libs/lib-jitsi-meet.min.js?v=5913:2:644039)
at P.Handler.run (https://meet.vc.example.com/libs/lib-jitsi-meet.min.js?v=5913:2:639338)
at https://meet.vc.example.com/libs/lib-jitsi-meet.min.js?v=5913:2:647776
at Object.forEachChild (https://meet.vc.example.com/libs/lib-jitsi-meet.min.js?v=5913:2:631006)
at P.Connection._dataRecv (https://meet.vc.example.com/libs/lib-jitsi-meet.min.js?v=5913:2:647625)
at O.Websocket._onMessage (https://meet.vc.example.com/libs/lib-jitsi-meet.min.js?v=5913:2:676855)
r @ VM336 lib-jitsi-meet.min.js:2
getGlobalOnErrorHandler @ VM336 lib-jitsi-meet.min.js:2
window.onerror @ VM337 app.bundle.min.js:138
callErrorHandler @ VM336 lib-jitsi-meet.min.js:2
rr._allocateConferenceFocusError @ VM336 lib-jitsi-meet.min.js:2
(anonymous) @ VM336 lib-jitsi-meet.min.js:2
(anonymous) @ VM336 lib-jitsi-meet.min.js:2
run @ VM336 lib-jitsi-meet.min.js:2
(anonymous) @ VM336 lib-jitsi-meet.min.js:2
forEachChild @ VM336 lib-jitsi-meet.min.js:2
_dataRecv @ VM336 lib-jitsi-meet.min.js:2
_onMessage @ VM336 lib-jitsi-meet.min.js:2
2022-04-11T07:54:21.749Z [modules/xmpp/moderator.js] <rr._allocateConferenceFocusError>: Focus error, retry after 1000 VM336 lib-jitsi-meet.min.js:2
<iq xmlns="jabber:client" id="de4c8340-137e-4aed-bfd7-bbda7050b50a:sendIQ" to="ba495caa-296c-49ba-afe4-9df567ffe0bf@meet.vc.example.com/ChDbh3PM" from="focus.meet.vc.example.com" type="error">
<error type="cancel">
<service-unavailable xmlns="urn:ietf:params:xml:ns:xmpp-stanzas"></service-unavailable>
</error>
</iq>
r @ VM336 lib-jitsi-meet.min.js:2
rr._allocateConferenceFocusError @ VM336 lib-jitsi-meet.min.js:2
(anonymous) @ VM336 lib-jitsi-meet.min.js:2
(anonymous) @ VM336 lib-jitsi-meet.min.js:2
run @ VM336 lib-jitsi-meet.min.js:2
(anonymous) @ VM336 lib-jitsi-meet.min.js:2
forEachChild @ VM336 lib-jitsi-meet.min.js:2
_dataRecv @ VM336 lib-jitsi-meet.min.js:2
_onMessage @ VM336 lib-jitsi-meet.min.js:2
2022-04-11T07:54:21.916Z [conference.js] <yoe._onConferenceFailed>: CONFERENCE FAILED: conference.focusDisconnected focus.meet.vc.example.com 1 VM336 lib-jitsi-meet.min.js:2
i @ VM337 app.bundle.min.js:138
_onConferenceFailed @ VM337 app.bundle.min.js:138
r.emit @ VM336 lib-jitsi-meet.min.js:2
r.emit @ VM336 lib-jitsi-meet.min.js:2
rr._allocateConferenceFocusError @ VM336 lib-jitsi-meet.min.js:2
(anonymous) @ VM336 lib-jitsi-meet.min.js:2
(anonymous) @ VM336 lib-jitsi-meet.min.js:2
run @ VM336 lib-jitsi-meet.min.js:2
(anonymous) @ VM336 lib-jitsi-meet.min.js:2
forEachChild @ VM336 lib-jitsi-meet.min.js:2
_dataRecv @ VM336 lib-jitsi-meet.min.js:2
_onMessage @ VM336 lib-jitsi-meet.min.js:2
To me it looks like prosody can’t connect to focus account in prosody session, logs don’t show any problem at attempting to start the session from the lobby:
Apr 11 11:53:58 c2s561f08835120 info Client connected
Apr 11 11:53:59 c2s561f08835120 info Authenticated as dbb02e66-b29b-48c9-9282-22124fc55a32@meet.vc.example.com
Environment
- Ubuntu 18.04
- Jitsi packages (this server is just JMS, videobridges are on other hosts and working fine with other JMS servers):
- jitsi-meet-prosody/stable,now 1.0.5913-1
- jitsi-meet-tokens/stable,now 1.0.5913-1
- jitsi-meet-turnserver/stable,now 1.0.5913-1
- jitsi-meet-web/stable,now 1.0.5913-1
- jitsi-meet-web-config/stable,now 1.0.5913-1
- prosody 0.11 nightly build 143 (2022-01-24, 458c5f8d5d3e)
Config files
Prosody virtualhost:
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 = "meet.vc.example.com";
external_service_secret = "**********************";
external_services = {
{ type = "stun", host = "meet.vc.example.com", port = 3478 },
{ type = "turn", host = "meet.vc.example.com", port = 3478, transport = "udp", secret = true, ttl = 86400, algorithm = "turn" },
{ type = "turns", host = "meet.vc.example.com", port = 5349, transport = "tcp", secret = true, ttl = 86400, algorithm = "turn" }
};
cross_domain_bosh = false;
consider_bosh_secure = true;
https_ports = { }; -- disable listening on port 5284
cross_domain_websocket = true;
consider_websocket_secure = true;
-- https://ssl-config.mozilla.org/#server=haproxy&version=2.1&config=intermediate&openssl=1.1.0g&guideline=5.4
ssl = {
protocol = "tlsv1_2+";
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"
}
unlimited_jids = {
"focus@auth.meet.vc.example.com",
"jvb@auth.meet.vc.example.com"
}
VirtualHost "guest.meet.vc.example.com"
authentication = "token"
app_id="moodle.example.com"
app_secret="**********************"
c2s_require_encryption = false
allow_empty_token = true
VirtualHost "meet.vc.example.com"
authentication = "token"
app_id="moodle.example.com"
app_secret="**********************"
allow_empty_token = false
allow_unencrypted_plain_auth = true
ssl = {
key = "/etc/prosody/certs/meet.vc.example.com.key";
certificate = "/etc/prosody/certs/meet.vc.example.com.crt";
}
av_moderation_component = "avmoderation.meet.vc.example.com"
speakerstats_component = "speakerstats.meet.vc.example.com"
conference_duration_component = "conferenceduration.meet.vc.example.com"
-- we need bosh
modules_enabled = {
"bosh";
"websocket";
"smacks";
"pubsub";
"ping"; -- Enable mod_ping
"speakerstats";
"external_services";
"conference_duration";
"muc_lobby_rooms";
"muc_breakout_rooms";
"av_moderation";
"presence_identity";
"frozen_nick";
}
c2s_require_encryption = false
lobby_muc = "lobby.meet.vc.example.com"
breakout_rooms_muc = "breakout.meet.vc.example.com"
main_muc = "conference.meet.vc.example.com"
Component "conference.meet.vc.example.com" "muc"
restrict_room_creation = true
storage = "memory"
modules_enabled = {
"muc_meeting_id";
"muc_domain_mapper";
"polls";
"muc_rate_limit";
"token_verification";
"token_affiliation";
"token_owner_party";
}
party_check_timeout = 20
admins = { "focus@auth.meet.vc.example.com" }
muc_room_locking = false
muc_room_default_public_jids = true
Component "breakout.meet.vc.example.com" "muc"
restrict_room_creation = true
storage = "memory"
modules_enabled = {
"muc_meeting_id";
"muc_domain_mapper";
"token_verification";
"muc_rate_limit";
"polls";
}
admins = { "focus@auth.meet.vc.example.com" }
muc_room_locking = false
muc_room_default_public_jids = true
-- internal muc component
Component "internal.auth.meet.vc.example.com" "muc"
storage = "memory"
modules_enabled = {
"ping";
}
admins = { "focus@auth.meet.vc.example.com", "jvb@auth.meet.vc.example.com" }
muc_room_locking = false
muc_room_default_public_jids = true
VirtualHost "auth.meet.vc.example.com"
ssl = {
key = "/etc/prosody/certs/auth.meet.vc.example.com.key";
certificate = "/etc/prosody/certs/auth.meet.vc.example.com.crt";
}
modules_enabled = {
"limits_exception";
}
authentication = "internal_hashed"
-- Proxy to jicofo's user JID, so that it doesn't have to register as a component.
Component "focus.meet.vc.example.com" "client_proxy"
target_address = "focus@auth.meet.vc.example.com"
Component "speakerstats.meet.vc.example.com" "speakerstats_component"
muc_component = "conference.meet.vc.example.com"
Component "conferenceduration.meet.vc.example.com" "conference_duration_component"
muc_component = "conference.meet.vc.example.com"
Component "avmoderation.meet.vc.example.com" "av_moderation_component"
muc_component = "conference.meet.vc.example.com"
Component "lobby.meet.vc.example.com" "muc"
storage = "memory"
restrict_room_creation = true
muc_room_locking = false
muc_room_default_public_jids = true
modules_enabled = {
"muc_rate_limit";
"polls";
}
Jicofo ./config
:
# Jitsi Conference Focus settings
# sets the host name of the XMPP server
JICOFO_HOST=meet.vc.example.com
# sets the XMPP domain (default: none)
JICOFO_HOSTNAME=meet.vc.example.com
# sets the XMPP domain name to use for XMPP user logins
JICOFO_AUTH_DOMAIN=auth.meet.vc.example.com
# sets the username to use for XMPP user logins
JICOFO_AUTH_USER=focus
# sets the password to use for XMPP user logins
JICOFO_AUTH_PASSWORD=*************
# extra options to pass to the jicofo daemon
JICOFO_OPTS=""
# adds java system props that are passed to jicofo (default are for home and logging config file)
JAVA_SYS_PROPS="-Dnet.java.sip.communicator.SC_HOME_DIR_LOCATION=/etc/jitsi -Dconfig.file=/etc/jitsi/jicofo/jicofo.conf -Dnet.java.sip.communicator.SC_HOME_DIR_NAME=jicofo -Dnet.java.sip.communicator.SC_LOG_DIR_LOCATION=/var/log/jitsi -Djava.util.logging.config.file=/etc/jitsi/jicofo/logging.properties -Dlog4j2.formatMsgNoLookups=true"
Jicofo ./jicofo.conf
:
# Jicofo HOCON configuration. See reference.conf in /usr/share/jicofo/jicofo.jar for
#available options, syntax, and default values.
jicofo {
conference {
enable-auto-owner = true
}
octo {
enabled = True
id = 3
}
bridge {
max-bridge-participants = -1
stress-threshold = 0.8
selection-strategy = IntraRegionBridgeSelectionStrategy
}
authentication {
enabled: true
type: JWT
login-url: meet.vc.example.com
enable-auto-login = false
}
xmpp: {
client: {
client-proxy: focus.meet.vc.example.com
disable-certificate-verification = false
}
trusted-domains: [ "recorder.meet.vc.example.com" ]
}
bridge: {
brewery-jid: "JvbBrewery@internal.auth.meet.vc.example.com"
}
health: {
enabled: true
interval: "10 seconds"
timeout: "30 seconds"
max-check-duration: "20 seconds"
room-name-prefix: "__jicofo-health-check"
}
}
Nginx virtualhost:
# forcefully unconfigured FIXME
# server_names_hash_bucket_size 64;
types {
# nginx's default mime.types doesn't include a mapping for wasm
application/wasm wasm;
}
server {
listen 80;
listen [::]:80;
server_name meet.vc.example.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 meet.vc.example.com;
# Mozilla Guideline v5.4, nginx 1.17.7, OpenSSL 1.1.1d, intermediate configuration
ssl_protocols TLSv1.1 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;
set $prefix "";
ssl_certificate /etc/letsencrypt/live/meet.vc.example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/meet.vc.example.com/privkey.pem;
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;
# our custom init page
location = /static/close3.html {
return 301 https://vc.example.com;
}
location = /config.js {
alias /etc/jitsi/meet/meet.vc.example.com-config.js;
# disable config caching
add_header Last-Modified $date_gmt;
add_header Cache-Control 'no-store, no-cache';
if_modified_since off;
expires off;
etag off;
}
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://127.0.0.1:5280/http-bind?prefix=$prefix&$args;
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;
proxy_buffer_size 512k;
proxy_buffers 16 512k;
proxy_busy_buffers_size 512k;
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/meet.vc.example.com-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;
}
}