forked from switnet/quick-jibri-installer
				
			Compare commits
	
		
			No commits in common. "cefb7361319836828206f36fb9620d8183d8d456" and "9e0264fe4a865ad1f299f12879a934a061cfb939" have entirely different histories.
		
	
	
		
			cefb736131
			...
			9e0264fe4a
		
	
		|  | @ -1,24 +0,0 @@ | ||||||
| -- Promotes to owner (real moderator) if the JWT has context.user.moderator = true |  | ||||||
| local util = module:require "util" |  | ||||||
| local is_admin = util.is_admin |  | ||||||
| local is_healthcheck_room = util.is_healthcheck_room |  | ||||||
| 
 |  | ||||||
| -- When a participant enters the room: |  | ||||||
| module:hook('muc-occupant-joined', function (event) |  | ||||||
|     local room, occupant, session = event.room, event.occupant, event.origin |  | ||||||
| 
 |  | ||||||
|     -- Ignores focus/healthcheck/etc. |  | ||||||
|     if is_healthcheck_room(room.jid) or is_admin(occupant.bare_jid) then |  | ||||||
|         return |  | ||||||
|     end |  | ||||||
| 
 |  | ||||||
|     -- Requires moderator token and claim (accepts boolean or string "true") |  | ||||||
|     local user = session and session.jitsi_meet_context_user |  | ||||||
|     local has_token = session and session.auth_token |  | ||||||
|     local wants_mod = user and (user.moderator == true or user.moderator == "true") |  | ||||||
| 
 |  | ||||||
|     if has_token and wants_mod then |  | ||||||
|         -- Assign owner affiliation -> Jicofo will see you as moderator |  | ||||||
|         room:set_affiliation(true, occupant.bare_jid, 'owner') |  | ||||||
|     end |  | ||||||
| end, 1) |  | ||||||
|  | @ -77,7 +77,7 @@ else | ||||||
|     echo "Seems no nodejs repo installed" |     echo "Seems no nodejs repo installed" | ||||||
| fi | fi | ||||||
| # True if $1 is greater than $2 | # True if $1 is greater than $2 | ||||||
| version_gt() { dpkg --compare-versions "$1" gt "$2"; } | version_gt() { test "$(printf '%s\n' "$@" | sort -V | head -n 1)" != "$1"; } | ||||||
| 
 | 
 | ||||||
| restart_jibri() { | restart_jibri() { | ||||||
| if [ "$(dpkg-query -W -f='${Status}' "jibri" 2>/dev/null | grep -c "ok installed")" == "1" ] | if [ "$(dpkg-query -W -f='${Status}' "jibri" 2>/dev/null | grep -c "ok installed")" == "1" ] | ||||||
|  | @ -116,7 +116,7 @@ update_google_repo() { | ||||||
| 		echo "Seems no chromedriver installed" | 		echo "Seems no chromedriver installed" | ||||||
| 	else | 	else | ||||||
| 		CHD_VER_LOCAL="$($CHDB -v | awk '{print $2}')" | 		CHD_VER_LOCAL="$($CHDB -v | awk '{print $2}')" | ||||||
| 		CHD_VER_2D="$(cut -d. -f1,2 <<< "$CHD_VER_LOCAL")" | 		CHD_VER_2D="$(awk '{printf "%.1f\n", $NF}' <<< "$CHD_VER_LOCAL")" | ||||||
| 	fi | 	fi | ||||||
| } | } | ||||||
| update_nodejs_repo() { | update_nodejs_repo() { | ||||||
|  | @ -127,7 +127,7 @@ update_nodejs_repo() { | ||||||
| check_latest_gc() { | check_latest_gc() { | ||||||
| printwc "${Purple}" "Checking for Google Chrome\n" | printwc "${Purple}" "Checking for Google Chrome\n" | ||||||
| if [ -f /usr/bin/google-chrome ]; then | if [ -f /usr/bin/google-chrome ]; then | ||||||
|     GOOGL_VER_2D="$(/usr/bin/google-chrome --product-version 2>/dev/null | cut -d. -f1,2)" |     GOOGL_VER_2D="$(/usr/bin/google-chrome --version|awk '{printf "%.1f\n", $NF}')" | ||||||
| else | else | ||||||
|     printwc "${Yellow}" " -> Seems there is no Google Chrome installed\n" |     printwc "${Yellow}" " -> Seems there is no Google Chrome installed\n" | ||||||
|     IS_GLG_CHRM="no" |     IS_GLG_CHRM="no" | ||||||
|  | @ -139,14 +139,6 @@ if [ -n "$GOOGL_VER_2D" ]; then | ||||||
|     check_latest_gc |     check_latest_gc | ||||||
|     if version_gt "$GOOGL_VER_2D" "$CHD_VER_2D" ; then |     if version_gt "$GOOGL_VER_2D" "$CHD_VER_2D" ; then | ||||||
|         echo "Upgrading Chromedriver to Google Chromes version" |         echo "Upgrading Chromedriver to Google Chromes version" | ||||||
|         if [ -x /usr/bin/google-chrome ]; then |  | ||||||
|             G_CHROME="$(/usr/bin/google-chrome --product-version 2>/dev/null | cut -d. -f1-3)" |  | ||||||
|         fi |  | ||||||
|         CHD_LTST_DWNL=$(curl -s $CHROMELAB_URL/known-good-versions-with-downloads.json | \ |  | ||||||
|                         jq -r ".versions[].downloads.chromedriver | select(. != null) | .[].url" | \ |  | ||||||
|                         grep linux64 | grep "$G_CHROME" | tail -1) |  | ||||||
|         CHD_LTST=$(awk -F '/' '{print$7}' <<< "$CHD_LTST_DWNL") |  | ||||||
|         CHD_LTST_2D="$(cut -d. -f1,2 <<< "$CHD_LTST")" |  | ||||||
|         wget -q "$CHD_LTST_DWNL" \ |         wget -q "$CHD_LTST_DWNL" \ | ||||||
|              -O /tmp/chromedriver_linux64.zip |              -O /tmp/chromedriver_linux64.zip | ||||||
|         unzip -o /tmp/chromedriver_linux64.zip -d /usr/local/bin/ |         unzip -o /tmp/chromedriver_linux64.zip -d /usr/local/bin/ | ||||||
|  | @ -155,7 +147,7 @@ if [ -n "$GOOGL_VER_2D" ]; then | ||||||
|         chmod 0755 "$CHDB" |         chmod 0755 "$CHDB" | ||||||
|         rm -rf /tpm/chromedriver_linux64.zip |         rm -rf /tpm/chromedriver_linux64.zip | ||||||
|         printf "Current version: " |         printf "Current version: " | ||||||
|         printwc "$Green" "$($CHDB -v | awk '{print $2}' | cut -d. -f1,2)" |         printwc "$Green" "$($CHDB -v |awk '{print $2}'|awk '{printf "%.1f\n", $NF}')" | ||||||
|         echo -e " (latest available)\n" |         echo -e " (latest available)\n" | ||||||
|     elif [ "$GOOGL_VER_2D" = "$CHD_LTST_2D" ]; then |     elif [ "$GOOGL_VER_2D" = "$CHD_LTST_2D" ]; then | ||||||
|         echo "No need to upgrade Chromedriver" |         echo "No need to upgrade Chromedriver" | ||||||
|  |  | ||||||
							
								
								
									
										5
									
								
								jm-bm.sh
								
								
								
								
							
							
						
						
									
										5
									
								
								jm-bm.sh
								
								
								
								
							|  | @ -23,7 +23,6 @@ if ! [ "$(id -u)" = 0 ]; then | ||||||
| fi | fi | ||||||
| 
 | 
 | ||||||
| DOMAIN="$(find /etc/prosody/conf.d/ -name \*.lua|awk -F'.cfg' '!/localhost/{print $1}'|xargs basename)" | DOMAIN="$(find /etc/prosody/conf.d/ -name \*.lua|awk -F'.cfg' '!/localhost/{print $1}'|xargs basename)" | ||||||
| MEET_CONF="/etc/jitsi/meet/$DOMAIN-config.js" |  | ||||||
| CSS_FILE="/usr/share/jitsi-meet/css/all.css" | CSS_FILE="/usr/share/jitsi-meet/css/all.css" | ||||||
| TITLE_FILE="/usr/share/jitsi-meet/title.html" | TITLE_FILE="/usr/share/jitsi-meet/title.html" | ||||||
| INT_CONF="/usr/share/jitsi-meet/interface_config.js" | INT_CONF="/usr/share/jitsi-meet/interface_config.js" | ||||||
|  | @ -88,10 +87,10 @@ sed -i "s| powered by the Jitsi Videobridge||g" "$TITLE_FILE" | ||||||
| sed -i "/appNotInstalled/ s|{{app}}|$MOVILE_APP_NAME|g" /usr/share/jitsi-meet/lang/* | sed -i "/appNotInstalled/ s|{{app}}|$MOVILE_APP_NAME|g" /usr/share/jitsi-meet/lang/* | ||||||
| 
 | 
 | ||||||
| #Custom UI changes | #Custom UI changes | ||||||
| if [ -f "$INT_CONF" ] && [ -f "$INT_CONF_ETC" ]; then | if [ -f "$INT_CONF_ETC" ]; then | ||||||
|     echo "Static interface_config.js exists, skipping modification..." |     echo "Static interface_config.js exists, skipping modification..." | ||||||
| else | else | ||||||
|     echo "This setup doesn't have a static interface_config.js, seting it up and applying changes..." |     echo "This setup doesn't have a static interface_config.js, checking changes..." | ||||||
|     echo -e "\nPlease note that brandless mode will also overwrite support links.\n" |     echo -e "\nPlease note that brandless mode will also overwrite support links.\n" | ||||||
|     sed -i "21,32 s|Jitsi Meet|$APP_NAME|g" "$INT_CONF" |     sed -i "21,32 s|Jitsi Meet|$APP_NAME|g" "$INT_CONF" | ||||||
|     sed -i  "s|\([[:space:]]\)APP_NAME:.*| APP_NAME: \'$APP_NAME\',|" "$INT_CONF" |     sed -i  "s|\([[:space:]]\)APP_NAME:.*| APP_NAME: \'$APP_NAME\',|" "$INT_CONF" | ||||||
|  |  | ||||||
|  | @ -42,7 +42,7 @@ apt-get update -q2 | ||||||
| # Manually add prerequisites. | # Manually add prerequisites. | ||||||
| apt-get install -y curl letsencrypt nginx | apt-get install -y curl letsencrypt nginx | ||||||
| 
 | 
 | ||||||
| MIN_PHP="8.3" | MIN_PHP="8.2" | ||||||
| DOMAIN="$(find /etc/prosody/conf.d/ -name \*.lua|awk -F'.cfg' '!/localhost/{print $1}'|xargs basename)" | DOMAIN="$(find /etc/prosody/conf.d/ -name \*.lua|awk -F'.cfg' '!/localhost/{print $1}'|xargs basename)" | ||||||
| PSGVER="$(apt-cache madison postgresql|tr -d '[:blank:]'|awk -F'[|+]' 'NR==1{print $2}')" | PSGVER="$(apt-cache madison postgresql|tr -d '[:blank:]'|awk -F'[|+]' 'NR==1{print $2}')" | ||||||
| NC_NGINX_SSL_PORT="$(grep "listen 44" /etc/nginx/sites-available/"$DOMAIN".conf | awk '{print$2}')" | NC_NGINX_SSL_PORT="$(grep "listen 44" /etc/nginx/sites-available/"$DOMAIN".conf | awk '{print$2}')" | ||||||
|  |  | ||||||
							
								
								
									
										112
									
								
								mode/jwt.sh
								
								
								
								
							
							
						
						
									
										112
									
								
								mode/jwt.sh
								
								
								
								
							|  | @ -14,102 +14,83 @@ done | ||||||
| 
 | 
 | ||||||
| #DEBUG | #DEBUG | ||||||
| if [ "$MODE" = "debug" ]; then | if [ "$MODE" = "debug" ]; then | ||||||
|     set -x | set -x | ||||||
| fi | fi | ||||||
| 
 | 
 | ||||||
| DOMAIN="$(find /etc/prosody/conf.d/ -name \*.lua|awk -F'.cfg' '!/localhost/{print $1}'|xargs basename)" | DOMAIN="$(find /etc/prosody/conf.d/ -name \*.lua|awk -F'.cfg' '!/localhost/{print $1}'|xargs basename)" | ||||||
| MEET_CONF="/etc/jitsi/meet/$DOMAIN-config.js" | MEET_CONF="/etc/jitsi/meet/$DOMAIN-config.js" | ||||||
| JICOFO_SIP="/etc/jitsi/jicofo/sip-communicator.properties" | JICOFO_SIP="/etc/jitsi/jicofo/sip-communicator.properties" | ||||||
| JICOFO_CONF="/etc/jitsi/jicofo/jicofo.conf" |  | ||||||
| PROSODY_FILE="/etc/prosody/conf.d/$DOMAIN.cfg.lua" | PROSODY_FILE="/etc/prosody/conf.d/$DOMAIN.cfg.lua" | ||||||
| PROSODY_SYS="/etc/prosody/prosody.cfg.lua" | PROSODY_SYS="/etc/prosody/prosody.cfg.lua" | ||||||
| APP_ID="$(tr -dc "a-zA-Z0-9" < /dev/urandom | fold -w 16 | head -n1)" | APP_ID="$(tr -dc "a-zA-Z0-9" < /dev/urandom | fold -w 16 | head -n1)" | ||||||
| SECRET_APP="$(tr -dc "a-zA-Z0-9" < /dev/urandom | fold -w 64 | head -n1)" | SECRET_APP="$(tr -dc "a-zA-Z0-9" < /dev/urandom | fold -w 64 | head -n1)" | ||||||
| ROOM="Two-Hour-Test-Room" |  | ||||||
| SRP_STR="$(grep -n "VirtualHost \"$DOMAIN\"" "$PROSODY_FILE" | head -n1 | cut -d ":" -f1)" | SRP_STR="$(grep -n "VirtualHost \"$DOMAIN\"" "$PROSODY_FILE" | head -n1 | cut -d ":" -f1)" | ||||||
| SRP_END="$((SRP_STR + 10))" | SRP_END="$((SRP_STR + 10))" | ||||||
| 
 | 
 | ||||||
| # Prosody 0.12{,.*} only | ## Required  openssl for Focal 20.04 | ||||||
| if command -v prosodyctl >/dev/null 2>&1; then | if [ "$(lsb_release -sc)" = "focal" ]; then | ||||||
|   PROSODY_VER="$(prosodyctl about 2>/dev/null | sed -n 's/^Prosody //p' | awk '{print $1}')" | echo "deb http://ppa.launchpad.net/rael-gc/rvm/ubuntu focal main" | \ | ||||||
|   case "$PROSODY_VER" in | sudo tee /etc/apt/sources.list.d/rvm.list | ||||||
|     0.12) : ;; # 22.04 | apt-key adv --keyserver keyserver.ubuntu.com --recv-keys F4E3FBBE | ||||||
|     0.12.*) : ;; # 24.04 | apt-get update | ||||||
|     *) echo "Prosody $PROSODY_VER NO supported for JWT mode (required 0.12.x)" |  | ||||||
|        exit 1 ;; |  | ||||||
|   esac |  | ||||||
| fi | fi | ||||||
| 
 | 
 | ||||||
| # Custom 5.4 lua workaround for prosody 0.12 | apt-get -y install \ | ||||||
| echo "Warning: Ubuntu 22.04/24.04 don't ship the required lua inspect module 5.4," |                     lua5.2 \ | ||||||
| echo "         so, we work arround it, be careful on further upgrades or changes." |                     liblua5.2 \ | ||||||
| install -d -m 755 /usr/share/lua/5.4 |                     luarocks \ | ||||||
| ln -sf /usr/share/lua/5.3/inspect.lua /usr/share/lua/5.4/inspect.lua |                     libssl1.0-dev \ | ||||||
| systemctl restart prosody jicofo jitsi-videobridge2 |                     python3-jwt | ||||||
| 
 | 
 | ||||||
| sleep .1 | luarocks install basexx | ||||||
| 
 | luarocks install luacrypto | ||||||
| # Install dependencies | luarocks install lua-cjson 2.1.0-1 | ||||||
| apt-get -y install python3-jwt |  | ||||||
| 
 | 
 | ||||||
| echo "set jitsi-meet-tokens/appid string $APP_ID" | debconf-set-selections | echo "set jitsi-meet-tokens/appid string $APP_ID" | debconf-set-selections | ||||||
| echo "set jitsi-meet-tokens/appsecret password $SECRET_APP" | debconf-set-selections | echo "set jitsi-meet-tokens/appsecret password $SECRET_APP" | debconf-set-selections | ||||||
| 
 | 
 | ||||||
| apt-get install -y jitsi-meet-tokens | apt-get install -y jitsi-meet-tokens | ||||||
| 
 | 
 | ||||||
| # Setting up prosody | #Setting up | ||||||
| sed -i "s|c2s_require_encryption = true|c2s_require_encryption = false|" "$PROSODY_SYS" | sed -i "s|c2s_require_encryption = true|c2s_require_encryption = false|" "$PROSODY_SYS" | ||||||
| #- | #- | ||||||
| sed -i "$SRP_STR,$SRP_END{s|authentication = \"jitsi-anonymous\"|authentication = \"token\"|}" "$PROSODY_FILE" | sed -i "$SRP_STR,$SRP_END{s|authentication = \"jitsi-anonymous\"|authentication = \"token\"|}" "$PROSODY_FILE" | ||||||
| sed -i "s|--app_id=\"example_app_id\"|app_id=\"$APP_ID\"|" "$PROSODY_FILE" | sed -i "s|--app_id=\"example_app_id\"|app_id=\"$APP_ID\"|" "$PROSODY_FILE" | ||||||
| sed -i "s|--app_secret=\"example_app_secret\"|app_secret=\"$SECRET_APP\"|" "$PROSODY_FILE" | sed -i "s|--app_secret=\"example_app_secret\"|app_secret=\"$SECRET_APP\"|" "$PROSODY_FILE" | ||||||
| sed -i "/app_secret/a \\\\" "$PROSODY_FILE" | sed -i "/app_secret/a \\\\" "$PROSODY_FILE" | ||||||
| ## Only token owners can create, open the room and become moderators, | sed -i "/app_secret/a \ \ \ \ allow_empty_token = false" "$PROSODY_FILE" | ||||||
| ## but require jibri: `allow_empty_token = true` |  | ||||||
| ## other participants are redirected authentication to guest. |  | ||||||
| sed -i "/app_secret/a \ \ \ \ allow_empty_token = true" "$PROSODY_FILE" |  | ||||||
| sed -i "/app_secret/a \\\\" "$PROSODY_FILE" | sed -i "/app_secret/a \\\\" "$PROSODY_FILE" | ||||||
| sed -i "/app_secret/a \ \ \ \ asap_accepted_issuers = { \"$APP_ID\" }" "$PROSODY_FILE" | sed -i "/app_secret/a \ \ \ \ asap_accepted_issuers = { \"$APP_ID\" }" "$PROSODY_FILE" | ||||||
| sed -i "/app_secret/a \ \ \ \ asap_accepted_audiences = { \"$APP_ID\" }" "$PROSODY_FILE" | sed -i "/app_secret/a \ \ \ \ asap_accepted_audiences = { \"$APP_ID\", \"RocketChat\" }" "$PROSODY_FILE" | ||||||
| sed -i "/app_secret/a \\\\" "$PROSODY_FILE" | sed -i "/app_secret/a \\\\" "$PROSODY_FILE" | ||||||
| sed -i "s|--allow_empty_token =.*|allow_empty_token = true|" "$PROSODY_FILE" | sed -i "s|--allow_empty_token =.*|allow_empty_token = false|" "$PROSODY_FILE" | ||||||
| sed -i 's|--"token_verification"|"token_verification"|' "$PROSODY_FILE" | sed -i 's|--"token_verification"|"token_verification"|' "$PROSODY_FILE" | ||||||
| sed -i "/muc_lobby_rooms/a \ \ \ \ \ \ \ \ \"persistent_lobby\";" "$PROSODY_FILE" |  | ||||||
| sed -i "/token_verification/a \ \ \ \ \ \ \ \ \"muc_wait_for_host\";" "$PROSODY_FILE" |  | ||||||
| 
 | 
 | ||||||
| # Set JWT and Guest settings | #Request auth | ||||||
| ## Harden JWT auth, preventing "free" moderator by racing into room, | sed -i "s|#org.jitsi.jicofo.auth.URL=EXT_JWT:|org.jitsi.jicofo.auth.URL=EXT_JWT:|" "$JICOFO_SIP" | ||||||
| ## only participants with token with moderator:true. |  | ||||||
| # Custom 'token_moderator_owner' module for passing JWT claim |  | ||||||
| cp files/mod_token_moderator_owner.lua /usr/share/jitsi-meet/prosody-plugins/ |  | ||||||
| sed -i '/"muc_password_whitelist"/a \ \ \ \ \ \ \ \ \"token_moderator_owner";' "$PROSODY_FILE" |  | ||||||
| envsubst < \ |  | ||||||
|   patches/prosody/001-enable_wait_for_host_disable_auto_owners.patch | \ |  | ||||||
|   patch --no-backup-if-mismatch -d / -p1 |  | ||||||
| hocon set jicofo.conference.enable-auto-owner false -f "$JICOFO_CONF" |  | ||||||
| 
 |  | ||||||
| ## config.js |  | ||||||
| sed -i "s|// anonymousdomain: 'guest.example.com'|anonymousdomain: \'guest.$DOMAIN\'|" "$MEET_CONF" | sed -i "s|// anonymousdomain: 'guest.example.com'|anonymousdomain: \'guest.$DOMAIN\'|" "$MEET_CONF" | ||||||
| awk ' | 
 | ||||||
|   BEGIN{done=0} | #Enable jibri recording | ||||||
|   /^[[:space:]]*var[[:space:]]+config[[:space:]]*=[[:space:]]*{\s*$/ && !done{ | cat  << REC-JIBRI >> "$PROSODY_FILE" | ||||||
|     print | 
 | ||||||
|     print "    enableFeaturesBasedOnToken: true," | VirtualHost "recorder.$DOMAIN" | ||||||
|     print "    enableUserRolesBasedOnToken: true," |   modules_enabled = { | ||||||
|     done=1 |     "ping"; | ||||||
|     next |  | ||||||
|   } |   } | ||||||
|   {print} |   authentication = "internal_hashed" | ||||||
| ' "$MEET_CONF" > "${MEET_CONF}.new" && mv "${MEET_CONF}.new" "$MEET_CONF" |  | ||||||
| 
 | 
 | ||||||
| # Setup guests and lobby | REC-JIBRI | ||||||
|  | 
 | ||||||
|  | #Setup guests and lobby | ||||||
| cat << P_SR >> "$PROSODY_FILE" | cat << P_SR >> "$PROSODY_FILE" | ||||||
| 
 | -- #Change back lobby - https://community.jitsi.org/t/64769/136 | ||||||
| -- jwt setup -QJI |  | ||||||
| VirtualHost "guest.$DOMAIN" | VirtualHost "guest.$DOMAIN" | ||||||
|     authentication = "anonymous" |     authentication = "token" | ||||||
|  |     allow_empty_token = true | ||||||
|     c2s_require_encryption = false |     c2s_require_encryption = false | ||||||
|     speakerstats_component = "speakerstats.$DOMAIN" |     speakerstats_component = "speakerstats.$DOMAIN" | ||||||
|  |     app_id="$APP_ID"; | ||||||
|  |     app_secret="$SECRET_APP"; | ||||||
| 
 | 
 | ||||||
|     modules_enabled = { |     modules_enabled = { | ||||||
|       "speakerstats"; |       "speakerstats"; | ||||||
|  | @ -121,13 +102,14 @@ echo -e "\nUse the following for your App (e.g. Rocket.Chat):\n" | ||||||
| echo -e "\nAPP_ID: $APP_ID" && \ | echo -e "\nAPP_ID: $APP_ID" && \ | ||||||
| echo -e "SECRET_APP: $SECRET_APP\n" | echo -e "SECRET_APP: $SECRET_APP\n" | ||||||
| 
 | 
 | ||||||
| echo -e "You can test JWT authentication with the following token for the next 2 hours:\n" | echo -e "You can test JWT authentication with the following token:\n" | ||||||
| python3 tools/jwt/jitsi_token_maker_features.py \ | pyjwt3 --key="$SECRET_APP" \ | ||||||
|   --app-id "$APP_ID" --secret "$SECRET_APP" \ |     encode \ | ||||||
|   --domain "$DOMAIN" --room "$ROOM" \ |     group="Rocket.Chat" \ | ||||||
|   --username "Moderator Token Test User" |     aud="$APP_ID" \ | ||||||
|   --moderator --features-all \ |     iss="$APP_ID" \ | ||||||
|   --minutes 120 --nbf-offset 300 --include-iat \ |     sub="$DOMAIN" \ | ||||||
|   --url "https://$DOMAIN/" |     room="*" \ | ||||||
|  |     algorithm="HS256" | ||||||
| 
 | 
 | ||||||
| read -n 1 -s -r -p $'\n'"Press any key to continue..."$'\n' | read -n 1 -s -r -p $'\n'"Press any key to continue..."$'\n' | ||||||
|  |  | ||||||
|  | @ -1,18 +0,0 @@ | ||||||
| # Quick Jibri Installer - *buntu (LTS) based systems. |  | ||||||
| # SwITNet Ltd © - 2025, https://switnet.net/ |  | ||||||
| # GPLv3 or later. |  | ||||||
| 
 |  | ||||||
| Patch prosody (0.12) $DOMAIN.cfg.lua to enable wait_for_host_disable_auto_owners. |  | ||||||
| 
 |  | ||||||
| diff --git a/etc/prosody/conf.d/${DOMAIN}.cfg.lua b/etc/prosody/conf.d/${DOMAIN}.cfg.lua
 |  | ||||||
| index f2b647e..031a064 100644
 |  | ||||||
| --- a/etc/prosody/conf.d/${DOMAIN}.cfg.lua
 |  | ||||||
| +++ b/etc/prosody/conf.d/${DOMAIN}.cfg.lua
 |  | ||||||
| @@ -103,6 +103,7 @@ Component "conference.${DOMAIN}" "muc"
 |  | ||||||
|      } |  | ||||||
|      muc_room_locking = false |  | ||||||
|      muc_room_default_public_jids = true |  | ||||||
| +    wait_for_host_disable_auto_owners = true
 |  | ||||||
|   |  | ||||||
|  Component "breakout.${DOMAIN}" "muc" |  | ||||||
|      restrict_room_creation = true |  | ||||||
|  | @ -303,12 +303,7 @@ do | ||||||
| done | done | ||||||
| sleep .1 | sleep .1 | ||||||
| #Prosody repository | #Prosody repository | ||||||
| if [ "$DIST" = "jammy" ]; then | add_prosody_repo | ||||||
|     add_prosody_repo |  | ||||||
|     prosody="prosody-0.12" |  | ||||||
| else |  | ||||||
|     prosody='prosody' |  | ||||||
| fi |  | ||||||
| sleep .1 | sleep .1 | ||||||
| # Jitsi-Meet Repo | # Jitsi-Meet Repo | ||||||
| printf "\nAdd Jitsi repo\n" | printf "\nAdd Jitsi repo\n" | ||||||
|  | @ -437,7 +432,7 @@ echo "jitsi-meet-web-config	jitsi-meet/jaas-choice	boolean	false" \ | ||||||
| apt-get -y install \ | apt-get -y install \ | ||||||
|                 jitsi-meet \ |                 jitsi-meet \ | ||||||
|                 jibri \ |                 jibri \ | ||||||
|                 $prosody \ |                 lua5.4 \ | ||||||
|                 openjdk-11-jre-headless |                 openjdk-11-jre-headless | ||||||
| 
 | 
 | ||||||
| # Fix RAND_load_file error | # Fix RAND_load_file error | ||||||
|  | @ -766,27 +761,7 @@ INT_CONF_ETC="/etc/jitsi/meet/$DOMAIN-interface_config.js" | ||||||
| ssl_wa() { | ssl_wa() { | ||||||
| if [ "$LE_SSL" = "yes" ]; then | if [ "$LE_SSL" = "yes" ]; then | ||||||
|   systemctl stop "$1" |   systemctl stop "$1" | ||||||
|   if certbot certonly --standalone --renew-by-default \ |   certbot certonly --standalone --renew-by-default --agree-tos --email "$5" -d "$6" --non-interactive | ||||||
|                       --agree-tos --email "$5" -d "$6" \ |  | ||||||
|                       --non-interactive |  | ||||||
|   then |  | ||||||
|     echo "> SSL cert issued correctly!" |  | ||||||
|   else |  | ||||||
|     echo "> SSL cert issued failed!" |  | ||||||
|     sleep .1 |  | ||||||
|     while [ "$SSL_FAILED" != "yes" ] && [ "$SSL_FAILED" != "no" ] |  | ||||||
|     do |  | ||||||
|         read -p "> Do you want to continue regardless? This might make the installer to fail later on: (yes or no)$NL" -r SSL_FAILED |  | ||||||
|         if [ "$SSL_FAILED" = "yes" ]; then |  | ||||||
|             printf " - Continuing installation regardless failure to retrieve SSL certs.\n\n" |  | ||||||
|         elif [ "$SSL_FAILED" = "no" ]; then |  | ||||||
|             printf " - Stoping the installer.You might want to take a look at:\n" |  | ||||||
|             printf "   - /var/log/letsencrypt/letsencrypt.log and / or\n" |  | ||||||
|             printf "   - https://letsencrypt.status.io/\n\n" |  | ||||||
|             exit 1 |  | ||||||
|         fi |  | ||||||
|     done |  | ||||||
|   fi |  | ||||||
|   sed -i "s|/etc/jitsi/meet/$3.crt|/etc/letsencrypt/live/$3/fullchain.pem|" "$4" |   sed -i "s|/etc/jitsi/meet/$3.crt|/etc/letsencrypt/live/$3/fullchain.pem|" "$4" | ||||||
|   sed -i "s|/etc/jitsi/meet/$3.key|/etc/letsencrypt/live/$3/privkey.pem|" "$4" |   sed -i "s|/etc/jitsi/meet/$3.key|/etc/letsencrypt/live/$3/privkey.pem|" "$4" | ||||||
|   systemctl restart "$1" |   systemctl restart "$1" | ||||||
|  | @ -819,6 +794,9 @@ restart_services() { | ||||||
|     check_jibri |     check_jibri | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | # Configure Jvb2 | ||||||
|  | sed -i "/shard.HOSTNAME/s|localhost|$DOMAIN|" "$JVB2_SIP" | ||||||
|  | 
 | ||||||
| #-------------------------------------------------- | #-------------------------------------------------- | ||||||
| print_title "Configure Jibri" | print_title "Configure Jibri" | ||||||
| #-------------------------------------------------- | #-------------------------------------------------- | ||||||
|  | @ -836,6 +814,16 @@ if [ "$ENABLE_SC" = "yes" ]; then | ||||||
| 
 | 
 | ||||||
| fi | fi | ||||||
| sleep .1 | sleep .1 | ||||||
|  | #Enable jibri recording | ||||||
|  | cat  << REC-JIBRI >> "$PROSODY_FILE" | ||||||
|  | 
 | ||||||
|  | VirtualHost "recorder.$DOMAIN" | ||||||
|  |   modules_enabled = { | ||||||
|  |     "ping"; | ||||||
|  |   } | ||||||
|  |   authentication = "internal_hashed" | ||||||
|  | 
 | ||||||
|  | REC-JIBRI | ||||||
| 
 | 
 | ||||||
| #Enable Jibri withelist | #Enable Jibri withelist | ||||||
| sed -i "s|-- muc_lobby_whitelist|muc_lobby_whitelist|" "$PROSODY_FILE" | sed -i "s|-- muc_lobby_whitelist|muc_lobby_whitelist|" "$PROSODY_FILE" | ||||||
|  | @ -961,17 +949,16 @@ JVB_SECRET=$(g_conf_value JVB_SECRET=) | ||||||
| JVB_OPTS=$(g_conf_value JVB_OPTS=) | JVB_OPTS=$(g_conf_value JVB_OPTS=) | ||||||
| JAVA_SYS_PROPS=$(g_conf_value JAVA_SYS_PROPS=) | JAVA_SYS_PROPS=$(g_conf_value JAVA_SYS_PROPS=) | ||||||
| 
 | 
 | ||||||
| #FIXME: - sip-communicator.properties - no longer exist | g_sip_value() { | ||||||
| #g_sip_value() { |   grep "$1" "$JVB2_SIP" |cut -d "=" -f2 | ||||||
| #  grep "$1" "$JVB2_SIP" |cut -d "=" -f2 | } | ||||||
| #} | DISABLE_AWS_HARVESTER=$(g_sip_value DISABLE_AWS_HARVESTER=) | ||||||
| #DISABLE_AWS_HARVESTER=$(g_sip_value DISABLE_AWS_HARVESTER=) | STUN_MAPPING_HARVESTER_ADDRESSES=$(g_sip_value STUN_MAPPING_HARVESTER_ADDRESSES=) | ||||||
| #STUN_MAPPING_HARVESTER_ADDRESSES=$(g_sip_value STUN_MAPPING_HARVESTER_ADDRESSES=) | ENABLE_STATISTICS=$(g_sip_value ENABLE_STATISTICS=) | ||||||
| #ENABLE_STATISTICS=$(g_sip_value ENABLE_STATISTICS=) | SHARD_HOSTNAME=$(g_sip_value shard.HOSTNAME=) | ||||||
| #SHARD_HOSTNAME=$(g_sip_value shard.HOSTNAME=) | SHARD_DOMAIN=$(g_sip_value shard.DOMAIN=) | ||||||
| #SHARD_DOMAIN=$(g_sip_value shard.DOMAIN=) | SHARD_PASSWORD=$(g_sip_value shard.PASSWORD=) | ||||||
| #SHARD_PASSWORD=$(g_sip_value shard.PASSWORD=) | MUC_JID=$(g_sip_value MUC_JIDS=) | ||||||
| #MUC_JID=$(g_sip_value MUC_JIDS=) |  | ||||||
| 
 | 
 | ||||||
| ##-- Replacing on add-jvb2-node.sh | ##-- Replacing on add-jvb2-node.sh | ||||||
| sed -i "s|JVB_HOSTNAME=.*|JVB_HOSTNAME=$JVB_HOSTNAME|" add-jvb2-node.sh | sed -i "s|JVB_HOSTNAME=.*|JVB_HOSTNAME=$JVB_HOSTNAME|" add-jvb2-node.sh | ||||||
|  | @ -980,14 +967,14 @@ sed -i "s|JVB_PORT=.*|JVB_PORT=$JVB_PORT|" add-jvb2-node.sh | ||||||
| sed -i "s|JVB_SECRET=.*|JVB_SECRET=$JVB_SECRET|" add-jvb2-node.sh | sed -i "s|JVB_SECRET=.*|JVB_SECRET=$JVB_SECRET|" add-jvb2-node.sh | ||||||
| sed -i "s|JVB_OPTS=.*|JVB_OPTS=$JVB_OPTS|" add-jvb2-node.sh | sed -i "s|JVB_OPTS=.*|JVB_OPTS=$JVB_OPTS|" add-jvb2-node.sh | ||||||
| sed -i "s|SYS_PROPS=.*|SYS_PROPS=$JAVA_SYS_PROPS|" add-jvb2-node.sh | sed -i "s|SYS_PROPS=.*|SYS_PROPS=$JAVA_SYS_PROPS|" add-jvb2-node.sh | ||||||
| #FIXME: - sip-communicator.properties - no longer exist | #- | ||||||
| #sed -i "s|AWS_HARVEST=.*|AWS_HARVEST=$DISABLE_AWS_HARVESTER|" add-jvb2-node.sh | sed -i "s|AWS_HARVEST=.*|AWS_HARVEST=$DISABLE_AWS_HARVESTER|" add-jvb2-node.sh | ||||||
| #sed -i "s|STUN_MAPPING=.*|STUN_MAPPING=$STUN_MAPPING_HARVESTER_ADDRESSES|" add-jvb2-node.sh | sed -i "s|STUN_MAPPING=.*|STUN_MAPPING=$STUN_MAPPING_HARVESTER_ADDRESSES|" add-jvb2-node.sh | ||||||
| #sed -i "s|ENABLE_STATISTICS=.*|ENABLE_STATISTICS=$ENABLE_STATISTICS|" add-jvb2-node.sh | sed -i "s|ENABLE_STATISTICS=.*|ENABLE_STATISTICS=$ENABLE_STATISTICS|" add-jvb2-node.sh | ||||||
| #sed -i "s|SHARD_HOSTNAME=.*|SHARD_HOSTNAME=$SHARD_HOSTNAME|" add-jvb2-node.sh | sed -i "s|SHARD_HOSTNAME=.*|SHARD_HOSTNAME=$SHARD_HOSTNAME|" add-jvb2-node.sh | ||||||
| #sed -i "s|SHARD_DOMAIN=.*|SHARD_DOMAIN=$SHARD_DOMAIN|" add-jvb2-node.sh | sed -i "s|SHARD_DOMAIN=.*|SHARD_DOMAIN=$SHARD_DOMAIN|" add-jvb2-node.sh | ||||||
| #sed -i "s|SHARD_PASS=.*|SHARD_PASS=$SHARD_PASSWORD|" add-jvb2-node.sh | sed -i "s|SHARD_PASS=.*|SHARD_PASS=$SHARD_PASSWORD|" add-jvb2-node.sh | ||||||
| #sed -i "s|MUC_JID=.*|MUC_JID=$MUC_JID|" add-jvb2-node.sh | sed -i "s|MUC_JID=.*|MUC_JID=$MUC_JID|" add-jvb2-node.sh | ||||||
| 
 | 
 | ||||||
| sed -i "s|MAIN_SRV_DIST=.*|MAIN_SRV_DIST=\"$DIST\"|" add-jvb2-node.sh | sed -i "s|MAIN_SRV_DIST=.*|MAIN_SRV_DIST=\"$DIST\"|" add-jvb2-node.sh | ||||||
| sed -i "s|MAIN_SRV_REPO=.*|MAIN_SRV_REPO=\"$JITSI_REPO\"|" add-jvb2-node.sh | sed -i "s|MAIN_SRV_REPO=.*|MAIN_SRV_REPO=\"$JITSI_REPO\"|" add-jvb2-node.sh | ||||||
|  |  | ||||||
|  | @ -1,158 +0,0 @@ | ||||||
| #!/usr/bin/env python3 |  | ||||||
| # -*- coding: utf-8 -*- |  | ||||||
| """ |  | ||||||
| JWT generator for self‑hosted Jitsi (also compatible with JAAS if desired) |  | ||||||
| - HS256 (HMAC) signing using only Python standard library (no external deps). |  | ||||||
| - Flags to omit exp/nbf (test tokens), include iat, and read secret from file/STDIN. |  | ||||||
| - Flags to populate context.features: recording, livestreaming, transcription, sip-in/out. |  | ||||||
| - Robust URL construction (escapes the room name). |  | ||||||
| """ |  | ||||||
| import argparse, base64, hashlib, hmac, json, time, sys |  | ||||||
| from urllib.parse import quote |  | ||||||
| 
 |  | ||||||
| def b64url(data: bytes) -> str: |  | ||||||
|     return base64.urlsafe_b64encode(data).rstrip(b"=").decode("ascii") |  | ||||||
| 
 |  | ||||||
| def sign_hs256(secret: str, signing_input: str) -> str: |  | ||||||
|     sig = hmac.new(secret.encode("utf-8"), signing_input.encode("ascii"), hashlib.sha256).digest() |  | ||||||
|     return b64url(sig) |  | ||||||
| 
 |  | ||||||
| def main(): |  | ||||||
|     p = argparse.ArgumentParser(description="JWT generator for Jitsi (HS256)") |  | ||||||
|     # Identity / target |  | ||||||
|     p.add_argument("--app-id", required=True, help="app_id configured in Prosody/JAAS") |  | ||||||
|     p.add_argument("--secret", required=False, help="app_secret (HMAC/HS256)") |  | ||||||
|     p.add_argument("--secret-file", help="Read secret from file or '-' for STDIN") |  | ||||||
|     p.add_argument("--domain", help="Jitsi domain (e.g. meet.example.com) used as 'sub' in self-hosted") |  | ||||||
|     p.add_argument("--room", default="*", help="Target room (or '*' for all)") |  | ||||||
|     # Time |  | ||||||
|     p.add_argument("--minutes", type=int, default=60, help="Validity (minutes). Ignored if --no-exp") |  | ||||||
|     p.add_argument("--no-exp", action="store_true", help="Do not include 'exp' (tests only)") |  | ||||||
|     p.add_argument("--nbf-offset", type=int, default=10, help="Backdating seconds for 'nbf' (default: 10)") |  | ||||||
|     p.add_argument("--no-nbf", action="store_true", help="Do not include 'nbf' (tests only)") |  | ||||||
|     p.add_argument("--include-iat", action="store_true", help="Include 'iat'=now") |  | ||||||
|     # User |  | ||||||
|     p.add_argument("--user-name", default=None, help="User display name") |  | ||||||
|     p.add_argument("--user-email", default=None, help="User email") |  | ||||||
|     p.add_argument("--user-id", default=None, help="User unique ID") |  | ||||||
|     p.add_argument("--avatar", default=None, help="Avatar URL") |  | ||||||
|     p.add_argument("--moderator", action="store_true", help="Grant moderator role via token") |  | ||||||
|     p.add_argument("--moderator-as-string", action="store_true", |  | ||||||
|                    help="Use 'moderator': 'true'/'false' (string) instead of boolean") |  | ||||||
|     # Features (self-hosted with enableFeaturesBasedOnToken) |  | ||||||
|     p.add_argument("--feature-recording", action="store_true", help="Enable 'recording' in context.features") |  | ||||||
|     p.add_argument("--feature-livestreaming", action="store_true", help="Enable 'livestreaming' in context.features") |  | ||||||
|     p.add_argument("--feature-transcription", action="store_true", help="Enable 'transcription' in context.features") |  | ||||||
|     p.add_argument("--feature-sip-in", action="store_true", help="Enable 'sip-inbound-call' in context.features") |  | ||||||
|     p.add_argument("--feature-sip-out", action="store_true", help="Enable 'sip-outbound-call' in context.features") |  | ||||||
|     p.add_argument("--features-all", action="store_true", help="Enable all the features above") |  | ||||||
|     # Overrides / modes |  | ||||||
|     p.add_argument("--aud", default=None, help="Override 'aud' (default: app_id in self-hosted)") |  | ||||||
|     p.add_argument("--iss", default=None, help="Override 'iss' (default: app_id in self-hosted)") |  | ||||||
|     p.add_argument("--jaas", action="store_true", |  | ||||||
|                    help="JAAS mode: aud='jitsi', iss='chat', sub=app_id (ignores --domain for 'sub')") |  | ||||||
|     # Output |  | ||||||
|     p.add_argument("--url", default=None, |  | ||||||
|                    help="If provided (e.g. 'https://meet.example.com/'), prints full join URL with ?jwt=") |  | ||||||
|     p.add_argument("--print-json", action="store_true", help="Print payload JSON to STDERR (debug)") |  | ||||||
| 
 |  | ||||||
|     args = p.parse_args() |  | ||||||
| 
 |  | ||||||
|     # Secret: --secret-file takes precedence |  | ||||||
|     secret = args.secret |  | ||||||
|     if args.secret_file: |  | ||||||
|         if args.secret_file == "-": |  | ||||||
|             secret = sys.stdin.read().strip() |  | ||||||
|         else: |  | ||||||
|             with open(args.secret_file, "r", encoding="utf-8") as fh: |  | ||||||
|                 secret = fh.read().strip() |  | ||||||
|     if not secret: |  | ||||||
|         p.error("You must provide --secret or --secret-file (or --secret-file - for STDIN).") |  | ||||||
| 
 |  | ||||||
|     now = int(time.time()) |  | ||||||
|     exp = None if args.no_exp else (now + args.minutes * 60) |  | ||||||
|     nbf = None if args.no_nbf else (now - max(args.nbf_offset, 0)) |  | ||||||
| 
 |  | ||||||
|     # Header |  | ||||||
|     header = {"typ": "JWT", "alg": "HS256"} |  | ||||||
| 
 |  | ||||||
|     # Base claims by mode |  | ||||||
|     if args.jaas: |  | ||||||
|         aud = "jitsi" |  | ||||||
|         iss = "chat" |  | ||||||
|         sub = args.app_id |  | ||||||
|     else: |  | ||||||
|         if not args.domain: |  | ||||||
|             p.error("--domain is required in self-hosted mode (without --jaas).") |  | ||||||
|         aud = args.aud or args.app_id |  | ||||||
|         iss = args.iss or args.app_id |  | ||||||
|         sub = args.domain |  | ||||||
| 
 |  | ||||||
|     # User / contexto |  | ||||||
|     user = {} |  | ||||||
|     if args.user_id: user["id"] = args.user_id |  | ||||||
|     if args.user_name: user["name"] = args.user_name |  | ||||||
|     if args.user_email: user["email"] = args.user_email |  | ||||||
|     if args.avatar: user["avatar"] = args.avatar |  | ||||||
|     if args.moderator: |  | ||||||
|         if args.moderator_as_string: |  | ||||||
|             user["moderator"] = "true" |  | ||||||
|         else: |  | ||||||
|             user["moderator"] = True |  | ||||||
| 
 |  | ||||||
|     # Features |  | ||||||
|     features = {} |  | ||||||
|     if args.features_all: |  | ||||||
|         features = { |  | ||||||
|             "recording": True, |  | ||||||
|             "livestreaming": True, |  | ||||||
|             "transcription": True, |  | ||||||
|             "sip-inbound-call": True, |  | ||||||
|             "sip-outbound-call": True |  | ||||||
|         } |  | ||||||
|     else: |  | ||||||
|         if args.feature_recording:     features["recording"] = True |  | ||||||
|         if args.feature_livestreaming: features["livestreaming"] = True |  | ||||||
|         if args.feature_transcription: features["transcription"] = True |  | ||||||
|         if args.feature_sip_in:        features["sip-inbound-call"] = True |  | ||||||
|         if args.feature_sip_out:       features["sip-outbound-call"] = True |  | ||||||
| 
 |  | ||||||
|     context = {} |  | ||||||
|     if user: context["user"] = user |  | ||||||
|     if features: context["features"] = features |  | ||||||
| 
 |  | ||||||
|     payload = { |  | ||||||
|         "aud": aud, |  | ||||||
|         "iss": iss, |  | ||||||
|         "sub": sub, |  | ||||||
|         "room": args.room, |  | ||||||
|     } |  | ||||||
|     if context: |  | ||||||
|         payload["context"] = context |  | ||||||
|     if exp is not None: |  | ||||||
|         payload["exp"] = exp |  | ||||||
|     if nbf is not None: |  | ||||||
|         payload["nbf"] = nbf |  | ||||||
|     if args.include_iat: |  | ||||||
|         payload["iat"] = now |  | ||||||
| 
 |  | ||||||
|     # Build JWT manually |  | ||||||
|     signing_input = f"{b64url(json.dumps(header, separators=(',', ':'), ensure_ascii=False).encode())}." \ |  | ||||||
|                     f"{b64url(json.dumps(payload, separators=(',', ':'), ensure_ascii=False).encode())}" |  | ||||||
|     signature = sign_hs256(secret, signing_input) |  | ||||||
|     token = f"{signing_input}.{signature}" |  | ||||||
| 
 |  | ||||||
|     if args.print_json: |  | ||||||
|         print(json.dumps(payload, indent=2, ensure_ascii=False), file=sys.stderr) |  | ||||||
| 
 |  | ||||||
|     if args.url: |  | ||||||
|         base = args.url if args.url.endswith("/") else args.url + "/" |  | ||||||
|         room_path = "" if args.room == "*" else quote(args.room, safe="") |  | ||||||
|         join_url = base + room_path |  | ||||||
|         sep = "&" if "?" in join_url else "?" |  | ||||||
|         print(f"{join_url}{sep}jwt={token}") |  | ||||||
|     else: |  | ||||||
|         print(token) |  | ||||||
| 
 |  | ||||||
| if __name__ == "__main__": |  | ||||||
|     main() |  | ||||||
|  | @ -128,7 +128,6 @@ echo "opcache.revalidate_freq=1" | ||||||
| echo "opcache.validate_timestamps=1" | echo "opcache.validate_timestamps=1" | ||||||
| } >> "$PHP_INI" | } >> "$PHP_INI" | ||||||
| 
 | 
 | ||||||
| echo "# Stick to default system php version to avoid breaking any other system application" |  | ||||||
| update-alternatives --set php           /usr/bin/php"$STABLE_PHP" | update-alternatives --set php           /usr/bin/php"$STABLE_PHP" | ||||||
| update-alternatives --set php-fpm.sock  /run/php/php"$STABLE_PHP"-fpm.sock | update-alternatives --set php-fpm.sock  /run/php/php"$STABLE_PHP"-fpm.sock | ||||||
| update-alternatives --set php-cgi      	/usr/bin/php-cgi"$STABLE_PHP" | update-alternatives --set php-cgi      	/usr/bin/php-cgi"$STABLE_PHP" | ||||||
|  |  | ||||||
		Loading…
	
		Reference in New Issue