#!/bin/bash -e # Copyright (c) 2022 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 . # BigBlueButton is an open source conferencing system. For more information see # https://www.bigbluebutton.org/. # # This bbb-install-2.5.sh script automates many of the installation and configuration # steps at # https://docs.bigbluebutton.org/2.5/install.html # # # Examples # # Install BigBlueButton 2.5.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://ubuntu.bigbluebutton.org/bbb-install-2.5.sh | bash -s -- -w -v focal-250 -s bbb.example.com -e info@example.com # # Same as above but also install the API examples for testing. # # wget -qO- https://ubuntu.bigbluebutton.org/bbb-install-2.5.sh | bash -s -- -w -a -v focal-250 -s bbb.example.com -e info@example.com # # Install BigBlueButton with SSL + Greenlight # # wget -qO- https://ubuntu.bigbluebutton.org/bbb-install-2.5.sh | bash -s -- -w -v focal-250 -s bbb.example.com -e info@example.com -g # usage() { set +x cat 1>&2 < Install given version of BigBlueButton (e.g. 'focal-250') (required) -s Configure server with -e Email for Let's Encrypt certbot -x Use Let's Encrypt certbot with manual dns challenges -a Install BBB API demos -g Install Greenlight -c : Configure with coturn server at using -m Create a Symbolic link from /var/bigbluebutton to -p [:] Use apt-get proxy at (default port 3142) -r 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 coturn only): -c : Setup a coturn server with and (required) -e Configure email for Let's Encrypt certbot (required) OPTIONS (install Let's Encrypt certificate only): -s Configure server with (required) -e 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) EXAMPLES: Sample options for setup a BigBlueButton server -v focal-250 -s bbb.example.com -e info@example.com -v focal-250 -s bbb.example.com -e info@example.com -g -v focal-250 -s bbb.example.com -e info@example.com -g -c turn.example.com:1234324 Sample options for setup of a coturn server (on a Ubuntu 20.04) -c turn.example.com:1234324 -e info@example.com SUPPORT: Community: https://bigbluebutton.org/support Docs: https://github.com/bigbluebutton/bbb-install HERE } main() { export DEBIAN_FRONTEND=noninteractive PACKAGE_REPOSITORY=ubuntu.bigbluebutton.org LETS_ENCRYPT_OPTIONS="--webroot --non-interactive" SOURCES_FETCHED=false CR_TMPFILE=$(mktemp /tmp/carriage-return.XXXXXX) echo "\n" > $CR_TMPFILE need_x64 while builtin getopts "hs:r:c:v:e:p:m:lxgadwji" 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 ;; l) LETS_ENCRYPT_ONLY=true ;; g) GREENLIGHT=true ;; a) API_DEMOS=true ;; 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" exit 1 ;; \?) err "Invalid option: -$OPTARG" >&2 usage ;; 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 # We're installing BigBlueButton env if [ "$DISTRO" == "focal" ]; then check_ubuntu 20.04 TOMCAT_USER=tomcat9 fi check_mem check_cpus need_pkg software-properties-common # needed for add-apt-repository sudo add-apt-repository universe need_pkg wget curl gpg-agent dirmngr # 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 E95B94BC # Needed for libopusenc0 if ! apt-key list 5AFA7A83 | grep -q -E "1024|4096"; then # Add Kurento package sudo apt-key adv --keyserver 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 grep -q 12 /etc/apt/sources.list.d/nodesource.list ; then # Node 12 might be installed, previously used in BigBlueButton sudo apt-get purge nodejs sudo rm -r /etc/apt/sources.list.d/nodesource.list fi if [ ! -f /etc/apt/sources.list.d/nodesource.list ]; then curl -sL https://deb.nodesource.com/setup_16.x | sudo -E bash - fi if ! apt-cache madison nodejs | grep -q node_16; then err "Did not detect nodejs 16.x candidate for installation" 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-11-jre java-common update-java-alternatives -s java-1.11.0-openjdk-amd64 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 TURN_XML=$SERVLET_DIR/WEB-INF/classes/spring/turn-stun-servers.xml fi while [ ! -f $SERVLET_DIR/WEB-INF/classes/bigbluebutton.properties ]; do sleep 1; echo -n '.'; done check_lxc check_nat check_LimitNOFILE configure_HTML5 if [ -n "$API_DEMOS" ]; then need_pkg bbb-demo while [ ! -f /var/lib/$TOMCAT_USER/webapps/demo/bbb_api_conf.jsp ]; do sleep 1; echo -n '.'; done fi 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 "$GREENLIGHT" ]; then install_greenlight fi if [ -n "$COTURN" ]; then configure_coturn fi apt-get auto-remove -y if systemctl status freeswitch.service | grep -q SETSCHEDULER; then sed -i "s/^CPUSchedulingPolicy=rr/#CPUSchedulingPolicy=rr/g" /lib/systemd/system/freeswitch.service systemctl daemon-reload fi 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-conf --check } say() { echo "bbb-install: $1" } err() { say "$1" >&2 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_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 # Determine external IP if grep -sqi ^ec2 /sys/devices/virtual/dmi/id/product_uuid; then # EC2 local 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 local 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 local 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 local 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 local 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 ${@:1} >/dev/null 2>&1; then LC_CTYPE=C.UTF-8 apt-get install -yq ${@:1} 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"; then err "This script can only install BigBlueButton 2.5 (or later) and is meant to be run on Ubuntu 20.04 (focal) server."; fi DISTRO=$(echo "$1" | sed 's/-.*//g') 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 :"; 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 "; fi if [ -z "$COTURN_SECRET" ]; then err "-c option must contain "; 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 running under LXC, then modify the FreeSWITCH systemctl service so it does not use realtime scheduler check_lxc() { if grep -qa container=lxc /proc/1/environ; then if grep IOSchedulingClass /lib/systemd/system/freeswitch.service > /dev/null; then cat > /lib/systemd/system/freeswitch.service << HERE [Unit] Description=freeswitch After=syslog.target network.target local-fs.target [Service] Type=forking PIDFile=/opt/freeswitch/var/run/freeswitch/freeswitch.pid Environment="DAEMON_OPTS=-nonat" EnvironmentFile=-/etc/default/freeswitch ExecStart=/opt/freeswitch/bin/freeswitch -u freeswitch -g daemon -ncwait \$DAEMON_OPTS TimeoutSec=45s Restart=always WorkingDirectory=/opt/freeswitch User=freeswitch Group=daemon LimitCORE=infinity LimitNOFILE=100000 LimitNPROC=60000 LimitSTACK=250000 LimitRTPRIO=infinity LimitRTTIME=7000000 #IOSchedulingClass=realtime #IOSchedulingPriority=2 #CPUSchedulingPolicy=rr #CPUSchedulingPriority=89 [Install] WantedBy=multi-user.target HERE systemctl daemon-reload fi fi } # 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_greenlight(){ install_docker docker pull bigbluebutton/greenlight:v2 # Ensure the current version of greenlight is pulled # Purge older docker compose 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 if [ ! -d ~/greenlight ]; then mkdir -p ~/greenlight fi # This will trigger the download of Greenlight docker image (if needed) SECRET_KEY_BASE=$(docker run --rm bigbluebutton/greenlight:v2 bundle exec rake secret) if [ ! -f ~/greenlight/.env ]; then docker run --rm bigbluebutton/greenlight:v2 cat ./sample.env > ~/greenlight/.env fi 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/ 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) SAFE_HOSTS=$(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 | sed 's/https\?:\/\///') # Update Greenlight configuration file in ~/greenlight/env sed -i "s|SECRET_KEY_BASE=.*|SECRET_KEY_BASE=$SECRET_KEY_BASE|" ~/greenlight/.env sed -i "s|.*BIGBLUEBUTTON_ENDPOINT=.*|BIGBLUEBUTTON_ENDPOINT=$BIGBLUEBUTTON_URL|" ~/greenlight/.env sed -i "s|.*BIGBLUEBUTTON_SECRET=.*|BIGBLUEBUTTON_SECRET=$BIGBLUEBUTTON_SECRET|" ~/greenlight/.env sed -i "s|SAFE_HOSTS=.*|SAFE_HOSTS=$SAFE_HOSTS|" ~/greenlight/.env # need_pkg bbb-webhooks if [ ! -f /usr/share/bigbluebutton/nginx/greenlight.nginx ]; then docker run --rm bigbluebutton/greenlight:v2 cat ./greenlight.nginx | tee /usr/share/bigbluebutton/nginx/greenlight.nginx cat > /usr/share/bigbluebutton/nginx/greenlight-redirect.nginx << HERE location = / { return 307 /b; } HERE systemctl restart nginx fi if ! gem list | grep -q java_properties; then gem install jwt java_properties fi if [ ! -f ~/greenlight/docker-compose.yml ]; then docker run --rm bigbluebutton/greenlight:v2 cat ./docker-compose.yml > ~/greenlight/docker-compose.yml fi # change the default passwords PGPASSWORD=$(openssl rand -base64 24) sed -i "s,^\([ \t-]*POSTGRES_PASSWORD\)\(=password\),\1=$PGPASSWORD,g" ~/greenlight/docker-compose.yml sed -i "s,^\([ \t]*DB_PASSWORD\)\(=password\),\1=$PGPASSWORD,g" ~/greenlight/.env # Remove old containers if docker ps | grep -q greenlight_db_1; then docker rm -f greenlight_db_1 fi if docker ps | grep -q greenlight-v2; then docker rm -f greenlight-v2 fi if ! docker ps | grep -q greenlight; then docker-compose -f ~/greenlight/docker-compose.yml up -d sleep 5 fi } 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 add-apt-repository \ "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 # Remove Docker Compose if dpkg -l | grep -q docker-compose; then apt-get purge -y docker-compose 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 fi if [ ! -f /etc/nginx/ssl/dhp-4096.pem ]; then openssl dhparam -dsaparam -out /etc/nginx/ssl/dhp-4096.pem 4096 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 < /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; index index.html index.htm; expires 1m; } } 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/ \ -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-2.5.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 cat < /etc/nginx/sites-available/bigbluebutton server_tokens off; server { listen 80; listen [::]:80; server_name $HOST; return 301 https://\$server_name\$request_uri; #redirect HTTP to HTTPS } 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/dhp-4096.pem; # HSTS (comment out to enable) #add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always; access_log /var/log/nginx/bigbluebutton.access.log; # BigBlueButton landing page. location / { root /var/www/bigbluebutton-default; index index.html index.htm; expires 1m; } # Include specific rules for record and playback include /usr/share/bigbluebutton/nginx/*.nginx; include /etc/bigbluebutton/nginx/*.nginx; # possible overrides } HERE # 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 source /etc/bigbluebutton/bigbluebutton-release if [ -n "$(echo "$BIGBLUEBUTTON_RELEASE" | grep '2.2')" ] && [ "$(echo "$BIGBLUEBUTTON_RELEASE" | cut -d\. -f3)" -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 if [ -f /var/lib/$TOMCAT_USER/webapps/demo/bbb_api_conf.jsp ]; then sed -i 's/String BigBlueButtonURL = "http:/String BigBlueButtonURL = "https:/g' /var/lib/$TOMCAT_USER/webapps/demo/bbb_api_conf.jsp fi if [ -f /usr/share/meteor/bundle/programs/server/assets/app/config/settings.yml ]; then yq w -i /usr/share/meteor/bundle/programs/server/assets/app/config/settings.yml public.note.url "https://$HOST/pad" fi # Update Greenlight (if installed) to use SSL if [ -f ~/greenlight/.env ]; then if ! grep ^BIGBLUEBUTTON_ENDPOINT ~/greenlight/.env | grep -q https; 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/ sed -i "s|.*BIGBLUEBUTTON_ENDPOINT=.*|BIGBLUEBUTTON_ENDPOINT=$BIGBLUEBUTTON_URL|" ~/greenlight/.env docker-compose -f ~/greenlight/docker-compose.yml down docker-compose -f ~/greenlight/docker-compose.yml up -d fi fi 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 [ -n "$(echo "$BIGBLUEBUTTON_RELEASE" | grep '2.2')" ] && [ "$(echo "$BIGBLUEBUTTON_RELEASE" | cut -d\. -f3)" -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" } configure_coturn() { cat < $TURN_XML HERE } 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 if ! certbot certonly --standalone --non-interactive --preferred-challenges http \ -d "$COTURN_HOST" --email "$EMAIL" --agree-tos -n ; then err "Let's Encrypt SSL request for $COTURN_HOST did not succeed - exiting" fi need_pkg coturn if [ -n "$INTERNAL_IP" ]; then EXTERNAL_IP="external-ip=$IP/$INTERNAL_IP" fi cat < /etc/turnserver.conf listening-port=3478 tls-listening-port=443 listening-ip=$IP relay-ip=$IP $EXTERNAL_IP min-port=32769 max-port=65535 verbose fingerprint lt-cred-mech use-auth-secret static-auth-secret=$COTURN_SECRET realm=$(echo "$COTURN_HOST" | cut -d'.' -f2-) cert=/etc/turnserver/fullchain.pem pkey=/etc/turnserver/privkey.pem # From https://ssl-config.mozilla.org/ Intermediate, openssl 1.1.0g, 2020-01 cipher-list="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" dh-file=/etc/turnserver/dhp.pem keep-address-family no-cli no-tlsv1 no-tlsv1_1 HERE mkdir -p /etc/turnserver if [ ! -f /etc/turnserver/dhp.pem ]; then openssl dhparam -dsaparam -out /etc/turnserver/dhp.pem 2048 fi mkdir -p /var/log/turnserver chown turnserver:turnserver /var/log/turnserver cat < /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 < /etc/letsencrypt/renewal-hooks/deploy/coturn < /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