From bdc09c1b0705767ec14706001ff3ec6443e87a00 Mon Sep 17 00:00:00 2001 From: zhongjin Date: Tue, 14 Feb 2023 23:15:32 +0800 Subject: [PATCH] first commit --- .gitignore | 17 + .gitmodules | 18 + CHANGELOG.md | 105 +++ LICENSE | 165 +++++ README.md | 61 ++ conf/.gitignore | 4 + conf/dialplan_public/.gitkeep | 0 conf/dialplan_public/example.xml | 17 + conf/sip_profiles/.gitkeep | 0 conf/sip_profiles/example.xml | 12 + docker-compose.tmpl.yml | 485 ++++++++++++++ docs/behind-nat.md | 14 + docs/development.md | 86 +++ docs/existing-web-server.md | 82 +++ docs/network-config.md | 35 + docs/upgrading.md | 22 + mod/apps-akka/Dockerfile | 34 + mod/apps-akka/bbb-apps-akka.conf | 14 + mod/apps-akka/entrypoint.sh | 9 + mod/apps-akka/logback.xml | 16 + mod/base-java/Dockerfile | 24 + mod/bbb-pads/Dockerfile | 18 + mod/bbb-pads/entrypoint.sh | 15 + mod/bbb-web/Dockerfile | 69 ++ mod/bbb-web/bbb-web.properties | 22 + mod/bbb-web/entrypoint.sh | 29 + mod/bbb-web/logback.xml | 28 + mod/bbb-web/mocked-ps | 8 + mod/bbb-web/office-convert.sh | 30 + mod/bbb-web/turn-stun-servers.xml | 37 ++ mod/coturn/entrypoint.sh | 31 + mod/coturn/turnserver.conf | 73 +++ mod/etherpad/Dockerfile | 28 + mod/etherpad/entrypoint.sh | 5 + mod/etherpad/etherpad-export.sh | 12 + mod/etherpad/settings.json | 607 ++++++++++++++++++ mod/freeswitch/Dockerfile | 66 ++ .../conf/autoload_configs/acl.conf.xml | 49 ++ .../autoload_configs/conference.conf.xml.tmpl | 285 ++++++++ .../autoload_configs/event_socket.conf.xml | 10 + .../conf/autoload_configs/modules.conf.xml | 30 + mod/freeswitch/conf/dialplan/public.xml | 43 ++ .../conf/sip_profiles/external-ipv6.xml | 113 ++++ mod/freeswitch/conf/sip_profiles/external.xml | 128 ++++ mod/freeswitch/conf/vars.xml.tmpl | 398 ++++++++++++ mod/freeswitch/entrypoint.sh | 52 ++ mod/fsesl-akka/Dockerfile | 38 ++ mod/fsesl-akka/bbb-fsesl-akka.conf | 18 + mod/fsesl-akka/entrypoint.sh | 9 + mod/fsesl-akka/logback.xml | 18 + mod/html5/Dockerfile | 34 + mod/html5/bbb-html5.yml | 24 + mod/html5/entrypoint.sh | 43 ++ mod/https/site-ipv4only.conf | 33 + mod/https/site.conf | 33 + mod/jodconverter/Dockerfile | 17 + mod/mongo/init-replica.sh | 26 + mod/mongo/mongod.conf | 33 + mod/nginx/Dockerfile | 22 + mod/nginx/bbb/bbb-exporter.nginx | 4 + mod/nginx/bbb/bbb-html5.nginx | 40 ++ mod/nginx/bbb/demo.nginx | 26 + mod/nginx/bbb/greenlight.nginx | 34 + mod/nginx/bbb/learning-dashboard.nginx | 9 + mod/nginx/bbb/loadbalancer.nginx | 4 + mod/nginx/bbb/notes-playback.nginx | 4 + mod/nginx/bbb/notes.nginx | 77 +++ mod/nginx/bbb/podcast.nginx | 22 + mod/nginx/bbb/presentation-slides.nginx | 53 ++ mod/nginx/bbb/presentation.nginx | 28 + mod/nginx/bbb/sip.nginx | 15 + mod/nginx/bbb/verto.nginx | 10 + mod/nginx/bbb/web.nginx | 180 ++++++ mod/nginx/bbb/webhooks.nginx | 9 + mod/nginx/bbb/webrtc-sfu.nginx | 22 + mod/nginx/bigbluebutton | 40 ++ mod/nginx/default.pdf | Bin 0 -> 42107 bytes mod/nginx/nginx.conf | 32 + mod/periodic/Dockerfile | 10 + mod/periodic/bbb-remove-old-recordings | 41 ++ mod/periodic/bbb-resync-freeswitch | 5 + mod/periodic/entrypoint.sh | 23 + mod/recordings/Dockerfile | 111 ++++ mod/recordings/bbb-web.properties | 1 + mod/recordings/bigbluebutton.yml | 59 ++ mod/recordings/entrypoint.sh | 12 + mod/recordings/supervisord.conf | 34 + mod/webhooks/Dockerfile | 25 + mod/webhooks/entrypoint.sh | 17 + mod/webrtc-sfu/Dockerfile | 35 + sample.env | 339 ++++++++++ scripts/bbb-record | 20 + scripts/checkout-submodules | 27 + scripts/fs_cli | 12 + scripts/generate-compose | 68 ++ scripts/publish | 51 ++ scripts/setup | 211 ++++++ scripts/upgrade | 38 ++ scripts/upgrade-and-build | 49 ++ tags.env | 27 + 100 files changed, 5578 insertions(+) create mode 100644 .gitignore create mode 100644 .gitmodules create mode 100644 CHANGELOG.md create mode 100644 LICENSE create mode 100644 README.md create mode 100644 conf/.gitignore create mode 100644 conf/dialplan_public/.gitkeep create mode 100644 conf/dialplan_public/example.xml create mode 100644 conf/sip_profiles/.gitkeep create mode 100644 conf/sip_profiles/example.xml create mode 100644 docker-compose.tmpl.yml create mode 100644 docs/behind-nat.md create mode 100644 docs/development.md create mode 100644 docs/existing-web-server.md create mode 100644 docs/network-config.md create mode 100644 docs/upgrading.md create mode 100644 mod/apps-akka/Dockerfile create mode 100644 mod/apps-akka/bbb-apps-akka.conf create mode 100755 mod/apps-akka/entrypoint.sh create mode 100644 mod/apps-akka/logback.xml create mode 100644 mod/base-java/Dockerfile create mode 100644 mod/bbb-pads/Dockerfile create mode 100755 mod/bbb-pads/entrypoint.sh create mode 100644 mod/bbb-web/Dockerfile create mode 100644 mod/bbb-web/bbb-web.properties create mode 100755 mod/bbb-web/entrypoint.sh create mode 100644 mod/bbb-web/logback.xml create mode 100755 mod/bbb-web/mocked-ps create mode 100755 mod/bbb-web/office-convert.sh create mode 100644 mod/bbb-web/turn-stun-servers.xml create mode 100755 mod/coturn/entrypoint.sh create mode 100644 mod/coturn/turnserver.conf create mode 100644 mod/etherpad/Dockerfile create mode 100755 mod/etherpad/entrypoint.sh create mode 100755 mod/etherpad/etherpad-export.sh create mode 100644 mod/etherpad/settings.json create mode 100644 mod/freeswitch/Dockerfile create mode 100644 mod/freeswitch/conf/autoload_configs/acl.conf.xml create mode 100644 mod/freeswitch/conf/autoload_configs/conference.conf.xml.tmpl create mode 100644 mod/freeswitch/conf/autoload_configs/event_socket.conf.xml create mode 100644 mod/freeswitch/conf/autoload_configs/modules.conf.xml create mode 100644 mod/freeswitch/conf/dialplan/public.xml create mode 100644 mod/freeswitch/conf/sip_profiles/external-ipv6.xml create mode 100644 mod/freeswitch/conf/sip_profiles/external.xml create mode 100644 mod/freeswitch/conf/vars.xml.tmpl create mode 100755 mod/freeswitch/entrypoint.sh create mode 100644 mod/fsesl-akka/Dockerfile create mode 100644 mod/fsesl-akka/bbb-fsesl-akka.conf create mode 100755 mod/fsesl-akka/entrypoint.sh create mode 100644 mod/fsesl-akka/logback.xml create mode 100644 mod/html5/Dockerfile create mode 100644 mod/html5/bbb-html5.yml create mode 100755 mod/html5/entrypoint.sh create mode 100644 mod/https/site-ipv4only.conf create mode 100644 mod/https/site.conf create mode 100644 mod/jodconverter/Dockerfile create mode 100755 mod/mongo/init-replica.sh create mode 100644 mod/mongo/mongod.conf create mode 100644 mod/nginx/Dockerfile create mode 100644 mod/nginx/bbb/bbb-exporter.nginx create mode 100644 mod/nginx/bbb/bbb-html5.nginx create mode 100644 mod/nginx/bbb/demo.nginx create mode 100644 mod/nginx/bbb/greenlight.nginx create mode 100644 mod/nginx/bbb/learning-dashboard.nginx create mode 100644 mod/nginx/bbb/loadbalancer.nginx create mode 100644 mod/nginx/bbb/notes-playback.nginx create mode 100644 mod/nginx/bbb/notes.nginx create mode 100644 mod/nginx/bbb/podcast.nginx create mode 100644 mod/nginx/bbb/presentation-slides.nginx create mode 100644 mod/nginx/bbb/presentation.nginx create mode 100644 mod/nginx/bbb/sip.nginx create mode 100644 mod/nginx/bbb/verto.nginx create mode 100755 mod/nginx/bbb/web.nginx create mode 100644 mod/nginx/bbb/webhooks.nginx create mode 100644 mod/nginx/bbb/webrtc-sfu.nginx create mode 100644 mod/nginx/bigbluebutton create mode 100644 mod/nginx/default.pdf create mode 100644 mod/nginx/nginx.conf create mode 100644 mod/periodic/Dockerfile create mode 100755 mod/periodic/bbb-remove-old-recordings create mode 100755 mod/periodic/bbb-resync-freeswitch create mode 100755 mod/periodic/entrypoint.sh create mode 100644 mod/recordings/Dockerfile create mode 100644 mod/recordings/bbb-web.properties create mode 100644 mod/recordings/bigbluebutton.yml create mode 100755 mod/recordings/entrypoint.sh create mode 100644 mod/recordings/supervisord.conf create mode 100644 mod/webhooks/Dockerfile create mode 100755 mod/webhooks/entrypoint.sh create mode 100644 mod/webrtc-sfu/Dockerfile create mode 100644 sample.env create mode 100755 scripts/bbb-record create mode 100755 scripts/checkout-submodules create mode 100755 scripts/fs_cli create mode 100755 scripts/generate-compose create mode 100755 scripts/publish create mode 100755 scripts/setup create mode 100755 scripts/upgrade create mode 100755 scripts/upgrade-and-build create mode 100644 tags.env diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..edf61fd --- /dev/null +++ b/.gitignore @@ -0,0 +1,17 @@ +docker-compose.yml +docker-compose.override.yml + +# IDEA generated +.idea + +# OS generated +.DS_Store +.AppleDouble + +# Tmp files +*.swp +*.log + +# App generated +.env +postgres-data diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..cc00452 --- /dev/null +++ b/.gitmodules @@ -0,0 +1,18 @@ +[submodule "bbb-webrtc-sfu"] + path = mod/webrtc-sfu/bbb-webrtc-sfu + url = https://github.com/bigbluebutton/bbb-webrtc-sfu.git +[submodule "mod/etherpad/bbb-etherpad-skin"] + path = mod/etherpad/bbb-etherpad-skin + url = https://github.com/alangecker/bbb-etherpad-skin +[submodule "mod/etherpad/bbb-etherpad-plugin"] + path = mod/etherpad/bbb-etherpad-plugin + url = https://github.com/alangecker/bbb-etherpad-plugin +[submodule "mod/bbb-pads/bbb-pads"] + path = mod/bbb-pads/bbb-pads + url = https://github.com/bigbluebutton/bbb-pads +[submodule "mod/webhooks/bbb-webhooks"] + path = mod/webhooks/bbb-webhooks + url = https://github.com/bigbluebutton/bbb-webhooks +[submodule "mod/nginx/bbb-playback"] + path = mod/nginx/bbb-playback + url = https://github.com/bigbluebutton/bbb-playback diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..d3e4644 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,105 @@ +# Changelog + +## Unreleased +- BBB 2.5.8 @alangecker + + +## Release v2.5.0 (2022-06-10) +- BigBlueButton v2.5 @alangecker [#207](https://github.com/bigbluebutton/docker/pull/207) +- central `tags.env` file with the tag names of most BBB components @alangecker +- Usage of [official docker build images](https://gitlab.senfcall.de/senfcall-public/docker-bbb-build) for building @alangecker +- publish docker images @alangecker [#174](https://github.com/bigbluebutton/docker/issues/174) +- etherpad: enforce bbb-pads session handling @pedrobmarin [#211](https://github.com/bigbluebutton/docker/pull/211) +- etherpad: avoid icons overlapping @pedrobmarin [#210](https://github.com/bigbluebutton/docker/pull/210) +- fix recordings which include presentation polls @lightweight [#205](https://github.com/bigbluebutton/docker/pull/205) + +## Release v2.4.5 (2022-03-24) +- Applied BBB v2.4.5 changes @alangecker +- New mute & unmute sounds by senfcall +- Update etherpad @pedrobmarin [#202](https://github.com/bigbluebutton/docker/pull/202) +- Use own freeswitch mirror instead of the official login-only one @alangecker [#203](https://github.com/bigbluebutton/docker/issues/203) +- Ignore docker-compose.override.yml @dorianim [#183](https://github.com/bigbluebutton/docker/pull/183) + +## Release v2.4.4 (2022-02-23) +- Applied v2.4.4 changes @alangecker [#195](https://github.com/bigbluebutton/docker/pull/195) +- Update Russian sound announcement examples @lexuzieel [#196](https://github.com/bigbluebutton/docker/pull/196) +- fix for presentation slides not displayed if they contain type 3 fonts @rottaran [#191](https://github.com/bigbluebutton/docker/pull/191) + +## Release v2.4.0 (2021-12-29) +- BigBlueButton v2.4 @alangecker [#159](https://github.com/bigbluebutton/docker/pull/159) +- **Breaking change:** change nginx port from `8080` to `48087`. see [upgrade note](docs/upgrading.md) @alangeker [#133](https://github.com/bigbluebutton/docker/issues/133) +- Enable optimization for Prometheus Exporter when recording is enabled @omidmaldar [#161](https://github.com/bigbluebutton/docker/pull/161) +- Automatically remove old recordings after N days @omidmaldar [#162](https://github.com/bigbluebutton/docker/pull/162) + + +## Release v2.3.14-1 (2021-10-06) +- Applied changes v2.3.5-v2.3.14 @alangecker +- updated wget to not use proxies [#143](https://github.com/bigbluebutton/docker/pull/143) @mghadam +- fixed sed delimiter for CERTPATH and KEYPATH [#144](https://github.com/bigbluebutton/docker/pull/144) @mghadam +- https_proxy: fix setting of ALLOWED_DOMAINS [#145](https://github.com/bigbluebutton/docker/pull/145) @clandmeter +- coturn: expose ENABLE_HTTPS_PROXY env variable [#146](https://github.com/bigbluebutton/docker/pull/146) [#156](https://github.com/bigbluebutton/docker/pull/156) @clandmeter @omidmaldar + +## Release v2.3.4-1 (2021-06-22) #131 +- Applied v2.3.4 changes [#130](https://github.com/bigbluebutton/docker/pull/130) @alangecker +- Reintegrate turn with default ports and support for external certificates [#126](https://github.com/bigbluebutton/docker/pull/126) @cjhille +- Fix freeswitch package names for languages with uppercase characters in the path [#119](https://github.com/bigbluebutton/docker/pull/119) @lexuzieel +- Exclude CLIENT_TITLE when generating compose file [#118](https://github.com/bigbluebutton/docker/pull/118) @bb +- Fix for preuploaded presentations not working [#116](https://github.com/bigbluebutton/docker/pull/116) @manfred-w +- Add POSTGRESQL_SECRET as environement variable [#111](https://github.com/bigbluebutton/docker/pull/111) @caminsha + + +## Release v2.3.0 +- :tada: **BigBlueButton 2.3** including all its changes +- Template based generation of docker-compose.yml [2.2.x#71](https://github.com/alangecker/bigbluebutton-docker/pull/71) [2.2.x#42](https://github.com/alangecker/bigbluebutton-docker/issues/42) @trickert76 @alangecker +- Removal of `core` and all dependencies on the bigbluebutton ubuntu repository. Seperate container for `bbb-web`, `fsesl-akka` and `apps-akka` [2.2.x#26](https://github.com/alangecker/bigbluebutton-docker/issues/26) @alangecker +- Fix recordings for Moodle BBB plugin: [2.2.x#110](https://github.com/alangecker/bigbluebutton-docker/pull/110) @danjesus +- Fixed recordings container restart setting [2.2.x#109](https://github.com/alangecker/bigbluebutton-docker/pull/109) @manfred-w +- Option for freeswitch language [2.2.x#85](https://github.com/alangecker/bigbluebutton-docker/pull/85) @alangecker @Daedalus3 +- Disabled integrated coturn [#73](https://github.com/bigbluebutton/docker/issues/73) + +## Release v2.2.31-1 (2020-12-23) #84 +- Applied v2.2.31 changes @alangecker +- Fix when presentation after recording unable to delete and change access rights #82 #63 @cardinalit +- Enable cameraQualityThresholds by default + +## Release v2.2.30-1 (2020-12-01) #79 +- Applied v2.2.30 changes @alangecker +- Applied v2.2.29 changes @alangecker +- Fix bug due to unnecessary port forward #81 @trickert76 @alangecker + +## Release v2.2.28-1 (2020-10-22) #67 +- Applied v2.2.28 changes @alangecker +- Etherpad skin & plugin #69 @alangecker +- Updated `development.md` docs (example config & note about issue #66) @alangecker +- Allow setting the breakout room limit @alangecker + +## Release v2.2.27-2 (2020-10-16) +- Increase proxy timeout to avoid aborting websocket connections @alangecker +- Added a changelog + +## Release v2.2.27-1 (2020-10-14) +- Applied BBB v2.2.27 changes https://github.com/bigbluebutton/bigbluebutton/releases/tag/v2.2.27 @alangecker +- Upgrade docker base images (etherpad and bigbluebutton-exporter) @alangecker + +## Release v2.2.26-1 (2020-09-29) +- Applied changes from BBB v2.2.24 to v2.2.26 #58 #60 @alangecker + +## Release v2.2.23-1 (2020-09-06) +- :tada: Recording #16 by @artemtech and @alangecker +- v2.2.23 changes by @alangecker +- sip_profile extension field #54 by @yksflip +- Remove greenlight container name #49 by @alangecker + +## Hotfix (2020-08-15) +- Allow imagemagick to convert to pdf/svg #51 #52 @alangecker + +## Release v2.2.22-1 (2020-08-12) #50 +- v2.2.22 changes by @alangecker +- Disable freeswitch logfiles inside containers + +## Release v2.2.21-1 (2020-7-18) +- Changes for v2.2.21 #44 @alangecker +- expose more BBB settings in .env file #34 @cjhille +- IPv6 Support #32 @alangecker +- Development Mode & Instructions #39 @alangecker +- Prometheus Exporter Integration #40 @alangecker diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..65c5ca8 --- /dev/null +++ b/LICENSE @@ -0,0 +1,165 @@ + GNU LESSER GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + + This version of the GNU Lesser General Public License incorporates +the terms and conditions of version 3 of the GNU General Public +License, supplemented by the additional permissions listed below. + + 0. Additional Definitions. + + As used herein, "this License" refers to version 3 of the GNU Lesser +General Public License, and the "GNU GPL" refers to version 3 of the GNU +General Public License. + + "The Library" refers to a covered work governed by this License, +other than an Application or a Combined Work as defined below. + + An "Application" is any work that makes use of an interface provided +by the Library, but which is not otherwise based on the Library. +Defining a subclass of a class defined by the Library is deemed a mode +of using an interface provided by the Library. + + A "Combined Work" is a work produced by combining or linking an +Application with the Library. The particular version of the Library +with which the Combined Work was made is also called the "Linked +Version". + + The "Minimal Corresponding Source" for a Combined Work means the +Corresponding Source for the Combined Work, excluding any source code +for portions of the Combined Work that, considered in isolation, are +based on the Application, and not on the Linked Version. + + The "Corresponding Application Code" for a Combined Work means the +object code and/or source code for the Application, including any data +and utility programs needed for reproducing the Combined Work from the +Application, but excluding the System Libraries of the Combined Work. + + 1. Exception to Section 3 of the GNU GPL. + + You may convey a covered work under sections 3 and 4 of this License +without being bound by section 3 of the GNU GPL. + + 2. Conveying Modified Versions. + + If you modify a copy of the Library, and, in your modifications, a +facility refers to a function or data to be supplied by an Application +that uses the facility (other than as an argument passed when the +facility is invoked), then you may convey a copy of the modified +version: + + a) under this License, provided that you make a good faith effort to + ensure that, in the event an Application does not supply the + function or data, the facility still operates, and performs + whatever part of its purpose remains meaningful, or + + b) under the GNU GPL, with none of the additional permissions of + this License applicable to that copy. + + 3. Object Code Incorporating Material from Library Header Files. + + The object code form of an Application may incorporate material from +a header file that is part of the Library. You may convey such object +code under terms of your choice, provided that, if the incorporated +material is not limited to numerical parameters, data structure +layouts and accessors, or small macros, inline functions and templates +(ten or fewer lines in length), you do both of the following: + + a) Give prominent notice with each copy of the object code that the + Library is used in it and that the Library and its use are + covered by this License. + + b) Accompany the object code with a copy of the GNU GPL and this license + document. + + 4. Combined Works. + + You may convey a Combined Work under terms of your choice that, +taken together, effectively do not restrict modification of the +portions of the Library contained in the Combined Work and reverse +engineering for debugging such modifications, if you also do each of +the following: + + a) Give prominent notice with each copy of the Combined Work that + the Library is used in it and that the Library and its use are + covered by this License. + + b) Accompany the Combined Work with a copy of the GNU GPL and this license + document. + + c) For a Combined Work that displays copyright notices during + execution, include the copyright notice for the Library among + these notices, as well as a reference directing the user to the + copies of the GNU GPL and this license document. + + d) Do one of the following: + + 0) Convey the Minimal Corresponding Source under the terms of this + License, and the Corresponding Application Code in a form + suitable for, and under terms that permit, the user to + recombine or relink the Application with a modified version of + the Linked Version to produce a modified Combined Work, in the + manner specified by section 6 of the GNU GPL for conveying + Corresponding Source. + + 1) Use a suitable shared library mechanism for linking with the + Library. A suitable mechanism is one that (a) uses at run time + a copy of the Library already present on the user's computer + system, and (b) will operate properly with a modified version + of the Library that is interface-compatible with the Linked + Version. + + e) Provide Installation Information, but only if you would otherwise + be required to provide such information under section 6 of the + GNU GPL, and only to the extent that such information is + necessary to install and execute a modified version of the + Combined Work produced by recombining or relinking the + Application with a modified version of the Linked Version. (If + you use option 4d0, the Installation Information must accompany + the Minimal Corresponding Source and Corresponding Application + Code. If you use option 4d1, you must provide the Installation + Information in the manner specified by section 6 of the GNU GPL + for conveying Corresponding Source.) + + 5. Combined Libraries. + + You may place library facilities that are a work based on the +Library side by side in a single library together with other library +facilities that are not Applications and are not covered by this +License, and convey such a combined library under terms of your +choice, if you do both of the following: + + a) Accompany the combined library with a copy of the same work based + on the Library, uncombined with any other library facilities, + conveyed under the terms of this License. + + b) Give prominent notice with the combined library that part of it + is a work based on the Library, and explaining where to find the + accompanying uncombined form of the same work. + + 6. Revised Versions of the GNU Lesser General Public License. + + The Free Software Foundation may publish revised and/or new versions +of the GNU Lesser General Public License from time to time. Such new +versions will be similar in spirit to the present version, but may +differ in detail to address new problems or concerns. + + Each version is given a distinguishing version number. If the +Library as you received it specifies that a certain numbered version +of the GNU Lesser General Public License "or any later version" +applies to it, you have the option of following the terms and +conditions either of that published version or of any later version +published by the Free Software Foundation. If the Library as you +received it does not specify a version number of the GNU Lesser +General Public License, you may choose any version of the GNU Lesser +General Public License ever published by the Free Software Foundation. + + If the Library as you received it specifies that a proxy can decide +whether future versions of the GNU Lesser General Public License shall +apply, that proxy's public statement of acceptance of any version is +permanent authorization for you to choose that version for the +Library. diff --git a/README.md b/README.md new file mode 100644 index 0000000..d8ef860 --- /dev/null +++ b/README.md @@ -0,0 +1,61 @@ +bbb-docker-banner + + +# 📦 BigBlueButton 2.5 Docker + +Version: 2.5.8 | [Changelog](CHANGELOG.md) | [Issues](https://github.com/bigbluebutton/docker/issues) + +## Features +- Easy installation +- Greenlight included +- TURN server included +- Fully automated HTTPS certificates +- Full IPv6 support +- Runs on any major linux distributon (Debian, Ubuntu, CentOS,...) + +## What is not implemented yet +- bbb-lti + +## Install +1. Install docker-ce & docker-compose + 1. follow instructions + * Debian: https://docs.docker.com/engine/install/debian/ + * CentOS: https://docs.docker.com/engine/install/centos/ + * Fedora: https://docs.docker.com/engine/install/fedora/ + * Ubuntu: https://docs.docker.com/engine/install/ubuntu/ + 2. Ensure docker works with `$ docker run hello-world` + 3. Install docker-compose: https://docs.docker.com/compose/install/ + 4. Ensure docker-compose works and that you use a version ≥ 1.28 : `$ docker-compose --version` +2. Clone this repository + ```sh + $ git clone --recurse-submodules https://github.com/bigbluebutton/docker.git bbb-docker + $ cd bbb-docker + + # use the more stable main branch (sometimes older) + $ git checkout main + ``` +3. Run setup: + ```bash + $ ./scripts/setup + ``` +4. (optional) Make additional configuration adjustments + ```bash + $ nano .env + # always recreate the docker-compose.yml file after making any changes + $ ./scripts/generate-compose + ``` +5. Start containers: + ```bash + $ docker-compose up -d + ``` +6. If you use greenlight, you can create an admin account with: + ```bash + $ docker-compose exec greenlight bundle exec rake admin:create + ``` + +## Further How-To's +- [Upgrading](docs/upgrading.md) +- [Running behind NAT](docs/behind-nat.md) +- [BBB-Docker Development](docs/development.md) +- [Integration into an existing web server](docs/existing-web-server.md) + diff --git a/conf/.gitignore b/conf/.gitignore new file mode 100644 index 0000000..55a0124 --- /dev/null +++ b/conf/.gitignore @@ -0,0 +1,4 @@ +*/* +!.gitkeep +!example.xml +*.pdf \ No newline at end of file diff --git a/conf/dialplan_public/.gitkeep b/conf/dialplan_public/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/conf/dialplan_public/example.xml b/conf/dialplan_public/example.xml new file mode 100644 index 0000000..a7b7429 --- /dev/null +++ b/conf/dialplan_public/example.xml @@ -0,0 +1,17 @@ + diff --git a/conf/sip_profiles/.gitkeep b/conf/sip_profiles/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/conf/sip_profiles/example.xml b/conf/sip_profiles/example.xml new file mode 100644 index 0000000..513d245 --- /dev/null +++ b/conf/sip_profiles/example.xml @@ -0,0 +1,12 @@ + + + diff --git a/docker-compose.tmpl.yml b/docker-compose.tmpl.yml new file mode 100644 index 0000000..a2180bb --- /dev/null +++ b/docker-compose.tmpl.yml @@ -0,0 +1,485 @@ +{{/* if you read this, you can ignore the following lines */}} +# auto generated by ./scripts/generate +# don't edit this directly. +{{/* -------- */}} + +version: '3.6' + +# html5 templates +x-html5-backend: &html5backend + build: + context: mod/html5 + args: + BBB_BUILD_TAG: {{ .Env.BBB_BUILD_TAG }} + TAG_HTML5: {{ .Env.TAG_HTML5 }} + image: alangecker/bbb-docker-html5:{{ .Env.TAG_HTML5 }} + restart: unless-stopped + depends_on: + - redis + - mongodb + - etherpad + environment: &html5backend-env + DOMAIN: ${DOMAIN} + CLIENT_TITLE: ${CLIENT_TITLE} + LISTEN_ONLY_MODE: ${LISTEN_ONLY_MODE:-true} + DISABLE_ECHO_TEST: ${DISABLE_ECHO_TEST:-false} + AUTO_SHARE_WEBCAM: ${AUTO_SHARE_WEBCAM:-false} + DISABLE_VIDEO_PREVIEW: ${DISABLE_VIDEO_PREVIEW:-false} + CHAT_ENABLED: ${CHAT_ENABLED:-true} + CHAT_START_CLOSED: ${CHAT_START_CLOSED:-false} + BREAKOUTROOM_LIMIT: ${BREAKOUTROOM_LIMIT:-8} + DEV_MODE: ${DEV_MODE:-} + BBB_HTML5_ROLE: backend + +x-html5-frontend: &html5frontend + <<: *html5backend + volumes: + - html5-static:/html5-static:rw + environment: &html5frontend-env + <<: *html5backend-env + BBB_HTML5_ROLE: frontend +# ========================= + +services: + bbb-web: + build: + context: mod/bbb-web + args: + BBB_BUILD_TAG: {{ .Env.BBB_BUILD_TAG }} + TAG_COMMON_MESSAGE: {{ .Env.TAG_COMMON_MESSAGE }} + TAG_BBB_WEB: {{ .Env.TAG_BBB_WEB }} + image: alangecker/bbb-docker-web:{{ .Env.TAG_BBB_WEB }} + restart: unless-stopped + depends_on: + - redis + - etherpad + healthcheck: + test: wget --no-proxy --no-verbose --tries=1 --spider http://10.7.7.2:8090/bigbluebutton/api || exit 1 + start_period: 2m + environment: + DEV_MODE: ${DEV_MODE:-} + DOMAIN: ${DOMAIN} + ENABLE_RECORDING: ${ENABLE_RECORDING:-false} + SHARED_SECRET: ${SHARED_SECRET} + WELCOME_MESSAGE: ${WELCOME_MESSAGE:-} + WELCOME_FOOTER: ${WELCOME_FOOTER} + STUN_SERVER: stun:${STUN_IP}:${STUN_PORT} + TURN_SERVER: ${TURN_SERVER:-} + TURN_SECRET: ${TURN_SECRET:-} + ENABLE_LEARNING_DASHBOARD: ${ENABLE_LEARNING_DASHBOARD:-true} + NUMBER_OF_BACKEND_NODEJS_PROCESSES: {{ .Env.NUMBER_OF_BACKEND_NODEJS_PROCESSES }} + volumes: + - bigbluebutton:/var/bigbluebutton + - vol-freeswitch:/var/freeswitch/meetings + networks: + bbb-net: + ipv4_address: 10.7.7.2 + + +{{ range $i := loop 0 (atoi .Env.NUMBER_OF_BACKEND_NODEJS_PROCESSES) }} + html5-backend-{{ add $i 1 }}: + <<: *html5backend + environment: + <<: *html5backend-env + INSTANCE_ID: {{ add $i 1 }} + PORT: {{ add 4000 $i }} + networks: + bbb-net: + ipv4_address: 10.7.7.{{ add 100 $i }} +{{end}} + +{{ range $i := loop 0 (atoi .Env.NUMBER_OF_FRONTEND_NODEJS_PROCESSES) }} + html5-frontend-{{ add $i 1 }}: + <<: *html5frontend + environment: + <<: *html5frontend-env + INSTANCE_ID: {{ add $i 1 }} + PORT: {{ add 4100 $i }} + networks: + bbb-net: + ipv4_address: 10.7.7.{{ add 200 $i }} +{{end}} + + + freeswitch: + container_name: bbb-freeswitch + build: + context: mod/freeswitch + args: + TAG_FS_CONFIG: {{ .Env.TAG_FS_CONFIG }} + image: alangecker/bbb-docker-freeswitch:{{ .Env.TAG_FS_CONFIG }} + restart: unless-stopped + cap_add: + - IPC_LOCK + - NET_ADMIN + - NET_RAW + - NET_BROADCAST + - SYS_NICE + - SYS_RESOURCE + environment: + DOMAIN: ${DOMAIN} + EXTERNAL_IPv4: ${EXTERNAL_IPv4} + EXTERNAL_IPv6: ${EXTERNAL_IPv6:-::1} + SIP_IP_ALLOWLIST: ${SIP_IP_ALLOWLIST:-} + DISABLE_SOUND_MUTED: ${DISABLE_SOUND_MUTED:-false} + DISABLE_SOUND_ALONE: ${DISABLE_SOUND_ALONE:-false} + SOUNDS_LANGUAGE: ${SOUNDS_LANGUAGE:-en-us-callie} + ESL_PASSWORD: ${FSESL_PASSWORD:-ClueCon} + volumes: + - ./conf/sip_profiles:/etc/freeswitch/sip_profiles/external + - ./conf/dialplan_public:/etc/freeswitch/dialplan/public_docker + - vol-freeswitch:/var/freeswitch/meetings + network_mode: host + + nginx: + build: + context: mod/nginx + args: + TAG_LEARNING_DASHBOARD: {{ .Env.TAG_LEARNING_DASHBOARD }} + image: alangecker/bbb-docker-nginx:1.21-{{ .Env.TAG_PLAYBACK }}-{{ .Env.TAG_LEARNING_DASHBOARD }} + restart: unless-stopped + depends_on: + - etherpad + - webrtc-sfu + - html5-backend-1 + volumes: + - bigbluebutton:/var/bigbluebutton + - html5-static:/html5-static:ro + - ${DEFAULT_PRESENTATION:-/dev/null}:/www/default.pdf + network_mode: host + extra_hosts: + - "host.docker.internal:10.7.7.1" + - "bbb-web:10.7.7.2" + - "etherpad:10.7.7.4" + - "webrtc-sfu:10.7.7.1" + - "html5:10.7.7.11" + + etherpad: + build: mod/etherpad + image: alangecker/bbb-docker-etherpad:1.8.18-3 + restart: unless-stopped + depends_on: + - redis + environment: + ETHERPAD_API_KEY: ${ETHERPAD_API_KEY} + networks: + bbb-net: + ipv4_address: 10.7.7.4 + + bbb-pads: + build: mod/bbb-pads + image: alangecker/bbb-docker-pads:{{ .Env.TAG_PADS }} + restart: unless-stopped + depends_on: + - redis + - etherpad + environment: + ETHERPAD_API_KEY: ${ETHERPAD_API_KEY} + networks: + bbb-net: + ipv4_address: 10.7.7.18 + + redis: + image: redis:7.0-alpine + restart: unless-stopped + healthcheck: + test: ["CMD", "redis-cli", "ping"] + interval: 1s + timeout: 3s + retries: 30 + networks: + bbb-net: + ipv4_address: 10.7.7.5 + + mongodb: + container_name: bbb-mongodb + image: mongo:4.4 + restart: unless-stopped + volumes: + - ./mod/mongo/mongod.conf:/etc/mongod.conf + - ./mod/mongo/init-replica.sh:/docker-entrypoint-initdb.d/init-replica.sh + tmpfs: + - /data/configdb + - /data/db + command: mongod --config /etc/mongod.conf --oplogSize 8 --replSet rs0 --noauth + healthcheck: + test: bash -c "if mongo --eval 'quit(db.runCommand({ ping':' 1 }).ok ? 0 ':' 2)'; then exit 0; fi; exit 1;" + networks: + bbb-net: + ipv4_address: 10.7.7.6 + + # TODO: remove as soon as not required anymore by webrtc-sfu + kurento: + image: kurento/kurento-media-server:6.18 + restart: unless-stopped + network_mode: host + volumes: + - vol-kurento:/var/kurento + + webrtc-sfu: + build: + context: mod/webrtc-sfu + args: + BBB_BUILD_TAG: {{ .Env.BBB_BUILD_TAG }} + image: alangecker/bbb-docker-webrtc-sfu:{{ .Env.TAG_WEBRTC_SFU }} + restart: unless-stopped + depends_on: + - redis + - freeswitch + environment: + CLIENT_HOST: 10.7.7.1 + REDIS_HOST: 10.7.7.5 + FREESWITCH_IP: 10.7.7.1 + FREESWITCH_SIP_IP: ${EXTERNAL_IPv4} + ESL_IP: 10.7.7.1 + ESL_PASSWORD: ${FSESL_PASSWORD:-ClueCon} + # TODO: add mediasoup IPv6 + # TODO: can listen to 0.0.0.0 for nat support? https://github.com/versatica/mediasoup/issues/487 + {{ if .Env.EXTERNAL_IPv6 }} + MS_WEBRTC_LISTEN_IPS: '[{"ip":"{{ .Env.EXTERNAL_IPv6 }}", "announcedIp":"{{ .Env.EXTERNAL_IPv6 }}"}, {"ip":"${EXTERNAL_IPv4}", "announcedIp":"${EXTERNAL_IPv4}"}]' + {{else}} + MS_WEBRTC_LISTEN_IPS: '[{"ip":"${EXTERNAL_IPv4}", "announcedIp":"${EXTERNAL_IPv4}"}]' + {{end}} + MS_RTP_LISTEN_IP: '{"ip":"0.0.0.0", "announcedIp":"${EXTERNAL_IPv4}"}' + volumes: + - vol-mediasoup:/var/mediasoup + tmpfs: + - /var/log/bbb-webrtc-sfu + network_mode: host + + fsesl-akka: + build: + context: mod/fsesl-akka + args: + BBB_BUILD_TAG: {{ .Env.BBB_BUILD_TAG }} + TAG_COMMON_MESSAGE: {{ .Env.TAG_COMMON_MESSAGE }} + TAG_FSESL_AKKA: {{ .Env.TAG_FSESL_AKKA }} + image: alangecker/bbb-docker-fsesl-akka:{{ .Env.TAG_FSESL_AKKA }} + restart: unless-stopped + depends_on: + - redis + - freeswitch + environment: + FSESL_PASSWORD: ${FSESL_PASSWORD:-ClueCon} + networks: + bbb-net: + ipv4_address: 10.7.7.14 + + apps-akka: + build: + context: mod/apps-akka + args: + BBB_BUILD_TAG: {{ .Env.BBB_BUILD_TAG }} + TAG_COMMON_MESSAGE: {{ .Env.TAG_COMMON_MESSAGE }} + TAG_APPS_AKKA: {{ .Env.TAG_APPS_AKKA }} + image: alangecker/bbb-docker-apps-akka:{{ .Env.TAG_APPS_AKKA }} + restart: unless-stopped + depends_on: + - redis + environment: + DOMAIN: ${DOMAIN} + SHARED_SECRET: ${SHARED_SECRET} + volumes: + - vol-freeswitch:/var/freeswitch/meetings + networks: + bbb-net: + ipv4_address: 10.7.7.15 + + jodconverter: + build: mod/jodconverter + image: alangecker/bbb-docker-jodconverter:latest + security_opt: + - 'no-new-privileges:true' + restart: unless-stopped + tmpfs: + - /tmp + deploy: + resources: + limits: + memory: 512M + networks: + bbb-net: + ipv4_address: 10.7.7.20 + + periodic: + build: mod/periodic + image: alangecker/bbb-docker-periodic:v2.5.0-rc.1 + restart: unless-stopped + depends_on: + - mongodb + volumes: + - /var/run/docker.sock:/var/run/docker.sock + - bigbluebutton:/var/bigbluebutton + - vol-mediasoup:/var/mediasoup + tmpfs: + - /var/log/bigbluebutton + environment: + ENABLE_RECORDING: ${ENABLE_RECORDING} + REMOVE_OLD_RECORDING: ${REMOVE_OLD_RECORDING} + RECORDING_MAX_AGE_DAYS: ${RECORDING_MAX_AGE_DAYS} + networks: + bbb-net: + ipv4_address: 10.7.7.12 + +{{ if isTrue .Env.ENABLE_RECORDING }} + # recordings + recordings: + build: + context: mod/recordings + args: + BBB_BUILD_TAG: {{ .Env.BBB_BUILD_TAG }} + TAG_RECORDINGS: {{ .Env.TAG_RECORDINGS }} + image: alangecker/bbb-docker-recordings:{{ .Env.TAG_RECORDINGS }} + restart: unless-stopped + depends_on: + - redis + - bbb-pads + environment: + DOMAIN: ${DOMAIN} + volumes: + - bigbluebutton:/var/bigbluebutton + - vol-freeswitch:/var/freeswitch/meetings + - vol-mediasoup:/var/mediasoup + - vol-kurento:/var/kurento + tmpfs: + - /var/log/bigbluebutton + - /tmp + networks: + bbb-net: + ipv4_address: 10.7.7.16 +{{end}} + +{{ if isTrue .Env.ENABLE_WEBHOOKS }} + # webhooks + webhooks: + build: mod/webhooks + image: alangecker/bbb-docker-webhooks:{{ .Env.TAG_WEBHOOKS }} + restart: unless-stopped + environment: + DOMAIN: ${DOMAIN} + SHARED_SECRET: ${SHARED_SECRET} + depends_on: + - redis + networks: + bbb-net: + ipv4_address: 10.7.7.17 +{{end}} + +{{ if isTrue .Env.ENABLE_HTTPS_PROXY }} + # https + https_proxy: + image: valian/docker-nginx-auto-ssl + restart: unless-stopped + volumes: + - ssl_data:/etc/resty-auto-ssl + {{ if .Env.EXTERNAL_IPv6 }} + - ./mod/https/site.conf:/etc/nginx/conf.d/bbb-docker.conf + {{else}} + - ./mod/https/site-ipv4only.conf:/etc/nginx/conf.d/bbb-docker.conf + {{end}} + environment: + {{ if isTrue .Env.DEV_MODE }} + ALLOWED_DOMAINS: "" + {{else}} + ALLOWED_DOMAINS: ${DOMAIN} + {{end}} + network_mode: host +{{end}} + +{{ if isTrue .Env.ENABLE_COTURN }} + # coturn + coturn: + image: coturn/coturn:4.6-alpine + restart: unless-stopped + command: + - "--external-ip=${EXTERNAL_IPv4}/${EXTERNAL_IPv4}" + - "--external-ip=${EXTERNAL_IPv6:-::1}/${EXTERNAL_IPv6:-::1}" + - "--static-auth-secret=${TURN_SECRET}" + volumes: + {{ if isTrue .Env.ENABLE_HTTPS_PROXY }} + - ssl_data:/etc/resty-auto-ssl + {{else}} + - ${COTURN_TLS_CERT_PATH}:/tmp/cert.pem + - ${COTURN_TLS_KEY_PATH}:/tmp/key.pem + {{end}} + - ./mod/coturn/entrypoint.sh:/usr/local/bin/docker-entrypoint.sh + - ./mod/coturn/turnserver.conf:/etc/coturn/turnserver.conf + environment: + ENABLE_HTTPS_PROXY: + user: root + network_mode: host +{{end}} + + +{{ if isTrue .Env.ENABLE_GREENLIGHT }} + # greenlight + greenlight: + image: bigbluebutton/greenlight:v2 + restart: unless-stopped + env_file: .env + environment: + DB_ADAPTER: postgresql + DB_HOST: postgres + DB_NAME: greenlight + DB_USERNAME: postgres + DB_PASSWORD: ${POSTGRESQL_SECRET:-password} + {{ if isTrue .Env.DEV_MODE }} + BIGBLUEBUTTON_ENDPOINT: http://10.7.7.1:48087/bigbluebutton/api/ + {{else}} + BIGBLUEBUTTON_ENDPOINT: https://${DOMAIN}/bigbluebutton/api/ + {{end}} + BIGBLUEBUTTON_SECRET: ${SHARED_SECRET} + SECRET_KEY_BASE: ${RAILS_SECRET} + ports: + - 10.7.7.1:5000:80 + postgres: + image: postgres:12-alpine + restart: unless-stopped + environment: + POSTGRES_DB: greenlight + POSTGRES_USER: postgres + POSTGRES_PASSWORD: ${POSTGRESQL_SECRET:-password} + healthcheck: + test: ["CMD-SHELL", "pg_isready -U postgres"] + interval: 10s + timeout: 5s + retries: 5 + volumes: + - ./postgres-data:/var/lib/postgresql/data +{{end}} + +{{ if isTrue .Env.ENABLE_PROMETHEUS_EXPORTER }} + # prometheus + prometheus-exporter: + image: greenstatic/bigbluebutton-exporter:latest + restart: unless-stopped + environment: + API_BASE_URL: http://10.7.7.1:48087/bigbluebutton/api/ + API_SECRET: ${SHARED_SECRET} + RECORDINGS_METRICS_READ_FROM_DISK: "${ENABLE_PROMETHEUS_EXPORTER_OPTIMIZATION:-false}" + networks: + bbb-net: + ipv4_address: 10.7.7.33 + {{ if isTrue .Env.ENABLE_PROMETHEUS_EXPORTER_OPTIMIZATION }} + volumes: + - bigbluebutton:/var/bigbluebutton:ro + {{end}} +{{end}} + + +volumes: + bigbluebutton: + vol-freeswitch: + vol-kurento: + vol-mediasoup: + html5-static: +{{ if isTrue .Env.ENABLE_HTTPS_PROXY }} + ssl_data: +{{end}} + +networks: + bbb-net: + ipam: + driver: default + config: + - subnet: "10.7.7.0/24" diff --git a/docs/behind-nat.md b/docs/behind-nat.md new file mode 100644 index 0000000..3a8d1e6 --- /dev/null +++ b/docs/behind-nat.md @@ -0,0 +1,14 @@ +# Note if you use a Firewall / NAT +Kurento binds somehow always to the external IP instead of the local one or `0.0.0.0`. For that reason you need to add your external IP to your interface. + +#### Temporary way (until next reboot) +``` +$ ip addr add 144.76.97.34/32 dev ens3 +``` + +#### Permanent way +Specific to your linux distribution. Use a search engine of your choice. ;) + +## Ports +Also don't forget to forward all necassary ports listed in https://docs.bigbluebutton.org/admin/configure-firewall.html + diff --git a/docs/development.md b/docs/development.md new file mode 100644 index 0000000..d9dcb09 --- /dev/null +++ b/docs/development.md @@ -0,0 +1,86 @@ +# bbb-docker Development + +## Basics +normally people start BBB with the pre-built docker images, but for developing you need to build them by yourself. For that you need to ensure that the submodules are also checked out: + +```sh +$ git submodule update --init +``` + + +## Running +you can run bbb-docker locally without any certificate issues with following `.env` configurations: + +``` +DEV_MODE=true + +ENABLE_HTTPS_PROXY=true +#ENABLE_COTURN=true +#ENABLE_GREENLIGHT=true +#ENABLE_WEBHOOKS=true +#ENABLE_PROMETHEUS_EXPORTER=true +#ENABLE_RECORDING=true + +DOMAIN=10.7.7.1 +EXTERNAL_IPv4=10.7.7.1 +STUN_IP=216.93.246.18 +STUN_PORT=3478 +TURN_SERVER=turns:localhost:5349?transport=tcp + +TURN_SECRET=SuperTurnSecret +SHARED_SECRET=SuperSecret +ETHERPAD_API_KEY=SuperEtherpadKey +RAILS_SECRET=SuperRailsSecret + +# ==================================== +# CUSTOMIZATION +# ==================================== + +[... add rest of sample.env here ...] +``` + +- regenerate `docker-compose.yml` \ + `$ ./scripts/generate-compose` +- build the images \ + `$ docker-compose build` +- you can than start it with \ + `$ docker-compose up -d` +- view the logs with \ + `$ docker-compose logs -f` +- and access the API via \ + https://mconf.github.io/api-mate/#server=https://10.7.7.1/bigbluebutton/api&sharedSecret=SuperSecret + * At some point your browser will warn you about an invalid certificate, but you can press _"Accept the Risk and Continue" / "Proceed to 10.7.7.1 (unsafe)"_ + + +## Notes +- Joining a room via Greenlight currently leads to a "401 session not found" error (see https://github.com/alangecker/bigbluebutton-docker/issues/66). Use the API Mate instead + +## Changes +- After doing some changes you usually must... + - recreate `docker-compose.yml` \ + `$ ./scripts/generate-compose` + * rebuild the image(s): \ + `$ docker-compose build [containername]` + * restart changes image(s): \ + `$ docker-compose up -d` + + +## How to do create a new update for a newer BBB release? +This always consists out of following steps +1. **Get an understanding about changes that happened and find out what changes to bbb-docker that require.** \ + * main source for that are the release notes in https://github.com/bigbluebutton/bigbluebutton/releases +2. **Apply these changes to this project.** + * Often you only need to update the TAGS in `tags.env` + * make sure only to switch to a newer tag if there were changes made avoid creating new (partialy big) images unnecessarily + * Also update submodules to the new state. + * List of all submodules `git submodule` + * for the main submodules you can use `./scripts/checkout-submodules` to checkout the tags specified in `tags.env` + +3. Test everything (with firefox **and** chromium/chrome) + * Audio + * Video + * Presentation upload + * Shared Notes +4. Create a `CHANGELOG.md` entry +5. Create a Pull Request +6. Receive big thanks from @alangecker diff --git a/docs/existing-web-server.md b/docs/existing-web-server.md new file mode 100644 index 0000000..adf2ff8 --- /dev/null +++ b/docs/existing-web-server.md @@ -0,0 +1,82 @@ +# How to integrate into an existing Web server setup + +Since the non-dockerized version of BigBlueButton has [many requirements](https://docs.bigbluebutton.org/2.2/install.html#minimum-server-requirements), such as a specific Ubuntu version (16.04) as well as ports 80/443 not being in use by other applications, and considering that [a "clean" server dedicated for BigBlueButton is recommended](https://docs.bigbluebutton.org/2.2/install.html#before-you-install), you may enjoy the benefits of this dockerized version in order to run BigBlueButton on a server that is not completely dedicated to this software, on which a Web server may be already in use. + +You could dedicate a virtual host to BigBlueButton, allowing external access to it through a reverse proxy. + +> **Note.** The automatic HTTPS Proxy is not needed if you are going to run BigBlueButton behind a reverse proxy; in that case, you should be able to enable SSL for the virtual host you are going to dedicate to BigBlueButton, using your Web server features. Please notice that it will not be possible to install and use the integrated TURN server, since it requires the automatic HTTPS Proxy to be installed; therefore, if a TURN server is required, you should install and configure it by yourself. You can set BigBlueButton to use a TURN server by uncommenting and adjusting `TURN_SERVER` and `TURN_SECRET` in the `.env` file, which is created after completion of the setup script. + +## Installation +1. Install BigBlueButton Docker [as explained above](#install). While running the setup script, please choose `n` when you're asked the following question: `Should an automatic HTTPS Proxy be included? (y/n)`. +2. Now all the required Docker containers should be running. BigBlueButton listens to port 48087. Create a virtual host by which BigBlueButton will be publicly accessible (in this case, let's assume the following server name for the virtual host: `bbb.example.com`). Enable SSL for the new _https_ virtual host. Make sure that the SSL certificate you will be using is signed by a CA (Certificate Authority). You could generate an SSL certificate for free using Let's Encrypt. It is suggested to add some directives to the _http_ virtual host `bbb.example.com` to redirect all requests to the _https_ one. + +At this point, choose one of the following sections according to which Web server you're running ([Apache](#integration-with-apache)). + +Eventually, BigBlueButton should be publicly accessible on `https://bbb.example.com/`. If you chose to install Greenlight, then the previous URL should allow you to open its home page. The APIs will be accessible through `https://bbb.example.com/bigbluebutton/`. + +## Integration with nginx +1. Add the following directives to the _https_ virtual host `bbb.example.com` +``` +map $http_upgrade $connection_upgrade { + default upgrade; + '' close; +} +map $remote_addr $endpoint_addr { + "~:" [::1]; + default 127.0.0.1; +} + +server { + listen 443 ssl http2 default_server; + listen [::]:443 ssl http2 default_server; + server_name bbb.example.com; + + ssl_certificate /etc/letsencrypt/live/bbb.example.com/fullchain.pem; + ssl_certificate_key /etc/letsencrypt/live/bbb.example.com/privkey.pem; + + access_log /var/log/nginx/bigbluebutton.access.log; + error_log /var/log/nginx/bigbluebutton.error.log; + + location / { + proxy_http_version 1.1; + proxy_pass http://$endpoint_addr:48087; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + proxy_set_header Upgrade $http_upgrade; + proxy_set_header Connection $connection_upgrade; + proxy_cache_bypass $http_upgrade; + } +} + +``` +2. Restart nginx +``` +service nginx restart +``` + +## Integration with Apache +1. Make sure that the following Apache modules are in use: `proxy`, `rewrite`, `proxy_http`, `proxy_wstunnel`. On _apache2_, the following command activates these modules, whenever they are not already enabled: +``` +sudo a2enmod proxy rewrite proxy_http proxy_wstunnel +``` +2. Add the following directives to the _https_ virtual host `bbb.example.com`: +``` +ProxyPreserveHost On + +RewriteEngine On +RewriteCond %{HTTP:UPGRADE} ^WebSocket$ [NC,OR] +RewriteCond %{HTTP:CONNECTION} ^Upgrade$ [NC] +RewriteRule .* ws://127.0.0.1:48087%{REQUEST_URI} [P,QSA,L] + + + Require all granted + ProxyPass http://127.0.0.1:48087/ + ProxyPassReverse http://127.0.0.1:48087/ + +``` +3. Restart Apache: +``` +service apache2 restart +``` diff --git a/docs/network-config.md b/docs/network-config.md new file mode 100644 index 0000000..8382fb9 --- /dev/null +++ b/docs/network-config.md @@ -0,0 +1,35 @@ + +## Network Configuration +Services as configured. +|Service | Network | IP address | Other Option | +--- | --- | --- | --- +| bbb-web | bbb-net | 10.7.7.2 | +| bbb-pads | bbb-net | 10.7.7.18 | +| html5-backend-{{$i}} | bbb-net | 10.7.7.{{add 100 $i}}| Port {{ add 4000 $i }} +| html5-frontend-{{$i}}| bbb-net | 10.7.7.{{add 200 $i}}| Port {{ add 4100 $i }} +| freeswitch| network_mode: host | | +| nginx | network_mode: host| | extra_hosts:
- "host.docker.internal:10.7.7.1"
- "core:10.7.7.2"
- "etherpad:10.7.7.4"
- "webrtc-sfu:10.7.7.10"
- "html5:10.7.7.11" +| etherpad | bbb-net | 10.7.7.4| +| redis | bbb-net | 10.7.7.5| +| mongodb | bbb-net | 10.7.7.6| +| kurento | network-mode: host | | +| webrtc-sfu | bbb-net | | network_mode: host +| fsesl-akka | bbb-net | 10.7.7.14 | +| apps-akka | bbb-net | 10.7.7.15 | +| libreoffice | bbb-net | 10.7.7.7 | +| periodic | bbb-net | 10.7.7.12 | +| recordings | bbb-net | 10.7.7.16 | +| webhooks | bbb-net | 10.7.7.17 | +| https_proxy | bbb-net | |network_mode: host +| coturn | network_mode: host | | +| greenlight | | | ports: 10.7.7.1:5000:80 +| prometheus | bbb-net | 10.7.7.33 | + +```yml +networks: + bbb-net: + ipam: + driver: default + config: + - subnet: "10.7.7.0/24" +``` diff --git a/docs/upgrading.md b/docs/upgrading.md new file mode 100644 index 0000000..80261ad --- /dev/null +++ b/docs/upgrading.md @@ -0,0 +1,22 @@ +# How To Upgrade bbb-docker + +### Upgrading `v2.3.x` -> `v2.4.x` +*Breaking change:* The nginx port changes from `8080` to the less common port `48087`, to avoid port conflicts (see [#133](https://github.com/bigbluebutton/docker/issues/133)). If you use an reverse proxy not included in this repo, ensure to update your config accordingly! + +apart from that follow the guide below. + +### within `v2.4.x` or `v2.3.x` +#### Backup +if you use greenlight, create a database backup first +```bash +docker exec -t docker_postgres_1 pg_dumpall -c -U postgres > /root/greenlight_`date +%d-%m-%Y"_"%H_%M_%S`.sql +``` + +#### Upgrading +```bash +# upgrade! +./scripts/upgrade + +# restart updated services +docker-compose up -d +``` diff --git a/mod/apps-akka/Dockerfile b/mod/apps-akka/Dockerfile new file mode 100644 index 0000000..3ad648e --- /dev/null +++ b/mod/apps-akka/Dockerfile @@ -0,0 +1,34 @@ +ARG BBB_BUILD_TAG +FROM gitlab.senfcall.de:5050/senfcall-public/docker-bbb-build:$BBB_BUILD_TAG AS builder + +ARG TAG_COMMON_MESSAGE + +# download bbb-common-message +RUN svn checkout https://github.com/bigbluebutton/bigbluebutton/tags/$TAG_COMMON_MESSAGE/bbb-common-message /bbb-common-message \ + && cd /bbb-common-message \ + && ./deploy.sh \ + && rm -rf /bbb-common-message + +# =================================================== + +ARG TAG_APPS_AKKA + +RUN svn checkout https://github.com/bigbluebutton/bigbluebutton/tags/$TAG_APPS_AKKA/akka-bbb-apps /source \ + && rm -rf /source/.svn + +# compile and unzip bin +RUN cd /source \ + && sbt universal:packageBin \ + && unzip /source/target/universal/bbb-apps-akka-0.0.4.zip -d / + +# =================================================== + +FROM alangecker/bbb-docker-base-java + +COPY --from=builder /bbb-apps-akka-0.0.4 /bbb-apps-akka +COPY bbb-apps-akka.conf /etc/bigbluebutton/bbb-apps-akka.conf.tmpl +COPY logback.xml /bbb-apps-akka/conf/logback.xml +COPY entrypoint.sh /entrypoint.sh + +USER bigbluebutton +ENTRYPOINT /entrypoint.sh diff --git a/mod/apps-akka/bbb-apps-akka.conf b/mod/apps-akka/bbb-apps-akka.conf new file mode 100644 index 0000000..d05cebc --- /dev/null +++ b/mod/apps-akka/bbb-apps-akka.conf @@ -0,0 +1,14 @@ +// include default config from upstream +include "/bbb-apps-akka/conf/application.conf" + +redis { + host="10.7.7.5" +} + +services { + bbbWebAPI="https://DOMAIN/bigbluebutton/api" + sharedSecret="SHARED_SECRET" +} +http { + interface = "0.0.0.0" +} \ No newline at end of file diff --git a/mod/apps-akka/entrypoint.sh b/mod/apps-akka/entrypoint.sh new file mode 100755 index 0000000..3938a7c --- /dev/null +++ b/mod/apps-akka/entrypoint.sh @@ -0,0 +1,9 @@ +#!/bin/sh -e + +TARGET=/etc/bigbluebutton/bbb-apps-akka.conf +cp /etc/bigbluebutton/bbb-apps-akka.conf.tmpl $TARGET +sed -i "s/DOMAIN/$DOMAIN/" $TARGET +sed -i "s/SHARED_SECRET/$SHARED_SECRET/" $TARGET + +cd /bbb-apps-akka +/bbb-apps-akka/bin/bbb-apps-akka \ No newline at end of file diff --git a/mod/apps-akka/logback.xml b/mod/apps-akka/logback.xml new file mode 100644 index 0000000..0d9c004 --- /dev/null +++ b/mod/apps-akka/logback.xml @@ -0,0 +1,16 @@ + + + + + %d{"yyyy-MM-dd'T'HH:mm:ss.SSSXXX"} %-5level %logger{35} - %msg%n + + + + + + + + + + + diff --git a/mod/base-java/Dockerfile b/mod/base-java/Dockerfile new file mode 100644 index 0000000..571126c --- /dev/null +++ b/mod/base-java/Dockerfile @@ -0,0 +1,24 @@ +FROM openjdk:11-jre-slim-bullseye + +RUN apt-get update && apt-get install -y \ + wget unzip gosu locales \ + imagemagick xpdf-utils curl \ + && sed -i '/en_US.UTF-8/s/^# //g' /etc/locale.gen && locale-gen + +ENV LANG en_US.UTF-8 +ENV LANGUAGE en_US:en +ENV LC_ALL en_US.UTF-8 + +# add user & group +RUN groupadd -g 998 bigbluebutton \ + && useradd -m -u 998 -g bigbluebutton bigbluebutton \ + && mkdir /etc/bigbluebutton \ + && chown bigbluebutton:bigbluebutton /etc/bigbluebutton + +# add dockerize +ENV DOCKERIZE_VERSION v0.6.1 +RUN wget -q https://github.com/jwilder/dockerize/releases/download/$DOCKERIZE_VERSION/dockerize-linux-amd64-$DOCKERIZE_VERSION.tar.gz \ + && tar -C /usr/local/bin -xzvf dockerize-linux-amd64-$DOCKERIZE_VERSION.tar.gz \ + && rm dockerize-linux-amd64-$DOCKERIZE_VERSION.tar.gz + + diff --git a/mod/bbb-pads/Dockerfile b/mod/bbb-pads/Dockerfile new file mode 100644 index 0000000..fdabe95 --- /dev/null +++ b/mod/bbb-pads/Dockerfile @@ -0,0 +1,18 @@ +FROM node:14.19.1-bullseye-slim AS builder + +COPY ./bbb-pads /bbb-pads +RUN cd /bbb-pads && rm -r .git && npm install --production + + +RUN chmod 777 /bbb-pads/config +# ------------------------------ + +FROM node:14.19.1-bullseye-slim + +RUN apt update && apt install -y jq moreutils \ + && useradd --uid 2003 --user-group bbb-pads + +COPY --from=builder /bbb-pads /bbb-pads +USER bbb-pads +COPY entrypoint.sh /entrypoint.sh +ENTRYPOINT /entrypoint.sh \ No newline at end of file diff --git a/mod/bbb-pads/entrypoint.sh b/mod/bbb-pads/entrypoint.sh new file mode 100755 index 0000000..9f93722 --- /dev/null +++ b/mod/bbb-pads/entrypoint.sh @@ -0,0 +1,15 @@ +#!/bin/sh +set -e + +TARGET=/bbb-pads/config/settings.json + +cp /bbb-pads/config/settings.json.template $TARGET +sed -i "s/ETHERPAD_API_KEY/\"$ETHERPAD_API_KEY\"/g" $TARGET +jq '.etherpad.host = "etherpad"' $TARGET | sponge $TARGET +jq '.express.host = "0.0.0.0"' $TARGET | sponge $TARGET +jq '.redis.host = "redis"' $TARGET | sponge $TARGET + + +cd /bbb-pads +export NODE_ENV=production +npm start \ No newline at end of file diff --git a/mod/bbb-web/Dockerfile b/mod/bbb-web/Dockerfile new file mode 100644 index 0000000..b27e0cc --- /dev/null +++ b/mod/bbb-web/Dockerfile @@ -0,0 +1,69 @@ +ARG BBB_BUILD_TAG +FROM gitlab.senfcall.de:5050/senfcall-public/docker-bbb-build:$BBB_BUILD_TAG AS builder + +ARG TAG_COMMON_MESSAGE + +# download bbb-common-message +RUN svn checkout https://github.com/bigbluebutton/bigbluebutton/tags/$TAG_COMMON_MESSAGE/bbb-common-message /bbb-common-message \ + && cd /bbb-common-message \ + && ./deploy.sh \ + && rm -rf /bbb-common-message + +# =================================================== + +ARG TAG_BBB_WEB + +# download bbb-common-web +RUN svn checkout https://github.com/bigbluebutton/bigbluebutton/tags/$TAG_BBB_WEB/bbb-common-web /bbb-common-web \ + && rm -rf /bbb-common-message/.svn + +# compile bbb-common-web +RUN cd /bbb-common-web \ + && ./deploy.sh + +# download bbb-web +RUN svn checkout https://github.com/bigbluebutton/bigbluebutton/tags/$TAG_BBB_WEB/bigbluebutton-web /bbb-web \ + && rm -rf /bbb-web/.svn + +# compile bbb-web +RUN cd /bbb-web && grails assemble + +# compile pres-checker +RUN cd /bbb-web/pres-checker && gradle resolveDeps + +# extract .war +RUN unzip -q /bbb-web/build/libs/bigbluebutton-0.10.0.war -d /dist + + +# =================================================== +FROM alangecker/bbb-docker-base-java + +# add blank presentation files and allow conversation to pdf/svg +RUN mkdir -p /usr/share/bigbluebutton/blank \ + && cd /usr/share/bigbluebutton/blank \ + && wget \ + https://raw.githubusercontent.com/bigbluebutton/bigbluebutton/v2.4.0/bigbluebutton-config/slides/blank-svg.svg \ + https://raw.githubusercontent.com/bigbluebutton/bigbluebutton/v2.4.0/bigbluebutton-config/slides/blank-thumb.png \ + https://raw.githubusercontent.com/bigbluebutton/bigbluebutton/v2.4.0/bigbluebutton-config/slides/blank-presentation.pdf \ + https://raw.githubusercontent.com/bigbluebutton/bigbluebutton/v2.4.0/bigbluebutton-config/slides/blank-png.png \ + && sed -i 's///g' /etc/ImageMagick-6/policy.xml \ + && sed -i '/potrace/d' /etc/ImageMagick-6/delegates.xml + + +# get bbb-web +COPY --from=builder /dist /usr/share/bbb-web + +# get pres-checker +COPY --from=builder /bbb-web/pres-checker/lib /usr/share/prescheck/lib +COPY --from=builder /bbb-web/pres-checker/run.sh /usr/share/prescheck/prescheck.sh + +COPY mocked-ps /usr/bin/ps + +# add entrypoint and templates +COPY entrypoint.sh /entrypoint.sh +COPY bbb-web.properties /etc/bigbluebutton/bbb-web.properties.tmpl +COPY turn-stun-servers.xml /usr/share/bbb-web/WEB-INF/classes/spring/turn-stun-servers.xml.tmpl +COPY logback.xml /usr/share/bbb-web/WEB-INF/classes/logback.xml +COPY office-convert.sh /usr/share/bbb-libreoffice-conversion/convert.sh + +ENTRYPOINT ["/entrypoint.sh"] diff --git a/mod/bbb-web/bbb-web.properties b/mod/bbb-web/bbb-web.properties new file mode 100644 index 0000000..0f500d2 --- /dev/null +++ b/mod/bbb-web/bbb-web.properties @@ -0,0 +1,22 @@ +defaultWelcomeMessage={{ .Env.WELCOME_MESSAGE }} +defaultWelcomeMessageFooter={{ .Env.WELCOME_FOOTER }} + +{{ if isTrue .Env.ENABLE_RECORDING }} +disableRecordingDefault=false +{{ else }} +disableRecordingDefault=true +{{ end }} + +bigbluebutton.web.serverURL=https://{{ .Env.DOMAIN }} + +securitySalt={{ .Env.SHARED_SECRET }} + +redisHost=redis + +{{ if isTrue .Env.DEV_MODE }} +beans.presentationService.defaultUploadedPresentation=https://test.bigbluebutton.org/default.pdf +{{else}} +beans.presentationService.defaultUploadedPresentation=${bigbluebutton.web.serverURL}/default.pdf +{{end}} + +learningDashboardEnabled={{ .Env.ENABLE_LEARNING_DASHBOARD }} \ No newline at end of file diff --git a/mod/bbb-web/entrypoint.sh b/mod/bbb-web/entrypoint.sh new file mode 100755 index 0000000..cf65053 --- /dev/null +++ b/mod/bbb-web/entrypoint.sh @@ -0,0 +1,29 @@ +#!/bin/bash +set -e + +# create recording directory structure if it doesn't exist yet +mkdir -p /var/bigbluebutton/recording/raw +mkdir -p /var/bigbluebutton/recording/process +mkdir -p /var/bigbluebutton/recording/publish +mkdir -p /var/bigbluebutton/recording/status/recorded +mkdir -p /var/bigbluebutton/recording/status/archived +mkdir -p /var/bigbluebutton/recording/status/processed +mkdir -p /var/bigbluebutton/recording/status/sanity +mkdir -p /var/bigbluebutton/recording/status/ended +mkdir -p /var/bigbluebutton/recording/status/published +mkdir -p /var/bigbluebutton/captions/inbox +mkdir -p /var/bigbluebutton/published +mkdir -p /var/bigbluebutton/published/notes +mkdir -p /var/bigbluebutton/deleted +mkdir -p /var/bigbluebutton/unpublished +chown -R bigbluebutton:bigbluebutton /var/bigbluebutton + +echo "$NUMBER_OF_BACKEND_NODEJS_PROCESSES" > /tmp/NUMBER_OF_BACKEND_NODEJS_PROCESSES + +cd /usr/share/bbb-web/ +dockerize \ + -template /etc/bigbluebutton/bbb-web.properties.tmpl:/etc/bigbluebutton/bbb-web.properties \ + -template /usr/share/bbb-web/WEB-INF/classes/spring/turn-stun-servers.xml.tmpl:/usr/share/bbb-web/WEB-INF/classes/spring/turn-stun-servers.xml \ + gosu bigbluebutton java -Dgrails.env=prod -Dserver.address=0.0.0.0 -Dserver.port=8090 -Xms384m -Xmx384m -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/var/bigbluebutton/diagnostics -cp WEB-INF/lib/*:/:WEB-INF/classes/:. org.springframework.boot.loader.WarLauncher + + diff --git a/mod/bbb-web/logback.xml b/mod/bbb-web/logback.xml new file mode 100644 index 0000000..8fb5838 --- /dev/null +++ b/mod/bbb-web/logback.xml @@ -0,0 +1,28 @@ + + + + + %d{"yyyy-MM-dd'T'HH:mm:ss.SSSXXX"} %-5level %logger{35} - %msg%n + + + + + + + + + + + + + + + + + + + + + + diff --git a/mod/bbb-web/mocked-ps b/mod/bbb-web/mocked-ps new file mode 100755 index 0000000..bb67da3 --- /dev/null +++ b/mod/bbb-web/mocked-ps @@ -0,0 +1,8 @@ +#!/bin/bash +echo "(mocked-ps for HTML5LoadBalancingService.java)" + +# fake random process load to distribute meetings equally +for i in `seq $(cat /tmp/NUMBER_OF_BACKEND_NODEJS_PROCESSES)`; do + randomLoad=$(echo $(( $RANDOM % 100 ))) + echo " $randomLoad.1 /usr/share/node-v12.16.1-linux-x64/bin/node main.js NODEJS_BACKEND_INSTANCE_ID=$i" +done diff --git a/mod/bbb-web/office-convert.sh b/mod/bbb-web/office-convert.sh new file mode 100755 index 0000000..b4c7c9e --- /dev/null +++ b/mod/bbb-web/office-convert.sh @@ -0,0 +1,30 @@ +#!/bin/bash +set -e +set -u +PATH="/bin/:/usr/bin/" + +# This script receives three params +# Param 1: Input office file path (e.g. "/tmp/test.odt") +# Param 2: Output pdf file path (e.g. "/tmp/test.pdf") +# Param 3: Destination Format (pdf default) +if (( $# == 0 )); then + echo "Missing parameter 1 (Input office file path)"; + exit 1 +elif (( $# == 1 )); then + echo "Missing parameter 2 (Output pdf file path)"; + exit 1 +fi; + + +source="${1}" +dest="${2}" + +#If output format is missing, define PDF +convertTo="${3:-pdf}" + +curl -v -X POST "http://jodconverter:8080/lool/convert-to/$convertTo" \ + -H "accept: application/octet-stream" \ + -H "Content-Type: multipart/form-data" \ + -F "data=@${source}" > "${dest}" + +exit 0 \ No newline at end of file diff --git a/mod/bbb-web/turn-stun-servers.xml b/mod/bbb-web/turn-stun-servers.xml new file mode 100644 index 0000000..e97dcab --- /dev/null +++ b/mod/bbb-web/turn-stun-servers.xml @@ -0,0 +1,37 @@ + + + + + + + + {{if .Env.TURN_SERVER }} + + + + + + {{end}} + + + + + + + + + + {{if .Env.TURN_SERVER }} + + {{end}} + + + + + + + + diff --git a/mod/coturn/entrypoint.sh b/mod/coturn/entrypoint.sh new file mode 100755 index 0000000..62cd126 --- /dev/null +++ b/mod/coturn/entrypoint.sh @@ -0,0 +1,31 @@ +#!/bin/sh +set -e +apk add jq su-exec +if [ "$ENABLE_HTTPS_PROXY" == true ]; then + + while [ ! -f /etc/resty-auto-ssl/storage/file/*latest ] + do + echo "ERROR: certificate doesn't exist yet." + echo "Certificate gets create on the first request to the HTTPS proxy." + echo "We will try again..." + sleep 10 + done + + # extract cert + cat /etc/resty-auto-ssl/storage/file/*%3Alatest | jq -r '.fullchain_pem' > /tmp/cert.pem + cat /etc/resty-auto-ssl/storage/file/*%3Alatest | jq -r '.privkey_pem' > /tmp/key.pem +fi + +if [ ! -f /tmp/cert.pem ] || [ ! -f /tmp/key.pem ]; then + echo "ERROR: certificate not found, but coturn relies on it." + echo "Use either auto HTTPS proxy or" + echo "provide path to certificates in .env file" + exit 1 +fi + +# If command starts with an option, prepend with turnserver binary. +if [ "${1:0:1}" == '-' ]; then + set -- turnserver "$@" +fi + +su-exec nobody $(eval "echo $@") \ No newline at end of file diff --git a/mod/coturn/turnserver.conf b/mod/coturn/turnserver.conf new file mode 100644 index 0000000..be71ffe --- /dev/null +++ b/mod/coturn/turnserver.conf @@ -0,0 +1,73 @@ +# Example coturn configuration for BigBlueButton + +# These are the two network ports used by the TURN server which the client +# may connect to. We enable the standard unencrypted port 3478 for STUN, +listening-port=3478 + +# and since TLS over SMTP port (465) is now blocked by major browser vendors, +# we reverted to the most common coturn TLS port 5349, which has limitations +# in restrictive firewall environments. For maximum client support run +# coturn on a dedicated host on port 443. +tls-listening-port=5349 + +# If the server has multiple IP addresses, you may wish to limit which +# addresses coturn is using. Do that by setting this option (it can be +# specified multiple times). The default is to listen on all addresses. +# You do not normally need to set this option. +#listening-ip=172.17.19.101 + +# If the server is behind NAT, you need to specify the external IP address. +# If there is only one external address, specify it like this: +#external-ip=172.17.19.120 +# If you have multiple external addresses, you have to specify which +# internal address each corresponds to, like this. The first address is the +# external ip, and the second address is the corresponding internal IP. +#external-ip=172.17.19.131/10.0.0.11 +#external-ip=172.17.18.132/10.0.0.12 + +# Fingerprints in TURN messages are required for WebRTC +fingerprint + +# The long-term credential mechanism is required for WebRTC +lt-cred-mech + +# Configure coturn to use the "TURN REST API" method for validating time- +# limited credentials. BigBlueButton will generate credentials in this +# format. Note that the static-auth-secret value specified here must match +# the configuration in BigBlueButton's turn-stun-servers.xml +# You can generate a new random value by running the command: +# openssl rand -hex 16 +use-auth-secret +# static-auth-secret= + +# If the realm value is unspecified, it defaults to the TURN server hostname. +# You probably want to configure it to a domain name that you control to +# improve log output. There is no functional impact. +realm=example.com + +# Configure TLS support. +# Adjust these paths to match the locations of your certificate files +cert=/tmp/cert.pem +pkey=/tmp/key.pem +# Limit the allowed ciphers to improve security +# Based on https://hynek.me/articles/hardening-your-web-servers-ssl-ciphers/ +cipher-list="ECDH+AESGCM:ECDH+CHACHA20:DH+AESGCM:ECDH+AES256:DH+AES256:ECDH+AES128:DH+AES:RSA+AESGCM:RSA+AES:!aNULL:!MD5:!DSS" + +# Enable longer DH TLS key to improve security +dh2066 + +# All WebRTC-compatible web browsers support TLS 1.2 or later, so disable +# older protocols +no-tlsv1 +no-tlsv1_1 + +# To enable single filename logs you need to enable the simple-log flag +syslog +#verbose + +# Allocate Address Family according +# If enabled then TURN server allocates address family according the TURN +# Client <=> Server communication address family. +# (By default Coturn works according RFC 6156.) +# !!Warning: Enabling this option breaks RFC6156 section-4.2 (violates use default IPv4)!! +keep-address-family diff --git a/mod/etherpad/Dockerfile b/mod/etherpad/Dockerfile new file mode 100644 index 0000000..7573eed --- /dev/null +++ b/mod/etherpad/Dockerfile @@ -0,0 +1,28 @@ +FROM etherpad/etherpad:1.8.18 + +USER root + +RUN apt-get update \ + && apt-get install -y git curl + +USER etherpad + +RUN npm install \ + ep_cursortrace@3.1.16 \ + git+https://github.com/mconf/ep_pad_ttl.git#360136cd38493dd698435631f2373cbb7089082d \ + git+https://github.com/mconf/ep_redis_publisher.git#a30a48e4bc1e501b5b102884b9a0b26c30798484 \ + ep_disable_chat@0.0.8 \ + ep_auth_session@1.1.1 \ +# remove npm lockfile, because somehow it prevents etherpad from detecting the manual added plugin ep_bigbluebutton_patches + && rm package-lock.json + +# add skin from git submodule +COPY --chown=etherpad:0 ./bbb-etherpad-skin /opt/etherpad-lite/src/static/skins/bigbluebutton + +# add plugin from git submodule +COPY --chown=etherpad:0 ./bbb-etherpad-plugin /opt/etherpad-lite/node_modules/ep_bigbluebutton_patches + +COPY settings.json /opt/etherpad-lite/settings.json +COPY etherpad-export.sh /etherpad-export.sh +COPY entrypoint.sh /entrypoint.sh +ENTRYPOINT ["/entrypoint.sh"] \ No newline at end of file diff --git a/mod/etherpad/entrypoint.sh b/mod/etherpad/entrypoint.sh new file mode 100755 index 0000000..7586f6c --- /dev/null +++ b/mod/etherpad/entrypoint.sh @@ -0,0 +1,5 @@ +#!/bin/bash +echo $ETHERPAD_API_KEY > /tmp/apikey +export NODE_ENV=production + +node /opt/etherpad-lite/node_modules/ep_etherpad-lite/node/server.js --apikey /tmp/apikey \ No newline at end of file diff --git a/mod/etherpad/etherpad-export.sh b/mod/etherpad/etherpad-export.sh new file mode 100755 index 0000000..a1f8f7c --- /dev/null +++ b/mod/etherpad/etherpad-export.sh @@ -0,0 +1,12 @@ +#!/bin/bash +src="$8" +dest="$(echo $8 | sed -E -e 's/html|odt/'$7'/')" +convertTo="$7" + + +curl -v -X POST "http://jodconverter:8080/lool/convert-to/$convertTo" \ + -H "accept: application/octet-stream" \ + -H "Content-Type: multipart/form-data" \ + -F "data=@$src" > $dest + +exit 0 \ No newline at end of file diff --git a/mod/etherpad/settings.json b/mod/etherpad/settings.json new file mode 100644 index 0000000..11442c6 --- /dev/null +++ b/mod/etherpad/settings.json @@ -0,0 +1,607 @@ +/* + * This file must be valid JSON. But comments are allowed + * + * Please edit settings.json, not settings.json.template + * + * Please note that starting from Etherpad 1.6.0 you can store DB credentials in + * a separate file (credentials.json). + * + * + * ENVIRONMENT VARIABLE SUBSTITUTION + * ================================= + * + * All the configuration values can be read from environment variables using the + * syntax "${ENV_VAR}" or "${ENV_VAR:default_value}". + * + * This is useful, for example, when running in a Docker container. + * + * DETAILED RULES: + * - If the environment variable is set to the string "true" or "false", the + * value becomes Boolean true or false. + * - If the environment variable is set to the string "null", the value + * becomes null. + * - If the environment variable is set to the string "undefined", the setting + * is removed entirely, except when used as the member of an array in which + * case it becomes null. + * - If the environment variable is set to a string representation of a finite + * number, the string is converted to that number. + * - If the environment variable is set to any other string, including the + * empty string, the value is that string. + * - If the environment variable is unset and a default value is provided, the + * value is as if the environment variable was set to the provided default: + * - "${UNSET_VAR:}" becomes the empty string. + * - "${UNSET_VAR:foo}" becomes the string "foo". + * - "${UNSET_VAR:true}" and "${UNSET_VAR:false}" become true and false. + * - "${UNSET_VAR:null}" becomes null. + * - "${UNSET_VAR:undefined}" causes the setting to be removed (or be set + * to null, if used as a member of an array). + * - If the environment variable is unset and no default value is provided, + * the value becomes null. THIS BEHAVIOR MAY CHANGE IN A FUTURE VERSION OF + * ETHERPAD; if you want the default value to be null, you should explicitly + * specify "null" as the default value. + * + * EXAMPLE: + * "port": "${PORT:9001}" + * "minify": "${MINIFY}" + * "skinName": "${SKIN_NAME:colibris}" + * + * Would read the configuration values for those items from the environment + * variables PORT, MINIFY and SKIN_NAME. + * + * If PORT and SKIN_NAME variables were not defined, the default values 9001 and + * "colibris" would be used. + * The configuration value "minify", on the other hand, does not have a + * designated default value. Thus, if the environment variable MINIFY were + * undefined, "minify" would be null. + * + * REMARKS: + * 1) please note that variable substitution always needs to be quoted. + * + * "port": 9001, <-- Literal values. When not using + * "minify": false substitution, only strings must be + * "skinName": "colibris" quoted. Booleans and numbers must not. + * + * "port": "${PORT:9001}" <-- CORRECT: if you want to use a variable + * "minify": "${MINIFY:true}" substitution, put quotes around its name, + * "skinName": "${SKIN_NAME}" even if the required value is a number or + * a boolean. + * Etherpad will take care of rewriting it + * to the proper type if necessary. + * + * "port": ${PORT:9001} <-- ERROR: this is not valid json. Quotes + * "minify": ${MINIFY} around variable names are missing. + * "skinName": ${SKIN_NAME} + * + * 2) Beware of undefined variables and default values: nulls and empty strings + * are different! + * + * This is particularly important for user's passwords (see the relevant + * section): + * + * "password": "${PASSW}" // if PASSW is not defined would result in password === null + * "password": "${PASSW:}" // if PASSW is not defined would result in password === '' + * + * If you want to use an empty value (null) as default value for a variable, + * simply do not set it, without putting any colons: "${ABIWORD}". + * + * 3) if you want to use newlines in the default value of a string parameter, + * use "\n" as usual. + * + * "defaultPadText" : "${DEFAULT_PAD_TEXT}Line 1\nLine 2" + */ +{ + /* + * Name your instance! + */ + "title": "Etherpad", + + /* + * Pathname of the favicon you want to use. If null, the skin's favicon is + * used if one is provided by the skin, otherwise the default Etherpad favicon + * is used. If this is a relative path it is interpreted as relative to the + * Etherpad root directory. + */ + "favicon": null, + + /* + * Skin name. + * + * Its value has to be an existing directory under src/static/skins. + * You can write your own, or use one of the included ones: + * + * - "no-skin": an empty skin (default). This yields the unmodified, + * traditional Etherpad theme. + * - "colibris": the new experimental skin (since Etherpad 1.8), candidate to + * become the default in Etherpad 2.0 + */ + "skinName": "bigbluebutton", + + /* + * Skin Variants + * + * Use the UI skin variants builder at /p/test#skinvariantsbuilder + * + * For the colibris skin only, you can choose how to render the three main + * containers: + * - toolbar (top menu with icons) + * - editor (containing the text of the pad) + * - background (area outside of editor, mostly visible when using page style) + * + * For each of the 3 containers you can choose 4 color combinations: + * super-light, light, dark, super-dark. + * + * For example, to make the toolbar dark, you will include "dark-toolbar" into + * skinVariants. + * + * You can provide multiple skin variants separated by spaces. Default + * skinVariant is "super-light-toolbar super-light-editor light-background". + * + * For the editor container, you can also make it full width by adding + * "full-width-editor" variant (by default editor is rendered as a page, with + * a max-width of 900px). + */ + "skinVariants": "super-light-toolbar super-light-editor light-background", + + /* + * IP and port which Etherpad should bind at. + * + * Binding to a Unix socket is also supported: just use an empty string for + * the ip, and put the full path to the socket in the port parameter. + * + * EXAMPLE USING UNIX SOCKET: + * "ip": "", // <-- has to be an empty string + * "port" : "/somepath/etherpad.socket", // <-- path to a Unix socket + */ + "ip": "0.0.0.0", + "port": 9001, + + /* + * Option to hide/show the settings.json in admin page. + * + * Default option is set to true + */ + "showSettingsInAdminPage": true, + + /* + * Node native SSL support + * + * This is disabled by default. + * Make sure to have the minimum and correct file access permissions set so + * that the Etherpad server can access them + */ + + /* + "ssl" : { + "key" : "/path-to-your/epl-server.key", + "cert" : "/path-to-your/epl-server.crt", + "ca": ["/path-to-your/epl-intermediate-cert1.crt", "/path-to-your/epl-intermediate-cert2.crt"] + }, + */ + + /* + * The type of the database. + * + * You can choose between many DB drivers, for example: dirty, postgres, + * sqlite, mysql. + * + * You shouldn't use "dirty" for for anything else than testing or + * development. + * + * + * Database specific settings are dependent on dbType, and go in dbSettings. + * Remember that since Etherpad 1.6.0 you can also store this information in + * credentials.json. + * + * For a complete list of the supported drivers, please refer to: + * https://www.npmjs.com/package/ueberdb2 + */ + + "dbType": "redis", + "dbSettings": { + "host": "redis", + "port": 6379 + }, + + /* + * An Example of MySQL Configuration (commented out). + * + * See: https://github.com/ether/etherpad-lite/wiki/How-to-use-Etherpad-Lite-with-MySQL + */ + + /* + "dbType" : "redis", + "dbSettings" : { + "host": "127.0.0.1", + "port": 6379, + "client_options": { + "password": "PASSWORD" + } + }, + */ + + /* + * The default text of a pad + */ + "defaultPadText" : "", + + /* + * Default Pad behavior. + * + * Change them if you want to override. + */ + "padOptions": { + "noColors": true, + "showControls": true, + "showChat": false, + "showLineNumbers": false, + "useMonospaceFont": false, + "userName": false, + "userColor": false, + "rtl": false, + "alwaysShowChat": false, + "chatAndUsers": false, + "lang": "en" + }, + + /* + * Pad Shortcut Keys + */ + "padShortcutEnabled" : { + "altF9": true, /* focus on the File Menu and/or editbar */ + "altC": false, /* focus on the Chat window */ + "cmdShift2": true, /* shows a gritter popup showing a line author */ + "delete": true, + "return": true, + "esc": true, /* in mozilla versions 14-19 avoid reconnecting pad */ + "cmdS": true, /* save a revision */ + "tab": true, /* indent */ + "cmdZ": true, /* undo/redo */ + "cmdY": true, /* redo */ + "cmdI": true, /* italic */ + "cmdB": true, /* bold */ + "cmdU": true, /* underline */ + "cmd5": true, /* strike through */ + "cmdShiftL": true, /* unordered list */ + "cmdShiftN": true, /* ordered list */ + "cmdShift1": true, /* ordered list */ + "cmdShiftC": true, /* clear authorship */ + "cmdH": true, /* backspace */ + "ctrlHome": true, /* scroll to top of pad */ + "pageUp": true, + "pageDown": true + }, + + /* + * Should we suppress errors from being visible in the default Pad Text? + */ + "suppressErrorsInPadText": true, + + /* + * If this option is enabled, a user must have a session to access pads. + * This effectively allows only group pads to be accessed. + */ + "requireSession": true, + + /* + * Users may edit pads but not create new ones. + * + * Pad creation is only via the API. + * This applies both to group pads and regular pads. + */ + "editOnly": true, + + /* + * If true, all css & js will be minified before sending to the client. + * + * This will improve the loading performance massively, but makes it difficult + * to debug the javascript/css + */ + "minify": true, + + /* + * How long may clients use served javascript code (in seconds)? + * + * Not setting this may cause problems during deployment. + * Set to 0 to disable caching. + */ + "maxAge": 21600, // 60 * 60 * 6 = 6 hours + + /* + * Absolute path to the Abiword executable. + * + * Abiword is needed to get advanced import/export features of pads. Setting + * it to null disables Abiword and will only allow plain text and HTML + * import/exports. + */ + "abiword": null, + + /* + * This is the absolute path to the soffice executable. + * + * LibreOffice can be used in lieu of Abiword to export pads. + * Setting it to null disables LibreOffice exporting. + */ + "soffice": "/etherpad-export.sh", + + /* + * Path to the Tidy executable. + * + * Tidy is used to improve the quality of exported pads. + * Setting it to null disables Tidy. + */ + "tidyHtml": null, + + /* + * Allow import of file types other than the supported ones: + * txt, doc, docx, rtf, odt, html & htm + */ + "allowUnknownFileEnds": false, + + /* + * This setting is used if you require authentication of all users. + * + * Note: "/admin" always requires authentication. + */ + "requireAuthentication": false, + + /* + * Require authorization by a module, or a user with is_admin set, see below. + */ + "requireAuthorization": false, + + /* + * When you use NGINX or another proxy/load-balancer set this to true. + * + * This is especially necessary when the reverse proxy performs SSL + * termination, otherwise the cookies will not have the "secure" flag. + * + * The other effect will be that the logs will contain the real client's IP, + * instead of the reverse proxy's IP. + */ + "trustProxy": true, + + /* + * Settings controlling the session cookie issued by Etherpad. + */ + "cookie": { + /* + * Value of the SameSite cookie property. "Lax" is recommended unless + * Etherpad will be embedded in an iframe from another site, in which case + * this must be set to "None". Note: "None" will not work (the browser will + * not send the cookie to Etherpad) unless https is used to access Etherpad + * (either directly or via a reverse proxy with "trustProxy" set to true). + * + * "Strict" is not recommended because it has few security benefits but + * significant usability drawbacks vs. "Lax". See + * https://stackoverflow.com/q/41841880 for discussion. + */ + "sameSite": "None" + }, + + /* + * Privacy: disable IP logging + */ + "disableIPlogging": true, + + /* + * Time (in seconds) to automatically reconnect pad when a "Force reconnect" + * message is shown to user. + * + * Set to 0 to disable automatic reconnection. + */ + "automaticReconnectionTimeout": 0, + + /* + * By default, when caret is moved out of viewport, it scrolls the minimum + * height needed to make this line visible. + */ + "scrollWhenFocusLineIsOutOfViewport": { + + /* + * Percentage of viewport height to be additionally scrolled. + * + * E.g.: use "percentage.editionAboveViewport": 0.5, to place caret line in + * the middle of viewport, when user edits a line above of the + * viewport + * + * Set to 0 to disable extra scrolling + */ + "percentage": { + "editionAboveViewport": 0, + "editionBelowViewport": 0 + }, + + /* + * Time (in milliseconds) used to animate the scroll transition. + * Set to 0 to disable animation + */ + "duration": 0, + + /* + * Flag to control if it should scroll when user places the caret in the + * last line of the viewport + */ + "scrollWhenCaretIsInTheLastLineOfViewport": false, + + /* + * Percentage of viewport height to be additionally scrolled when user + * presses arrow up in the line of the top of the viewport. + * + * Set to 0 to let the scroll to be handled as default by Etherpad + */ + "percentageToScrollWhenUserPressesArrowUp": 0 + }, + + /* + * User accounts. These accounts are used by: + * - default HTTP basic authentication if no plugin handles authentication + * - some but not all authentication plugins + * - some but not all authorization plugins + * + * User properties: + * - password: The user's password. Some authentication plugins will ignore + * this. + * - is_admin: true gives access to /admin. Defaults to false. If you do not + * uncomment this, /admin will not be available! + * - readOnly: If true, this user will not be able to create new pads or + * modify existing pads. Defaults to false. + * - canCreate: If this is true and readOnly is false, this user can create + * new pads. Defaults to true. + * + * Authentication and authorization plugins may define additional properties. + * + * WARNING: passwords should not be stored in plaintext in this file. + * If you want to mitigate this, please install ep_hash_auth and + * follow the section "secure your installation" in README.md + */ + + /* + "users": { + "admin": { + // 1) "password" can be replaced with "hash" if you install ep_hash_auth + // 2) please note that if password is null, the user will not be created + "password": "changeme1", + "is_admin": true + }, + "user": { + // 1) "password" can be replaced with "hash" if you install ep_hash_auth + // 2) please note that if password is null, the user will not be created + "password": "changeme1", + "is_admin": false + } + }, + */ + + /* + * Restrict socket.io transport methods + */ + "socketTransportProtocols" : ["xhr-polling", "jsonp-polling", "htmlfile"], + + "socketIo": { + /* + * Maximum permitted client message size (in bytes). All messages from + * clients that are larger than this will be rejected. Large values make it + * possible to paste large amounts of text, and plugins may require a larger + * value to work properly, but increasing the value increases susceptibility + * to denial of service attacks (malicious clients can exhaust memory). + */ + "maxHttpBufferSize": 10000 + }, + + /* + * Allow Load Testing tools to hit the Etherpad Instance. + * + * WARNING: this will disable security on the instance. + */ + "loadTest": false, + + /** + * Disable dump of objects preventing a clean exit + */ + "dumpOnUncleanExit": false, + + /* + * Disable indentation on new line when previous line ends with some special + * chars (':', '[', '(', '{') + */ + + /* + "indentationOnNewLine": false, + */ + + "ep_pad_ttl": { + "ttl": 86400, // 24 hours + "timeout": 30, + "interval": 21600 // 6 hours + }, + + /* + * Redis publisher plugin configuration. + * npm i git+https://git@github.com/mconf/ep_redis_publisher.git + */ + + "ep_redis_publisher": { + "host": "redis", + "port": 6379 + }, + + /* + * From Etherpad 1.8.3 onwards, import and export of pads is always rate + * limited. + * + * The default is to allow at most 10 requests per IP in a 90 seconds window. + * After that the import/export request is rejected. + * + * See https://github.com/nfriedly/express-rate-limit for more options + */ + "importExportRateLimiting": { + // duration of the rate limit window (milliseconds) + "windowMs": 90000, + + // maximum number of requests per IP to allow during the rate limit window + "max": 10 + }, + + /* + * From Etherpad 1.8.3 onwards, the maximum allowed size for a single imported + * file is always bounded. + * + * File size is specified in bytes. Default is 50 MB. + */ + "importMaxFileSize": 52428800, // 50 * 1024 * 1024 + + /* + * From Etherpad 1.8.5 onwards, when Etherpad is in production mode commits from individual users are rate limited + * + * The default is to allow at most 10 changes per IP in a 1 second window. + * After that the change is rejected. + * + * See https://github.com/animir/node-rate-limiter-flexible/wiki/Overall-example#websocket-single-connection-prevent-flooding for more options + */ + "commitRateLimiting": { + // duration of the rate limit window (seconds) + "duration": 1, + + // maximum number of changes per IP to allow during the rate limit window + "points": 100 + }, + + + /* + * Toolbar buttons configuration. + * + * Uncomment to customize. + */ + + "toolbar": { + "left": [ + ["bold", "italic", "underline", "strikethrough"], + ["orderedlist", "unorderedlist", "undo", "redo"], + ["importexport"] + ], + "right": [[]] + }, + + /* + * Expose Etherpad version in the web interface and in the Server http header. + * + * Do not enable on production machines. + */ + "exposeVersion": false, + + /* + * The log level we are using. + * + * Valid values: DEBUG, INFO, WARN, ERROR + */ + "loglevel": "INFO", + + /* Override any strings found in locale directories */ + "customLocaleStrings": {}, + + /* Disable Admin UI tests */ + "enableAdminUITests": false +} + + + diff --git a/mod/freeswitch/Dockerfile b/mod/freeswitch/Dockerfile new file mode 100644 index 0000000..6480b23 --- /dev/null +++ b/mod/freeswitch/Dockerfile @@ -0,0 +1,66 @@ +FROM debian:bullseye-slim + +# install dependencies +RUN apt-get update && \ + apt-get install -y --no-install-recommends \ + subversion curl wget ca-certificates gnupg gnupg2 lsb-release unzip + +COPY --from=alangecker/bbb-docker-base-java /usr/local/bin/dockerize /usr/local/bin/dockerize + + +# install freeswitch +RUN wget -q -O /usr/share/keyrings/freeswitch-archive-keyring.gpg https://freeswitch-mirror.chandi.it/repo/deb/debian-release/signalwire-freeswitch-repo.gpg && \ + echo 'deb [signed-by=/usr/share/keyrings/freeswitch-archive-keyring.gpg] http://freeswitch-mirror.chandi.it/repo/deb/debian-release/ bullseye main' > /etc/apt/sources.list.d/freeswitch.list && \ + apt-get update && \ + apt-get install -y \ + freeswitch \ + freeswitch-mod-commands \ + freeswitch-mod-conference \ + freeswitch-mod-console \ + freeswitch-mod-dialplan-xml \ + freeswitch-mod-dptools \ + freeswitch-mod-event-socket \ + freeswitch-mod-native-file \ + freeswitch-mod-opusfile \ + freeswitch-mod-opus \ + freeswitch-mod-sndfile \ + freeswitch-mod-spandsp \ + freeswitch-mod-sofia \ + freeswitch-sounds-en-us-callie \ + iptables + +# replace mute & unmute sounds +RUN wget -q https://gitlab.senfcall.de/senfcall-public/mute-and-unmute-sounds/-/archive/master/mute-and-unmute-sounds-master.zip && \ + unzip mute-and-unmute-sounds-master.zip && \ + cd mute-and-unmute-sounds-master/sounds/ && \ + find . -name "*.wav" -exec /bin/bash -c "echo {};sox -v 0.3 {} /tmp/tmp.wav; mv /tmp/tmp.wav /usr/share/freeswitch/sounds/en/us/callie/conference/{}" \; && \ + cd ../.. && \ + rm -r mute-and-unmute-sounds-master mute-and-unmute-sounds-master.zip + + +# -- get official bbb freeswitch config +# we use svn for retrieving the files since the repo is quite large, +# git sparse-checkout is not yet available with buster and there +# is no other sane way of downloading a single directory via git + +ARG TAG_FS_CONFIG +RUN svn checkout https://github.com/bigbluebutton/bigbluebutton/tags/$TAG_FS_CONFIG/bbb-voice-conference/config/freeswitch/conf /etc/freeswitch \ + && rm -rf /etc/freeswitch/.svn + +# the current available freeswitch-mod-opusfile is broken, +# it can't write any .opus files. The fix provided in +# https://github.com/signalwire/freeswitch/pull/719/files +# is not sufficient as the module still comes without opus +# write support, so we rather switch to the binary built +# by bigbluebutton and add its dependencies +RUN wget -O /usr/lib/freeswitch/mod/mod_opusfile.so https://github.com/bbb-pkg/bbb-freeswitch-core/raw/43f3a47af1fcf5ea559e16bb28b900c925a7f2c3/opt/freeswitch/lib/freeswitch/mod/mod_opusfile.so \ + && wget -O /tmp/libopusenc0_0.2.1-1bbb1_amd64.deb https://launchpad.net/~bigbluebutton/+archive/ubuntu/support/+files/libopusenc0_0.2.1-1bbb1_amd64.deb \ + && dpkg -i /tmp/libopusenc0_0.2.1-1bbb1_amd64.deb \ + && rm /tmp/libopusenc0_0.2.1-1bbb1_amd64.deb + +# add modifications +COPY ./conf /etc/freeswitch/ + + +COPY ./entrypoint.sh /entrypoint.sh +ENTRYPOINT /entrypoint.sh \ No newline at end of file diff --git a/mod/freeswitch/conf/autoload_configs/acl.conf.xml b/mod/freeswitch/conf/autoload_configs/acl.conf.xml new file mode 100644 index 0000000..78628d8 --- /dev/null +++ b/mod/freeswitch/conf/autoload_configs/acl.conf.xml @@ -0,0 +1,49 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/mod/freeswitch/conf/autoload_configs/conference.conf.xml.tmpl b/mod/freeswitch/conf/autoload_configs/conference.conf.xml.tmpl new file mode 100644 index 0000000..1171614 --- /dev/null +++ b/mod/freeswitch/conf/autoload_configs/conference.conf.xml.tmpl @@ -0,0 +1,285 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + {{if not (isTrue .Env.DISABLE_SOUND_MUTED) }} + + + {{end}} + {{if not (isTrue .Env.DISABLE_SOUND_ALONE) }} + + {{end}} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/mod/freeswitch/conf/autoload_configs/event_socket.conf.xml b/mod/freeswitch/conf/autoload_configs/event_socket.conf.xml new file mode 100644 index 0000000..664aa8f --- /dev/null +++ b/mod/freeswitch/conf/autoload_configs/event_socket.conf.xml @@ -0,0 +1,10 @@ + + + + + + + + + + \ No newline at end of file diff --git a/mod/freeswitch/conf/autoload_configs/modules.conf.xml b/mod/freeswitch/conf/autoload_configs/modules.conf.xml new file mode 100644 index 0000000..f30804c --- /dev/null +++ b/mod/freeswitch/conf/autoload_configs/modules.conf.xml @@ -0,0 +1,30 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/mod/freeswitch/conf/dialplan/public.xml b/mod/freeswitch/conf/dialplan/public.xml new file mode 100644 index 0000000..ac355dc --- /dev/null +++ b/mod/freeswitch/conf/dialplan/public.xml @@ -0,0 +1,43 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/mod/freeswitch/conf/sip_profiles/external-ipv6.xml b/mod/freeswitch/conf/sip_profiles/external-ipv6.xml new file mode 100644 index 0000000..94339de --- /dev/null +++ b/mod/freeswitch/conf/sip_profiles/external-ipv6.xml @@ -0,0 +1,113 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/mod/freeswitch/conf/sip_profiles/external.xml b/mod/freeswitch/conf/sip_profiles/external.xml new file mode 100644 index 0000000..afb4069 --- /dev/null +++ b/mod/freeswitch/conf/sip_profiles/external.xml @@ -0,0 +1,128 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/mod/freeswitch/conf/vars.xml.tmpl b/mod/freeswitch/conf/vars.xml.tmpl new file mode 100644 index 0000000..b31edeb --- /dev/null +++ b/mod/freeswitch/conf/vars.xml.tmpl @@ -0,0 +1,398 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/mod/freeswitch/entrypoint.sh b/mod/freeswitch/entrypoint.sh new file mode 100755 index 0000000..14d2c8a --- /dev/null +++ b/mod/freeswitch/entrypoint.sh @@ -0,0 +1,52 @@ +#!/bin/bash + +# remove all SIP (port 5060) iptable rules +iptables -S INPUT | grep "\-\-dport 5060 " | cut -d " " -f 2- | xargs -rL1 iptables -D + +# block requests to 5060 (tcp/udp) +iptables -A INPUT -p tcp --dport 5060 -s 0.0.0.0/0 -j REJECT +iptables -A INPUT -p udp --dport 5060 -s 0.0.0.0/0 -j REJECT + +# allow some IPs +IFS=',' read -ra ADDR <<< "$SIP_IP_ALLOWLIST" +for IP in "${ADDR[@]}"; do + # process "$i" + echo "allow port 5060/udp for $IP" + iptables -I INPUT -p udp --dport 5060 -s $IP -j ACCEPT +done + +chown -R freeswitch:daemon /var/freeswitch/meetings +chmod 777 /var/freeswitch/meetings + + +# install freeswitch sounds if missing +SOUNDS_DIR=/usr/share/freeswitch/sounds +if [ "$SOUNDS_LANGUAGE" == "de-de-daedalus3" ]; then + if [ ! -d "$SOUNDS_DIR/de/de/daedalus3" ]; then + echo "sounds package for de-de-daedalus3 not installed yet" + wget -O /tmp/freeswitch-german-soundfiles.zip https://github.com/Daedalus3/freeswitch-german-soundfiles/archive/master.zip + mkdir -p $SOUNDS_DIR/de/de/daedalus3 + unzip /tmp/freeswitch-german-soundfiles.zip -d /tmp/ + mv /tmp/freeswitch-german-soundfiles-master $SOUNDS_DIR/de/de/daedalus3/conference + + # symlink other folders + for folder in "digits" "ivr" "misc"; do + ln -s $SOUNDS_DIR/en/us/callie/$folder $SOUNDS_DIR/de/de/daedalus3/$folder + done + + fi +else + SOUNDS_PACKAGE=$(echo "freeswitch-sounds-${SOUNDS_LANGUAGE}" | tr '[:upper:]' '[:lower:]') + if ! dpkg -s $SOUNDS_PACKAGE >/dev/null 2>&1; then + echo "sounds package for $SOUNDS_LANGUAGE not installed yet" + apt-get install $SOUNDS_PACKAGE + fi +fi + + +export SOUNDS_PATH=$SOUNDS_DIR/$(echo "$SOUNDS_LANGUAGE" | sed 's|-|/|g') + +dockerize \ + -template /etc/freeswitch/vars.xml.tmpl:/etc/freeswitch/vars.xml \ + -template /etc/freeswitch/autoload_configs/conference.conf.xml.tmpl:/etc/freeswitch/autoload_configs/conference.conf.xml \ + /usr/bin/freeswitch -u freeswitch -g daemon -nonat -nf diff --git a/mod/fsesl-akka/Dockerfile b/mod/fsesl-akka/Dockerfile new file mode 100644 index 0000000..61494d1 --- /dev/null +++ b/mod/fsesl-akka/Dockerfile @@ -0,0 +1,38 @@ +ARG BBB_BUILD_TAG +FROM gitlab.senfcall.de:5050/senfcall-public/docker-bbb-build:$BBB_BUILD_TAG AS builder + +ARG TAG_COMMON_MESSAGE + +# download bbb-common-message +RUN svn checkout https://github.com/bigbluebutton/bigbluebutton/tags/$TAG_COMMON_MESSAGE/bbb-common-message /bbb-common-message \ + && cd /bbb-common-message \ + && ./deploy.sh \ + && rm -rf /bbb-common-message + +# =================================================== +ARG TAG_FSESL_AKKA +RUN svn checkout https://github.com/bigbluebutton/bigbluebutton/tags/$TAG_FSESL_AKKA/bbb-fsesl-client /bbb-fsesl-client \ + && rm -rf /bbb-fsesl-client/.svn + +RUN cd /bbb-fsesl-client \ + && ./deploy.sh + +RUN svn checkout https://github.com/bigbluebutton/bigbluebutton/tags/$TAG_FSESL_AKKA/akka-bbb-fsesl /source \ + && rm -rf /source/.svn + +# compile and unzip bin +RUN cd /source \ + && sbt universal:packageBin +RUN unzip /source/target/universal/bbb-fsesl-akka-0.0.2.zip -d / + +# # =================================================== + +FROM alangecker/bbb-docker-base-java + +COPY --from=builder /bbb-fsesl-akka-0.0.2 /bbb-fsesl-akka +COPY bbb-fsesl-akka.conf /etc/bigbluebutton/bbb-fsesl-akka.conf.tmpl +COPY logback.xml /bbb-fsesl-akka/conf/logback.xml +COPY entrypoint.sh /entrypoint.sh + +USER bigbluebutton +ENTRYPOINT /entrypoint.sh \ No newline at end of file diff --git a/mod/fsesl-akka/bbb-fsesl-akka.conf b/mod/fsesl-akka/bbb-fsesl-akka.conf new file mode 100644 index 0000000..ed1b475 --- /dev/null +++ b/mod/fsesl-akka/bbb-fsesl-akka.conf @@ -0,0 +1,18 @@ +// include default config from upstream +include "/bbb-fsesl-akka/conf/application.conf" + + +freeswitch { + esl { + host="10.7.7.1" + password="FSESL_PASSWORD" + } +} + +redis { + host="10.7.7.5" +} + +http { + interface = "0.0.0.0" +} \ No newline at end of file diff --git a/mod/fsesl-akka/entrypoint.sh b/mod/fsesl-akka/entrypoint.sh new file mode 100755 index 0000000..2000ce3 --- /dev/null +++ b/mod/fsesl-akka/entrypoint.sh @@ -0,0 +1,9 @@ +#!/bin/sh -e + +TARGET=/etc/bigbluebutton/bbb-fsesl-akka.conf + +cp /etc/bigbluebutton/bbb-fsesl-akka.conf.tmpl $TARGET +sed -i "s/FSESL_PASSWORD/$FSESL_PASSWORD/" $TARGET + +cd /bbb-fsesl-akka +/bbb-fsesl-akka/bin/bbb-fsesl-akka \ No newline at end of file diff --git a/mod/fsesl-akka/logback.xml b/mod/fsesl-akka/logback.xml new file mode 100644 index 0000000..a6749c0 --- /dev/null +++ b/mod/fsesl-akka/logback.xml @@ -0,0 +1,18 @@ + + + + + %d{"yyyy-MM-dd'T'HH:mm:ss.SSSXXX"} %-5level %logger{35} - %msg%n + + + + + + + + + + + + + diff --git a/mod/html5/Dockerfile b/mod/html5/Dockerfile new file mode 100644 index 0000000..9364fed --- /dev/null +++ b/mod/html5/Dockerfile @@ -0,0 +1,34 @@ +ARG BBB_BUILD_TAG +FROM gitlab.senfcall.de:5050/senfcall-public/docker-bbb-build:$BBB_BUILD_TAG AS builder + +# RUN groupadd -g 2000 meteor && useradd -m -u 2001 -g meteor meteor +# USER meteor + +ARG TAG_HTML5 +RUN svn checkout https://github.com/bigbluebutton/bigbluebutton/tags/$TAG_HTML5/bigbluebutton-html5 /source \ + && cd /source \ + && meteor npm ci --production \ + && METEOR_DISABLE_OPTIMISTIC_CACHING=1 meteor build --architecture os.linux.x86_64 --allow-superuser --directory /app \ + && rm -rf /source + +RUN cd /app/bundle/programs/server \ + && npm install --production + +RUN sed -i "s/VERSION/$TAG_HTML5/" /app/bundle/programs/web.browser/head.html \ + && find /app/bundle/programs/web.browser -name '*.js' -exec gzip -k -f -9 '{}' \; \ + && find /app/bundle/programs/web.browser -name '*.css' -exec gzip -k -f -9 '{}' \; \ + && find /app/bundle/programs/web.browser -name '*.wasm' -exec gzip -k -f -9 '{}' \; + +# ------------------------------ + +FROM node:14.19.1-alpine + +RUN addgroup -g 2000 meteor && \ + adduser -D -u 2001 -G meteor meteor && \ + apk add su-exec +COPY --from=alangecker/bbb-docker-base-java /usr/local/bin/dockerize /usr/local/bin/dockerize +COPY --from=builder --chown=meteor:meteor /app/bundle /app +COPY entrypoint.sh /entrypoint.sh +COPY bbb-html5.yml /app/bbb-html5.yml.tmpl + +ENTRYPOINT ["/entrypoint.sh"] diff --git a/mod/html5/bbb-html5.yml b/mod/html5/bbb-html5.yml new file mode 100644 index 0000000..baf6de8 --- /dev/null +++ b/mod/html5/bbb-html5.yml @@ -0,0 +1,24 @@ +public: + app: + bbbServerVersion: {{ .Env.TAG_HTML5 }}-docker + listenOnlyMode: {{ .Env.LISTEN_ONLY_MODE }} + skipCheck: {{ .Env.DISABLE_ECHO_TEST }} + clientTitle: {{ .Env.CLIENT_TITLE }} + appName: BigBlueButton HTML5 Client (docker) + breakouts: + breakoutRoomLimit: {{ .Env.BREAKOUTROOM_LIMIT }} + kurento: + wsUrl: wss://{{ .Env.DOMAIN }}/bbb-webrtc-sfu + autoShareWebcam: {{ .Env.AUTO_SHARE_WEBCAM }} + skipVideoPreview: {{ .Env.DISABLE_VIDEO_PREVIEW }} + chat: + enabled: {{ .Env.CHAT_ENABLED }} + startClosed: {{ .Env.CHAT_START_CLOSED }} + pads: + url: https://{{ .Env.DOMAIN }}/pad +private: + app: + host: 0.0.0.0 + redis: + host: redis + port: '6379' diff --git a/mod/html5/entrypoint.sh b/mod/html5/entrypoint.sh new file mode 100755 index 0000000..7d46472 --- /dev/null +++ b/mod/html5/entrypoint.sh @@ -0,0 +1,43 @@ +#!/bin/sh +set -e + +cd /app +export MONGO_OPLOG_URL=mongodb://10.7.7.6/local +export MONGO_URL=mongodb://10.7.7.6/meteor +export ROOT_URL=http://127.0.0.1/html5client +export NODE_ENV=production +export SERVER_WEBSOCKET_COMPRESSION='{"level":5, "maxWindowBits":13, "memLevel":7, "requestMaxWindowBits":13}' +export BIND_IP=0.0.0.0 +export LANG=en_US.UTF-8 +export INSTANCE_MAX=1 +export ENVIRONMENT_TYPE=production +export NODE_VERSION=node-v14.19.1-linux-x64 +export BBB_HTML5_LOCAL_SETTINGS=/app/bbb-html5.yml + +if [ "$DEV_MODE" == true ]; then + echo "DEV_MODE=true, disable TLS certificate rejecting" + export NODE_TLS_REJECT_UNAUTHORIZED=0 +fi + +if [ "$BBB_HTML5_ROLE" == "backend" ]; then + PARAM=NODEJS_BACKEND_INSTANCE_ID=$INSTANCE_ID +fi + + +# if container is the first frontend, do some additional tasks +if [ "$BBB_HTML5_ROLE" == "frontend" ] && [ "$INSTANCE_ID" == "1" ]; then + + + # copy static files into volume for direct access by nginx + # https://github.com/bigbluebutton/bigbluebutton/issues/10739 + if [ -d "/html5-static" ]; then + rm -rf /html5-static/* + cp -r /app/programs/web.browser/* /html5-static + fi + +fi + +dockerize \ + -template /app/bbb-html5.yml.tmpl:/app/bbb-html5.yml \ + su-exec meteor \ + node --max-old-space-size=2048 --max_semi_space_size=128 main.js $PARAM diff --git a/mod/https/site-ipv4only.conf b/mod/https/site-ipv4only.conf new file mode 100644 index 0000000..3ab9c1c --- /dev/null +++ b/mod/https/site-ipv4only.conf @@ -0,0 +1,33 @@ +map $http_upgrade $connection_upgrade { + default upgrade; + '' close; +} + +server { + listen 443 ssl http2 default_server; + + # we at still serve https via IPv6 for the + # case that an AAAA record is set. + listen [::]:443 ssl http2 default_server; + + server_name _; + + include resty-server-https.conf; + + location / { + proxy_http_version 1.1; + proxy_pass http://127.0.0.1:48087; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + proxy_set_header Upgrade $http_upgrade; + proxy_set_header Connection $connection_upgrade; + proxy_cache_bypass $http_upgrade; + + proxy_read_timeout 6h; + proxy_send_timeout 6h; + client_body_timeout 6h; + send_timeout 6h; + } +} diff --git a/mod/https/site.conf b/mod/https/site.conf new file mode 100644 index 0000000..ba81f57 --- /dev/null +++ b/mod/https/site.conf @@ -0,0 +1,33 @@ +map $http_upgrade $connection_upgrade { + default upgrade; + '' close; +} +map $remote_addr $endpoint_addr { + "~:" [::1]; + default 127.0.0.1; +} + +server { + listen 443 ssl http2 default_server; + listen [::]:443 ssl http2 default_server; + server_name _; + + include resty-server-https.conf; + + location / { + proxy_http_version 1.1; + proxy_pass http://$endpoint_addr:48087; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + proxy_set_header Upgrade $http_upgrade; + proxy_set_header Connection $connection_upgrade; + proxy_cache_bypass $http_upgrade; + + proxy_read_timeout 6h; + proxy_send_timeout 6h; + client_body_timeout 6h; + send_timeout 6h; + } +} diff --git a/mod/jodconverter/Dockerfile b/mod/jodconverter/Dockerfile new file mode 100644 index 0000000..fdbab6f --- /dev/null +++ b/mod/jodconverter/Dockerfile @@ -0,0 +1,17 @@ +FROM eugenmayer/jodconverter:rest +RUN echo "ttf-mscorefonts-installer msttcorefonts/accepted-mscorefonts-eula select true" | debconf-set-selections +RUN sed -i 's/main/main contrib/' /etc/apt/sources.list && apt-get update +RUN apt-get update && apt -y install --no-install-recommends \ + fonts-arkpandora \ + fonts-crosextra-carlito \ + fonts-crosextra-caladea \ + fonts-noto \ + fonts-noto-cjk \ + fonts-liberation \ + fontconfig \ + ttf-mscorefonts-installer + + +# avoid "APPLICATION FAILED TO START. Config data location '/etc/app/' does not exist" +# https://github.com/bigbluebutton/docker/issues/178 +CMD ["--spring.config.additional-location=optional:/etc/app/"] \ No newline at end of file diff --git a/mod/mongo/init-replica.sh b/mod/mongo/init-replica.sh new file mode 100755 index 0000000..188cdda --- /dev/null +++ b/mod/mongo/init-replica.sh @@ -0,0 +1,26 @@ +#!/bin/sh +set -e + + +host=${HOSTNAME:-$(hostname -f)} + +# shut down again +mongod --pidfilepath /tmp/docker-entrypoint-temp-mongod.pid --shutdown +# restart again binding to 0.0.0.0 to allow a replset with 10.7.7.6 +mongod --oplogSize 8 --replSet rs0 --noauth \ + --config /tmp/docker-entrypoint-temp-config.json \ + --bind_ip 0.0.0.0 --port 27017 \ + --tlsMode disabled \ + --logpath /proc/1/fd/1 --logappend \ + --pidfilepath /tmp/docker-entrypoint-temp-mongod.pid --fork + +# init replset with defaults +mongo 10.7.7.6 --eval "rs.initiate({ + _id: 'rs0', + members: [ { _id: 0, host: '10.7.7.6:27017' } ] +})" + +echo "Waiting to become a master" +echo 'while (!db.isMaster().ismaster) { sleep(100); }' | mongo + +echo "I'm the master!" \ No newline at end of file diff --git a/mod/mongo/mongod.conf b/mod/mongo/mongod.conf new file mode 100644 index 0000000..f791065 --- /dev/null +++ b/mod/mongo/mongod.conf @@ -0,0 +1,33 @@ +# mongod.conf + +# for documentation of all options, see: +# http://docs.mongodb.org/manual/reference/configuration-options/ + +storage: + dbPath: /data/db + journal: + enabled: true + wiredTiger: + engineConfig: + cacheSizeGB: 1 + journalCompressor: none + directoryForIndexes: true + collectionConfig: + blockCompressor: none + indexConfig: + prefixCompression: false + + +net: + port: 27017 + bindIp: 0.0.0.0 + + +replication: + replSetName: rs0 + +setParameter: + diagnosticDataCollectionEnabled: false + +security: + javascriptEnabled: false diff --git a/mod/nginx/Dockerfile b/mod/nginx/Dockerfile new file mode 100644 index 0000000..e7788eb --- /dev/null +++ b/mod/nginx/Dockerfile @@ -0,0 +1,22 @@ +FROM node:14-alpine AS builder + +RUN apk add subversion git + +# -------------------- + +ARG TAG_LEARNING_DASHBOARD +RUN svn checkout https://github.com/bigbluebutton/bigbluebutton/tags/$TAG_LEARNING_DASHBOARD/bbb-learning-dashboard /bbb-learning-dashboard && rm -r /bbb-learning-dashboard/.svn +RUN cd /bbb-learning-dashboard && npm ci && npm run build + +COPY ./bbb-playback /bbb-playback +RUN cd /bbb-playback && npm ci && npm run build + +# -------------------- + +FROM nginx:1.21-alpine + +COPY --from=builder /bbb-learning-dashboard/build /www/learning-analytics-dashboard/ +COPY --from=builder /bbb-playback/build /www/playback/presentation/2.3 +COPY ./bbb /etc/nginx/bbb +COPY ./bigbluebutton /etc/nginx/conf.d/default.conf +COPY ./nginx.conf /etc/nginx/nginx.conf diff --git a/mod/nginx/bbb/bbb-exporter.nginx b/mod/nginx/bbb/bbb-exporter.nginx new file mode 100644 index 0000000..2a2be48 --- /dev/null +++ b/mod/nginx/bbb/bbb-exporter.nginx @@ -0,0 +1,4 @@ +location /bbb-exporter { + proxy_pass http://10.7.7.33:9688; + proxy_http_version 1.1; +} \ No newline at end of file diff --git a/mod/nginx/bbb/bbb-html5.nginx b/mod/nginx/bbb/bbb-html5.nginx new file mode 100644 index 0000000..fefc7cf --- /dev/null +++ b/mod/nginx/bbb/bbb-html5.nginx @@ -0,0 +1,40 @@ +location @html5client { + proxy_pass http://poolhtml5servers; # use for production + proxy_http_version 1.1; + proxy_set_header Upgrade $http_upgrade; + proxy_set_header Connection "Upgrade"; +} + +location /html5client/locales { + alias /html5-static/app/locales; +} + +location /html5client/compatibility { + alias /html5-static/app/compatibility; +} + +location /html5client/resources { + alias /html5-static/app/resources; +} + +location /html5client/svgs { + alias /html5-static/app/svgs; +} + +location /html5client/fonts { + alias /html5-static/app/fonts; +} + +location /html5client/wasm { + types { + application/wasm wasm; + } + gzip_static on; + alias /html5-static/app/wasm; +} + +location /html5client/ { + alias /html5-static; + try_files $uri @html5client; +} + diff --git a/mod/nginx/bbb/demo.nginx b/mod/nginx/bbb/demo.nginx new file mode 100644 index 0000000..8b68775 --- /dev/null +++ b/mod/nginx/bbb/demo.nginx @@ -0,0 +1,26 @@ + # Forward request to /demo to tomcat. This is for + # the BigBlueButton api demos. + location = /demo/ { + return 301 /demo/demo1.jsp; + } + location /demo { + proxy_pass http://host.docker.internal:8001; + proxy_redirect default; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + + # Allow 30M uploaded presentation document. + client_max_body_size 30m; + client_body_buffer_size 128k; + + proxy_connect_timeout 90; + proxy_send_timeout 90; + proxy_read_timeout 90; + + proxy_buffer_size 4k; + proxy_buffers 4 32k; + proxy_busy_buffers_size 64k; + proxy_temp_file_write_size 64k; + + include fastcgi_params; + } + diff --git a/mod/nginx/bbb/greenlight.nginx b/mod/nginx/bbb/greenlight.nginx new file mode 100644 index 0000000..4ed4626 --- /dev/null +++ b/mod/nginx/bbb/greenlight.nginx @@ -0,0 +1,34 @@ +# Routes requests to Greenlight based on the '/b' prefix. +# Use this file to route '/b' paths on your BigBlueButton server +# to the Greenlight application. If you are using a different +# subpath, you should change it here. + + +location /b { + proxy_pass http://host.docker.internal:5000; + proxy_set_header Host $host; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + proxy_set_header X-Forwarded-Ssl on; + proxy_http_version 1.1; +} + +location /b/cable { + proxy_pass http://host.docker.internal:5000; + proxy_set_header Host $host; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + proxy_set_header X-Forwarded-Ssl on; + proxy_set_header Upgrade $http_upgrade; + proxy_set_header Connection "Upgrade"; + proxy_http_version 1.1; + proxy_read_timeout 6h; + proxy_send_timeout 6h; + client_body_timeout 6h; + send_timeout 6h; +} + +# this is necessary for the preupload_presentation feature +location /rails/active_storage { + return 301 /b$request_uri; +} diff --git a/mod/nginx/bbb/learning-dashboard.nginx b/mod/nginx/bbb/learning-dashboard.nginx new file mode 100644 index 0000000..d7899bf --- /dev/null +++ b/mod/nginx/bbb/learning-dashboard.nginx @@ -0,0 +1,9 @@ +location ~ /learning-analytics-dashboard/([0-9a-f]+-[0-9]+)/(.*) { + root /var/bigbluebutton/learning-analytics-dashboard/; + autoindex off; +} + +location /learning-analytics-dashboard/ { + alias /www/learning-analytics-dashboard/; + autoindex off; +} diff --git a/mod/nginx/bbb/loadbalancer.nginx b/mod/nginx/bbb/loadbalancer.nginx new file mode 100644 index 0000000..e254118 --- /dev/null +++ b/mod/nginx/bbb/loadbalancer.nginx @@ -0,0 +1,4 @@ +# If you run this BBB node behind a proxy loadbalancer set the hostname of +# the loadbalancer here. This will be used to add CORS headers so requesting +# slides and API calls won't fail +set $bbb_loadbalancer_node ''; diff --git a/mod/nginx/bbb/notes-playback.nginx b/mod/nginx/bbb/notes-playback.nginx new file mode 100644 index 0000000..16f7c43 --- /dev/null +++ b/mod/nginx/bbb/notes-playback.nginx @@ -0,0 +1,4 @@ +location /notes { + root /var/bigbluebutton/published; + index index.html index.htm; +} \ No newline at end of file diff --git a/mod/nginx/bbb/notes.nginx b/mod/nginx/bbb/notes.nginx new file mode 100644 index 0000000..bc37eed --- /dev/null +++ b/mod/nginx/bbb/notes.nginx @@ -0,0 +1,77 @@ +# https://github.com/ether/etherpad-lite/wiki/How-to-put-Etherpad-Lite-behind-a-reverse-Proxy +location /pad/p/ { + # Avoid setting the user name from the embedded URL + if ($arg_userName) { + return 401; + } + + rewrite /pad/p/(.*) /p/$1 break; + rewrite ^/pad/p$ /pad/p/ permanent; + proxy_pass http://etherpad:9001/p; + proxy_pass_header Server; + proxy_redirect /p /pad/p; + proxy_set_header Host $host; + proxy_buffering off; + + proxy_set_header X-Real-IP $remote_addr; # http://wiki.nginx.org/HttpProxyModule + proxy_set_header X-Forwarded-For $remote_addr; # EP logs to show the actual remote IP + proxy_set_header X-Forwarded-Proto $scheme; # for EP to set secure cookie flag when https is used + proxy_http_version 1.1; + + auth_request /bigbluebutton/connection/checkAuthorization; + auth_request_set $auth_status $upstream_status; +} + +location /pad/auth_session { + rewrite /pad/auth_session(.*) /auth_session$1 break; + proxy_pass http://etherpad:9001/; + proxy_pass_header Server; + proxy_set_header Host $host; + proxy_buffering off; + auth_request /bigbluebutton/connection/checkAuthorization; + auth_request_set $auth_status $upstream_status; +} + +location /pad { + rewrite /pad/(.*) /$1 break; + rewrite ^/pad$ /pad/ permanent; + proxy_pass http://etherpad:9001/; + proxy_pass_header Server; + proxy_redirect / /pad/; + proxy_set_header Host $host; + proxy_buffering off; +} + +location /pad/socket.io/socket.io.js { + rewrite /pad/socket.io/socket.io.js /socket.io/socket.io.js break; + proxy_pass http://etherpad:9001/; + proxy_set_header Host $host; + proxy_buffering off; +} + +location /pad/socket.io { + rewrite /pad/socket.io/(.*) /socket.io/$1 break; + proxy_pass http://etherpad:9001/; + proxy_redirect / /pad/; + proxy_set_header Host $host; + proxy_buffering off; + proxy_set_header X-Real-IP $remote_addr; # http://wiki.nginx.org/HttpProxyModule + proxy_set_header X-Forwarded-For $remote_addr; # EP logs to show the actual remote IP + proxy_set_header X-Forwarded-Proto $scheme; # for EP to set secure cookie flag when https is used + proxy_set_header Host $host; # pass the host header + proxy_http_version 1.1; # recommended with keepalive connections + # WebSocket proxying - from http://nginx.org/en/docs/http/websocket.html + proxy_set_header Upgrade $http_upgrade; + proxy_set_header Connection "Upgrade"; + + auth_request /bigbluebutton/connection/checkAuthorization; + auth_request_set $auth_status $upstream_status; +} + +location /static { + rewrite /static/(.*) /static/$1 break; + proxy_pass http://etherpad:9001/; + proxy_set_header Host $host; + proxy_buffering off; +} + diff --git a/mod/nginx/bbb/podcast.nginx b/mod/nginx/bbb/podcast.nginx new file mode 100644 index 0000000..7d8c0a8 --- /dev/null +++ b/mod/nginx/bbb/podcast.nginx @@ -0,0 +1,22 @@ +# +# BigBlueButton open source conferencing system - http://www.bigbluebutton.org/ +# +# Copyright (c) 2012 BigBlueButton Inc. and by respective authors (see below). +# +# This program is free software; you can redistribute it and/or modify it under the +# terms of the GNU Lesser General Public License as published by the Free Software +# Foundation; either version 3.0 of the License, or (at your option) any later +# version. +# +# BigBlueButton is distributed in the hope that it will be useful, but WITHOUT ANY +# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A +# PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License along +# with BigBlueButton; if not, see . +# + + location /podcast { + root /var/bigbluebutton/published; + index index.html index.htm; + } \ No newline at end of file diff --git a/mod/nginx/bbb/presentation-slides.nginx b/mod/nginx/bbb/presentation-slides.nginx new file mode 100644 index 0000000..1ea4c4f --- /dev/null +++ b/mod/nginx/bbb/presentation-slides.nginx @@ -0,0 +1,53 @@ +# +# BigBlueButton open source conferencing system - http://www.bigbluebutton.org/ +# +# Copyright (c) 2012 BigBlueButton Inc. and by respective authors (see below). +# +# This program is free software; you can redistribute it and/or modify it under the +# terms of the GNU Lesser General Public License as published by the Free Software +# Foundation; either version 3.0 of the License, or (at your option) any later +# version. +# +# BigBlueButton is distributed in the hope that it will be useful, but WITHOUT ANY +# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A +# PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License along +# with BigBlueButton; if not, see . +# + +# Have nginx serve the presentation slides instead of tomcat as large files +# causes tomcat to OOM. (ralam sept 20, 2018) + + location ~^\/bigbluebutton\/presentation\/(?[A-Za-z0-9\-]+)\/(?[A-Za-z0-9\-]+)\/(?[A-Za-z0-9\-]+)\/svg\/(?\d+)$ { + default_type image/svg+xml; + alias /var/bigbluebutton/$meeting_id_2/$meeting_id_2/$pres_id/svgs/slide$page_num.svg; + if ($bbb_loadbalancer_node) { + add_header 'Access-Control-Allow-Origin' $bbb_loadbalancer_node always; + } + } + + location ~^\/bigbluebutton\/presentation\/(?[A-Za-z0-9\-]+)\/(?[A-Za-z0-9\-]+)\/(?[A-Za-z0-9\-]+)\/slide\/(?\d+)$ { + alias /var/bigbluebutton/$meeting_id_2/$meeting_id_2/$pres_id/slide-$page_num.swf; + if ($bbb_loadbalancer_node) { + add_header 'Access-Control-Allow-Origin' $bbb_loadbalancer_node always; + } + } + + location ~^\/bigbluebutton\/presentation\/(?[A-Za-z0-9\-]+)\/(?[A-Za-z0-9\-]+)\/(?[A-Za-z0-9\-]+)\/thumbnail\/(?\d+)$ { + default_type image/png; + alias /var/bigbluebutton/$meeting_id_2/$meeting_id_2/$pres_id/thumbnails/thumb-$page_num.png; + if ($bbb_loadbalancer_node) { + add_header 'Access-Control-Allow-Origin' $bbb_loadbalancer_node always; + } + } + + location ~^\/bigbluebutton\/presentation\/(?[A-Za-z0-9\-]+)\/(?[A-Za-z0-9\-]+)\/(?[A-Za-z0-9\-]+)\/textfiles\/(?\d+)$ { + default_type text/plain; + alias /var/bigbluebutton/$meeting_id_2/$meeting_id_2/$pres_id/textfiles/slide-$page_num.txt; + if ($bbb_loadbalancer_node) { + add_header 'Access-Control-Allow-Origin' $bbb_loadbalancer_node always; + } + } + + diff --git a/mod/nginx/bbb/presentation.nginx b/mod/nginx/bbb/presentation.nginx new file mode 100644 index 0000000..2199332 --- /dev/null +++ b/mod/nginx/bbb/presentation.nginx @@ -0,0 +1,28 @@ +# +# BigBlueButton open source conferencing system - http://www.bigbluebutton.org/ +# +# Copyright (c) 2012 BigBlueButton Inc. and by respective authors (see below). +# +# This program is free software; you can redistribute it and/or modify it under the +# terms of the GNU Lesser General Public License as published by the Free Software +# Foundation; either version 3.0 of the License, or (at your option) any later +# version. +# +# BigBlueButton is distributed in the hope that it will be useful, but WITHOUT ANY +# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A +# PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License along +# with BigBlueButton; if not, see . +# + location /playback/presentation/2.0/playback.html { + return 301 /playback/presentation/2.3/$arg_meetingId?$query_string; + } + location /playback/presentation/2.3 { + try_files $uri /playback/presentation/2.3/index.html; + } + + location /presentation { + root /var/bigbluebutton/published; + index index.html index.htm; + } diff --git a/mod/nginx/bbb/sip.nginx b/mod/nginx/bbb/sip.nginx new file mode 100644 index 0000000..ee4993e --- /dev/null +++ b/mod/nginx/bbb/sip.nginx @@ -0,0 +1,15 @@ +location /ws { + proxy_pass https://$freeswitch_addr:7443; + proxy_http_version 1.1; + proxy_set_header Upgrade $http_upgrade; + proxy_set_header Connection "Upgrade"; + proxy_set_header X-Forwarded-Proto https; + proxy_set_header X-Forwarded-Ssl on; + proxy_read_timeout 6h; + proxy_send_timeout 6h; + client_body_timeout 6h; + send_timeout 6h; + + auth_request /bigbluebutton/connection/checkAuthorization; + auth_request_set $auth_status $upstream_status; +} \ No newline at end of file diff --git a/mod/nginx/bbb/verto.nginx b/mod/nginx/bbb/verto.nginx new file mode 100644 index 0000000..cfafdab --- /dev/null +++ b/mod/nginx/bbb/verto.nginx @@ -0,0 +1,10 @@ +location /verto { + proxy_pass https://host.docker.internal:8082; + proxy_http_version 1.1; + proxy_set_header Upgrade $http_upgrade; + proxy_set_header Connection "Upgrade"; + proxy_read_timeout 6h; + proxy_send_timeout 6h; + client_body_timeout 6h; + send_timeout 6h; +} diff --git a/mod/nginx/bbb/web.nginx b/mod/nginx/bbb/web.nginx new file mode 100755 index 0000000..c14e5bb --- /dev/null +++ b/mod/nginx/bbb/web.nginx @@ -0,0 +1,180 @@ + # Handle request to bbb-web running within a SpringBoot Tomcat embedded servlet container. This is for BBB-API and Presentation. + location /bigbluebutton { + proxy_http_version 1.1; + + location /bigbluebutton { + proxy_pass http://bbb-web:8090; + proxy_redirect default; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + + # Workaround IE refusal to set cookies in iframe + add_header P3P 'CP="No P3P policy available"'; + if ($bbb_loadbalancer_node) { + add_header 'Access-Control-Allow-Origin' $bbb_loadbalancer_node always; + add_header 'Access-Control-Allow-Credentials' 'true' always; + } + } + + + location ~ "^\/bigbluebutton\/presentation\/(?[a-zA-Z0-9_-]+)/upload$" { + # Grails can't handle CORS OPTION preflight requests correctly -> lets do this in nginx + if ($request_method = 'OPTIONS') { + add_header 'Access-Control-Allow-Origin' $bbb_loadbalancer_node always; + add_header 'Access-Control-Allow-Credentials' 'true' always; + add_header 'Content-Type' 'text/plain; charset=utf-8'; + add_header 'Content-Length' 0; + return 204; + } + proxy_pass http://bbb-web:8090; + proxy_redirect default; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + + # Workaround IE refusal to set cookies in iframe + add_header P3P 'CP="No P3P policy available"'; + if ($bbb_loadbalancer_node) { + add_header 'Access-Control-Allow-Origin' $bbb_loadbalancer_node always; + add_header 'Access-Control-Allow-Credentials' 'true' always; + } + + # high limit for presentation as bbb-web will reject upload if larger than configured + client_max_body_size 1000m; + client_body_buffer_size 128k; + + proxy_connect_timeout 90; + proxy_send_timeout 90; + proxy_read_timeout 90; + + proxy_buffer_size 4k; + proxy_buffers 4 32k; + proxy_busy_buffers_size 64k; + proxy_temp_file_write_size 64k; + + include fastcgi_params; + + proxy_request_buffering off; + + # Send a sub-request to allow bbb-web to refuse before loading + # If file is larger than configured bbb-web will return with code 403 and Header: x-file-too-large = 1 + auth_request /bigbluebutton/presentation/checkPresentation; + error_page 403 = @error403; + auth_request_set $file_too_large_header $upstream_http_x_file_too_large; + + } + + location /bigbluebutton/presentation/download { + return 404; + } + + location ~ "^/bigbluebutton/presentation/download\/[0-9a-f]+-[0-9]+/[0-9a-f]+-[0-9]+$" { + if ($arg_presFilename !~ "^[0-9a-f]+-[0-9]+\.[0-9a-zA-Z]+$") { + return 404; + } + proxy_pass http://bbb-web:8090$uri$is_args$args; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + # Workaround IE refusal to set cookies in iframe + add_header P3P 'CP="No P3P policy available"'; + if ($bbb_loadbalancer_node) { + add_header 'Access-Control-Allow-Origin' $bbb_loadbalancer_node always; + } + } + + location = /bigbluebutton/presentation/checkPresentation { + proxy_pass http://bbb-web:8090; + proxy_redirect default; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + + proxy_set_header X-Presentation-Token $prestoken; + proxy_set_header X-Original-URI $request_uri; + proxy_set_header Content-Length ""; + proxy_set_header X-Original-Content-Length $http_content_length; + + # high limit for presentation as bbb-web will reject upload if larger than configured + client_max_body_size 1000m; + client_body_buffer_size 128k; + + proxy_pass_request_body off; + proxy_request_buffering off; + + } + + # To check connection authentication, include: + # auth_request /bigbluebutton/connection/checkAuthorization; + # auth_request_set $auth_status $upstream_status; + # + # and make sure to add sessionToken param in the request URI + location = /bigbluebutton/connection/checkAuthorization { + internal; + proxy_pass http://bbb-web:8090; + proxy_pass_request_body off; + proxy_set_header Content-Length ""; + proxy_set_header X-Original-URI $request_uri; + } + location = /bigbluebutton/connection/legacyCheckAuthorization { + internal; + proxy_pass http://bbb-web:8090; + proxy_pass_request_body off; + proxy_set_header Content-Length ""; + proxy_set_header X-Original-URI $request_uri; + } + + location = /bigbluebutton/connection/validatePad { + internal; + proxy_pass http://bbb-web:8090; + proxy_pass_request_body off; + proxy_set_header Content-Length ""; + proxy_set_header X-Original-URI $request_uri; + } + + location ~ "^/bigbluebutton\/textTrack\/(?[a-zA-Z0-9]+)\/(?[a-zA-Z0-9_-]+)\/(?.+)$" { + # Workaround IE refusal to set cookies in iframe + add_header P3P 'CP="No P3P policy available"'; + if ($bbb_loadbalancer_node) { + add_header 'Access-Control-Allow-Origin' $bbb_loadbalancer_node always; + } + + # Allow 30M uploaded presentation document. + client_max_body_size 30m; + client_body_buffer_size 128k; + + proxy_connect_timeout 90; + proxy_send_timeout 90; + proxy_read_timeout 90; + + proxy_buffer_size 4k; + proxy_buffers 4 32k; + proxy_busy_buffers_size 64k; + proxy_temp_file_write_size 64k; + + include fastcgi_params; + + proxy_request_buffering off; + + # Send a sub-request to allow bbb-web to refuse before loading + auth_request /bigbluebutton/textTrack/validateAuthToken; + + default_type text/plain; + alias /var/bigbluebutton/captions/$recordId/$textTrack; + + } + + location = /bigbluebutton/textTrack/validateAuthToken { + internal; + proxy_pass http://bbb-web:8090; + proxy_redirect default; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + + proxy_set_header X-textTrack-token $textTrackToken; + proxy_set_header X-textTrack-recordId $recordId; + proxy_set_header X-textTrack-track $textTrack; + proxy_set_header X-Original-URI $request_uri; + } + + } + + location @error403 { + if ($file_too_large_header = '1') { + return 413; + } + + return 403; + } \ No newline at end of file diff --git a/mod/nginx/bbb/webhooks.nginx b/mod/nginx/bbb/webhooks.nginx new file mode 100644 index 0000000..003e35c --- /dev/null +++ b/mod/nginx/bbb/webhooks.nginx @@ -0,0 +1,9 @@ +# Pass to the webhooks app all requests made to the webhooks API. +location /bigbluebutton/api/hooks { + proxy_pass http://10.7.7.17:3005; + proxy_redirect default; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header Host $http_host; + proxy_set_header X-NginX-Proxy true; +} diff --git a/mod/nginx/bbb/webrtc-sfu.nginx b/mod/nginx/bbb/webrtc-sfu.nginx new file mode 100644 index 0000000..c226485 --- /dev/null +++ b/mod/nginx/bbb/webrtc-sfu.nginx @@ -0,0 +1,22 @@ +location /bbb-webrtc-sfu { + auth_request /bigbluebutton/connection/checkAuthorization; + auth_request_set $auth_status $upstream_status; + # Extra variables are annotated by bbb-web as custom headers + auth_request_set $user_id $sent_http_user_id; + auth_request_set $meeting_id $sent_http_meeting_id; + auth_request_set $voice_bridge $sent_http_voice_bridge; + + proxy_pass http://10.7.7.1:3008; + proxy_http_version 1.1; + proxy_set_header Upgrade $http_upgrade; + proxy_set_header Connection "Upgrade"; + # Following custom headers are used by bbb-webrtc-sfu + proxy_set_header User-Id $user_id; + proxy_set_header Meeting-Id $meeting_id; + proxy_set_header Voice-Bridge $voice_bridge; + proxy_read_timeout 6h; + proxy_send_timeout 6h; + client_body_timeout 6h; + send_timeout 6h; +} + diff --git a/mod/nginx/bigbluebutton b/mod/nginx/bigbluebutton new file mode 100644 index 0000000..833ef33 --- /dev/null +++ b/mod/nginx/bigbluebutton @@ -0,0 +1,40 @@ +map $remote_addr $freeswitch_addr { + "~:" [::1]; + default 10.7.7.1; +} + +upstream poolhtml5servers { + zone poolhtml5servers 32k; + least_conn; + server 10.7.7.200:4100 fail_timeout=10s max_fails=4 backup; + server 10.7.7.201:4101 fail_timeout=120s max_fails=1; + server 10.7.7.202:4102 fail_timeout=120s max_fails=1; + server 10.7.7.203:4103 fail_timeout=120s max_fails=1; + # TODO: set server list based on NUMBER_OF_FRONTEND_NODEJS_PROCESSES + # server 10.7.7.204:4104 fail_timeout=120s max_fails=1; + # server 10.7.7.205:4105 fail_timeout=120s max_fails=1; + # server 10.7.7.206:4106 fail_timeout=120s max_fails=1; + # server 10.7.7.207:4107 fail_timeout=120s max_fails=1; +} + +server { + listen 48087 default_server; + listen [::]:48087 default_server; + server_name _; + access_log /dev/stdout; + absolute_redirect off; + root /www/; + + # opt-out of google's floc tracking + # https://www.eff.org/deeplinks/2021/03/googles-floc-terrible-idea + add_header Permissions-Policy "interest-cohort=()"; + + # redirect to greenlight + location = / { + return 302 /b; + } + + # Include specific rules for record and playback + include /etc/nginx/bbb/*.nginx; + +} diff --git a/mod/nginx/default.pdf b/mod/nginx/default.pdf new file mode 100644 index 0000000000000000000000000000000000000000..8a23582439fe4bf0703c51fc7451d28059b7a410 GIT binary patch literal 42107 zcmce8V|ZrEx@~OR>exodww-j6j*X6Ob!^*CI(E{rZQHhUlePBRd!2LkITt@}zGr?< z;jNlg_01>sj(3cjByvI`Gz_%NP$XO1hO+A!PWLSpV-;&*AXCxGPw*A z0ec2}JH8i{Au>4jz9jj=H&IvQooR_aGc7tNW%064(>$LA_d1DODw=o?+Wrmf^%Pbv zv!TFx8)H{>9sJfqu3>$(!HE7;DIP%hATlyCJVJKjy&DWc0^9p;R{t2RKVk3P4QGbV z+x?NRJNQ75Ss}Xt0v;tyuKhLVjeq$RJshJ4NR}mz#i`ab-WVk$V&r9%3hfXf=w`rs zD+;mq3-ZLsfG?69k%WLsT$q9!WwlQ&TwI`YTm*t!1b5JxF`4-K#r0iiF#s(#LdvWE zj1{+FXh$y3`ejIwD^S}{ZIN-$a#L_ufDF-g5l%nP2yF!k@QE|8&O2sT1_f6j4}8n_ zmRFS=%g%j-5wGJT&?Aonilj%BCkAR~EVS=)%F%Q16O|q5sBge;EUq|h0NnVZWScJD za>e5l+e+BYsZVUKNZi4WBK9lLuWplstspeOW)B*HV3P;9QiT&dH(W~~E&F@DGGlM3 zy=xB)V(rKeC!$mnOUojy-tQuwCfEa>Fl{9hr2AO2(l-)EEFme6c&;!!{0}37*F~cT zn5_ag4xnE-mzFhNq0vV&=WSK(6mxgXiz-V|+gN6m<571F^GSL^A2V84(xeiZzNE|A zEg)^dy2_wqp$B*y?O*&^MnG|1eRM{?Q~k)rIK)IS1?MGAVH&q z0}rcuMiRj~VLYpQHaN_PrqHnlzDSLTDA_*-&$YFiuvcP6$;~DeBe~?aPe4)W}Q{Jc7s@ zK-gE7=cim1^`%*oV>eM7VIlS6a$(kbTF3#VKm+_kU?}&t*4&XJdi&Aa%l9@T5Iz7L z@8WKL6tA1O;HPXNYfD)FW5*e5?g*jC7Rdrv@!`cLG(|Gg#jo?WcFuJ0rHfN|NioS` zdw^@;7`#hl)~DcZ8sZ7Q^TW+_<#3*qW8dlS?I}D?36!rpp;|`10-|9!JOfmrFd9fy zD%tAUDX^#P8clGx_yM9@B6G8I!;bs%HvJFaX|3VN7D~Iq*WWJL+cHbG? zuC11X3OjT)i03I4v43n;L54Woki(q?^Ajd^``B=%g^N+*?g49H#Bp88T2Kw7E7Te` zO{D8&_mHUN78MXRy8>)#@f*D3?u|4kSfGJsr2~NDQWW=hhXFlwY8!NXj1!_jFbG4f zz(bs&Fh=EdL=)3}O$|Sv>R^IRu&#)~i`CzO@M_%_ojV57fqi&cW%lUyGr+%T`F$!& zeGALRCOXT(J5ebaW}7M905m7RStAIJRX>Aa6TOPPnoFtc$=piYttjO|xgVvLMbL^q z42@=IE#f6(h?qbWN!v-=gE<3b$#;qnB2ps zSF&whx7-Po6a}2JPYEKWZmVexu4<)p#8xWp#LPC%vRugQF8Pe2J0^-j6xFIO(&OLD z=%>8+YQTG~fN!CgHG&~DAz2O~c1^H`MDG;!DBrBxnjN0ohJ2K@JqC`5U$;#LW~=9SJdcd@7c*#0 z_tmZ!wn4;o?|$GYs)=B?__av-9iR-V1-pC#9da}&>z5Zlc(Fck4%%ym5&gh2YWVoz z!5@PO-z_ZApbAKgL6v_ba}N%x&*8D8&*p{*u2HQ~{n*W*+U+cyuzA zF0%C9oNs>Y+XO$?T5D4&!nyXXfh-*7;ZYKdN`$?NojmGqSf$nGH@Wm&*U1sh+u@QJ zK`DV*fJsM#JLEfUM_yaP)PY};F;h2ON52@pDMb!K=s{of;`x|FPTLb0Qj&NJ<)h(_ z7UpvnZ{;+`*k?!mD!MlLs=IyoEWEyheY{*>slLO0fa7*G!c7-o6iKUb#V8W==6#K& z6K;*MH0ZwTwbhkCgPwV0L#L+P@_jSH=Zb~5pJALUGItFlE>Z=RHo97u1W?xz0U^SN zi(P#t`@4m^oFRzN_C5kAJ?if*ig*d15QhF@>)(BF6ckrlrcr=@ipFPI#U?&afjOtbvS^ciM03-z>0<}3 zwbW|mHlXcQ5^=)ZwT-(7*OqMmY440PKHt3u!r1#f@-v zx!bP}x!Ye5{6>&TI}-iwj#q+e*{oJWqZ}SRT;|v`W0W#HI&IffY!=~|{0mME%9qco zx<7zj(xt9{n*l=L0Xm^;TBdOLP@!0vl4Vr?w+XA}NJ7jMR`U!whN5W^g41`D1zxWQ z7kfP=DJcsndx4uo{J^KR42Fdq{alVVjs71cbTXdVTkPo?# zj0_C&BwUuNA4FmYm&G_Wn1(g$mWY4jMs9C&c*XF@Uii6zjdTXNG{g3&q1u5eUO)gP z3G?%$?h%*jXh=x;l(j7>0~hGBVs4EJ=!zFZ;z(j)Kvw2z^?VPR{knu=-6zZIROGM> z9r~W~?N?)UQEp%Hlep62g-^?PTdLMz{?XJ?x`3TFP`Sh1)NFXb@Y1(Rx$nvx-3DNmQ%UXgpXU|3PWQi0oq z?t}R5QP^*nqr9tynapa9YeXn$8M-rbA_W;p=e7&Gh62lXBy^osk8n+8&}Dt0bp+A7 z10#d#l%PpS1axt5XhcGmWpbVothKK;&%%fG@O)ULz|F}BxqPjQbc{TWd*!&+#K0?tq(-8$uhLh#;{%C zPLNb0J;7DR144wc&6t-+Ql-C`x_IT_F2~+4>5rHn+41+^@J&q$Y}~ zBf)XIRJoWZT~s)H{fV?-f~q=lp1z|!tpdwC*|7UyqU&n;-C3(QhPzwu#B{Y8^*TtH zDt^wx#&UGvDCy}rA)e0l>ZOl`x4lEL3Wc`4kRSaU1q?p18Xb^$B3Me1caHQ=-Uu|c7Y=3R8?Et zsqm-@7*Sx2kpNdSBVKDlPZi2gQ~e^o?095yB){Lt8^|<6Gh3|eS(KaVS{(~cw=I*0^Z-UxWyL+h$ovS0`hW=IA>*8Z_oTsRUhSnjA3L&!#}DE6~mEVFe|Ds!d2 z*tV*-T;ViN-A*OG)H=1gKD+mZr2rI_0KWv$CBCOxIb1vdk3u@JH$VQ#(zRmFn$i5V zm-0kO7d3dF=H#p$N!=1jFrdjqfJ?A=v5eZB!E2ft6%uqy1>^#)YBS8~go(1vZ9S^U zjm7Gk@78-KZ;OhvKP9kZ{?zdv7Xz0!4cw?xjt()jF-~y%oLDF1fxKFshT7%Mgedh5LUfbE?Eu_6CWl9Q*RlnxAkM8{j@wK#$7iVZ3|7~;O z$@r<}8Y}~BksnB)dd$ zqq}y>tL`bpDlb5S$-&x2QLj?=O_@P5Q1>Uvaf%Q(DA<4@hDH6Drr#YyCX=bU`y$%j zJfigN9rafRMHLDbyO1B7c%Qudr1-|yS`C!)0+PTkGmY+F5Ew{;SFSXhJ(}JG4P$av z1ktdisD@SE4C(l;FWJas7vSiEqYql{4$>yjZf7F+sMuE{T$mZq^W;-UI=LiQWcpn! z?!*8efMdZ<2Nlo-T!0eKQHz8URdI@v#9!~4SXM6#B~|5Qe1@!~vpFn{ECGg&`X*4C zvh^~q8*p&MN-)KMOn}l7DK5$YvE7iv1m!$gRinK&!7CtXI21$0TZY{H5Si^orAZA z*3B9!Y!4#n2D>9V^4r*&QPTN=JTM+BRmWS}lsZedYZc&dw=LBP_)9OH*RRPHZ}=1p z9JLrbNs(zqL^!cppo3|7vSXKphod;gvvCiL)}e#M&?NGh-ycZ@)=`*ho}cxR`>)iH z!ca#dbb}|Pau!$*aGyP$_EI6^U!9c8za@zZ*bQF|flyjr2fU{H01jp7BqJT^DOVH> zS;yKsCN=K zUyWouE2h=5TP<_8x@2r#Cta`nEH+r-(q@2dn>a0P!>3J2Ybyu#8Lvs(SC%xMgtQQX z1{AGiFT>ca#vhfp)$O+Ci-5@bW^k_pCm%MpnnfB#AJ|WJVD<_hc zKj;0@H+O~;$YpY3p|DA!LxE5#5>>$2M9E9O9P*uj@ElHw*a)3i}`O zkciS8c|}CVC{J}mzxGwA-E3E18jY!+>s4;VD_ZMs6;9`LFSj@=ze&9rTY3r_uN6Sv zzs%O1eK(d&25#B6G;t^lrrKLXYdF%XdjwsxNS%y2#ZGze@d3vRAE(`KYSFMA(xP}e z5L#122hg&p$y8XhfTQ;GNh+Jvm8ND*y`54)t4UkJ-wyAR#~Q;}bUSy1?wOmX?5xl# zB^K@_c1(mqD1Q{wlQuMVNO*mIg-5l1N%TT&6$hnAIar`8y07=RTdYqW1Jw=>`Tjuv z{ef3Xc;Wav1-CqzFmk0nYl!qyFQuY6j<4wX#=RA$Zw0xA!u?yi3xPJGetdqM0m zZDs6jSju>YwYHdt-;wi%bvnQ^}DMa(NJc%uQ4>)w%j8jJ{Qia{Iq?Q$-w z&0p%H*Eg$5xzu<*zOKjWI~J5iM<9C-0~7;rz(?vm&Np zDck4*9o(J@<%*>&Smh#>Prb71O2^q{lSLf2n8~iIM^1arwsvWj^~o)KUxM=2GB#}s zmW7aS_mg|_n&X)9%x=YJNUC$t_%8G(=&)RRK;lUy!ZAgLyMja}bw_Sw`ePO0NT6%@W@bLR?{O!E zgGM?68~Jf**B;3Gg7E|@X{HhKN1hae(e{mN{2WAz+M9#xl)UgCZ|`wTtI(^_tIBVn z+f&%@dJBi1qCDwy@9x6&C7kiR>n2a7@yVf~D3lwM~#Gzu}+t_Aax zUOLe5(xj|YaM^~rA9^vBg;%`=oeEsf%@9vfUPF1inYuE}UQ0eV4V;w@ZspBz6NK$J zN|^vss^q{vg7v7teCumDhFM_QFSfmK-#vhJM0EA>zz*+x1)^Z`QaF;pJhLj=FLwGu z!xVwm{fgihw=SFqx2;ID%l%+ki@a2@;UsZOv;pgI?F}=T4#yNGV+d1zNCm%cpHY8pA_xQyb{kv4k61$adfPWG~jD3<4)T% zh)Z%w9K%V`PqAd**3Cn)WD$o0vAhw!%+hGa&=Gb*1(j@zw4kpO5E?#x(cD)jer^!q zIW0bwxR!DniQRS;9zd`;EG96nWq$3)-pyF6rL}9Yr^tMou$lbK-dEmB0Iw$#j3qXj z@Z4a4HD70Zm*lz4gs!8pNBw@i@{zreW=ZW?1u<^UgWc$2G-xCb!@m#@h_^pKK*hml z0!5MNJi6aUhoU?=`|G(R1nXI(+DooM{p)760jJ#~TOyrSLGQ4@PkVKVs@L`> zh5h&P0!}*J=Ii%~8rCz9kv86E9+qbwo@=HT#g1vmQ|2@7fQI&vCe_x;Z*Bo=l;^P& z>O53BQKsX=Yl+3ikwc=zHKKGl`COu{qD98qUB)`C7esXz7i-;d`xghBOhLx*OyKZ& z%P-&;2cGUmylZs%3wLMT#`oF_l3L45EkPQMrN;9Iw61z9=GZ|L=LsQ};_TT>@?r-{ z(DsE;AjhDt@GFw}K+7@TV`<1ygO4ryx*BB)qRw#g8ARPok2H=gs7UJ362Pa`|A?}Bf+KIJdi^`E$(Nn&{CLPb9+oq$4q+{r&hF{xqub@S2vcoq-Vej7A zCN`?eoPsW~MBoWKmtxjDpsca2PM$e7q$!No*9~aABVnFLF)a-$TEGsK;4&V;&_YV- zw-z36uC~Z)NrZk|z>9KIY}VKTBoEH3LWO#YGi53{PQl1jJ10elL!a1QbJLrH8_x#tuRp?(3#b2q&X{ey@lr^!wz9 z{z35k#TGICjV=0&qP4O^zp?&&q7bY!9cGCMJH~rYhkL- zZ)I#@NI(xo$8V=^Xk|~pLC*q3_j$w53=Jd0M?ZqPHe!aR#wPXztnBPibc*(dmMWhu zqzrWpOs$Lwn3$mG)c#??!uVmq?`W)QYG7|dz{37fCH(;^|Hv`Wb1?rost@YRkdfdI zZcO3tWEsQ1o&Luy9rWz~AlsB|9SlF$1&U5U*Us>Bg8%(G{D*}jVEkNK1t>aUs}G{> zbJQQ>(pRvy)V2Dn2ws!V{Cc3tNET5F_KQauA3{Z4`Ou>#olaY=6qoe=s^FP=A zy%j4f8v)zLpgyn9G%LgB|5y5RJb#rl z{?RwfN9|{dor&q6HGf$!eU^RJ|JCXv{W+q~vX4va??2V&2>y3OC>zJ$Td@7Dg$%*R zgTepFe_DNzp9CKdoTI6}p@OKuKN(O)*3U=p-*KdWqeTJJ6P%P37tw;o+t}JBkcbFK zZm?V-38py3P(kE>ihIie1^SaZ3W18CAX2CpD0Kpg1`+8Cqd@!l>dix5A;|UpmKPG< zT@5pbJTGUtUQ2oHS6^S;Z+KKGb!29SI41?jR^1N3IjRzo>E{{0OJdAR2u2m=8S zP7Sbg)5r)!^g#${=B150Ik`xzujcNI*4v#7^vd9r8k3}rN(b+VIt_!y(_H~ zgaJv^Et5^6Ps?#mPu1u4OddNH*YP|RU%3ZdpOr63PsTI&!!p#@El$el+Ry;*h&57i zYue+L0ctJ5_;xqTWX}lzu0R25*V3->0xh&-dbVK5phHhiEbIU?&1tC)*q$?et%o`S zIqs8(&z@B{3($FezKEkT84B+f6c$Jdjf}v+C5{`s-XpwkcXF7H4t7(gWp<)p1{^5M zAH+fW|1@a{;PO$6qBVf>_nO`G!eAu>$Mj4$M=^2G0q_XmkZTkNAF$>I5c~p6Y{mOQ zydG;JFhC8ox0;kkSK37~y1wl{;X{85vD%d4CsN$IgNZb-CCQ=2%w6}l_zi><1#X`O z#q2{%4@CVP7RQ^(26&ACz~5U80XPAeRt(6L52Tu(X8{0;pJ4&e(a+2VV8+LF0z}4_ z)duVvh-xRS4K$3mC?8bdZ%KN zQ3x@7j}xRt7?ge`*@AN1H(%_rnjy6Op0dCv2$;cM0X_T?lYpjmVHW`Ey%(!EaUevy zhBt(qQPaTDJC`>l8qs**8oJOou`d9q19ZA#A@~L$fG0)NAh0im1ftZ40TBt!qlxl> ziiK06;PSAW!o{M0>;QuWW$Pwkp!H#H2dRf`>Z$0?=}FMfCg~@sNTKhs8X++GJo@JA z&HgG-BP-)|gk(ib4X5c*`5yUQvnufy@Ph6MCk|YHC*~H8jb|gqH%2R#3Xlf)`LGu6 z<8IhZ(o5%7$~Ba8U-;j47kci5?NDAgUU=(~_j89Ahl58>GVbv5#J|ULCbiT_}W36=U*CoRx zdR$SqyyxlI!`>VGgSHz;LlQ&HUGiO=U98=jAqB9m4@}0SfSDf_8`csQdCk1nG`>B) z%vx!x{oYCu|43;?=^jrMZxjz0A6X)$Y(0xIn_gmBB0EEGMrx*OwrKV|OIsQ~UO4WQ zF`ll&{J!%NbX3*+)3x!*?6&)e`zRJG1*;tk6H6H@k-3irGsP-pFl9GopSi!*L<71G zL}OQDzfPMzKKX1!$%v#Tlq0*m;wkn?@@#98R{cyDj}Z?alvZ4rSyTGnJYG8vW4>oX%3R9Q(w|ecqOM*hGMB~C z%{Abj>yr9N1il#$-O07t9OpUKjp$@8MS$kCV)Vm{(I05GtzR=% zu)0@QUQDy@*m3H?nW9y-iq<02GJXelNAyhk%mB_EZ*?A$)SDv3d z;XKIgj_su$bPu|(K(CHZtJm`v`%m-FvLNgrWsnC@c_1PnD&RcOjF3p64j|pX0e`pn z;?qs(5PzFN0}^-0j!fwKe!jg$<)ao@mB4HX~1O?n^_}Hj!#E!Pb_fxBqLE9%=P}^s; zOy3e}PL^!zc8iC{E24CMtN)|q; zL^q_0N{fk`tc|XlIhS6rdB}A3Oc^Q*d_IF-at9lFM>dd*nW zFmp(L1aa?XCuPTK74X?x9!J~ zmIRh!ixsBFaGxAyTRqfwRYR>oi1eyfoH`{2&G~q0IEl@2^SB+}Q&6X31 zv?p)%hPOhtWJy+Hj@Q*aR3BGI?)*>N^6&DWoi-dHoK{v3o3GW9_0+W&Cn`3y%6CVu za;_R$R8QJfbTxl`hc}Hhh^%ESs&130G!vaIJjy%r!HP`TYddmlI89&bU!Lz+xAd9@ z3ltN#K2JOhSn(Iv#AO3je57(Yj@2ayAki{*ubG+PiM64ZtvPF+=m`(uan@# z@Y=j1uI#TSulgt3Ib=|>lo=(w<*&03^G3yOHz!&1-^Xi$jUxja1?k@7A1h_MLC1^7 zsYGf;jzr5v$HN}OR$YJH^j-{yB~EV#bWpt2-^WjuW_mh3w;AgV_FR}x9w+lktQEC$ zJk8x%?i(EJOgovL7Zir9mA3QV`MhR7>)-T8AD+lQ$i`=e^E$ahzFawzS{m)G-}_%b z{~OT7`VYrclTKVv@Do=Ue0abgWeQsVRPk~A&3FA1m#N9>nHlQa{|%dnTk0Br0F!@U z9X1A5D7t@uB4&Dy4-{o>VQs5uqpSZI zSM%@Mzz-)`Q0Y&Z;0Jj7Ga;vIYx#jmtUkyCf=@-Fyu$~ivv(n2qyKQcKdkiu54hbQE!UKVdlN%#P+Xd^+4u&Ge8KS|OgeqcB ziY6lX(#?m^6)i^$$)=GkQoSMf>yf)muiolTqfX z(>o{CX*?WXJ%WTfW^tLn08w-adE61$s5JJ>468{l3#<8A*U4hJ-{fMk`FFFtQtPq~ z#r>>XLVYG^9Jq%5iL<2B22p96A81p?QItrRazB6aSp??MAb|NIgamhh-Q4pH3{sI( zQhZUCE2&0SOC{j&Z*5KUZOzH?^(~V~m?aDt4bgPOt5#8Pf|Q$Us=d7ke|{X$VvXm` z)#AW+!soz#^n;SnPu)}1aI?QZIy`6UOG)z7H?lG}KKJHz9hn?m?VcWfMj^add4%Tw zP1sX(khIYFFbv5S8>48o+8&$orr+l`Qg#x=0XoywFWg%jGexE>h1SwsP+#%rGE7%a zr(OU0R1ms6dVF|#jPJtxq-)`>XKUu_se2jANB-nts=fqI$g2~H%A9F8*-+XmqvpHO8D@BfJtH=&YbMm)VJUe60<&!SML&MvexI6 z{520m2Je*qeX%o=x$OWWs+y!VNCqYb1oABJCxLjy1-KzGQ)Mwm&nkDbIeE2aVbxKv z$W`mo;We*48obf;qsZ57%22_ik&bquZ%MViJ$n){Wd@R#Ppxs~7RBD-)%o5)P(^fd z91Z(A{i7M2xO%#6LPJ}D)qVWYAP9Uf{aZfo2jU%D3?gy;$o{fNoPg4_*B;U>8%-*j zLb=NTEAdmu5Fx{CMh$F z2{9e6AKkxDhK48&)dmp}5{&tu{X;SGMTSG@?Wu7$o#V)uC1;lNkAP4*3+tByx_v z6|>+A3aPp*vVx?MOL@cqoB8J5QXf*O3%fp$xaKoR-+|cdGa{>UoQ&xq%p^f+Q8Lyl z(&vUnCL@_(=!K&f+9iz!cb23r4dhJ}gFSV_d2Gs%tjaPlx1y%^2 z3R@Re>dqcZoarm18D!i-58oJ28F~<@3w-k07s2$__Wc<55kyynlV^ zEu@yTb!yU%hHVwQZSof!WI5wy;|pMQE{QrsTx8D_Z(u_??My@X_Gr}sXUp7u>jRr9 z*tJFhmU=@C8st=-G`Xaki2mX3HU@nqukkv5d!ZUT?XTOx>7MAo``_~A9{M;U^7@V_ zv>&X5eK8v;-r^uo$v;r$8P=GV`ZsVw|u3*(mmNk;(Ydh z-2$2ZYvAqwdVdqQ`iSP(SbvJy3G6?@bpLM0`y;@kD6MN}PQduNrGCWY{y%JgAEB%N zxcg0tOCihS^k3fZjm}|m9s-oleov7>*OZ&CU( zID-Xkh$XjLK~U;MK6A}EbHy|c9`5yXy}EcN%bb4COmx2<0la1yL=cWK5LYr#S3wn? zZKWMOoFQ%GZf4tn28GSsgqM|(f)(AC?_i+84MitWELJj+Fm*9<{1Bs3n(_*uD$k~A zv=7H;hG9hS=tR1tA23>?abmDQ6e<=d)}%f_WCP*iUU`uBJM+gG1m$Rer_*-H_pxU{ z0*z}fpU!1mG)HYm=J&3Im<<+T=d|`zfAkFqMWZX$XtsC^i7!>DRBy3*4Gd3Zowr_Ww%o5+b-O!T_S)FU z?iP)}<6K*ra@^V}o;DuK;Pl$m6Wz}rR;|!(`BC#ccgpE>e!Fsh>~(Q5*|{-s5nDK( z(c%1fbenpz>~zlS{&Mp;tlAQaB;qw<-C|!Fy6w^@>4?62bPAk~{=&dOrbVbczlQ}1 zYpX4;XzrHyMNW-ltD^W=nd|q14Y_$?c*3i?+BR?eE6npOi%=iEnFrpqIKyDQ(2^rL zz`y&u&>?39?*OzW`JVf4p7_6?%lOly`;P}X{(gvq>Awa$KSR%d^Zh=1_y`jJD<~~y zXyIsRZ>q2RKSrECe6oK;{+a$B9sTQ^2s8T!aqyQj_;+9MpPn5PGy8u$Cz7t_qLexx z?{&UR?1LacV9*Ic?Bfp_EDRmmW0e486x->Io4V?)4(zC=uP+Fp4aP~)Bcn$){R0Qy z!E%fJS8A=yQterCgeB>Ao>k1%Ofa}TwZPT!;6(Dxc3DBu7(K8Qpv9{;>x!SjprERFMld5OZ z-AUdo93ZN|ILLd>lue zz8w&iZ)b#TSAp8ruNR)H&~557UbHbCY3n00N;FZ=`=aR2mcs$bxz^CngTs4#^>hMz z0O1PTmP}#hk6&-|HD%_jkQ)XrHFq{>YV|^$I?*n*HPu*Dq&s=*f_U7F%?-&pgzHFn zS}yQk&trHqf6TT2e5I43X`4ahZ&zYJa*VraUCOXHbG~(rB2~HlU3m5^?%=`@8K!5N zEPwOkM^2^w{3V3gpF89ZHjBJ(9QNMZd=o#{fzuQlV<__~++*XM53Ey@= zriIRybW!b|?(xIrPDP(@r*LCePcgpTKG%P4sP^1OJGvUHs7O;Vo&ZrG` zpcnO(e2=FgiIstz0jbhu7FHbM`$fbz7X40|=H)Gbgd#PKB6zm_I?O|G_f_Hf}T)VNa~1{fWGi^cl^syxVVaB4&zwSeW74y zT;v@|)Vv!ianfiE6EY=WZ4=D)XY^k&{Zj~MUXX&MGTQt-h9$1oVO9B;|%aQd~ZpEv5 zmR`T=j(6vTj*Kg#c{|qM1_qZr`OeZ0JnWp_BzEGZv@Uj7wDCC(zFhZYk^w_<1m7Cr ztEpsM-b5A-I1zNf%Id7e3R|75N+#H7oXt_Qy#6lmTCef(4o*uaTWWb04`H>-RXt;f z!Ujl2r>-MXf}{#2c2;Iq6mOT6@!hy5^ZJ?`yW!3V9~s*{y}g6kO6Ft^=AZiPbmHIk zoZolVX8CA)PxQK3k2Zx6tpetz3;z7$dj|~NRsRbXj4}N$6-mLX#1m4w3lhr;!{pg>^V9nHz$IR4k<>KyzVow?9LYTq6>oL^w2m~4J z>T#u7IioB1ZT7qFhZoklRNJP-lai?+%!2T)7Nz0!Uj&4U-b_!{FkAe_+bT$1kQ`EhnM<*l9sJ8){ zzxp!b^J&SY9Fjz*OgPSiB7!oRT9O$Jgpb=&y4MVKOWA;gCX<;vVkIJ36yn@9=CJJ< zaUfF2{d_^8HM`xb7SjMU=8vRZ_EILmVRFSWe2r`lLeIxYQ}87q<-qIL(k^QsFI0IBz2yB+elA zyBSW)%RS=!bKD)8OtB@TNTiMM-kCQt4J$icx~`Jgj+~w54WNnvCBBj|8tc`t)vIhT z^^K6$;Vx$^$SI;oOu|S;^G;I3{l3s0*>|iRXG<9{)_VO_v67Tzp?Y%1FTvD&<4ZN5 zWTCh;!l1Y6AN!cZENtXo(*1;$W~UlRmocp}?&ySPW4g|l!PRYnR;5meCS8jY2Z=Pt7YU87HKK<7AK* z)td(IPHS|Z?~XxkQ*HYfoELGn3;IM7y>jdJ>LAem?@-H@b*$Q^(D?;3#*hXO-Je8- z$2#D8k_xpbJcMVEL%a`{CzAN5(v@zvY|~q}oF%c>6+MT`gQ<@LFav7v<6R zyW4uJt_Jc3jwA`}(6(AZ4Z1jx-R@H85U5yDkBHBaAB2_;3sVY8Z+GBMa%WHMO$ID+ zfIwlUrS&})Jw(k`m1h#lB9Qc3?FiOi1@E9fj*Z0{T>+>5#(F?0y@)(=SoNMv_0+PC_OL!`EOFjpOIVZEn;MDhL13wnKLadDr>hD zUuI0zeA;Z+x1u>mj|bzfSzkh2-M40oO!HlYU7BwgKuy^RBq;*3F)N>D=9E#r_|OkS zuFE_X%pD!8=0W01d9Jt~#0WZ;BrM7wej-S$NCX&UjITRc8eQ!(fx3)qFW;}!wPEf| z7Y6l_IxODSkMw<`$;WG9-7kB(n|ipIbxAZoI~s@_qi&yf&f;OMfAdzQ3-Q+8Y#ob? z%T{gkRrDpqJIrP-P+ky>dFfKP!R0gwswkr$l{KRqP+iI#m=tg1wTTIsU-i0KD1*2O z23Wl7XB2|csU&cK1P#>=7en+K*x|YjF(R_<8-ust~ ztJlA1X{!?rq}+Wc3#mrK(A1cVw-_epSjmjUHPpvOo_XYspS&zK!(Xq;GtpP@A6j2m zo6&63ZTzaOxS)t;MqlbN8gbBQli$HrZ&2vXw6lpmqsuyLr{doV;1jKlX(asnM2qYbUEvV)jSnSw3o&Qp- zclOOjWda^`Tt@%bvcMH~nuO3Dq@QYpJc3qHoN8R%gY402#9Ia@KoPtYzIgV!HfT&U z4PRJIsPyz|4WrNSNLRTr6VNMWk8+8s{fZgx4NR;WSt%v3Gq5GXA=wm7ID^K^mygaL zpksmqs5X+;>?mG-?PxlC7UDWnW{C=eBZ6MzPGkn| z0W|~7O#pI3R~u!YySP4}+OuZb-}r57pq3(I(7IMYtL2DB}M`b4b>3d%+{k=HGsX#YS1Neg<{T%4J%8aiReKX#k zFi@dtR6Vd!EfMUHFi8=VGEf6ZFVGa+49IqCD}UBwT{_?O)t0Um-GI5zW4Q^o*a4DC zxwjLv+!I?&9Nd1rP#nkuQouP|wEUM5jEk!;ZBy+(^Jd?z@vtVsD;J&C=pI%Rx=p`e znE;3Ii$vjpGfI*3PY`1!ZxZVXQfx#XR=M&}3JD)*dgq|TXWQ~A1(X0@i(C-iyfZb+ z=lmNC`WNx=ua^mY!pi?n?WG_uB>A7zUNi#M76u;(@!v_l7}%Nrl6w41z8Dxj^kkpY`cw1u2hRPw=8K8tzfddx8C(Bb&Da018vkJp379@t`TtS) z#qhz~{GSNFK7~`C60HCI=TlDg_v=&E^&ztQ5L|th{w2KnOn(Z(K1Eu8Sbx<2!}7D{ zA9AtJn*T`*_NS!m^ZQfa_2(ErbAO7|KFj`0eU|-cOHaVW&hax&&~h=y|t=?d&&=?!J))!XddJN|)Vtn|Ic>e~ zQd|eQP(@(>CbCh%a7c7jN#JGeVs&Ug?D>FpHPT`2s5=#S9VPhIFd%7ncNpSMbOCmC zfM4t4R$)IrAJH1|(lPPgJJf|N36B0GKUW=irDpg`$>q4P;}PzPy4?W{q{>}Q1PRWY zUeG_$|NQ6vcYL=l@C!2Ua^4mVT37;iokjpJ|Aqi68}$e7R=D@)go}!7-0k<7p3p5} zcDV07<(I!+OssaMGhKIt?jCIRHtmO`NFy*WbKzleG2Lx8e7ZAFnmSsJ`HXk8U-9a4 zX^LvktRNpes)?bvM6;;!3q!F^sTHyn#|@e>E0-Y_mc5B)5hTuMvNa}^ryb3(iZ~oX z3*_gSQ&NN`1HTuwNJ<`Jwdox#VR0naMDZ9OfrhuAPenG?P8>3}L_-Em3P?J{Nr%Ve zB;*|K+OjA!PwJGDM9swJ^Esi<0MUrm69~f}#A9*T#5ICA5wNIjcQ&L-OFoJXIUb0O zNhD|6G;Zic*?Y)Sh#Sr)vUckQ?$>I}=O3xgqa=~{IdU}XdyVC^hKnfkD3|j{`Z0XG z<^qe`t_@g`c9M2gYBDKetU7lT5(4#0NQJUB09a+m`}%z3p#x=lS3S@n)Z`rn!D{O?aTTnX0vCDs8io82Z5>*t ziFnBJ7HHM=Q^XVUdGdAjv?S`9Lamac0(G4RhEOb0q`=eNl#<)Ktahy)VxtAd9qOfA zRg=_=A}WHcUsxkMH1j=7xt8Kkz2f5`cX?81i3ufWqpsc~i5WGto!RA1X0I9tb%GP{ zplINhvU>7g6_tmRbIcfaHr~>v%H)!;K)<#7TE#<^Ni+-Oo^ROeI*H6f*|i9_WREZ= ze&HG25^(oXh6bbwXX9q@2Lc`5tuD*?NjOSGXi~IEodK5X_`n|+Nz5eG!!&I`ibCsu zeA?lUa;1m#Wu{>?(U*^@;EC&OhUrjbw@1O<*XB5~l*2+|$W-QC@xAR!$Jh=jBf(ukBG-Q6J_ z(%tZFQ13;(_x^tG_k8zxzCT`+!`ZWEX3t)0&6+iIzk14kbf2KX7^h<~ z1M}}$BYdy7FS@bu=Cz$E<>X$tO2pY45|)wFZAVU#eJ5J*7NK0;ls2-|JP!>6x> z%T7Cp%{5XhSlLOYuM0pxc1!e#0F(Uf8IN8$qI9)!ZNW~!O?>-oR*0Y z;faJ(dd)ZAznA?;>PXQ;jr%-_QKf+{LLmYUK4o(bK^S{g|Cu@Zxgf~m5!rJ@;s|#1 z)0#Gt>9aTL6@};757Bh{S5{&M>8U@Ri*}WEfAA%)vq5IZq=;zN7a$Z&Ub)FMpAL)v&53PBIP%L?^R$^Ts z8ka@JbA&5@3ZA2YDNIbj7&<@<2#u%e5lw7LbbiHVLGJg2ZFpv$)*;sG* zSm93u?zJzaW~%OKd1JQo!ZYJOcEadUaqfNDnd**Ipi(I|RdW^)_ck+GzjklmrJrhA zbVGTE@t&W{$dVPZ$$}mMMYtAUhfXjpQmQdHB)Vwbob>HWzgo_1h8tDRg!Rs@6Dv~Y z#0?{O5wC5O1a>ONpCfLXBCGjNy&EHfc`U_N%k5l-7i<)>nEJIhC6$gM)zo0}@gmke zhsMyXK5}8&dn_iLSs}{z@DzBM*5A$s%-e;=@vlC^R|qq@%j}~(YA=sfFjw##4HHTd6;gWrp}E|i2YVsz zgIO5c?$b1joxC&a4=mbd@%GkLImwUQTM!e*e*FXuZtBbX3la2p$Cker zL3uBK1ZY%stPKSK4|-i4dpl8i{vYD!ch~-J0S62qS}=qO#LmhBVgp0|R`3MwT?;k< zv$9?fHeh80e8m3(_uN@z;bNL!48IsA=*Dew4X{suvOhyjWs6zZ<Hpx1pkqaqr{ zu0+NM8wtH^O4{2{h=FCApYrQ5)j@4z)MgRT(#(zAtt6F$mfv=j6NFQunzwdRH0$E z0vy&-smf5##X0vQA(FjitHi}B@1d>Din@u~gAkvnq(ZO40pEVSV|7k>!G`<3_DRtM*4RE}V6Jp2agn75nYm|iVr$_M+SDO6P3R{j7OA4j z^QXFNb(q${IYjiiM6V+b3^@ypy~2hkY57n}ot_LVuX*pSaW&3t8+L9RTKeib9x->c z_pZE3SAyHgHkq3*KAPVpVVEzphQLeG-MzOX9JNEwK#})azA0bQ={J81UUCmgfYmgwE(OsgjMLUh7(wWVQKvrKj?Oyk8q@prGi zwHBMWRh-+;J8D5Ll|0dCK@EZXXc+WnGBTT0L;tI!KnXSfUB;KGUj&zzGKZJs_yR+? zvosro@^q_|)}P8()4gQR6*Yi+ZZ6p?U3!06xZWe%a&?B=O?m@O#DXM@j3uIekhY-r zdG3U+8J_Mk=B(=jjqv;?Z#9hloEw57nZ~{;ZXdBImozEtX;Ge#qb5m|)l*I*b%-9z8;V>N2QK@V9o}xl7k^*PnC9Z+X+Wv}B?Px6>TO$S>og zg`a-;$D78c4y*&iGtd<-$Z`6tSjrtyITVao*)9D~Owxb|@7Pr&0t7ejT3Z66{BYMn zXAjs8WsN#z!l?Jr>KFbs z8M$6rGbEcadbTwyxLaF=e42M2Bnv)#^Z5xiISuOzKJt0L$ZhB}^EYt!>F$7f@OYpC zjm7o^(~nQmIRY^!&M-CZrO-Zj8d&1nFbWe#BFP)EkXUgN=l)3Y@!QT+nR%S7ZDe_; zi3z!d@;0RfP++{fdLqedsl)`zPj*8-IqEKo?}I6rg#A{!+IblZPbPLJs%Lp!6^Z*_ zC&G{0wyswBinqNW!S97oG4ltGN2g6jr*)@^M`GC6a@dQzN4=+{4j}>OB8oU*l4k^B z#>)yIl^tR~b{As}N3HJ}UoGd2>c4| z;qMZwUKi$k71WIHD%?J2wR2Qz=0MYZ9Vf=&Y(^zIl9qy0(o;qD+G5smM7@|dykm7F z!GwZyw%yz{`A`Oq7cSLX+WX)GU(z$@cct{D0lj?BF;P>d_*X*s(&;e=H`-S_*Zs7O zMwq^wUU*esoPP1@M18y~)>9Zv=WG1#>!`1UtjqauC8kCuo$HhGcQNHbS$gE#N7SFm zbsMU7%6DLWpUodn?Voop(MnH{ufG*YcGG4R4=smtkEA+{GaaFO{GyiI_HzlGC-R}^ z9Td1u6!k3|(J6RhJvv=OWa|3LTMx2TG0vANYG6?;_Qme(4?Vfj-m!WAgM$Kw>YPPU z&Yf|)!#a&|i--%vm|z0a{r3-V>vhiTVvL_WKA}n1Rh<%k()F%kYZ;-sG^cBx;E=dY z688x)>DN&OwRlOW4Ok+YF|4ARspa0{QGKku_(8)&pEK@ZCAyG;nsqHq-mCs><6Rsy>bA;-F}JqjOWaho}o;{d|E>a7MouM(AZ$ zu_!VQ6~E{>ZQ(R^loY~)yPCI9n&g-a^GPW-j?NAZpYh>XE!=GHkGMB*fHc6MmC93_ z2`_=1zA5jM%Qv=jNK-a41y(@|DuO*E)|XpFzoV0ymY+GoWFDNZLuh~qyY8+=sQZvL zqG8bm3I8J=tA8MUGGk2OZ7;!RqEa>8$qiywxnG8sa?VoTYkJHM&NFGPn>M^J7{Ghq zM3-dJCxJMmi{A5Yw5P&^_?*_Aw9?P!VI_t&)pXM5JBPXaxr#BRS#-zb^3rc8>Q?G8 zzYaVmw92;3_eo9YGM_iwEod>0&ZeoVM&^S)H(i$s?YvbyGt1L>5t!G&6X13Cg5Oo` zSiT(OLE%B?AslG`xSY&`)`K)5ruxRrhmDV)oV*j=9B#aR%eUHOShW_Y>(wgi^5HZ; zKKh_JtW}Qp1<64(|8`m>Ug89=lWK*^ayM_LUU$yB!Tpc*E#0sb#)Rc~9)oK5oSjV% z6m$BJtVd_;6+1-b})K$$$CFWxb2Ti_PAU$WYoG#gnk6Ii^PlGvnH>REZmsZzN3_?c=^ zj0ZQeZ#AWDE23Z3qRfNmPD5RL#}<`!DOMO3)hc-xCpuNtM;M2!0dxcx&==&+D1X6F zf9#E}1&lzhwlsePj6klt^T`_6SvlD18Q5LMXaJ1pd*bNdJ#2-nY%PF&1MqnzOJ6z^ zUWM_|lLgY-{CUeHHT|2cnm^1tR~gb!-hR^?-m9emM~2a)iKk zO7y!Yt~kh0$K1}~>fwL2)%~$=0dfO;XV-s(iWta=TGlh5R-E8?KGmj;&NbTYM?@{Z zn*ncl?nQ+|`3g$#76MXGn*RLb$=1|o_kH&W2>J)(!+)vIwm3(`3& z90MMQn%1510Fy;zVMQudcBN*JO5fy_resN9Jo5^l9Fm2RGL@UA#9jAN2O7k>5MTWr z`F`7Yi3Pl<`Vi$()amE17vbLMNO8-hA~|R=iN?fc_hF%Q46CCVcG-MlFc$2EPk^ST z-r(ga@THIk;zHY)lQ8K6jY*y?PO*?R1ah%$MS0*N{Ws#Z|+oIPBKs2wTv5LFkVS_W1c z$3jEFT5YcuN25I4c2a^}=~$nUy?2&e*x&Fki`m$IuC;>Z{rcU_-wEytEti{dCSZB{ z?l8)Hec$_sB?fYte&L@m%m`+_T3`RbZ-}CqwcHTK)bJKE1BOm_FuqbDwcZH62{12!9i{>vP}9)g7GZMgs~6e zacZK;Gg{n7#%~mtu`3z}n6KA8ns2vCe01t$KUo`@uv=eGtU^SG;@Tv$?)Ia!>qGZ% zdcH$z+S2o!sp->E>c+;sV+YLRd1Y=^%*tgA*f*V6^h`pi>0xJ27&`c8hxNDY^{~4z z&YM4D`A)|Z>`B5pdpzTh`qIkm(!WsGgj!Skmb+jTQl!#gP^L_ zAYO3<-X%P`PvDp#jl9qZBx{PGTiz141MKX#?snSlargsDvLJp9l8=7* zWWw!IOuAJMhZ8zBtL;6k4+fjG2tDMpT@TAbBaGcr3b1BNS^_39vQN|#439vQoQXHQ*x!2D=ux)wX71RJG} zfqvCx3128KjHW>CFaZ(yYnWpv?~4kj!|_EOIG-A4JkqE2HW?}e=(lH&pEc6#$J^OvW|9l134{KuMgLp=w^y`&kz2^Jb|aw%{8 zULVfaA$Hzx9pM+oenB7548y9={(3{%ZoT{Q$KC9QoVG{j8nB$9M5CBlf%bxDq6up3 zc~8oR+b$Z|<@xc<{i?!!n5?1S%XxB+tfc9Tw@6W@7tB;wlub~33Qrntw<1})96waf z85Vs|6cv~@=Qg)KD#}kXnXf)MXLUojq+LQ3+gDZ$oqEWe;NmtWL%qt!WX3MGraK*e zk=@0({FbO`cVM3?@|SAM*eo?Z5rRp(A9??xqCS-UfyF3& zbqtnk;~NsXJaO;Yozj?W-M5TRkt>iTyOB)-Q(jhZd4y^RCo$S>m&KJ=H!eQx~`R1uYCu3*@b1tD&*va-|W8=M6EeTJ*owBD+=x1e&ijw zxp&2uO0%oI6zYgV!D4=m6YXRdEb{jQqpB?nr0Cl$BhhAGDh!ureaI`pQ8RYV+(@n# z_G2;3x2mhHwQ9mvB)wta;Tr&{cGEcwFvoyHM2p|@nxE3UmR9Vh+j}hq5qa4}h>^66%iTMaQukx+b>V`F zVDizl$pzmcZabYFth?v0yBF-4v~3%cer37A*&|^fFvPAH!K@L(QLNBHQ!;)$wxy)H z%wRm4P9aGXGM>CeyG|c+$ey*D<>!_E#P3kiOyuL7*5OS;H68oR2M#6Oyt@$d&f*r$ zEEuYiz4f*CD2TH#TC8dLXaumX%YC@L1*nMj8Rhmz{5+IVNr&$|&lBHycEZF^O|<20 z-MfznObB7~Z~-LFxG}P0e;Wl$Gc|vqk|(XK^4l>nr!R4zIPv1tFTw5C;Y~qtLG#Nz z$%C0-M{N_t=#xxby+{!wB5z2ScjAgKTQt{ZOKg0xw(ZOjI!K+|Q<>>#c!8GK$KD6e zQNtUG7}M9OZ8Kcr`bl|ZkIr3UR?~mMuu@FgXDO!H>*u+DZyeYO^i zUOc1KYa?rG3tBup*h^<<&hRJO2kS>enYmk4ZKqSZCIxb3okL?@A_$%Q9PVejly@;x zAUss+PF5jKL}BP9VeOAiIhbFL+QcN{#m~-hQ)CHoU!wj+PJ3hmMvPrL>0owvSD@ zS2Ny(0q(7{5tXH5Ryv;JdH}Uu@r7^ek?A;dJH9}N$B62RBykh?x3;#W|oT|k2 z{%T(8)&pxL7VTA91?cqN}5N+IuKc4eF4T?8Mm_of{XhabM~mb_RRpgulke zFhcfi8M%Ntz{M8)3$f<9UinJmy|Uf>wg1zlX+d6=hR?)^&)mU)&%xf_$`TNDt~{T9 zGCk1(nXWF=U4g+YOdvJ}RzO((>r7X`y=%rNpvh0hCopjH598C1OjqoHzW)zH6cbS6 z4?`6DWooNyhNw&1)t`o_D`D-r8ie_}PUeRp>T*1PCA(eMU1)^Yn$|I(3v3(*KFsh3 zWg6*GQ9~0)Msu7&2@>4lt;94tn);NN_5$0ro43f7si?Q5)Ztz^;-|G})cCN%gYnbYtQfP$K212eE$^ zs#&m8`;Z~8|eNdiR+P8f8h=G zH?;Ay(TVwz+JHULpGK!EB>cV62?8MdhtUbl0HnbBpFge}rkH5nd4B z*md=$e1IJWuzUgLFyJ3B4ZgvX*V59;{u)3fOf1ci>1Baj27na{Frd=0G6OzmbZo#M zFcTy5w=Y03GYcbN;ri3?2EMME`zx~jv8jYy8fmZ5aQvEvhLt*Nq{oJqjS-LGD&hxv z9(tq)xcP+AGb7INB{=#|w284llAC=nV5emtIXz*|44kW?EwNx4l?FcCULuT!s1G#c9~NM-c~YygN%P zO`5t&$VyMWjymF@|eYhoia z8pP0$qEieDQ$!03EsIF)Q`qe-sJyF@<5_u7=MgoyKe=%3gF&z6!Tzy-D5#gqI5YL6 zG1e`nB*cq2iN+y=J`Ba@;8@m=hi-1~?8s_*d;i?)rm&aF!-t^%zO6y4z&s1U6r-ACUOYxp&aT|l4VO%2r!5M z_w$WA>LS^j<+5K7AB8b~ogg}?B2tQRqZv*xkKQ;hXWh)4SED?k;i&qwR5q5hJm((9 zHTL@AL-xviH;RR473ubq2MksEn zVXrWv4Kr%XZ3r_Vu}IPD8MSwc92v4_9IO?@l{^k75MY4q*10q1YA3rVW5k&Wy=OaP zV{pbM1?R>5p(|56N!i1o zq#2H0Hl_lK4}v{%51=#NcGzf)D*C z9rOEpn{7up(6hQlAGy8U_C*zhk%!WJ*+?$@=@eE&=uF>{8a4=;4bgRF-E=lm4|m^l z0hlbAw}QdQ5~BejcqKPKCoq?Z<+zdV?a~QoEKb`>^yFE==Z-uhpjNO`-8aJ%7S7sa}F zcAdND+{Pjw4jhf@!w-+3`8mvmq?0}!wiR{xqaeB}JUit`!Y%~E}3oIRYEt@RJr5k$ z)F9c>fQ(WC|7=~e`1kf9b2|Y9D>Pj~G3;Xj86O&pj7vLImhYS>H=cKO+Y5Gg4ee%A z(kF4Bt!#dMElkfNJZ3U4qZic=4{akyz~CliITL| zEgtus)urWGyf95qhlA1Nj&qDEFOd9&^G5c>R$krS{K&#_hoKYCGq;sH!h|0jHaa9H z+&<);h$&4BVHB;Om1VlkQc$(hFWpcWoxia->snhF?$9{;X}Q2AC+P(} zXFA)RoiAspIaB0fpg6X6Yw>;02h0Z#ws?ry#LCLXd8URr{YN;F-VxG=#u%}uFDBQ; zYjqxUaoE@7TWJ;tG<^B;QIHseu`2J*naPI!tocUH;?z;Uv|9)XA?T=MKH+)FTd$US+nQ=XOY8iF)Hu#N z3>SinWCe3mh!-KYL;+W4tpLI2c^Yh*H`0`-%KjS&!qW4Bap*+u7AQn_XhJY3veB>z zHNebDUkTxT;?29PqW%%KtqATZCJ@B362gfTL@v#Y?vjXoQSTZ7=b0O~S4D`pi0W8w zq7^jPZZ;!9^64eSR_KIU>5YTnyD+wUfS3#k2G|-33=~|S66<<0Xjs}OR5_;grS?*( zq#zfiB~baJpu{eGx~Mb^_VA~-ZcyMNyhnZFJi-|0IKsaQoh|6zPD(pKox2a#b&8Jr+~lGgm^Q zqP1Bm#j*kXVN(%Xk^^*X_~n^oHVh)-NT4-gJQ|H1Lsq=tFxD91cDH&7KiX3-6r*PQ z)>w4jNFqp|eBPm=Me+^@b5_J1*tJTG)YrDMpQH<^5@GM;5J63Ue5jn1ih_<9IqcA? zInIr+*N72aL#?eo{?MbL2oV8c@BLdg?VLHy`~ziGZw0Rsxo~K9{5`E!lyy$S0DAqcMFjwe7*|3V0EvJWN)A0!fHTZh1kyq?aiasDP6buaJ5KT8RR28bh$}~WWwkH_bCbF3BRj3<<88Afz6P5c7JdI=yr=zGv zQ%}G5BKCG*;|`O8#87n5 z;9qwYkbe#?Yw6r=$(4T<#xyRKbTmy=gLYq}^^T|OJtIUR{kcm0`sPWwTfPX$@VH$D z9&wm;si|yXST~@SVwGjJ8@>4ZrA+&yXutyjNIeS_NUM$VzGnBQpL4g1xjzs^+bLNk zuh6xfX(51qGUC)KlJeZ@`7LJAURY963aLjmY!F7qmEavH0rHh1RH_FB9?ugPNqOW9 zEH)OIq1_{g?BRpRtzq*Y2=|UwEwH+)^2**!R*8EZNx+6jHo_nv*JfTPN@YEFGj&^$ z)FUM8QIGNro}Kmv^9Zb!_|r%DegO!)wl`gOXJaqGsRpjMU#;LTCu25yn@0|boVH>~ z??Ey@sI&4kYjao_hYh>*BC?b1SIj7;4s)EzC*L8WyF;9PAsZPL|LBIH>oN}*g__EY z*)#0jXEm7vRCofurv(CM^YwR=ofYDp?g_Dp`C(DE`1U^-fh{CN$qiB&WtRq9j&VOE zfj*XGp?zG?K4hHZ4YfSjwBfYm<@!OYs+rI=Vk~G}b8esH1K!7~<~2b}dRw<^MOvlJ zFK;q#?(b9)E- zv6me*K^9NB^&-hov)=IWaSeJ$Izj&sVy_rA?2*g}41C(M{+{-ywn^h;dzsz0Pf~XA zYzA*RBo6i*=6o@f_3f~tZ(3HBR#d7Y+aPzLZ*dcy6yMg2Jcz7}nftlO>nM^-7$ zqEsIw^dI&-G!4~<@Ek!y;NY z&MQWSFT;mCOHl^_gm1W;ANL}jO-<~hO}f4Ol6{)2bX3*4q2QUfeRZihe{lcPx1#gS zi^JLq?7WI19Jw2m83J#uci#-cMB!6P7MW0Xm*1vcx!aM$-(A@#+MiAeTL8*@5X;*X zX&?QPOHRa3sw1~0PL-Cnx$|Mn!vmdYx{sKj%}QH?24=b5VaYGB3AhyWwl^Tqgkju} zq@fz?mGh1l7b#$*WOCV3vzR`%2qAALI1-O3kQe^SbYlRFFKD`;wP#OCIbo8T>)beV znwq0mVXWhAF;6C-)AEYMUM!=#c!Q*>7v`4kbPyP81~=+uYeA32MoD`7J99kR$Mwo8 zDX#`pUKe*mWZ2S@)M`j{IRfv}f-4&2@SP35wrFkX^Tp@HnUATlKY)RVwst^bh5280 zo+>;n8h16BE-d=YZ<4$W?~RRRFxWo8c9i8Pn-fF$id|_oad#-eNR`~qw(Z_Rg~BnC z6i@9kCEFQXX42FyR=lTopR~2#)#snL7E!n}YkiPKwPV#^-6PY)nno0BX?JqvK0R)2 zyXoo0AO&0 z_;Y00BTJIfV?Ll&J8~6SsDpNBug}LeN(`~A$fLEsol!g{h+^knjhiT7Y+BjL0nWt) z1-%s+?^iv1!+WOqytIaqOx!qW+8;FJ+$$s$g6QD%{7CCN;U0jZ zkaJKIDq(HKFioCY*$9SET-b&qaqX;E91Z#KJ>{Ur_OD4eoK0jMAv1^~c+EppX`TRH zStmK_+jgAf)R4F{S3rHCGStiCd9>T(Re5-B?0huSuv77cW-?>0pnvYxgpfGpLua(D zBzh;jy1j+N8PhT@+8F61&GlPaGFl7M@Oyo-yG~_q&dr^U&L=ik=GVQrU3zRjVn(x; zAqvDF6^_-^#T>m?(4IM+o-yIT+2`uCru$6Oa!eVwP(rzU z2q&R9+tLN=VP6=>w8X}Sj?2JWWnLeD_dP#R;^~ssBuKkg(ef8Or`P9%%QOV=zbg!{ z>rubyw`8uea7r5Jo9J9QPh4uh!0f=m-Czc`OD)(>-WY$L-hJguA#4XkM}G5xpcl3S z;xR9~2Q-Ajc1%G0)G^94g07NAU3~}IeshBZ z%9()(%O7=ELCoLkU-t9e8|11hWgrgodr!(>(3LOAmAa4qyQlRxzYpLb@hi`4;2Cnp zjG(JC0j{cDcE=A43rHqv2fX^X$6cxY0IzK3tDyjJ0CI<3W*hyc6$P67@GJP?+;SO| z{7-5-%ZP3~pK#ZRD6@Wz{Ty}%%G@BpQLc^boZBSwrnp@C z;$c;cQ6`hQ4nhclWEv`mvHJcdsxU*DXB#EN)!VC0xoahP2HG!jj)Xe2nTS?R@Y#6w zZSyiD3n8nl_`2tnwhOJnN`CCDG1}2}Ff?7yMjGb`RmEFv-{%aMoioa1PjQL0?<`h| zxv;oSiUL!Tl z&yM_y@2+CHo=Nrmz_ffT$;U6R6FO-t3={PYD?YAhf*;oEwR+j1con&uHvCSF*Y&Xf zH`I8InEoId05v}$l=(aT{2l)Ow`$q{;_r9-{jPui&$!9|3V+`x*MFJN#WT?*HQN|BioNUwi&7{>k!vW&Jz+T{lYqcmDln z^Y8lF^RMCWyB+uM@ORxW^1t}|&+rGnzWDrW2>dQc{~ZFai}e3R;D3m~>#NVdhQRN; zqQ67nbt(A22>cHbczyZ#*AV!9JNkDByej4w7v?fEIiRW3ETofk(Uh#4`x!j9rKMXKDV!~>7&~jqqFmo1Sw-~X4UFroIq&?I^{o@E~uHh@%({q2x#ye zXtz{j64(M7e32|$s#6%?z_&;(sE=U;uW1>Js@sM{B27&aG>m|qC@X+a;15%2qFoq3 z4rRnf5j4yR7aGxAcoc+RzhFffq)f5N(V&kZ?Ar;(#22Ux?XwzN$dpqt3ndMDtq1(6 zShG8U&ytBlN0)~VGJ@vClEucLm+`%?tN8|54zB8v2cjtOi4%9j_33?yqqt0>-BG9f z{6{$DKmkQR$Q!68cv<1!W1ac^0?>C2E3w%#k#|T$d%OdC%MMr`!ip%F62HG zF7Yf5l9xQ^{IG0y;NUSymW1L-IBk7$l9^U@&$HD$W{8DpdQo&Pj&x`)_z`R8iL}I6 z9fGB?4Mu|@`?Qgv#_Nk~}+p~Y>E5F33{Fkr%;43CIVElS~5MtaN3BRKxf`B7weRQ?YX#yGs|HxOK z^8Y{KD|0uVqw3zeNg^zwk0B1D$sGGYm$*iJ>sB;57w&C{)T{rHuMk=Pm#_S9_{!eG z%IK*P*K99=4RJmXR?Plj71zuTHMcv+k|00zFhXvuou<_Y@nF}w=Ijm6c$vHDhooCh z>t|7Qf=_nm963EFLV4)vhFZkg0#}sAwp2?wTC-j^$<0&REYB|Y9^9-J-CJmKbPad- zbiUj->$%bQ(5u;PePN&h(aU)`k+?MG;Lb+J(BxlmmFu$lzs6NA*~UL8T&^n({)wwV z*nZ(EFD1?Hf>HedX@$cOZDll?C(jr2#$34L8~C%Yt3{8``-7{&MFq7z?%b5p zqMPO<>b(?1u1kY;EW2dTo@!{gU-X zcBe3V18_Y;R}%YeZJ|v}F7`Z&?>X>r7z?~!N?87I%JvGa?^!X+8mjF$qlDYen)xW( zolZ;tGp5y9!!g8gUEGzSy2<9ZD&>yStEbe_o}0ZB=w9UI6~uMC$BADOmQ7kZ*f$J1 zeJdy3{g*$x$2HcuL4(YLC;ozBuWwHNHH!Ub6+O#!HOW6wEECf&6e}w(4xGeR-IW)s zXSY;dQ4}_JKLF$`gU1zsSQ3KM7d^;#QC$>PjDn*EvvX)z{mLrRd@f^<%f7asKpQU zZNLP#wm?aX4rsxpk3~e(p-`z)W)^vBMB2gS(eO!}lLiOUMb$Aip~yOqz`u;&`6}nW zB(0Jm5V=zP=2lB8IgK(jeosm1!=v@|o^16{D|NT&erWt#H!vt?weMf>s}4qPrbUd2 zxcLtjlITi%zhIh`qE=ljD6RCW48s${@+H6=bimH5yk(68V?(?9=wn`4{<9cSpIZxPBBCH(!e$9i4U^N)nW^7Gk`A(4{euwZn* zjUAeWVIqPkiR%3;Ch`-Z?37GAYS2KAaxq$X^(w8xA8{B~I5Z`q*1Bmc-`i-|yXKJ7 zoe#-Mn`*g`IKh0V8^$QBww7zsFJQnK#`i!cphmeIy)3-od41T;a3-qQ#u;B@bV9E5 z+;f4Y#!+#80Rip^Vz01h$Al zZ3**fDE^6OcgUS)?kLbhRO3(WJY?~ohntY4BDADXbbLlgQ=ToZICK?H#$hKs7BzW= zu_)pciVokynz5~rsS_GY{yaH2jrpy^aKLBL;AfA|Bs${@l*br8ebTz!te`HB(U+0z z+-8QRG%447giAH_vd(JCU|})7WrX9BbQXe~wQ0-mf0A%iP;zj1`*2~(?WJqmI}6M& z%%khQHgEMZgAzj1oz_2QyW!fhdkK0WvT~ofd>YpNviKLIb6r~b*GT7|O&ou%VzNV~ z7dN@g+jgbh16`?zKvx?&(3JoNx?0pgSL_S$rMQ&IfEc@Pf38&WK)BGit8X2Ezx4qA z)&=-mAK&0LTsgr{}?Sg8l{>ejR%qb1S1uTbmJ(J>%*r$n=T^wst`N zJrF&Z?#ct<#|c`%!++S_Tz*vg28KEg=JvD@YT$vk20Hduwje4wD}5c1jDXOO zGN3Dg%QNe!1UMKO7{Lq>Acqhm69Y55Dgy&0@E@orV{4`Fpa+CfU2iODrT-W0m>Aeq zsjt11v#o(4GLS}_5gCXeyZT{d2C*?7y{Thd=;Y`{@}F1|X8{moi2cc7|We zfJT>e`cqp5R)7qCEn@)&_-h#h1Iw>)VPJ*)h7X`~#vgG0fpakMh~J*U3i%BmKwG9C z@VnObHymBo`vI3f>M;TX{C&&-m0i9r!yohqW@NpzX{BRj*dZ~?Qk{zVzk7ff1EznBnVpRl;6Oi(j}grB z+uQ;oeJ>Z!U+S^_MyJ3uV*eEeK-q7wVgyp8{W@kwCKk5e+5)ipb)GXav;Rgfj7+S* z!Jd%?{QG;bumZ{Ye|Zl8N58=Uh`;63OGpM^6<`| z;K#(o%K9q|m;mqe-<|"${LOGFILE}" + +# Find the name of recordings container in order to access `bbb-record` utility +BBB_RECORDINGS_CONTAINER_NAME=$(docker ps --filter "name=recordings" --filter "status=running" --format "{{.Names}}") +if [ $BBB_RECORDINGS_CONTAINER_NAME == "" ]; then + echo "$(date --rfc-3339=seconds) ERROR: recordings container is not running" >>"${LOGFILE}" + exit 1 +fi + +for donefile in /var/bigbluebutton/recording/status/published/*-presentation.done ; do + MTIME=$(stat -c %Y "${donefile}") + # Check the age of the recording + if [ $(( ( $NOW - $MTIME ) / 86400 )) -gt $RECORDING_MAX_AGE_DAYS ]; then + MEETING_ID=$(basename "${donefile}") + MEETING_ID=${MEETING_ID%-presentation.done} + echo "${MEETING_ID}" >> "${LOGFILE}" + + docker exec "$BBB_RECORDINGS_CONTAINER_NAME" bbb-record --delete "${MEETING_ID}" >>"${LOGFILE}" + fi +done + +for eventsfile in /var/bigbluebutton/recording/raw/*/events.xml ; do + MTIME=$(stat -c %Y "${eventsfile}") + # Check the age of the recording + if [ $(( ( $NOW - $MTIME ) / 86400 )) -gt $RECORDING_MAX_AGE_DAYS ]; then + MEETING_ID="${eventsfile%/events.xml}" + MEETING_ID="${MEETING_ID##*/}" + echo "${MEETING_ID}" >> "${LOGFILE}" + + docker exec "$BBB_RECORDINGS_CONTAINER_NAME" bbb-record --delete "${MEETING_ID}" >>"${LOGFILE}" + fi +done diff --git a/mod/periodic/bbb-resync-freeswitch b/mod/periodic/bbb-resync-freeswitch new file mode 100755 index 0000000..ee3d087 --- /dev/null +++ b/mod/periodic/bbb-resync-freeswitch @@ -0,0 +1,5 @@ +#!/bin/bash + +# https://github.com/bigbluebutton/bigbluebutton/pull/9597/files + +docker exec -it bbb-freeswitch fs_cli -H 10.7.7.1 -P 8021 -x 'fsctl sync_clock_when_idle' \ No newline at end of file diff --git a/mod/periodic/entrypoint.sh b/mod/periodic/entrypoint.sh new file mode 100755 index 0000000..cc3bbb0 --- /dev/null +++ b/mod/periodic/entrypoint.sh @@ -0,0 +1,23 @@ +#!/bin/bash + +# +# How N days back to keep files +# +history=5 + +while : +do + + # resync freeswitch + /bbb-resync-freeswitch + + # delete presentations older than N days + find /var/bigbluebutton/ -maxdepth 1 -type d -name "*-[0-9]*" -mtime +$history -exec rm -rf '{}' + + + # delete recordings older than $RECORDING_MAX_AGE_DAYS + if [ "$ENABLE_RECORDING" == true ] && [ "$REMOVE_OLD_RECORDING" == true ]; then + /bbb-remove-old-recordings + fi + + sleep 30m +done diff --git a/mod/recordings/Dockerfile b/mod/recordings/Dockerfile new file mode 100644 index 0000000..ad401b3 --- /dev/null +++ b/mod/recordings/Dockerfile @@ -0,0 +1,111 @@ + +FROM ruby:2.7-slim-buster + +# install apt dependencies +RUN apt-get update && apt-get install -y \ + wget \ + subversion \ + rsync \ + build-essential \ + libsystemd-dev \ + python3 \ + python3-attr \ + python3-cairo \ + python3-gi \ + python3-gi-cairo \ + python3-lxml \ + python3-icu \ + python3-pyinotify \ + gir1.2-pangocairo-1.0 \ + ffmpeg \ + poppler-utils \ + imagemagick \ + supervisor \ + locales \ + locales-all +# TODO: missing packages + +ENV LC_ALL en_US.UTF-8 +ENV LANG en_US.UTF-8 +ENV LANGUAGE en_US.UTF-8 + +# compile and install mkclean +RUN cd /tmp \ + && wget https://netcologne.dl.sourceforge.net/project/matroska/mkclean/mkclean-0.8.10.tar.bz2 \ + && tar -xf /tmp/mkclean-0.8.10.tar.bz2 \ + && cd /tmp/mkclean-0.8.10 \ + && sed -i 's/\r//g' ./mkclean/configure.compiled \ + && ./mkclean/configure.compiled \ + && make -C mkclean \ + && cp ./release/gcc_linux_x64/mkclean /usr/bin/mkclean \ + && rm -r /tmp/mkclean-* + +# add dockerize +ENV DOCKERIZE_VERSION v0.6.1 +RUN wget -q https://github.com/jwilder/dockerize/releases/download/$DOCKERIZE_VERSION/dockerize-linux-amd64-$DOCKERIZE_VERSION.tar.gz \ + && tar -C /usr/local/bin -xzvf dockerize-linux-amd64-$DOCKERIZE_VERSION.tar.gz \ + && rm dockerize-linux-amd64-$DOCKERIZE_VERSION.tar.gz + +# add yq for bbb-record +RUN wget -q https://github.com/mikefarah/yq/releases/download/3.4.1/yq_linux_amd64 -O /usr/bin/yq \ + && chmod +x /usr/bin/yq + +RUN mkdir -p \ + /usr/local/bigbluebutton \ + /usr/local/bigbluebutton/core \ + /etc/bigbluebutton + +ARG TAG_RECORDINGS + +# add bbb-record-core (lib, scripts and Gemfile) +RUN cd /usr/local/bigbluebutton/core \ + && svn checkout https://github.com/bigbluebutton/bigbluebutton/tags/$TAG_RECORDINGS/record-and-playback/core/lib \ + && svn checkout https://github.com/bigbluebutton/bigbluebutton/tags/$TAG_RECORDINGS/record-and-playback/core/scripts \ + && rm -rf /usr/local/bigbluebutton/core/*/.svn \ + && wget https://raw.githubusercontent.com/bigbluebutton/bigbluebutton/$TAG_RECORDINGS/record-and-playback/core/Gemfile.lock \ + && wget https://raw.githubusercontent.com/bigbluebutton/bigbluebutton/$TAG_RECORDINGS/record-and-playback/core/Gemfile \ + && wget https://raw.githubusercontent.com/bigbluebutton/bigbluebutton/$TAG_RECORDINGS/record-and-playback/core/Rakefile + +# add bbb-playback-presentation scripts +RUN cd /tmp \ + && svn checkout https://github.com/bigbluebutton/bigbluebutton/tags/$TAG_RECORDINGS/record-and-playback/presentation/scripts \ + && rsync -av /tmp/scripts/ /usr/local/bigbluebutton/core/scripts/ \ + && rm -rf /tmp/scripts + +# install ruby dependencies +RUN cd /usr/local/bigbluebutton/core \ + && gem install builder \ + && gem install bundler --no-document \ + && /usr/local/bin/bundle + +# log to file instead of journald +RUN sed -i 's|Journald::Logger\.new.*|Logger.new("/var/log/bigbluebutton/recording.log")|g' /usr/local/bigbluebutton/core/lib/recordandplayback.rb && \ + sed -i 's|Journald::Logger\.new.*|Logger.new("/var/log/bigbluebutton/recording.log")|g' /usr/local/bigbluebutton/core/scripts/rap-caption-inbox.rb && \ + sed -i 's|Logger\.new.*|Logger.new("/var/log/bigbluebutton/recording.log")|g' /usr/local/bigbluebutton/core/scripts/rap-process-worker.rb && \ + sed -i 's|Logger\.new.*|Logger.new("/var/log/bigbluebutton/recording.log")|g' /usr/local/bigbluebutton/core/scripts/archive/archive.rb && \ + sed -i 's|Logger\.new.*|Logger.new("/var/log/bigbluebutton/recording.log")|g' /usr/local/bigbluebutton/core/scripts/publish/presentation.rb && \ + sed -i 's|Logger\.new.*|Logger.new("/var/log/bigbluebutton/recording.log")|g' /usr/local/bigbluebutton/core/scripts/utils/captions.rb && \ + sed -i 's|Logger\.new.*|Logger.new("/var/log/bigbluebutton/recording.log")|g' /usr/local/bigbluebutton/core/scripts/process/presentation.rb + +# add bbb-record with some adjustments so bbb-record works in this environment +RUN cd /usr/bin \ + && wget https://raw.githubusercontent.com/bigbluebutton/bigbluebutton/$TAG_RECORDINGS/bigbluebutton-config/bin/bbb-record \ + && chmod +x /usr/bin/bbb-record \ + && sed -i 's/^BBB_WEB.*/BBB_WEB=""/' /usr/bin/bbb-record \ + && sed -i 's/systemctl.*//' /usr/bin/bbb-record \ + && echo "BIGBLUEBUTTON_RELEASE=$TAG_RECORDINGS" > /etc/bigbluebutton/bigbluebutton-release + +# create user +# the ID should match the one creating the files in `core` +RUN groupadd -g 998 bigbluebutton && useradd -m -u 998 -g bigbluebutton bigbluebutton + +# change owner +# https://github.com/alangecker/bigbluebutton-docker/issues/63 +RUN chown -R 998:998 /usr/local/bigbluebutton + +COPY bbb-web.properties /etc/bigbluebutton/bbb-web.properties.tmpl +COPY bigbluebutton.yml /usr/local/bigbluebutton/core/scripts/bigbluebutton.yml.tmpl +COPY supervisord.conf /etc/supervisor/conf.d/supervisord.conf +COPY entrypoint.sh /entrypoint.sh + +ENTRYPOINT /entrypoint.sh diff --git a/mod/recordings/bbb-web.properties b/mod/recordings/bbb-web.properties new file mode 100644 index 0000000..c36b7e6 --- /dev/null +++ b/mod/recordings/bbb-web.properties @@ -0,0 +1 @@ +securitySalt={{ .Env.SHARED_SECRET }} \ No newline at end of file diff --git a/mod/recordings/bigbluebutton.yml b/mod/recordings/bigbluebutton.yml new file mode 100644 index 0000000..e93228d --- /dev/null +++ b/mod/recordings/bigbluebutton.yml @@ -0,0 +1,59 @@ +bbb_version: '2.1.0' +raw_audio_src: /var/freeswitch/meetings +raw_video_src: /usr/share/red5/webapps/video/streams +kurento_video_src: /var/kurento/recordings +kurento_screenshare_src: /var/kurento/screenshare +raw_screenshare_src: /usr/share/red5/webapps/screenshare/streams +raw_webrtc_deskshare_src: /usr/share/red5/webapps/video-broadcast/streams +raw_deskshare_src: /var/bigbluebutton/deskshare +raw_presentation_src: /var/bigbluebutton +notes_endpoint: http://bbb-pads:9002/p +# Specify the notes formats we archive +# txt, doc and odt are also supported +notes_formats: + - etherpad + - html + - pdf +redis_host: redis +redis_port: 6379 +# Uncomment and set password if redis require it. +# redis_password: changeme + +# redis_workers_host: 127.0.0.1 +# redis_workers_port: 6379 + +# Set to true to insert recording process status into +# redis list with key "store_recording_status: true". +# This is useful if you want to track progress status +# and have another script process it. +store_recording_status: false + +# Sequence of recording steps. Keys are the current step, values +# are the next step(s). Examples: +# current_step: next_step +# "current_step-format": "next_step-format" +# current_step: +# - next_step +# - another_step-format +steps: + archive: "sanity" + sanity: "captions" + captions: "process:presentation" + "process:presentation": "publish:presentation" + +# For PRODUCTION +log_dir: /var/log/bigbluebutton +events_dir: /var/bigbluebutton/events +recording_dir: /var/bigbluebutton/recording +published_dir: /var/bigbluebutton/published +captions_dir: /var/bigbluebutton/captions +playback_host: {{ .Env.DOMAIN }} +playback_protocol: https + +# For DEVELOPMENT +# This allows us to run the scripts manually +#scripts_dir: /home/ubuntu/dev/bigbluebutton/record-and-playback/core/scripts +#log_dir: /home/ubuntu/temp/log +#recording_dir: /home/ubuntu/temp/recording +#published_dir: /home/ubuntu/temp/published +#playback_host: 127.0.0.1 \ No newline at end of file diff --git a/mod/recordings/entrypoint.sh b/mod/recordings/entrypoint.sh new file mode 100755 index 0000000..7b73930 --- /dev/null +++ b/mod/recordings/entrypoint.sh @@ -0,0 +1,12 @@ +#!/bin/bash + +touch /var/log/bigbluebutton/recording.log +touch /var/log/bigbluebutton/bbb-web.log +mkdir -p /var/log/bigbluebutton/presentation +chown -R bigbluebutton:bigbluebutton /var/log/bigbluebutton + +dockerize \ + -template /usr/local/bigbluebutton/core/scripts/bigbluebutton.yml.tmpl:/usr/local/bigbluebutton/core/scripts/bigbluebutton.yml \ + -template /etc/bigbluebutton/bbb-web.properties.tmpl:/etc/bigbluebutton/bbb-web.properties \ + -stdout /var/log/bigbluebutton/recording.log \ + /usr/bin/supervisord --nodaemon \ No newline at end of file diff --git a/mod/recordings/supervisord.conf b/mod/recordings/supervisord.conf new file mode 100644 index 0000000..4780d47 --- /dev/null +++ b/mod/recordings/supervisord.conf @@ -0,0 +1,34 @@ +[supervisord] +user=root + +[program:rasque_workers] +command=rake resque:workers +directory=/usr/local/bigbluebutton/core/scripts +environment=QUEUE="rap:archive,rap:publish,rap:process,rap:sanity,rap:captions,rap:events",COUNT="1",VVERBOSE="1",HOME="/home/bigbluebutton" +user=bigbluebutton +stdout_logfile=/dev/fd/1 +stdout_logfile_maxbytes=0 +stderr_logfile=/dev/fd/2 +stderr_logfile_maxbytes=0 + + +[program:rap_starter] +command=bundle exec ruby /usr/local/bigbluebutton/core/scripts/rap-starter.rb +directory=/usr/local/bigbluebutton/core/scripts +environment=HOME="/home/bigbluebutton" +user=bigbluebutton +stdout_logfile=/dev/fd/1 +stdout_logfile_maxbytes=0 +stderr_logfile=/dev/fd/2 +stderr_logfile_maxbytes=0 + + +[program:rap_caption_inbox] +command=bundle exec ruby /usr/local/bigbluebutton/core/scripts/rap-caption-inbox.rb +directory=/usr/local/bigbluebutton/core/scripts +environment=HOME="/home/bigbluebutton" +user=bigbluebutton +stdout_logfile=/dev/fd/1 +stdout_logfile_maxbytes=0 +stderr_logfile=/dev/fd/2 +stderr_logfile_maxbytes=0 diff --git a/mod/webhooks/Dockerfile b/mod/webhooks/Dockerfile new file mode 100644 index 0000000..ee62cc2 --- /dev/null +++ b/mod/webhooks/Dockerfile @@ -0,0 +1,25 @@ +FROM node:14.18.3-bullseye-slim AS builder + + +RUN apt-get update && apt-get install -y git wget + +RUN wget -q https://github.com/mikefarah/yq/releases/download/v4.25.1/yq_linux_amd64 -O /usr/bin/yq \ + && chmod +x /usr/bin/yq + +COPY ./bbb-webhooks /bbb-webhooks +RUN cd /bbb-webhooks && npm install --production + +RUN chmod 777 /bbb-webhooks/config +# ------------------------------ + +FROM node:14.18.3-bullseye-slim +RUN useradd --uid 2004 --user-group bbb-webhooks + +COPY --from=builder /usr/bin/yq /usr/bin/yq +COPY --from=builder /bbb-webhooks /bbb-webhooks +COPY entrypoint.sh /entrypoint.sh + +RUN mkdir /bbb-webhooks/log && chmod 777 /bbb-webhooks/log +USER bbb-webhooks +ENTRYPOINT /entrypoint.sh + diff --git a/mod/webhooks/entrypoint.sh b/mod/webhooks/entrypoint.sh new file mode 100755 index 0000000..22107ac --- /dev/null +++ b/mod/webhooks/entrypoint.sh @@ -0,0 +1,17 @@ +#!/bin/sh +set -e +TARGET=/bbb-webhooks/config/production.yml +cp /bbb-webhooks/config/default.example.yml $TARGET + +yq e -i ".bbb.sharedSecret = \"$SHARED_SECRET\"" $TARGET +yq e -i ".bbb.serverDomain = \"$DOMAIN\"" $TARGET +yq e -i ".bbb.auth2_0 = true" $TARGET +yq e -i ".server.bind = \"0.0.0.0\"" $TARGET +yq e -i ".hooks.getRaw = false" $TARGET +yq e -i ".redis.host = \"redis\"" $TARGET + +export NODE_ENV=production + +cd /bbb-webhooks +node app.js + diff --git a/mod/webrtc-sfu/Dockerfile b/mod/webrtc-sfu/Dockerfile new file mode 100644 index 0000000..e8d3263 --- /dev/null +++ b/mod/webrtc-sfu/Dockerfile @@ -0,0 +1,35 @@ +ARG BBB_BUILD_TAG +FROM gitlab.senfcall.de:5050/senfcall-public/docker-bbb-build:$BBB_BUILD_TAG AS builder + + +RUN useradd --uid 2004 --user-group webrtc-sfu + +ADD ./bbb-webrtc-sfu /app + + +ENV NODE_ENV production + +# due to the git submodule npm install crashes with following error: +# npm ERR! fatal: Not a git repository: ../.git/modules/bbb-webrtc-sfu +# we simply delete the .git file +RUN cd /app \ + && cp config/default.example.yml config/production.yml \ + && rm .git \ + && npm install --unsafe-perm \ + && npm cache clear --force \ + && rm -rf node_modules/mediasoup/worker/out/Release/subprojects \ + && rm -rf node_modules/mediasoup/worker/out/Release/mediasoup-worker.p \ + && rm -rf node_modules/mediasoup/worker/out/Release/deps + + +# ============================= +FROM node:14.19.1-bullseye-slim +RUN useradd --uid 2004 --user-group webrtc-sfu +ENV NODE_ENV production + +COPY --from=builder /app /app +RUN chown -R webrtc-sfu:webrtc-sfu /app/config + +USER webrtc-sfu +WORKDIR /app +CMD [ "npm", "start" ] diff --git a/sample.env b/sample.env new file mode 100644 index 0000000..f83534d --- /dev/null +++ b/sample.env @@ -0,0 +1,339 @@ +# ==================================== +# ADDITIONS to BigBlueButton +# ==================================== +# (place a '#' before to disable them) + +# HTTPS Proxy +# fully automated Lets Encrypt certificates +ENABLE_HTTPS_PROXY=true + +# coturn (a TURN Server) +# requires either the abhove HTTPS Proxy to be enabled +# or TLS certificates to be mounted to container +ENABLE_COTURN=true +#COTURN_TLS_CERT_PATH= +#COTURN_TLS_KEY_PATH= + +# Greenlight Frontend +# https://docs.bigbluebutton.org/greenlight/gl-overview.html +ENABLE_GREENLIGHT=true + +# Enable Webhooks +# used by some integrations +#ENABLE_WEBHOOKS=true + +# Prometheus Exporter +# serves the bigbluebutton-exporter under following URL: +# https://yourdomain/bbb-exporter +#ENABLE_PROMETHEUS_EXPORTER=true +#ENABLE_PROMETHEUS_EXPORTER_OPTIMIZATION=true + +# Recording +# IMPORTANT: this is currently a big privacy issues, because it will +# record everything which happens in the conference, even when the button +# suggets, that it does not. +# https://github.com/bigbluebutton/bigbluebutton/issues/9202 +# make sure that you get peoples consent, before they join a room +#ENABLE_RECORDING=true +#REMOVE_OLD_RECORDING=false +#RECORDING_MAX_AGE_DAYS=14 + +# ==================================== +# SECRETS +# ==================================== +# important! change these to any random values +SHARED_SECRET=SuperSecret +ETHERPAD_API_KEY=SuperEtherpadKey +RAILS_SECRET=SuperRailsSecret +POSTGRESQL_SECRET=SuperPostgresSecret +FSESL_PASSWORD=SuperFreeswitchESLPassword + + + +# ==================================== +# CONNECTION +# ==================================== + +DOMAIN=bbb.example.com + +EXTERNAL_IPv4=144.76.97.10 +EXTERNAL_IPv6= + +# STUN SERVER +# stun.freeswitch.org +STUN_IP=216.93.246.18 +STUN_PORT=3478 + +# TURN SERVER +# uncomment and adjust following two lines to add an external TURN server +#TURN_SERVER=turns:turn.example.com:443?transport=tcp +#TURN_SECRET= + +# Allowed SIP IPs +# due to high traffic caused by bots, by default the SIP port is blocked. +# but you can allow access by your providers IP or IP ranges (comma seperated) +# Hint: if you want to allow requests from every IP, you can use 0.0.0.0/0 +SIP_IP_ALLOWLIST= + + +# ==================================== +# CUSTOMIZATION +# ==================================== + +CLIENT_TITLE=BigBlueButton + +# use following lines to replace the default welcome message and footer +WELCOME_MESSAGE="Welcome to %%CONFNAME%%!

For help on using BigBlueButton see these (short) tutorial videos.

To join the audio bridge click the speaker button. Use a headset to avoid causing background noise for others." +WELCOME_FOOTER="This server is running BigBlueButton." + +# use following line for an additional SIP dial-in message +#WELCOME_FOOTER="This server is running BigBlueButton.

To join this meeting by phone, dial:
INSERT_YOUR_PHONE_NUMBER_HERE
Then enter %%CONFNUM%% as the conference PIN number." + +# for a different default presentation, place the pdf file in ./conf/ and +# adjust the following path +DEFAULT_PRESENTATION=./mod/nginx/default.pdf + +# language of sound announcements +# options: +# - en-ca-june - EN Canadian June +# - en-us-allison - US English Allison +# - en-us-callie - US English Callie +# - de-de-daedalus3 - German by Daedalus3 (https://github.com/Daedalus3/freeswitch-german-soundfiles) +# - es-ar-mario - Spanish/Argentina Mario +# - fr-ca-june - FR Canadian June +# - pt-br-karina - Brazilian Portuguese Karina +# - ru-RU-elena - RU Russian Elena +# - ru-RU-kirill - RU Russian Kirill +# - ru-RU-vika - RU Russian Viktoriya +# - sv-se-jakob - Swedish (Sweden) Jakob +# - zh-cn-sinmei - Chinese/China Sinmei +# - zh-hk-sinmei - Chinese/Hong Kong Sinmei +SOUNDS_LANGUAGE=en-us-callie + +# set to false to disable listenOnlyMode +LISTEN_ONLY_MODE=true + +# set to true to disable echo test +DISABLE_ECHO_TEST=false + +# set to true to automatically share webcam +AUTO_SHARE_WEBCAM=false + +# set to true to disable video preview for webcam sharing +DISABLE_VIDEO_PREVIEW=false + +# set to false to disable chat +CHAT_ENABLED=true + +# set to true to start chat closed +CHAT_START_CLOSED=false + +# set to true to disable announcements "You are now (un-)muted" +DISABLE_SOUND_MUTED=false + +# set to true to disable announcement "You are the only person in this conference" +DISABLE_SOUND_ALONE=false + +# maximum count of breakout rooms per meeting +# Warning: increasing the limit of breakout rooms per meeting +# can generate excessive overhead to the server. We recommend +# this value to be kept under 12. +BREAKOUTROOM_LIMIT=8 + +# set to false to disable the learning dashboard +ENABLE_LEARNING_DASHBOARD=true + +# ==================================== +# Tuning +# ==================================== +# Default = 2; Min = 1; Max = 4 +# On powerful systems with high number of meetings you can set values up to 4 to accelerate handling of events +NUMBER_OF_BACKEND_NODEJS_PROCESSES=2 + +# Default = 2; Min = 1; Max = 8 +# Set a number between 1 and 4 times the value of NUMBER_OF_BACKEND_NODEJS_PROCESSES where higher number helps with meetings +# stretching the recommended number of users in BigBlueButton +NUMBER_OF_FRONTEND_NODEJS_PROCESSES=2 + + +# ==================================== +# GREENLIGHT CONFIGURATION +# ==================================== + +# Microsoft Office365 Login Provider (optional) +# +# For in-depth steps on setting up a Office 365 Login Provider, see: +# +# https://docs.bigbluebutton.org/greenlight/gl-config.html#office365-oauth2 +# +OFFICE365_KEY= +OFFICE365_SECRET= +OFFICE365_HD= + +# OAUTH2_REDIRECT allows you to specify the redirect_url passed to oauth on sign in. +# It is useful for cases when Greenlight is deployed behind a Network Load Balancer or proxy +OAUTH2_REDIRECT= + +# LDAP Login Provider (optional) +# +# You can enable LDAP authentication by providing values for the variables below. +# Configuring LDAP authentication will take precedence over all other providers. +# For information about setting up LDAP, see: +# +# https://docs.bigbluebutton.org/greenlight/gl-config.html#ldap-auth +# +# LDAP_SERVER=ldap.example.com +# LDAP_PORT=389 +# LDAP_METHOD=plain +# LDAP_UID=uid +# LDAP_BASE=dc=example,dc=com +# LDAP_AUTH=simple +# LDAP_BIND_DN=cn=admin,dc=example,dc=com +# LDAP_PASSWORD=password +# LDAP_ROLE_FIELD=ou +# LDAP_FILTER=(&(attr1=value1)(attr2=value2)) +LDAP_SERVER= +LDAP_PORT= +LDAP_METHOD= +LDAP_UID= +LDAP_BASE= +LDAP_BIND_DN= +LDAP_AUTH= +LDAP_PASSWORD= +LDAP_ROLE_FIELD= +LDAP_FILTER= + +# Set this to true if you want GreenLight to support user signup and login without +# Omniauth. For more information, see: +# +# https://docs.bigbluebutton.org/greenlight/gl-overview.html#accounts-and-profile +# +ALLOW_GREENLIGHT_ACCOUNTS=true + +# Set this to true if you want GreenLight to send verification emails upon +# the creation of a new account +# +# ALLOW_MAIL_NOTIFICATIONS=true +# +# The notifications are sent using sendmail, unless the SMTP_SERVER variable is set. +# In that case, make sure the rest of the variables are properly set. +# +# SMTP_SERVER=smtp.gmail.com +# SMTP_PORT=587 +# SMTP_DOMAIN=gmail.com +# SMTP_USERNAME= +# SMTP_PASSWORD= +# SMTP_AUTH=plain +# SMTP_STARTTLS_AUTO=true +# +# If your mail server has a self-signed certificate, you'll also need to include the line below. +# Please note that enable this presents its own security risks and should not be done unless necessary. +# SMTP_OPENSSL_VERIFY_MODE=none +# +SMTP_SERVER= +SMTP_PORT= +SMTP_DOMAIN= +SMTP_USERNAME= +SMTP_PASSWORD= +SMTP_AUTH= +SMTP_STARTTLS_AUTO= + +# Specify the email address that all mail is sent from +SMTP_SENDER= + +# Prefix for the applications root URL. +# Useful for deploying the application to a subdirectory, which is highly recommended +# if deploying on a BigBlueButton server. Keep in mind that if you change this, you'll +# have to update your authentication callback URL's to reflect this change. +# +# The recommended prefix is "/b". +# +RELATIVE_URL_ROOT=/b + +# Specify which settings you would like the users to configure on room creation +# or edit after the room has been created +# By default, all settings are turned OFF. +# +# Current settings available: +# mute-on-join: Automatically mute users by default when they join a room +# require-moderator-approval: Require moderators to approve new users before they can join the room +# anyone-can-start: Allows anyone with the join url to start the room in BigBlueButton +# all-join-moderator: All users join as moderators in BigBlueButton +ROOM_FEATURES=mute-on-join,require-moderator-approval,anyone-can-start,all-join-moderator + +# Specify the maximum number of records to be sent to the BigBlueButton API in one call +# Default is set to 25 records +PAGINATION_NUMBER=25 + +# Specify the maximum number of rows that should be displayed per page for a paginated table +# Default is set to 25 rows +NUMBER_OF_ROWS=25 + +# Specify if you want to display the Google Calendar button +# ENABLE_GOOGLE_CALENDAR_BUTTON=true|false +ENABLE_GOOGLE_CALENDAR_BUTTON= + +# Set the application into Maintenance Mode +# +# Current options supported: +# true: Renders an error page that does not allow users to access any of the features in the application +# false: Application runs normally +MAINTENANCE_MODE=false + +# Displays a flash that appears to inform the user of a scheduled maintenance window +# This variable should contain ONLY the date and time of the scheduled maintenance +# +# Ex: MAINTENANCE_WINDOW=Friday August 18 6pm-10pm EST +MAINTENANCE_WINDOW= + +# The link to the Report an Issue button that appears on the 500 page and in the Account Dropdown +# +# Defaults to the Github Issues Page for Greenlight +# Button can be disabled by setting the value to blank +# +# REPORT_ISSUE_URL=https://github.com/bigbluebutton/greenlight/issues/new + +# The link to the Need help? button that appears on the Account Dropdown +# +# Defaults to the Greenlight documentation +# Button can be disabled by setting the value to blank +HELP_URL=https://docs.bigbluebutton.org/greenlight/gl-overview.html + +# Comment this out to send logs to STDOUT in production instead of log/production.log . +# +# RAILS_LOG_TO_STDOUT=true +# +# When using docker-compose the logs can be sent to an centralized repository like PaperTrail +# just by using the built in driver. Make sure to add to docker-compose.yml the next lines: +# +# logging: +# driver: $LOG_DRIVER +# options: +# syslog-address: $LOG_ADDRESS +# tag: $LOG_TAG +# +# And set this variables up: +# +# LOG_DRIVER=syslog +# LOG_ADDRESS=udp://logs4.papertrailapp.com:[99999] +# LOG_TAG=greenlight.example.com:v2 +# +# Check docker-compose and papertrail documentation for encrypting and +# protecting access to the log repository. +# https://docs.docker.com/config/containers/logging/syslog/#options +# https://help.papertrailapp.com/kb/configuration/encrypting-remote-syslog-with-tls-ssl/ +# +# For sending logs to a remote aggregator enable these variables: +# +# RAILS_LOG_REMOTE_NAME=logxx.papertrailapp.com +# RAILS_LOG_REMOTE_PORT=9999 +# RAILS_LOG_REMOTE_TAG=greenlight +# + +# Specify the default registration to be used by Greenlight until an administrator sets the +# registration method +# Allowed values are: +# open - For open registration +# invite - For invite only registration +# approval - For approve/decline registration +DEFAULT_REGISTRATION=open diff --git a/scripts/bbb-record b/scripts/bbb-record new file mode 100755 index 0000000..3814956 --- /dev/null +++ b/scripts/bbb-record @@ -0,0 +1,20 @@ +#!/bin/bash +set -e + +cd $(dirname $0)/.. + +# load .env +if [ -f .env ] +then + # exclude WELCOME_FOOTER because it may contain invalid characters + export $(cat .env | sed 's/#.*//g' | grep -v "WELCOME_FOOTER" | grep -v "WELCOME_MESSAGE" | xargs) +fi + +if [ ! "$ENABLE_RECORDING" == true ]; then + echo "Error: recording is disabled why can't use bbb-record" + echo "set ENABLE_RECORDING in .env if you want to use it" + exit 1 +fi + +docker-compose exec recordings bbb-record $@ +docker-compose logs --tail=15 recordings \ No newline at end of file diff --git a/scripts/checkout-submodules b/scripts/checkout-submodules new file mode 100755 index 0000000..6724ea0 --- /dev/null +++ b/scripts/checkout-submodules @@ -0,0 +1,27 @@ +#!/bin/bash + +# checkouts submodules based on the tag +# provided in `tags.env` + + +set -e +cd "$(dirname "$0")/.." + +export $(cat tags.env | sed 's/#.*//g' | xargs) + +function checkout { + path=$1 + ref=$2 + pushd $path + git checkout $ref + popd > /dev/null +} + +checkout mod/webrtc-sfu/bbb-webrtc-sfu $TAG_WEBRTC_SFU +checkout mod/nginx/bbb-playback $TAG_PLAYBACK +checkout mod/bbb-pads/bbb-pads $TAG_PADS +checkout mod/webhooks/bbb-webhooks $TAG_WEBHOOKS + +echo "" +echo "# git submodule" +git submodule \ No newline at end of file diff --git a/scripts/fs_cli b/scripts/fs_cli new file mode 100755 index 0000000..56462bf --- /dev/null +++ b/scripts/fs_cli @@ -0,0 +1,12 @@ +#!/bin/bash +set -e + +cd $(dirname $0)/.. + +# load .env +if [ -f .env ] +then + export $(cat .env | sed 's/#.*//g' | grep "FSESL_PASSWORD" | xargs) +fi + +docker-compose exec freeswitch fs_cli -H 10.7.7.1 -p "$FSESL_PASSWORD" diff --git a/scripts/generate-compose b/scripts/generate-compose new file mode 100755 index 0000000..7beb472 --- /dev/null +++ b/scripts/generate-compose @@ -0,0 +1,68 @@ +#!/bin/bash +set -e + +cd $(dirname $0)/.. + +# load .env +if [ -f .env ] +then + # exclude WELCOME_MESSAGE && WELCOME_FOOTER && CLIENT_TITLE because it may contain invalid characters + export $(cat .env | sed 's/#.*//g' | grep -v "WELCOME_FOOTER" | grep -v "WELCOME_MESSAGE" | grep -v "CLIENT_TITLE" | xargs) +fi + +# check for non-optional environment variables, +# which got introduced later and may miss in existing +# .env files during upgrades +if [ -z "$EXTERNAL_IPv4" ]; then + echo "ERROR: EXTERNAL_IPv4 is not set in .env" + echo "BBB won't work without it." + exit 1 +fi + +if [ "$ENABLE_COTURN" == true ]; then + if [ -z "$ENABLE_HTTPS_PROXY" ] && [ -z "$COTURN_TLS_CERT_PATH" ]; then + echo "ERROR: coturn requires TLS certificates." + echo "Either enable the https proxy for certificate retrival" + echo "or provide a path to your certificates in .env file." + exit 1 + fi + if [ -z "$ENABLE_HTTPS_PROXY" ] && [ "$DEV_MODE" == true ]; then + echo "ERROR: the https proxy can't get a certificate if ran locally and therefor coturn will never start" + echo "you should disable coturn in .env" + exit 1 + fi +fi + +export $(cat tags.env | sed 's/#.*//g' | xargs) + +docker run \ + --rm \ + -v $(pwd)/docker-compose.tmpl.yml:/docker-compose.tmpl.yml \ + -e BBB_BUILD_TAG=${BBB_BUILD_TAG} \ + -e TAG_COMMON_MESSAGE=${TAG_COMMON_MESSAGE} \ + -e TAG_APPS_AKKA=${TAG_APPS_AKKA} \ + -e TAG_FSESL_AKKA=${TAG_FSESL_AKKA} \ + -e TAG_BBB_WEB=${TAG_BBB_WEB} \ + -e TAG_HTML5=${TAG_HTML5} \ + -e TAG_FS_CONFIG=${TAG_FS_CONFIG} \ + -e TAG_LEARNING_DASHBOARD=${TAG_LEARNING_DASHBOARD} \ + -e TAG_RECORDINGS=${TAG_RECORDINGS} \ + -e TAG_WEBRTC_SFU=${TAG_WEBRTC_SFU} \ + -e TAG_WEBHOOKS=${TAG_WEBHOOKS} \ + -e TAG_PLAYBACK=${TAG_PLAYBACK} \ + -e TAG_PADS=${TAG_PADS} \ + -e DEV_MODE=${DEV_MODE:-false} \ + -e EXTERNAL_IPv6=${EXTERNAL_IPv6:-} \ + -e ENABLE_RECORDING=${ENABLE_RECORDING:-false} \ + -e REMOVE_OLD_RECORDING=${REMOVE_OLD_RECORDING:-false} \ + -e RECORDING_MAX_AGE_DAYS=${RECORDING_MAX_AGE_DAYS:-14} \ + -e ENABLE_HTTPS_PROXY=${ENABLE_HTTPS_PROXY:-false} \ + -e ENABLE_WEBHOOKS=${ENABLE_WEBHOOKS:-false} \ + -e ENABLE_COTURN=${ENABLE_COTURN:-false} \ + -e ENABLE_GREENLIGHT=${ENABLE_GREENLIGHT:-false} \ + -e ENABLE_PROMETHEUS_EXPORTER=${ENABLE_PROMETHEUS_EXPORTER:-false} \ + -e ENABLE_PROMETHEUS_EXPORTER_OPTIMIZATION=${ENABLE_PROMETHEUS_EXPORTER_OPTIMIZATION:-false} \ + -e NUMBER_OF_BACKEND_NODEJS_PROCESSES=${NUMBER_OF_BACKEND_NODEJS_PROCESSES:-1} \ + -e NUMBER_OF_FRONTEND_NODEJS_PROCESSES=${NUMBER_OF_FRONTEND_NODEJS_PROCESSES:-1} \ + jwilder/dockerize -template /docker-compose.tmpl.yml \ + > docker-compose.yml diff --git a/scripts/publish b/scripts/publish new file mode 100755 index 0000000..41b475f --- /dev/null +++ b/scripts/publish @@ -0,0 +1,51 @@ +#!/bin/bash + +set -e +cd "$(dirname "$0")/.." + +# load .env +if [ -f .env ] +then + # exclude WELCOME_MESSAGE && WELCOME_FOOTER && CLIENT_TITLE because it may contain invalid characters + export $(cat .env | sed 's/#.*//g' | grep -v "WELCOME_FOOTER" | grep -v "WELCOME_MESSAGE" | grep -v "CLIENT_TITLE" | xargs) +fi + +if [ -z "$ENABLE_WEBHOOKS" ]; then + echo "ERROR: ENABLE_WEBHOOKS must be set to true, otherwise the image would not be built" + exit 1 +fi + +if [ -z "$ENABLE_RECORDING" ]; then + echo "ERROR: ENABLE_RECORDING must be set to true, otherwise the image would not be built" + exit 1 +fi + + +# generate compose file +./scripts/generate-compose + +# ensure submodules are matching tags.env +./scripts/checkout-submodules + +# build and push java base image +docker build -t alangecker/bbb-docker-base-java:latest mod/base-java + +# buld and push other images +docker-compose build + +# push images +docker push alangecker/bbb-docker-base-java:latest +docker-compose push \ + html5-backend-1 \ + bbb-web \ + freeswitch \ + apps-akka \ + bbb-pads \ + etherpad \ + fsesl-akka \ + jodconverter \ + nginx \ + periodic \ + recordings \ + webhooks \ + webrtc-sfu diff --git a/scripts/setup b/scripts/setup new file mode 100755 index 0000000..3f4ecdf --- /dev/null +++ b/scripts/setup @@ -0,0 +1,211 @@ +#!/bin/bash + +set -e +cd "$(dirname "$0")/.." + +if ! [ -x "$(command -v curl)" ]; then + echo "Error: curl is not installed, but the setup script relies on it." + echo "on debian based operating systems try following command:" + echo " $ sudo apt-get install curl" + exit 1 +fi + +# load .env +if [ -f .env ] +then + echo "Error: the configuration file .env already exists." + echo "either edit variables manually in there or remove the file and try this script again" + exit 1 +fi + + +EXTERNAL_IPv4=$(curl -4 -s https://icanhazip.com) +EXTERNAL_IPv6=$(curl -6 -s -m 10 https://icanhazip.com || true) + +greenlight="" +while [[ ! $greenlight =~ ^(y|n)$ ]]; do + read -p "Should greenlight be included? (y/n): " greenlight +done + +https_proxy="" +while [[ ! $https_proxy =~ ^(y|n)$ ]]; do + read -p "Should an automatic HTTPS Proxy be included? (y/n): " https_proxy +done + +coturn="" +while [[ ! $coturn =~ ^(y|n)$ ]]; do + read -p "Should a coturn be included? (y/n): " coturn +done +if [ "$coturn" == "y" ] && [ ! "$https_proxy" == "y" ] +then + echo "Coturn needs TLS to function properly." + echo " Since automatic HTTPS Proxy is disabled," + echo " you must provide a relative or absolute path" + echo " to your certificates." + while [[ -z "$CERTPATH" ]]; do + read -p "Please enter path to cert.pem: " CERTPATH + done + while [[ -z "$KEYPATH" ]]; do + read -p "Please enter path to key.pem: " KEYPATH + done +fi + +DOMAIN="" +while [[ -z "$DOMAIN" ]]; do + read -p "Please enter the domain name: " DOMAIN +done + +recording="" +echo "Should the recording feature be included?" +echo " IMPORTANT: this is currently a big privacy issues, because it will " +echo " record everything which happens in the conference, even when the button" +echo " suggests, that it does not." +echo " make sure that you always get people's consent, before they join a room!" +echo " https://github.com/bigbluebutton/bigbluebutton/issues/9202" +while [[ ! $recording =~ ^(y|n)$ ]]; do + read -p "Choice (y/n): " recording +done + +prometheus_exporter="" +while [[ ! $prometheus_exporter =~ ^(y|n)$ ]]; do + read -p "Should a Prometheus exporter be included? (y/n): " prometheus_exporter +done +if [ "$prometheus_exporter" == "y" ] && [ "$recording" == "y" ] +then + echo "Should Prometheus exporter optimization be enabled?" + echo " This instructs exporter to collect expensive recordings metrics by querying the disk instead of the API." + echo " Enabling this can substantially decrease the scrape time required for the exporter to respond to metrics requests" + prometheus_exporter_optimization="" + while [[ ! $prometheus_exporter_optimization =~ ^(y|n)$ ]]; do + read -p "Choice (y/n): " prometheus_exporter_optimization + done +fi + +if [ "$recording" == "y" ] +then + + remove_old_recording="" + while [[ ! $remove_old_recording =~ ^(y|n)$ ]]; do + read -p "Should old recordings be removed? (y/n): " remove_old_recording + done + + if [ "$remove_old_recording" == "y" ] + then + recording_max_age_days="" + while [[ ! $recording_max_age_days =~ ^[0-9]{1,4}$ ]]; do + read -p "Please enter max age(days) for keeping recordings: " recording_max_age_days + done + fi + +fi + +ip_correct="" +while [[ ! $ip_correct =~ ^(y|n)$ ]]; do + read -p "Is $EXTERNAL_IPv4 your external IPv4 address? (y/n): " ip_correct +done + +if [ ! "$ip_correct" == "y" ] +then + EXTERNAL_IPv4="" + while [[ ! $EXTERNAL_IPv4 =~ ^[1-9][0-9]{0,2}\.[0-9]{0,3}\.[0-9]{0,3}\.[1-9][0-9]{0,2}$ ]]; do + read -p "Please enter correct IPv4 address: " EXTERNAL_IPv4 + done +fi + +if [ -n "$EXTERNAL_IPv6" ] +then + ip_correct="" + while [[ ! $ip_correct =~ ^(y|n)$ ]]; do + read -p "Is $EXTERNAL_IPv6 your external IPv6 address? (y/n): " ip_correct + done + + if [ ! "$ip_correct" == "y" ] + then + EXTERNAL_IPv6="" + while [[ ! $EXTERNAL_IPv6 =~ ^[0-9a-z:]{3,39}$ ]]; do + read -p "Please enter correct IPv6 address: " EXTERNAL_IPv6 + done + fi +fi + + + +# write settings +cp sample.env .env +sed -i "s/EXTERNAL_IPv4=.*/EXTERNAL_IPv4=$EXTERNAL_IPv4/" .env +sed -i "s/EXTERNAL_IPv6=.*/EXTERNAL_IPv6=$EXTERNAL_IPv6/" .env +sed -i "s/DOMAIN=.*/DOMAIN=$DOMAIN/" .env + +if [ ! "$greenlight" == "y" ] +then + sed -i "s/ENABLE_GREENLIGHT.*/#ENABLE_GREENLIGHT=true/" .env +fi + +if [ ! "$https_proxy" == "y" ] +then + sed -i "s/ENABLE_HTTPS_PROXY.*/#ENABLE_HTTPS_PROXY=true/" .env +fi + +if [ "$recording" == "y" ] +then + sed -i "s/#ENABLE_RECORDING.*/ENABLE_RECORDING=true/" .env +fi + +if [ "$remove_old_recording" == "y" ] +then + sed -i "s/#REMOVE_OLD_RECORDING=.*/REMOVE_OLD_RECORDING=true/" .env + sed -i "s/#RECORDING_MAX_AGE_DAYS=.*/RECORDING_MAX_AGE_DAYS=$recording_max_age_days/" .env +fi + +if [ "$coturn" == "y" ] +then + sed -i "s/.*TURN_SERVER=.*/TURN_SERVER=turns:$DOMAIN:5349?transport=tcp/" .env + TURN_SECRET=$(head /dev/urandom | tr -dc A-Za-f0-9 | head -c 32) + sed -i "s/.*TURN_SECRET=.*/TURN_SECRET=$TURN_SECRET/" .env + sed -i "s/.*STUN_IP=.*/STUN_IP=$EXTERNAL_IPv4/" .env +else + sed -i "s/ENABLE_COTURN.*/#ENABLE_COTURN=true/" .env +fi + +if [ -n "$CERTPATH" ] && [ -n "$KEYPATH" ] +then + sed -i "s,#COTURN_TLS_CERT_PATH=.*,COTURN_TLS_CERT_PATH=$CERTPATH," .env + sed -i "s,#COTURN_TLS_KEY_PATH=.*,COTURN_TLS_KEY_PATH=$KEYPATH," .env +fi + +if [ "$prometheus_exporter" == "y" ] +then + sed -i "s/#ENABLE_PROMETHEUS_EXPORTER=.*/ENABLE_PROMETHEUS_EXPORTER=true/" .env +fi + +if [ "$prometheus_exporter_optimization" == "y" ] +then + sed -i "s/#ENABLE_PROMETHEUS_EXPORTER_OPTIMIZATION=.*/ENABLE_PROMETHEUS_EXPORTER_OPTIMIZATION=true/" .env +fi + +# change secrets +RANDOM_1=$(head /dev/urandom | tr -dc A-Za-z0-9 | head -c 40) +RANDOM_2=$(head /dev/urandom | tr -dc A-Za-z0-9 | head -c 40) +RANDOM_3=$(head /dev/urandom | tr -dc a-f0-9 | head -c 128) +RANDOM_4=$(head /dev/urandom | tr -dc A-Za-z0-9 | head -c 40) +RANDOM_5=$(head /dev/urandom | tr -dc A-Za-z0-9 | head -c 40) + +sed -i "s/SHARED_SECRET=.*/SHARED_SECRET=$RANDOM_1/" .env +sed -i "s/ETHERPAD_API_KEY=.*/ETHERPAD_API_KEY=$RANDOM_2/" .env +sed -i "s/RAILS_SECRET=.*/RAILS_SECRET=$RANDOM_3/" .env +sed -i "s/FSESL_PASSWORD=.*/FSESL_PASSWORD=$RANDOM_4/" .env +sed -i "s/POSTGRESQL_SECRET=.*/POSTGRESQL_SECRET=$RANDOM_5/" .env + +./scripts/generate-compose + +echo "--------------------------------------------------" +echo "configuration file .env got successfully created!" +echo "" +echo "you can look through it for further adjusments" +echo " $ nano .env" +echo "" +echo "make sure to recreate the docker-compose.yml after each change" +echo " $ ./scripts/generate-compose" +echo "" +echo "to start bigbluebutton run" +echo " $ docker-compose up -d" diff --git a/scripts/upgrade b/scripts/upgrade new file mode 100755 index 0000000..b950c48 --- /dev/null +++ b/scripts/upgrade @@ -0,0 +1,38 @@ +#!/bin/bash + +set -e +cd $(dirname $0)/.. + +if [ -z $RESTARTED ] +then + + echo "# pull newest bigblugbutton-docker.git" + git pull + + # restart script, since it might have changed. + RESTARTED=1 ./scripts/upgrade + exit + +else + + echo "" + echo "# recreate docker-compose.yml" + ./scripts/generate-compose + + echo "" + echo "# pull newest images" + docker-compose pull --ignore-pull-failures + + + COMMIT_HASH=$(git rev-parse --short HEAD) + BRANCH_NAME=$(git rev-parse --abbrev-ref HEAD) + + echo "" + echo "-------------------------------------" + echo "update is ready!" + echo "we are on $COMMIT_HASH ($BRANCH_NAME)" + echo "" + echo "use following command for restarting bbb:" + echo " $ docker-compose up -d" + echo "-------------------------------------" +fi \ No newline at end of file diff --git a/scripts/upgrade-and-build b/scripts/upgrade-and-build new file mode 100755 index 0000000..8f7b0e9 --- /dev/null +++ b/scripts/upgrade-and-build @@ -0,0 +1,49 @@ +#!/bin/bash + +set -e +cd $(dirname $0)/.. + +if [ -z $RESTARTED ] +then + + echo "# pull newest bigblugbutton-docker.git" + git pull + + # restart script, since it might have changed. + RESTARTED=1 ./scripts/upgrade-and-build + exit + +else + + echo "" + echo "# pull newest git submodules" + git submodule init + git submodule update + + echo "" + echo "# recreate docker-compose.yml" + ./scripts/generate-compose + + echo "" + echo "# pull newest images" + docker-compose pull --ignore-pull-failures + + echo "" + echo "# rebuild images" + docker build -t alangecker/bbb-docker-base-java:latest mod/base-java + + # rebuild everything which got modified + docker-compose build + + COMMIT_HASH=$(git rev-parse --short HEAD) + BRANCH_NAME=$(git rev-parse --abbrev-ref HEAD) + + echo "" + echo "-------------------------------------" + echo "images successfully rebuilt!" + echo "we are on $COMMIT_HASH ($BRANCH_NAME)" + echo "" + echo "use following command for restarting bbb:" + echo " $ docker-compose up -d" + echo "-------------------------------------" +fi \ No newline at end of file diff --git a/tags.env b/tags.env new file mode 100644 index 0000000..21e5d15 --- /dev/null +++ b/tags.env @@ -0,0 +1,27 @@ +# after changing a tag always run following commands to apply +# - ./scripts/generate-compose +# - docker-compose build + + +# https://gitlab.senfcall.de/senfcall-public/docker-bbb-build +BBB_BUILD_TAG=v2022-03-30 + +# https://github.com/bigbluebutton/bigbluebutton +TAG_COMMON_MESSAGE=v2.5.7 +TAG_APPS_AKKA=v2.5.7 +TAG_FSESL_AKKA=v2.5.7 +TAG_BBB_WEB=v2.5.8 +TAG_HTML5=v2.5.7 +TAG_RECORDINGS=v2.5.7 + +TAG_LEARNING_DASHBOARD=v2.5.5 +TAG_FS_CONFIG=v2.5.7 + + +# individual git submodules +# use `./scripts/checkout-submodules` to ensure, that the submodules +# match the provided tags here +TAG_WEBRTC_SFU=v2.8.6 +TAG_WEBHOOKS=v2.6.0 +TAG_PLAYBACK=v4.0.0 +TAG_PADS=v1.2.2 \ No newline at end of file