Skip to content
Snippets Groups Projects
Select Git revision
  • aacc5be00b37f556b7c44152a31c90a8eb2da5a9
  • 2023ss default protected
  • 2022ss
  • 2021ss
  • 2020ss
  • 2019ss
  • 2018ss
  • 2017ss
  • 2016ss
  • 2015ss
  • 2014ss
11 results

hello-1.c

Blame
  • Forked from Peter Gerwinski / bs
    Source project has a limited visibility.
    gl-install.sh 31.95 KiB
    #!/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 <http://www.gnu.org/licenses/>.
    
    # BigBlueButton is an open source conferencing system. For more information see
    #    https://www.bigbluebutton.org/.
    #
    # This gl-install.sh script automates many of the installation and configuration
    # steps at https://docs.bigbluebutton.org/greenlight/v3/install
    #
    #
    #  Examples
    #
    #  Install a standaolne Greenlight 3.x.x with a publicly trusted SSL certificate issued by Let's Encrypt using a FQDN of www.example.com
    #  and an email address of info@example.com.
    #
    #    wget -qO- https://raw.githubusercontent.com/bigbluebutton/greenlight/master/gl-install.sh | bash -s -- -s www.example.com -e info@example.com 
    #
    
    
    usage() {
        set +x
        cat 1>&2 <<HERE
    
    Script for installing a Greenlight 3.x standalone server in under 15 minutes. It also supports upgrading an existing installation of Greenlight 3.x on replay.
    
    USAGE:
        wget -qO- https://raw.githubusercontent.com/bigbluebutton/greenlight/master/gl-install.sh | bash -s -- [OPTIONS]
    
    OPTIONS (install Greenlight):
    
      -s <hostname>          Configure server with <hostname> (Required)
    
      -e <email>             Email for Let's Encrypt certbot (Required, if -d is omitted)
                              * Cannot be used when -d is used.
    
      -b <hostname>:<secret> The BigBlueButton server to be used that is accessible on <hostname> with secret <secret> (Optional)
                              * If omitted, defaults to our public BigBlueButton testing server, use it for testing purposes ONLY, DO NOT use for production!
    
      -d                     Skip SSL certificates generation (Required, if -e is omitted).
                              * Used to provide certificate files skipping the auto generation using Let's encrypt. 
                                Certificate files to be used must be named fullchain.pem and privkey.pem and must be placed in /local/certs/.
                              * Cannot be used when -e is used.
    
      -k                     Setup Keycloak 20.0 on the system (Optional)
      
      -h                     Print help
    
    VARIABLES (configure Greenlight):
      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 Greenlight 3.x server with a publicly signed (by Let's encrypt) SSL certificate for a FQDN of www.example.com and an email
    of info@example.com that uses a BigBlueButton server at bbb.example.com with secret SECRET: 
    
        -s www.example.com -e info@example.com -b bbb.example.com:SECRET
    
    Sample options for setup a Greenlight 3.x server with pre-owned SSL certificates for a FQDN of www.example.com that uses a BigBlueButton server at bbb.example.com with secret SECRET: 
    
        -s www.example.com -b bbb.example.com:SECRET -d
    
    SUPPORT:
             Community: https://groups.google.com/g/bigbluebutton-greenlight
             Source: https://github.com/bigbluebutton/greenlight-run
             Docs: https://docs.bigbluebutton.org/greenlight/v3/install
    
    HERE
    }
    
    main() {
      export DEBIAN_FRONTEND=noninteractive
      LETS_ENCRYPT_OPTIONS="--webroot --non-interactive"
      SOURCES_FETCHED=false
      GL3_DIR=~/greenlight-v3
      ACCESS_LOG_DEST=/var/log/nginx
      NGINX_FILES_DEST=/etc/greenlight/nginx
      ASSETS_DEST=/var/www/greenlight-default/assets
      EXIT_CODE=0
    
      # Eager checks and assertions.
      check_root
      check_ubuntu_lts
      need_x64
    
      while builtin getopts "s:e:b:hdk" opt "${@}"; do
    
        case $opt in
          h)
            usage
            exit 0
            ;;
    
          s)
            HOST=$OPTARG
            if [ "$HOST" == "bbb.example.com" ]; then 
              err "You must specify a valid FQDN (not the FQDN given in the docs)."
            fi
            ;;
          e)
            EMAIL=$OPTARG
            if [ "$EMAIL" == "info@example.com" ]; then 
              err "You must specify a valid email address (not the email in the docs)."
            fi
            ;;
          b)
            BIGBLUEBUTTON=$OPTARG
            if [ "$BIGBLUEBUTTON" == "bbb.example.com:SECRET" ]; then 
              err "You must use a valid BigBlueButton server (not the one in the example)."
            fi
    
            if [[ ! $BIGBLUEBUTTON =~ .+:.+ ]]; then
              err "You must respect the format <hostname>:<secret> when specifying your BigBlueButton server."
            fi
    
            IFS=: BIGBLUEBUTTON=($BIGBLUEBUTTON) IFS=' ' # Making BIGBLUEBUTTON an array, first element is the BBB hostname and the second is the BBB secret.
            ;;
          d)
            PROVIDED_CERTIFICATE=true
            ;;      
          k)
            INSTALL_KC=true
            ;;      
          :)
            err "Missing option argument for -$OPTARG"
            ;;
    
          \?)
            err "Invalid option: -$OPTARG"
            ;;
        esac
      done
    
      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
    
      check_env # Meeting requirements.
    
      say "Environment checks passed, installing/upgrading Greenlight!"
    
      apt-get update
      apt-get -y -o Dpkg::Options::="--force-confdef" -o Dpkg::Options::="--force-confnew" dist-upgrade
      
      install_ssl
      install_greenlight_v3
    
      apt-get auto-remove -y
      say "DONE ^^"
    
      return $EXIT_CODE
    }
    
    check_env() {
      # Required ARGS
      if [ -z "$HOST" ]; then
        err "Missing required ARG, You must provide the -s <FQDN>; FQDN must point to this system public IP that Greenlight will be accessible through."
      fi
    
      if [ -n "$PROVIDED_CERTIFICATE" ] && [ -n "$EMAIL" ]; then
        err "Illegal usage of options, either use -d to provide your already generated certificates OR use -e <EMAIL> to have this script generate one for you."
      fi
    
      if [ -z "$PROVIDED_CERTIFICATE" ] && [ -z "$EMAIL" ]; then
        err "Missing required ARG, You must provide the -e <EMAIL> to auto generate a certificate OR use -d to include your own files skipping issuing them by the script."
      fi
    
      local bbb_detected_err="This deployment installs Greenlight without BigBlueButton if planning to install both on the same system then please follow https://github.com/bigbluebutton/bbb-install instead."
    
      # Detecting BBB on the system
      if [ "${BIGBLUEBUTTON[0]}" == "$HOST" ]; then
        say "Your FQDN match that of the BigBlueButton server to be used, are you willing to install Greenlight with BigBlueButton on this system?"
        err "$bbb_detected_err."
      fi
    
      if dpkg -l | grep -q bbb; then
        say "BigBlueButton modules has been detected on this system!" 
        err "$bbb_detected_err."
      fi
    
      # Possible conflicts on setup.
      if [ ! -f /etc/nginx/sites-available/greenlight ]; then
        # Conflict detection of existent nginx on the system not installed by this script (possible collision with other applications).
        if dpkg -s nginx 1> /dev/null 2>&1; then
          say "Nginx is already installed on this system by another mean, this deployment may impact your workload!"
          err "Remove and cleanup nginx configurations on this system OR kindly consider using a clean enviroment before proceeding."
        fi
    
        # Conflict detection of required ports being already in use.
        if check_ports_listen ':80$|:443$|:5050$|:5151$'; then
          say "Some required ports are already in use by another application!"
          err "Make sure to clear out the required ports (TCP 80, 443, 5050, 5151) if possible OR kindly consider using a clean enviroment before proceeding."
        fi
      fi
    
      check_host "$HOST"
    }
    
    say() {
      echo "gl-install: $1"
    }
    
    err() {
      say "$1" >&2
      exit 1
    }
    
    warn() {
      say "$1" >&2
      EXIT_CODE=1
    }
    
    check_root() {
      if [ $EUID != 0 ]; then err "You must run this command as root."; fi
    }
    
    check_ubuntu_lts() {
      lsb_release -i | grep -iq ubuntu || err "You must run this command on Ubuntu server."
      RELEASE=$(lsb_release -r | sed 's/^[^0-9]*//g')
      [ "$RELEASE" == "20.04" ] || [ "$RELEASE" == "22.04" ]  || err "You must run this command on Ubuntu version 20.04 or 22.04 LTS."
    }
    
    need_x64() {
      UNAME=`uname -m`
      if [ "$UNAME" != "x86_64" ]; then err "You must run this command on a 64-bit server."; fi
    }
    
    wait_443() {
      check_ports_clearing ':443$' && say "Waiting for port 443 to clear "
    
      while check_ports_clearing ':443$'; do sleep 1; echo -n '.'; done
      echo
    }
    
    check_ports_listen() {
      local pattern=${1:-':80$|:443$'}
    
      ss -lnt | awk '{print $4}' | egrep -q "$pattern"
    }
    
    check_ports_clearing() {
      local pattern=${1:-':80$|:443$'}
    
      ss -ant | grep TIME-WAIT | awk '{print $4}' | egrep -q "$pattern"
    }
    
    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
    }
    
    check_host() {
        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 this system IP $IP."; fi
    }
    
    # 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/v3/install
    install_greenlight_v3(){
      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..."
    
      # 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=$(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=$(docker run --rm --entrypoint bundle $GL_IMG_REPO exec rake 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
    
      if [ -n "$BIGBLUEBUTTON" ]; then
        # BigBlueButton server configuration.
        local BIGBLUEBUTTON_ENDPOINT="https://${BIGBLUEBUTTON[0]}/bigbluebutton/api"
        local BIGBLUEBUTTON_SECRET=${BIGBLUEBUTTON[1]}
    
        # Re-configure a new BBB server to be used by Greenlight. 
        sed -i "s|^[# \t]*BIGBLUEBUTTON_ENDPOINT=.*|BIGBLUEBUTTON_ENDPOINT=$BIGBLUEBUTTON_ENDPOINT|" $GL3_DIR/.env
        sed -i "s|^[# \t]*BIGBLUEBUTTON_SECRET=.*|BIGBLUEBUTTON_SECRET=$BIGBLUEBUTTON_SECRET|"  $GL3_DIR/.env
      else
        # Demo BigBlueButton server configuration.
        local BIGBLUEBUTTON_ENDPOINT="https://test-install.blindsidenetworks.com/bigbluebutton/api"
        local BIGBLUEBUTTON_SECRET=8cd8ef52e8e101574e400365b55e11a6
    
        # The demo BBB server should be used when not specifying a dedicated one on installation only (no overwriting).
        sed -i "s|^[# \t]*BIGBLUEBUTTON_ENDPOINT=[ \t]*$|BIGBLUEBUTTON_ENDPOINT=$BIGBLUEBUTTON_ENDPOINT|" $GL3_DIR/.env # Do not overwrite the value if not empty.
        sed -i "s|^[# \t]*BIGBLUEBUTTON_SECRET=[ \t]*$|BIGBLUEBUTTON_SECRET=$BIGBLUEBUTTON_SECRET|"  $GL3_DIR/.env # Do not overwrite the value if not empty.
      fi
    
      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"
    
      # 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
    
      # 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=$(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;
    }
    
    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;
    }
    
    install_ssl() {
      # Assertions for fresh installations
      if [ ! -f /etc/nginx/sites-available/greenlight ]; then
        if [ -d "/etc/letsencrypt/live/$HOST" ]; then
          err "Unable to manage certificates for $HOST, /etc/letsencrypt/live/$HOST/ already exists."
        fi
      fi
    
      if [ -n "$PROVIDED_CERTIFICATE" ]; then
        if [ ! -f /local/certs/fullchain.pem ] || [ ! -f /local/certs/privkey.pem ]; then
          err "Unable to find your provided certificate files in /local/certs, Have you placed the full chain and private key for your certificate as expected?"
        fi
    
        # Detecting generated certs and possible conflicts.
        if [ -f /etc/letsencrypt/live/$HOST/fullchain.pem ]; then
          if [ ! "$(readlink -e /etc/letsencrypt/live/$HOST/fullchain.pem)" == /local/certs/fullchain.pem ]; then
            err "fullchain.pem was probably generated and not provided by this script, exiting to avoid conflict."
          fi
        fi
    
        if [ -f /etc/letsencrypt/live/$HOST/privkey.pem ]; then
          if [ ! "$(readlink -e /etc/letsencrypt/live/$HOST/privkey.pem)" == /local/certs/privkey.pem ]; then
            err "privkey.pem was probably generated and not provided by this script, exiting to avoid conflict."
          fi
        fi
      else
        # Detecting provided certs and possible conflicts.
        if [ -f /etc/letsencrypt/live/$HOST/fullchain.pem ]; then
          if [[ ! "$(readlink -e /etc/letsencrypt/live/$HOST/fullchain.pem)" =~ ^/etc/letsencrypt/archive/$HOST.*/fullchain.*\.pem$ ]]; then
            err "fullchain.pem was probably provided and not generated by this script, exiting to avoid conflict."
          fi
          local GENERATED_CERTS_EXIST=true
        fi
    
        if [ -f /etc/letsencrypt/live/$HOST/privkey.pem ]; then
          if [[ ! "$(readlink -e /etc/letsencrypt/live/$HOST/privkey.pem)" =~ ^/etc/letsencrypt/archive/$HOST.*/privkey.*\.pem$ ]]; then
            err "privkey.pem was probably provided and not generated by this script, exiting to avoid conflict."
          fi
        fi
    
        need_pkg certbot
      fi
    
      need_pkg nginx
      mkdir -p /etc/nginx/ssl $ACCESS_LOG_DEST $NGINX_FILES_DEST $ASSETS_DEST
    
      cp -v /etc/nginx/sites-available/greenlight /etc/nginx/sites-available/greenlight.old # Preserve older config for admins.
    
      # HTTP only
      # Updating HTTP config.
      cat <<HERE > /etc/nginx/sites-available/greenlight
    server_tokens off;
    server {
      listen 80;
      listen [::]:80;
      server_name $HOST;
    
      access_log  $ACCESS_LOG_DEST/greenlight.access.log;
    
      # Greenlight landing page.
      location / {
        root   $ASSETS_DEST;
        try_files \$uri @bbb-fe;
      }
    
      include $NGINX_FILES_DEST/*.nginx;
    }
    
    HERE
    
      if [ ! -f /etc/nginx/sites-enabled/greenlight ]; then
        ln -sf /etc/nginx/sites-available/greenlight /etc/nginx/sites-enabled/greenlight # Activate greenlight nginx config.
      fi
    
      if [ -f /etc/nginx/sites-enabled/default ]; then
        rm -v /etc/nginx/sites-enabled/default # Remove nginx default config.
      fi
    
      # Validating the config.
      nginx -qt || warn "Unable to configure nginx - if following the official guides then please contact the maintainers."
      systemctl restart nginx
    
    # Enabling HTTPS.
      cp -v /etc/nginx/sites-available/greenlight /etc/nginx/sites-available/greenlight.http # Preserve valid HTTP config for admins.
    
      if [ -n "$PROVIDED_CERTIFICATE" ]; then
          say "Providing SSL certificates for $HOST..."
          mkdir -p "/etc/letsencrypt/live/$HOST" && say "Created $HOST live directory"
          ln -sf /local/certs/fullchain.pem "/etc/letsencrypt/live/$HOST/fullchain.pem" && say "fullchain.pem found and placed"
          ln -sf /local/certs/privkey.pem "/etc/letsencrypt/live/$HOST/privkey.pem" && say "privkey.pem found and placed"
      else
        # Auto generate a standalone SSL x509 certificate publicly signed by Let's encrypt for this domain $HOST.
        if [ -n "$GENERATED_CERTS_EXIST" ]; then
          say "Checking certificates of $HOST for renewal..."
          if certbot --email "$EMAIL" --agree-tos --rsa-key-size 4096 -w $ASSETS_DEST \
                -d "$HOST" --deploy-hook "systemctl reload nginx" $LETS_ENCRYPT_OPTIONS certonly; then
            say "Renewal checks passed!"
          else
            warn "Something went wrong when attempting to renew certificates!"
          fi
        else
          say "Generating certificates for $HOST..."
          say "Rehearsal phase..."
          if certbot --staging --email "$EMAIL" --agree-tos --rsa-key-size 4096 -w $ASSETS_DEST \
              -d "$HOST" --deploy-hook "systemctl reload nginx" $LETS_ENCRYPT_OPTIONS certonly; then
            say "Generating SSL certificates for $HOST..."
            say "Rehearsal passed, ready to issue production certificates!"
            say "Issuing production certificates..."
            if certbot --force-renewal --email "$EMAIL" --agree-tos --rsa-key-size 4096 -w $ASSETS_DEST \
                -d "$HOST" --deploy-hook "systemctl reload nginx" $LETS_ENCRYPT_OPTIONS certonly; then
              say "Production SSL certificates has been generated!"
            else
              warn "Something went wrong when generating production certificates"
            fi
          else
            warn "Unable to pass the rehearsal phase, avoided generating production certificates to keep rates under limit."
          fi
        fi
      fi
    
      say "Generating DH key exchange parameters..."
      cp -v /etc/nginx/ssl/dhp-4096.pem /etc/nginx/ssl/dhp-4096.pem.old
    
      # For security reasons upon upgrade, the Diffie Hellman key exchange parameters will rotate.
      if ! openssl dhparam -dsaparam  -out /etc/nginx/ssl/dhp-4096.pem 4096; then
        warn "Unable to generate DH key exchange parameters - rolling back..."
        mv -v /etc/nginx/ssl/dhp-4096.pem.old /etc/nginx/ssl/dhp-4096.pem || warn "Unable to generate new DH key exchage parameters nor to recover."
      else
        say "DH key exchange parameters was generated!"
      fi
    
      say "Configuring nginx with SSL enabled..."
    
      # Updating HTTPS config.
      cat <<HERE > /etc/nginx/sites-available/greenlight
    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  $ACCESS_LOG_DEST/greenlight.access.log;
    
      # Greenlight landing page.
      location / {
        root   $ASSETS_DEST;
        try_files \$uri @bbb-fe;
      }
    
      include $NGINX_FILES_DEST/*.nginx;
    }
    
    HERE
    
      # Validating new config
      if ! nginx -qt; then
        # Rollback logic
        warn "Something went wrong configuring nginx - attempting to recover..."
    
        mv -v /etc/nginx/sites-available/greenlight /etc/nginx/sites-available/greenlight.https # Preserve used HTTPS config for admins.
    
        if mv -v /etc/nginx/sites-available/greenlight.old /etc/nginx/sites-available/greenlight; then
          warn "Fallen back to previous configuration!"
        else
          cp -v /etc/nginx/sites-available/greenlight.http /etc/nginx/sites-available/greenlight # Preserve used HTTP config for admins while falling back to HTTP. 
          warn "No previous configuration was found - fallen back to http configuration!"
        fi
        
        systemctl restart nginx
    
        warn "Unable to configure nginx with certificates, retry to resolve."
    
        return 1
      fi
    
      say "Nginx was configured successuflly with SSL enabled!"
      systemctl restart nginx && say "Nginx is UP!"
    
      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;
    }
    
    install_docker() {
      apt-get remove --purge -y docker docker-engine docker.io containerd runc
      need_pkg ca-certificates curl gnupg lsb-release
    
      # Install Docker
      if ! apt-key list | grep -iq docker; then
        curl -fsSL https://download.docker.com/linux/ubuntu/gpg | gpg --dearmor --yes -o /etc/apt/trusted.gpg.d/docker.gpg || err "Something went wrong adding docker gpg key - exiting"
      fi
    
      if ! dpkg -l | grep -iq docker-ce; then
        echo \
          "deb [ arch=amd64 ] https://download.docker.com/linux/ubuntu \
          $(lsb_release -cs) stable" | tee /etc/apt/sources.list.d/docker.list > /dev/null
    
        chmod a+r /etc/apt/trusted.gpg.d/docker.gpg
    
        apt-get update
        need_pkg docker-ce docker-ce-cli containerd.io docker-compose-plugin
      fi
    
      if ! which docker; then err "Docker did not install"; fi
    
      # Purge older docker compose if exists.
      # DEPRECATED
      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 ! docker version > /dev/null ; then
        warn "Docker is failing, restarting it..."
        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"
      fi
    
      say "docker is running!"
      return 0;
    }
    
    main "$@" || exit 1