diff --git a/package/secubox/luci-app-jitsi/Makefile b/package/secubox/luci-app-jitsi/Makefile new file mode 100644 index 00000000..dc9e2823 --- /dev/null +++ b/package/secubox/luci-app-jitsi/Makefile @@ -0,0 +1,29 @@ +include $(TOPDIR)/rules.mk + +LUCI_TITLE:=LuCI Jitsi Meet Configuration +LUCI_DEPENDS:=+secubox-app-jitsi +LUCI_PKGARCH:=all + +PKG_NAME:=luci-app-jitsi +PKG_VERSION:=1.0.0 +PKG_RELEASE:=1 +PKG_MAINTAINER:=CyberMind Studio +PKG_LICENSE:=Apache-2.0 + +include $(TOPDIR)/feeds/luci/luci.mk + +define Package/luci-app-jitsi/install + $(INSTALL_DIR) $(1)/usr/share/luci/menu.d + $(INSTALL_DATA) ./root/usr/share/luci/menu.d/luci-app-jitsi.json $(1)/usr/share/luci/menu.d/ + + $(INSTALL_DIR) $(1)/usr/share/rpcd/acl.d + $(INSTALL_DATA) ./root/usr/share/rpcd/acl.d/luci-app-jitsi.json $(1)/usr/share/rpcd/acl.d/ + + $(INSTALL_DIR) $(1)/www/luci-static/resources/view/jitsi + $(INSTALL_DATA) ./htdocs/luci-static/resources/view/jitsi/*.js $(1)/www/luci-static/resources/view/jitsi/ + + $(INSTALL_DIR) $(1)/usr/libexec/rpcd + $(INSTALL_BIN) ./root/usr/libexec/rpcd/luci.jitsi $(1)/usr/libexec/rpcd/ +endef + +$(eval $(call BuildPackage,luci-app-jitsi)) diff --git a/package/secubox/luci-app-jitsi/htdocs/luci-static/resources/view/jitsi/config.js b/package/secubox/luci-app-jitsi/htdocs/luci-static/resources/view/jitsi/config.js new file mode 100644 index 00000000..dfaaaac4 --- /dev/null +++ b/package/secubox/luci-app-jitsi/htdocs/luci-static/resources/view/jitsi/config.js @@ -0,0 +1,298 @@ +'use strict'; +'require view'; +'require form'; +'require uci'; +'require rpc'; +'require poll'; + +var callJitsiStatus = rpc.declare({ + object: 'luci.jitsi', + method: 'status', + expect: { '': {} } +}); + +var callJitsiStart = rpc.declare({ + object: 'luci.jitsi', + method: 'start' +}); + +var callJitsiStop = rpc.declare({ + object: 'luci.jitsi', + method: 'stop' +}); + +var callJitsiRestart = rpc.declare({ + object: 'luci.jitsi', + method: 'restart' +}); + +var callJitsiInstall = rpc.declare({ + object: 'luci.jitsi', + method: 'install' +}); + +var callJitsiGenerateConfig = rpc.declare({ + object: 'luci.jitsi', + method: 'generate_config' +}); + +return view.extend({ + load: function() { + return Promise.all([ + uci.load('jitsi'), + callJitsiStatus() + ]); + }, + + render: function(data) { + var status = data[1]; + var m, s, o; + + m = new form.Map('jitsi', _('Jitsi Meet Video Conferencing'), + _('Self-hosted video conferencing with end-to-end encryption.')); + + // Status section + s = m.section(form.NamedSection, 'main', 'jitsi', _('Service Status')); + s.anonymous = true; + + o = s.option(form.DummyValue, '_status', _('Status')); + o.rawhtml = true; + o.cfgvalue = function() { + var html = '
'; + + if (!status.docker_available) { + html += 'Docker not available - Install docker package'; + } else { + // Container status + var containers = status.containers || {}; + for (var name in containers) { + var state = containers[name]; + var color = state === 'running' ? 'green' : (state === 'not_found' ? 'gray' : 'orange'); + html += '' + name.replace('jitsi-', '') + ': '; + html += '' + state + ''; + } + + // Stats + if (status.stats) { + html += ''; + html += 'Active: ' + (status.stats.conferences || 0) + ' conferences, '; + html += (status.stats.participants || 0) + ' participants'; + html += ''; + } + } + + html += '
'; + return html; + }; + + // Control buttons + o = s.option(form.Button, '_start', _('Start')); + o.inputtitle = _('Start'); + o.inputstyle = 'apply'; + o.onclick = function() { + return callJitsiStart().then(function() { + window.location.reload(); + }); + }; + + o = s.option(form.Button, '_stop', _('Stop')); + o.inputtitle = _('Stop'); + o.inputstyle = 'remove'; + o.onclick = function() { + return callJitsiStop().then(function() { + window.location.reload(); + }); + }; + + o = s.option(form.Button, '_restart', _('Restart')); + o.inputtitle = _('Restart'); + o.inputstyle = 'reload'; + o.onclick = function() { + return callJitsiRestart().then(function() { + window.location.reload(); + }); + }; + + // Configuration section + s = m.section(form.NamedSection, 'main', 'jitsi', _('General Configuration')); + s.anonymous = true; + + o = s.option(form.Flag, 'enabled', _('Enabled'), + _('Enable Jitsi Meet service')); + o.rmempty = false; + + o = s.option(form.Value, 'domain', _('Domain'), + _('Public domain for Jitsi Meet (e.g., meet.example.com)')); + o.placeholder = 'meet.example.com'; + o.rmempty = false; + + o = s.option(form.Value, 'public_url', _('Public URL'), + _('Full public URL (leave empty to auto-generate from domain)')); + o.placeholder = 'https://meet.example.com'; + o.optional = true; + + o = s.option(form.ListValue, 'timezone', _('Timezone')); + o.value('UTC', 'UTC'); + o.value('Europe/Paris', 'Europe/Paris'); + o.value('Europe/London', 'Europe/London'); + o.value('America/New_York', 'America/New York'); + o.value('America/Los_Angeles', 'America/Los Angeles'); + o.value('Asia/Tokyo', 'Asia/Tokyo'); + o.default = 'UTC'; + + // Web settings + s = m.section(form.NamedSection, 'web', 'jitsi', _('Web Interface')); + s.anonymous = true; + + o = s.option(form.Value, 'port', _('HTTPS Port'), + _('Port for web interface')); + o.datatype = 'port'; + o.default = '8443'; + + o = s.option(form.Flag, 'enable_guests', _('Allow Guests'), + _('Allow users to join without authentication')); + o.default = '1'; + + o = s.option(form.Flag, 'enable_auth', _('Require Authentication'), + _('Require login to create meetings')); + o.default = '0'; + + o = s.option(form.ListValue, 'default_language', _('Default Language')); + o.value('en', 'English'); + o.value('fr', 'French'); + o.value('de', 'German'); + o.value('es', 'Spanish'); + o.value('it', 'Italian'); + o.value('pt', 'Portuguese'); + o.value('zh', 'Chinese'); + o.value('ja', 'Japanese'); + o.default = 'en'; + + // JVB settings + s = m.section(form.NamedSection, 'jvb', 'jitsi', _('Video Bridge (JVB)')); + s.anonymous = true; + + o = s.option(form.Value, 'port', _('Media Port (UDP)'), + _('UDP port for video/audio streams')); + o.datatype = 'port'; + o.default = '10000'; + + o = s.option(form.Flag, 'enable_tcp_fallback', _('Enable TCP Fallback'), + _('Allow TCP fallback for restrictive networks')); + o.default = '0'; + + o = s.option(form.Value, 'tcp_port', _('TCP Fallback Port'), + _('TCP port for fallback')); + o.datatype = 'port'; + o.default = '4443'; + o.depends('enable_tcp_fallback', '1'); + + o = s.option(form.Value, 'stun_servers', _('STUN Servers'), + _('STUN servers for NAT traversal')); + o.placeholder = 'meet-jit-si-turnrelay.jitsi.net:443'; + + // Security settings + s = m.section(form.NamedSection, 'security', 'jitsi', _('Security')); + s.anonymous = true; + + o = s.option(form.Flag, 'lobby_enabled', _('Enable Lobby'), + _('Require moderator approval to join')); + o.default = '1'; + + o = s.option(form.Flag, 'password_required', _('Require Room Password'), + _('Prompt for password when creating rooms')); + o.default = '0'; + + o = s.option(form.Flag, 'jwt_enabled', _('Enable JWT Authentication'), + _('Use JWT tokens for authentication')); + o.default = '0'; + + o = s.option(form.Value, 'jwt_app_id', _('JWT App ID')); + o.depends('jwt_enabled', '1'); + + o = s.option(form.Value, 'jwt_app_secret', _('JWT App Secret')); + o.password = true; + o.depends('jwt_enabled', '1'); + + // TURN settings + s = m.section(form.NamedSection, 'turn', 'jitsi', _('TURN Server')); + s.anonymous = true; + + o = s.option(form.Flag, 'enabled', _('Use External TURN'), + _('Use external TURN server for better NAT traversal')); + o.default = '0'; + + o = s.option(form.Value, 'server', _('TURN Server')); + o.placeholder = 'turn.example.com:443'; + o.depends('enabled', '1'); + + o = s.option(form.Value, 'username', _('Username')); + o.depends('enabled', '1'); + + o = s.option(form.Value, 'password', _('Password')); + o.password = true; + o.depends('enabled', '1'); + + // Mesh integration + s = m.section(form.NamedSection, 'mesh', 'jitsi', _('Mesh Integration')); + s.anonymous = true; + + o = s.option(form.Flag, 'enabled', _('Enable Mesh Integration'), + _('Announce Jitsi on SecuBox mesh network')); + o.default = '0'; + + o = s.option(form.Flag, 'announce_service', _('Announce Service'), + _('Register with mesh DNS')); + o.default = '1'; + o.depends('enabled', '1'); + + // Actions + s = m.section(form.NamedSection, 'main', 'jitsi', _('Actions')); + s.anonymous = true; + + o = s.option(form.Button, '_install', _('Install Containers')); + o.inputtitle = _('Install'); + o.inputstyle = 'apply'; + o.onclick = function() { + if (confirm(_('This will download Docker images. Continue?'))) { + return callJitsiInstall().then(function(res) { + alert(res.output || 'Installation started'); + window.location.reload(); + }); + } + }; + + o = s.option(form.Button, '_regenerate', _('Regenerate Configuration')); + o.inputtitle = _('Regenerate'); + o.inputstyle = 'reload'; + o.onclick = function() { + return callJitsiGenerateConfig().then(function(res) { + alert(res.output || 'Configuration regenerated'); + }); + }; + + // Help section + s = m.section(form.NamedSection, 'main', 'jitsi', _('Quick Start')); + s.anonymous = true; + + o = s.option(form.DummyValue, '_help'); + o.rawhtml = true; + o.cfgvalue = function() { + var domain = uci.get('jitsi', 'main', 'domain') || 'meet.example.com'; + return '
' + + '

Setup Steps:

' + + '
    ' + + '
  1. Set your domain above
  2. ' + + '
  3. Click "Install Containers" to download images
  4. ' + + '
  5. Enable the service and save
  6. ' + + '
  7. Configure HAProxy/DNS to point to this device
  8. ' + + '
' + + '

Access:

' + + '

Web: https://' + domain + '

' + + '

CLI: jitsctl status

' + + '
'; + }; + + return m.render(); + } +}); diff --git a/package/secubox/luci-app-jitsi/root/usr/libexec/rpcd/luci.jitsi b/package/secubox/luci-app-jitsi/root/usr/libexec/rpcd/luci.jitsi new file mode 100644 index 00000000..96bb9a98 --- /dev/null +++ b/package/secubox/luci-app-jitsi/root/usr/libexec/rpcd/luci.jitsi @@ -0,0 +1,120 @@ +#!/bin/sh + +. /lib/functions.sh +. /usr/share/libubox/jshn.sh + +JITSI_DIR="/srv/jitsi" + +case "$1" in + list) + echo '{"status":{},"start":{},"stop":{},"restart":{},"install":{},"generate_config":{},"add_user":{"username":"str","password":"str"},"remove_user":{"username":"str"},"list_users":{},"logs":{"service":"str","lines":"int"}}' + ;; + call) + case "$2" in + status) + # Get service status + json_init + + # Check enabled + enabled=$(uci -q get jitsi.main.enabled) + domain=$(uci -q get jitsi.main.domain) + + json_add_boolean "enabled" ${enabled:-0} + json_add_string "domain" "$domain" + + # Check containers + if command -v docker >/dev/null 2>&1; then + json_add_boolean "docker_available" 1 + + json_add_object "containers" + for container in jitsi-web jitsi-prosody jitsi-jicofo jitsi-jvb; do + state=$(docker inspect -f '{{.State.Status}}' "$container" 2>/dev/null) + json_add_string "$container" "${state:-not_found}" + done + json_close_object + + # Get stats from JVB if running + stats=$(docker exec jitsi-jvb curl -s http://localhost:8080/colibri/stats 2>/dev/null) + if [ -n "$stats" ]; then + conferences=$(echo "$stats" | jsonfilter -e '@.conferences' 2>/dev/null) + participants=$(echo "$stats" | jsonfilter -e '@.participants' 2>/dev/null) + json_add_object "stats" + json_add_int "conferences" ${conferences:-0} + json_add_int "participants" ${participants:-0} + json_close_object + fi + else + json_add_boolean "docker_available" 0 + fi + + json_dump + ;; + start) + /etc/init.d/jitsi start >/dev/null 2>&1 + echo '{"success":true}' + ;; + stop) + /etc/init.d/jitsi stop >/dev/null 2>&1 + echo '{"success":true}' + ;; + restart) + /etc/init.d/jitsi restart >/dev/null 2>&1 + echo '{"success":true}' + ;; + install) + output=$(/usr/sbin/jitsctl install 2>&1) + json_init + json_add_boolean "success" 1 + json_add_string "output" "$output" + json_dump + ;; + generate_config) + output=$(/usr/sbin/jitsctl generate-config 2>&1) + json_init + json_add_boolean "success" 1 + json_add_string "output" "$output" + json_dump + ;; + add_user) + read -r input + username=$(echo "$input" | jsonfilter -e '@.username') + password=$(echo "$input" | jsonfilter -e '@.password') + output=$(/usr/sbin/jitsctl add-user "$username" "$password" 2>&1) + code=$? + json_init + json_add_boolean "success" $((code == 0)) + json_add_string "output" "$output" + json_dump + ;; + remove_user) + read -r input + username=$(echo "$input" | jsonfilter -e '@.username') + output=$(/usr/sbin/jitsctl remove-user "$username" 2>&1) + code=$? + json_init + json_add_boolean "success" $((code == 0)) + json_add_string "output" "$output" + json_dump + ;; + list_users) + domain=$(uci -q get jitsi.main.domain) + users=$(docker exec jitsi-prosody ls -1 /config/data/"$domain"/accounts/ 2>/dev/null | sed 's/\.dat$//' | tr '\n' ',') + json_init + json_add_string "users" "$users" + json_dump + ;; + logs) + read -r input + service=$(echo "$input" | jsonfilter -e '@.service') + lines=$(echo "$input" | jsonfilter -e '@.lines') + [ -z "$lines" ] && lines=50 + logs=$(cd "$JITSI_DIR" && docker-compose logs --tail="$lines" ${service:-} 2>&1 | tail -100) + json_init + json_add_string "logs" "$logs" + json_dump + ;; + esac + ;; +esac + +exit 0 diff --git a/package/secubox/luci-app-jitsi/root/usr/share/luci/menu.d/luci-app-jitsi.json b/package/secubox/luci-app-jitsi/root/usr/share/luci/menu.d/luci-app-jitsi.json new file mode 100644 index 00000000..3cc3147d --- /dev/null +++ b/package/secubox/luci-app-jitsi/root/usr/share/luci/menu.d/luci-app-jitsi.json @@ -0,0 +1,14 @@ +{ + "admin/services/jitsi": { + "title": "Jitsi Meet", + "order": 60, + "action": { + "type": "view", + "path": "jitsi/config" + }, + "depends": { + "acl": ["luci-app-jitsi"], + "uci": {"jitsi": true} + } + } +} diff --git a/package/secubox/luci-app-jitsi/root/usr/share/rpcd/acl.d/luci-app-jitsi.json b/package/secubox/luci-app-jitsi/root/usr/share/rpcd/acl.d/luci-app-jitsi.json new file mode 100644 index 00000000..e7946d33 --- /dev/null +++ b/package/secubox/luci-app-jitsi/root/usr/share/rpcd/acl.d/luci-app-jitsi.json @@ -0,0 +1,25 @@ +{ + "luci-app-jitsi": { + "description": "Grant access to Jitsi Meet configuration", + "read": { + "file": { + "/etc/config/jitsi": ["read"], + "/srv/jitsi/.env": ["read"] + }, + "ubus": { + "file": ["read", "stat"], + "luci.jitsi": ["*"] + }, + "uci": ["jitsi"] + }, + "write": { + "file": { + "/etc/config/jitsi": ["write"] + }, + "ubus": { + "luci.jitsi": ["*"] + }, + "uci": ["jitsi"] + } + } +} diff --git a/package/secubox/secubox-app-jitsi/Makefile b/package/secubox/secubox-app-jitsi/Makefile new file mode 100644 index 00000000..070755f3 --- /dev/null +++ b/package/secubox/secubox-app-jitsi/Makefile @@ -0,0 +1,90 @@ +include $(TOPDIR)/rules.mk + +PKG_NAME:=secubox-app-jitsi +PKG_VERSION:=1.0.0 +PKG_RELEASE:=1 +PKG_ARCH:=all +PKG_MAINTAINER:=CyberMind Studio +PKG_LICENSE:=Apache-2.0 + +include $(INCLUDE_DIR)/package.mk + +define Package/secubox-app-jitsi + SECTION:=utils + CATEGORY:=Utilities + PKGARCH:=all + SUBMENU:=SecuBox Apps + TITLE:=SecuBox Jitsi Meet Video Conferencing + DEPENDS:=+docker +docker-compose +wget +openssl-util +endef + +define Package/secubox-app-jitsi/description +Jitsi Meet - Secure, fully featured video conferencing for SecuBox. + +Features: +- End-to-end encrypted video conferences +- No account required for guests +- Screen sharing and recording +- Chat, reactions, and virtual backgrounds +- Mobile app support (iOS/Android) +- WebRTC-based, works in any browser +- Self-hosted for complete privacy + +Runs via Docker containers for easy deployment. +Integrates with HAProxy for SSL termination. +Configure in /etc/config/jitsi. +endef + +define Package/secubox-app-jitsi/conffiles +/etc/config/jitsi +endef + +define Build/Compile +endef + +define Package/secubox-app-jitsi/install + $(INSTALL_DIR) $(1)/etc/config + $(INSTALL_CONF) ./files/etc/config/jitsi $(1)/etc/config/jitsi + + $(INSTALL_DIR) $(1)/etc/init.d + $(INSTALL_BIN) ./files/etc/init.d/jitsi $(1)/etc/init.d/jitsi + + $(INSTALL_DIR) $(1)/usr/sbin + $(INSTALL_BIN) ./files/usr/sbin/jitsctl $(1)/usr/sbin/jitsctl + + $(INSTALL_DIR) $(1)/usr/share/jitsi + $(INSTALL_DATA) ./files/usr/share/jitsi/docker-compose.yml $(1)/usr/share/jitsi/ + $(INSTALL_DATA) ./files/usr/share/jitsi/env.template $(1)/usr/share/jitsi/ + + $(INSTALL_DIR) $(1)/usr/share/jitsi/prosody + $(INSTALL_DATA) ./files/usr/share/jitsi/prosody/prosody.cfg.lua.template $(1)/usr/share/jitsi/prosody/ + + $(INSTALL_DIR) $(1)/usr/lib/secubox/haproxy.d + $(INSTALL_DATA) ./files/usr/lib/secubox/haproxy.d/jitsi.cfg $(1)/usr/lib/secubox/haproxy.d/ +endef + +define Package/secubox-app-jitsi/postinst +#!/bin/sh +[ -n "$${IPKG_INSTROOT}" ] || { + echo "" + echo "============================================" + echo " Jitsi Meet Video Conferencing Installed" + echo "============================================" + echo "" + echo "Quick Start:" + echo " 1. Configure: uci set jitsi.main.domain='meet.example.com'" + echo " 2. Install: jitsctl install" + echo " 3. Start: /etc/init.d/jitsi start" + echo "" + echo "Control commands:" + echo " jitsctl status - Show service status" + echo " jitsctl logs - View container logs" + echo " jitsctl shell - Access container shell" + echo "" + echo "Web interface: https://" + echo "" +} +exit 0 +endef + +$(eval $(call BuildPackage,secubox-app-jitsi)) diff --git a/package/secubox/secubox-app-jitsi/README.md b/package/secubox/secubox-app-jitsi/README.md new file mode 100644 index 00000000..c5aeb49e --- /dev/null +++ b/package/secubox/secubox-app-jitsi/README.md @@ -0,0 +1,188 @@ +# SecuBox Jitsi Meet + +Self-hosted video conferencing with end-to-end encryption for SecuBox. + +## Features + +- **Secure Video Calls**: End-to-end encrypted video conferences +- **No Account Required**: Guests can join without registration +- **Screen Sharing**: Share your screen with participants +- **Chat & Reactions**: In-meeting chat and emoji reactions +- **Breakout Rooms**: Split meetings into smaller groups +- **Recording**: Optional recording to Dropbox (requires setup) +- **Mobile Support**: iOS and Android apps available +- **HAProxy Integration**: Automatic SSL and reverse proxy setup +- **Mesh Federation**: Announce service on SecuBox mesh network + +## Requirements + +- Docker and docker-compose +- 2GB+ RAM (4GB recommended) +- Public domain with DNS pointing to your SecuBox +- SSL certificate (via Let's Encrypt or HAProxy) + +## Quick Start + +```bash +# Install +opkg install secubox-app-jitsi luci-app-jitsi + +# Configure domain +uci set jitsi.main.domain='meet.example.com' +uci set jitsi.main.enabled='1' +uci commit jitsi + +# Install Docker containers +jitsctl install + +# Start service +/etc/init.d/jitsi start +``` + +## Configuration + +### Via LuCI +Navigate to **Services > Jitsi Meet** in the LuCI web interface. + +### Via CLI +```bash +# Show status +jitsctl status + +# View logs +jitsctl logs + +# Add authenticated user +jitsctl add-user admin secretpassword + +# Regenerate configuration +jitsctl generate-config + +# Restart containers +jitsctl restart +``` + +### UCI Options + +``` +config jitsi 'main' + option enabled '1' + option domain 'meet.example.com' + option timezone 'Europe/Paris' + +config jitsi 'web' + option port '8443' + option enable_guests '1' + option enable_auth '0' + option default_language 'en' + +config jitsi 'jvb' + option port '10000' + option enable_tcp_fallback '0' + option stun_servers 'meet-jit-si-turnrelay.jitsi.net:443' + +config jitsi 'security' + option lobby_enabled '1' + option password_required '0' + option jwt_enabled '0' +``` + +## HAProxy Integration + +If secubox-app-haproxy is installed, Jitsi will automatically configure a vhost: + +```bash +jitsctl configure-haproxy +``` + +This creates: +- HTTPS frontend on port 443 +- WebSocket support for real-time communication +- SSL termination (using your certificate) + +## Firewall + +The following ports are required: + +| Port | Protocol | Description | +|------|----------|-------------| +| 443 | TCP | HTTPS (via HAProxy) | +| 8443 | TCP | Direct web access | +| 10000 | UDP | Video/audio streams | +| 4443 | TCP | TCP fallback (optional) | + +Firewall rules are automatically added during installation. + +## Mesh Integration + +Enable mesh federation to: +- Announce Jitsi on the SecuBox mesh network +- Auto-register DNS entry (e.g., meet.c3box.mesh.local) +- Enable multi-node video bridge deployment + +```bash +uci set jitsi.mesh.enabled='1' +uci commit jitsi +/etc/init.d/jitsi restart +``` + +## Troubleshooting + +### Containers not starting +```bash +# Check Docker status +docker ps -a + +# View container logs +jitsctl logs web +jitsctl logs prosody +jitsctl logs jicofo +jitsctl logs jvb +``` + +### Video/audio not working +1. Check UDP port 10000 is open on firewall +2. Verify STUN servers are reachable +3. Enable TCP fallback if behind strict NAT + +### Authentication issues +```bash +# List users +jitsctl list-users + +# Reset user password +jitsctl remove-user admin +jitsctl add-user admin newpassword +``` + +## Backup & Restore + +```bash +# Create backup +jitsctl backup /tmp/jitsi-backup.tar.gz + +# Restore +jitsctl restore /tmp/jitsi-backup.tar.gz +``` + +## Architecture + +``` +┌─────────────────────────────────────────────────────────┐ +│ HAProxy (443) │ +│ SSL Termination │ +└──────────────────────┬──────────────────────────────────┘ + │ +┌──────────────────────┴──────────────────────────────────┐ +│ Docker Network: meet.jitsi │ +│ ┌─────────┐ ┌─────────┐ ┌─────────┐ ┌─────────────┐ │ +│ │ Web │ │ Prosody │ │ Jicofo │ │ JVB │ │ +│ │ :8443 │ │ :5222 │ │ :8888 │ │ :10000/UDP │ │ +│ │ React │ │ XMPP │ │ Focus │ │ Media │ │ +│ └─────────┘ └─────────┘ └─────────┘ └─────────────┘ │ +└─────────────────────────────────────────────────────────┘ +``` + +## License + +Apache 2.0 - See LICENSE file for details. diff --git a/package/secubox/secubox-app-jitsi/files/etc/config/jitsi b/package/secubox/secubox-app-jitsi/files/etc/config/jitsi new file mode 100644 index 00000000..5f544861 --- /dev/null +++ b/package/secubox/secubox-app-jitsi/files/etc/config/jitsi @@ -0,0 +1,53 @@ +# Jitsi Meet Video Conferencing Configuration + +config jitsi 'main' + option enabled '0' + option domain 'meet.secubox.local' + option public_url '' + option container_type 'docker' + option timezone 'Europe/Paris' + option letsencrypt '0' + option letsencrypt_email '' + +config jitsi 'web' + option port '8443' + option enable_guests '1' + option enable_auth '0' + option default_language 'en' + option enable_transcription '0' + option enable_recording '0' + option watermark_link '' + +config jitsi 'jvb' + option port '10000' + option enable_tcp_fallback '0' + option tcp_port '4443' + option stun_servers 'meet-jit-si-turnrelay.jitsi.net:443' + option max_bandwidth '10000000' + +config jitsi 'prosody' + option c2s_port '5222' + option http_port '5280' + option guest_domain_enabled '1' + +config jitsi 'jicofo' + option port '8888' + option enable_ocontrol '0' + +config jitsi 'security' + option jwt_enabled '0' + option jwt_app_id '' + option jwt_app_secret '' + option lobby_enabled '1' + option password_required '0' + +config jitsi 'mesh' + option enabled '0' + option announce_service '1' + option cascade_jvb '0' + +config jitsi 'turn' + option enabled '0' + option server '' + option username '' + option password '' diff --git a/package/secubox/secubox-app-jitsi/files/etc/init.d/jitsi b/package/secubox/secubox-app-jitsi/files/etc/init.d/jitsi new file mode 100644 index 00000000..cb5f4484 --- /dev/null +++ b/package/secubox/secubox-app-jitsi/files/etc/init.d/jitsi @@ -0,0 +1,49 @@ +#!/bin/sh /etc/rc.common +# Jitsi Meet Video Conferencing Service + +START=95 +STOP=15 +USE_PROCD=1 + +JITSI_DIR="/srv/jitsi" +COMPOSE_FILE="$JITSI_DIR/docker-compose.yml" + +start_service() { + local enabled=$(uci -q get jitsi.main.enabled) + [ "$enabled" != "1" ] && return 0 + + # Check Docker + if ! command -v docker >/dev/null 2>&1; then + logger -t jitsi "Docker not available" + return 1 + fi + + # Generate config if needed + if [ ! -f "$JITSI_DIR/.env" ]; then + /usr/sbin/jitsctl generate-config + fi + + # Start containers + cd "$JITSI_DIR" + docker-compose up -d + + logger -t jitsi "Jitsi Meet started" +} + +stop_service() { + if [ -f "$COMPOSE_FILE" ]; then + cd "$JITSI_DIR" + docker-compose down + fi + logger -t jitsi "Jitsi Meet stopped" +} + +reload_service() { + /usr/sbin/jitsctl generate-config + cd "$JITSI_DIR" + docker-compose restart +} + +status() { + /usr/sbin/jitsctl status +} diff --git a/package/secubox/secubox-app-jitsi/files/usr/lib/secubox/haproxy.d/jitsi.cfg b/package/secubox/secubox-app-jitsi/files/usr/lib/secubox/haproxy.d/jitsi.cfg new file mode 100644 index 00000000..d447855e --- /dev/null +++ b/package/secubox/secubox-app-jitsi/files/usr/lib/secubox/haproxy.d/jitsi.cfg @@ -0,0 +1,21 @@ +# HAProxy configuration snippet for Jitsi Meet +# Include in main haproxy.cfg or via haproxy.d directory + +# Frontend rule for Jitsi (add to your HTTPS frontend) +# acl is_jitsi hdr(host) -i meet.example.com +# use_backend jitsi_web if is_jitsi + +backend jitsi_web + mode http + option httpchk GET / + http-check expect status 200 + # WebSocket support + option http-server-close + timeout tunnel 1h + server jitsi 127.0.0.1:8443 check ssl verify none + +# XMPP BOSH/WebSocket backend (if needed separately) +backend jitsi_xmpp + mode http + option httpchk GET /http-bind + server prosody 127.0.0.1:5280 check diff --git a/package/secubox/secubox-app-jitsi/files/usr/sbin/jitsctl b/package/secubox/secubox-app-jitsi/files/usr/sbin/jitsctl new file mode 100644 index 00000000..bc0b555f --- /dev/null +++ b/package/secubox/secubox-app-jitsi/files/usr/sbin/jitsctl @@ -0,0 +1,667 @@ +#!/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 diff --git a/package/secubox/secubox-app-jitsi/files/usr/share/jitsi/docker-compose.yml b/package/secubox/secubox-app-jitsi/files/usr/share/jitsi/docker-compose.yml new file mode 100644 index 00000000..ef48dda5 --- /dev/null +++ b/package/secubox/secubox-app-jitsi/files/usr/share/jitsi/docker-compose.yml @@ -0,0 +1,138 @@ +# Jitsi Meet Docker Compose for SecuBox +# Based on https://github.com/jitsi/docker-jitsi-meet + +version: '3.5' + +services: + # Frontend web server + web: + image: jitsi/web:${JITSI_IMAGE_VERSION:-stable} + container_name: jitsi-web + restart: unless-stopped + ports: + - '${HTTP_PORT:-8000}:80' + - '${HTTPS_PORT:-8443}:443' + volumes: + - ${CONFIG}/web:/config:Z + - ${CONFIG}/transcripts:/usr/share/jitsi-meet/transcripts:Z + environment: + - ENABLE_AUTH + - ENABLE_GUESTS + - ENABLE_LOBBY + - ENABLE_PREJOIN_PAGE + - ENABLE_WELCOME_PAGE + - ENABLE_BREAKOUT_ROOMS + - ENABLE_REACTIONS + - ENABLE_NOISY_MIC_DETECTION + - ENABLE_TALK_WHILE_MUTED + - DEFAULT_LANGUAGE + - PUBLIC_URL + - TZ + - XMPP_AUTH_DOMAIN + - XMPP_BOSH_URL_BASE + - XMPP_DOMAIN + - XMPP_GUEST_DOMAIN + - XMPP_MUC_DOMAIN + networks: + meet.jitsi: + aliases: + - ${XMPP_DOMAIN} + + # XMPP server + prosody: + image: jitsi/prosody:${JITSI_IMAGE_VERSION:-stable} + container_name: jitsi-prosody + restart: unless-stopped + expose: + - '5222' + - '5347' + - '5280' + volumes: + - ${CONFIG}/prosody/config:/config:Z + - ${CONFIG}/prosody/prosody-plugins-custom:/prosody-plugins-custom:Z + environment: + - AUTH_TYPE + - ENABLE_AUTH + - ENABLE_GUESTS + - ENABLE_LOBBY + - ENABLE_XMPP_WEBSOCKET + - JICOFO_AUTH_USER + - JICOFO_AUTH_PASSWORD + - JICOFO_COMPONENT_SECRET + - JVB_AUTH_USER + - JVB_AUTH_PASSWORD + - JWT_APP_ID + - JWT_APP_SECRET + - JWT_ACCEPTED_ISSUERS + - JWT_ACCEPTED_AUDIENCES + - PUBLIC_URL + - TZ + - XMPP_DOMAIN + - XMPP_AUTH_DOMAIN + - XMPP_GUEST_DOMAIN + - XMPP_MUC_DOMAIN + - XMPP_INTERNAL_MUC_DOMAIN + networks: + meet.jitsi: + aliases: + - ${XMPP_SERVER:-xmpp.meet.jitsi} + + # Conference focus + jicofo: + image: jitsi/jicofo:${JITSI_IMAGE_VERSION:-stable} + container_name: jitsi-jicofo + restart: unless-stopped + volumes: + - ${CONFIG}/jicofo:/config:Z + environment: + - AUTH_TYPE + - ENABLE_AUTH + - ENABLE_AUTO_OWNER + - JICOFO_AUTH_USER + - JICOFO_AUTH_PASSWORD + - JICOFO_ENABLE_BRIDGE_HEALTH_CHECKS + - JICOFO_ENABLE_HEALTH_CHECKS + - JVB_BREWERY_MUC + - TZ + - XMPP_DOMAIN + - XMPP_AUTH_DOMAIN + - XMPP_INTERNAL_MUC_DOMAIN + - XMPP_MUC_DOMAIN + - XMPP_SERVER + depends_on: + - prosody + networks: + meet.jitsi: + + # Video bridge (SFU) + jvb: + image: jitsi/jvb:${JITSI_IMAGE_VERSION:-stable} + container_name: jitsi-jvb + restart: unless-stopped + ports: + - '${JVB_PORT:-10000}:${JVB_PORT:-10000}/udp' + - '127.0.0.1:8080:8080' + volumes: + - ${CONFIG}/jvb:/config:Z + environment: + - DOCKER_HOST_ADDRESS + - ENABLE_COLIBRI_WEBSOCKET + - JVB_AUTH_USER + - JVB_AUTH_PASSWORD + - JVB_BREWERY_MUC + - JVB_PORT + - JVB_STUN_SERVERS + - JVB_ENABLE_APIS + - PUBLIC_URL + - TZ + - XMPP_AUTH_DOMAIN + - XMPP_INTERNAL_MUC_DOMAIN + - XMPP_SERVER + depends_on: + - prosody + networks: + meet.jitsi: + +networks: + meet.jitsi: + driver: bridge diff --git a/package/secubox/secubox-app-jitsi/files/usr/share/jitsi/env.template b/package/secubox/secubox-app-jitsi/files/usr/share/jitsi/env.template new file mode 100644 index 00000000..9735185e --- /dev/null +++ b/package/secubox/secubox-app-jitsi/files/usr/share/jitsi/env.template @@ -0,0 +1,37 @@ +# Jitsi Meet Environment Template +# This file is processed by jitsctl to generate .env + +CONFIG=/srv/jitsi/.jitsi-meet-cfg +HTTP_PORT=8000 +HTTPS_PORT=8443 +TZ=UTC + +PUBLIC_URL=https://meet.example.com +XMPP_DOMAIN=meet.example.com +XMPP_SERVER=xmpp.meet.example.com +XMPP_AUTH_DOMAIN=auth.meet.example.com +XMPP_GUEST_DOMAIN=guest.meet.example.com +XMPP_MUC_DOMAIN=muc.meet.example.com +XMPP_INTERNAL_MUC_DOMAIN=internal-muc.meet.example.com + +ENABLE_AUTH=0 +ENABLE_GUESTS=1 +AUTH_TYPE=internal + +JICOFO_COMPONENT_SECRET=changeme +JICOFO_AUTH_USER=focus +JICOFO_AUTH_PASSWORD=changeme +JVB_AUTH_USER=jvb +JVB_AUTH_PASSWORD=changeme +JVB_BREWERY_MUC=jvbbrewery + +JVB_PORT=10000 +JVB_STUN_SERVERS=meet-jit-si-turnrelay.jitsi.net:443 + +ENABLE_LOBBY=1 +ENABLE_BREAKOUT_ROOMS=1 +ENABLE_PREJOIN_PAGE=1 +ENABLE_WELCOME_PAGE=1 +ENABLE_REACTIONS=1 + +DEFAULT_LANGUAGE=en diff --git a/package/secubox/secubox-app-jitsi/files/usr/share/jitsi/prosody/prosody.cfg.lua.template b/package/secubox/secubox-app-jitsi/files/usr/share/jitsi/prosody/prosody.cfg.lua.template new file mode 100644 index 00000000..4898c96f --- /dev/null +++ b/package/secubox/secubox-app-jitsi/files/usr/share/jitsi/prosody/prosody.cfg.lua.template @@ -0,0 +1,58 @@ +-- Prosody configuration template for SecuBox Jitsi +-- This is a reference template; actual config is generated by Docker + +plugin_paths = { "/prosody-plugins/", "/prosody-plugins-custom" } + +muc_mapper_domain_base = "XMPP_DOMAIN"; +muc_mapper_domain_prefix = "muc"; + +http_default_host = "XMPP_DOMAIN" + +consider_bosh_secure = true; +consider_websocket_secure = true; + +cross_domain_bosh = true; +cross_domain_websocket = true; + +VirtualHost "XMPP_DOMAIN" + authentication = "AUTH_TYPE" + ssl = { + key = "/config/certs/XMPP_DOMAIN.key"; + certificate = "/config/certs/XMPP_DOMAIN.crt"; + } + modules_enabled = { + "bosh"; + "websocket"; + "smacks"; + "pubsub"; + "ping"; + "speakerstats"; + "conference_duration"; + "room_metadata"; + "muc_lobby_rooms"; + "muc_breakout_rooms"; + "av_moderation"; + "polls"; + } + main_muc = "MUC_DOMAIN" + lobby_muc = "lobby.XMPP_DOMAIN" + breakout_rooms_muc = "breakout.XMPP_DOMAIN" + +VirtualHost "GUEST_DOMAIN" + authentication = "anonymous" + +Component "MUC_DOMAIN" "muc" + storage = "memory" + modules_enabled = { + "muc_meeting_id"; + "muc_domain_mapper"; + "polls"; + } + muc_room_locking = false + muc_room_default_public_jids = true + +Component "AUTH_DOMAIN" "speakerstats_component" + muc_component = "MUC_DOMAIN" + +Component "focus.XMPP_DOMAIN" "client_proxy" + target_address = "focus@AUTH_DOMAIN" diff --git a/package/secubox/secubox-core/root/usr/share/secubox/plugins/catalog/secubox-app-jitsi.json b/package/secubox/secubox-core/root/usr/share/secubox/plugins/catalog/secubox-app-jitsi.json new file mode 100644 index 00000000..5602013e --- /dev/null +++ b/package/secubox/secubox-core/root/usr/share/secubox/plugins/catalog/secubox-app-jitsi.json @@ -0,0 +1,59 @@ +{ + "id": "secubox-app-jitsi", + "name": "Jitsi Meet", + "version": "1.0.0", + "category": "communication", + "description": "Self-hosted video conferencing with end-to-end encryption", + "long_description": "Jitsi Meet is a secure, fully featured, and completely free video conferencing solution. Host your own video meetings with end-to-end encryption, no account required for guests, screen sharing, chat, reactions, and more. Runs via Docker containers for easy deployment.", + "icon": "video", + "author": "CyberMind Studio", + "license": "Apache-2.0", + "homepage": "https://jitsi.org", + "repository": "https://github.com/jitsi/jitsi-meet", + "packages": [ + "secubox-app-jitsi", + "luci-app-jitsi" + ], + "dependencies": [ + "docker", + "docker-compose" + ], + "optional": [ + "secubox-app-haproxy" + ], + "ports": [ + {"port": 8443, "protocol": "tcp", "description": "Web interface (HTTPS)"}, + {"port": 10000, "protocol": "udp", "description": "Video/audio streams"}, + {"port": 4443, "protocol": "tcp", "description": "TCP fallback"} + ], + "resources": { + "ram_min": "2048", + "ram_recommended": "4096", + "disk": "500", + "cpu": "medium" + }, + "features": [ + "End-to-end encrypted video", + "No account required for guests", + "Screen sharing", + "Chat and reactions", + "Virtual backgrounds", + "Breakout rooms", + "Recording (optional)", + "Mobile app support", + "HAProxy integration", + "Mesh federation" + ], + "tags": ["video", "conferencing", "webrtc", "communication", "meetings", "collaboration"], + "screenshots": [], + "documentation": "https://jitsi.github.io/handbook/docs/intro", + "config_file": "/etc/config/jitsi", + "service_name": "jitsi", + "web_interface": { + "port": 8443, + "path": "/", + "ssl": true + }, + "haproxy_integration": true, + "mesh_service": true +}