[__jitsi_meet*] Make rooms on different domains not equivalent

This is a backwards-compatible change.

We switch the approach from "treat all domains as if they were the main domain"
to: "each domain has its own prosody settings".

This works perfectly fine, even with secured domains.

There is a caveat with secured domains, in that they use the main domain to log
in; this means that users are shared across all domains (as they were before
this commit).

This is due to jicofo refusing to start meetings from a domain that is not
configured, and it only accepting one domain.

Right now, this is acceptable, however we could want to authenticate against
e.g. different LDAP / IMAP servers in the future, so this would need addressing
at that stage.

Probably the best way to solve it is by patching jicofo, so it accepts starting
conferences from multiple domains and getting that patch upstream.

Sponsored by:   camilion.eu, eXO.cat
This commit is contained in:
evilham 2022-04-21 13:20:30 +02:00
parent a12b343660
commit 87cc109bf1
Signed by untrusted user: evilham
GPG key ID: AE3EE30D970886BF
10 changed files with 403 additions and 30 deletions

View file

@ -0,0 +1 @@
../../__jitsi_meet_domain/files/prosody.cfg.lua.sh

View file

@ -4,8 +4,7 @@ if grep -qE "^__file/etc/nginx" "${__messages_in}"; then
echo "service nginx reload" echo "service nginx reload"
fi fi
JITSI_HOST="${__object_id}" if grep -qE "^(__line/jitsi_jicofo_secured_domains|(__file|__link)/etc/prosody/conf.d/|__file/etc/jitsi/jicofo/jicofo.conf)" "${__messages_in}"; then
if grep -qE "^(__line/jitsi_jicofo_secured_domains|__file/etc/prosody/conf.d/${JITSI_HOST}.zauth.cfg.lua|__file/etc/jitsi/jicofo/jicofo.conf)" "${__messages_in}"; then
echo "systemctl restart prosody" echo "systemctl restart prosody"
echo "systemctl restart jicofo" echo "systemctl restart jicofo"
echo "systemctl restart jitsi-videobridge2" echo "systemctl restart jitsi-videobridge2"

View file

@ -161,18 +161,22 @@ else
SECURED_DOMAINS_STATE='absent' SECURED_DOMAINS_STATE='absent'
fi fi
__file "/etc/prosody/conf.d/${JITSI_HOST}.zauth.cfg.lua" \ # This is the main host config
--owner prosody --group prosody --mode 0440 \ PROSODY_MAIN_CONFIG="YES"
--state ${SECURED_DOMAINS_STATE} \ # Prosody settings for common components (jvb, focus, ...)
--source - <<EOF # shellcheck source=type/__jitsi_meet/files/prosody.cfg.lua.sh
VirtualHost "${JITSI_HOST}" . "${__type}/files/prosody.cfg.lua.sh" # This defines PROSODY_CONFIG
authentication = "internal_plain" __file "/etc/prosody/conf.d/00_jitsi_base.cfg.lua" \
--group prosody \
VirtualHost "guest.${JITSI_HOST}" --mode 0440 \
authentication = "anonymous" --source - <<EOF
c2s_require_encryption = false ${PROSODY_CONFIG}
EOF EOF
# Clean up zauth.cfg.lua file, which we don't use now
__file "/etc/prosody/conf.d/${JITSI_HOST}.zauth.cfg.lua" \
--state absent
export SECURED_DOMAINS_STATE export SECURED_DOMAINS_STATE
export JITSI_HOST export JITSI_HOST
"${__type}/files/jicofo.conf.sh" | \ "${__type}/files/jicofo.conf.sh" | \

View file

@ -28,3 +28,4 @@ download_file() {
download_file config.js download_file config.js
download_file interface_config.js download_file interface_config.js
download_file doc/debian/jitsi-meet/jitsi-meet.example nginx.sh.orig download_file doc/debian/jitsi-meet/jitsi-meet.example nginx.sh.orig
download_file doc/debian/jitsi-meet-prosody/prosody.cfg.lua-jvb.example prosody.cfg.lua.sh.orig

View file

@ -10,20 +10,21 @@ var config = {
hosts: { hosts: {
// XMPP domain. // XMPP domain.
domain: '${JITSI_HOST}', domain: '${DOMAIN}',
// When using authentication, domain for guest users. // When using authentication, domain for guest users.
$( if [ -z "${SECURED_DOMAINS}" ]; then printf "// " $( if [ -z "${SECURED_DOMAINS}" ]; then printf "// "
fi)anonymousdomain: 'guest.${JITSI_HOST}', fi)anonymousdomain: 'guest.${DOMAIN}',
// Domain for authenticated users. Defaults to <domain>. // Domain for authenticated users. Defaults to <domain>.
// authdomain: '${JITSI_HOST}', // NOTE [cdist]: if we use '${DOMAIN}', jicofo won't start the meeting
authdomain: '${JITSI_HOST}',
// Focus component domain. Defaults to focus.<domain>. // Focus component domain. Defaults to focus.<domain>.
// focus: 'focus.${JITSI_HOST}', focus: 'focus.${JITSI_HOST}',
// XMPP MUC domain. FIXME: use XEP-0030 to discover it. // XMPP MUC domain. FIXME: use XEP-0030 to discover it.
muc: 'conference.${JITSI_HOST}' muc: 'conference.${DOMAIN}'
}, },
// BOSH URL. FIXME: use XEP-0156 to discover it. // BOSH URL. FIXME: use XEP-0156 to discover it.
@ -31,12 +32,12 @@ var config = {
bosh: '//<!--# echo var="http_host" -->/<!--# echo var="subdir" default="" -->http-bind', bosh: '//<!--# echo var="http_host" -->/<!--# echo var="subdir" default="" -->http-bind',
// Websocket URL // Websocket URL
// websocket: 'wss://${JITSI_HOST}/xmpp-websocket', // websocket: 'wss://${DOMAIN}/xmpp-websocket',
// The real JID of focus participant - can be overridden here // The real JID of focus participant - can be overridden here
// Do not change username - FIXME: Make focus username configurable // Do not change username - FIXME: Make focus username configurable
// https://github.com/jitsi/jitsi-meet/issues/7376 // https://github.com/jitsi/jitsi-meet/issues/7376
// focusUserJid: 'focus@auth.${JITSI_HOST}', focusUserJid: 'focus@auth.${JITSI_HOST}',
// Testing / experimental features. // Testing / experimental features.
@ -270,9 +271,9 @@ $(if [ -n "${VIDEO_CONSTRAINTS}" ]; then echo "${VIDEO_CONSTRAINTS},"; fi)
// appKey: '<APP_KEY>' // Specify your app key here. // appKey: '<APP_KEY>' // Specify your app key here.
// // A URL to redirect the user to, after authenticating // // A URL to redirect the user to, after authenticating
// // by default uses: // // by default uses:
// // 'https://${JITSI_HOST}/static/oauth.html' // // 'https://${DOMAIN}/static/oauth.html'
// redirectURI: // redirectURI:
// 'https://${JITSI_HOST}/subfolder/static/oauth.html' // 'https://${DOMAIN}/subfolder/static/oauth.html'
// }, // },
// When integrations like dropbox are enabled only that will be shown, // When integrations like dropbox are enabled only that will be shown,
// by enabling fileRecordingsServiceEnabled, we show both the integrations // by enabling fileRecordingsServiceEnabled, we show both the integrations

View file

@ -100,7 +100,7 @@ server {
proxy_set_header X-Forwarded-For \$remote_addr; proxy_set_header X-Forwarded-For \$remote_addr;
# Prevision for 'multi-domain' jitsi instances # Prevision for 'multi-domain' jitsi instances
# https://community.jitsi.org/t/same-jitsi-meet-instance-with-multiple-domain-names/17391 # https://community.jitsi.org/t/same-jitsi-meet-instance-with-multiple-domain-names/17391
proxy_set_header Host ${JITSI_HOST}; proxy_set_header Host ${DOMAIN};
} }
# xmpp websockets # xmpp websockets
@ -111,7 +111,7 @@ server {
proxy_set_header Connection "upgrade"; proxy_set_header Connection "upgrade";
# Prevision for 'multi-domain' jitsi instances # Prevision for 'multi-domain' jitsi instances
# https://community.jitsi.org/t/same-jitsi-meet-instance-with-multiple-domain-names/17391 # https://community.jitsi.org/t/same-jitsi-meet-instance-with-multiple-domain-names/17391
proxy_set_header Host ${JITSI_HOST}; proxy_set_header Host ${DOMAIN};
tcp_nodelay on; tcp_nodelay on;
} }

View file

@ -0,0 +1,199 @@
#!/bin/sh -eu
# Source:
# https://github.com/jitsi/jitsi-meet/blob/master/doc/debian/jitsi-meet-prosody/prosody.cfg.lua-jvb.example
FOCUS_USER="focus"
JITSI_DOMAIN="${JITSI_DOMAIN:-${JITSI_HOST:?}}"
# PROSODY_MAIN_CONFIG: defined in __jitsi_meet, empty in __jitsi_meet_domain
PROSODY_SECUREDOMAIN_START="--[["
PROSODY_SECUREDOMAIN_END="--]]"
if [ -n "${PROSODY_MAIN_CONFIG}" ]; then
PROSODY_MAIN_START=""
PROSODY_MAIN_END=""
PROSODY_DOMAIN_START="--[["
PROSODY_DOMAIN_END="--]]"
else
PROSODY_MAIN_START="--[["
PROSODY_MAIN_END="--]]"
PROSODY_DOMAIN_START=""
PROSODY_DOMAIN_END=""
if [ -n "${SECURED_DOMAINS}" ]; then
PROSODY_SECUREDOMAIN_START=""
PROSODY_SECUREDOMAIN_END=""
fi
fi
# Websockets haven't been fully tested in this type and don't work reliably
PROSODY_WEBSOCKET="-- "
# shellcheck disable=SC2034 # This is intended to be included
PROSODY_CONFIG="$(cat <<EOFPROSODY
-- Managed remotely, changes will be lost
${PROSODY_MAIN_START}
-- This will be managed by __jitsi_meet
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 = "${JITSI_HOST:?}";
external_service_secret = "${TURN_SECRET:-TurnSecret}";
external_services = {
{ type = "stun", host = "${JITSI_HOST:?}", port = 3478 },
{ type = "turn", host = "${JITSI_HOST:?}", port = 3478, transport = "udp", secret = true, ttl = 86400, algorithm = "turn" },
{ type = "turns", host = "${JITSI_HOST:?}", port = 5349, transport = "tcp", secret = true, ttl = 86400, algorithm = "turn" }
};
cross_domain_bosh = false;
consider_bosh_secure = true;
-- Use websockets
-- https://community.jitsi.org/t/how-to-how-to-enable-websockets-xmpp-websocket-and-smacks-for-prosody/87920
${PROSODY_WEBSOCKET}consider_websocket_secure = true;
-- https_ports = { }; -- Remove this line to prevent listening on port 5284
-- 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_USER:?}@auth.${JITSI_HOST:?}",
"jvb@auth.${JITSI_HOST:?}"
}
${PROSODY_MAIN_END}
${PROSODY_DOMAIN_START}
-- This will be managed by __jitsi_meet_domain
VirtualHost "${JITSI_DOMAIN:?}"
-- enabled = false -- Remove this line to enable this host
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/${JITSI_DOMAIN:?}.key";
certificate = "/etc/prosody/certs/${JITSI_DOMAIN:?}.crt";
}
av_moderation_component = "avmoderation.${JITSI_DOMAIN:?}"
speakerstats_component = "speakerstats.${JITSI_DOMAIN:?}"
conference_duration_component = "conferenceduration.${JITSI_DOMAIN:?}"
-- we need bosh
modules_enabled = {
"bosh";
"pubsub";
"ping"; -- Enable mod_ping
"speakerstats";
"external_services";
"conference_duration";
"muc_lobby_rooms";
"muc_breakout_rooms";
"av_moderation";
${PROSODY_WEBSOCKET} "websocket";
${PROSODY_WEBSOCKET} "smacks";
}
smacks_max_unacked_stanzas = 5;
smacks_hibernation_time = 60;
smacks_max_hibernated_sessions = 1;
smacks_max_old_sessions = 1;
c2s_require_encryption = false
lobby_muc = "lobby.${JITSI_DOMAIN:?}"
breakout_rooms_muc = "breakout.${JITSI_DOMAIN:?}"
main_muc = "conference.${JITSI_DOMAIN:?}"
-- muc_lobby_whitelist = { "recorder.${JITSI_DOMAIN:?}" } -- Here we can whitelist jibri to enter lobby enabled rooms
Component "conference.${JITSI_DOMAIN:?}" "muc"
restrict_room_creation = true
storage = "memory"
modules_enabled = {
"muc_meeting_id";
"muc_domain_mapper";
"polls";
--"token_verification";
"muc_rate_limit";
}
admins = { "${FOCUS_USER:?}@auth.${JITSI_HOST:?}" }
muc_room_locking = false
muc_room_default_public_jids = true
Component "breakout.${JITSI_DOMAIN:?}" "muc"
restrict_room_creation = true
storage = "memory"
modules_enabled = {
"muc_meeting_id";
"muc_domain_mapper";
--"token_verification";
"muc_rate_limit";
"polls";
}
admins = { "${FOCUS_USER:?}@auth.${JITSI_HOST:?}" }
muc_room_locking = false
muc_room_default_public_jids = true
-- internal muc component
Component "internal.auth.${JITSI_DOMAIN:?}" "muc"
storage = "memory"
modules_enabled = {
"ping";
}
admins = { "${FOCUS_USER:?}@auth.${JITSI_HOST:?}", "jvb@auth.${JITSI_HOST:?}" }
muc_room_locking = false
muc_room_default_public_jids = true
${PROSODY_DOMAIN_END}
${PROSODY_MAIN_START}
-- This will be managed by __jitsi_meet
VirtualHost "auth.${JITSI_DOMAIN:?}"
ssl = {
key = "/etc/prosody/certs/auth.${JITSI_DOMAIN:?}.key";
certificate = "/etc/prosody/certs/auth.${JITSI_DOMAIN:?}.crt";
}
modules_enabled = {
"limits_exception";
}
authentication = "internal_hashed"
${PROSODY_MAIN_END}
${PROSODY_DOMAIN_START}
-- This will be managed by __jitsi_meet_domain
-- Proxy to jicofo's user JID, so that it doesn't have to register as a component.
Component "focus.${JITSI_DOMAIN:?}" "client_proxy"
-- Single focus user for the whole instance
target_address = "${FOCUS_USER:?}@auth.${JITSI_HOST:?}"
Component "speakerstats.${JITSI_DOMAIN:?}" "speakerstats_component"
muc_component = "conference.${JITSI_DOMAIN:?}"
Component "conferenceduration.${JITSI_DOMAIN:?}" "conference_duration_component"
muc_component = "conference.${JITSI_DOMAIN:?}"
Component "avmoderation.${JITSI_DOMAIN:?}" "av_moderation_component"
muc_component = "conference.${JITSI_DOMAIN:?}"
Component "lobby.${JITSI_DOMAIN:?}" "muc"
storage = "memory"
restrict_room_creation = true
muc_room_locking = false
muc_room_default_public_jids = true
modules_enabled = {
"muc_rate_limit";
"polls";
}
${PROSODY_DOMAIN_END}
${PROSODY_SECUREDOMAIN_START}
-- Only used on secured domains
VirtualHost "${JITSI_DOMAIN}"
authentication = "internal_plain"
VirtualHost "guest.${JITSI_DOMAIN}"
authentication = "anonymous"
c2s_require_encryption = false
${PROSODY_SECUREDOMAIN_END}
EOFPROSODY
)"

View file

@ -0,0 +1,129 @@
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 = "jitmeet.example.com";
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" }
};
cross_domain_bosh = false;
consider_bosh_secure = true;
-- https_ports = { }; -- Remove this line to prevent listening on port 5284
-- 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 = {
"focusUser@auth.jitmeet.example.com",
"jvb@auth.jitmeet.example.com"
}
VirtualHost "jitmeet.example.com"
-- enabled = false -- Remove this line to enable this host
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/jitmeet.example.com.key";
certificate = "/etc/prosody/certs/jitmeet.example.com.crt";
}
av_moderation_component = "avmoderation.jitmeet.example.com"
speakerstats_component = "speakerstats.jitmeet.example.com"
conference_duration_component = "conferenceduration.jitmeet.example.com"
-- we need bosh
modules_enabled = {
"bosh";
"pubsub";
"ping"; -- Enable mod_ping
"speakerstats";
"external_services";
"conference_duration";
"muc_lobby_rooms";
"muc_breakout_rooms";
"av_moderation";
}
c2s_require_encryption = false
lobby_muc = "lobby.jitmeet.example.com"
breakout_rooms_muc = "breakout.jitmeet.example.com"
main_muc = "conference.jitmeet.example.com"
-- muc_lobby_whitelist = { "recorder.jitmeet.example.com" } -- Here we can whitelist jibri to enter lobby enabled rooms
Component "conference.jitmeet.example.com" "muc"
restrict_room_creation = true
storage = "memory"
modules_enabled = {
"muc_meeting_id";
"muc_domain_mapper";
"polls";
--"token_verification";
"muc_rate_limit";
}
admins = { "focusUser@auth.jitmeet.example.com" }
muc_room_locking = false
muc_room_default_public_jids = true
Component "breakout.jitmeet.example.com" "muc"
restrict_room_creation = true
storage = "memory"
modules_enabled = {
"muc_meeting_id";
"muc_domain_mapper";
--"token_verification";
"muc_rate_limit";
"polls";
}
admins = { "focusUser@auth.jitmeet.example.com" }
muc_room_locking = false
muc_room_default_public_jids = true
-- internal muc component
Component "internal.auth.jitmeet.example.com" "muc"
storage = "memory"
modules_enabled = {
"ping";
}
admins = { "focusUser@auth.jitmeet.example.com", "jvb@auth.jitmeet.example.com" }
muc_room_locking = false
muc_room_default_public_jids = true
VirtualHost "auth.jitmeet.example.com"
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.jitmeet.example.com" "client_proxy"
target_address = "focusUser@auth.jitmeet.example.com"
Component "speakerstats.jitmeet.example.com" "speakerstats_component"
muc_component = "conference.jitmeet.example.com"
Component "conferenceduration.jitmeet.example.com" "conference_duration_component"
muc_component = "conference.jitmeet.example.com"
Component "avmoderation.jitmeet.example.com" "av_moderation_component"
muc_component = "conference.jitmeet.example.com"
Component "lobby.jitmeet.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";
}

View file

@ -11,14 +11,18 @@ DESCRIPTION
----------- -----------
This type installs and configures the frontend for Jitsi-Meet. This type installs and configures the frontend for Jitsi-Meet.
This supports "multi-domain" installations, notice that in such a setup, all This supports "multi-domain" installations.
rooms are shared across the different URLs, e.g.
https://jitsi1.example.org/room1 and https://jitsi2.example.org/room1 are New in April 2022: rooms are independent for each domain, that is:
equivalent. https://jitsi1.example.org/room1 and https://jitsi2.example.org/room1 are
different rooms.
Note however, that right now if using secured domains, users are still shared
across any domains hosted in the same instance.
One way to work around that could be to run multiple jicofos, but we do not
want to bloat the servers.
A better way is to patch jicofo, get in touch with the type authors if you want
the gory details.
This is due to the underlying XMPP and signaling rooms being common.
There might be a way to perform tricks on the Nginx-side to avoid this, but
time is lacking :-).
This assumes `__jitsi_meet` has already been ran on the target host, and, This assumes `__jitsi_meet` has already been ran on the target host, and,
amongst others, that Jitsi was set up with `__target_host` as the Jitsi domain. amongst others, that Jitsi was set up with `__target_host` as the Jitsi domain.

View file

@ -131,3 +131,38 @@ __file "/usr/share/jitsi-meet/images/watermark-${DOMAIN}.png" \
--mode 0644 \ --mode 0644 \
--state "$(_var_state "${BRANDING_WATERMARK}")" \ --state "$(_var_state "${BRANDING_WATERMARK}")" \
--source "${BRANDING_WATERMARK}" --source "${BRANDING_WATERMARK}"
#
# Take care of prosody settings for the domain
#
JITSI_DOMAIN="${DOMAIN}"
# Prosody settings for common components (jvb, focus, ...)
# shellcheck source=type/__jitsi_meet_domain/files/prosody.cfg.lua.sh
. "${__type}/files/prosody.cfg.lua.sh" # This defines PROSODY_CONFIG
__file "/etc/prosody/conf.avail/${DOMAIN}.cfg.lua" \
--group prosody \
--mode 0440 \
--state "${STATE}" \
--source '-' <<EOF
${PROSODY_CONFIG}
EOF
__link "/etc/prosody/conf.d/${DOMAIN}.cfg.lua" \
--source "/etc/prosody/conf.avail/${DOMAIN}.cfg.lua" \
--state "${STATE}" \
--type symbolic
if [ "${STATE}" = "present" ]; then
export require="${require} __file/etc/prosody/conf.avail/${DOMAIN}.cfg.lua __link/etc/prosody/conf.d/${DOMAIN}.cfg.lua"
__check_messages "prosody/${DOMAIN}" \
--pattern '^(__file|__link)/etc/prosody/conf[.](avail|d)/' \
--execute "$(cat <<EOF
if [ ! -f "/var/lib/prosody/${DOMAIN}.crt" ]; then
echo | prosodyctl cert generate '${DOMAIN}';
ln -sf '/var/lib/prosody/${DOMAIN}.key' '/etc/prosody/certs/${DOMAIN}.key'
ln -sf '/var/lib/prosody/${DOMAIN}.crt' '/etc/prosody/certs/${DOMAIN}.crt'
fi
# Surprisingly, a reload is not enough
service prosody restart
EOF
)"
fi