feat: Adds docs, config and scripts around the visitor mode. (#12658)
* feat: Moves handle of vnode from conferenceIQ stanza error to result. * feat: Handles redirected to visitor node event. * feat: Adds README and configurations. * squash: Drop comment. * copy edits * image fix * fix background for dark mode * fix the background * feat: Update s2soutinjection. * Update README commands formatting. * Update doc/extra-large-conference/README.md Co-authored-by: Saúl Ibarra Corretgé <saghul@jitsi.org> * squash: Creates a generateVisitorConfig helper. * squash: Moves the folder from doc. * squash: Update example. * squash: Drop config. * squash: Rename var to look like template. * squash: Fix plugins path. * squash: Fix sort order of import. * squash: Fix lint errors. Co-authored-by: scott boone <scott.e.boone@gmail.com> Co-authored-by: Scott Boone <scott.boone@8x8.com> Co-authored-by: Saúl Ibarra Corretgé <saghul@jitsi.org>
This commit is contained in:
parent
3445c513ba
commit
9fbbe05d6c
|
@ -47,6 +47,7 @@ import {
|
||||||
dataChannelClosed,
|
dataChannelClosed,
|
||||||
dataChannelOpened,
|
dataChannelOpened,
|
||||||
e2eRttChanged,
|
e2eRttChanged,
|
||||||
|
generateVisitorConfig,
|
||||||
getConferenceOptions,
|
getConferenceOptions,
|
||||||
kickedOut,
|
kickedOut,
|
||||||
lockStateChanged,
|
lockStateChanged,
|
||||||
|
@ -277,7 +278,8 @@ class ConferenceConnector {
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
constructor(resolve, reject) {
|
constructor(resolve, reject, conference) {
|
||||||
|
this._conference = conference;
|
||||||
this._resolve = resolve;
|
this._resolve = resolve;
|
||||||
this._reject = reject;
|
this._reject = reject;
|
||||||
this.reconnectTimeout = null;
|
this.reconnectTimeout = null;
|
||||||
|
@ -336,6 +338,26 @@ class ConferenceConnector {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
case JitsiConferenceErrors.REDIRECTED: {
|
||||||
|
generateVisitorConfig(APP.store.getState(), params);
|
||||||
|
|
||||||
|
connection.disconnect().then(() => {
|
||||||
|
connect(this._conference.roomName).then(con => {
|
||||||
|
const localTracks = getLocalTracks(APP.store.getState()['features/base/tracks']);
|
||||||
|
|
||||||
|
const jitsiTracks = localTracks.map(t => t.jitsiTrack);
|
||||||
|
|
||||||
|
// visitors connect muted
|
||||||
|
jitsiTracks.forEach(t => t.mute());
|
||||||
|
|
||||||
|
// TODO disable option to unmute audio or video
|
||||||
|
this._conference.startConference(con, jitsiTracks);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
case JitsiConferenceErrors.GRACEFUL_SHUTDOWN:
|
case JitsiConferenceErrors.GRACEFUL_SHUTDOWN:
|
||||||
APP.UI.notifyGracefulShutdown();
|
APP.UI.notifyGracefulShutdown();
|
||||||
break;
|
break;
|
||||||
|
@ -732,7 +754,7 @@ export default {
|
||||||
// XXX The API will take care of disconnecting from the XMPP
|
// XXX The API will take care of disconnecting from the XMPP
|
||||||
// server (and, thus, leaving the room) on unload.
|
// server (and, thus, leaving the room) on unload.
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
new ConferenceConnector(resolve, reject).connect();
|
new ConferenceConnector(resolve, reject, this).connect();
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -1349,7 +1371,7 @@ export default {
|
||||||
this._createRoom(localTracks);
|
this._createRoom(localTracks);
|
||||||
|
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
new ConferenceConnector(resolve, reject).connect();
|
new ConferenceConnector(resolve, reject, this).connect();
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
|
@ -15,6 +15,17 @@ upstream jvb1 {
|
||||||
server 127.0.0.1:9090;
|
server 127.0.0.1:9090;
|
||||||
keepalive 2;
|
keepalive 2;
|
||||||
}
|
}
|
||||||
|
map $arg_vnode $prosody_node {
|
||||||
|
default prosody;
|
||||||
|
v1 v1;
|
||||||
|
v2 v2;
|
||||||
|
v3 v3;
|
||||||
|
v4 v4;
|
||||||
|
v5 v5;
|
||||||
|
v6 v6;
|
||||||
|
v7 v7;
|
||||||
|
v8 v8;
|
||||||
|
}
|
||||||
server {
|
server {
|
||||||
listen 80;
|
listen 80;
|
||||||
listen [::]:80;
|
listen [::]:80;
|
||||||
|
@ -95,7 +106,7 @@ server {
|
||||||
|
|
||||||
# BOSH
|
# BOSH
|
||||||
location = /http-bind {
|
location = /http-bind {
|
||||||
proxy_pass http://prosody/http-bind?prefix=$prefix&$args;
|
proxy_pass http://$prosody_node/http-bind?prefix=$prefix&$args;
|
||||||
proxy_http_version 1.1;
|
proxy_http_version 1.1;
|
||||||
proxy_set_header X-Forwarded-For $remote_addr;
|
proxy_set_header X-Forwarded-For $remote_addr;
|
||||||
proxy_set_header Host $http_host;
|
proxy_set_header Host $http_host;
|
||||||
|
@ -104,7 +115,7 @@ server {
|
||||||
|
|
||||||
# xmpp websockets
|
# xmpp websockets
|
||||||
location = /xmpp-websocket {
|
location = /xmpp-websocket {
|
||||||
proxy_pass http://prosody/xmpp-websocket?prefix=$prefix&$args;
|
proxy_pass http://$prosody_node/xmpp-websocket?prefix=$prefix&$args;
|
||||||
proxy_http_version 1.1;
|
proxy_http_version 1.1;
|
||||||
proxy_set_header Upgrade $http_upgrade;
|
proxy_set_header Upgrade $http_upgrade;
|
||||||
proxy_set_header Connection "upgrade";
|
proxy_set_header Connection "upgrade";
|
||||||
|
|
|
@ -252,6 +252,34 @@ export function getConferenceOptions(stateful: IStateful) {
|
||||||
return options;
|
return options;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns an object aggregating the conference options.
|
||||||
|
*
|
||||||
|
* @param {IStateful} stateful - The redux store state.
|
||||||
|
* @param {Array<string>} params - The received parameters.
|
||||||
|
* @returns {void}
|
||||||
|
*/
|
||||||
|
export function generateVisitorConfig(stateful: IStateful, params: Array<string>) {
|
||||||
|
const [ vnode, focusJid ] = params;
|
||||||
|
|
||||||
|
const config = toState(stateful)['features/base/config'];
|
||||||
|
|
||||||
|
if (!config || !config.hosts) {
|
||||||
|
logger.warn('Wrong configuration, missing hosts.');
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const oldDomain = config.hosts.domain;
|
||||||
|
|
||||||
|
config.hosts.domain = `${vnode}.meet.jitsi`;
|
||||||
|
config.hosts.muc = config.hosts.muc.replace(oldDomain, config.hosts.domain);
|
||||||
|
config.hosts.visitorFocus = focusJid;
|
||||||
|
|
||||||
|
config.bosh += `?vnode=${vnode}`;
|
||||||
|
config.websocket += `?vnode=${vnode}`;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the UTC timestamp when the first participant joined the conference.
|
* Returns the UTC timestamp when the first participant joined the conference.
|
||||||
*
|
*
|
||||||
|
|
|
@ -339,6 +339,7 @@ export interface IConfig {
|
||||||
domain: string;
|
domain: string;
|
||||||
focus?: string;
|
focus?: string;
|
||||||
muc: string;
|
muc: string;
|
||||||
|
visitorFocus: string;
|
||||||
};
|
};
|
||||||
iAmRecorder?: boolean;
|
iAmRecorder?: boolean;
|
||||||
iAmSipGateway?: boolean;
|
iAmSipGateway?: boolean;
|
||||||
|
|
|
@ -0,0 +1,88 @@
|
||||||
|
WARNING: This is still a Work In Progress
|
||||||
|
|
||||||
|
The final implementation may diverge. Currently, only the participants after a
|
||||||
|
configured threshold will be just viewers (visitors) and there is no promotion
|
||||||
|
mechanism to become a main participant yet.
|
||||||
|
|
||||||
|
TODO:
|
||||||
|
* Merge messaging between visitor nodes and main conference
|
||||||
|
* Polls
|
||||||
|
* Raise hand to be promoted to enter the main conference
|
||||||
|
* Make sure it works with tenants.
|
||||||
|
|
||||||
|
|
||||||
|
# Low-latency conference streaming to very large audiences
|
||||||
|
|
||||||
|
To have a low-latency conference with a very large audience, the media and
|
||||||
|
signaling load must be spread beyond what can be handled by a typical Jitsi
|
||||||
|
installation. A call with 10k participants requires around 50 bridges on decent
|
||||||
|
vms (8+ cores). The main participants of a conference with a very large
|
||||||
|
audience will share a main prosody, like with normal conferences, and
|
||||||
|
additional prosody vms are needed to support signaling to the audience.
|
||||||
|
|
||||||
|
In the example configuration we use a 16 core machine. Eight of the cores are
|
||||||
|
used for the main prosody and other services (nginx, jicofo, etc) and the other
|
||||||
|
eight cores are used to run prosody services for visitors, i.e., "visitor
|
||||||
|
prosodies".
|
||||||
|
|
||||||
|
We consider 2000 participants per visitor node a safe value. So eight visitor
|
||||||
|
prosodies will be enough for one 10k participants meeting.
|
||||||
|
|
||||||
|
<img src="imgs/visitors-prosody.svg" alt="diagram of a central prosody connected to several visitor prosodies" width="500"/>
|
||||||
|
|
||||||
|
# Configuration
|
||||||
|
Use the `pre-configure.sh` script to configure your system, passing it the
|
||||||
|
number of visitor prosodies to set up.
|
||||||
|
`./pre-configure.sh 8`
|
||||||
|
|
||||||
|
The script will add for each visitor prosody:
|
||||||
|
- folders in `/etc/`
|
||||||
|
- a systemd unit file in `/lib/systemd/system/`
|
||||||
|
- a user for jicofo
|
||||||
|
- a config entry in jicofo.conf
|
||||||
|
|
||||||
|
Setting up configuration for the main prosody is a manual process:
|
||||||
|
- Add to the enabled modules list in the general part (e.g. [here](https://github.com/bjc/prosody/blob/76bf6d511f851c7cde8a81257afaaae0fb7a4160/prosody.cfg.lua.dist#L33)):
|
||||||
|
```
|
||||||
|
"s2s_bidi";
|
||||||
|
"certs_s2soutinjection";
|
||||||
|
"s2soutinjection";
|
||||||
|
"s2s_whitelist";
|
||||||
|
```
|
||||||
|
|
||||||
|
- Add the following config also in the general part (matching the number of prosodies you generated config for):
|
||||||
|
```
|
||||||
|
-- targets must be IPs, not hostnames
|
||||||
|
s2s_connect_overrides = {
|
||||||
|
["conference.v1.meet.jitsi"] = { "127.0.0.1", 52691 };
|
||||||
|
["conference.v2.meet.jitsi"] = { "127.0.0.1", 52692 };
|
||||||
|
["conference.v3.meet.jitsi"] = { "127.0.0.1", 52693 };
|
||||||
|
["conference.v4.meet.jitsi"] = { "127.0.0.1", 52694 };
|
||||||
|
["conference.v5.meet.jitsi"] = { "127.0.0.1", 52695 };
|
||||||
|
["conference.v6.meet.jitsi"] = { "127.0.0.1", 52696 };
|
||||||
|
["conference.v7.meet.jitsi"] = { "127.0.0.1", 52697 };
|
||||||
|
["conference.v8.meet.jitsi"] = { "127.0.0.1", 52698 };
|
||||||
|
}
|
||||||
|
-- allowed list of server-2-server connections
|
||||||
|
s2s_whitelist = {
|
||||||
|
"conference.v1.meet.jitsi", "conference.v2.meet.jitsi", "conference.v3.meet.jitsi", "conference.v4.meet.jitsi",
|
||||||
|
"conference.v5.meet.jitsi", "conference.v6.meet.jitsi", "conference.v7.meet.jitsi", "conference.v8.meet.jitsi"
|
||||||
|
};
|
||||||
|
```
|
||||||
|
|
||||||
|
- Make sure s2s is not in modules_disabled
|
||||||
|
- Enable `"xxl_conference";` module under the main virtual host (e.g. [here](https://github.com/jitsi/jitsi-meet/blob/f42772ec5bcc87ff6de17423d36df9bcad6e770d/doc/debian/jitsi-meet-prosody/prosody.cfg.lua-jvb.example#L57))
|
||||||
|
|
||||||
|
After configuring you can set the maximum number of main participants, before
|
||||||
|
redirecting to visitors.
|
||||||
|
```
|
||||||
|
hocon -f /etc/jitsi/jicofo/jicofo.conf set "jicofo.visitors.max-participants" 30
|
||||||
|
```
|
||||||
|
Now restart prosody and jicofo
|
||||||
|
```
|
||||||
|
service prosody restart
|
||||||
|
service jicofo restart
|
||||||
|
```
|
||||||
|
|
||||||
|
Now after the main 30 participants join, the rest will be visitors using the
|
||||||
|
visitor nodes.
|
File diff suppressed because it is too large
Load Diff
After Width: | Height: | Size: 198 KiB |
|
@ -0,0 +1,51 @@
|
||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
SCRIPT_DIR=`dirname "$0"`
|
||||||
|
cd $SCRIPT_DIR
|
||||||
|
|
||||||
|
NUMBER_OF_INSTANCES=$1
|
||||||
|
|
||||||
|
if ! [[ $NUMBER_OF_INSTANCES =~ ^[0-9]+([.][0-9]+)?$ ]] ; then
|
||||||
|
echo "error: Not a number param" >&2;
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "Will configure $NUMBER_OF_INSTANCES number of visitor prosodies"
|
||||||
|
set -e
|
||||||
|
set -x
|
||||||
|
|
||||||
|
# Configure prosody instances
|
||||||
|
for (( i=1 ; i<=${NUMBER_OF_INSTANCES} ; i++ ));
|
||||||
|
do
|
||||||
|
cp prosody-v.service.template /lib/systemd/system/prosody-v${i}.service
|
||||||
|
sed -i "s/vX/v${i}/g" /lib/systemd/system/prosody-v${i}.service
|
||||||
|
mkdir /etc/prosody-v${i}
|
||||||
|
ln -s /etc/prosody/certs /etc/prosody-v${i}/certs
|
||||||
|
cp prosody.cfg.lua.visitor.template /etc/prosody-v${i}/prosody.cfg.lua
|
||||||
|
sed -i "s/vX/v${i}/g" /etc/prosody-v${i}/prosody.cfg.lua
|
||||||
|
done
|
||||||
|
|
||||||
|
# Configure jicofo
|
||||||
|
HOCON_CONFIG="/etc/jitsi/jicofo/jicofo.conf"
|
||||||
|
hocon -f $HOCON_CONFIG set "jicofo.bridge.selection-strategy" "VisitorSelectionStrategy"
|
||||||
|
hocon -f $HOCON_CONFIG set "jicofo.bridge.visitor-selection-strategy" "RegionBasedBridgeSelectionStrategy"
|
||||||
|
hocon -f $HOCON_CONFIG set "jicofo.bridge.topology-strategy" "VisitorTopologyStrategy"
|
||||||
|
|
||||||
|
PASS=$(hocon -f $HOCON_CONFIG get "jicofo.xmpp.client.password")
|
||||||
|
for (( i=1 ; i<=${NUMBER_OF_INSTANCES} ; i++ ));
|
||||||
|
do
|
||||||
|
prosodyctl --config /etc/prosody-v${i}/prosody.cfg.lua register focus auth.meet.jitsi $PASS
|
||||||
|
hocon -f $HOCON_CONFIG set "jicofo.xmpp.visitors.v${i}.enabled" true
|
||||||
|
hocon -f $HOCON_CONFIG set "jicofo.xmpp.visitors.v${i}.conference-service" "conference.v${i}.meet.jitsi"
|
||||||
|
hocon -f $HOCON_CONFIG set "jicofo.xmpp.visitors.v${i}.hostname" 127.0.0.1
|
||||||
|
hocon -f $HOCON_CONFIG set "jicofo.xmpp.visitors.v${i}.port" 5222${i}
|
||||||
|
hocon -f $HOCON_CONFIG set "jicofo.xmpp.visitors.v${i}.domain" "auth.meet.jitsi"
|
||||||
|
hocon -f $HOCON_CONFIG set "jicofo.xmpp.visitors.v${i}.password" "${PASS}"
|
||||||
|
hocon -f $HOCON_CONFIG set "jicofo.xmpp.visitors.v${i}.disable-certificate-verification" true
|
||||||
|
done
|
||||||
|
|
||||||
|
for (( i=1 ; i<=${NUMBER_OF_INSTANCES} ; i++ ));
|
||||||
|
do
|
||||||
|
service prosody-v${i} restart
|
||||||
|
done
|
||||||
|
service jicofo restart
|
|
@ -0,0 +1,46 @@
|
||||||
|
[Unit]
|
||||||
|
### see man systemd.unit
|
||||||
|
Description=Prosody vX (visitor vX) JVB XMPP Server
|
||||||
|
Documentation=https://prosody.im/doc
|
||||||
|
|
||||||
|
Requires=network-online.target
|
||||||
|
After=network-online.target network.target mariadb.service mysql.service postgresql.service
|
||||||
|
Before=biboumi.service
|
||||||
|
|
||||||
|
[Service]
|
||||||
|
### see man systemd.service
|
||||||
|
Type=simple
|
||||||
|
|
||||||
|
# Start by executing the main executable
|
||||||
|
# Note: -F option requires Prosody 0.11.5 or later
|
||||||
|
ExecStart=/usr/bin/prosody --config /etc/prosody-vX/prosody.cfg.lua -F
|
||||||
|
ExecReload=/bin/kill -HUP $MAINPID
|
||||||
|
Restart=on-abnormal
|
||||||
|
|
||||||
|
### see man systemd.exec
|
||||||
|
User=prosody
|
||||||
|
Group=prosody
|
||||||
|
UMask=0027
|
||||||
|
|
||||||
|
RuntimeDirectory=prosody-vX
|
||||||
|
ConfigurationDirectory=prosody-vX
|
||||||
|
StateDirectory=prosody-vX
|
||||||
|
StateDirectoryMode=0750
|
||||||
|
LogsDirectory=prosody-vX
|
||||||
|
WorkingDirectory=~
|
||||||
|
|
||||||
|
# Set stdin to /dev/null since Prosody does not need it
|
||||||
|
StandardInput=null
|
||||||
|
|
||||||
|
# Direct stdout/-err to journald for use with log = "*stdout"
|
||||||
|
StandardOutput=journal
|
||||||
|
StandardError=inherit
|
||||||
|
|
||||||
|
# Allow binding low ports
|
||||||
|
AmbientCapabilities=CAP_NET_BIND_SERVICE
|
||||||
|
|
||||||
|
[Install]
|
||||||
|
### see man systemd.unit
|
||||||
|
WantedBy=multi-user.target
|
||||||
|
|
||||||
|
# vim: filetype=systemd
|
|
@ -0,0 +1,121 @@
|
||||||
|
---------- Server-wide settings ----------
|
||||||
|
s2s_ports = { 52691 };
|
||||||
|
c2s_ports = { 52221 }
|
||||||
|
http_ports = { 52801 }
|
||||||
|
https_ports = { 52811 }
|
||||||
|
|
||||||
|
daemonize = true;
|
||||||
|
|
||||||
|
-- we use a common jid for jicofo
|
||||||
|
admins = {
|
||||||
|
'focus@auth.meet.jitsi'
|
||||||
|
}
|
||||||
|
|
||||||
|
-- Enable use of native prosody 0.11 support for epoll over select
|
||||||
|
network_backend = 'epoll';
|
||||||
|
network_settings = {
|
||||||
|
tcp_backlog = 511;
|
||||||
|
}
|
||||||
|
|
||||||
|
modules_enabled = {
|
||||||
|
'saslauth';
|
||||||
|
'tls';
|
||||||
|
'disco';
|
||||||
|
'posix';
|
||||||
|
|
||||||
|
'secure_interfaces';
|
||||||
|
|
||||||
|
-- jitsi
|
||||||
|
'websocket';
|
||||||
|
'bosh';
|
||||||
|
's2s_bidi';
|
||||||
|
's2s_whitelist';
|
||||||
|
};
|
||||||
|
|
||||||
|
s2s_whitelist = {};
|
||||||
|
|
||||||
|
external_service_secret = '__turnSecret__';
|
||||||
|
|
||||||
|
external_services = {
|
||||||
|
{ type = 'stun', host = 'jitmeet.example.com', port = 3478 },
|
||||||
|
{ type = 'turn', host = 'jitmeet.example.com', port = 3478, transport = 'udp', secret = true, ttl = 86400, algorithm = 'turn' },
|
||||||
|
{ type = 'turns', host = 'jitmeet.example.com', port = 5349, transport = 'tcp', secret = true, ttl = 86400, algorithm = 'turn' }
|
||||||
|
};
|
||||||
|
|
||||||
|
muc_mapper_domain_base = 'vX.meet.jitsi';
|
||||||
|
|
||||||
|
-- https://prosody.im/doc/modules/mod_smacks
|
||||||
|
smacks_max_unacked_stanzas = 5;
|
||||||
|
smacks_hibernation_time = 60;
|
||||||
|
-- this is dropped in 0.12
|
||||||
|
smacks_max_hibernated_sessions = 1;
|
||||||
|
smacks_max_old_sessions = 1;
|
||||||
|
|
||||||
|
unlimited_jids = { 'focus@auth.meet.jitsi' }
|
||||||
|
limits = {
|
||||||
|
c2s = {
|
||||||
|
rate = '512kb/s';
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
modules_disabled = {
|
||||||
|
'offline';
|
||||||
|
'pubsub';
|
||||||
|
'register';
|
||||||
|
};
|
||||||
|
|
||||||
|
allow_registration = false;
|
||||||
|
authentication = 'internal_hashed'
|
||||||
|
storage = 'internal'
|
||||||
|
log = {
|
||||||
|
-- Log files (change 'info' to 'debug' for debug logs):
|
||||||
|
info = '/var/log/prosody-vX/prosody.log';
|
||||||
|
error = '/var/log/prosody-vX/prosody.err';
|
||||||
|
}
|
||||||
|
|
||||||
|
consider_websocket_secure = true;
|
||||||
|
consider_bosh_secure = true;
|
||||||
|
bosh_max_inactivity = 60;
|
||||||
|
|
||||||
|
plugin_paths = { '/usr/share/jitsi-meet/prosody-plugins/' }
|
||||||
|
|
||||||
|
----------- Virtual hosts -----------
|
||||||
|
VirtualHost 'vX.meet.jitsi'
|
||||||
|
authentication = 'jitsi-anonymous'
|
||||||
|
ssl = {
|
||||||
|
key = '/etc/prosody/certs/jitmeet.example.com.key';
|
||||||
|
certificate = '/etc/prosody/certs/jitmeet.example.com.crt';
|
||||||
|
}
|
||||||
|
modules_enabled = {
|
||||||
|
'bosh';
|
||||||
|
'ping';
|
||||||
|
'external_services';
|
||||||
|
'smacks';
|
||||||
|
'jiconop';
|
||||||
|
}
|
||||||
|
main_muc = 'conference.vX.meet.jitsi';
|
||||||
|
|
||||||
|
VirtualHost 'auth.meet.jitsi'
|
||||||
|
modules_enabled = {
|
||||||
|
'limits_exception';
|
||||||
|
'ping';
|
||||||
|
}
|
||||||
|
authentication = 'internal_hashed'
|
||||||
|
|
||||||
|
Component 'conference.vX.meet.jitsi' 'muc'
|
||||||
|
storage = 'memory'
|
||||||
|
muc_room_cache_size = 10000
|
||||||
|
restrict_room_creation = true
|
||||||
|
modules_enabled = {
|
||||||
|
'muc_hide_all';
|
||||||
|
'muc_domain_mapper';
|
||||||
|
'muc_meeting_id';
|
||||||
|
'fmuc';
|
||||||
|
}
|
||||||
|
muc_room_default_presence_broadcast = {
|
||||||
|
visitor = false;
|
||||||
|
participant = true;
|
||||||
|
moderator = true;
|
||||||
|
};
|
||||||
|
muc_room_locking = false
|
||||||
|
muc_room_default_public_jids = true
|
|
@ -1,16 +0,0 @@
|
||||||
-- validates all certificates, global module
|
|
||||||
-- Warning: use this only for testing purposes as it will accept all kind of certificates for s2s connections
|
|
||||||
-- you can use https://modules.prosody.im/mod_s2s_whitelist.html for whitelisting only certain destinations
|
|
||||||
module:set_global();
|
|
||||||
|
|
||||||
function attach(event)
|
|
||||||
local session = event.session;
|
|
||||||
|
|
||||||
session.cert_chain_status = 'valid';
|
|
||||||
session.cert_identity_status = 'valid';
|
|
||||||
|
|
||||||
return true;
|
|
||||||
end
|
|
||||||
module:wrap_event('s2s-check-certificate', function (handlers, event_name, event_data)
|
|
||||||
return attach(event_data);
|
|
||||||
end);
|
|
|
@ -0,0 +1,19 @@
|
||||||
|
-- global module
|
||||||
|
-- validates certificates for all hosts used for s2soutinjection
|
||||||
|
module:set_global();
|
||||||
|
|
||||||
|
local s2s_overrides = module:get_option("s2s_connect_overrides");
|
||||||
|
|
||||||
|
function attach(event)
|
||||||
|
local session = event.session;
|
||||||
|
|
||||||
|
if s2s_overrides and s2s_overrides[event.host] then
|
||||||
|
session.cert_chain_status = 'valid';
|
||||||
|
session.cert_identity_status = 'valid';
|
||||||
|
|
||||||
|
return true;
|
||||||
|
end
|
||||||
|
end
|
||||||
|
module:wrap_event('s2s-check-certificate', function (handlers, event_name, event_data)
|
||||||
|
return attach(event_data);
|
||||||
|
end);
|
|
@ -7,12 +7,6 @@
|
||||||
--- };
|
--- };
|
||||||
--- Enable in global modules: 's2s_bidi'
|
--- Enable in global modules: 's2s_bidi'
|
||||||
--- Make sure 's2s' is not in modules_disabled
|
--- Make sure 's2s' is not in modules_disabled
|
||||||
--- TODO: Do we need the /etc/hosts changes? We can drop it for https://modules.prosody.im/mod_s2soutinjection.html
|
|
||||||
--- In /etc/hosts add:
|
|
||||||
--- vmmain-ip-address focus.domain.com
|
|
||||||
--- vmmain-ip-address conference.domain.com
|
|
||||||
--- vmmain-ip-address domain.com
|
|
||||||
--- Open port 5269 on the provider side and on the firewall of the machine, so the core node can access this visitor one
|
|
||||||
local jid = require 'util.jid';
|
local jid = require 'util.jid';
|
||||||
local st = require 'util.stanza';
|
local st = require 'util.stanza';
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,20 @@
|
||||||
|
-- Using version https://hg.prosody.im/prosody-modules/file/c1a8ce147885/mod_s2s_whitelist/mod_s2s_whitelist.lua
|
||||||
|
local st = require "util.stanza";
|
||||||
|
|
||||||
|
local whitelist = module:get_option_inherited_set("s2s_whitelist", {});
|
||||||
|
|
||||||
|
module:hook("route/remote", function (event)
|
||||||
|
if not whitelist:contains(event.to_host) then
|
||||||
|
module:send(st.error_reply(event.stanza, "cancel", "not-allowed", "Communication with this domain is restricted"));
|
||||||
|
return true;
|
||||||
|
end
|
||||||
|
end, 100);
|
||||||
|
|
||||||
|
module:hook("s2s-stream-features", function (event)
|
||||||
|
if not whitelist:contains(event.origin.from_host) then
|
||||||
|
event.origin:close({
|
||||||
|
condition = "policy-violation";
|
||||||
|
text = "Communication with this domain is restricted";
|
||||||
|
});
|
||||||
|
end
|
||||||
|
end, 1000);
|
|
@ -0,0 +1,90 @@
|
||||||
|
-- Using version https://hg.prosody.im/prosody-modules/file/4fb922aa0ace/mod_s2soutinjection/mod_s2soutinjection.lua
|
||||||
|
local st = require"util.stanza";
|
||||||
|
local new_outgoing = require"core.s2smanager".new_outgoing;
|
||||||
|
local bounce_sendq = module:depends"s2s".route_to_new_session.bounce_sendq;
|
||||||
|
local initialize_filters = require "util.filters".initialize;
|
||||||
|
|
||||||
|
local portmanager = require "core.portmanager";
|
||||||
|
|
||||||
|
local addclient = require "net.server".addclient;
|
||||||
|
|
||||||
|
module:depends("s2s");
|
||||||
|
|
||||||
|
local sessions = module:shared("sessions");
|
||||||
|
|
||||||
|
local injected = module:get_option("s2s_connect_overrides");
|
||||||
|
|
||||||
|
-- The proxy_listener handles connection while still connecting to the proxy,
|
||||||
|
-- then it hands them over to the normal listener (in mod_s2s)
|
||||||
|
local proxy_listener = { default_port = nil, default_mode = "*a", default_interface = "*" };
|
||||||
|
|
||||||
|
function proxy_listener.onconnect(conn)
|
||||||
|
local session = sessions[conn];
|
||||||
|
|
||||||
|
-- Now the real s2s listener can take over the connection.
|
||||||
|
local listener = portmanager.get_service("s2s").listener;
|
||||||
|
|
||||||
|
local log = session.log;
|
||||||
|
|
||||||
|
local filter = initialize_filters(session);
|
||||||
|
|
||||||
|
session.version = 1;
|
||||||
|
|
||||||
|
session.sends2s = function (t)
|
||||||
|
log("debug", "sending (s2s over proxy): %s", (t.top_tag and t:top_tag()) or t:match("^[^>]*>?"));
|
||||||
|
if t.name then
|
||||||
|
t = filter("stanzas/out", t);
|
||||||
|
end
|
||||||
|
if t then
|
||||||
|
t = filter("bytes/out", tostring(t));
|
||||||
|
if t then
|
||||||
|
return conn:write(tostring(t));
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
session.open_stream = function ()
|
||||||
|
session.sends2s(st.stanza("stream:stream", {
|
||||||
|
xmlns='jabber:server', ["xmlns:db"]='jabber:server:dialback',
|
||||||
|
["xmlns:stream"]='http://etherx.jabber.org/streams',
|
||||||
|
from=session.from_host, to=session.to_host, version='1.0', ["xml:lang"]='en'}):top_tag());
|
||||||
|
end
|
||||||
|
|
||||||
|
conn.setlistener(conn, listener);
|
||||||
|
|
||||||
|
listener.register_outgoing(conn, session);
|
||||||
|
|
||||||
|
listener.onconnect(conn);
|
||||||
|
end
|
||||||
|
|
||||||
|
function proxy_listener.register_outgoing(conn, session)
|
||||||
|
session.direction = "outgoing";
|
||||||
|
sessions[conn] = session;
|
||||||
|
end
|
||||||
|
|
||||||
|
function proxy_listener.ondisconnect(conn, err)
|
||||||
|
sessions[conn] = nil;
|
||||||
|
end
|
||||||
|
|
||||||
|
module:hook("route/remote", function(event)
|
||||||
|
local from_host, to_host, stanza = event.from_host, event.to_host, event.stanza;
|
||||||
|
local inject = injected and injected[to_host];
|
||||||
|
if not inject then return end
|
||||||
|
module:log("debug", "opening a new outgoing connection for this stanza");
|
||||||
|
local host_session = new_outgoing(from_host, to_host);
|
||||||
|
|
||||||
|
-- Store in buffer
|
||||||
|
host_session.bounce_sendq = bounce_sendq;
|
||||||
|
host_session.sendq = { {tostring(stanza), stanza.attr.type ~= "error" and stanza.attr.type ~= "result" and st.reply(stanza)} };
|
||||||
|
host_session.log("debug", "stanza [%s] queued until connection complete", tostring(stanza.name));
|
||||||
|
|
||||||
|
local host, port = inject[1] or inject, tonumber(inject[2]) or 5269;
|
||||||
|
|
||||||
|
local conn = addclient(host, port, proxy_listener, "*a");
|
||||||
|
|
||||||
|
proxy_listener.register_outgoing(conn, host_session);
|
||||||
|
|
||||||
|
host_session.conn = conn;
|
||||||
|
return true;
|
||||||
|
end, -2);
|
||||||
|
|
|
@ -0,0 +1,20 @@
|
||||||
|
-- Using version https://hg.prosody.im/prosody-modules/file/6c806a99f802/mod_secure_interfaces/mod_secure_interfaces.lua
|
||||||
|
local secure_interfaces = module:get_option_set("secure_interfaces", { "127.0.0.1", "::1" });
|
||||||
|
|
||||||
|
module:hook("stream-features", function (event)
|
||||||
|
local session = event.origin;
|
||||||
|
if session.type ~= "c2s_unauthed" then return; end
|
||||||
|
local socket = session.conn:socket();
|
||||||
|
if not socket.getsockname then
|
||||||
|
module:log("debug", "Unable to determine local address of incoming connection");
|
||||||
|
return;
|
||||||
|
end
|
||||||
|
local localip = socket:getsockname();
|
||||||
|
if secure_interfaces:contains(localip) then
|
||||||
|
module:log("debug", "Marking session from %s to %s as secure", session.ip or "[?]", localip);
|
||||||
|
session.secure = true;
|
||||||
|
session.conn.starttls = false;
|
||||||
|
else
|
||||||
|
module:log("debug", "Not marking session from %s to %s as secure", session.ip or "[?]", localip);
|
||||||
|
end
|
||||||
|
end, 2500);
|
|
@ -56,29 +56,23 @@ local visitors_nodes = {};
|
||||||
--- Jicofo is connected to the room when sending this error
|
--- Jicofo is connected to the room when sending this error
|
||||||
module:log('info', 'Hook to iq/host');
|
module:log('info', 'Hook to iq/host');
|
||||||
module:hook('iq/full', function(event)
|
module:hook('iq/full', function(event)
|
||||||
local session, stanza = event.origin, event.stanza;
|
local stanza = event.stanza;
|
||||||
|
|
||||||
if stanza.name ~= 'iq' or stanza.attr.type ~= 'error' or stanza.attr.from ~= focus_component_host then
|
if stanza.name ~= 'iq' or stanza.attr.type ~= 'result' or stanza.attr.from ~= focus_component_host then
|
||||||
return; -- not IQ from jicofo. Ignore this event.
|
return; -- not IQ from jicofo. Ignore this event.
|
||||||
end
|
end
|
||||||
|
|
||||||
local error = stanza:get_child('error');
|
local conference = stanza:get_child('conference', 'http://jitsi.org/protocol/focus');
|
||||||
if error == nil then
|
if not conference then
|
||||||
return; -- not Conference IQ error. Ignore.
|
|
||||||
end
|
|
||||||
|
|
||||||
local redirect = error:get_child('redirect', 'urn:ietf:params:xml:ns:xmpp-stanzas');
|
|
||||||
local redirect_host = error:get_child_text('url', 'http://jitsi.org/jitmeet');
|
|
||||||
|
|
||||||
if not redirect or not redirect_host then
|
|
||||||
return;
|
return;
|
||||||
end
|
end
|
||||||
|
|
||||||
-- let's send participants if any from the room to the visitors room
|
-- let's send participants if any from the room to the visitors room
|
||||||
-- TODO fix room name extract, make sure it works wit tenants
|
-- TODO fix room name extract, make sure it works wit tenants
|
||||||
local main_room = error:get_child_text('main-room', 'http://jitsi.org/jitmeet');
|
local main_room = conference.attr.room;
|
||||||
|
local vnode = conference.attr.vnode;
|
||||||
|
|
||||||
if not main_room then
|
if not main_room or not vnode then
|
||||||
return;
|
return;
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -88,7 +82,7 @@ module:hook('iq/full', function(event)
|
||||||
return; -- room does not exists. Continue with normal flow
|
return; -- room does not exists. Continue with normal flow
|
||||||
end
|
end
|
||||||
|
|
||||||
local conference_service = muc_domain_prefix..'.'..redirect_host;
|
local conference_service = muc_domain_prefix..'.'..vnode..'.meet.jitsi';
|
||||||
|
|
||||||
if visitors_nodes[room.jid] and
|
if visitors_nodes[room.jid] and
|
||||||
visitors_nodes[room.jid].nodes and
|
visitors_nodes[room.jid].nodes and
|
||||||
|
|
Loading…
Reference in New Issue