1890 lines
71 KiB
Bash
1890 lines
71 KiB
Bash
#!/bin/bash -e
|
|
|
|
# Copyright (c) 2023 BigBlueButton Inc.
|
|
#
|
|
# 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 <http://www.gnu.org/licenses/>.
|
|
|
|
# BigBlueButton is an open source conferencing system. For more information see
|
|
# https://www.bigbluebutton.org/.
|
|
#
|
|
# This bbb-install.sh script automates many of the installation and configuration
|
|
# steps at https://docs.bigbluebutton.org/2.7/administration/install
|
|
#
|
|
#
|
|
# Examples
|
|
#
|
|
# Install BigBlueButton 2.7.x with a SSL certificate from Let's Encrypt using hostname bbb.example.com
|
|
# and email address info@example.com and apply a basic firewall
|
|
#
|
|
# wget -qO- https://raw.githubusercontent.com/bigbluebutton/bbb-install/v2.7.x-release/bbb-install.sh | bash -s -- -w -v focal-270 -s bbb.example.com -e info@example.com
|
|
#
|
|
# Install BigBlueButton with SSL + Greenlight
|
|
#
|
|
# wget -qO- https://raw.githubusercontent.com/bigbluebutton/bbb-install/v2.7.x-release/bbb-install.sh | bash -s -- -w -v focal-270 -s bbb.example.com -e info@example.com -g
|
|
#
|
|
|
|
usage() {
|
|
set +x
|
|
cat 1>&2 <<HERE
|
|
|
|
Script for installing a BigBlueButton 2.7 server in under 30 minutes. It also supports upgrading a BigBlueButton server to version 2.7 (from version 2.6.0+ or an earlier 2.7.x version)
|
|
|
|
This script also checks if your server supports https://docs.bigbluebutton.org/administration/install/#minimum-server-requirements
|
|
|
|
USAGE:
|
|
wget -qO- https://raw.githubusercontent.com/bigbluebutton/bbb-install/v2.7.x-release/bbb-install.sh | bash -s -- [OPTIONS]
|
|
|
|
OPTIONS (install BigBlueButton):
|
|
|
|
-v <version> Install given version of BigBlueButton (e.g. 'focal-270') (required)
|
|
|
|
-s <hostname> Configure server with <hostname>
|
|
-e <email> Email for Let's Encrypt certbot
|
|
|
|
-x Use Let's Encrypt certbot with manual DNS challenges
|
|
|
|
-g Install Greenlight version 3
|
|
-k Install Keycloak version 20
|
|
|
|
-t <key>:<secret> Install BigBlueButton LTI framework tools and add/update LTI consumer credentials <key>:<secret>
|
|
|
|
-c <hostname>:<secret> Configure with external coturn server at <hostname> using <secret> (instead of built-in TURN server)
|
|
|
|
-m <link_path> Create a Symbolic link from /var/bigbluebutton to <link_path>
|
|
|
|
-p <host>[:<port>] Use apt-get proxy at <host> (default port 3142)
|
|
-r <host> Use alternative apt repository (such as packages-eu.bigbluebutton.org)
|
|
|
|
-d Skip SSL certificates request (use provided certificates from mounted volume) in /local/certs/
|
|
-w Install UFW firewall (recommended)
|
|
|
|
-j Allows the installation of BigBlueButton to proceed even if not all requirements [for production use] are met.
|
|
Note that not all requirements can be ignored. This is useful in development / testing / ci scenarios.
|
|
|
|
-i Allows the installation of BigBlueButton to proceed even if Apache webserver is installed.
|
|
|
|
-h Print help
|
|
|
|
OPTIONS (install Let's Encrypt certificate only):
|
|
|
|
-s <hostname> Configure server with <hostname> (required)
|
|
-e <email> Configure email for Let's Encrypt certbot (required)
|
|
-l Only install Let's Encrypt certificate (not BigBlueButton)
|
|
-x Use Let's Encrypt certbot with manual dns challenges (optional)
|
|
|
|
OPTIONS (install Greenlight only):
|
|
|
|
-g Install Greenlight version 3 (required)
|
|
-k Install Keycloak version 20 (optional)
|
|
|
|
OPTIONS (install BigBlueButton LTI framework only):
|
|
|
|
-t <key>:<secret> Install BigBlueButton LTI framework tools and add/update LTI consumer credentials <key>:<secret> (required)
|
|
|
|
VARIABLES (configure Greenlight only):
|
|
GL_PATH Configure Greenlight relative URL root path (Optional)
|
|
* Use this when deploying Greenlight behind a reverse proxy on a path other than the default '/' e.g. '/gl'.
|
|
|
|
|
|
EXAMPLES:
|
|
|
|
Sample options for setup a BigBlueButton 2.7 server
|
|
|
|
-v focal-270 -s bbb.example.com -e info@example.com
|
|
|
|
Sample options for setup a BigBlueButton 2.7 server with Greenlight 3 and optionally Keycloak
|
|
|
|
-v focal-270 -s bbb.example.com -e info@example.com -g [-k]
|
|
|
|
Sample options for setup a BigBlueButton 2.7 server with LTI framework while managing LTI consumer credentials MY_KEY:MY_SECRET
|
|
|
|
-v focal-270 -s bbb.example.com -e info@example.com -t MY_KEY:MY_SECRET
|
|
|
|
SUPPORT:
|
|
Community: https://bigbluebutton.org/support
|
|
Docs: https://github.com/bigbluebutton/bbb-install
|
|
https://docs.bigbluebutton.org/administration/install/#minimum-server-requirements
|
|
|
|
HERE
|
|
}
|
|
|
|
main() {
|
|
export DEBIAN_FRONTEND=noninteractive
|
|
PACKAGE_REPOSITORY=ubuntu.bigbluebutton.org
|
|
LETS_ENCRYPT_OPTIONS=(--webroot --non-interactive)
|
|
SOURCES_FETCHED=false
|
|
GL3_DIR=~/greenlight-v3
|
|
LTI_DIR=~/bbb-lti
|
|
NGINX_FILES_DEST=/usr/share/bigbluebutton/nginx
|
|
CR_TMPFILE=$(mktemp /tmp/carriage-return.XXXXXX)
|
|
printf '\n' > "$CR_TMPFILE"
|
|
|
|
need_x64
|
|
|
|
while builtin getopts "hs:r:c:v:e:p:m:t:xgadwjik" opt "${@}"; do
|
|
|
|
case $opt in
|
|
h)
|
|
usage
|
|
exit 0
|
|
;;
|
|
|
|
s)
|
|
HOST=$OPTARG
|
|
if [ "$HOST" == "bbb.example.com" ]; then
|
|
err "You must specify a valid hostname (not the hostname given in the docs)."
|
|
fi
|
|
;;
|
|
r)
|
|
PACKAGE_REPOSITORY=$OPTARG
|
|
;;
|
|
e)
|
|
EMAIL=$OPTARG
|
|
if [ "$EMAIL" == "info@example.com" ]; then
|
|
err "You must specify a valid email address (not the email in the docs)."
|
|
fi
|
|
;;
|
|
x)
|
|
LETS_ENCRYPT_OPTIONS=(--manual --preferred-challenges dns)
|
|
;;
|
|
c)
|
|
COTURN=$OPTARG
|
|
check_coturn "$COTURN"
|
|
;;
|
|
v)
|
|
VERSION=$OPTARG
|
|
;;
|
|
|
|
p)
|
|
PROXY=$OPTARG
|
|
if [ -n "$PROXY" ]; then
|
|
if [[ "$PROXY" =~ : ]]; then
|
|
echo "Acquire::http::Proxy \"http://$PROXY\";" > /etc/apt/apt.conf.d/01proxy
|
|
else
|
|
echo "Acquire::http::Proxy \"http://$PROXY:3142\";" > /etc/apt/apt.conf.d/01proxy
|
|
fi
|
|
fi
|
|
;;
|
|
|
|
g)
|
|
GREENLIGHT=true
|
|
GL_DEFAULT_PATH=/
|
|
|
|
if [ -n "$GL_PATH" ] && [ "$GL_PATH" != "$GL_DEFAULT_PATH" ]; then
|
|
if [[ ! $GL_PATH =~ ^/.*[^/]$ ]]; then
|
|
err "\$GL_PATH ENV is set to '$GL_PATH' which is invalid, Greenlight relative URL root path must start but not end with '/'."
|
|
fi
|
|
fi
|
|
;;
|
|
k)
|
|
INSTALL_KC=true
|
|
;;
|
|
t)
|
|
LTI_CREDS_STR=$OPTARG
|
|
|
|
if [ "$LTI_CREDS_STR" == "MY_KEY:MY_SECRET" ]; then
|
|
err "You must use a valid complex credentials for your LTI setup (not the ones in the example)."
|
|
fi
|
|
|
|
if [[ ! $LTI_CREDS_STR == *:* ]]; then
|
|
err "You must respect the format <key>:<secret> when specifying your LTI credentials."
|
|
fi
|
|
|
|
# Making LTI_CREDS an array, first element is the LTI TC key and the second is the LTI TC secret.
|
|
IFS=: read -ra LTI_CREDS <<<"${LTI_CREDS_STR}"
|
|
;;
|
|
a)
|
|
err "Error: bbb-demo (API demos, '-a' option) were deprecated in BigBlueButton 2.6. Please use Greenlight or API MATE"
|
|
;;
|
|
m)
|
|
LINK_PATH=$OPTARG
|
|
;;
|
|
d)
|
|
PROVIDED_CERTIFICATE=true
|
|
;;
|
|
w)
|
|
SSH_PORT=$(grep Port /etc/ssh/ssh_config | grep -v \# | sed 's/[^0-9]*//g')
|
|
if [[ -n "$SSH_PORT" && "$SSH_PORT" != "22" ]]; then
|
|
err "Detected sshd not listening to standard port 22 -- unable to install default UFW firewall rules. See https://docs.bigbluebutton.org/2.2/customize.html#secure-your-system--restrict-access-to-specific-ports"
|
|
fi
|
|
UFW=true
|
|
;;
|
|
j)
|
|
SKIP_MIN_SERVER_REQUIREMENTS_CHECK=true
|
|
;;
|
|
i)
|
|
SKIP_APACHE_INSTALLED_CHECK=true
|
|
;;
|
|
:)
|
|
err "Missing option argument for -$OPTARG"
|
|
;;
|
|
|
|
\?)
|
|
usage_err "Invalid option: -$OPTARG" >&2
|
|
;;
|
|
esac
|
|
done
|
|
|
|
if [ -n "$HOST" ]; then
|
|
check_host "$HOST"
|
|
fi
|
|
|
|
if [ -n "$VERSION" ]; then
|
|
check_version "$VERSION"
|
|
fi
|
|
|
|
if [ "$SKIP_APACHE_INSTALLED_CHECK" != true ]; then
|
|
check_apache2
|
|
fi
|
|
|
|
# Check if we're installing coturn (need an e-mail address for Let's Encrypt)
|
|
if [ -z "$VERSION" ] && [ -n "$COTURN" ]; then
|
|
if [ -z "$EMAIL" ]; then err "Installing coturn needs an e-mail address for Let's Encrypt"; fi
|
|
check_ubuntu 20.04
|
|
|
|
install_coturn
|
|
exit 0
|
|
fi
|
|
|
|
if [ -z "$VERSION" ]; then
|
|
usage
|
|
exit 0
|
|
fi
|
|
|
|
if [ -n "$INSTALL_KC" ] && [ -z "$GREENLIGHT" ]; then
|
|
err "Keycloak cannot be installed without Greenlight."
|
|
fi
|
|
|
|
# We're installing BigBlueButton
|
|
env
|
|
|
|
check_mem
|
|
check_cpus
|
|
check_ipv6
|
|
|
|
need_pkg software-properties-common # needed for add-apt-repository
|
|
sudo add-apt-repository universe
|
|
need_pkg wget curl gpg-agent dirmngr apparmor-utils
|
|
|
|
# need_pkg xmlstarlet
|
|
get_IP "$HOST"
|
|
|
|
if [ "$DISTRO" == "focal" ]; then
|
|
need_pkg ca-certificates
|
|
|
|
# yq version 3 is provided by ppa:bigbluebutton/support
|
|
# Uncomment the following to enable yq 4 after bigbluebutton/bigbluebutton#14511 is resolved
|
|
#need_ppa rmescandon-ubuntu-yq-bionic.list ppa:rmescandon/yq CC86BB64 # Edit yaml files with yq
|
|
|
|
#need_ppa libreoffice-ubuntu-ppa-focal.list ppa:libreoffice/ppa 1378B444 # Latest version of libreoffice
|
|
need_ppa bigbluebutton-ubuntu-support-focal.list ppa:bigbluebutton/support 2E1B01D0E95B94BC # Needed for libopusenc0
|
|
need_ppa martin-uni-mainz-ubuntu-coturn-focal.list ppa:martin-uni-mainz/coturn 4B77C2225D3BBDB3 # Coturn
|
|
|
|
if ! apt-key list 5AFA7A83 | grep -q -E "1024|4096"; then # Add Kurento package
|
|
sudo apt-key adv --keyserver https://keyserver.ubuntu.com --recv-keys 5AFA7A83
|
|
fi
|
|
|
|
rm -rf /etc/apt/sources.list.d/kurento.list # Kurento 6.15 now packaged with 2.3
|
|
|
|
if [ -f /etc/apt/sources.list.d/nodesource.list ] && grep -q 16 /etc/apt/sources.list.d/nodesource.list; then
|
|
# Node 16 might be installed, previously used in BigBlueButton
|
|
# Remove the repository config. This will cause the repository to get
|
|
# re-added using the current nodejs version, and nodejs will be upgraded.
|
|
sudo rm -r /etc/apt/sources.list.d/nodesource.list
|
|
fi
|
|
if [ ! -f /etc/apt/sources.list.d/nodesource.list ]; then
|
|
sudo mkdir -p /etc/apt/keyrings
|
|
curl -fsSL https://deb.nodesource.com/gpgkey/nodesource-repo.gpg.key | sudo gpg --dearmor -o /etc/apt/keyrings/nodesource.gpg
|
|
NODE_MAJOR=18
|
|
echo "deb [signed-by=/etc/apt/keyrings/nodesource.gpg] https://deb.nodesource.com/node_$NODE_MAJOR.x nodistro main" | sudo tee /etc/apt/sources.list.d/nodesource.list
|
|
fi
|
|
if ! apt-key list MongoDB | grep -q 4.4; then
|
|
wget -qO - https://www.mongodb.org/static/pgp/server-4.4.asc | sudo apt-key add -
|
|
fi
|
|
echo "deb [ arch=amd64 ] https://repo.mongodb.org/apt/ubuntu focal/mongodb-org/4.4 multiverse" | sudo tee /etc/apt/sources.list.d/mongodb-org-4.4.list
|
|
rm -f /etc/apt/sources.list.d/mongodb-org-4.2.list
|
|
|
|
touch /root/.rnd
|
|
MONGODB=mongodb-org
|
|
install_docker # needed for bbb-libreoffice-docker
|
|
need_pkg ruby
|
|
|
|
BBB_WEB_ETC_CONFIG=/etc/bigbluebutton/bbb-web.properties # Override file for local settings
|
|
|
|
need_pkg openjdk-17-jre
|
|
update-java-alternatives -s java-1.17.0-openjdk-amd64
|
|
|
|
# Remove old bbb-demo if installed from a previous 2.5 setup
|
|
if dpkg -s bbb-demo > /dev/null 2>&1; then
|
|
apt purge -y bbb-demo tomcat9
|
|
rm -rf /var/lib/tomcat9
|
|
fi
|
|
fi
|
|
|
|
apt-get update
|
|
apt-get -y -o Dpkg::Options::="--force-confdef" -o Dpkg::Options::="--force-confnew" dist-upgrade
|
|
|
|
need_pkg nodejs "$MONGODB" apt-transport-https haveged
|
|
need_pkg bigbluebutton
|
|
need_pkg bbb-html5
|
|
|
|
if [ -f /usr/share/bbb-web/WEB-INF/classes/bigbluebutton.properties ]; then
|
|
SERVLET_DIR=/usr/share/bbb-web
|
|
fi
|
|
|
|
while [ ! -f "$SERVLET_DIR/WEB-INF/classes/bigbluebutton.properties" ]; do sleep 1; echo -n '.'; done
|
|
|
|
check_cap_sys_nice
|
|
check_nat
|
|
check_LimitNOFILE
|
|
|
|
configure_HTML5
|
|
|
|
if [ -n "$LINK_PATH" ]; then
|
|
ln -s "$LINK_PATH" "/var/bigbluebutton"
|
|
fi
|
|
|
|
if [ -n "$PROVIDED_CERTIFICATE" ] ; then
|
|
install_ssl
|
|
elif [ -n "$HOST" ] && [ -n "$EMAIL" ] ; then
|
|
install_ssl
|
|
fi
|
|
|
|
if [ -n "$COTURN" ]; then
|
|
configure_coturn
|
|
|
|
if systemctl is-active --quiet haproxy.service; then
|
|
systemctl disable --now haproxy.service
|
|
fi
|
|
else
|
|
install_coturn
|
|
install_haproxy
|
|
systemctl enable --now haproxy.service # In case we had previously disabled (see above)
|
|
|
|
# The turn server will always try to connect to the BBB server's public IP address,
|
|
# so if NAT is in use, add an iptables rule to adjust the destination IP address
|
|
# of UDP packets sent from the turn server to FreeSWITCH.
|
|
if [ -n "$INTERNAL_IP" ]; then
|
|
need_pkg iptables-persistent
|
|
iptables -t nat -A OUTPUT -p udp -s "$INTERNAL_IP" -d "$IP" -j DNAT --to-destination "$INTERNAL_IP"
|
|
netfilter-persistent save
|
|
fi
|
|
fi
|
|
|
|
apt-get auto-remove -y
|
|
|
|
systemctl restart systemd-journald
|
|
|
|
if [ -n "$UFW" ]; then
|
|
setup_ufw
|
|
fi
|
|
|
|
if [ -n "$HOST" ]; then
|
|
bbb-conf --setip "$HOST"
|
|
else
|
|
bbb-conf --setip "$IP"
|
|
fi
|
|
|
|
if ! systemctl show-environment | grep LANG= | grep -q UTF-8; then
|
|
sudo systemctl set-environment LANG=C.UTF-8
|
|
fi
|
|
|
|
# BBB ecosystem apps:
|
|
if [[ ${#LTI_CREDS[*]} -eq 2 ]]; then
|
|
install_lti
|
|
fi
|
|
|
|
if [ -n "$GREENLIGHT" ]; then
|
|
install_greenlight_v3
|
|
fi
|
|
|
|
bbb-conf --check
|
|
}
|
|
|
|
say() {
|
|
echo "bbb-install: $1"
|
|
}
|
|
|
|
err() {
|
|
say "$1" >&2
|
|
exit 1
|
|
}
|
|
|
|
usage_err() {
|
|
say "$1" >&2
|
|
usage
|
|
exit 1
|
|
}
|
|
|
|
check_root() {
|
|
if [ $EUID != 0 ]; then err "You must run this command as root."; fi
|
|
}
|
|
|
|
check_mem() {
|
|
if awk '$1~/MemTotal/ {exit !($2<3940000)}' /proc/meminfo; then
|
|
echo "Your server needs to have (at least) 4G of memory."
|
|
if [ "$SKIP_MIN_SERVER_REQUIREMENTS_CHECK" != true ]; then
|
|
exit 1
|
|
fi
|
|
fi
|
|
}
|
|
|
|
check_ipv6() {
|
|
if [ ! -f /proc/net/if_inet6 ]; then
|
|
echo "Your server does not support IPV6"
|
|
if [ "$SKIP_MIN_SERVER_REQUIREMENTS_CHECK" != true ]; then
|
|
exit 1
|
|
fi
|
|
fi
|
|
}
|
|
|
|
check_cpus() {
|
|
if [ "$(nproc --all)" -lt 4 ]; then
|
|
echo "Your server needs to have (at least) 4 CPUs (8 recommended for production)."
|
|
if [ "$SKIP_MIN_SERVER_REQUIREMENTS_CHECK" != true ]; then
|
|
exit 1
|
|
fi
|
|
fi
|
|
}
|
|
|
|
check_ubuntu(){
|
|
RELEASE=$(lsb_release -r | sed 's/^[^0-9]*//g')
|
|
if [ "$RELEASE" != "$1" ]; then err "You must run this command on Ubuntu $1 server."; fi
|
|
}
|
|
|
|
need_x64() {
|
|
UNAME=$(uname -m)
|
|
if [ "$UNAME" != "x86_64" ]; then err "You must run this command on a 64-bit server."; fi
|
|
}
|
|
|
|
wait_443() {
|
|
echo "Waiting for port 443 to clear "
|
|
# ss fields 4 and 6 are Local Address and State
|
|
while ss -ant | awk '{print $4, $6}' | grep TIME_WAIT | grep -q ":443"; do sleep 1; echo -n '.'; done
|
|
echo
|
|
}
|
|
|
|
get_IP() {
|
|
if [ -n "$IP" ]; then return 0; fi
|
|
|
|
# Determine local IP
|
|
if [ -e "/sys/class/net/venet0:0" ]; then
|
|
# IP detection for OpenVZ environment
|
|
_dev="venet0:0"
|
|
else
|
|
_dev=$(awk '$2 == 00000000 { print $1 }' /proc/net/route | head -1)
|
|
fi
|
|
_ips=$(LANG=C ip -4 -br address show dev "$_dev" | awk '{ $1=$2=""; print $0 }')
|
|
_ips=${_ips/127.0.0.1\/8/}
|
|
read -r IP _ <<< "$_ips"
|
|
IP=${IP/\/*} # strip subnet provided by ip address
|
|
if [ -z "$IP" ]; then
|
|
read -r IP _ <<< "$(hostname -I)"
|
|
fi
|
|
|
|
|
|
local external_ip
|
|
# Determine external IP
|
|
if grep -sqi ^ec2 /sys/devices/virtual/dmi/id/product_uuid; then
|
|
# EC2
|
|
external_ip=$(wget -qO- http://169.254.169.254/latest/meta-data/public-ipv4)
|
|
elif [ -f /var/lib/dhcp/dhclient.eth0.leases ] && grep -q unknown-245 /var/lib/dhcp/dhclient.eth0.leases; then
|
|
# Azure
|
|
external_ip=$(curl -H Metadata:true "http://169.254.169.254/metadata/instance/network/interface/0/ipv4/ipAddress/0/publicIpAddress?api-version=2017-08-01&format=text")
|
|
elif [ -f /run/scw-metadata.cache ]; then
|
|
# Scaleway
|
|
external_ip=$(grep "PUBLIC_IP_ADDRESS" /run/scw-metadata.cache | cut -d '=' -f 2)
|
|
elif which dmidecode > /dev/null && dmidecode -s bios-vendor | grep -q Google; then
|
|
# Google Compute Cloud
|
|
external_ip=$(wget -O - -q "http://metadata/computeMetadata/v1/instance/network-interfaces/0/access-configs/0/external-ip" --header 'Metadata-Flavor: Google')
|
|
elif [ -n "$1" ]; then
|
|
# Try and determine the external IP from the given hostname
|
|
need_pkg dnsutils
|
|
external_ip=$(dig +short "$1" @resolver1.opendns.com | grep '^[.0-9]*$' | tail -n1)
|
|
fi
|
|
|
|
# Check if the external IP reaches the internal IP
|
|
if [ -n "$external_ip" ] && [ "$IP" != "$external_ip" ]; then
|
|
if which nginx; then
|
|
systemctl stop nginx
|
|
fi
|
|
|
|
need_pkg netcat-openbsd
|
|
|
|
wait_443
|
|
|
|
nc -l -p 443 > /dev/null 2>&1 &
|
|
nc_PID=$!
|
|
sleep 1
|
|
|
|
# Check if we can reach the server through it's external IP address
|
|
if nc -zvw3 "$external_ip" 443 > /dev/null 2>&1; then
|
|
INTERNAL_IP=$IP
|
|
IP=$external_ip
|
|
echo
|
|
echo " Detected this server has an internal/external IP address."
|
|
echo
|
|
echo " INTERNAL_IP: $INTERNAL_IP"
|
|
echo " (external) IP: $IP"
|
|
echo
|
|
fi
|
|
|
|
kill $nc_PID > /dev/null 2>&1;
|
|
|
|
if which nginx; then
|
|
systemctl start nginx
|
|
fi
|
|
fi
|
|
|
|
if [ -z "$IP" ]; then err "Unable to determine local IP address."; fi
|
|
}
|
|
|
|
need_pkg() {
|
|
check_root
|
|
while fuser /var/lib/dpkg/lock >/dev/null 2>&1; do echo "Sleeping for 1 second because of dpkg lock"; sleep 1; done
|
|
|
|
if [ ! "$SOURCES_FETCHED" = true ]; then
|
|
apt-get update
|
|
SOURCES_FETCHED=true
|
|
fi
|
|
|
|
if ! dpkg -s "${@}" >/dev/null 2>&1; then
|
|
LC_CTYPE=C.UTF-8 apt-get install -yq "${@}"
|
|
fi
|
|
while fuser /var/lib/dpkg/lock >/dev/null 2>&1; do echo "Sleeping for 1 second because of dpkg lock"; sleep 1; done
|
|
}
|
|
|
|
need_ppa() {
|
|
need_pkg software-properties-common
|
|
if [ ! -f "/etc/apt/sources.list.d/$1" ]; then
|
|
LC_CTYPE=C.UTF-8 add-apt-repository -y "$2"
|
|
fi
|
|
if ! apt-key list "$3" | grep -q -E "1024|4096"; then # Let's try it a second time
|
|
LC_CTYPE=C.UTF-8 add-apt-repository "$2" -y
|
|
if ! apt-key list "$3" | grep -q -E "1024|4096"; then
|
|
err "Unable to setup PPA for $2"
|
|
fi
|
|
fi
|
|
}
|
|
|
|
check_version() {
|
|
if ! echo "$1" | grep -Eq "focal-27"; then err "This script can only install BigBlueButton 2.7 and is meant to be run on Ubuntu 20.04 (focal) server."; fi
|
|
DISTRO=${1%%-*}
|
|
if ! wget -qS --spider "https://$PACKAGE_REPOSITORY/$1/dists/bigbluebutton-$DISTRO/Release.gpg" > /dev/null 2>&1; then
|
|
err "Unable to locate packages for $1 at $PACKAGE_REPOSITORY."
|
|
fi
|
|
check_root
|
|
need_pkg apt-transport-https
|
|
if ! apt-key list | grep -q "BigBlueButton apt-get"; then
|
|
wget "https://$PACKAGE_REPOSITORY/repo/bigbluebutton.asc" -O- | apt-key add -
|
|
fi
|
|
|
|
echo "deb https://$PACKAGE_REPOSITORY/$VERSION bigbluebutton-$DISTRO main" > /etc/apt/sources.list.d/bigbluebutton.list
|
|
}
|
|
|
|
check_host() {
|
|
if [ -z "$PROVIDED_CERTIFICATE" ] && [ -z "$HOST" ]; then
|
|
need_pkg dnsutils apt-transport-https
|
|
DIG_IP=$(dig +short "$1" | grep '^[.0-9]*$' | tail -n1)
|
|
if [ -z "$DIG_IP" ]; then err "Unable to resolve $1 to an IP address using DNS lookup."; fi
|
|
get_IP "$1"
|
|
if [ "$DIG_IP" != "$IP" ]; then err "DNS lookup for $1 resolved to $DIG_IP but didn't match local $IP."; fi
|
|
fi
|
|
}
|
|
|
|
check_coturn() {
|
|
if ! echo "$1" | grep -q ':'; then err "Option for coturn must be <hostname>:<secret>"; fi
|
|
|
|
COTURN_HOST=$(echo "$OPTARG" | cut -d':' -f1)
|
|
COTURN_SECRET=$(echo "$OPTARG" | cut -d':' -f2)
|
|
|
|
if [ -z "$COTURN_HOST" ]; then err "-c option must contain <hostname>"; fi
|
|
if [ -z "$COTURN_SECRET" ]; then err "-c option must contain <secret>"; fi
|
|
|
|
if [ "$COTURN_HOST" == "turn.example.com" ]; then
|
|
err "You must specify a valid hostname (not the example given in the docs)"
|
|
fi
|
|
if [ "$COTURN_SECRET" == "1234abcd" ]; then
|
|
err "You must specify a new password (not the example given in the docs)."
|
|
fi
|
|
|
|
check_host "$COTURN_HOST"
|
|
}
|
|
|
|
check_apache2() {
|
|
if dpkg -l | grep -q apache2-bin; then
|
|
echo "You must uninstall the Apache2 server first"
|
|
if [ "$SKIP_APACHE_INSTALLED_CHECK" != true ]; then
|
|
exit 1
|
|
fi
|
|
fi
|
|
}
|
|
|
|
# If CAP_SYS_NICE is not available, then the FreeSWITCH systemctl service
|
|
# will fail to start, with an error message like "status=214/SETSCHEDULER".
|
|
# In this case we need to modify this service so that it does not require a realtime scheduler.
|
|
# A similar modification needs to be done to a couple of other services as well,
|
|
# like: bbb-html5-frontend@.service, bbb-html5-backend@.service and bbb-webrtc-sfu.service
|
|
check_cap_sys_nice() {
|
|
# if we don't detect a SETSCHEDULER error message in the status of the service,
|
|
# then there is nothing to be modified/customized
|
|
{ systemctl status freeswitch | grep -q SETSCHEDULER; } || return
|
|
|
|
# override /lib/systemd/system/freeswitch.service so that it does not use realtime scheduler
|
|
mkdir -p /etc/systemd/system/freeswitch.service.d
|
|
cat <<HERE > /etc/systemd/system/freeswitch.service.d/override.conf
|
|
[Service]
|
|
IOSchedulingClass=
|
|
IOSchedulingPriority=
|
|
CPUSchedulingPolicy=
|
|
CPUSchedulingPriority=
|
|
HERE
|
|
|
|
# override /usr/lib/systemd/system/bbb-html5-frontend@.service
|
|
mkdir -p /etc/systemd/system/bbb-html5-frontend@.service.d
|
|
cat <<HERE > /etc/systemd/system/bbb-html5-frontend@.service.d/override.conf
|
|
[Service]
|
|
CPUSchedulingPolicy=
|
|
HERE
|
|
|
|
# override /usr/lib/systemd/system/bbb-html5-backend@.service
|
|
mkdir -p /etc/systemd/system/bbb-html5-backend@.service.d
|
|
cat <<HERE > /etc/systemd/system/bbb-html5-backend@.service.d/override.conf
|
|
[Service]
|
|
CPUSchedulingPolicy=
|
|
HERE
|
|
|
|
# override /usr/lib/systemd/system/bbb-webrtc-sfu.service
|
|
mkdir -p /etc/systemd/system/bbb-webrtc-sfu.service.d
|
|
cat <<HERE > /etc/systemd/system/bbb-webrtc-sfu.service.d/override.conf
|
|
[Service]
|
|
CPUSchedulingPolicy=
|
|
HERE
|
|
|
|
systemctl daemon-reload
|
|
}
|
|
|
|
# Check if running externally with internal/external IP addresses
|
|
check_nat() {
|
|
xmlstarlet edit --inplace --update '//X-PRE-PROCESS[@cmd="set" and starts-with(@data, "external_rtp_ip=")]/@data' --value "external_rtp_ip=$IP" /opt/freeswitch/conf/vars.xml
|
|
xmlstarlet edit --inplace --update '//X-PRE-PROCESS[@cmd="set" and starts-with(@data, "external_sip_ip=")]/@data' --value "external_sip_ip=$IP" /opt/freeswitch/conf/vars.xml
|
|
|
|
if [ -n "$INTERNAL_IP" ]; then
|
|
xmlstarlet edit --inplace --update '//param[@name="ext-rtp-ip"]/@value' --value "\$\${external_rtp_ip}" /opt/freeswitch/conf/sip_profiles/external.xml
|
|
xmlstarlet edit --inplace --update '//param[@name="ext-sip-ip"]/@value' --value "\$\${external_sip_ip}" /opt/freeswitch/conf/sip_profiles/external.xml
|
|
|
|
sed -i "s/$INTERNAL_IP:/$IP:/g" /usr/share/bigbluebutton/nginx/sip.nginx
|
|
ip addr add "$IP" dev lo
|
|
|
|
# If dummy NIC is not in dummy-nic.service (or the file does not exist), update/create it
|
|
if ! grep -q "$IP" /lib/systemd/system/dummy-nic.service > /dev/null 2>&1; then
|
|
if [ -f /lib/systemd/system/dummy-nic.service ]; then
|
|
DAEMON_RELOAD=true;
|
|
fi
|
|
|
|
cat > /lib/systemd/system/dummy-nic.service << HERE
|
|
[Unit]
|
|
Description=Configure dummy NIC for FreeSWITCH
|
|
Before=freeswitch.service
|
|
After=network.target
|
|
|
|
[Service]
|
|
ExecStart=/sbin/ip addr add $IP dev lo
|
|
|
|
[Install]
|
|
WantedBy=multi-user.target
|
|
HERE
|
|
|
|
if [ "$DAEMON_RELOAD" == "true" ]; then
|
|
systemctl daemon-reload
|
|
systemctl restart dummy-nic
|
|
else
|
|
systemctl enable dummy-nic
|
|
systemctl start dummy-nic
|
|
fi
|
|
fi
|
|
fi
|
|
}
|
|
|
|
check_LimitNOFILE() {
|
|
CPU=$(nproc --all)
|
|
|
|
if [ "$CPU" -ge 8 ]; then
|
|
if [ -f /lib/systemd/system/bbb-web.service ]; then
|
|
# Let's create an override file to increase the number of LimitNOFILE
|
|
mkdir -p /etc/systemd/system/bbb-web.service.d/
|
|
cat > /etc/systemd/system/bbb-web.service.d/override.conf << HERE
|
|
[Service]
|
|
LimitNOFILE=8192
|
|
HERE
|
|
systemctl daemon-reload
|
|
fi
|
|
fi
|
|
}
|
|
|
|
configure_HTML5() {
|
|
# Use Google's default STUN server
|
|
if [ -n "$INTERNAL_IP" ]; then
|
|
sed -i "s/[;]*externalIPv4=.*/externalIPv4=$IP/g" /etc/kurento/modules/kurento/WebRtcEndpoint.conf.ini
|
|
sed -i "s/[;]*iceTcp=.*/iceTcp=0/g" /etc/kurento/modules/kurento/WebRtcEndpoint.conf.ini
|
|
fi
|
|
}
|
|
|
|
install_haproxy() {
|
|
need_pkg haproxy
|
|
if [ -n "$INTERNAL_IP" ]; then
|
|
TURN_IP="$INTERNAL_IP"
|
|
else
|
|
TURN_IP="$IP"
|
|
fi
|
|
HAPROXY_CFG=/etc/haproxy/haproxy.cfg
|
|
cat > "$HAPROXY_CFG" <<END
|
|
global
|
|
log /dev/log local0
|
|
log /dev/log local1 notice
|
|
chroot /var/lib/haproxy
|
|
stats socket /run/haproxy/admin.sock mode 660 level admin expose-fd listeners
|
|
stats timeout 30s
|
|
user haproxy
|
|
group haproxy
|
|
daemon
|
|
|
|
# Default SSL material locations
|
|
ca-base /etc/ssl/certs
|
|
crt-base /etc/ssl/private
|
|
|
|
# Default ciphers to use on SSL-enabled listening sockets.
|
|
# For more information, see ciphers(1SSL). This list is from:
|
|
# https://hynek.me/articles/hardening-your-web-servers-ssl-ciphers/
|
|
# An alternative list with additional directives can be obtained from
|
|
# https://mozilla.github.io/server-side-tls/ssl-config-generator/?server=haproxy
|
|
ssl-default-bind-ciphers ECDH+AESGCM:DH+AESGCM:ECDH+AES256:DH+AES256:ECDH+AES128:DH+AES:RSA+AESGCM:RSA+AES:!aNULL:!MD5:!DSS
|
|
ssl-default-bind-options ssl-min-ver TLSv1.2
|
|
tune.ssl.default-dh-param 2048
|
|
|
|
defaults
|
|
log global
|
|
mode http
|
|
option httplog
|
|
option dontlognull
|
|
timeout connect 5000
|
|
timeout client 50000
|
|
timeout server 50000
|
|
errorfile 400 /etc/haproxy/errors/400.http
|
|
errorfile 403 /etc/haproxy/errors/403.http
|
|
errorfile 408 /etc/haproxy/errors/408.http
|
|
errorfile 500 /etc/haproxy/errors/500.http
|
|
errorfile 502 /etc/haproxy/errors/502.http
|
|
errorfile 503 /etc/haproxy/errors/503.http
|
|
errorfile 504 /etc/haproxy/errors/504.http
|
|
|
|
|
|
frontend nginx_or_turn
|
|
bind *:443,:::443 ssl crt /etc/haproxy/certbundle.pem ssl-min-ver TLSv1.2 alpn h2,http/1.1,stun.turn
|
|
mode tcp
|
|
option tcplog
|
|
tcp-request content capture req.payload(0,1) len 1
|
|
log-format "%ci:%cp [%t] %ft %b/%s %Tw/%Tc/%Tt %B %ts %ac/%fc/%bc/%sc/%rc %sq/%bq captured_user:%{+X}[capture.req.hdr(0)]"
|
|
tcp-request inspect-delay 30s
|
|
# We terminate SSL on haproxy. HTTP2 is a binary protocol. haproxy has to
|
|
# decide which protocol is spoken. This is negotiated by ALPN.
|
|
#
|
|
# Depending on the ALPN value traffic is redirected to either port 82 (HTTP2,
|
|
# ALPN value h2) or 81 (HTTP 1.0 or HTTP 1.1, ALPN value http/1.1 or no value)
|
|
# If no ALPN value is set, the first byte is inspected and depending on the
|
|
# value traffic is sent to either port 81 or coturn.
|
|
use_backend nginx-http2 if { ssl_fc_alpn h2 }
|
|
use_backend nginx if { ssl_fc_alpn http/1.1 }
|
|
use_backend turn if { ssl_fc_alpn stun.turn }
|
|
use_backend %[capture.req.hdr(0),map_str(/etc/haproxy/protocolmap,turn)]
|
|
default_backend turn
|
|
|
|
backend turn
|
|
mode tcp
|
|
server localhost $TURN_IP:3478
|
|
|
|
backend nginx
|
|
mode tcp
|
|
server localhost 127.0.0.1:81 send-proxy check
|
|
|
|
backend nginx-http2
|
|
mode tcp
|
|
server localhost 127.0.0.1:82 send-proxy check
|
|
END
|
|
chown root:haproxy "$HAPROXY_CFG"
|
|
chmod 640 "$HAPROXY_CFG"
|
|
for l in {a..z} {A..Z}; do echo "$l" nginx ; done > /etc/haproxy/protocolmap
|
|
chmod 0644 /etc/haproxy/protocolmap
|
|
|
|
# cert renewal
|
|
mkdir -p /etc/letsencrypt/renewal-hooks/deploy
|
|
cat > /etc/letsencrypt/renewal-hooks/deploy/haproxy <<HERE
|
|
#!/bin/bash -e
|
|
|
|
touch /etc/haproxy/certbundle.pem.new
|
|
chmod 0640 /etc/haproxy/certbundle.pem.new
|
|
|
|
{ cat /etc/letsencrypt/live/$HOST/fullchain.pem; echo; cat /etc/letsencrypt/live/$HOST/privkey.pem; } > /etc/haproxy/certbundle.pem.new
|
|
chown root:haproxy /etc/haproxy/certbundle.pem.new
|
|
mv /etc/haproxy/certbundle.pem.new /etc/haproxy/certbundle.pem
|
|
systemctl reload haproxy
|
|
HERE
|
|
chmod 0755 /etc/letsencrypt/renewal-hooks/deploy/haproxy
|
|
/etc/letsencrypt/renewal-hooks/deploy/haproxy
|
|
}
|
|
|
|
# This function will install the latest official version of greenlight-v3 and set it as the hosting Bigbluebutton default frontend or update greenlight-v3 if installed.
|
|
# Greenlight is a simple to use Bigbluebutton room manager that offers a set of features useful to online workloads especially virtual schooling.
|
|
# https://docs.bigbluebutton.org/greenlight/gl-overview.html
|
|
install_greenlight_v3(){
|
|
# This function depends on the following files existing on their expected location so an eager check is done asserting that.
|
|
if [[ -z $SERVLET_DIR || ! -f $SERVLET_DIR/WEB-INF/classes/bigbluebutton.properties || ! -f $CR_TMPFILE || ! -f $BBB_WEB_ETC_CONFIG ]]; then
|
|
err "greenlight-v3 failed to install/update due to unmet requirements, have you followed the recommended steps to install Bigbluebutton?"
|
|
fi
|
|
|
|
check_root
|
|
install_docker
|
|
|
|
# Preparing and checking the enviroment.
|
|
say "preparing and checking the enviroment to install/update greelight-v3..."
|
|
|
|
if [ ! -d $GL3_DIR ]; then
|
|
mkdir -p $GL3_DIR && say "created $GL3_DIR"
|
|
fi
|
|
|
|
local GL_IMG_REPO=bigbluebutton/greenlight:v3
|
|
|
|
say "pulling latest $GL_IMG_REPO image..."
|
|
docker pull $GL_IMG_REPO
|
|
|
|
if [ ! -s $GL3_DIR/docker-compose.yml ]; then
|
|
docker run --rm --entrypoint sh $GL_IMG_REPO -c 'cat docker-compose.yml' > $GL3_DIR/docker-compose.yml
|
|
|
|
if [ ! -s $GL3_DIR/docker-compose.yml ]; then
|
|
err "failed to create docker compose file - is docker running?"
|
|
fi
|
|
|
|
say "greenlight-v3 docker compose file was created"
|
|
fi
|
|
|
|
# Configuring Greenlight v3.
|
|
say "checking the configuration of greenlight-v3..."
|
|
|
|
local ROOT_URL BIGBLUEBUTTON_URL BIGBLUEBUTTON_SECRET
|
|
ROOT_URL=$(cat "$SERVLET_DIR/WEB-INF/classes/bigbluebutton.properties" "$CR_TMPFILE" "$BBB_WEB_ETC_CONFIG" | grep -v '#' | sed -n '/^bigbluebutton.web.serverURL/{s/.*=//;p}' | tail -n 1 )
|
|
BIGBLUEBUTTON_URL=$ROOT_URL/bigbluebutton/
|
|
BIGBLUEBUTTON_SECRET=$(cat "$SERVLET_DIR/WEB-INF/classes/bigbluebutton.properties" "$CR_TMPFILE" "$BBB_WEB_ETC_CONFIG" | grep -v '#' | grep ^securitySalt | tail -n 1 | cut -d= -f2)
|
|
|
|
# Configuring Greenlight v3 docker-compose.yml (if configured no side effect will happen).
|
|
sed -i "s|^\([ \t-]*POSTGRES_PASSWORD\)\(=[ \t]*\)$|\1=$(openssl rand -hex 24)|g" $GL3_DIR/docker-compose.yml # Do not overwrite the value if not empty.
|
|
|
|
local PGUSER=postgres # Postgres db user to be used by greenlight-v3.
|
|
local PGTXADDR=postgres:5432 # Postgres DB transport address (pair of (@ip:@port)).
|
|
local RSTXADDR=redis:6379 # Redis DB transport address (pair of (@ip:@port)).
|
|
local PGPASSWORD
|
|
PGPASSWORD=$(sed -ne "s/^\([ \t-]*POSTGRES_PASSWORD=\)\(.*\)$/\2/p" $GL3_DIR/docker-compose.yml) # Extract generated Postgres password.
|
|
|
|
if [ -z "$PGPASSWORD" ]; then
|
|
err "failed to retrieve greenlight-v3 DB password - retry to resolve."
|
|
fi
|
|
|
|
local DATABASE_URL_ROOT="postgres://$PGUSER:$PGPASSWORD@$PGTXADDR"
|
|
local REDIS_URL_ROOT="redis://$RSTXADDR"
|
|
|
|
local PGDBNAME=greenlight-v3-production
|
|
local SECRET_KEY_BASE
|
|
SECRET_KEY_BASE=$(docker run --rm --entrypoint bundle $GL_IMG_REPO exec rails secret)
|
|
|
|
if [ -z "$SECRET_KEY_BASE" ]; then
|
|
err "failed to generate greenlight-v3 secret key base - is docker running?"
|
|
fi
|
|
|
|
if [ ! -s $GL3_DIR/.env ]; then
|
|
docker run --rm --entrypoint sh $GL_IMG_REPO -c 'cat sample.env' > $GL3_DIR/.env
|
|
|
|
if [ ! -s $GL3_DIR/.env ]; then
|
|
err "failed to create greenlight-v3 .env file - is docker running?"
|
|
fi
|
|
|
|
say "greenlight-v3 .env file was created"
|
|
fi
|
|
|
|
# A note for future maintainers:
|
|
# The following configuration operations were made idempotent, meaning that playing these actions will have an outcome on the system (configure it) only once.
|
|
# Replaying these steps are a safe and an expected operation, this gurantees the seemless simple installation and upgrade of Greenlight v3.
|
|
# A simple change can impact that property and therefore render the upgrading functionnality unoperationnal or impact the running system.
|
|
|
|
# Configuring Greenlight v3 .env file (if already configured this will only update the BBB endpoint and secret).
|
|
cp -v $GL3_DIR/.env $GL3_DIR/.env.old && say "old .env file can be retrieved at $GL3_DIR/.env.old" #Backup
|
|
|
|
sed -i "s|^[# \t]*BIGBLUEBUTTON_ENDPOINT=.*|BIGBLUEBUTTON_ENDPOINT=$BIGBLUEBUTTON_URL|" $GL3_DIR/.env
|
|
sed -i "s|^[# \t]*BIGBLUEBUTTON_SECRET=.*|BIGBLUEBUTTON_SECRET=$BIGBLUEBUTTON_SECRET|" $GL3_DIR/.env
|
|
sed -i "s|^[# \t]*SECRET_KEY_BASE=[ \t]*$|SECRET_KEY_BASE=$SECRET_KEY_BASE|" $GL3_DIR/.env # Do not overwrite the value if not empty.
|
|
sed -i "s|^[# \t]*DATABASE_URL=[ \t]*$|DATABASE_URL=$DATABASE_URL_ROOT/$PGDBNAME|" $GL3_DIR/.env # Do not overwrite the value if not empty.
|
|
sed -i "s|^[# \t]*REDIS_URL=[ \t]*$|REDIS_URL=$REDIS_URL_ROOT/|" $GL3_DIR/.env # Do not overwrite the value if not empty.
|
|
|
|
# Placing greenlight-v3 nginx file, this will enable greenlight-v3 as your Bigbluebutton frontend (bbb-fe).
|
|
cp -v $NGINX_FILES_DEST/greenlight-v3.nginx $NGINX_FILES_DEST/greenlight-v3.nginx.old && say "old greenlight-v3 nginx config can be retrieved at $NGINX_FILES_DEST/greenlight-v3.nginx.old" #Backup
|
|
docker run --rm --entrypoint sh $GL_IMG_REPO -c 'cat greenlight-v3.nginx' > $NGINX_FILES_DEST/greenlight-v3.nginx && say "added greenlight-v3 nginx file"
|
|
|
|
# For backward compatibility with deployments running greenlight-v2 and haven't picked the patch from COMMIT (583f868).
|
|
# Move any nginx files from greenlight-v2 to the expected location.
|
|
if [ -s /etc/bigbluebutton/nginx/greenlight.nginx ]; then
|
|
mv /etc/bigbluebutton/nginx/greenlight.nginx $NGINX_FILES_DEST/greenlight.nginx && say "found /etc/bigbluebutton/nginx/greenlight.nginx and moved to expected location."
|
|
fi
|
|
|
|
if [ -s /etc/bigbluebutton/nginx/greenlight-redirect.nginx ]; then
|
|
mv /etc/bigbluebutton/nginx/greenlight-redirect.nginx $NGINX_FILES_DEST/greenlight-redirect.nginx && say "found /etc/bigbluebutton/nginx/greenlight-redirect.nginx and moved to expected location."
|
|
fi
|
|
|
|
if [ -z "$COTURN" ]; then
|
|
# When NGINX is the frontend reverse proxy, 'X-Forwarded-Proto' proxy header will dynamically match the $scheme of the received client request.
|
|
# In case a builtin turn server is installed, then HAPROXY is introduced and it becomes the frontend reverse proxy.
|
|
# NGINX will then act as a backend reverse proxy residing behind of it.
|
|
# HTTPS traffic from the client then is terminated at HAPROXY and plain HTTP traffic is proxied to NGINX.
|
|
# Therefore the 'X-Forwarded-Proto' proxy header needs to correctly indicate that HTTPS traffic was proxied in such scenario.
|
|
# shellcheck disable=SC2016
|
|
sed -i '/X-Forwarded-Proto/s/$scheme/"https"/' $NGINX_FILES_DEST/greenlight-v3.nginx
|
|
|
|
if [ -s $NGINX_FILES_DEST/greenlight.nginx ]; then
|
|
# For backward compatibility with deployments running greenlight-v2 and haven't picked the patch from PR (#579).
|
|
# shellcheck disable=SC2016
|
|
sed -i '/X-Forwarded-Proto/s/$scheme/"https"/' $NGINX_FILES_DEST/greenlight.nginx
|
|
fi
|
|
fi
|
|
|
|
# For backward compatibility, any already installed greenlight-v2 application will remain but it will not be the default frontend for BigBluebutton.
|
|
# To access greelight-v2 an explicit /b relative root needs to be indicated, otherwise greelight-v3 will be served by default.
|
|
|
|
# Disabling the greenlight-v2 redirection rule.
|
|
disable_nginx_site greenlight-redirect.nginx && say "found greelight-v2 redirection rule and disabled it!"
|
|
|
|
# Disabling the Bigbluebutton default Welcome page frontend.
|
|
disable_nginx_site default-fe.nginx && say "found default bbb-fe 'Welcome' and disabled it!"
|
|
|
|
# Adding Keycloak
|
|
if [ -n "$INSTALL_KC" ]; then
|
|
# When attepmting to install/update Keycloak let us attempt to create the database to resolve any issues caused by postgres false negatives.
|
|
docker-compose -f $GL3_DIR/docker-compose.yml up -d postgres && say "started postgres"
|
|
wait_postgres_start
|
|
docker-compose -f $GL3_DIR/docker-compose.yml exec -T postgres psql -U postgres -c 'CREATE DATABASE keycloakdb;'
|
|
fi
|
|
|
|
if ! grep -q 'keycloak:' $GL3_DIR/docker-compose.yml; then
|
|
# The following logic is expected to run only once when adding Keycloak.
|
|
# Keycloak isn't installed
|
|
if [ -n "$INSTALL_KC" ]; then
|
|
# Add Keycloak
|
|
say "Adding Keycloak..."
|
|
|
|
docker-compose -f $GL3_DIR/docker-compose.yml down
|
|
cp -v $GL3_DIR/docker-compose.yml $GL3_DIR/docker-compose.base.yml # Persist working base compose file for admins as a Backup.
|
|
|
|
docker run --rm --entrypoint sh $GL_IMG_REPO -c 'cat docker-compose.kc.yml' >> $GL3_DIR/docker-compose.yml
|
|
|
|
if ! grep -q 'keycloak:' $GL3_DIR/docker-compose.yml; then
|
|
err "failed to add Keycloak service to greenlight-v3 compose file - is docker running?"
|
|
fi
|
|
say "added Keycloak to compose file"
|
|
|
|
KCPASSWORD=$(openssl rand -hex 12) # Keycloak admin password.
|
|
sed -i "s|^\([ \t-]*KEYCLOAK_ADMIN_PASSWORD\)\(=[ \t]*\)$|\1=$KCPASSWORD|g" $GL3_DIR/docker-compose.yml # Do not overwrite the value if not empty.
|
|
sed -i "s|^\([ \t-]*KC_DB_PASSWORD\)\(=[ \t]*\)$|\1=$PGPASSWORD|g" $GL3_DIR/docker-compose.yml # Do not overwrite the value if not empty.
|
|
|
|
# Updating Keycloak nginx file.
|
|
cp -v $NGINX_FILES_DEST/keycloak.nginx $NGINX_FILES_DEST/keycloak.nginx.old && say "old Keycloak nginx config can be retrieved at $NGINX_FILES_DEST/keycloak.nginx.old"
|
|
docker run --rm --entrypoint sh $GL_IMG_REPO -c 'cat keycloak.nginx' > $NGINX_FILES_DEST/keycloak.nginx && say "added Keycloak nginx file"
|
|
fi
|
|
|
|
else
|
|
# Update Keycloak nginx file only.
|
|
cp -v $NGINX_FILES_DEST/keycloak.nginx $NGINX_FILES_DEST/keycloak.nginx.old && say "old Keycloak nginx config can be retrieved at $NGINX_FILES_DEST/keycloak.nginx.old"
|
|
docker run --rm --entrypoint sh $GL_IMG_REPO -c 'cat keycloak.nginx' > $NGINX_FILES_DEST/keycloak.nginx && say "added Keycloak nginx file"
|
|
fi
|
|
|
|
if [ -z "$COTURN" ] && [ -s $NGINX_FILES_DEST/keycloak.nginx ]; then
|
|
# shellcheck disable=SC2016
|
|
sed -i '/X-Forwarded-Proto/s/$scheme/"https"/' $NGINX_FILES_DEST/keycloak.nginx
|
|
fi
|
|
|
|
# Update .env file catching new configurations:
|
|
if ! grep -q 'RELATIVE_URL_ROOT=' $GL3_DIR/.env; then
|
|
cat <<HERE >> $GL3_DIR/.env
|
|
#RELATIVE_URL_ROOT=/gl
|
|
|
|
HERE
|
|
fi
|
|
|
|
if [ -n "$GL_PATH" ]; then
|
|
sed -i "s|^[# \t]*RELATIVE_URL_ROOT=.*|RELATIVE_URL_ROOT=$GL_PATH|" $GL3_DIR/.env
|
|
fi
|
|
|
|
local GL_RELATIVE_URL_ROOT
|
|
GL_RELATIVE_URL_ROOT=$(sed -ne "s/^\([ \t]*RELATIVE_URL_ROOT=\)\(.*\)$/\2/p" $GL3_DIR/.env) # Extract relative URL root path.
|
|
say "Deploying Greenlight on the '${GL_RELATIVE_URL_ROOT:-$GL_DEFAULT_PATH}' path..."
|
|
|
|
if [ -n "$GL_RELATIVE_URL_ROOT" ] && [ "$GL_RELATIVE_URL_ROOT" != "$GL_DEFAULT_PATH" ]; then
|
|
sed -i "s|^\([ \t]*location\)[ \t]*\(.*/cable\)[ \t]*\({\)$|\1 $GL_RELATIVE_URL_ROOT/cable \3|" $NGINX_FILES_DEST/greenlight-v3.nginx
|
|
sed -i "s|^\([ \t]*location\)[ \t]*\(@bbb-fe\)[ \t]*\({\)$|\1 $GL_RELATIVE_URL_ROOT \3|" $NGINX_FILES_DEST/greenlight-v3.nginx
|
|
fi
|
|
|
|
nginx -qt || err 'greenlight-v3 failed to install/update due to nginx tests failing to pass - if using the official image then please contact the maintainers.'
|
|
nginx -qs reload && say 'greenlight-v3 was successfully configured'
|
|
|
|
# Eager pulling images.
|
|
say "pulling latest greenlight-v3 services images..."
|
|
docker-compose -f $GL3_DIR/docker-compose.yml pull
|
|
|
|
if check_container_running greenlight-v3; then
|
|
# Restarting Greenlight-v3 services after updates.
|
|
say "greenlight-v3 is updating..."
|
|
say "shutting down greenlight-v3..."
|
|
docker-compose -f $GL3_DIR/docker-compose.yml down
|
|
fi
|
|
|
|
say "starting greenlight-v3..."
|
|
docker-compose -f $GL3_DIR/docker-compose.yml up -d
|
|
sleep 5
|
|
say "greenlight-v3 is now installed and accessible on: https://$HOST${GL_RELATIVE_URL_ROOT:-$GL_DEFAULT_PATH}"
|
|
say "To create Greenlight administrator account, see: https://docs.bigbluebutton.org/greenlight/v3/install#creating-an-admin-account"
|
|
|
|
|
|
if grep -q 'keycloak:' $GL3_DIR/docker-compose.yml; then
|
|
say "Keycloak is installed, up to date and accessible for configuration on: https://$HOST/keycloak/"
|
|
if [ -n "$KCPASSWORD" ];then
|
|
say "Use the following credentials when accessing the admin console:"
|
|
say " admin"
|
|
say " $KCPASSWORD"
|
|
fi
|
|
|
|
say "To complete the configuration of Keycloak, see: https://docs.bigbluebutton.org/greenlight/v3/external-authentication#configuring-keycloak"
|
|
fi
|
|
|
|
return 0;
|
|
}
|
|
|
|
# This function will install and update to the latest official version of BigBlueButton LTI framework.
|
|
# BigBlueButton LTI tools framewrok provides a simple interface to integrate Bigbluebutton features into any LTI certified LMS.
|
|
install_lti(){
|
|
# This function depends on the following files existing on their expected location so an eager check is done asserting that.
|
|
if [[ -z $SERVLET_DIR || ! -f $SERVLET_DIR/WEB-INF/classes/bigbluebutton.properties || ! -f $CR_TMPFILE || ! -f $BBB_WEB_ETC_CONFIG ]]; then
|
|
err "BBB LTI framework failed to install/update due to unmet requirements, have you followed the recommended steps to install Bigbluebutton?"
|
|
fi
|
|
|
|
check_root
|
|
install_docker
|
|
|
|
# Preparing and checking the enviroment.
|
|
say "preparing and checking the enviroment to install/update BBB LTI framework..."
|
|
|
|
if [ ! -d $LTI_DIR ]; then
|
|
mkdir -p $LTI_DIR && say "created $LTI_DIR"
|
|
fi
|
|
|
|
BROKER_IMG_REPO=bigbluebutton/bbb-lti-broker
|
|
|
|
# Installing/Updating the LTI broker.
|
|
say "pulling latest $BROKER_IMG_REPO image..."
|
|
docker pull $BROKER_IMG_REPO
|
|
|
|
if [ ! -s $LTI_DIR/docker-compose.yml ]; then
|
|
docker run --rm --entrypoint sh $BROKER_IMG_REPO -c 'cat docker-compose.yml' > $LTI_DIR/docker-compose.yml
|
|
|
|
if [ ! -s $LTI_DIR/docker-compose.yml ]; then
|
|
err "failed to create docker compose file - is docker running?"
|
|
fi
|
|
|
|
say "LTI framework docker compose file was created"
|
|
fi
|
|
|
|
# Configuring BBB LTI.
|
|
say "prepping the configuration of BBB LTI framework..."
|
|
|
|
local ROOT_URL
|
|
ROOT_URL=$(cat "$SERVLET_DIR/WEB-INF/classes/bigbluebutton.properties" "$CR_TMPFILE" "$BBB_WEB_ETC_CONFIG" | grep -v '#' | sed -n '/^bigbluebutton.web.serverURL/{s/.*=//;p}' | tail -n 1 )
|
|
BIGBLUEBUTTON_URL=$ROOT_URL/bigbluebutton/
|
|
BIGBLUEBUTTON_SECRET=$(cat "$SERVLET_DIR/WEB-INF/classes/bigbluebutton.properties" "$CR_TMPFILE" "$BBB_WEB_ETC_CONFIG" | grep -v '#' | grep ^securitySalt | tail -n 1 | cut -d= -f2)
|
|
|
|
# Configuring BBB LTI docker-compose.yml (if configured no side effect will happen).
|
|
sed -i "s|^\([ \t-]*POSTGRES_PASSWORD\)\(=[ \t]*\)$|\1=$(openssl rand -hex 24)|g" $LTI_DIR/docker-compose.yml # Do not overwrite the value if not empty.
|
|
|
|
say "installing/updating BBB LTI framework Broker and applications..."
|
|
local PGUSER=postgres # Postgres db user to be used by bbb-lti.
|
|
local PGTXADDR=postgres:5432 # Postgres DB transport address (pair of (@ip:@port)).
|
|
local RSTXADDR=redis:6379 # Redis DB transport address (pair of (@ip:@port)).
|
|
local PGPASSWORD
|
|
PGPASSWORD=$(sed -ne "s/^\([ \t-]*POSTGRES_PASSWORD=\)\(.*\)$/\2/p" $LTI_DIR/docker-compose.yml) # Extract generated Postgres password.
|
|
|
|
if [ -z "$PGPASSWORD" ]; then
|
|
err "failed to retrieve the LTI framework DB password - retry to resolve."
|
|
fi
|
|
|
|
DATABASE_URL_ROOT="postgres://$PGUSER:$PGPASSWORD@$PGTXADDR" # Must be global - expected by install_lti_tool.
|
|
REDIS_URL_ROOT="redis://$RSTXADDR" # Must be global - expected by install_lti_tool.
|
|
BROKER_RELATIVE_URL_ROOT=lti # Must be global - expected by install_lti_tools, will be dynamic in the future.
|
|
APPS_RELATIVE_URL_ROOT=apps # Must be global - expected by install_lti_tools, will be dynamic in the future.
|
|
|
|
install_lti_tools || err "BBB LTI framework failed to install/update tools!"
|
|
|
|
# Updating BBB LTI framework images.
|
|
say "pulling latest BBB LTI framework services images..."
|
|
docker-compose -f $LTI_DIR/docker-compose.yml pull
|
|
|
|
if check_container_running broker; then
|
|
# Restarting BBB LTI framework services after updates.
|
|
say "BBB LTI framework is updating..."
|
|
say "shutting down BBB LTI framework services..."
|
|
docker-compose -f $LTI_DIR/docker-compose.yml down
|
|
fi
|
|
|
|
say "starting BBB LTI framework services..."
|
|
docker-compose -f $LTI_DIR/docker-compose.yml up -d
|
|
|
|
wait_lti_broker_start
|
|
|
|
local LTI_KEY=${LTI_CREDS[0]}
|
|
local LTI_SECRET=${LTI_CREDS[1]}
|
|
|
|
say "Setting/updating LTI credentials for LTI KEY: $LTI_KEY..."
|
|
|
|
if ! docker-compose -f $LTI_DIR/docker-compose.yml exec -T broker bundle exec rake db:keys:update["$LTI_KEY","$LTI_SECRET"] \
|
|
2> /dev/null 1>&2; then
|
|
docker-compose -f $LTI_DIR/docker-compose.yml exec -T broker bundle exec rake db:keys:add["$LTI_KEY","$LTI_SECRET"] \
|
|
2> /dev/null 1>&2 || err "failed to set LTI credentials $LTI_KEY:$LTI_SECRET."
|
|
|
|
say "New LTI credentials for LTI KEY: $LTI_KEY were added!"
|
|
else
|
|
say "LTI credentials for LTI KEY: $LTI_KEY were updated!"
|
|
fi
|
|
|
|
say "BBB LTI framework is installed, up to date and accessible on: https://$HOST/$BROKER_RELATIVE_URL_ROOT"
|
|
say "You can refer to your LMS documentation on how to add a LTI application."
|
|
say " The LTI launch links for all of the installed BBB LTI framework applications can be found in https://$HOST/$BROKER_RELATIVE_URL_ROOT."
|
|
|
|
return 0;
|
|
}
|
|
|
|
install_lti_tools() {
|
|
# BBB LTI FRAMEWORK COMPONENTS
|
|
if [[ -z $BROKER_IMG_REPO || -z $DATABASE_URL_ROOT || -z $REDIS_URL_ROOT || -z $BROKER_RELATIVE_URL_ROOT || -z $APPS_RELATIVE_URL_ROOT ]]; then
|
|
err "BBB LTI tools installation/update failed due to unmet requirements!"
|
|
fi
|
|
|
|
# BBB LTI BROKER setup ↓
|
|
say "installing/updating BBB LTI framework broker..."
|
|
LTI_APP_DIR=$LTI_DIR/broker APP_IMG_REPO=$BROKER_IMG_REPO LOG_NAME='LTI Broker' RELATIVE_URL_ROOT=$BROKER_RELATIVE_URL_ROOT \
|
|
NGINX_NAME=bbb-lti-broker PGDBNAME=bbb_lti_broker install_lti_tool || return 1
|
|
|
|
say "BBB LTI Broker is installed, configured and up to date!"
|
|
# BBB LTI TOOLS setup ↓
|
|
say "installing/updating BBB LTI framework apps..."
|
|
LTI_APP_DIR=$LTI_DIR/rooms APP_IMG_REPO=bigbluebutton/bbb-app-rooms LOG_NAME='LTI Rooms' RELATIVE_URL_ROOT=$APPS_RELATIVE_URL_ROOT \
|
|
NGINX_NAME=bbb-app-rooms PGDBNAME=bbb_app_rooms install_lti_tool || return 1
|
|
|
|
say "All BBB LTI apps are installed, configured and up to date!"
|
|
# BBB LTI TOOLS registration ↓
|
|
register_lti_tools || return 1
|
|
|
|
say "All BBB LTI apps are registered to the LTI framework!"
|
|
|
|
return 0;
|
|
}
|
|
|
|
install_lti_tool() {
|
|
# Preparing and checking the enviroment.
|
|
if [[ -z $LTI_APP_DIR || -z $APP_IMG_REPO || -z $LOG_NAME || -z $RELATIVE_URL_ROOT || -z $NGINX_NAME || -z $PGDBNAME ]]; then
|
|
err "$LOG_NAME installation/update failed due to unmet requirements!"
|
|
fi
|
|
|
|
say "preparing and checking the enviroment to install/update $LOG_NAME..."
|
|
|
|
if [ ! -d "$LTI_APP_DIR" ]; then
|
|
mkdir -p "$LTI_APP_DIR" && say "created $LTI_APP_DIR"
|
|
fi
|
|
|
|
# Installing/Updating the LTI broker.
|
|
say "pulling latest $APP_IMG_REPO image..."
|
|
docker pull "$APP_IMG_REPO"
|
|
|
|
# Configuring BBB LTI.
|
|
say "checking/updating the configuration of $LOG_NAME..."
|
|
|
|
local SECRET_KEY_BASE
|
|
SECRET_KEY_BASE=$(docker run --rm --entrypoint bundle "$APP_IMG_REPO" exec rake secret)
|
|
|
|
if [ -z "$SECRET_KEY_BASE" ]; then
|
|
err "failed to generate $LOG_NAME secret key base - is docker running?"
|
|
fi
|
|
|
|
if [ ! -s "$LTI_APP_DIR"/.env ]; then
|
|
docker run --rm --entrypoint sh "$APP_IMG_REPO" -c 'cat dotenv' > "$LTI_APP_DIR"/.env
|
|
|
|
if [ ! -s "$LTI_APP_DIR"/.env ]; then
|
|
err "failed to create $LOG_NAME .env file - is docker running?"
|
|
fi
|
|
|
|
say "$LOG_NAME .env file was created"
|
|
fi
|
|
|
|
# A note for future maintainers:
|
|
# The following configuration operations were made idempotent, meaning that playing these actions will have an outcome on the system (configure it) only once.
|
|
# Replaying these steps are a safe and an expected operation, this gurantees the seemless simple installation and upgrade of BBB LTI framework.
|
|
# A simple change can impact that property and therefore render the upgrading functionnality unoperationnal or impact the running system.
|
|
|
|
# Configuring BBB LTI .env file (if already configured this will only update some expected or safe to change variables).
|
|
cp -v "$LTI_APP_DIR"/.env "$LTI_APP_DIR"/.env.old && say "old $LOG_NAME .env file can be retrieved at $LTI_APP_DIR/.env.old" #Backup
|
|
|
|
sed -i "s|^[# \t]*SECRET_KEY_BASE=[ \t]*$|SECRET_KEY_BASE=$SECRET_KEY_BASE|" "$LTI_APP_DIR"/.env # Do not overwrite the value if not empty.
|
|
sed -i "s|^[# \t]*BIGBLUEBUTTON_ENDPOINT=.*|BIGBLUEBUTTON_ENDPOINT=$BIGBLUEBUTTON_URL|" "$LTI_APP_DIR"/.env
|
|
sed -i "s|^[# \t]*BIGBLUEBUTTON_SECRET=.*|BIGBLUEBUTTON_SECRET=$BIGBLUEBUTTON_SECRET|" "$LTI_APP_DIR"/.env
|
|
sed -i "s|^[# \t]*URL_HOST=.*$|URL_HOST=$HOST|" "$LTI_APP_DIR"/.env
|
|
sed -i "s|^[# \t]*RELATIVE_URL_ROOT=.*$|RELATIVE_URL_ROOT=$RELATIVE_URL_ROOT|" "$LTI_APP_DIR"/.env
|
|
sed -i "s|^[# \t]*DATABASE_URL=.*myuser:mypass@localhost.*$|DATABASE_URL=$DATABASE_URL_ROOT/$PGDBNAME|" "$LTI_APP_DIR"/.env # Do not overwrite the value if not a default.
|
|
sed -i "s|^[# \t]*DATABASE_URL=[ \t]*$|DATABASE_URL=$DATABASE_URL_ROOT/$PGDBNAME|" "$LTI_APP_DIR"/.env # Do not overwrite the value if not empty.
|
|
sed -i "s|^[# \t]*REDIS_URL=.*myuser:mypass@localhost.*$|REDIS_URL=$REDIS_URL_ROOT/|" "$LTI_APP_DIR"/.env # Do not overwrite the value if not a default.
|
|
sed -i "s|^[# \t]*REDIS_URL=[ \t]*$|REDIS_URL=$REDIS_URL_ROOT/|" "$LTI_APP_DIR"/.env # Do not overwrite the value if not empty.
|
|
sed -i "s|^[# \t]*OMNIAUTH_BBBLTIBROKER_SITE=.*|OMNIAUTH_BBBLTIBROKER_SITE=https://$HOST|" "$LTI_APP_DIR"/.env
|
|
sed -i "s|^[# \t]*OMNIAUTH_BBBLTIBROKER_ROOT=.*|OMNIAUTH_BBBLTIBROKER_ROOT=$BROKER_RELATIVE_URL_ROOT|" "$LTI_APP_DIR"/.env
|
|
sed -i "s|^[# \t]*OMNIAUTH_BBBLTIBROKER_KEY=.*|OMNIAUTH_BBBLTIBROKER_KEY=$(openssl rand -hex 24)|" "$LTI_APP_DIR"/.env # Credentials are rotated on update.
|
|
sed -i "s|^[# \t]*OMNIAUTH_BBBLTIBROKER_SECRET=.*|OMNIAUTH_BBBLTIBROKER_SECRET=$(openssl rand -hex 24)|" "$LTI_APP_DIR"/.env # Credentials are rotated on update.
|
|
|
|
# Placing application nginx file.
|
|
say "configuring nginx for $LOG_NAME..."
|
|
|
|
cp -v $NGINX_FILES_DEST/"$NGINX_NAME.nginx" $NGINX_FILES_DEST/"$NGINX_NAME.nginx.old" && say "old $LOG_NAME nginx config can be retrieved at $NGINX_FILES_DEST/$NGINX_NAME.nginx.old" # Backup.
|
|
docker run --rm --entrypoint sh "$APP_IMG_REPO" -c 'cat config.nginx' > $NGINX_FILES_DEST/"$NGINX_NAME.nginx" && say "added $LOG_NAME nginx file"
|
|
|
|
if [ -z "$COTURN" ]; then
|
|
# When NGINX is the frontend reverse proxy, 'X-Forwarded-Proto' proxy header will dynamically match the $scheme of the received client request.
|
|
# In case a builtin turn server is installed, then HAPROXY is introduced and it becomes the frontend reverse proxy.
|
|
# NGINX will then act as a backend reverse proxy residing behind of it.
|
|
# HTTPS traffic from the client then is terminated at HAPROXY and plain HTTP traffic is proxied to NGINX.
|
|
# Therefore the 'X-Forwarded-Proto' proxy header needs to correctly indicate that HTTPS traffic was proxied in such scenario.
|
|
# shellcheck disable=SC2016
|
|
sed -i '/X-Forwarded-Proto/s/$scheme/"https"/' $NGINX_FILES_DEST/"$NGINX_NAME.nginx"
|
|
fi
|
|
|
|
nginx -qt || return 1
|
|
nginx -qs reload && say "$LOG_NAME was successfully configured"
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
register_lti_tools() {
|
|
# Registering/Updating LTI apps.
|
|
wait_lti_broker_start
|
|
|
|
# BBB LTI TOOLS registration ↓
|
|
say "Registering All BBB LTI framework apps..."
|
|
LTI_APP_DIR=$LTI_DIR/rooms LOG_NAME='LTI Rooms' APP_NAME=rooms register_lti_tool || return 1
|
|
|
|
return 0;
|
|
}
|
|
|
|
wait_lti_broker_start() {
|
|
say "Waiting for the LTI broker to start..."
|
|
docker-compose -f $LTI_DIR/docker-compose.yml up -d broker || err "failed to register LTI framework apps due to LTI broker failling to start - retry to resolve"
|
|
|
|
local tries=0
|
|
while ! docker-compose -f $LTI_DIR/docker-compose.yml exec -T broker bundle exec rake db:version 2> /dev/null 1>&2; do
|
|
echo -n .
|
|
sleep 3
|
|
if (( ++tries == 3 )); then
|
|
err "failed to register LTI framework apps due to reaching LTI broker waiting timeout - retry to resolve"
|
|
fi
|
|
done
|
|
|
|
sleep 3 # Optimistically wait for LTI Broker to become ready.
|
|
|
|
say "LTI broker is ready!"
|
|
|
|
return 0;
|
|
}
|
|
|
|
wait_postgres_start() {
|
|
say "Waiting for the Postgres DB to start..."
|
|
docker-compose -f $GL3_DIR/docker-compose.yml up -d postgres || err "failed to start Postgres service - retry to resolve"
|
|
|
|
local tries=0
|
|
while ! docker-compose -f $GL3_DIR/docker-compose.yml exec -T postgres pg_isready 2> /dev/null 1>&2; do
|
|
echo -n .
|
|
sleep 3
|
|
if (( ++tries == 3 )); then
|
|
err "failed to start Postgres due to reaching waiting timeout - retry to resolve"
|
|
fi
|
|
done
|
|
|
|
say "Postgres is ready!"
|
|
|
|
return 0;
|
|
}
|
|
|
|
register_lti_tool() {
|
|
# Preparing and checking the enviroment.
|
|
if [[ -z $LTI_APP_DIR || -z $APP_NAME || -z $LOG_NAME ]]; then
|
|
err "$LOG_NAME registration failed due to unmet requirements!"
|
|
fi
|
|
|
|
say "Registering $LOG_NAME..."
|
|
|
|
local OAUTH_KEY OAUTH_SECRET RELATIVE_URL_ROOT
|
|
OAUTH_KEY=$(sed -ne "s/^\([ \t]*OMNIAUTH_BBBLTIBROKER_KEY=\)\(.*\)$/\2/p" "$LTI_APP_DIR"/.env) # Extract the LTI app OAUTH key.
|
|
OAUTH_SECRET=$(sed -ne "s/^\([ \t]*OMNIAUTH_BBBLTIBROKER_SECRET=\)\(.*\)$/\2/p" "$LTI_APP_DIR"/.env) # Extract LTI app OAUTH secret.
|
|
RELATIVE_URL_ROOT=$(sed -ne "s/^\([ \t]*RELATIVE_URL_ROOT=\)\(.*\)$/\2/p" "$LTI_APP_DIR"/.env) # Extract LTI app realtive URL root path.
|
|
|
|
if [ -z "$OAUTH_KEY" ] || [ -z "$OAUTH_SECRET" ] ; then
|
|
err "failed to retrieve the $LOG_NAME OAUTH credentials - retry to resolve."
|
|
fi
|
|
|
|
local CALLBACK_URI_SUFFIX=auth/bbbltibroker/callback
|
|
local CALLBACK_URI=https://$HOST/$RELATIVE_URL_ROOT/$APP_NAME/$CALLBACK_URI_SUFFIX
|
|
|
|
if ! check_container_running broker; then
|
|
err "failed to register $LOG_NAME due to LTI broker not running - retry to resolve."
|
|
fi
|
|
|
|
if ! docker-compose -f $LTI_DIR/docker-compose.yml exec -T broker bundle exec rake db:apps:show["$APP_NAME"] \
|
|
2> /dev/null 1>&2; then
|
|
docker-compose -f $LTI_DIR/docker-compose.yml exec -T broker bundle exec rake db:apps:add["$APP_NAME","$CALLBACK_URI","$OAUTH_KEY","$OAUTH_SECRET"] \
|
|
2> /dev/null 1>&2 && say "$LOG_NAME was successfully registered."
|
|
else
|
|
docker-compose -f $LTI_DIR/docker-compose.yml exec -T broker bundle exec rake db:apps:update["$APP_NAME","$CALLBACK_URI","$OAUTH_KEY","$OAUTH_SECRET"] \
|
|
2> /dev/null 1>&2 && say "$LOG_NAME was successfully updated."
|
|
fi
|
|
|
|
return 0;
|
|
}
|
|
|
|
# Given a container name as $1, this function will check if there's a match for that name in the list of running docker containers on the system.
|
|
# The result will be binded to $?.
|
|
check_container_running() {
|
|
docker ps | grep -q "$1" || return 1;
|
|
|
|
return 0;
|
|
}
|
|
|
|
# Given a filename as $1, if file exists under $sites_dir then the file will be suffixed with '.disabled'.
|
|
# sites_dir points to Bigbluebutton nginx sites, when suffixed with '.disabled' nginx will not include the site on reload/restart thus disabling it.
|
|
disable_nginx_site() {
|
|
local site_path="$1"
|
|
|
|
if [ -z "$site_path" ]; then
|
|
return 1;
|
|
fi
|
|
|
|
if [ -f $NGINX_FILES_DEST/"$site_path" ]; then
|
|
mv $NGINX_FILES_DEST/"$site_path" $NGINX_FILES_DEST/"$site_path.disabled" && return 0;
|
|
fi
|
|
|
|
return 1;
|
|
}
|
|
|
|
install_docker() {
|
|
need_pkg apt-transport-https ca-certificates curl gnupg-agent software-properties-common openssl
|
|
|
|
# Install Docker
|
|
if ! apt-key list | grep -q Docker; then
|
|
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | apt-key add -
|
|
fi
|
|
|
|
if ! dpkg -l | grep -q docker-ce; then
|
|
echo "deb [ arch=amd64 ] https://download.docker.com/linux/ubuntu \
|
|
$(lsb_release -cs) stable" | sudo tee /etc/apt/sources.list.d/docker.list
|
|
|
|
add-apt-repository --remove\
|
|
"deb [arch=amd64] https://download.docker.com/linux/ubuntu \
|
|
$(lsb_release -cs) \
|
|
stable"
|
|
|
|
apt-get update
|
|
need_pkg docker-ce docker-ce-cli containerd.io
|
|
fi
|
|
if ! which docker; then err "Docker did not install"; fi
|
|
|
|
# Purge older docker compose if exists.
|
|
if dpkg -l | grep -q docker-compose; then
|
|
apt-get purge -y docker-compose
|
|
fi
|
|
|
|
if [ ! -x /usr/local/bin/docker-compose ]; then
|
|
curl -L "https://github.com/docker/compose/releases/download/1.24.0/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose
|
|
chmod +x /usr/local/bin/docker-compose
|
|
fi
|
|
|
|
# Ensuring docker is running
|
|
if ! docker version > /dev/null ; then
|
|
# Attempting to auto resolve by restarting docker socket and engine.
|
|
systemctl restart docker.socket docker.service
|
|
sleep 5
|
|
|
|
docker version > /dev/null || err "docker is failing to restart, something is wrong retry to resolve - exiting"
|
|
say "docker is running!"
|
|
fi
|
|
|
|
}
|
|
|
|
|
|
install_ssl() {
|
|
if ! grep -q "$HOST" /usr/local/bigbluebutton/core/scripts/bigbluebutton.yml; then
|
|
bbb-conf --setip "$HOST"
|
|
fi
|
|
|
|
mkdir -p /etc/nginx/ssl
|
|
|
|
if [ -z "$PROVIDED_CERTIFICATE" ]; then
|
|
add-apt-repository universe
|
|
apt-get update
|
|
need_pkg certbot
|
|
|
|
if [[ -f "/etc/letsencrypt/live/$HOST/fullchain.pem" ]] && [[ -f "/etc/letsencrypt/renewal/$HOST.conf" ]] \
|
|
&& ! grep -q '/var/www/bigbluebutton-default/assets' "/etc/letsencrypt/renewal/$HOST.conf"; then
|
|
sed -i -e 's#/var/www/bigbluebutton-default#/var/www/bigbluebutton-default/assets#' "/etc/letsencrypt/renewal/$HOST.conf"
|
|
if ! certbot renew; then
|
|
err "Let's Encrypt SSL renewal request for $HOST did not succeed - exiting"
|
|
fi
|
|
fi
|
|
fi
|
|
|
|
if [ ! -f "/etc/letsencrypt/live/$HOST/fullchain.pem" ]; then
|
|
rm -f /tmp/bigbluebutton.bak
|
|
if ! grep -q "$HOST" /etc/nginx/sites-available/bigbluebutton; then # make sure we can do the challenge
|
|
if [ -f /etc/nginx/sites-available/bigbluebutton ]; then
|
|
cp /etc/nginx/sites-available/bigbluebutton /tmp/bigbluebutton.bak
|
|
fi
|
|
cat <<HERE > /etc/nginx/sites-available/bigbluebutton
|
|
server_tokens off;
|
|
server {
|
|
listen 80;
|
|
listen [::]:80;
|
|
server_name $HOST;
|
|
|
|
access_log /var/log/nginx/bigbluebutton.access.log;
|
|
|
|
# BigBlueButton landing page.
|
|
location / {
|
|
root /var/www/bigbluebutton-default/assets;
|
|
try_files \$uri @bbb-fe;
|
|
}
|
|
}
|
|
HERE
|
|
systemctl restart nginx
|
|
fi
|
|
|
|
if [ -z "$PROVIDED_CERTIFICATE" ]; then
|
|
if ! certbot --email "$EMAIL" --agree-tos --rsa-key-size 4096 -w /var/www/bigbluebutton-default/assets/ \
|
|
-d "$HOST" --deploy-hook "systemctl reload nginx" "${LETS_ENCRYPT_OPTIONS[@]}" certonly; then
|
|
systemctl restart nginx
|
|
err "Let's Encrypt SSL request for $HOST did not succeed - exiting"
|
|
fi
|
|
else
|
|
# Place your fullchain.pem and privkey.pem files in /local/certs/ and bbb-install.sh will deal with the rest.
|
|
mkdir -p "/etc/letsencrypt/live/$HOST/"
|
|
ln -s /local/certs/fullchain.pem "/etc/letsencrypt/live/$HOST/fullchain.pem"
|
|
ln -s /local/certs/privkey.pem "/etc/letsencrypt/live/$HOST/privkey.pem"
|
|
fi
|
|
fi
|
|
|
|
if [ -z "$COTURN" ]; then
|
|
# No COTURN credentials provided, setup a local TURN server
|
|
cat <<HERE > /etc/nginx/sites-available/bigbluebutton
|
|
server_tokens off;
|
|
|
|
server {
|
|
listen 80;
|
|
listen [::]:80;
|
|
server_name $HOST;
|
|
|
|
location ^~ / {
|
|
return 301 https://\$server_name\$request_uri; #redirect HTTP to HTTPS
|
|
}
|
|
|
|
location ^~ /.well-known/acme-challenge/ {
|
|
allow all;
|
|
default_type "text/plain";
|
|
root /var/www/bigbluebutton-default/assets;
|
|
}
|
|
|
|
location = /.well-known/acme-challenge/ {
|
|
return 404;
|
|
}
|
|
}
|
|
|
|
set_real_ip_from 127.0.0.1;
|
|
real_ip_header proxy_protocol;
|
|
real_ip_recursive on;
|
|
server {
|
|
# this double listenting is intended. We terminate SSL on haproxy. HTTP2 is a
|
|
# binary protocol. haproxy has to decide which protocol is spoken. This is
|
|
# negotiated by ALPN.
|
|
#
|
|
# Depending on the ALPN value traffic is redirected to either port 82 (HTTP2,
|
|
# ALPN value h2) or 81 (HTTP 1.0 or HTTP 1.1, ALPN value http/1.1 or no value)
|
|
|
|
listen 127.0.0.1:82 http2 proxy_protocol;
|
|
listen [::1]:82 http2;
|
|
listen 127.0.0.1:81 proxy_protocol;
|
|
listen [::1]:81;
|
|
server_name $HOST;
|
|
|
|
# nginx does not know its external port/protocol behind haproxy, so use relative redirects.
|
|
absolute_redirect off;
|
|
|
|
# HSTS (uncomment to enable)
|
|
#add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
|
|
|
|
access_log /var/log/nginx/bigbluebutton.access.log;
|
|
|
|
# This variable is used instead of \$scheme by bigbluebutton nginx include
|
|
# files, so \$scheme can be overridden in reverse-proxy configurations.
|
|
set \$real_scheme "https";
|
|
|
|
# BigBlueButton landing page.
|
|
location / {
|
|
root /var/www/bigbluebutton-default/assets;
|
|
try_files \$uri @bbb-fe;
|
|
}
|
|
|
|
# Include specific rules for record and playback
|
|
include /etc/bigbluebutton/nginx/*.nginx;
|
|
}
|
|
HERE
|
|
else
|
|
# We've been given COTURN credentials, so HAPROXY is not installed for local TURN server
|
|
cat <<HERE > /etc/nginx/sites-available/bigbluebutton
|
|
server_tokens off;
|
|
|
|
server {
|
|
listen 80;
|
|
listen [::]:80;
|
|
server_name $HOST;
|
|
|
|
location ^~ / {
|
|
return 301 https://\$server_name\$request_uri; #redirect HTTP to HTTPS
|
|
}
|
|
|
|
location ^~ /.well-known/acme-challenge/ {
|
|
allow all;
|
|
default_type "text/plain";
|
|
root /var/www/bigbluebutton-default/assets;
|
|
}
|
|
|
|
location = /.well-known/acme-challenge/ {
|
|
return 404;
|
|
}
|
|
}
|
|
|
|
server {
|
|
listen 443 ssl http2;
|
|
listen [::]:443 ssl http2;
|
|
server_name $HOST;
|
|
|
|
ssl_certificate /etc/letsencrypt/live/$HOST/fullchain.pem;
|
|
ssl_certificate_key /etc/letsencrypt/live/$HOST/privkey.pem;
|
|
ssl_session_cache shared:SSL:10m;
|
|
ssl_session_timeout 10m;
|
|
ssl_protocols TLSv1.2 TLSv1.3;
|
|
ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384;
|
|
ssl_dhparam /etc/nginx/ssl/ffdhe2048.pem;
|
|
|
|
# HSTS (comment out to enable)
|
|
#add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
|
|
|
|
access_log /var/log/nginx/bigbluebutton.access.log;
|
|
|
|
# This variable is used instead of \$scheme by bigbluebutton nginx include
|
|
# files, so \$scheme can be overridden in reverse-proxy configurations.
|
|
set \$real_scheme \$scheme;
|
|
|
|
# BigBlueButton landing page.
|
|
location / {
|
|
root /var/www/bigbluebutton-default/assets;
|
|
try_files \$uri @bbb-fe;
|
|
}
|
|
|
|
# Include specific rules for record and playback
|
|
include /etc/bigbluebutton/nginx/*.nginx;
|
|
}
|
|
HERE
|
|
|
|
if [[ ! -f /etc/nginx/ssl/ffdhe2048.pem ]]; then
|
|
cat >/etc/nginx/ssl/ffdhe2048.pem <<"HERE"
|
|
-----BEGIN DH PARAMETERS-----
|
|
MIIBCAKCAQEA//////////+t+FRYortKmq/cViAnPTzx2LnFg84tNpWp4TZBFGQz
|
|
+8yTnc4kmz75fS/jY2MMddj2gbICrsRhetPfHtXV/WVhJDP1H18GbtCFY2VVPe0a
|
|
87VXE15/V8k1mE8McODmi3fipona8+/och3xWKE2rec1MKzKT0g6eXq8CrGCsyT7
|
|
YdEIqUuyyOP7uWrat2DX9GgdT0Kj3jlN9K5W7edjcrsZCwenyO4KbXCeAvzhzffi
|
|
7MA0BM0oNC9hkXL+nOmFg/+OTxIy7vKBg8P+OxtMb61zO7X8vC7CIAXFjvGDfRaD
|
|
ssbzSibBsu/6iGtCOGEoXJf//////////wIBAg==
|
|
-----END DH PARAMETERS-----
|
|
HERE
|
|
fi
|
|
if [[ -f /etc/nginx/ssl/dhp-4096.pem ]]; then
|
|
rm /etc/nginx/ssl/dhp-4096.pem
|
|
fi
|
|
fi
|
|
# Create the default Welcome page Bigbluebutton Frontend unless it exists.
|
|
if [[ ! -f /usr/share/bigbluebutton/nginx/default-fe.nginx && ! -f /usr/share/bigbluebutton/nginx/default-fe.nginx.disabled ]]; then
|
|
cat <<HERE > /usr/share/bigbluebutton/nginx/default-fe.nginx
|
|
# Default Bigbluebutton Landing page.
|
|
|
|
location @bbb-fe {
|
|
index index.html index.htm;
|
|
expires 1m;
|
|
}
|
|
|
|
HERE
|
|
fi
|
|
|
|
# Configure rest of BigBlueButton Configuration for SSL
|
|
xmlstarlet edit --inplace --update '//param[@name="wss-binding"]/@value' --value "$IP:7443" /opt/freeswitch/conf/sip_profiles/external.xml
|
|
|
|
# shellcheck disable=SC1091
|
|
eval "$(source /etc/bigbluebutton/bigbluebutton-release && declare -p BIGBLUEBUTTON_RELEASE)"
|
|
if [[ $BIGBLUEBUTTON_RELEASE == 2.2.* ]] && [[ ${BIGBLUEBUTTON_RELEASE#*.*.} -lt 29 ]]; then
|
|
sed -i "s/proxy_pass .*/proxy_pass https:\/\/$IP:7443;/g" /usr/share/bigbluebutton/nginx/sip.nginx
|
|
else
|
|
# Use nginx as proxy for WSS -> WS (see https://github.com/bigbluebutton/bigbluebutton/issues/9667)
|
|
yq w -i /usr/share/meteor/bundle/programs/server/assets/app/config/settings.yml public.media.sipjsHackViaWs true
|
|
sed -i "s/proxy_pass .*/proxy_pass http:\/\/$IP:5066;/g" /usr/share/bigbluebutton/nginx/sip.nginx
|
|
xmlstarlet edit --inplace --update '//param[@name="ws-binding"]/@value' --value "$IP:5066" /opt/freeswitch/conf/sip_profiles/external.xml
|
|
fi
|
|
|
|
sed -i 's/^bigbluebutton.web.serverURL=http:/bigbluebutton.web.serverURL=https:/g' "$SERVLET_DIR/WEB-INF/classes/bigbluebutton.properties"
|
|
if [ -f "$BBB_WEB_ETC_CONFIG" ]; then
|
|
sed -i 's/^bigbluebutton.web.serverURL=http:/bigbluebutton.web.serverURL=https:/g' "$BBB_WEB_ETC_CONFIG"
|
|
fi
|
|
|
|
yq w -i /usr/local/bigbluebutton/core/scripts/bigbluebutton.yml playback_protocol https
|
|
chmod 644 /usr/local/bigbluebutton/core/scripts/bigbluebutton.yml
|
|
|
|
# Update Greenlight (if installed) to use SSL
|
|
for gl_dir in ~/greenlight $GL3_DIR;do
|
|
if [ -f "$gl_dir"/.env ]; then
|
|
if ! grep ^BIGBLUEBUTTON_ENDPOINT "$gl_dir"/.env | grep -q https; then
|
|
if [[ -z $BIGBLUEBUTTON_URL ]]; then
|
|
BIGBLUEBUTTON_URL=$(cat "$SERVLET_DIR/WEB-INF/classes/bigbluebutton.properties" "$CR_TMPFILE" "$BBB_WEB_ETC_CONFIG" | grep -v '#' | sed -n '/^bigbluebutton.web.serverURL/{s/.*=//;p}' | tail -n 1 )/bigbluebutton/
|
|
fi
|
|
|
|
sed -i "s|.*BIGBLUEBUTTON_ENDPOINT=.*|BIGBLUEBUTTON_ENDPOINT=$BIGBLUEBUTTON_URL|" ~/greenlight/.env
|
|
docker-compose -f "$gl_dir"/docker-compose.yml down
|
|
docker-compose -f "$gl_dir"/docker-compose.yml up -d
|
|
fi
|
|
fi
|
|
done
|
|
|
|
TARGET=/usr/local/bigbluebutton/bbb-webrtc-sfu/config/default.yml
|
|
if [ -f $TARGET ]; then
|
|
if grep -q kurentoIp $TARGET; then
|
|
# 2.0
|
|
yq w -i $TARGET kurentoIp "$IP"
|
|
else
|
|
# 2.2
|
|
yq w -i $TARGET kurento[0].ip "$IP"
|
|
yq w -i $TARGET freeswitch.ip "$IP"
|
|
|
|
if [[ $BIGBLUEBUTTON_RELEASE == 2.2.* ]] && [[ ${BIGBLUEBUTTON_RELEASE#*.*.} -lt 29 ]]; then
|
|
if [ -n "$INTERNAL_IP" ]; then
|
|
yq w -i $TARGET freeswitch.sip_ip "$INTERNAL_IP"
|
|
else
|
|
yq w -i $TARGET freeswitch.sip_ip "$IP"
|
|
fi
|
|
else
|
|
# Use nginx as proxy for WSS -> WS (see https://github.com/bigbluebutton/bigbluebutton/issues/9667)
|
|
yq w -i $TARGET freeswitch.sip_ip "$IP"
|
|
fi
|
|
fi
|
|
chown bigbluebutton:bigbluebutton $TARGET
|
|
chmod 644 $TARGET
|
|
fi
|
|
|
|
mkdir -p /etc/bigbluebutton/bbb-webrtc-sfu
|
|
TARGET=/etc/bigbluebutton/bbb-webrtc-sfu/production.yml
|
|
touch $TARGET
|
|
|
|
# Configure mediasoup IPs, reference: https://raw.githubusercontent.com/bigbluebutton/bbb-webrtc-sfu/v2.7.2/docs/mediasoup.md
|
|
# mediasoup IPs: WebRTC
|
|
yq w -i "$TARGET" mediasoup.webrtc.listenIps[0].ip "0.0.0.0"
|
|
yq w -i "$TARGET" mediasoup.webrtc.listenIps[0].announcedIp "$IP"
|
|
|
|
# mediasoup IPs: plain RTP (internal comms, FS <-> mediasoup)
|
|
yq w -i "$TARGET" mediasoup.plainRtp.listenIp.ip "0.0.0.0"
|
|
yq w -i "$TARGET" mediasoup.plainRtp.listenIp.announcedIp "$IP"
|
|
|
|
systemctl reload nginx
|
|
}
|
|
|
|
configure_coturn() {
|
|
TURN_XML=/etc/bigbluebutton/turn-stun-servers.xml
|
|
|
|
if [ -z "$COTURN" ]; then
|
|
# the user didn't pass '-c', so use the local TURN server's host
|
|
COTURN_HOST=$HOST
|
|
fi
|
|
|
|
cat <<HERE > $TURN_XML
|
|
<?xml version="1.0" encoding="UTF-8"?>
|
|
<beans xmlns="http://www.springframework.org/schema/beans"
|
|
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
|
xsi:schemaLocation="http://www.springframework.org/schema/beans
|
|
http://www.springframework.org/schema/beans/spring-beans-2.5.xsd">
|
|
|
|
<!--
|
|
We need turn0 for FireFox to workaround its limited ICE implementation.
|
|
This is UDP connection. Note that port 3478 must be open on this BigBlueButton
|
|
and reachble by the client.
|
|
|
|
Also, in 2.5, we previously defined turn:\$HOST:443?transport=tcp (not 'turns')
|
|
to workaround a bug in Safari's handling of Let's Encrypt. This bug is now fixed
|
|
https://bugs.webkit.org/show_bug.cgi?id=219274, so we omit the 'turn' protocol over
|
|
port 443.
|
|
-->
|
|
<bean id="turn0" class="org.bigbluebutton.web.services.turn.TurnServer">
|
|
<constructor-arg index="0" value="$COTURN_SECRET"/>
|
|
<constructor-arg index="1" value="turn:$COTURN_HOST:3478"/>
|
|
<constructor-arg index="2" value="86400"/>
|
|
</bean>
|
|
<bean id="turn1" class="org.bigbluebutton.web.services.turn.TurnServer">
|
|
<constructor-arg index="0" value="$COTURN_SECRET"/>
|
|
<constructor-arg index="1" value="turns:$COTURN_HOST:443?transport=tcp"/>
|
|
<constructor-arg index="2" value="86400"/>
|
|
</bean>
|
|
|
|
<bean id="stunTurnService"
|
|
class="org.bigbluebutton.web.services.turn.StunTurnService">
|
|
<property name="stunServers">
|
|
<set>
|
|
</set>
|
|
</property>
|
|
<property name="turnServers">
|
|
<set>
|
|
<ref bean="turn0"/>
|
|
<ref bean="turn1"/>
|
|
</set>
|
|
</property>
|
|
</bean>
|
|
</beans>
|
|
HERE
|
|
|
|
chown root:bigbluebutton "$TURN_XML"
|
|
chmod 640 "$TURN_XML"
|
|
}
|
|
|
|
|
|
install_coturn() {
|
|
apt-get update
|
|
apt-get -y -o Dpkg::Options::="--force-confdef" -o Dpkg::Options::="--force-confnew" dist-upgrade
|
|
|
|
need_pkg software-properties-common certbot
|
|
|
|
need_pkg coturn
|
|
|
|
if [ -n "$INTERNAL_IP" ]; then
|
|
SECOND_ALLOWED_PEER_IP="allowed-peer-ip=$INTERNAL_IP"
|
|
fi
|
|
# check if this is still the default coturn config file. Replace it in this case.
|
|
if grep "#static-auth-secret=north" /etc/turnserver.conf > /dev/null ; then
|
|
COTURN_SECRET="$(openssl rand -base64 32)"
|
|
cat <<HERE > /etc/turnserver.conf
|
|
listening-port=3478
|
|
|
|
listening-ip=${INTERNAL_IP:-$IP}
|
|
relay-ip=${INTERNAL_IP:-$IP}
|
|
|
|
min-port=32769
|
|
max-port=65535
|
|
verbose
|
|
|
|
fingerprint
|
|
lt-cred-mech
|
|
use-auth-secret
|
|
static-auth-secret=$COTURN_SECRET
|
|
realm=$HOST
|
|
|
|
keep-address-family
|
|
|
|
no-cli
|
|
no-tlsv1
|
|
no-tlsv1_1
|
|
|
|
# Block connections to IP ranges which shouldn't be reachable
|
|
no-loopback-peers
|
|
no-multicast-peers
|
|
|
|
|
|
# we only need to allow peer connections from the machine itself (from mediasoup or freeswitch).
|
|
denied-peer-ip=0.0.0.0-255.255.255.255
|
|
denied-peer-ip=::-ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff
|
|
allowed-peer-ip=$IP
|
|
$SECOND_ALLOWED_PEER_IP
|
|
|
|
HERE
|
|
chown root:turnserver /etc/turnserver.conf
|
|
chmod 640 /etc/turnserver.conf
|
|
else
|
|
# fetch secret for later setting up in BBB turn config
|
|
COTURN_SECRET="$(grep static-auth-secret= /etc/turnserver.conf |cut -d = -f 2-)"
|
|
fi
|
|
|
|
mkdir -p /var/log/turnserver
|
|
chown turnserver:turnserver /var/log/turnserver
|
|
|
|
cat <<HERE > /etc/logrotate.d/coturn
|
|
/var/log/turnserver/*.log
|
|
{
|
|
rotate 7
|
|
daily
|
|
missingok
|
|
notifempty
|
|
compress
|
|
postrotate
|
|
/bin/systemctl kill -s HUP coturn.service
|
|
endscript
|
|
}
|
|
HERE
|
|
|
|
# Eanble coturn to bind to port 443 with CAP_NET_BIND_SERVICE
|
|
mkdir -p /etc/systemd/system/coturn.service.d
|
|
rm -rf /etc/systemd/system/coturn.service.d/ansible.conf # Remove previous file
|
|
cat > /etc/systemd/system/coturn.service.d/override.conf <<HERE
|
|
[Service]
|
|
LimitNOFILE=1048576
|
|
# AmbientCapabilities=CAP_NET_BIND_SERVICE
|
|
ExecStart=
|
|
ExecStart=/usr/bin/turnserver -c /etc/turnserver.conf --pidfile= --no-stdout-log --simple-log --log-file /var/log/turnserver/turnserver.log
|
|
Restart=always
|
|
HERE
|
|
|
|
systemctl daemon-reload
|
|
systemctl restart coturn
|
|
configure_coturn
|
|
}
|
|
|
|
|
|
setup_ufw() {
|
|
if [ ! -f /etc/bigbluebutton/bbb-conf/apply-config.sh ]; then
|
|
cat > /etc/bigbluebutton/bbb-conf/apply-config.sh << HERE
|
|
#!/bin/bash
|
|
|
|
# Pull in the helper functions for configuring BigBlueButton
|
|
source /etc/bigbluebutton/bbb-conf/apply-lib.sh
|
|
|
|
enableUFWRules
|
|
HERE
|
|
chmod +x /etc/bigbluebutton/bbb-conf/apply-config.sh
|
|
fi
|
|
}
|
|
|
|
main "$@" || exit 1
|
|
|