From 7f311000aeaa3bae5e0c84272cf1b2e23360aaa2 Mon Sep 17 00:00:00 2001 From: damencho Date: Fri, 15 Nov 2024 13:09:13 -0600 Subject: [PATCH 1/4] feat(recording): Shows notification when you try to start recording too quick. --- lang/main.json | 2 + react/features/recording/middleware.ts | 6 +++ .../prosody-plugins/mod_filter_iq_jibri.lua | 41 +++++++++++++++++-- resources/prosody-plugins/mod_rate_limit.lua | 14 ++----- resources/prosody-plugins/util.lib.lua | 15 +++++++ 5 files changed, 63 insertions(+), 15 deletions(-) diff --git a/lang/main.json b/lang/main.json index ff7c8be21822..2551b8209856 100644 --- a/lang/main.json +++ b/lang/main.json @@ -641,6 +641,7 @@ "on": "Live Streaming started", "onBy": "{{name}} started the live streaming", "pending": "Starting Live Stream…", + "policyError": "You tried to start live stream too quickly. Please try again later!", "serviceName": "Live Streaming service", "sessionAlreadyActive": "This session is already being recorded or live streamed.", "signIn": "Sign in with Google", @@ -1055,6 +1056,7 @@ "onBy": "{{name}} started the recording", "onlyRecordSelf": "Record only my audio and video streams", "pending": "Preparing to record the meeting…", + "policyError": "You tried to start recording too quickly. Please try again later!", "recordAudioAndVideo": "Record audio and video", "recordTranscription": "Record transcription", "saveLocalRecording": "Save recording file locally (Beta)", diff --git a/react/features/recording/middleware.ts b/react/features/recording/middleware.ts index bc95aa5ad1f3..ddc50bdbb057 100644 --- a/react/features/recording/middleware.ts +++ b/react/features/recording/middleware.ts @@ -364,6 +364,12 @@ function _showRecordingErrorNotification(session: any, dispatch: IStore['dispatc titleKey: isStreamMode ? 'liveStreaming.inProgress' : 'recording.inProgress' })); break; + case JitsiMeetJS.constants.recording.error.POLICY_VIOLATION: + dispatch(showRecordingWarning({ + descriptionKey: isStreamMode ? 'liveStreaming.policyError' : 'recording.policyError', + titleKey: isStreamMode ? 'liveStreaming.failedToStart' : 'recording.failedToStart' + })); + break; default: dispatch(showRecordingError({ descriptionKey: isStreamMode diff --git a/resources/prosody-plugins/mod_filter_iq_jibri.lua b/resources/prosody-plugins/mod_filter_iq_jibri.lua index be3d82339de5..842da4348e7c 100644 --- a/resources/prosody-plugins/mod_filter_iq_jibri.lua +++ b/resources/prosody-plugins/mod_filter_iq_jibri.lua @@ -1,11 +1,26 @@ -- This module is enabled under the main virtual host +local cache = require 'util.cache'; +local new_throttle = require 'util.throttle'.create; local st = require "util.stanza"; local jid_bare = require "util.jid".bare; local util = module:require 'util'; local is_feature_allowed = util.is_feature_allowed; +local get_ip = util.get_ip; local get_room_from_jid = util.get_room_from_jid; local room_jid_match_rewrite = util.room_jid_match_rewrite; +local limit_jibri_reach_attempts; +local rates_per_ip; +local function load_config() + limit_jibri_reach_attempts = module:get_option_number("max_number_attempt_per_minute", 3); + -- The size of the cache that saves state for IP addresses + cache_size = module:get_option_number("jibri_rate_limit_cache_size", 10000); + + -- Maps an IP address to a util.throttle which keeps the rate of attempts to reach jibri events from that IP. + rates_per_ip = cache.new(cache_size); +end +load_config(); + -- filters jibri iq in case of requested from jwt authenticated session that -- has features in the user context, but without feature for recording module:hook("pre-iq/full", function(event) @@ -24,10 +39,28 @@ module:hook("pre-iq/full", function(event) session.granted_jitsi_meet_context_features, occupant.role == 'moderator'); - if jibri.attr.action == 'start' and not is_allowed then - module:log('info', 'Filtering jibri start recording, stanza:%s', tostring(stanza)); - session.send(st.error_reply(stanza, 'auth', 'forbidden')); - return true; + if jibri.attr.action == 'start' then + if not is_allowed then + module:log('info', 'Filtering jibri start recording, stanza:%s', tostring(stanza)); + session.send(st.error_reply(stanza, 'auth', 'forbidden')); + return true; + end + + local ip = get_ip(session); + if not rates_per_ip:get(ip) then + rates_per_ip:set(ip, new_throttle(limit_jibri_reach_attempts, 60)); + end + + if not room.jibri_throttle then + room.jibri_throttle = new_throttle(limit_jibri_reach_attempts, 60); + end + + if not rates_per_ip:get(ip):poll(1) or not room.jibri_throttle:poll(1) then + module:log('warn', 'Filtering jibri start recording, ip:%s, room:%s stanza:%s', + ip, room.jid, tostring(stanza)); + session.send(st.error_reply(stanza, 'wait', 'policy-violation')); + return true; + end end end end diff --git a/resources/prosody-plugins/mod_rate_limit.lua b/resources/prosody-plugins/mod_rate_limit.lua index 7d07d53d7113..c0256debcaf0 100644 --- a/resources/prosody-plugins/mod_rate_limit.lua +++ b/resources/prosody-plugins/mod_rate_limit.lua @@ -14,6 +14,7 @@ local ip_util = require "util.ip"; local new_ip = ip_util.new_ip; local match_ip = ip_util.match; local parse_cidr = ip_util.parse_cidr; +local get_ip = module:require "util".get_ip; local config = {}; local limits_resolution = 1; @@ -76,14 +77,6 @@ local function is_whitelisted_host(h) return config.whitelist_hosts:contains(h); end --- Discover real remote IP of a session --- Note: http_server.get_request_from_conn() was added in Prosody 0.12.3, --- this code provides backwards compatibility with older versions -local get_request_from_conn = http_server.get_request_from_conn or function (conn) - local response = conn and conn._http_open_response; - return response and response.request or nil; -end; - -- Add an IP to the set of limied IPs local function limit_ip(ip) module:log("info", "Limiting %s due to login/join rate exceeded.", ip); @@ -192,9 +185,8 @@ local function filter_hook(session) return; end - local request = get_request_from_conn(session.conn); - local ip = request and request.ip or session.ip; - module:log("debug", "New session from %s", ip); + local ip = get_ip(session); + module:log("debug", "New session from %s", ip); if is_whitelisted(ip) or is_whitelisted_host(session.host) then return; end diff --git a/resources/prosody-plugins/util.lib.lua b/resources/prosody-plugins/util.lib.lua index 47ae6e19444f..1878cb75fb26 100644 --- a/resources/prosody-plugins/util.lib.lua +++ b/resources/prosody-plugins/util.lib.lua @@ -1,3 +1,4 @@ +local http_server = require "net.http.server"; local jid = require "util.jid"; local st = require 'util.stanza'; local timer = require "util.timer"; @@ -578,6 +579,19 @@ function respond_iq_result(origin, stanza) })); end +-- Note: http_server.get_request_from_conn() was added in Prosody 0.12.3, +-- this code provides backwards compatibility with older versions +local get_request_from_conn = http_server.get_request_from_conn or function (conn) + local response = conn and conn._http_open_response; + return response and response.request or nil; +end; + +-- Discover real remote IP of a session +function get_ip(session) + local request = get_request_from_conn(session.conn); + return request and request.ip or session.ip; +end + return { OUTBOUND_SIP_JIBRI_PREFIXES = OUTBOUND_SIP_JIBRI_PREFIXES; INBOUND_SIP_JIBRI_PREFIXES = INBOUND_SIP_JIBRI_PREFIXES; @@ -590,6 +604,7 @@ return { is_transcriber_jigasi = is_transcriber_jigasi; is_vpaas = is_vpaas; get_focus_occupant = get_focus_occupant; + get_ip = get_ip; get_room_from_jid = get_room_from_jid; get_room_by_name_and_subdomain = get_room_by_name_and_subdomain; get_sip_jibri_email_prefix = get_sip_jibri_email_prefix; From 83cd4aba8f1529eb4a2d91ca5f26862669c6e99f Mon Sep 17 00:00:00 2001 From: damencho Date: Fri, 15 Nov 2024 13:20:57 -0600 Subject: [PATCH 2/4] squash: separate values ip and room. --- resources/prosody-plugins/mod_filter_iq_jibri.lua | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/resources/prosody-plugins/mod_filter_iq_jibri.lua b/resources/prosody-plugins/mod_filter_iq_jibri.lua index 842da4348e7c..8e299172af36 100644 --- a/resources/prosody-plugins/mod_filter_iq_jibri.lua +++ b/resources/prosody-plugins/mod_filter_iq_jibri.lua @@ -9,10 +9,12 @@ local get_ip = util.get_ip; local get_room_from_jid = util.get_room_from_jid; local room_jid_match_rewrite = util.room_jid_match_rewrite; -local limit_jibri_reach_attempts; +local limit_jibri_reach_ip_attempts; +local limit_jibri_reach_room_attempts; local rates_per_ip; local function load_config() - limit_jibri_reach_attempts = module:get_option_number("max_number_attempt_per_minute", 3); + limit_jibri_reach_ip_attempts = module:get_option_number("max_number_ip_attempts_per_minute", 9); + limit_jibri_reach_room_attempts = module:get_option_number("max_number_room_attempts_per_minute", 3); -- The size of the cache that saves state for IP addresses cache_size = module:get_option_number("jibri_rate_limit_cache_size", 10000); @@ -48,11 +50,11 @@ module:hook("pre-iq/full", function(event) local ip = get_ip(session); if not rates_per_ip:get(ip) then - rates_per_ip:set(ip, new_throttle(limit_jibri_reach_attempts, 60)); + rates_per_ip:set(ip, new_throttle(limit_jibri_reach_ip_attempts, 60)); end if not room.jibri_throttle then - room.jibri_throttle = new_throttle(limit_jibri_reach_attempts, 60); + room.jibri_throttle = new_throttle(limit_jibri_reach_room_attempts, 60); end if not rates_per_ip:get(ip):poll(1) or not room.jibri_throttle:poll(1) then From af53390e2c612d5a0c1031493a23c009f8d2498b Mon Sep 17 00:00:00 2001 From: damencho Date: Fri, 15 Nov 2024 13:38:00 -0600 Subject: [PATCH 3/4] chore(deps) lib-jitsi-meet@latest https://github.com/jitsi/lib-jitsi-meet/compare/v1886.0.0+bc446e99...v1887.0.0+9652999d --- package-lock.json | 10 +++++----- package.json | 2 +- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/package-lock.json b/package-lock.json index 0d992556fc84..d98e50658b67 100644 --- a/package-lock.json +++ b/package-lock.json @@ -62,7 +62,7 @@ "js-md5": "0.6.1", "js-sha512": "0.8.0", "jwt-decode": "2.2.0", - "lib-jitsi-meet": "https://github.com/jitsi/lib-jitsi-meet/releases/download/v1886.0.0+bc446e99/lib-jitsi-meet.tgz", + "lib-jitsi-meet": "https://github.com/jitsi/lib-jitsi-meet/releases/download/v1887.0.0+9652999d/lib-jitsi-meet.tgz", "lodash-es": "4.17.21", "moment": "2.29.4", "moment-duration-format": "2.2.2", @@ -15970,8 +15970,8 @@ }, "node_modules/lib-jitsi-meet": { "version": "0.0.0", - "resolved": "https://github.com/jitsi/lib-jitsi-meet/releases/download/v1886.0.0+bc446e99/lib-jitsi-meet.tgz", - "integrity": "sha512-/XTGm2r3cgKZBMyPS5LHaX9DCZVpY6omWSnh7xkwYtSQtJXIbSKI07Mgmah6o0p8Y3/XsC7xUMfS0qOKn8TlYQ==", + "resolved": "https://github.com/jitsi/lib-jitsi-meet/releases/download/v1887.0.0+9652999d/lib-jitsi-meet.tgz", + "integrity": "sha512-WQ4sF0B+K0V+nqTr8ldFuQkvNxp+2DTUIu+ix0cE6L+NsS1yorRus/mG5lMlaQU9So0W83fnsl38kIj1RuymRQ==", "hasInstallScript": true, "license": "Apache-2.0", "dependencies": { @@ -35316,8 +35316,8 @@ } }, "lib-jitsi-meet": { - "version": "https://github.com/jitsi/lib-jitsi-meet/releases/download/v1886.0.0+bc446e99/lib-jitsi-meet.tgz", - "integrity": "sha512-/XTGm2r3cgKZBMyPS5LHaX9DCZVpY6omWSnh7xkwYtSQtJXIbSKI07Mgmah6o0p8Y3/XsC7xUMfS0qOKn8TlYQ==", + "version": "https://github.com/jitsi/lib-jitsi-meet/releases/download/v1887.0.0+9652999d/lib-jitsi-meet.tgz", + "integrity": "sha512-WQ4sF0B+K0V+nqTr8ldFuQkvNxp+2DTUIu+ix0cE6L+NsS1yorRus/mG5lMlaQU9So0W83fnsl38kIj1RuymRQ==", "requires": { "@jitsi/js-utils": "2.2.1", "@jitsi/logger": "2.0.2", diff --git a/package.json b/package.json index 3b4b8f6b63a6..ab612566d597 100644 --- a/package.json +++ b/package.json @@ -68,7 +68,7 @@ "js-md5": "0.6.1", "js-sha512": "0.8.0", "jwt-decode": "2.2.0", - "lib-jitsi-meet": "https://github.com/jitsi/lib-jitsi-meet/releases/download/v1886.0.0+bc446e99/lib-jitsi-meet.tgz", + "lib-jitsi-meet": "https://github.com/jitsi/lib-jitsi-meet/releases/download/v1887.0.0+9652999d/lib-jitsi-meet.tgz", "lodash-es": "4.17.21", "moment": "2.29.4", "moment-duration-format": "2.2.2", From 5d7d463bff1dc8c43c2ff877251c342ce51fdfdc Mon Sep 17 00:00:00 2001 From: damencho Date: Fri, 15 Nov 2024 14:36:34 -0600 Subject: [PATCH 4/4] squash: text adjust --- lang/main.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lang/main.json b/lang/main.json index 2551b8209856..3f7624cf5519 100644 --- a/lang/main.json +++ b/lang/main.json @@ -641,7 +641,7 @@ "on": "Live Streaming started", "onBy": "{{name}} started the live streaming", "pending": "Starting Live Stream…", - "policyError": "You tried to start live stream too quickly. Please try again later!", + "policyError": "You tried to start a live stream too quickly. Please try again later!", "serviceName": "Live Streaming service", "sessionAlreadyActive": "This session is already being recorded or live streamed.", "signIn": "Sign in with Google", @@ -1056,7 +1056,7 @@ "onBy": "{{name}} started the recording", "onlyRecordSelf": "Record only my audio and video streams", "pending": "Preparing to record the meeting…", - "policyError": "You tried to start recording too quickly. Please try again later!", + "policyError": "You tried to start a recording too quickly. Please try again later!", "recordAudioAndVideo": "Record audio and video", "recordTranscription": "Record transcription", "saveLocalRecording": "Save recording file locally (Beta)",