mirror of
https://github.com/casjaysdevdocker/prosody
synced 2026-06-24 02:01:03 -04:00
🦈🏠🐜❗ Initial Commit ❗🐜🦈🏠
This commit is contained in:
@@ -0,0 +1,489 @@
|
||||
#!/usr/bin/env bash
|
||||
# shellcheck shell=bash
|
||||
# entrypoint.sh — container startup script
|
||||
# Called by: tini → entrypoint.sh → prosody
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
# - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
# Defaults
|
||||
# - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
|
||||
: "${XMPP_DOMAIN:=meet.jitsi}"
|
||||
: "${XMPP_AUTH_DOMAIN:=auth.meet.jitsi}"
|
||||
: "${XMPP_GUEST_DOMAIN:=guest.meet.jitsi}"
|
||||
: "${XMPP_MUC_DOMAIN:=muc.meet.jitsi}"
|
||||
: "${XMPP_INTERNAL_MUC_DOMAIN:=internal-muc.meet.jitsi}"
|
||||
: "${XMPP_HIDDEN_DOMAIN:=hidden.meet.jitsi}"
|
||||
: "${XMPP_PORT:=5222}"
|
||||
: "${PROSODY_HTTP_PORT:=5280}"
|
||||
: "${ENABLE_AUTH:=0}"
|
||||
: "${ENABLE_GUESTS:=0}"
|
||||
: "${ENABLE_XMPP_WEBSOCKET:=1}"
|
||||
: "${ENABLE_LOBBY:=1}"
|
||||
: "${ENABLE_BREAKOUT_ROOMS:=1}"
|
||||
: "${ENABLE_AV_MODERATION:=1}"
|
||||
: "${ENABLE_END_CONFERENCE:=1}"
|
||||
: "${ENABLE_RECORDING:=0}"
|
||||
: "${ENABLE_TRANSCRIPTIONS:=0}"
|
||||
: "${JVB_AUTH_USER:=jvb}"
|
||||
: "${JIBRI_RECORDER_USER:=recorder}"
|
||||
: "${JIBRI_XMPP_USER:=jibri}"
|
||||
: "${JIGASI_XMPP_USER:=jigasi}"
|
||||
: "${LOG_LEVEL:=info}"
|
||||
: "${PROSODY_MODE:=client}"
|
||||
: "${PROSODY_C2S_REQUIRE_ENCRYPTION:=true}"
|
||||
: "${PROSODY_ADMINS:=}"
|
||||
: "${AUTH_TYPE:=internal}"
|
||||
: "${DISABLE_POLLS:=0}"
|
||||
|
||||
# Derived
|
||||
XMPP_MUC_DOMAIN_PREFIX="${XMPP_MUC_DOMAIN%%.*}"
|
||||
PROSODY_CFG="/config/prosody.cfg.lua"
|
||||
PROSODY_SITE_CFG="/config/conf.d/jitsi-meet.cfg.lua"
|
||||
|
||||
# - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
# Guards
|
||||
# - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
|
||||
[[ -n "${JICOFO_AUTH_PASSWORD:-}" ]] || { echo "FATAL: JICOFO_AUTH_PASSWORD must be set" >&2; exit 1; }
|
||||
[[ -n "${JVB_AUTH_PASSWORD:-}" ]] || { echo "FATAL: JVB_AUTH_PASSWORD must be set" >&2; exit 1; }
|
||||
|
||||
# - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
# Directory setup
|
||||
# - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
|
||||
mkdir -p /config/certs /config/conf.d /config/data \
|
||||
/prosody-plugins-custom /prosody-plugins-contrib
|
||||
|
||||
chown -R prosody:prosody /config /prosody-plugins-custom /prosody-plugins-contrib 2>/dev/null || true
|
||||
|
||||
# - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
# Generate main prosody.cfg.lua
|
||||
# - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
|
||||
__generate_main_cfg() {
|
||||
# Build admins list
|
||||
local admins_list=""
|
||||
if [[ -n "${PROSODY_ADMINS}" ]]; then
|
||||
while IFS=',' read -ra parts; do
|
||||
for admin in "${parts[@]}"; do
|
||||
admin="${admin// /}"
|
||||
[[ -n "$admin" ]] && admins_list+=" \"${admin}\";\n"
|
||||
done
|
||||
done <<< "${PROSODY_ADMINS}"
|
||||
fi
|
||||
|
||||
cat > "${PROSODY_CFG}" << MAIN_CFG
|
||||
-- Generated by entrypoint.sh — do not edit manually
|
||||
|
||||
---------- Server-wide settings ----------
|
||||
|
||||
admins = {
|
||||
$(printf '%b' "${admins_list}")}
|
||||
|
||||
component_admins_as_room_owners = true
|
||||
|
||||
modules_enabled = {
|
||||
-- Core
|
||||
"roster";
|
||||
"saslauth";
|
||||
"tls";
|
||||
"disco";
|
||||
"ping";
|
||||
"version";
|
||||
"posix";
|
||||
"limits";
|
||||
"private";
|
||||
|
||||
-- HTTP (BOSH + WebSocket — required for Jitsi)
|
||||
"bosh";
|
||||
"websocket";
|
||||
"http";
|
||||
"http_health";
|
||||
|
||||
-- Status
|
||||
"uptime";
|
||||
};
|
||||
|
||||
modules_disabled = {
|
||||
"offline";
|
||||
"register";
|
||||
"s2s";
|
||||
};
|
||||
|
||||
-- Never allow unauthenticated registration globally
|
||||
allow_registration = false;
|
||||
|
||||
-- Rate limits for incoming connections
|
||||
limits = {
|
||||
c2s = {
|
||||
rate = "10kb/s";
|
||||
};
|
||||
};
|
||||
|
||||
-- Garbage collector
|
||||
gc = {
|
||||
mode = "incremental";
|
||||
threshold = 400;
|
||||
speed = 250;
|
||||
step_size = 13;
|
||||
};
|
||||
|
||||
pidfile = "/config/data/prosody.pid";
|
||||
|
||||
c2s_require_encryption = ${PROSODY_C2S_REQUIRE_ENCRYPTION};
|
||||
c2s_ports = { ${XMPP_PORT} }
|
||||
c2s_interfaces = { "*" }
|
||||
|
||||
s2s_secure_auth = false
|
||||
|
||||
authentication = "internal_hashed"
|
||||
|
||||
-- Logging
|
||||
log = {
|
||||
{ levels = { min = "${LOG_LEVEL}" }, timestamps = "%Y-%m-%d %X", to = "console" };
|
||||
};
|
||||
|
||||
-- HTTP configuration
|
||||
-- consider_bosh_secure and consider_websocket_secure allow HTTPS-terminated
|
||||
-- connections from a reverse proxy to be treated as secure.
|
||||
consider_bosh_secure = true;
|
||||
consider_websocket_secure = true;
|
||||
|
||||
-- Allow WebSocket + BOSH on the same port (5280) that the reverse proxy reaches
|
||||
http_ports = { ${PROSODY_HTTP_PORT} }
|
||||
http_interfaces = { "*" }
|
||||
|
||||
-- Stream management (required for smacks / WebSocket reconnect)
|
||||
smacks_max_unacked_stanzas = 5;
|
||||
smacks_hibernation_time = 60;
|
||||
smacks_max_old_sessions = 1;
|
||||
|
||||
-- epoll backend for better performance
|
||||
network_backend = "epoll";
|
||||
network_settings = {
|
||||
tcp_backlog = 511;
|
||||
};
|
||||
|
||||
-- Data storage
|
||||
data_path = "/config/data";
|
||||
|
||||
-- Plugin paths
|
||||
plugin_paths = { "/prosody-plugins-custom", "/prosody-plugins/", "/prosody-plugins-contrib" };
|
||||
|
||||
Include "conf.d/*.cfg.lua"
|
||||
MAIN_CFG
|
||||
}
|
||||
|
||||
# - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
# Generate conf.d/jitsi-meet.cfg.lua
|
||||
# - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
|
||||
__generate_site_cfg() {
|
||||
# Determine main VirtualHost authentication
|
||||
local main_auth="jitsi-anonymous"
|
||||
if [[ "${ENABLE_AUTH}" == "1" ]] || [[ "${ENABLE_AUTH}" == "true" ]]; then
|
||||
main_auth="internal_hashed"
|
||||
fi
|
||||
|
||||
# Determine guest VirtualHost authentication
|
||||
local guest_auth="jitsi-anonymous"
|
||||
|
||||
# Build global admins block for conf.d
|
||||
cat > "${PROSODY_SITE_CFG}" << SITE_START
|
||||
-- Generated by entrypoint.sh — do not edit manually
|
||||
|
||||
admins = {
|
||||
"focus@${XMPP_AUTH_DOMAIN}",
|
||||
"${JVB_AUTH_USER}@${XMPP_AUTH_DOMAIN}"
|
||||
}
|
||||
|
||||
unlimited_jids = {
|
||||
"focus@${XMPP_AUTH_DOMAIN}",
|
||||
"${JVB_AUTH_USER}@${XMPP_AUTH_DOMAIN}"
|
||||
}
|
||||
|
||||
muc_mapper_domain_base = "${XMPP_DOMAIN}";
|
||||
muc_mapper_domain_prefix = "${XMPP_MUC_DOMAIN_PREFIX}";
|
||||
|
||||
recorder_prefixes = { "${JIBRI_RECORDER_USER}@${XMPP_HIDDEN_DOMAIN}" };
|
||||
transcriber_prefixes = { "${JIGASI_TRANSCRIBER_USER:-transcriber}@${XMPP_HIDDEN_DOMAIN}" };
|
||||
|
||||
http_default_host = "${XMPP_DOMAIN}"
|
||||
|
||||
-- Main virtual host
|
||||
VirtualHost "${XMPP_DOMAIN}"
|
||||
authentication = "${main_auth}"
|
||||
ssl = {
|
||||
key = "/config/certs/${XMPP_DOMAIN}.key";
|
||||
certificate = "/config/certs/${XMPP_DOMAIN}.crt";
|
||||
}
|
||||
modules_enabled = {
|
||||
"bosh";
|
||||
"websocket";
|
||||
"smacks";
|
||||
"conference_duration";
|
||||
"muc_lobby_rooms";
|
||||
"muc_breakout_rooms";
|
||||
"features_identity";
|
||||
SITE_START
|
||||
|
||||
# Optional XMPP_MODULES
|
||||
if [[ -n "${XMPP_MODULES:-}" ]]; then
|
||||
while IFS=',' read -ra mods; do
|
||||
for mod in "${mods[@]}"; do
|
||||
mod="${mod// /}"
|
||||
[[ -n "$mod" ]] && printf ' "%s";\n' "${mod}" >> "${PROSODY_SITE_CFG}"
|
||||
done
|
||||
done <<< "${XMPP_MODULES}"
|
||||
fi
|
||||
|
||||
cat >> "${PROSODY_SITE_CFG}" << SITE_MAIN_HOST
|
||||
}
|
||||
main_muc = "${XMPP_MUC_DOMAIN}"
|
||||
lobby_muc = "lobby.${XMPP_DOMAIN}"
|
||||
breakout_rooms_muc = "breakout.${XMPP_DOMAIN}"
|
||||
c2s_require_encryption = ${PROSODY_C2S_REQUIRE_ENCRYPTION}
|
||||
|
||||
SITE_MAIN_HOST
|
||||
|
||||
# Guest domain (only when auth is enabled and guests are enabled)
|
||||
if { [[ "${ENABLE_AUTH}" == "1" ]] || [[ "${ENABLE_AUTH}" == "true" ]]; } && \
|
||||
{ [[ "${ENABLE_GUESTS}" == "1" ]] || [[ "${ENABLE_GUESTS}" == "true" ]]; }; then
|
||||
cat >> "${PROSODY_SITE_CFG}" << SITE_GUEST
|
||||
VirtualHost "${XMPP_GUEST_DOMAIN}"
|
||||
authentication = "${guest_auth}"
|
||||
modules_enabled = {
|
||||
"smacks";
|
||||
}
|
||||
main_muc = "${XMPP_MUC_DOMAIN}"
|
||||
lobby_muc = "lobby.${XMPP_DOMAIN}"
|
||||
breakout_rooms_muc = "breakout.${XMPP_DOMAIN}"
|
||||
c2s_require_encryption = ${PROSODY_C2S_REQUIRE_ENCRYPTION}
|
||||
|
||||
SITE_GUEST
|
||||
fi
|
||||
|
||||
# Auth domain
|
||||
cat >> "${PROSODY_SITE_CFG}" << SITE_AUTH
|
||||
VirtualHost "${XMPP_AUTH_DOMAIN}"
|
||||
ssl = {
|
||||
key = "/config/certs/${XMPP_AUTH_DOMAIN}.key";
|
||||
certificate = "/config/certs/${XMPP_AUTH_DOMAIN}.crt";
|
||||
}
|
||||
modules_enabled = {
|
||||
"limits_exception";
|
||||
"smacks";
|
||||
}
|
||||
authentication = "internal_hashed"
|
||||
smacks_hibernation_time = 15;
|
||||
|
||||
SITE_AUTH
|
||||
|
||||
# Hidden domain for recording/transcription
|
||||
if [[ "${ENABLE_RECORDING}" == "1" ]] || [[ "${ENABLE_RECORDING}" == "true" ]] || \
|
||||
[[ "${ENABLE_TRANSCRIPTIONS}" == "1" ]] || [[ "${ENABLE_TRANSCRIPTIONS}" == "true" ]]; then
|
||||
cat >> "${PROSODY_SITE_CFG}" << SITE_HIDDEN
|
||||
VirtualHost "${XMPP_HIDDEN_DOMAIN}"
|
||||
modules_enabled = {
|
||||
"smacks";
|
||||
}
|
||||
authentication = "internal_hashed"
|
||||
|
||||
SITE_HIDDEN
|
||||
fi
|
||||
|
||||
# Components
|
||||
cat >> "${PROSODY_SITE_CFG}" << SITE_COMPONENTS
|
||||
-- Internal MUC (JVB/jicofo signaling — never exposed to clients)
|
||||
Component "${XMPP_INTERNAL_MUC_DOMAIN}" "muc"
|
||||
storage = "memory"
|
||||
modules_enabled = {
|
||||
"muc_hide_all";
|
||||
"muc_filter_access";
|
||||
}
|
||||
restrict_room_creation = true
|
||||
muc_filter_whitelist = "${XMPP_AUTH_DOMAIN}"
|
||||
muc_room_locking = false
|
||||
muc_room_default_public_jids = true
|
||||
muc_room_cache_size = 1000
|
||||
muc_tombstones = false
|
||||
muc_room_allow_persistent = false
|
||||
|
||||
-- Conference MUC (participants join rooms here)
|
||||
Component "${XMPP_MUC_DOMAIN}" "muc"
|
||||
restrict_room_creation = true
|
||||
storage = "memory"
|
||||
modules_enabled = {
|
||||
"muc_hide_all";
|
||||
"muc_meeting_id";
|
||||
"muc_domain_mapper";
|
||||
"muc_password_whitelist";
|
||||
}
|
||||
muc_room_cache_size = 10000
|
||||
muc_room_locking = false
|
||||
muc_room_default_public_jids = true
|
||||
muc_tombstones = false
|
||||
muc_room_allow_persistent = false
|
||||
muc_password_whitelist = {
|
||||
"focus@${XMPP_AUTH_DOMAIN}";
|
||||
}
|
||||
|
||||
-- Focus component proxy
|
||||
Component "focus.${XMPP_DOMAIN}" "client_proxy"
|
||||
target_address = "focus@${XMPP_AUTH_DOMAIN}"
|
||||
|
||||
-- Speakerstats component
|
||||
Component "speakerstats.${XMPP_DOMAIN}" "speakerstats_component"
|
||||
muc_component = "${XMPP_MUC_DOMAIN}"
|
||||
|
||||
-- End conference component
|
||||
Component "endconference.${XMPP_DOMAIN}" "end_conference"
|
||||
muc_component = "${XMPP_MUC_DOMAIN}"
|
||||
|
||||
-- AV moderation component
|
||||
Component "avmoderation.${XMPP_DOMAIN}" "av_moderation_component"
|
||||
muc_component = "${XMPP_MUC_DOMAIN}"
|
||||
|
||||
-- Lobby MUC
|
||||
Component "lobby.${XMPP_DOMAIN}" "muc"
|
||||
storage = "memory"
|
||||
restrict_room_creation = true
|
||||
muc_tombstones = false
|
||||
muc_room_allow_persistent = false
|
||||
muc_room_cache_size = 10000
|
||||
muc_room_locking = false
|
||||
muc_room_default_public_jids = true
|
||||
modules_enabled = {
|
||||
"muc_hide_all";
|
||||
}
|
||||
|
||||
-- Breakout rooms MUC
|
||||
Component "breakout.${XMPP_DOMAIN}" "muc"
|
||||
storage = "memory"
|
||||
restrict_room_creation = true
|
||||
muc_room_cache_size = 10000
|
||||
muc_room_locking = false
|
||||
muc_room_default_public_jids = true
|
||||
muc_tombstones = false
|
||||
muc_room_allow_persistent = false
|
||||
modules_enabled = {
|
||||
"muc_hide_all";
|
||||
"muc_meeting_id";
|
||||
}
|
||||
|
||||
-- Room metadata component
|
||||
Component "metadata.${XMPP_DOMAIN}" "room_metadata_component"
|
||||
muc_component = "${XMPP_MUC_DOMAIN}"
|
||||
breakout_rooms_component = "breakout.${XMPP_DOMAIN}"
|
||||
|
||||
SITE_COMPONENTS
|
||||
|
||||
# Polls component (optional disable)
|
||||
if [[ "${DISABLE_POLLS}" != "1" ]] && [[ "${DISABLE_POLLS}" != "true" ]]; then
|
||||
cat >> "${PROSODY_SITE_CFG}" << SITE_POLLS
|
||||
Component "polls.${XMPP_DOMAIN}" "polls_component"
|
||||
|
||||
SITE_POLLS
|
||||
fi
|
||||
}
|
||||
|
||||
# - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
# Certificate generation
|
||||
# - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
|
||||
__generate_cert() {
|
||||
local domain="${1}"
|
||||
local cert_dir="/config/certs"
|
||||
local key="${cert_dir}/${domain}.key"
|
||||
local crt="${cert_dir}/${domain}.crt"
|
||||
|
||||
[[ -f "${key}" && -f "${crt}" ]] && return 0
|
||||
|
||||
echo "INFO: Generating self-signed certificate for ${domain}"
|
||||
openssl req -x509 -newkey rsa:4096 \
|
||||
-keyout "${key}" -out "${crt}" \
|
||||
-days 3650 -nodes \
|
||||
-subj "/CN=${domain}" 2>/dev/null
|
||||
chmod 600 "${key}"
|
||||
chown prosody:prosody "${key}" "${crt}" 2>/dev/null || true
|
||||
}
|
||||
|
||||
__generate_certs() {
|
||||
__generate_cert "${XMPP_DOMAIN}"
|
||||
__generate_cert "${XMPP_AUTH_DOMAIN}"
|
||||
}
|
||||
|
||||
# - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
# User registration
|
||||
# - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
|
||||
__register_user() {
|
||||
local user="${1}" domain="${2}" pass="${3}"
|
||||
# prosodyctl register is idempotent — re-register just updates the password
|
||||
su-exec prosody prosodyctl --config "${PROSODY_CFG}" \
|
||||
register "${user}" "${domain}" "${pass}" 2>/dev/null || true
|
||||
}
|
||||
|
||||
__register_jitsi_users() {
|
||||
# Start prosody temporarily in background so prosodyctl can connect
|
||||
su-exec prosody prosody --config "${PROSODY_CFG}" -F &
|
||||
local pid=$!
|
||||
|
||||
# Wait until prosody is accepting connections (up to 30s)
|
||||
local i=0
|
||||
while ! su-exec prosody prosodyctl --config "${PROSODY_CFG}" status >/dev/null 2>&1; do
|
||||
sleep 1
|
||||
i=$(( i + 1 ))
|
||||
if (( i >= 30 )); then
|
||||
echo "FATAL: prosody did not start within 30s" >&2
|
||||
kill "${pid}" 2>/dev/null || true
|
||||
exit 1
|
||||
fi
|
||||
done
|
||||
|
||||
echo "INFO: Registering jicofo user"
|
||||
__register_user "focus" "${XMPP_AUTH_DOMAIN}" "${JICOFO_AUTH_PASSWORD}"
|
||||
|
||||
echo "INFO: Registering jvb user"
|
||||
__register_user "${JVB_AUTH_USER}" "${XMPP_AUTH_DOMAIN}" "${JVB_AUTH_PASSWORD}"
|
||||
|
||||
# Subscribe focus to the focus component so roster is correct
|
||||
if [[ "${PROSODY_MODE}" == "client" ]]; then
|
||||
su-exec prosody prosodyctl --config "${PROSODY_CFG}" \
|
||||
mod_roster_command subscribe "focus.${XMPP_DOMAIN}" "focus@${XMPP_AUTH_DOMAIN}" 2>/dev/null || true
|
||||
fi
|
||||
|
||||
# Optional: jibri user
|
||||
if [[ -n "${JIBRI_XMPP_PASSWORD:-}" ]]; then
|
||||
echo "INFO: Registering jibri user"
|
||||
__register_user "${JIBRI_XMPP_USER}" "${XMPP_AUTH_DOMAIN}" "${JIBRI_XMPP_PASSWORD}"
|
||||
fi
|
||||
|
||||
# Optional: jigasi user
|
||||
if [[ -n "${JIGASI_XMPP_PASSWORD:-}" ]]; then
|
||||
echo "INFO: Registering jigasi user"
|
||||
__register_user "${JIGASI_XMPP_USER}" "${XMPP_AUTH_DOMAIN}" "${JIGASI_XMPP_PASSWORD}"
|
||||
fi
|
||||
|
||||
# Shut down the temporary instance
|
||||
kill "${pid}" 2>/dev/null || true
|
||||
wait "${pid}" 2>/dev/null || true
|
||||
# Clean up pid file so the main instance can start
|
||||
rm -f /config/data/prosody.pid
|
||||
sleep 1
|
||||
}
|
||||
|
||||
# - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
# Main
|
||||
# - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
|
||||
echo "INFO: Generating prosody configuration"
|
||||
__generate_main_cfg
|
||||
__generate_site_cfg
|
||||
__generate_certs
|
||||
__register_jitsi_users
|
||||
|
||||
echo "INFO: Starting prosody"
|
||||
exec su-exec prosody prosody --config "${PROSODY_CFG}" -F
|
||||
@@ -0,0 +1,7 @@
|
||||
#!/usr/bin/env bash
|
||||
# healthcheck.sh — container health probe
|
||||
# Returns 0 (healthy) if prosody's HTTP health endpoint responds 200
|
||||
|
||||
: "${PROSODY_HTTP_PORT:=5280}"
|
||||
|
||||
curl -q -LSs --max-time 5 "http://127.0.0.1:${PROSODY_HTTP_PORT}/health" -o /dev/null
|
||||
Reference in New Issue
Block a user