secubox-openwrt/package/secubox/secubox-app-jitsi/files/usr/sbin/jitsctl
CyberMind-FR 6db547f7f8 feat: Add WebRadio, TURN server, and Lyrion streaming integration
New packages:
- luci-app-webradio: Web radio management with Lyrion bridge tab
- luci-app-turn: TURN/STUN server UI for WebRTC (Jitsi integration)
- secubox-app-lyrion-bridge: Lyrion → Squeezelite → FFmpeg → Icecast pipeline
- secubox-app-squeezelite: Squeezelite audio player with FIFO output
- secubox-app-turn: TURN server with ACME SSL and Jitsi setup
- secubox-app-webradio: Icecast/ezstream web radio server

Features:
- HTTPS streaming via HAProxy (stream.gk2.secubox.in)
- Lyrion Music Server bridge for streaming playlists to Icecast
- TURN server with time-limited credential generation
- CrowdSec integration for WebRadio security
- Schedule-based radio programming with jingles

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-21 17:46:54 +01:00

724 lines
18 KiB
Bash

#!/bin/sh
# Jitsi Meet Control Script for SecuBox
# Manages Jitsi Meet Docker deployment
VERSION="1.0.0"
JITSI_DIR="/srv/jitsi"
CONFIG_DIR="$JITSI_DIR/.jitsi-meet-cfg"
TEMPLATE_DIR="/usr/share/jitsi"
ENV_FILE="$JITSI_DIR/.env"
COMPOSE_FILE="$JITSI_DIR/docker-compose.yml"
# Jitsi Docker image versions
JITSI_IMAGE_VERSION="stable-9location1"
# Colors
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
CYAN='\033[0;36m'
NC='\033[0m'
log() { echo -e "${GREEN}[JITSI]${NC} $1"; }
warn() { echo -e "${YELLOW}[WARN]${NC} $1"; }
error() { echo -e "${RED}[ERROR]${NC} $1"; }
# ============================================================================
# Configuration Generation
# ============================================================================
generate_secret() {
openssl rand -hex 16 2>/dev/null || head -c 32 /dev/urandom | md5sum | cut -d' ' -f1
}
generate_config() {
log "Generating Jitsi configuration..."
mkdir -p "$JITSI_DIR"
mkdir -p "$CONFIG_DIR"/{web,transcripts,prosody/config,prosody/prosody-plugins-custom,jicofo,jvb,jigasi,jibri}
# Read UCI configuration
local domain=$(uci -q get jitsi.main.domain || echo "meet.secubox.local")
local public_url=$(uci -q get jitsi.main.public_url)
[ -z "$public_url" ] && public_url="https://$domain"
local timezone=$(uci -q get jitsi.main.timezone || echo "UTC")
local letsencrypt=$(uci -q get jitsi.main.letsencrypt || echo "0")
local le_email=$(uci -q get jitsi.main.letsencrypt_email)
# Web config
local web_port=$(uci -q get jitsi.web.port || echo "8443")
local enable_guests=$(uci -q get jitsi.web.enable_guests || echo "1")
local enable_auth=$(uci -q get jitsi.web.enable_auth || echo "0")
local default_lang=$(uci -q get jitsi.web.default_language || echo "en")
# JVB config
local jvb_port=$(uci -q get jitsi.jvb.port || echo "10000")
local jvb_tcp=$(uci -q get jitsi.jvb.enable_tcp_fallback || echo "0")
local jvb_tcp_port=$(uci -q get jitsi.jvb.tcp_port || echo "4443")
local stun_servers=$(uci -q get jitsi.jvb.stun_servers || echo "meet-jit-si-turnrelay.jitsi.net:443")
# Security
local jwt_enabled=$(uci -q get jitsi.security.jwt_enabled || echo "0")
local jwt_app_id=$(uci -q get jitsi.security.jwt_app_id)
local jwt_secret=$(uci -q get jitsi.security.jwt_app_secret)
local lobby=$(uci -q get jitsi.security.lobby_enabled || echo "1")
# TURN
local turn_enabled=$(uci -q get jitsi.turn.enabled || echo "0")
local turn_server=$(uci -q get jitsi.turn.server)
local turn_user=$(uci -q get jitsi.turn.username)
local turn_pass=$(uci -q get jitsi.turn.password)
# Generate secrets if not already stored
local jicofo_secret=$(uci -q get jitsi.secrets.jicofo_component_secret)
local jicofo_auth_pass=$(uci -q get jitsi.secrets.jicofo_auth_password)
local jvb_auth_pass=$(uci -q get jitsi.secrets.jvb_auth_password)
if [ -z "$jicofo_secret" ]; then
jicofo_secret=$(generate_secret)
uci -q set jitsi.secrets=jitsi
uci -q set jitsi.secrets.jicofo_component_secret="$jicofo_secret"
fi
if [ -z "$jicofo_auth_pass" ]; then
jicofo_auth_pass=$(generate_secret)
uci -q set jitsi.secrets.jicofo_auth_password="$jicofo_auth_pass"
fi
if [ -z "$jvb_auth_pass" ]; then
jvb_auth_pass=$(generate_secret)
uci -q set jitsi.secrets.jvb_auth_password="$jvb_auth_pass"
uci commit jitsi
fi
# Determine auth type
local auth_type="internal"
[ "$enable_auth" = "1" ] && auth_type="internal_hashed"
[ "$jwt_enabled" = "1" ] && auth_type="jwt"
# Write .env file
cat > "$ENV_FILE" << EOF
# Jitsi Meet Configuration
# Generated by SecuBox jitsctl v$VERSION
# $(date)
# Core settings
CONFIG=$CONFIG_DIR
HTTP_PORT=8000
HTTPS_PORT=$web_port
TZ=$timezone
# Domain settings
PUBLIC_URL=$public_url
XMPP_DOMAIN=$domain
XMPP_SERVER=xmpp.$domain
JVB_BREWERY_MUC=jvbbrewery
XMPP_AUTH_DOMAIN=auth.$domain
XMPP_GUEST_DOMAIN=guest.$domain
XMPP_MUC_DOMAIN=muc.$domain
XMPP_INTERNAL_MUC_DOMAIN=internal-muc.$domain
XMPP_MODULES=
XMPP_MUC_MODULES=
XMPP_INTERNAL_MUC_MODULES=
# Authentication
ENABLE_AUTH=$enable_auth
ENABLE_GUESTS=$enable_guests
AUTH_TYPE=$auth_type
# JWT (if enabled)
EOF
if [ "$jwt_enabled" = "1" ]; then
cat >> "$ENV_FILE" << EOF
JWT_APP_ID=$jwt_app_id
JWT_APP_SECRET=$jwt_secret
JWT_ACCEPTED_ISSUERS=$jwt_app_id
JWT_ACCEPTED_AUDIENCES=$jwt_app_id
EOF
fi
cat >> "$ENV_FILE" << EOF
# XMPP secrets
JICOFO_COMPONENT_SECRET=$jicofo_secret
JICOFO_AUTH_USER=focus
JICOFO_AUTH_PASSWORD=$jicofo_auth_pass
JVB_AUTH_USER=jvb
JVB_AUTH_PASSWORD=$jvb_auth_pass
# JVB settings
JVB_PORT=$jvb_port
JVB_STUN_SERVERS=$stun_servers
JVB_TCP_HARVESTER_DISABLED=$([ "$jvb_tcp" = "1" ] && echo "false" || echo "true")
JVB_TCP_PORT=$jvb_tcp_port
# Features
ENABLE_LOBBY=$lobby
ENABLE_BREAKOUT_ROOMS=1
ENABLE_PREJOIN_PAGE=1
ENABLE_WELCOME_PAGE=1
ENABLE_CLOSE_PAGE=0
ENABLE_NOISY_MIC_DETECTION=1
ENABLE_TALK_WHILE_MUTED=1
ENABLE_REACTIONS=1
# UI settings
DEFAULT_LANGUAGE=$default_lang
DISABLE_AUDIO_LEVELS=0
DISABLE_POLLS=0
ENABLE_CALENDAR=0
EOF
# Add TURN config if enabled
if [ "$turn_enabled" = "1" ] && [ -n "$turn_server" ]; then
cat >> "$ENV_FILE" << EOF
# TURN server
TURN_CREDENTIALS=$turn_user
TURN_SECRET=$turn_pass
TURN_HOST=$turn_server
TURN_PORT=443
TURNS_HOST=$turn_server
TURNS_PORT=443
EOF
fi
# Add Let's Encrypt config
if [ "$letsencrypt" = "1" ] && [ -n "$le_email" ]; then
cat >> "$ENV_FILE" << EOF
# Let's Encrypt
ENABLE_LETSENCRYPT=1
LETSENCRYPT_DOMAIN=$domain
LETSENCRYPT_EMAIL=$le_email
LETSENCRYPT_USE_STAGING=0
EOF
fi
# Copy docker-compose.yml
cp "$TEMPLATE_DIR/docker-compose.yml" "$COMPOSE_FILE"
log "Configuration generated at $ENV_FILE"
}
# ============================================================================
# Installation
# ============================================================================
install_jitsi() {
log "Installing Jitsi Meet..."
# Check Docker
if ! command -v docker >/dev/null 2>&1; then
error "Docker is required. Install with: opkg install docker docker-compose"
return 1
fi
# Create directories
mkdir -p "$JITSI_DIR"
mkdir -p "$CONFIG_DIR"
# Generate configuration
generate_config
# Pull images
log "Pulling Jitsi Docker images (this may take a while)..."
cd "$JITSI_DIR"
docker-compose pull
# Configure HAProxy if available
if [ -x /usr/sbin/haproxyctl ]; then
configure_haproxy
fi
# Configure firewall
configure_firewall
# Register with mesh
register_mesh_service
log "Jitsi Meet installed successfully!"
echo ""
echo "Next steps:"
echo " 1. Set your domain: uci set jitsi.main.domain='meet.example.com'"
echo " 2. Commit changes: uci commit jitsi"
echo " 3. Regenerate: jitsctl generate-config"
echo " 4. Enable service: uci set jitsi.main.enabled=1 && uci commit jitsi"
echo " 5. Start: /etc/init.d/jitsi start"
echo ""
}
# ============================================================================
# HAProxy Integration
# ============================================================================
configure_haproxy() {
log "Configuring HAProxy for Jitsi..."
local domain=$(uci -q get jitsi.main.domain || echo "meet.secubox.local")
local web_port=$(uci -q get jitsi.web.port || echo "8443")
# Check if vhost already exists
local existing=$(uci show haproxy 2>/dev/null | grep "\.domain='$domain'" | head -1)
if [ -n "$existing" ]; then
warn "HAProxy vhost for $domain already exists"
return 0
fi
# Add backend
uci -q add haproxy backend
uci -q set haproxy.@backend[-1].name='jitsi_web'
uci -q set haproxy.@backend[-1].mode='http'
uci -q add_list haproxy.@backend[-1].server="jitsi 192.168.255.1:$web_port check"
# Add vhost
uci -q add haproxy vhost
uci -q set haproxy.@vhost[-1].enabled='1'
uci -q set haproxy.@vhost[-1].domain="$domain"
uci -q set haproxy.@vhost[-1].backend='jitsi_web'
uci -q set haproxy.@vhost[-1].ssl='1'
uci -q set haproxy.@vhost[-1].ssl_redirect='1'
uci -q set haproxy.@vhost[-1].websocket='1'
uci commit haproxy
# Reload HAProxy
/etc/init.d/haproxy reload 2>/dev/null
log "HAProxy configured for $domain"
}
# ============================================================================
# Firewall
# ============================================================================
configure_firewall() {
log "Configuring firewall..."
local jvb_port=$(uci -q get jitsi.jvb.port || echo "10000")
local jvb_tcp_port=$(uci -q get jitsi.jvb.tcp_port || echo "4443")
# JVB UDP port (required for video)
if ! uci show firewall 2>/dev/null | grep -q "Jitsi-JVB"; then
uci add firewall rule
uci set firewall.@rule[-1].name='Jitsi-JVB'
uci set firewall.@rule[-1].src='wan'
uci set firewall.@rule[-1].dest_port="$jvb_port"
uci set firewall.@rule[-1].proto='udp'
uci set firewall.@rule[-1].target='ACCEPT'
uci set firewall.@rule[-1].enabled='1'
fi
# JVB TCP fallback port
local jvb_tcp=$(uci -q get jitsi.jvb.enable_tcp_fallback || echo "0")
if [ "$jvb_tcp" = "1" ]; then
if ! uci show firewall 2>/dev/null | grep -q "Jitsi-JVB-TCP"; then
uci add firewall rule
uci set firewall.@rule[-1].name='Jitsi-JVB-TCP'
uci set firewall.@rule[-1].src='wan'
uci set firewall.@rule[-1].dest_port="$jvb_tcp_port"
uci set firewall.@rule[-1].proto='tcp'
uci set firewall.@rule[-1].target='ACCEPT'
uci set firewall.@rule[-1].enabled='1'
fi
fi
uci commit firewall
/etc/init.d/firewall reload 2>/dev/null
log "Firewall configured"
}
# ============================================================================
# Mesh Integration
# ============================================================================
register_mesh_service() {
local mesh_enabled=$(uci -q get jitsi.mesh.enabled || echo "0")
[ "$mesh_enabled" != "1" ] && return 0
local domain=$(uci -q get jitsi.main.domain)
local web_port=$(uci -q get jitsi.web.port || echo "8443")
# Register with P2P daemon
if [ -x /usr/sbin/secubox-p2p ]; then
/usr/sbin/secubox-p2p register-service jitsi-meet "$web_port" 2>/dev/null
log "Registered Jitsi with mesh network"
fi
# Add DNS entry if DNS federation enabled
local dns_enabled=$(uci -q get secubox-p2p.dns.enabled || echo "0")
if [ "$dns_enabled" = "1" ]; then
local dns_domain=$(uci -q get secubox-p2p.dns.base_domain || echo "mesh.local")
local hostname=$(echo "$domain" | cut -d'.' -f1)
log "Mesh DNS: $hostname.$dns_domain"
fi
}
# ============================================================================
# User Management
# ============================================================================
add_user() {
local username="$1"
local password="$2"
if [ -z "$username" ] || [ -z "$password" ]; then
echo "Usage: jitsctl add-user <username> <password>"
return 1
fi
local domain=$(uci -q get jitsi.main.domain)
log "Adding user: $username"
docker exec jitsi-prosody prosodyctl register "$username" "$domain" "$password"
if [ $? -eq 0 ]; then
log "User $username added successfully"
else
error "Failed to add user $username"
return 1
fi
}
remove_user() {
local username="$1"
if [ -z "$username" ]; then
echo "Usage: jitsctl remove-user <username>"
return 1
fi
local domain=$(uci -q get jitsi.main.domain)
log "Removing user: $username"
docker exec jitsi-prosody prosodyctl unregister "$username" "$domain"
if [ $? -eq 0 ]; then
log "User $username removed"
else
error "Failed to remove user $username"
return 1
fi
}
list_users() {
local domain=$(uci -q get jitsi.main.domain)
echo "Registered users for $domain:"
docker exec jitsi-prosody ls -1 /config/data/"$domain"/accounts/ 2>/dev/null | sed 's/\.dat$//'
}
# ============================================================================
# Status & Logs
# ============================================================================
show_status() {
echo ""
echo "========================================"
echo " Jitsi Meet Status v$VERSION"
echo "========================================"
echo ""
local enabled=$(uci -q get jitsi.main.enabled)
local domain=$(uci -q get jitsi.main.domain)
local public_url=$(uci -q get jitsi.main.public_url)
[ -z "$public_url" ] && public_url="https://$domain"
echo "Configuration:"
echo " Enabled: $([ "$enabled" = "1" ] && echo -e "${GREEN}Yes${NC}" || echo -e "${RED}No${NC}")"
echo " Domain: $domain"
echo " Public URL: $public_url"
echo ""
if ! command -v docker >/dev/null 2>&1; then
echo -e "Docker: ${RED}Not installed${NC}"
return
fi
echo "Containers:"
for container in jitsi-web jitsi-prosody jitsi-jicofo jitsi-jvb; do
local state=$(docker inspect -f '{{.State.Status}}' "$container" 2>/dev/null)
if [ "$state" = "running" ]; then
echo -e " $container: ${GREEN}Running${NC}"
elif [ -n "$state" ]; then
echo -e " $container: ${YELLOW}$state${NC}"
else
echo -e " $container: ${RED}Not found${NC}"
fi
done
echo ""
# Show ports
local jvb_port=$(uci -q get jitsi.jvb.port || echo "10000")
local web_port=$(uci -q get jitsi.web.port || echo "8443")
echo "Ports:"
echo " Web: $web_port/tcp"
echo " JVB Media: $jvb_port/udp"
echo ""
# Show active conferences (if available)
local stats=$(docker exec jitsi-jvb curl -s http://localhost:8080/colibri/stats 2>/dev/null)
if [ -n "$stats" ]; then
local conferences=$(echo "$stats" | jsonfilter -e '@.conferences' 2>/dev/null || echo "0")
local participants=$(echo "$stats" | jsonfilter -e '@.participants' 2>/dev/null || echo "0")
echo "Active:"
echo " Conferences: $conferences"
echo " Participants: $participants"
echo ""
fi
}
show_logs() {
local service="$1"
local lines="${2:-50}"
cd "$JITSI_DIR"
case "$service" in
web|prosody|jicofo|jvb)
docker-compose logs --tail="$lines" "$service"
;;
all|"")
docker-compose logs --tail="$lines"
;;
*)
echo "Usage: jitsctl logs [web|prosody|jicofo|jvb|all] [lines]"
;;
esac
}
shell() {
local container="${1:-jitsi-prosody}"
log "Connecting to $container..."
docker exec -it "$container" /bin/bash 2>/dev/null || docker exec -it "$container" /bin/sh
}
# ============================================================================
# Upgrade
# ============================================================================
upgrade() {
log "Upgrading Jitsi Meet..."
cd "$JITSI_DIR"
# Pull latest images
docker-compose pull
# Restart with new images
docker-compose down
docker-compose up -d
# Cleanup old images
docker image prune -f
log "Jitsi Meet upgraded"
}
# ============================================================================
# Backup / Restore
# ============================================================================
backup() {
local backup_file="${1:-/tmp/jitsi-backup-$(date +%Y%m%d-%H%M%S).tar.gz}"
log "Creating backup..."
tar -czf "$backup_file" \
-C / \
etc/config/jitsi \
"$CONFIG_DIR" \
"$ENV_FILE" \
2>/dev/null
if [ -f "$backup_file" ]; then
local size=$(ls -lh "$backup_file" | awk '{print $5}')
log "Backup created: $backup_file ($size)"
else
error "Backup failed"
return 1
fi
}
restore() {
local backup_file="$1"
if [ -z "$backup_file" ] || [ ! -f "$backup_file" ]; then
echo "Usage: jitsctl restore <backup_file>"
return 1
fi
log "Restoring from $backup_file..."
# Stop service
/etc/init.d/jitsi stop 2>/dev/null
# Extract backup
tar -xzf "$backup_file" -C /
# Restart
/etc/init.d/jitsi start
log "Restore complete"
}
# ============================================================================
# TURN Server Integration
# ============================================================================
setup_turn() {
local turn_domain="${1:-turn.secubox.in}"
log "Setting up TURN server for Jitsi..."
# Check if turnctl is available
if ! command -v turnctl >/dev/null 2>&1; then
error "turnctl not found. Install secubox-app-turn first."
echo " opkg install secubox-app-turn"
return 1
fi
# Setup TURN server for Jitsi
local jitsi_domain=$(uci -q get jitsi.main.domain || echo "meet.secubox.local")
turnctl setup-jitsi "$jitsi_domain" "$turn_domain"
# Get the TURN credentials
local turn_secret=$(uci -q get turn.main.static_auth_secret)
local external_ip=$(uci -q get turn.main.external_ip)
if [ -z "$turn_secret" ]; then
error "TURN server not configured. Run 'turnctl setup-jitsi' first."
return 1
fi
# Update Jitsi UCI config
uci set jitsi.turn=jitsi
uci set jitsi.turn.enabled='1'
uci set jitsi.turn.server="$turn_domain"
uci set jitsi.turn.use_secret='1'
uci set jitsi.turn.secret="$turn_secret"
uci commit jitsi
# Update STUN servers
uci set jitsi.jvb.stun_servers="$turn_domain:3478"
uci commit jitsi
# Regenerate config
generate_config
log "TURN configured for Jitsi!"
echo ""
echo "TURN Server: $turn_domain"
echo "External IP: $external_ip"
echo ""
echo "Restart Jitsi to apply: jitsctl restart"
}
# ============================================================================
# Main
# ============================================================================
show_help() {
cat << EOF
Jitsi Meet Control v$VERSION
Usage: jitsctl <command> [options]
Commands:
install Install Jitsi Meet Docker stack
generate-config Generate/update configuration from UCI
status Show service status
logs [svc] [n] Show container logs
shell [container] Access container shell
start Start all containers
stop Stop all containers
restart Restart all containers
upgrade Upgrade to latest images
add-user <u> <p> Add authenticated user
remove-user <u> Remove user
list-users List registered users
backup [file] Create configuration backup
restore <file> Restore from backup
configure-haproxy Add HAProxy vhost
configure-fw Configure firewall rules
setup-turn [dom] Configure TURN server for NAT traversal
Examples:
jitsctl install
jitsctl status
jitsctl logs jvb 100
jitsctl add-user admin secretpassword
EOF
}
case "$1" in
install)
install_jitsi
;;
generate-config)
generate_config
;;
status)
show_status
;;
logs)
show_logs "$2" "$3"
;;
shell)
shell "$2"
;;
start)
cd "$JITSI_DIR" && docker-compose up -d
;;
stop)
cd "$JITSI_DIR" && docker-compose down
;;
restart)
cd "$JITSI_DIR" && docker-compose restart
;;
upgrade)
upgrade
;;
add-user)
add_user "$2" "$3"
;;
remove-user)
remove_user "$2"
;;
list-users)
list_users
;;
backup)
backup "$2"
;;
restore)
restore "$2"
;;
configure-haproxy)
configure_haproxy
;;
configure-fw)
configure_firewall
;;
setup-turn)
setup_turn "$2"
;;
-h|--help|help)
show_help
;;
*)
show_help
exit 1
;;
esac
exit 0