#!/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 127.0.0.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 " 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 " 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 " 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" } # ============================================================================ # Main # ============================================================================ show_help() { cat << EOF Jitsi Meet Control v$VERSION Usage: jitsctl [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

Add authenticated user remove-user Remove user list-users List registered users backup [file] Create configuration backup restore Restore from backup configure-haproxy Add HAProxy vhost configure-fw Configure firewall rules 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 ;; -h|--help|help) show_help ;; *) show_help exit 1 ;; esac exit 0