diff --git a/.env.sample b/.env.sample index 31d6c9a..1037f0c 100644 --- a/.env.sample +++ b/.env.sample @@ -16,6 +16,7 @@ SECRET_SHARED_SECRET_VERSION=v1 SECRET_RAILS_SECRET_VERSION=v1 SECRET_TURN_SECRET_VERSION=v1 SECRET_FSESL_PASSWORD_VERSION=v1 +SECRET_HASURA_ADMIN_SECRET_VERSION=v1 ETHERPAD_API_KEY_VERSION=v1 ENTRYPOINT_GREENLIGHT_VERSION=v1 @@ -25,12 +26,20 @@ BBBHTML5_CONF_VERSION=v1 ENTRYPOINT_APPSAKKA_VERSION=v1 ENTRYPOINT_FSESLAKKA_VERSION=v1 ENTRYPOINT_WEBRTCSFU_VERSION=v1 -MONGOD_CONF_VERSION=v1 -INIT_REPLICA_VERSION=v1 +ENTRYPOINT_GRAPHQLSERVER_VERSION=v1 +INITDB_SH_VERSION=v1 ENTRYPOINT_BBBPADS_VERSION=v1 ENTRYPOINT_ETHERPAD_VERSION=v1 ENTRYPOINT_FREESWITCH_VERSION=v1 ENTRYPOINT_BBBWEB_VERSION=v1 +WS_CONF_VERSION=v1 +WN_CONF_VERSION=v1 +WSN_CONF_VERSION=v1 +NGINX_CONF_VERSION=v1 +BRF_CONF_VERSION=v1 +BHDN_CONF_VERSION=v1 +BEG_CONF_VERSION=v1 +AA_CONF_VERSION=v1 # ==================================== # ADDITIONS to BigBlueButton diff --git a/compose-original.yml b/compose-original.yml.old similarity index 99% rename from compose-original.yml rename to compose-original.yml.old index 9dbf617..0be0804 100644 --- a/compose-original.yml +++ b/compose-original.yml.old @@ -431,6 +431,7 @@ services: extra_hosts: - "redis:10.7.7.5" + # HAPROXY NOT NECESSARY FOR CO-OP CLOUD haproxy: build: mod/haproxy image: alangecker/bbb-haproxy:3.0.9 diff --git a/compose.yml b/compose.yml index 216f8a2..92c42bf 100644 --- a/compose.yml +++ b/compose.yml @@ -3,7 +3,7 @@ version: '3.8' services: bbb-web: - image: alangecker/bbb-docker-web:v3.0.4 + image: alangecker/bbb-docker-web:v3.0.19 depends_on: - redis - etherpad @@ -24,6 +24,7 @@ services: # SHARED_SECRET is set via entrypoint.bbb-web.sh # TURN_SECRET is set via entrypoint.bbb-web.sh ENABLE_LEARNING_DASHBOARD: ${ENABLE_LEARNING_DASHBOARD:-true} + COLLABORA_URL: ${COLLABORA_URL:-https://collabora:9980/cool} # NUMBER_OF_BACKEND_NODEJS_PROCESSES: 2 # look for containers? configs: - source: entrypoint_bbbweb @@ -37,13 +38,11 @@ services: - bigbluebutton:/var/bigbluebutton - vol-freeswitch:/var/freeswitch/meetings networks: - bbb-net: - aliases: - - bbb-web + - internal # freeswitch (audio input streaming and telco dial-in support) freeswitch: - image: alangecker/bbb-docker-freeswitch:v1.10.12-v3.0.4 + image: alangecker/bbb-docker-freeswitch:v1.10.12-v3.0.19 cap_add: - IPC_LOCK - NET_ADMIN @@ -73,9 +72,7 @@ services: - vol-freeswitch:/var/freeswitch/meetings # network_mode: host networks: - bbb-net: - aliases: - - freeswitch + - internal logging: # reduce logs to a minimum, so `docker compose logs -f` still works driver: "local" @@ -86,16 +83,42 @@ services: # nginx (webserver gateway for a bunch of things) nginx: - image: alangecker/bbb-docker-nginx:v3.0.4-v5.3.1-1.25 + image: alangecker/bbb-docker-nginx:v3.0.19-v5.4.3-1.29 volumes: - bigbluebutton:/var/bigbluebutton - ${DEFAULT_PRESENTATION:-/dev/null}:/www/default.pdf tmpfs: - /tmp + configs: + - source: nginx_conf + target: /etc/nginx/nginx.conf + mode: 0555 + - source: beg_conf + target: /etc/nginx/bbb/bbb-exporter.nginx + mode: 0555 + - source: bhdn_conf + target: /etc/nginx/bbb/bbb-html5.dev.nginx + mode: 0555 + - source: wn_conf + target: /etc/nginx/bbb/webhooks.nginx + mode: 0555 + - source: wsn_conf + target: /etc/nginx/bbb/webrtc-sfu.nginx + mode: 0555 networks: - bbb-net: - aliases: - - nginx + - proxy + - internal + deploy: + labels: + - "traefik.enable=true" + - "traefik.docker.network=proxy" + - "traefik.http.services.${STACK_NAME}.loadbalancer.server.port=80" + - "traefik.http.routers.${STACK_NAME}.rule=Host(`${DOMAIN}`${EXTRA_DOMAINS})" + - "traefik.http.routers.${STACK_NAME}.tls.certresolver=${LETS_ENCRYPT_ENV}" + - "traefik.http.routers.${STACK_NAME}.entrypoints=web-secure" + - "traefik.http.routers.${STACK_NAME}.middlewares=${STACK_NAME}-redirect" + - "traefik.http.middlewares.${STACK_NAME}-redirect.redirectscheme.scheme=https" + - "traefik.http.middlewares.${STACK_NAME}-redirect.redirectscheme.permanent=true" # extra_hosts: # - "host.docker.internal:10.7.7.1" # - "bbb-web:10.7.7.2" @@ -107,11 +130,12 @@ services: # etherpad (collaborative notepad) etherpad: - image: alangecker/bbb-docker-etherpad:2.2.7-s8328b77-p88f3f6b + image: alangecker/bbb-docker-etherpad:2.4.2-s8328b77-p88f3f6b depends_on: - redis - collabora - # environment: + environment: + COLLABORA_URL: ${COLLABORA_URL:-https://collabora:9980/cool} # ETHERPAD_API_KEY is set via entrypoint configs: - source: entrypoint_etherpad @@ -121,13 +145,11 @@ services: - etherpad_api_key entrypoint: /usr/local/bin/docker-entrypoint.sh networks: - bbb-net: - aliases: - - etherpad + - internal # bbb-pads (bbb etherpad integration) bbb-pads: - image: alangecker/bbb-docker-pads:v1.5.3 + image: alangecker/bbb-docker-pads:v1.5.6 depends_on: - redis - etherpad @@ -141,25 +163,21 @@ services: - etherpad_api_key entrypoint: /usr/local/bin/docker-entrypoint.sh networks: - bbb-net: - aliases: - - bbb-pads + - internal # redis (caching) redis: - image: redis:7.2-alpine + image: redis:8.4-alpine #healthcheck: # test: ["CMD", "redis-cli", "ping"] # interval: 1s # timeout: 3s # retries: 30 networks: - bbb-net: - aliases: - - redis + - internal bbb-export-annotations: - image: alangecker/bbb-docker-bbb-export-annotations:v3.0.4 + image: alangecker/bbb-docker-bbb-export-annotations:v3.0.19 depends_on: - redis - etherpad @@ -169,9 +187,7 @@ services: # https://github.com/bigbluebutton/bigbluebutton/blob/v2.7.0/bbb-export-annotations/config/settings.json # "bbbWebAPI": "http://127.0.0.1:8090", -> bbb-web # "bbbPadsAPI": "http://127.0.0.1:9002", -> bbb-pads - bbb-net: - aliases: - - bbb-export-annotations + - internal volumes: - bigbluebutton:/var/bigbluebutton tmpfs: @@ -179,17 +195,24 @@ services: # webrtc-sfu (webrtc controller for negotiation and media stream) webrtc-sfu: - image: alangecker/bbb-docker-webrtc-sfu:v2.17.0-beta.6 + image: alangecker/bbb-docker-webrtc-sfu:v2.21.2 depends_on: - redis - freeswitch + ports: + # mediasoup UDP port range — must match mediasoup.worker.rtcMinPort/rtcMaxPort in webrtc-sfu-config.yaml + - "24577-24676:24577-24676/udp" environment: # ESL_PASSWORD is set via entrypoint.webrtc-sfu.sh - MS_WEBRTC_LISTEN_IPS: '[{"ip":"${EXTERNAL_IPv4}", "announcedIp":"${EXTERNAL_IPv4}"}]' + # ip must be 0.0.0.0 (container can't bind to external host IP) + MS_WEBRTC_LISTEN_IPS: '[{"ip":"0.0.0.0", "announcedIp":"${EXTERNAL_IPv4}"}]' configs: - source: entrypoint_webrtcsfu target: /usr/local/bin/docker-entrypoint.sh mode: 0555 + - source: ws_conf + target: /etc/bigbluebutton/bbb-webrtc-sfu/production.yml + mode: 0555 entrypoint: /usr/local/bin/docker-entrypoint.sh volumes: - vol-mediasoup:/var/mediasoup @@ -197,16 +220,19 @@ services: - /var/log/bbb-webrtc-sfu secrets: - fsesl_password - # network_mode: host + # network_mode: host would be needed for mediasoup UDP to work from outside; + # keeping on overlay network for now so other services can reach it by name. # security_opt: # - seccomp:unconfined # allow io_uring access for mediasoup ulimits: memlock: -1 # allow io_uring_register_buffers to allocate enough ram + networks: + - internal # fsesl-akka (bbb freeswitch integration) fsesl-akka: - image: alangecker/bbb-docker-fsesl-akka:v3.0.4 + image: alangecker/bbb-docker-fsesl-akka:v3.0.19 depends_on: - redis - freeswitch @@ -220,13 +246,11 @@ services: - fsesl_password entrypoint: /usr/local/bin/docker-entrypoint.sh networks: - bbb-net: - aliases: - - fsesl-akka + - internal # apps-akka (seems to be an bbb app integration endpoint) apps-akka: - image: alangecker/bbb-docker-apps-akka:v3.0.4 + image: alangecker/bbb-docker-apps-akka:v3.0.19 depends_on: - redis environment: @@ -241,17 +265,18 @@ services: - source: bbbhtml5_conf target: /etc/bigbluebutton/bbb-html5.yml mode: 0555 + - source: aa_conf + target: /etc/bigbluebutton/mod/bbb-apps-akka.conf.tmpl + mode: 0555 secrets: - shared_secret - postgres_password entrypoint: /usr/local/bin/docker-entrypoint.sh networks: - bbb-net: - aliases: - - apps-akka + - internal bbb-graphql-server: - image: alangecker/bbb-docker-graphql-server:v3.0.4 + image: alangecker/bbb-docker-graphql-server:v3.0.19 depends_on: - postgres - bbb-web @@ -261,34 +286,36 @@ services: - postgres_password environment: POSTGRES_USER: postgres - POSTGRES_PASSWORD: ${POSTGRESQL_SECRET:-test} - HASURA_GRAPHQL_ADMIN_SECRET: TODO_CHANGE_ME + # POSTGRES_PASSWORD and HASURA_GRAPHQL_ADMIN_SECRET are set via entrypoint + configs: + - source: entrypoint_graphqlserver + target: /usr/local/bin/docker-entrypoint.sh + mode: 0555 + entrypoint: /usr/local/bin/docker-entrypoint.sh + command: ["graphql-engine", "serve"] + secrets: + - postgres_password + - hasura_admin_secret networks: - bbb-net: - aliases: - - bbb-graphql-server + - internal bbb-graphql-actions: - image: alangecker/bbb-docker-graphql-actions:v3.0.4 + image: alangecker/bbb-docker-graphql-actions:v3.0.19 depends_on: - redis - apps-akka networks: - bbb-net: - aliases: - - bbb-graphql-actions + - internal bbb-graphql-middleware: - image: alangecker/bbb-docker-graphql-middleware:v3.0.4 + image: alangecker/bbb-docker-graphql-middleware:v3.0.19 depends_on: - bbb-graphql-server - bbb-graphql-actions - bbb-web - redis networks: - bbb-net: - aliases: - - bbb-graphql-middleware + - internal # extra_hosts: # - "nginx:10.7.7.1" @@ -297,9 +324,7 @@ services: tmpfs: - /tmp networks: - bbb-net: - aliases: - - collabora + - internal # disable logging (way to verbose) logging: driver: none @@ -308,7 +333,7 @@ services: periodic: image: alangecker/bbb-docker-periodic:v3.0.0 depends_on: - - mongodb + - postgres volumes: - /var/run/docker.sock:/var/run/docker.sock # not sure what prupose this has? - bigbluebutton:/var/bigbluebutton # access bbb data @@ -320,32 +345,23 @@ services: REMOVE_OLD_RECORDING: ${REMOVE_OLD_RECORDING} RECORDING_MAX_AGE_DAYS: ${RECORDING_MAX_AGE_DAYS} networks: - bbb-net: - aliases: - - periodic + - internal bbb-webrtc-recorder: - image: alangecker/bbb-docker-webrtc-recorder:v0.8.0 + image: alangecker/bbb-docker-webrtc-recorder:v0.13.1 depends_on: - redis - volumes: - - webrtc-recorder-data:/var/lib/bbb-webrtc-recorder - # WebRTC connection to bbb-webrtc-sfu seem to - # only to work via the external IP - # extra_hosts: - # - "redis:10.7.7.5" + volumes: + - vol-webrtc-recorder:/var/lib/bbb-webrtc-recorder networks: - bbb-net: - aliases: - - bbb-webrtc-recorder + - internal - # coturn + # coturn (TURN server — must use host networking for UDP relay to reach clients) coturn: - image: coturn/coturn:4.6-alpine + image: coturn/coturn:4.8-alpine command: - "--external-ip=${EXTERNAL_IPv4}/${EXTERNAL_IPv4}" - "--external-ip=${EXTERNAL_IPv6:-::1}/${EXTERNAL_IPv6:-::1}" - - "--static-auth-secret=${TURN_SECRET}" - "--allowed-peer-ip=${EXTERNAL_IPv4}" - "--relay-ip=${EXTERNAL_IPv4}" - "--relay-ip=${EXTERNAL_IPv6:-::1}" @@ -362,13 +378,14 @@ services: volumes: - "/var/home/thebastard/.abra/recipes/bigbluebutton/cert/cert.pem:/tmp/cert.pem:ro" - "/var/home/thebastard/.abra/recipes/bigbluebutton/cert/key.pem:/tmp/key.pem:ro" - # network_mode: host + # network_mode: host <- mine has this commented out + network_mode: host user: root entrypoint: /usr/local/bin/docker-entrypoint.sh # greenlight greenlight: - image: bigbluebutton/greenlight:v3.5.0 + image: bigbluebutton/greenlight:v3.6.3 depends_on: - postgres - redis @@ -391,39 +408,37 @@ services: - rails_secret entrypoint: /usr/local/bin/docker-entrypoint.sh networks: - bbb-net: - aliases: - - greenlight + - internal - # postgres for greenlight + # postgres for greenlight postgres: image: postgres:16-alpine environment: - POSTGRES_DB: greenlight-v3 + POSTGRES_MULTIPLE_DATABASES: bbb_graphql,hasura_app,greenlight POSTGRES_USER: postgres POSTGRES_PASSWORD_FILE: /run/secrets/postgres_password - #healthcheck: - #test: ["CMD-SHELL", "pg_isready -U postgres"] - #interval: 10s - #timeout: 5s - #retries: 5 + healthcheck: + test: ["CMD-SHELL", "pg_isready -U postgres"] + interval: 10s + timeout: 5s + retries: 5 volumes: - "postgres_data:/var/lib/postgresql/data" + configs: + - source: initdb_sh + target: /docker-entrypoint-initdb.d/initdb.sh + mode: 0555 secrets: - postgres_password networks: - bbb-net: - aliases: - - postgres + - internal netshoot: image: nicolaka/netshoot entrypoint: /bin/bash command: -c "sleep infinity" networks: - bbb-net: - aliases: - - netshoot + - internal volumes: greenlight_data: @@ -431,7 +446,7 @@ volumes: bigbluebutton: vol-mediasoup: vol-freeswitch: - webrtc-recorder-data: + vol-webrtc-recorder: configs: entrypoint_greenlight: @@ -455,12 +470,12 @@ configs: entrypoint_webrtcsfu: name: ${STACK_NAME}_entrypoint_webrtcsfu_${ENTRYPOINT_WEBRTCSFU_VERSION} file: ./entrypoint.webrtc-sfu.sh - mongod_conf: - name: ${STACK_NAME}_mongod_conf_${MONGOD_CONF_VERSION} - file: ./mongod.conf - init_replica: - name: ${STACK_NAME}_init_replica_${INIT_REPLICA_VERSION} - file: ./init-replica.sh + entrypoint_graphqlserver: + name: ${STACK_NAME}_entrypoint_graphqlserver_${ENTRYPOINT_GRAPHQLSERVER_VERSION} + file: ./entrypoint.bbb-graphql-server.sh + initdb_sh: + name: ${STACK_NAME}_initdb_sh_${INITDB_SH_VERSION} + file: ./mod/initdb.sh entrypoint_bbbpads: name: ${STACK_NAME}_entrypoint_bbbpads_${ENTRYPOINT_BBBPADS_VERSION} file: ./entrypoint.bbb-pads.sh @@ -473,6 +488,30 @@ configs: entrypoint_bbbweb: name: ${STACK_NAME}_entrypoint_bbbweb_${ENTRYPOINT_BBBWEB_VERSION} file: ./entrypoint.bbb-web.sh + aa_conf: + name: ${STACK_NAME}_aa_conf_${AA_CONF_VERSION} + file: ./mod/bbb-apps-akka.conf + beg_conf: + name: ${STACK_NAME}_beg_conf_${BEG_CONF_VERSION} + file: ./mod/bbb-exporter.nginx + bhdn_conf: + name: ${STACK_NAME}_bhdn_conf_${BHDN_CONF_VERSION} + file: ./mod/bbb-html5.dev.nginx + brf_conf: + name: ${STACK_NAME}_brf_conf_${BRF_CONF_VERSION} + file: ./mod/bbb-resync-freeswitch + nginx_conf: + name: ${STACK_NAME}_nginx_conf_${NGINX_CONF_VERSION} + file: ./mod/nginx.conf + wn_conf: + name: ${STACK_NAME}_wn_conf_${WN_CONF_VERSION} + file: ./mod/webhooks.nginx + wsn_conf: + name: ${STACK_NAME}_wsn_conf_${WSN_CONF_VERSION} + file: ./mod/webrtc-sfu.nginx + ws_conf: + name: ${STACK_NAME}_ws_conf_${WS_CONF_VERSION} + file: ./mod/webrtc-sfu-config.yaml secrets: postgres_password: @@ -493,10 +532,11 @@ secrets: etherpad_api_key: external: true name: ${STACK_NAME}_etherpad_api_key_${ETHERPAD_API_KEY_VERSION} + hasura_admin_secret: + external: true + name: ${STACK_NAME}_hasura_admin_secret_${SECRET_HASURA_ADMIN_SECRET_VERSION} networks: - bbb-net: - ipam: - driver: default - config: - - subnet: "10.7.7.0/24" + proxy: + external: true + internal: \ No newline at end of file diff --git a/entrypoint.bbb-graphql-server.sh b/entrypoint.bbb-graphql-server.sh new file mode 100644 index 0000000..fad2e3b --- /dev/null +++ b/entrypoint.bbb-graphql-server.sh @@ -0,0 +1,34 @@ +#!/bin/sh +set -e + +# set POSTGRES_PASSWORD from Docker secret +if test -f "/run/secrets/postgres_password"; then + pwd=$(cat /run/secrets/postgres_password) + if [ -z "$pwd" ]; then + echo >&2 "error: /run/secrets/postgres_password is empty" + exit 1 + fi + echo "entrypoint.bbb-graphql-server.sh setting POSTGRES_PASSWORD" + export POSTGRES_PASSWORD="${pwd}" + unset pwd +else + echo >&2 "error: /run/secrets/postgres_password does not exist" + exit 1 +fi + +# set HASURA_GRAPHQL_ADMIN_SECRET from Docker secret +if test -f "/run/secrets/hasura_admin_secret"; then + pwd=$(cat /run/secrets/hasura_admin_secret) + if [ -z "$pwd" ]; then + echo >&2 "error: /run/secrets/hasura_admin_secret is empty" + exit 1 + fi + echo "entrypoint.bbb-graphql-server.sh setting HASURA_GRAPHQL_ADMIN_SECRET" + export HASURA_GRAPHQL_ADMIN_SECRET="${pwd}" + unset pwd +else + echo >&2 "error: /run/secrets/hasura_admin_secret does not exist" + exit 1 +fi + +exec "$@" diff --git a/entrypoint.coturn.sh b/entrypoint.coturn.sh index 561c70b..b23e73d 100755 --- a/entrypoint.coturn.sh +++ b/entrypoint.coturn.sh @@ -5,8 +5,13 @@ apk add jq su-exec # create certs for proxy if [ "$ENABLE_HTTPS_PROXY" == true ]; then - while [ ! -f /etc/resty-auto-ssl/storage/file/*latest ] + while true do + for cert_file in /etc/resty-auto-ssl/storage/file/*latest; do + if [ -f "$cert_file" ]; then + break 2 + fi + done echo "ERROR: certificate doesn't exist yet." echo "Certificate gets create on the first request to the HTTPS proxy." echo "We will try again..." @@ -18,13 +23,6 @@ if [ "$ENABLE_HTTPS_PROXY" == true ]; then cat /etc/resty-auto-ssl/storage/file/*%3Alatest | jq -r '.privkey_pem' > /tmp/key.pem fi -if [ ! -f /tmp/cert.pem ] || [ ! -f /tmp/key.pem ]; then - echo "ERROR: certificate not found, but coturn relies on it." - echo "Use either auto HTTPS proxy or" - echo "provide path to certificates in .env file" - exit 1 -fi - # set turn_secret variable if test -f "/run/secrets/turn_secret"; then pwd=$(cat /run/secrets/turn_secret) diff --git a/entrypoint.greenlight.sh b/entrypoint.greenlight.sh index 86a0dbf..8510412 100644 --- a/entrypoint.greenlight.sh +++ b/entrypoint.greenlight.sh @@ -9,7 +9,7 @@ if test -f "/run/secrets/postgres_password"; then exit 1 fi echo "entrypoint.greenlight.sh setting DATABASE_URL" - export "DATABASE_URL"="postgres://postgres:${pwd}@postgres:5432/greenlight-v3" + export "DATABASE_URL"="postgres://postgres:${pwd}@postgres:5432/greenlight" unset "pwd" else echo >&2 "error: /run/secrets/postgres_password does not exist" diff --git a/init-replica.sh b/init-replica.sh deleted file mode 100755 index 188cdda..0000000 --- a/init-replica.sh +++ /dev/null @@ -1,26 +0,0 @@ -#!/bin/sh -set -e - - -host=${HOSTNAME:-$(hostname -f)} - -# shut down again -mongod --pidfilepath /tmp/docker-entrypoint-temp-mongod.pid --shutdown -# restart again binding to 0.0.0.0 to allow a replset with 10.7.7.6 -mongod --oplogSize 8 --replSet rs0 --noauth \ - --config /tmp/docker-entrypoint-temp-config.json \ - --bind_ip 0.0.0.0 --port 27017 \ - --tlsMode disabled \ - --logpath /proc/1/fd/1 --logappend \ - --pidfilepath /tmp/docker-entrypoint-temp-mongod.pid --fork - -# init replset with defaults -mongo 10.7.7.6 --eval "rs.initiate({ - _id: 'rs0', - members: [ { _id: 0, host: '10.7.7.6:27017' } ] -})" - -echo "Waiting to become a master" -echo 'while (!db.isMaster().ismaster) { sleep(100); }' | mongo - -echo "I'm the master!" \ No newline at end of file diff --git a/mod/apps-akka/Dockerfile b/mod/apps-akka/Dockerfile new file mode 100644 index 0000000..6d4bf57 --- /dev/null +++ b/mod/apps-akka/Dockerfile @@ -0,0 +1,43 @@ +ARG BBB_BUILD_TAG +FROM bigbluebutton/bbb-build:$BBB_BUILD_TAG AS builder + +COPY --from=src-common-message / /bbb-common-message + +# build bbb-common-message +RUN cd /bbb-common-message && ./deploy.sh + +# =================================================== + +ARG TAG_APPS_AKKA + +COPY --from=src-apps-akka / /source + +# compile and unzip bin +RUN cd /source \ + && sbt universal:packageBin \ + && unzip /source/target/universal/bbb-apps-akka-0.0.4.zip -d / + +# =================================================== + + +FROM bigbluebutton/bbb-build:$BBB_BUILD_TAG AS builder-settings +RUN wget -qO /usr/local/bin/yq https://github.com/mikefarah/yq/releases/download/v4.44.3/yq_linux_amd64 && chmod a+x /usr/local/bin/yq +COPY --from=src-config /settings.yml /settings.yml +ARG TAG_BBB +RUN yq e -i ".public.app.bbbServerVersion = \"$TAG_BBB\"" /settings.yml +RUN yq e -i ".public.app.html5ClientBuild = \"$TAG_BBB\"" /settings.yml + + +# =================================================== + +FROM alangecker/bbb-docker-base-java + +COPY --from=builder-settings /usr/local/bin/yq /usr/local/bin/yq +COPY --from=builder /bbb-apps-akka-0.0.4 /bbb-apps-akka +COPY bbb-apps-akka.conf /etc/bigbluebutton/bbb-apps-akka.conf.tmpl +COPY logback.xml /bbb-apps-akka/conf/logback.xml +COPY entrypoint.sh /entrypoint.sh +COPY --from=builder-settings --chown=bigbluebutton:bigbluebutton /settings.yml /usr/share/bigbluebutton/html5-client/private/config/settings.yml + +USER bigbluebutton +ENTRYPOINT /entrypoint.sh diff --git a/mod/apps-akka/entrypoint.sh b/mod/apps-akka/entrypoint.sh new file mode 100644 index 0000000..033429e --- /dev/null +++ b/mod/apps-akka/entrypoint.sh @@ -0,0 +1,17 @@ +#!/bin/sh -e + +# bbb-apps-akka.conf +TARGET=/etc/bigbluebutton/bbb-apps-akka.conf +cp /etc/bigbluebutton/bbb-apps-akka.conf.tmpl $TARGET +sed -i "s/DOMAIN/$DOMAIN/" $TARGET +sed -i "s/SHARED_SECRET/$SHARED_SECRET/" $TARGET +sed -i "s/POSTGRES_PASSWORD/$POSTGRES_PASSWORD/" $TARGET + + +# settings.yml +TARGET=/usr/share/bigbluebutton/html5-client/private/config/settings.yml +yq e -i ".public.kurento.wsUrl = \"wss://$DOMAIN/bbb-webrtc-sfu\"" $TARGET +yq e -i ".public.pads.url = \"https://$DOMAIN/pad\"" $TARGET + +cd /bbb-apps-akka +/bbb-apps-akka/bin/bbb-apps-akka \ No newline at end of file diff --git a/mod/apps-akka/logback.xml b/mod/apps-akka/logback.xml new file mode 100644 index 0000000..c29b1fb --- /dev/null +++ b/mod/apps-akka/logback.xml @@ -0,0 +1,18 @@ + + + + + %d{"yyyy-MM-dd'T'HH:mm:ss.SSSXXX"} %-5level %logger{35} - %msg%n + + + + + + + + + + + + + diff --git a/mod/base-java/Dockerfile b/mod/base-java/Dockerfile new file mode 100644 index 0000000..810feee --- /dev/null +++ b/mod/base-java/Dockerfile @@ -0,0 +1,24 @@ +FROM eclipse-temurin:17-jre-jammy + +RUN apt-get update && apt-get install -y \ + wget unzip gosu locales \ + imagemagick xpdf-utils curl \ + && sed -i '/en_US.UTF-8/s/^# //g' /etc/locale.gen && locale-gen + +ENV LANG en_US.UTF-8 +ENV LANGUAGE en_US:en +ENV LC_ALL en_US.UTF-8 + +# add user & group +RUN groupadd -g 998 bigbluebutton \ + && useradd -m -u 998 -g bigbluebutton bigbluebutton \ + && mkdir /etc/bigbluebutton \ + && chown bigbluebutton:bigbluebutton /etc/bigbluebutton + +# add dockerize +ENV DOCKERIZE_VERSION v0.7.0 +RUN wget -q https://github.com/jwilder/dockerize/releases/download/$DOCKERIZE_VERSION/dockerize-linux-amd64-$DOCKERIZE_VERSION.tar.gz \ + && tar -C /usr/local/bin -xzvf dockerize-linux-amd64-$DOCKERIZE_VERSION.tar.gz \ + && rm dockerize-linux-amd64-$DOCKERIZE_VERSION.tar.gz + + diff --git a/mod/bbb-apps-akka.conf b/mod/bbb-apps-akka.conf new file mode 100644 index 0000000..ae21ad1 --- /dev/null +++ b/mod/bbb-apps-akka.conf @@ -0,0 +1,25 @@ +// include default config from upstream +include "/bbb-apps-akka/conf/application.conf" + +redis { + host="redis" +} + +services { + bbbWebAPI="https://DOMAIN/bigbluebutton/api" + sharedSecret="SHARED_SECRET" + graphqlMiddlewareAPI = "http://bbb-graphql-middleware:8378" +} +http { + interface = "0.0.0.0" +} + +postgres { + properties = { + serverName = "postgres" + portNumber = "5432" + databaseName = "bbb_graphql" + user = "postgres" + password = "POSTGRES_PASSWORD" + } +} \ No newline at end of file diff --git a/mod/bbb-export-annotations/Dockerfile b/mod/bbb-export-annotations/Dockerfile new file mode 100644 index 0000000..d6c591f --- /dev/null +++ b/mod/bbb-export-annotations/Dockerfile @@ -0,0 +1,23 @@ +FROM node:22-bookworm-slim AS builder + +COPY --from=src / /bbb-export-annotations +RUN cd /bbb-export-annotations && npm ci && npm install + +# -------------------- + +FROM node:22-bookworm-slim + +RUN groupadd -g 998 bigbluebutton \ + && useradd -m -u 998 -g bigbluebutton bigbluebutton + +RUN apt update && apt install -y \ + nodejs npm cairosvg ghostscript imagemagick nodejs poppler-utils + +COPY --from=builder /bbb-export-annotations /bbb-export-annotations +COPY ./config/settings.json /bbb-export-annotations/config/settings.json + +USER bigbluebutton +WORKDIR /bbb-export-annotations +ENV NODE_ENV=production + +ENTRYPOINT npm start diff --git a/mod/bbb-export-annotations/config/settings.json b/mod/bbb-export-annotations/config/settings.json new file mode 100644 index 0000000..294c3be --- /dev/null +++ b/mod/bbb-export-annotations/config/settings.json @@ -0,0 +1,40 @@ +{ + "log": { + "level": "info", + "msgName": "PresAnnStatusMsg" + }, + "shared": { + "presAnnDropboxDir": "/tmp/pres-ann-dropbox", + "cairosvg": "/usr/bin/cairosvg", + "ghostscript": "/usr/bin/gs" + }, + "process": { + "maxImageWidth": 1440, + "maxImageHeight": 1080, + "pointsPerInch": 72, + "pixelsPerInch": 96, + "cairoSVGUnsafeFlag": false + }, + "notifier": { + "pod_id": "DEFAULT_PRESENTATION_POD", + "is_downloadable": "false", + "msgName": "NewPresFileAvailableMsg" + }, + "bbbWebAPI": "http://bbb-web:8090", + "bbbPadsAPI": "http://bbb-pads:9002", + "redis": { + "host": "redis", + "port": 6379, + "password": null, + "channels": { + "queue": "exportJobs", + "publish": "to-akka-apps-redis-channel" + } + }, + "fonts": { + "draw": "/usr/local/share/fonts/Shantell_Sans-Tldrawish.woff2", + "sans": "/usr/local/share/fonts/IBMPlexSans-Medium.woff2", + "serif": "/usr/local/share/fonts/IBMPlexSerif-Medium.woff2", + "mono": "/usr/local/share/fonts/IBMPlexMono-Medium.woff2" + } +} \ No newline at end of file diff --git a/mod/bbb-exporter.nginx b/mod/bbb-exporter.nginx new file mode 100644 index 0000000..7b1dfaa --- /dev/null +++ b/mod/bbb-exporter.nginx @@ -0,0 +1,4 @@ +location /bbb-exporter { + proxy_pass http://prometheus-exporter:9688; + proxy_http_version 1.1; +} \ No newline at end of file diff --git a/mod/bbb-graphql-actions/Dockerfile b/mod/bbb-graphql-actions/Dockerfile new file mode 100644 index 0000000..66ac9b8 --- /dev/null +++ b/mod/bbb-graphql-actions/Dockerfile @@ -0,0 +1,34 @@ +ARG BBB_BUILD_TAG +FROM bigbluebutton/bbb-build:$BBB_BUILD_TAG AS builder + +COPY --from=src ./ /src +RUN cd /src && \ + npm ci --no-progress && \ + npm run build + +# delete node_modules (it should create a fresh one inside /src/dist/) +RUN rm -rf /src/node_modules + +RUN cd /src/dist && \ + mv index.js bbb-graphql-actions.js && \ + cp ../package.json ../package-lock.json . && \ + npm ci --no-progress --omit=dev + + +# ------------------------------ +FROM node:22-bookworm-slim + +RUN groupadd -g 2062 app \ + && useradd -m -u 2063 -g app app + +USER app + +WORKDIR /app + +ENV SERVER_HOST 0.0.0.0 +ENV BBB_REDIS_HOST redis +ENV NODE_ENV=production + +COPY --from=builder /src/dist /app + +CMD [ "node", "/app/bbb-graphql-actions.js" ] \ No newline at end of file diff --git a/mod/bbb-graphql-actions/Dockerfile.dev b/mod/bbb-graphql-actions/Dockerfile.dev new file mode 100644 index 0000000..22babbb --- /dev/null +++ b/mod/bbb-graphql-actions/Dockerfile.dev @@ -0,0 +1,16 @@ +ARG BBB_BUILD_TAG +FROM bigbluebutton/bbb-build:$BBB_BUILD_TAG AS builder + +RUN apt-get update && apt-get install -y gosu + +# allow any user to use node in /root/.nvm +RUN chmod 755 /root + +COPY dev-entrypoint.sh /dev-entrypoint.sh +ENTRYPOINT [ "/dev-entrypoint.sh" ] + +WORKDIR /app +ENV SERVER_HOST 0.0.0.0 +ENV BBB_REDIS_HOST redis + +CMD [ "npm install && npm start" ] \ No newline at end of file diff --git a/mod/bbb-graphql-actions/dev-entrypoint.sh b/mod/bbb-graphql-actions/dev-entrypoint.sh new file mode 100644 index 0000000..7c3700c --- /dev/null +++ b/mod/bbb-graphql-actions/dev-entrypoint.sh @@ -0,0 +1,12 @@ +#!/bin/bash + +# get owner of /app +OWNER="$(stat -c '%u' "/app")" +GROUP="$(stat -c '%g' "/app")" +useradd --home-dir /tmp -u $OWNER user || /bin/true + +# run with same user to avoid any issues +# with file permissions +. /root/.nvm/nvm.sh +gosu $OWNER:$GROUP bash -c "$@" + diff --git a/mod/bbb-graphql-middleware/Dockerfile b/mod/bbb-graphql-middleware/Dockerfile new file mode 100644 index 0000000..763ee1a --- /dev/null +++ b/mod/bbb-graphql-middleware/Dockerfile @@ -0,0 +1,12 @@ +ARG BBB_BUILD_TAG +FROM bigbluebutton/bbb-build:$BBB_BUILD_TAG AS builder + +COPY --from=src / /src/ +RUN cd /src/ && CGO_ENABLED=0 go build -o bbb-graphql-middleware cmd/bbb-graphql-middleware/main.go + +# ------------------------------ +FROM alpine +COPY --from=builder /src/bbb-graphql-middleware /app/bbb-graphql-middleware +COPY --from=builder /src/config/config.yml /usr/share/bbb-graphql-middleware/config.yml +COPY config.yml /etc/bigbluebutton/bbb-graphql-middleware.yml +CMD [ "/app/bbb-graphql-middleware" ] \ No newline at end of file diff --git a/mod/bbb-graphql-middleware/Dockerfile.dev b/mod/bbb-graphql-middleware/Dockerfile.dev new file mode 100644 index 0000000..d1952d4 --- /dev/null +++ b/mod/bbb-graphql-middleware/Dockerfile.dev @@ -0,0 +1,8 @@ +ARG BBB_BUILD_TAG +FROM bigbluebutton/bbb-build:$BBB_BUILD_TAG AS builder + +WORKDIR /app + +ENV GOPATH /gopath + +CMD ["go", "run", "cmd/bbb-graphql-middleware/main.go", "--signal", "SIGTERM"] \ No newline at end of file diff --git a/mod/bbb-graphql-middleware/config.yml b/mod/bbb-graphql-middleware/config.yml new file mode 100644 index 0000000..f323157 --- /dev/null +++ b/mod/bbb-graphql-middleware/config.yml @@ -0,0 +1,15 @@ +server: + listen_host: 0.0.0.0 + listen_port: 8378 +redis: + host: redis + port: 6379 + password: "" +hasura: + url: ws://nginx:8185/v1/graphql +graphql-actions: + url: http://bbb-graphql-actions:8093 +auth_hook: + url: http://bbb-web:8090/bigbluebutton/connection/checkGraphqlAuthorization +session_vars_hook: + url: http://apps-akka:8901/userInfo \ No newline at end of file diff --git a/mod/bbb-graphql-server/Dockerfile b/mod/bbb-graphql-server/Dockerfile new file mode 100644 index 0000000..3c087cc --- /dev/null +++ b/mod/bbb-graphql-server/Dockerfile @@ -0,0 +1,25 @@ +ARG BBB_BUILD_TAG +ARG GRAPHQL_ENGINE_TAG +FROM bigbluebutton/bbb-build:$BBB_BUILD_TAG AS builder + +RUN curl -L https://github.com/hasura/graphql-engine/raw/stable/cli/get.sh | INSTALL_PATH=/usr/local/bin VERSION=v2.44.0 bash +RUN wget -qO /usr/local/bin/yq https://github.com/mikefarah/yq/releases/latest/download/yq_linux_amd64 && chmod a+x /usr/local/bin/yq + +# ---------------------------- +FROM hasura/graphql-engine:$GRAPHQL_ENGINE_TAG + +# install netstat, required for start script +RUN apt-get update && apt-get install -y net-tools gosu + +COPY --from=builder /usr/local/bin/yq /usr/local/bin/yq +COPY --from=builder /usr/local/bin/hasura /usr/local/bin/hasura + +COPY --from=src /bbb_schema.sql /app/ +COPY --from=src /metadata /app/metadata + +COPY config.yaml /app/config.yaml +COPY entrypoint.sh /entrypoint.sh +COPY start.sh /app/start.sh + +ENTRYPOINT [ "/entrypoint.sh" ] +CMD [ "/app/start.sh" ] diff --git a/mod/bbb-graphql-server/config.yaml b/mod/bbb-graphql-server/config.yaml new file mode 100644 index 0000000..cd39dc2 --- /dev/null +++ b/mod/bbb-graphql-server/config.yaml @@ -0,0 +1,7 @@ +version: 3 +endpoint: http://localhost:8085 +admin_secret: bigbluebutton +metadata_directory: metadata +actions: + kind: synchronous + handler_webhook_baseurl: http://localhost:3000 diff --git a/mod/bbb-graphql-server/entrypoint.sh b/mod/bbb-graphql-server/entrypoint.sh new file mode 100644 index 0000000..f14e2f4 --- /dev/null +++ b/mod/bbb-graphql-server/entrypoint.sh @@ -0,0 +1,27 @@ +#!/bin/bash + +# for psql +export PGHOST=postgres +export PGUSER="${POSTGRES_USER}" +export PGPASSWORD="${POSTGRES_PASSWORD}" + + +# for hasura +export HASURA_GRAPHQL_DATABASE_URL=postgres://${POSTGRES_USER}:${POSTGRES_PASSWORD}@postgres:5432/hasura_app +export HASURA_GRAPHQL_METADATA_DATABASE_URL=postgres://${POSTGRES_USER}:${POSTGRES_PASSWORD}@postgres:5432/hasura_app +export HASURA_GRAPHQL_LOG_LEVEL=warn +export HASURA_GRAPHQL_ENABLE_CONSOLE=false +export HASURA_GRAPHQL_LIVE_QUERIES_MULTIPLEXED_REFETCH_INTERVAL=333 +export HASURA_GRAPHQL_LIVE_QUERIES_MULTIPLEXED_BATCH_SIZE=1000 +export HASURA_GRAPHQL_STREAMING_QUERIES_MULTIPLEXED_REFETCH_INTERVAL=150 +export HASURA_GRAPHQL_STREAMING_QUERIES_MULTIPLEXED_BATCH_SIZE=1000 +export HASURA_GRAPHQL_SERVER_PORT=8085 +export HASURA_GRAPHQL_ENABLE_TELEMETRY=false +export HASURA_GRAPHQL_WEBSOCKET_KEEPALIVE=10 +export HASURA_GRAPHQL_AUTH_HOOK=http://apps-akka:8901/userInfo +export HASURA_BBB_GRAPHQL_ACTIONS_ADAPTER_URL=http://bbb-graphql-actions:8093 + + +export HASURA_GRAPHQL_BBB_DATABASE_URL=postgres://${POSTGRES_USER}:${POSTGRES_PASSWORD}@postgres:5432/bbb_graphql + +exec $@ \ No newline at end of file diff --git a/mod/bbb-graphql-server/start.sh b/mod/bbb-graphql-server/start.sh new file mode 100644 index 0000000..5fc3354 --- /dev/null +++ b/mod/bbb-graphql-server/start.sh @@ -0,0 +1,39 @@ +#!/bin/bash +set -e + +cd /app/ + +# patch database url +# TODO: this should be possible upstream in BBB via an environment variable +yq e -i ".[1].configuration.connection_info.database_url = \"$HASURA_GRAPHQL_BBB_DATABASE_URL\"" metadata/databases/databases.yaml + +sed -i "s/^admin_secret: .*/admin_secret: $HASURA_GRAPHQL_ADMIN_SECRET/g" /app/config.yaml + +echo "SELECT 'CREATE DATABASE hasura_app' WHERE NOT EXISTS (SELECT FROM pg_database WHERE datname = 'hasura_app')\gexec" | psql + +echo "Restarting database bbb_graphql" +psql -c "SELECT pg_terminate_backend(pg_stat_activity.pid) FROM pg_stat_activity WHERE datname = 'bbb_graphql'" > /dev/null +psql -c "drop database if exists bbb_graphql with (force)" +psql -c "create database bbb_graphql WITH TEMPLATE template0 LC_COLLATE 'C.UTF-8'" +psql -c "alter database bbb_graphql set timezone to 'UTC'" + +echo "Creating tables in bbb_graphql" +psql -U postgres -d bbb_graphql -q -f bbb_schema.sql --set ON_ERROR_STOP=on + +echo "Starting hasura-graphql-engine" +gosu nobody graphql-engine serve & +PID=$! + +sleep 1 + + +#Check if Hasura is ready before applying metadata +while ! netstat -tuln | grep ":$HASURA_GRAPHQL_SERVER_PORT " > /dev/null; do + echo "Waiting for Hasura's port ($HASURA_GRAPHQL_SERVER_PORT) to be ready..." + sleep 1 +done + +echo "Applying new metadata to Hasura" +/usr/local/bin/hasura metadata apply --skip-update-check + +wait "$PID" \ No newline at end of file diff --git a/mod/bbb-html5.dev.nginx b/mod/bbb-html5.dev.nginx new file mode 100644 index 0000000..5b059ad --- /dev/null +++ b/mod/bbb-html5.dev.nginx @@ -0,0 +1,23 @@ +# serve locale index from prebuilt static files +location = /html5client/locales/ { + alias /usr/share/bigbluebutton/html5-client/locales/; + autoindex on; + autoindex_format json; + + # Prevent browsers from caching + add_header Cache-Control "no-store, no-cache, must-revalidate, proxy-revalidate, max-age=0"; + add_header Pragma "no-cache"; + add_header Expires 0; +} + +# running from source (npm start) +location /html5client/ { + rewrite /html5client/(.*) /$1 break; + gzip_static on; + proxy_pass http://webrtc-sfu:3000/; + proxy_http_version 1.1; + proxy_set_header Upgrade $http_upgrade; + proxy_set_header Connection "Upgrade"; + proxy_set_header Host $host; +} + diff --git a/mod/bbb-pads/Dockerfile b/mod/bbb-pads/Dockerfile new file mode 100644 index 0000000..e366366 --- /dev/null +++ b/mod/bbb-pads/Dockerfile @@ -0,0 +1,18 @@ +FROM node:22-bookworm-slim AS builder + +COPY --from=src / /bbb-pads +RUN cd /bbb-pads && npm install --production + + +RUN chmod 777 /bbb-pads/config +# ------------------------------ + +FROM node:22-bookworm-slim + +RUN apt update && apt install -y jq moreutils \ + && useradd --uid 2003 --create-home --user-group bbb-pads + +COPY --from=builder /bbb-pads /bbb-pads +USER bbb-pads +COPY entrypoint.sh /entrypoint.sh +ENTRYPOINT /entrypoint.sh \ No newline at end of file diff --git a/mod/bbb-pads/entrypoint.sh b/mod/bbb-pads/entrypoint.sh new file mode 100644 index 0000000..9f93722 --- /dev/null +++ b/mod/bbb-pads/entrypoint.sh @@ -0,0 +1,15 @@ +#!/bin/sh +set -e + +TARGET=/bbb-pads/config/settings.json + +cp /bbb-pads/config/settings.json.template $TARGET +sed -i "s/ETHERPAD_API_KEY/\"$ETHERPAD_API_KEY\"/g" $TARGET +jq '.etherpad.host = "etherpad"' $TARGET | sponge $TARGET +jq '.express.host = "0.0.0.0"' $TARGET | sponge $TARGET +jq '.redis.host = "redis"' $TARGET | sponge $TARGET + + +cd /bbb-pads +export NODE_ENV=production +npm start \ No newline at end of file diff --git a/mod/bbb-resync-freeswitch b/mod/bbb-resync-freeswitch new file mode 100644 index 0000000..8b73c06 --- /dev/null +++ b/mod/bbb-resync-freeswitch @@ -0,0 +1,5 @@ +#!/bin/bash + +# https://github.com/bigbluebutton/bigbluebutton/pull/9597/files + +docker exec -it bbb-freeswitch fs_cli -H webrtc-sfu -P 8021 -x 'fsctl sync_clock_when_idle' \ No newline at end of file diff --git a/mod/bbb-web/Dockerfile b/mod/bbb-web/Dockerfile new file mode 100644 index 0000000..5a08075 --- /dev/null +++ b/mod/bbb-web/Dockerfile @@ -0,0 +1,60 @@ +ARG BBB_BUILD_TAG +FROM bigbluebutton/bbb-build:$BBB_BUILD_TAG AS builder + +COPY --from=src-common-message / /bbb-common-message + +# build bbb-common-message +RUN cd /bbb-common-message && ./deploy.sh + +# =================================================== + + +COPY --from=src-common-web / /bbb-common-web +# build bbb-common-web +RUN cd /bbb-common-web && ./deploy.sh + +COPY --from=src-web / /bbb-web + +# compile bbb-web +RUN cd /bbb-web && sh ./gradlew assemble + +# compile pres-checker +RUN cd /bbb-web/pres-checker && gradle jar +RUN mkdir -p /bbb-web/pres-checker/lib && find /bbb-web/pres-checker && \ + cp /bbb-web/pres-checker/build/libs/bbb-pres-check-0.0.1.jar /bbb-web/pres-checker/lib + +# extract .war +RUN unzip -q /bbb-web/build/libs/bigbluebutton-0.10.0.war -d /dist + + +# =================================================== +FROM alangecker/bbb-docker-base-java + +# add blank presentation files and allow conversion to pdf/svg +RUN mkdir -p /usr/share/bigbluebutton/blank \ + && cd /usr/share/bigbluebutton/blank \ + && wget \ + https://raw.githubusercontent.com/bigbluebutton/bigbluebutton/v3.0.19/bigbluebutton-config/slides/blank-svg.svg \ + https://raw.githubusercontent.com/bigbluebutton/bigbluebutton/v3.0.19/bigbluebutton-config/slides/blank-thumb.png \ + https://raw.githubusercontent.com/bigbluebutton/bigbluebutton/v3.0.19/bigbluebutton-config/slides/blank-presentation.pdf \ + https://raw.githubusercontent.com/bigbluebutton/bigbluebutton/v3.0.19/bigbluebutton-config/slides/blank-png.png \ + && sed -i 's///g' /etc/ImageMagick-6/policy.xml \ + && sed -i '/potrace/d' /etc/ImageMagick-6/delegates.xml + + +# get bbb-web +COPY --from=builder /dist /usr/share/bbb-web + +# get pres-checker +COPY --from=builder /bbb-web/pres-checker/lib /usr/share/prescheck/lib +COPY --from=builder /bbb-web/pres-checker/run.sh /usr/share/prescheck/prescheck.sh + +# add entrypoint and templates +COPY entrypoint.sh /entrypoint.sh +COPY bbb-web.properties /etc/bigbluebutton/bbb-web.properties.tmpl +COPY turn-stun-servers.xml /etc/bigbluebutton/turn-stun-servers.xml.tmpl +COPY logback.xml /usr/share/bbb-web/WEB-INF/classes/logback.xml +COPY office-convert.sh /usr/share/bbb-libreoffice-conversion/convert.sh +COPY run-in-systemd.sh /usr/share/bbb-web/run-in-systemd.sh + +ENTRYPOINT ["/entrypoint.sh"] diff --git a/mod/bbb-web/bbb-web.properties b/mod/bbb-web/bbb-web.properties new file mode 100644 index 0000000..7c653cd --- /dev/null +++ b/mod/bbb-web/bbb-web.properties @@ -0,0 +1,24 @@ +defaultWelcomeMessage={{ .Env.WELCOME_MESSAGE }} +defaultWelcomeMessageFooter={{ .Env.WELCOME_FOOTER }} + +{{ if isTrue .Env.ENABLE_RECORDING }} +disableRecordingDefault=false +{{ else }} +disableRecordingDefault=true +{{ end }} + +bigbluebutton.web.serverURL=https://{{ .Env.DOMAIN }} + +securitySalt={{ .Env.SHARED_SECRET }} + +redisHost=redis + +{{ if isTrue .Env.IGNORE_TLS_CERT_ERRORS }} +beans.presentationService.defaultUploadedPresentation=https://raw.githubusercontent.com/bigbluebutton/bigbluebutton/v3.0.19/bigbluebutton-config/assets/default.pdf +{{else}} +beans.presentationService.defaultUploadedPresentation=${bigbluebutton.web.serverURL}/default.pdf +{{end}} + +learningDashboardEnabled={{ .Env.ENABLE_LEARNING_DASHBOARD }} + +defaultNumDigitsForTelVoice=9 \ No newline at end of file diff --git a/mod/bbb-web/entrypoint.sh b/mod/bbb-web/entrypoint.sh new file mode 100644 index 0000000..f9182f2 --- /dev/null +++ b/mod/bbb-web/entrypoint.sh @@ -0,0 +1,31 @@ +#!/bin/bash +set -e + +# create recording directory structure if it doesn't exist yet +mkdir -p /var/bigbluebutton/recording/status +mkdir -p /var/bigbluebutton/events +mkdir -p /var/bigbluebutton/recording +mkdir -p /var/bigbluebutton/recording/raw +mkdir -p /var/bigbluebutton/recording/process +mkdir -p /var/bigbluebutton/recording/publish +mkdir -p /var/bigbluebutton/recording/status/recorded +mkdir -p /var/bigbluebutton/recording/status/archived +mkdir -p /var/bigbluebutton/recording/status/processed +mkdir -p /var/bigbluebutton/recording/status/ended +mkdir -p /var/bigbluebutton/recording/status/sanity +mkdir -p /var/bigbluebutton/recording/status/published +mkdir -p /var/bigbluebutton/captions +mkdir -p /var/bigbluebutton/captions/inbox +mkdir -p /var/bigbluebutton/published +mkdir -p /var/bigbluebutton/deleted +mkdir -p /var/bigbluebutton/unpublished +mkdir -p /var/bigbluebutton/basic_stats +chown -R bigbluebutton:bigbluebutton /var/bigbluebutton + +cd /usr/share/bbb-web/ +dockerize \ + -template /etc/bigbluebutton/bbb-web.properties.tmpl:/etc/bigbluebutton/bbb-web.properties \ + -template /etc/bigbluebutton/turn-stun-servers.xml.tmpl:/etc/bigbluebutton/turn-stun-servers.xml \ + gosu bigbluebutton java -Dgrails.env=prod -Dserver.address=0.0.0.0 -Dserver.port=8090 -Dspring.main.allow-circular-references=true -Xms384m -Xmx384m -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/var/bigbluebutton/diagnostics -cp WEB-INF/lib/*:/:WEB-INF/classes/:. org.springframework.boot.loader.launch.WarLauncher + + diff --git a/mod/bbb-web/logback.xml b/mod/bbb-web/logback.xml new file mode 100644 index 0000000..def4f59 --- /dev/null +++ b/mod/bbb-web/logback.xml @@ -0,0 +1,28 @@ + + + + + %d{"yyyy-MM-dd'T'HH:mm:ss.SSSXXX"} %-5level %logger{35} - %msg%n + + + + + + + + + + + + + + + + + + + + + + diff --git a/mod/bbb-web/office-convert.sh b/mod/bbb-web/office-convert.sh new file mode 100644 index 0000000..ee88b34 --- /dev/null +++ b/mod/bbb-web/office-convert.sh @@ -0,0 +1,36 @@ +#!/bin/bash +set -e +set -u +PATH="/bin/:/usr/bin/" + +# This script receives three params +# Param 1: Input office file path (e.g. "/tmp/test.odt") +# Param 2: Output pdf file path (e.g. "/tmp/test.pdf") +# Param 3: Destination Format (pdf default) +# Param 4: Timeout (secs) (optional) + +if (( $# == 0 )); then + echo "Missing parameter 1 (Input office file path)"; + exit 1 +elif (( $# == 1 )); then + echo "Missing parameter 2 (Output pdf file path)"; + exit 1 +fi; + + +source="$1" +dest="$2" + +# If output format is missing, define PDF +convertTo="${3:-pdf}" + +# If timeout is missing, define 60 +timeoutSecs="${4:-60}" +# Truncate timeout to max 3 digits (as expected by sudoers) +timeoutSecs="${timeoutSecs:0:3}" + +# The timeout is important. + +timeout $(printf %03d $timeoutSecs)s curl -F "data=@${source}" -k $COLLABORA_URL/convert-to/$convertTo > "${dest}" + +exit 0 diff --git a/mod/bbb-web/run-in-systemd.sh b/mod/bbb-web/run-in-systemd.sh new file mode 100644 index 0000000..79018d1 --- /dev/null +++ b/mod/bbb-web/run-in-systemd.sh @@ -0,0 +1,10 @@ +#!/bin/sh -e + +# bbb-web requires a script under /usr/share/bbb-web/run-in-systemd.sh to run certain tasks +# this is used for sandboxing, which is in our case a bit more difficult, because +# we run it without systemd in an unprivileged container, with currently no extra sandboxing capabilities + +# TODO: exploring better ways! (e. g. firejail) + +timeout_secs="$1"; shift +exec timeout "${timeout_secs}" "$@" \ No newline at end of file diff --git a/mod/bbb-web/turn-stun-servers.xml b/mod/bbb-web/turn-stun-servers.xml new file mode 100644 index 0000000..de92838 --- /dev/null +++ b/mod/bbb-web/turn-stun-servers.xml @@ -0,0 +1,59 @@ + + + + + + + + + + + + + + {{if and (isTrue .Env.ENABLE_HTTPS_PROXY) (not (isTrue .Env.IGNORE_TLS_CERT_ERRORS)) }} + {{/* ignore when using a self signed certificate in dev mode */}} + + + + + + {{end}} + + + {{if .Env.TURN_EXT_SERVER }} + + + + + + {{end}} + + + + + + + + + + + + {{if and (isTrue .Env.ENABLE_HTTPS_PROXY) (not (isTrue .Env.IGNORE_TLS_CERT_ERRORS)) }} + + {{end}} + + {{if .Env.TURN_EXT_SERVER }} + + {{end}} + + + + + + + + diff --git a/mod/bbb-webrtc-recorder/Dockerfile b/mod/bbb-webrtc-recorder/Dockerfile new file mode 100644 index 0000000..61fd252 --- /dev/null +++ b/mod/bbb-webrtc-recorder/Dockerfile @@ -0,0 +1,40 @@ +# Build stage +FROM golang:1.25 as builder + +ARG APP_VERSION=devel +ARG GOMOD=github.com/bigbluebutton/bbb-webrtc-recorder + +WORKDIR /app + +COPY --from=src go.* ./ + +RUN go mod tidy + +COPY --from=src . ./ + +RUN APP_VERSION=$(cat ./VERSION | sed 's/ /-/g') \ + go build -o ./build/bbb-webrtc-recorder \ + -ldflags="-X '$GOMOD/internal.AppVersion=v${APP_VERSION1}'" \ + ./cmd/bbb-webrtc-recorder + + +RUN mv /app/build/bbb-webrtc-recorder /usr/bin/bbb-webrtc-recorder + +# Running stage +FROM debian:bookworm-slim + +RUN apt-get update && apt-get install -y gosu + +# use same UID as in the recordings container +RUN groupadd -g 998 bigbluebutton && useradd -m -u 998 -g bigbluebutton bigbluebutton + +# config +ENV BBBRECORDER_PUBSUB_ADAPTERS_REDIS_ADDRESS=redis:6379 +ENV BBBRECORDER_PUBSUB_ADAPTERS_REDIS_NETWORK=tcp +ENV BBBRECORDER_DEBUG=true + +# Copy the binary to the production image from the builder stage. +COPY --from=builder /usr/bin/bbb-webrtc-recorder /usr/bin/bbb-webrtc-recorder +COPY --from=builder /app/config/bbb-webrtc-recorder.yml /etc/bbb-webrtc-recorder/bbb-webrtc-recorder.yml + +CMD ["/bin/sh", "-c", "chown -R bigbluebutton:bigbluebutton /var/lib/bbb-webrtc-recorder && gosu bigbluebutton /usr/bin/bbb-webrtc-recorder"] \ No newline at end of file diff --git a/mod/coturn/turnserver.conf b/mod/coturn/turnserver.conf new file mode 100644 index 0000000..55cdf9d --- /dev/null +++ b/mod/coturn/turnserver.conf @@ -0,0 +1,28 @@ +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 +realm=bbb-docker + +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 \ No newline at end of file diff --git a/mod/etherpad/Dockerfile b/mod/etherpad/Dockerfile new file mode 100644 index 0000000..1633561 --- /dev/null +++ b/mod/etherpad/Dockerfile @@ -0,0 +1,29 @@ +ARG TAG_ETHERPAD +FROM etherpad/etherpad:$TAG_ETHERPAD + +USER root + +RUN apk add git curl + +USER etherpad + +RUN pnpm run plugins i \ + ep_disable_chat@0.0.13 \ + ep_auth_session@1.1.1 \ + --github \ + mconf/ep_cursortrace#56fb8c2b211cdda4fc8715ec99e1cb7b7d9eb851 \ + mconf/ep_pad_ttl#360136cd38493dd698435631f2373cbb7089082d \ + mconf/ep_redis_publisher#2b6e47c1c59362916a0b2961a29b259f2977b694 + + +# add skin from git submodule +COPY --chown=etherpad:0 --from=skin / /opt/etherpad-lite/src/static/skins/bigbluebutton + +# add plugin from git submodule +COPY --chown=etherpad:0 --from=plugin / /ep_bigbluebutton_patches +RUN pnpm run plugins i --path /ep_bigbluebutton_patches + +COPY settings.json /opt/etherpad-lite/settings.json +COPY etherpad-export.sh /etherpad-export.sh +COPY entrypoint.sh /entrypoint.sh +ENTRYPOINT ["/entrypoint.sh"] \ No newline at end of file diff --git a/mod/etherpad/entrypoint.sh b/mod/etherpad/entrypoint.sh new file mode 100644 index 0000000..acc2392 --- /dev/null +++ b/mod/etherpad/entrypoint.sh @@ -0,0 +1,3 @@ +#!/bin/sh +echo $ETHERPAD_API_KEY > /tmp/apikey +pnpm run prod --apikey /tmp/apikey \ No newline at end of file diff --git a/mod/etherpad/etherpad-export.sh b/mod/etherpad/etherpad-export.sh new file mode 100644 index 0000000..0ebd733 --- /dev/null +++ b/mod/etherpad/etherpad-export.sh @@ -0,0 +1,9 @@ +#!/bin/sh +src="$8" +dest="$(echo $8 | sed -E -e 's/html|odt/'$7'/')" +convertTo="$7" + + +curl -v -F "data=@${src}" -k $COLLABORA_URL/convert-to/$convertTo > "${dest}" + +exit 0 \ No newline at end of file diff --git a/mod/etherpad/settings.json b/mod/etherpad/settings.json new file mode 100644 index 0000000..d3c1c4e --- /dev/null +++ b/mod/etherpad/settings.json @@ -0,0 +1,723 @@ +/* + * This file must be valid JSON. But comments are allowed + * + * Please edit settings.json, not settings.json.template + * + * Please note that starting from Etherpad 1.6.0 you can store DB credentials in + * a separate file (credentials.json). + * + * + * ENVIRONMENT VARIABLE SUBSTITUTION + * ================================= + * + * All the configuration values can be read from environment variables using the + * syntax "${ENV_VAR}" or "${ENV_VAR:default_value}". + * + * This is useful, for example, when running in a Docker container. + * + * DETAILED RULES: + * - If the environment variable is set to the string "true" or "false", the + * value becomes Boolean true or false. + * - If the environment variable is set to the string "null", the value + * becomes null. + * - If the environment variable is set to the string "undefined", the setting + * is removed entirely, except when used as the member of an array in which + * case it becomes null. + * - If the environment variable is set to a string representation of a finite + * number, the string is converted to that number. + * - If the environment variable is set to any other string, including the + * empty string, the value is that string. + * - If the environment variable is unset and a default value is provided, the + * value is as if the environment variable was set to the provided default: + * - "${UNSET_VAR:}" becomes the empty string. + * - "${UNSET_VAR:foo}" becomes the string "foo". + * - "${UNSET_VAR:true}" and "${UNSET_VAR:false}" become true and false. + * - "${UNSET_VAR:null}" becomes null. + * - "${UNSET_VAR:undefined}" causes the setting to be removed (or be set + * to null, if used as a member of an array). + * - If the environment variable is unset and no default value is provided, + * the value becomes null. THIS BEHAVIOR MAY CHANGE IN A FUTURE VERSION OF + * ETHERPAD; if you want the default value to be null, you should explicitly + * specify "null" as the default value. + * + * EXAMPLE: + * "port": "${PORT:9001}" + * "minify": "${MINIFY}" + * "skinName": "${SKIN_NAME:colibris}" + * + * Would read the configuration values for those items from the environment + * variables PORT, MINIFY and SKIN_NAME. + * + * If PORT and SKIN_NAME variables were not defined, the default values 9001 and + * "colibris" would be used. + * The configuration value "minify", on the other hand, does not have a + * designated default value. Thus, if the environment variable MINIFY were + * undefined, "minify" would be null. + * + * REMARKS: + * 1) please note that variable substitution always needs to be quoted. + * + * "port": 9001, <-- Literal values. When not using + * "minify": false substitution, only strings must be + * "skinName": "colibris" quoted. Booleans and numbers must not. + * + * "port": "${PORT:9001}" <-- CORRECT: if you want to use a variable + * "minify": "${MINIFY:true}" substitution, put quotes around its name, + * "skinName": "${SKIN_NAME}" even if the required value is a number or + * a boolean. + * Etherpad will take care of rewriting it + * to the proper type if necessary. + * + * "port": ${PORT:9001} <-- ERROR: this is not valid json. Quotes + * "minify": ${MINIFY} around variable names are missing. + * "skinName": ${SKIN_NAME} + * + * 2) Beware of undefined variables and default values: nulls and empty strings + * are different! + * + * This is particularly important for user's passwords (see the relevant + * section): + * + * "password": "${PASSW}" // if PASSW is not defined would result in password === null + * "password": "${PASSW:}" // if PASSW is not defined would result in password === '' + * + * If you want to use an empty value (null) as default value for a variable, + * simply do not set it, without putting any colons: "${ABIWORD}". + * + * 3) if you want to use newlines in the default value of a string parameter, + * use "\n" as usual. + * + * "defaultPadText" : "${DEFAULT_PAD_TEXT}Line 1\nLine 2" + */ + { + /* + * Name your instance! + */ + "title": "Etherpad", + + /* + * Pathname of the favicon you want to use. If null, the skin's favicon is + * used if one is provided by the skin, otherwise the default Etherpad favicon + * is used. If this is a relative path it is interpreted as relative to the + * Etherpad root directory. + */ + "favicon": null, + + /* + * Skin name. + * + * Its value has to be an existing directory under src/static/skins. + * You can write your own, or use one of the included ones: + * + * - "no-skin": an empty skin (default). This yields the unmodified, + * traditional Etherpad theme. + * - "colibris": the new experimental skin (since Etherpad 1.8), candidate to + * become the default in Etherpad 2.0 + */ + "skinName": "bigbluebutton", + + /* + * Skin Variants + * + * Use the UI skin variants builder at /p/test#skinvariantsbuilder + * + * For the colibris skin only, you can choose how to render the three main + * containers: + * - toolbar (top menu with icons) + * - editor (containing the text of the pad) + * - background (area outside of editor, mostly visible when using page style) + * + * For each of the 3 containers you can choose 4 color combinations: + * super-light, light, dark, super-dark. + * + * For example, to make the toolbar dark, you will include "dark-toolbar" into + * skinVariants. + * + * You can provide multiple skin variants separated by spaces. Default + * skinVariant is "super-light-toolbar super-light-editor light-background". + * + * For the editor container, you can also make it full width by adding + * "full-width-editor" variant (by default editor is rendered as a page, with + * a max-width of 900px). + */ + "skinVariants": "", + + /* + * IP and port which Etherpad should bind at. + * + * Binding to a Unix socket is also supported: just use an empty string for + * the ip, and put the full path to the socket in the port parameter. + * + * EXAMPLE USING UNIX SOCKET: + * "ip": "", // <-- has to be an empty string + * "port" : "/somepath/etherpad.socket", // <-- path to a Unix socket + */ + "ip": "0.0.0.0", + "port": 9001, + + /* + * Option to hide/show the settings.json in admin page. + * + * Default option is set to true + */ + "showSettingsInAdminPage": true, + + /* + * Settings for cleanup of pads + */ + "cleanup": { + "enabled": false, + "keepRevisions": 5 + }, + + /* + * Node native SSL support + * + * This is disabled by default. + * Make sure to have the minimum and correct file access permissions set so + * that the Etherpad server can access them + */ + + /* + "ssl" : { + "key" : "/path-to-your/epl-server.key", + "cert" : "/path-to-your/epl-server.crt", + "ca": ["/path-to-your/epl-intermediate-cert1.crt", "/path-to-your/epl-intermediate-cert2.crt"] + }, + */ + + /* + * The type of the database. + * + * You can choose between many DB drivers, for example: dirty, postgres, + * sqlite, mysql. + * + * You shouldn't use "dirty" for for anything else than testing or + * development. + * + * + * Database specific settings are dependent on dbType, and go in dbSettings. + * Remember that since Etherpad 1.6.0 you can also store this information in + * credentials.json. + * + * For a complete list of the supported drivers, please refer to: + * https://www.npmjs.com/package/ueberdb2 + */ + + "dbType": "redis", + "dbSettings": { + "url": "redis://redis:6379" + }, + + /* + * An Example of MySQL Configuration (commented out). + * + * See: https://github.com/ether/etherpad-lite/wiki/How-to-use-Etherpad-Lite-with-MySQL + */ + + /* + "dbType" : "redis", + "dbSettings" : { + "host": "127.0.0.1", + "port": 6379, + "client_options": { + "password": "PASSWORD" + } + }, + */ + + /* + * The default text of a pad: A zero-width-space is used to work around an issue with Etherpad 1.9.1 where empty pads are not being created. + * See: https://github.com/ether/etherpad-lite/issues/5787 + */ + "defaultPadText" : "\u200b", + + /* + * Default Pad behavior. + * + * Change them if you want to override. + */ + "padOptions": { + "noColors": true, + "showControls": true, + "showChat": false, + "showLineNumbers": false, + "useMonospaceFont": false, + "userName": false, + "userColor": false, + "rtl": false, + "alwaysShowChat": false, + "chatAndUsers": false, + "lang": "en" + }, + + /* + * Pad Shortcut Keys + */ + "padShortcutEnabled" : { + "altF9": true, /* focus on the File Menu and/or editbar */ + "altC": false, /* focus on the Chat window */ + "cmdShift2": true, /* shows a gritter popup showing a line author */ + "delete": true, + "return": true, + "esc": true, /* in mozilla versions 14-19 avoid reconnecting pad */ + "cmdS": true, /* save a revision */ + "tab": true, /* indent */ + "cmdZ": true, /* undo/redo */ + "cmdY": true, /* redo */ + "cmdI": true, /* italic */ + "cmdB": true, /* bold */ + "cmdU": true, /* underline */ + "cmd5": true, /* strike through */ + "cmdShiftL": true, /* unordered list */ + "cmdShiftN": true, /* ordered list */ + "cmdShift1": true, /* ordered list */ + "cmdShiftC": true, /* clear authorship */ + "cmdH": true, /* backspace */ + "ctrlHome": true, /* scroll to top of pad */ + "pageUp": true, + "pageDown": true + }, + + /* + * Enables the use of a different server. We have a different one that syncs changes from the original server. + * It is hosted on GitHub and should not be blocked by many firewalls. + * https://etherpad.org/ep_infos + */ + + "updateServer": "https://etherpad.org/ep_infos", + + /* + * Should we suppress errors from being visible in the default Pad Text? + */ + "suppressErrorsInPadText": true, + + /* + * If this option is enabled, a user must have a session to access pads. + * This effectively allows only group pads to be accessed. + */ + "requireSession": true, + + /* + * Users may edit pads but not create new ones. + * + * Pad creation is only via the API. + * This applies both to group pads and regular pads. + */ + "editOnly": true, + + /* + * If true, all css & js will be minified before sending to the client. + * + * This will improve the loading performance massively, but makes it difficult + * to debug the javascript/css + */ + "minify": true, + + /* + * How long may clients use served javascript code (in seconds)? + * + * Not setting this may cause problems during deployment. + * Set to 0 to disable caching. + */ + "maxAge": 21600, // 60 * 60 * 6 = 6 hours + + /* + * Absolute path to the Abiword executable. + * + * Abiword is needed to get advanced import/export features of pads. Setting + * it to null disables Abiword and will only allow plain text and HTML + * import/exports. + */ + "abiword": null, + + /* + * This is the absolute path to the soffice executable. + * + * LibreOffice can be used in lieu of Abiword to export pads. + * Setting it to null disables LibreOffice exporting. + */ + "soffice": "/etherpad-export.sh", + + /* + * Allow import of file types other than the supported ones: + * txt, doc, docx, rtf, odt, html & htm + */ + "allowUnknownFileEnds": false, + + /* + * This setting is used if you require authentication of all users. + * + * Note: "/admin" always requires authentication. + */ + "requireAuthentication": false, + + /* + * Require authorization by a module, or a user with is_admin set, see below. + */ + "requireAuthorization": false, + + /* + * When you use NGINX or another proxy/load-balancer set this to true. + * + * This is especially necessary when the reverse proxy performs SSL + * termination, otherwise the cookies will not have the "secure" flag. + * + * The other effect will be that the logs will contain the real client's IP, + * instead of the reverse proxy's IP. + */ + "trustProxy": true, + + /* + * Settings controlling the session cookie issued by Etherpad. + */ + "cookie": { + /* + * How often (in milliseconds) the key used to sign the express_sid cookie + * should be rotated. Long rotation intervals reduce signature verification + * overhead (because there are fewer historical keys to check) and database + * load (fewer historical keys to store, and less frequent queries to + * get/update the keys). Short rotation intervals are slightly more secure. + * + * Multiple Etherpad processes sharing the same database (table) is + * supported as long as the clock sync error is significantly less than this + * value. + * + * Key rotation can be disabled (not recommended) by setting this to 0 or + * null, or by disabling session expiration (see sessionLifetime). + */ + "keyRotationInterval": 86400000, // = 1d * 24h/d * 60m/h * 60s/m * 1000ms/s + + /* + * Value of the SameSite cookie property. "Lax" is recommended unless + * Etherpad will be embedded in an iframe from another site, in which case + * this must be set to "None". Note: "None" will not work (the browser will + * not send the cookie to Etherpad) unless https is used to access Etherpad + * (either directly or via a reverse proxy with "trustProxy" set to true). + * + * "Strict" is not recommended because it has few security benefits but + * significant usability drawbacks vs. "Lax". See + * https://stackoverflow.com/q/41841880 for discussion. + */ + "sameSite": "None", + + /* + * How long (in milliseconds) after navigating away from Etherpad before the + * user is required to log in again. (The express_sid cookie is set to + * expire at time now + sessionLifetime when first created, and its + * expiration time is periodically refreshed to a new now + sessionLifetime + * value.) If requireAuthentication is false then this value does not really + * matter. + * + * The "best" value depends on your users' usage patterns and the amount of + * convenience you desire. A long lifetime is more convenient (users won't + * have to log back in as often) but has some drawbacks: + * - It increases the amount of state kept in the database. + * - It might weaken security somewhat: The cookie expiration is refreshed + * indefinitely without consulting authentication or authorization + * hooks, so once a user has accessed a pad, the user can continue to + * use the pad until the user leaves for longer than sessionLifetime. + * - More historical keys (sessionLifetime / keyRotationInterval) must be + * checked when verifying signatures. + * + * Session lifetime can be set to infinity (not recommended) by setting this + * to null or 0. Note that if the session does not expire, most browsers + * will delete the cookie when the browser exits, but a session record is + * kept in the database forever. + */ + "sessionLifetime": 864000000, // = 10d * 24h/d * 60m/h * 60s/m * 1000ms/s + + /* + * How long (in milliseconds) before the expiration time of an active user's + * session is refreshed (to now + sessionLifetime). This setting affects the + * following: + * - How often a new session expiration time will be written to the + * database. + * - How often each user's browser will ping the Etherpad server to + * refresh the expiration time of the session cookie. + * + * High values reduce the load on the database and the load from browsers, + * but can shorten the effective session lifetime if Etherpad is restarted + * or the user navigates away. + * + * Automatic session refreshes can be disabled (not recommended) by setting + * this to null. + */ + "sessionRefreshInterval": 86400000 // = 1d * 24h/d * 60m/h * 60s/m * 1000ms/s + }, + + /* + * Privacy: disable IP logging + */ + "disableIPlogging": true, + + /* + * Time (in seconds) to automatically reconnect pad when a "Force reconnect" + * message is shown to user. + * + * Set to 0 to disable automatic reconnection. + */ + "automaticReconnectionTimeout": 0, + + /* + * By default, when caret is moved out of viewport, it scrolls the minimum + * height needed to make this line visible. + */ + "scrollWhenFocusLineIsOutOfViewport": { + + /* + * Percentage of viewport height to be additionally scrolled. + * + * E.g.: use "percentage.editionAboveViewport": 0.5, to place caret line in + * the middle of viewport, when user edits a line above of the + * viewport + * + * Set to 0 to disable extra scrolling + */ + "percentage": { + "editionAboveViewport": 0, + "editionBelowViewport": 0 + }, + + /* + * Time (in milliseconds) used to animate the scroll transition. + * Set to 0 to disable animation + */ + "duration": 0, + + /* + * Flag to control if it should scroll when user places the caret in the + * last line of the viewport + */ + "scrollWhenCaretIsInTheLastLineOfViewport": false, + + /* + * Percentage of viewport height to be additionally scrolled when user + * presses arrow up in the line of the top of the viewport. + * + * Set to 0 to let the scroll to be handled as default by Etherpad + */ + "percentageToScrollWhenUserPressesArrowUp": 0 + }, + + /* + * User accounts. These accounts are used by: + * - default HTTP basic authentication if no plugin handles authentication + * - some but not all authentication plugins + * - some but not all authorization plugins + * + * User properties: + * - password: The user's password. Some authentication plugins will ignore + * this. + * - is_admin: true gives access to /admin. Defaults to false. If you do not + * uncomment this, /admin will not be available! + * - readOnly: If true, this user will not be able to create new pads or + * modify existing pads. Defaults to false. + * - canCreate: If this is true and readOnly is false, this user can create + * new pads. Defaults to true. + * + * Authentication and authorization plugins may define additional properties. + * + * WARNING: passwords should not be stored in plaintext in this file. + * If you want to mitigate this, please install ep_hash_auth and + * follow the section "secure your installation" in README.md + */ + + /* + "users": { + "admin": { + // 1) "password" can be replaced with "hash" if you install ep_hash_auth + // 2) please note that if password is null, the user will not be created + "password": "changeme1", + "is_admin": true + }, + "user": { + // 1) "password" can be replaced with "hash" if you install ep_hash_auth + // 2) please note that if password is null, the user will not be created + "password": "changeme1", + "is_admin": false + } + }, + */ + + /* + * Restrict socket.io transport methods + */ + "socketTransportProtocols" : ["websocket", "polling"], + + "socketIo": { + /* + * Maximum permitted client message size (in bytes). All messages from + * clients that are larger than this will be rejected. Large values make it + * possible to paste large amounts of text, and plugins may require a larger + * value to work properly, but increasing the value increases susceptibility + * to denial of service attacks (malicious clients can exhaust memory). + */ + "maxHttpBufferSize": 50000 + }, + + /* + * Allow Load Testing tools to hit the Etherpad Instance. + * + * WARNING: this will disable security on the instance. + */ + "loadTest": false, + + /** + * Disable dump of objects preventing a clean exit + */ + "dumpOnUncleanExit": false, + + /* + * Disable indentation on new line when previous line ends with some special + * chars (':', '[', '(', '{') + */ + + /* + "indentationOnNewLine": false, + */ + + "ep_pad_ttl": { + "ttl": 86400, // 24 hours + "timeout": 30, + "interval": 21600 // 6 hours + }, + + /* + * Redis publisher plugin configuration. + * npm i git+https://git@github.com/mconf/ep_redis_publisher.git + */ + + "ep_redis_publisher": { + "host": "redis", + "port": 6379 + }, + + /* + * From Etherpad 1.8.3 onwards, import and export of pads is always rate + * limited. + * + * The default is to allow at most 10 requests per IP in a 90 seconds window. + * After that the import/export request is rejected. + * + * See https://github.com/nfriedly/express-rate-limit for more options + */ + "importExportRateLimiting": { + // duration of the rate limit window (milliseconds) + "windowMs": 90000, + + // maximum number of requests per IP to allow during the rate limit window + "max": 32 + }, + + /* + * From Etherpad 1.8.3 onwards, the maximum allowed size for a single imported + * file is always bounded. + * + * File size is specified in bytes. Default is 50 MB. + */ + "importMaxFileSize": 52428800, // 50 * 1024 * 1024 + + /* + The authentication method used by the server. + The default value is sso + If you want to use the old authentication system, change this to apikey + */ + "authenticationMethod": "apikey", + + /* + * From Etherpad 1.8.5 onwards, when Etherpad is in production mode commits from individual users are rate limited + * + * The default is to allow at most 10 changes per IP in a 1 second window. + * After that the change is rejected. + * + * See https://github.com/animir/node-rate-limiter-flexible/wiki/Overall-example#websocket-single-connection-prevent-flooding for more options + */ + "commitRateLimiting": { + // duration of the rate limit window (seconds) + "duration": 1, + + // maximum number of changes per IP to allow during the rate limit window + "points": 100 + }, + + /* + * Toolbar buttons configuration. + * + * Uncomment to customize. + */ + + "toolbar": { + "left": [ + ["bold", "italic", "underline", "strikethrough"], + ["orderedlist", "unorderedlist", "undo", "redo"], + ["importexport"] + ], + "right": [[]] + }, + + /* + * Expose Etherpad version in the web interface and in the Server http header. + * + * Do not enable on production machines. + */ + "exposeVersion": false, + + /* + * The log level we are using. + * + * Valid values: DEBUG, INFO, WARN, ERROR + */ + "loglevel": "INFO", + + /* + * The log layout type to use. + * + * Valid values: basic, colored + */ + "logLayoutType": "colored", + + /* Override any strings found in locale directories */ + "customLocaleStrings": { + "de": { + "pad.importExport.import_export": "Export", + "pad.toolbar.import_export.title": "Export zu verschiedenen Dateiformaten" + }, + "en-gb": { + "pad.importExport.import_export": "Export", + "pad.toolbar.import_export.title": "Export to different file formats" + }, + "en": { + "pad.importExport.import_export": "Export", + "pad.toolbar.import_export.title": "Export to different file formats" + }, + "es": { + "pad.importExport.import_export": "Exportar", + "pad.toolbar.import_export.title": "Exportar a diferentes formatos de archivos" + }, + "fr": { + "pad.importExport.import_export": "Exporter", + "pad.toolbar.import_export.title": "Exporter vers un format de fichier différent" + }, + "it": { + "pad.importExport.import_export": "Esportazione", + "pad.toolbar.import_export.title": "Esporta a diversi formati di file" + }, + "pt-br": { + "pad.importExport.import_export": "Exportar", + "pad.toolbar.import_export.title": "Exportar para diferentes formatos de arquivo" + }, + "pt": { + "pad.importExport.import_export": "Exportar", + "pad.toolbar.import_export.title": "Exportar para diferentes formatos de ficheiro" + } + }, + + /* Disable Admin UI tests */ + "enableAdminUITests": false, + + /* + * Enable/Disable case-insensitive pad names. + */ + "lowerCasePadIds": false +} \ No newline at end of file diff --git a/mod/freeswitch/Dockerfile b/mod/freeswitch/Dockerfile new file mode 100644 index 0000000..0153763 --- /dev/null +++ b/mod/freeswitch/Dockerfile @@ -0,0 +1,71 @@ +ARG BBB_BUILD_TAG +FROM bigbluebutton/bbb-build:$BBB_BUILD_TAG AS builder + +COPY --from=freeswitch / /build/freeswitch + +# install most recent git version for proper sparse-checkout support +# https://stackoverflow.com/questions/72223738/failed-to-initialize-sparse-checkout +RUN echo 'deb https://ppa.launchpadcontent.net/git-core/ppa/ubuntu focal main' > /etc/apt/sources.list.d/git-core-ppa.list && \ + apt-key adv --keyserver keyserver.ubuntu.com --recv-keys A1715D88E1DF1F24 && \ + apt-get update && \ + apt-get install -y git + +# get build files for bbb-freeswitch (build/packages-template/bbb-freeswitch-core/) +COPY --from=build-files / /build/ + +# mock files expected by build.sh +RUN mkdir -p /build/bbb-voice-conference/config/freeswitch/conf/ && \ + touch \ + /build/opts-build.sh \ + /build/freeswitch.service.build \ + /build/bbb-voice-conference/config/freeswitch/conf/a \ + && \ + echo "" > /usr/local/bin/fpm + +# build freeswitch +RUN cd /build && ./build.sh + + +# add english sounds +RUN mkdir -p /build/staging/opt/freeswitch/share/freeswitch && \ + wget https://ubuntu.bigbluebutton.org/sounds.tar.gz -O sounds.tar.gz && \ + tar xvfz sounds.tar.gz -C /build/staging/opt/freeswitch/share/freeswitch && \ + wget https://gitlab.senfcall.de/senfcall-public/mute-and-unmute-sounds/-/archive/master/mute-and-unmute-sounds-master.zip && \ + unzip mute-and-unmute-sounds-master.zip && \ + cd mute-and-unmute-sounds-master/sounds && \ + find . -name "*.wav" -exec /bin/bash -c "sox -v 0.3 {} /tmp/tmp.wav; cp /tmp/tmp.wav /build/staging/opt/freeswitch/share/freeswitch/sounds/en/us/callie/conference/{}" \; + +# add bigblugbutton config +ARG TAG_FS_CONFIG +COPY --from=fs-config / /build/staging/opt/freeswitch/etc/freeswitch/ + +# =============================================== + +# we are using ubuntu here, because libjpeg8 is required, but not available in debian +FROM ubuntu:22.04 +RUN apt-get update && \ + apt-get install -y \ + xmlstarlet wget iptables curl \ + libfreetype6 libcurl4 libspeex1 libspeexdsp1 libopus0 libsndfile1 libopusfile0 liblua5.2-0 libjbig0 libldns3 libedit2 libtiff5 libpng16-16 libsqlite3-0 \ + && \ +# install libopusenc0 + wget -O /tmp/libopusenc0_0.2.1-1bbb2_amd64.deb https://launchpad.net/~bigbluebutton/+archive/ubuntu/support/+files/libopusenc0_0.2.1-1bbb2_amd64.deb \ + && dpkg -i /tmp/libopusenc0_0.2.1-1bbb2_amd64.deb \ + && rm /tmp/libopusenc0_0.2.1-1bbb2_amd64.deb + +# add dockerize +COPY --from=alangecker/bbb-docker-base-java /usr/local/bin/dockerize /usr/local/bin/dockerize + +# copy over built freeswitch & config +COPY --from=builder /build/staging/opt /opt +COPY --from=builder /build/staging/etc /etc + +RUN ldconfig && \ + ln -s /opt/freeswitch/conf /etc/freeswitch && \ + groupadd freeswitch && \ + useradd --home-dir /opt/freeswitch --shell /usr/sbin/nologin -g freeswitch freeswitch + +COPY ./entrypoint.sh /entrypoint.sh +COPY ./conf /etc/freeswitch/ + +ENTRYPOINT /entrypoint.sh \ No newline at end of file diff --git a/mod/freeswitch/conf/autoload_configs/conference.conf.xml.tmpl b/mod/freeswitch/conf/autoload_configs/conference.conf.xml.tmpl new file mode 100644 index 0000000..1171614 --- /dev/null +++ b/mod/freeswitch/conf/autoload_configs/conference.conf.xml.tmpl @@ -0,0 +1,285 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + {{if not (isTrue .Env.DISABLE_SOUND_MUTED) }} + + + {{end}} + {{if not (isTrue .Env.DISABLE_SOUND_ALONE) }} + + {{end}} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/mod/freeswitch/conf/autoload_configs/event_socket.conf.xml b/mod/freeswitch/conf/autoload_configs/event_socket.conf.xml new file mode 100644 index 0000000..6d7d4d7 --- /dev/null +++ b/mod/freeswitch/conf/autoload_configs/event_socket.conf.xml @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/mod/freeswitch/conf/autoload_configs/modules.conf.xml b/mod/freeswitch/conf/autoload_configs/modules.conf.xml new file mode 100644 index 0000000..f05aa52 --- /dev/null +++ b/mod/freeswitch/conf/autoload_configs/modules.conf.xml @@ -0,0 +1,32 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/mod/freeswitch/conf/dialplan/public/dialin.xml b/mod/freeswitch/conf/dialplan/public/dialin.xml new file mode 100644 index 0000000..02c4ed7 --- /dev/null +++ b/mod/freeswitch/conf/dialplan/public/dialin.xml @@ -0,0 +1,31 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/mod/freeswitch/conf/sip_profiles/external-dialin.xml b/mod/freeswitch/conf/sip_profiles/external-dialin.xml new file mode 100644 index 0000000..fbe7ca3 --- /dev/null +++ b/mod/freeswitch/conf/sip_profiles/external-dialin.xml @@ -0,0 +1,86 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/mod/freeswitch/conf/sip_profiles/external.xml b/mod/freeswitch/conf/sip_profiles/external.xml new file mode 100644 index 0000000..53c9de1 --- /dev/null +++ b/mod/freeswitch/conf/sip_profiles/external.xml @@ -0,0 +1,115 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/mod/freeswitch/conf/vars.xml.tmpl b/mod/freeswitch/conf/vars.xml.tmpl new file mode 100644 index 0000000..b86502d --- /dev/null +++ b/mod/freeswitch/conf/vars.xml.tmpl @@ -0,0 +1,453 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/mod/freeswitch/entrypoint.sh b/mod/freeswitch/entrypoint.sh new file mode 100644 index 0000000..bf6dae1 --- /dev/null +++ b/mod/freeswitch/entrypoint.sh @@ -0,0 +1,72 @@ +#!/bin/bash -e + +# remove all SIP (port 5060) iptable rules +iptables -S INPUT | grep "\-\-dport 5060 " | cut -d " " -f 2- | xargs -rL1 iptables -D + +# block requests to 5060 (tcp/udp) +iptables -A INPUT -p tcp --dport 5060 -s 0.0.0.0/0 -j REJECT +iptables -A INPUT -p udp --dport 5060 -s 0.0.0.0/0 -j REJECT + +# allow some IPs +IFS=',' read -ra ADDR <<< "$SIP_IP_ALLOWLIST" +for IP in "${ADDR[@]}"; do + # process "$i" + echo "allow port 5060/udp for $IP" + iptables -I INPUT -p udp --dport 5060 -s $IP -j ACCEPT +done + +mkdir -p /var/freeswitch/meetings +chown -R freeswitch:daemon /var/freeswitch/meetings +chmod 777 /var/freeswitch/meetings +chown -R freeswitch:daemon /opt/freeswitch/var +chown -R freeswitch:daemon /opt/freeswitch/etc +chmod -R g-rwx,o-rwx /opt/freeswitch/etc + +# install freeswitch sounds if missing +SOUNDS_DIR=/opt/freeswitch/share/freeswitch/sounds +if [ "$SOUNDS_LANGUAGE" == "en-us-callie" ]; then + # default, is already installed + echo "" +elif [ "$SOUNDS_LANGUAGE" == "de-de-daedalus3" ]; then + if [ ! -d "$SOUNDS_DIR/de/de/daedalus3" ]; then + echo "sounds package for de-de-daedalus3 not installed yet" + wget -O /tmp/freeswitch-german-soundfiles.zip https://github.com/Daedalus3/freeswitch-german-soundfiles/archive/master.zip + mkdir -p $SOUNDS_DIR/de/de/daedalus3 + unzip /tmp/freeswitch-german-soundfiles.zip -d /tmp/ + mv /tmp/freeswitch-german-soundfiles-master $SOUNDS_DIR/de/de/daedalus3/conference + + # symlink other folders + for folder in "digits" "ivr" "misc"; do + ln -s $SOUNDS_DIR/en/us/callie/$folder $SOUNDS_DIR/de/de/daedalus3/$folder + done + + fi +else + if [ ! -f $SOUNDS_DIR/$SOUNDS_LANGUAGE.installed ]; then + echo "sounds package for $SOUNDS_LANGUAGE not installed yet" + + # get filename of latest release for this sound package + FILENAME=$(curl -s https://files.freeswitch.org/releases/sounds/ | grep -i $SOUNDS_LANGUAGE 2> /dev/null | awk -F'\"' '{print $8}' | grep -E '\-48000-.*\.gz$' | sort -V | tail -n 1) + + if [ "$FILENAME" = "" ]; then + echo "Error: could not find sounds for language '$SOUNDS_LANGUAGE'" + echo "make sure to specify a value for SOUNDS_LANGUAGE which exists on https://files.freeswitch.org/releases/sounds/" + exit 1 + fi + for bitrate in 8000 16000 32000 48000; do + URL=https://files.freeswitch.org/releases/sounds/$(echo $FILENAME | sed "s/48000/$bitrate/") + wget -O /tmp/sounds.tar.gz $URL + tar xvfz /tmp/sounds.tar.gz -C $SOUNDS_DIR + done + + touch $SOUNDS_DIR/$SOUNDS_LANGUAGE.installed + fi +fi + + +export SOUNDS_PATH=$SOUNDS_DIR/$(echo "$SOUNDS_LANGUAGE" | sed 's|-|/|g') + +dockerize \ + -template /etc/freeswitch/vars.xml.tmpl:/etc/freeswitch/vars.xml \ + -template /etc/freeswitch/autoload_configs/conference.conf.xml.tmpl:/etc/freeswitch/autoload_configs/conference.conf.xml \ + /opt/freeswitch/bin/freeswitch -u freeswitch -g daemon -nonat -nf diff --git a/mod/fsesl-akka/Dockerfile b/mod/fsesl-akka/Dockerfile new file mode 100644 index 0000000..8f66f59 --- /dev/null +++ b/mod/fsesl-akka/Dockerfile @@ -0,0 +1,30 @@ +ARG BBB_BUILD_TAG +FROM bigbluebutton/bbb-build:$BBB_BUILD_TAG AS builder + +COPY --from=src-common-message / /bbb-common-message + +# build bbb-common-message +RUN cd /bbb-common-message && ./deploy.sh + +# =================================================== +COPY --from=src-fsesl-client / /bbb-fsesl-client +RUN cd /bbb-fsesl-client && ./deploy.sh + +COPY --from=src-fsesl-akka / /source + +# compile and unzip bin +RUN cd /source \ + && sbt universal:packageBin +RUN unzip /source/target/universal/bbb-fsesl-akka-0.0.2.zip -d / + +# # =================================================== + +FROM alangecker/bbb-docker-base-java + +COPY --from=builder /bbb-fsesl-akka-0.0.2 /bbb-fsesl-akka +COPY bbb-fsesl-akka.conf /etc/bigbluebutton/bbb-fsesl-akka.conf.tmpl +COPY logback.xml /bbb-fsesl-akka/conf/logback.xml +COPY entrypoint.sh /entrypoint.sh + +USER bigbluebutton +ENTRYPOINT /entrypoint.sh \ No newline at end of file diff --git a/mod/fsesl-akka/bbb-fsesl-akka.conf b/mod/fsesl-akka/bbb-fsesl-akka.conf new file mode 100644 index 0000000..12acb3e --- /dev/null +++ b/mod/fsesl-akka/bbb-fsesl-akka.conf @@ -0,0 +1,18 @@ +// include default config from upstream +include "/bbb-fsesl-akka/conf/application.conf" + + +freeswitch { + esl { + host="freeswitch" + password="FSESL_PASSWORD" + } +} + +redis { + host="redis" +} + +http { + interface = "0.0.0.0" +} \ No newline at end of file diff --git a/mod/fsesl-akka/entrypoint.sh b/mod/fsesl-akka/entrypoint.sh new file mode 100644 index 0000000..2000ce3 --- /dev/null +++ b/mod/fsesl-akka/entrypoint.sh @@ -0,0 +1,9 @@ +#!/bin/sh -e + +TARGET=/etc/bigbluebutton/bbb-fsesl-akka.conf + +cp /etc/bigbluebutton/bbb-fsesl-akka.conf.tmpl $TARGET +sed -i "s/FSESL_PASSWORD/$FSESL_PASSWORD/" $TARGET + +cd /bbb-fsesl-akka +/bbb-fsesl-akka/bin/bbb-fsesl-akka \ No newline at end of file diff --git a/mod/fsesl-akka/logback.xml b/mod/fsesl-akka/logback.xml new file mode 100644 index 0000000..9e587d9 --- /dev/null +++ b/mod/fsesl-akka/logback.xml @@ -0,0 +1,17 @@ + + + + + %d{"yyyy-MM-dd'T'HH:mm:ss.SSSXXX"} %-5level %logger{35} - %msg%n + + + + + + + + + + + + diff --git a/mod/haproxy.cfg b/mod/haproxy.cfg new file mode 100644 index 0000000..feb3532 --- /dev/null +++ b/mod/haproxy.cfg @@ -0,0 +1,80 @@ +global + log stdout format raw local0 debug + + maxconn 20480 + ############# IMPORTANT ################################# + ## DO NOT SET CHROOT OTHERWISE YOU HAVE TO CHANGE THE ## + ## acme-http01-webroot.lua file ## + # chroot /jail ## + ######################################################### + lua-load /etc/haproxy/acme-http01-webroot.lua + # + # SSL options + 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 4096 + + + # workaround for bug #14 (Cert renewal blocks HAProxy indefinitely with Websocket connections) + hard-stop-after 3s + + +# DNS runt-time resolution on backend hosts +resolvers docker + nameserver dns "127.0.0.11:53" + +defaults + log global + mode http + timeout connect 5000ms + timeout client 50000ms + timeout server 50000ms + # option forwardfor + option httplog + + option dontlognull + timeout connect 5000 + timeout client 50000 + timeout server 50000 + + # never fail on address resolution + default-server init-addr last,libc,none + +frontend http + bind *:80,[::]:80 + mode http + acl url_acme_http01 path_beg /.well-known/acme-challenge/ + http-request use-service lua.acme-http01 if METH_GET url_acme_http01 + redirect scheme https code 301 if !{ ssl_fc } + +frontend nginx_or_turn + bind *:443,:::443 ssl crt /etc/haproxy/certs/ 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 webrtc-sfu:3478 check + +backend nginx + mode tcp + server localhost nginx:48081 send-proxy check + +backend nginx-http2 + mode tcp + server localhost nginx:48082 send-proxy check diff --git a/mod/haproxy/Dockerfile b/mod/haproxy/Dockerfile new file mode 100644 index 0000000..f172a4b --- /dev/null +++ b/mod/haproxy/Dockerfile @@ -0,0 +1,4 @@ +FROM ghcr.io/tomdess/docker-haproxy-certbot:2.8.10 + +# overwrite bootstrap.sh +COPY bootstrap.sh /bootstrap.sh \ No newline at end of file diff --git a/mod/haproxy/bootstrap.sh b/mod/haproxy/bootstrap.sh new file mode 100644 index 0000000..c17e9f9 --- /dev/null +++ b/mod/haproxy/bootstrap.sh @@ -0,0 +1,30 @@ +#!/usr/bin/env bash + +set -e + +# save container environment variables to use it +# in cron scripts + +declare -p | grep -Ev '^declare -[[:alpha:]]*r' > /container.env + +# when used with an IP, we'll also disable certbot +if [[ "$CERT1" =~ ^[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+$ ]]; then + IGNORE_TLS_CERT_ERRORS=true +fi + +if [ "$IGNORE_TLS_CERT_ERRORS" ] && [ "$IGNORE_TLS_CERT_ERRORS" != "false" ]; then + # use self signed certificate + if [ ! -f /etc/haproxy/certs/haproxy-10.7.7.1.pem ]; then + mkdir -p /etc/haproxy/certs + # generate self signed certificate + openssl req -x509 -nodes -days 700 -newkey rsa:2048 \ + -keyout /tmp/domain.key -out /tmp/domain.crt \ + -subj "/C=CA/ST=Quebec/L=Montreal/O=BigBlueButton Development/OU=bbb-docker/CN=10.7.7.1" + + cat /tmp/domain.key /tmp/domain.crt | tee /etc/haproxy/certs/haproxy-10.7.7.1.pem >/dev/null + fi +else + # obtain certificates from lets encrypt + /certs.sh +fi +supervisord -c /etc/supervisord.conf -n \ No newline at end of file diff --git a/mod/haproxy/protocolmap b/mod/haproxy/protocolmap new file mode 100644 index 0000000..0f85dd0 --- /dev/null +++ b/mod/haproxy/protocolmap @@ -0,0 +1,52 @@ +a nginx +b nginx +c nginx +d nginx +e nginx +f nginx +g nginx +h nginx +i nginx +j nginx +k nginx +l nginx +m nginx +n nginx +o nginx +p nginx +q nginx +r nginx +s nginx +t nginx +u nginx +v nginx +w nginx +x nginx +y nginx +z nginx +A nginx +B nginx +C nginx +D nginx +E nginx +F nginx +G nginx +H nginx +I nginx +J nginx +K nginx +L nginx +M nginx +N nginx +O nginx +P nginx +Q nginx +R nginx +S nginx +T nginx +U nginx +V nginx +W nginx +X nginx +Y nginx +Z nginx \ No newline at end of file diff --git a/mod/html5-dev/Dockerfile b/mod/html5-dev/Dockerfile new file mode 100644 index 0000000..474410a --- /dev/null +++ b/mod/html5-dev/Dockerfile @@ -0,0 +1,13 @@ +ARG BBB_BUILD_TAG +FROM bigbluebutton/bbb-build:$BBB_BUILD_TAG + + +# use /tmp as home dir as writeable directory for whatever UID we get +ENV HOME /tmp + +# allow all user to access .nvm in root +RUN chmod 755 /root + +WORKDIR /app +COPY /entrypoint.sh /entrypoint.sh +ENTRYPOINT /entrypoint.sh diff --git a/mod/html5-dev/entrypoint.sh b/mod/html5-dev/entrypoint.sh new file mode 100644 index 0000000..09c1ee1 --- /dev/null +++ b/mod/html5-dev/entrypoint.sh @@ -0,0 +1,11 @@ +set -e + +# enable nvm +. /root/.nvm/nvm.sh + +if [ -n "$1" ]; then + exec "$@" +else + npm install + npm start -- --host 0.0.0.0 +fi \ No newline at end of file diff --git a/mod/initdb.sh b/mod/initdb.sh new file mode 100644 index 0000000..fb0560e --- /dev/null +++ b/mod/initdb.sh @@ -0,0 +1,21 @@ +#!/bin/bash + +set -e +set -u + +function create_user_and_database() { + local database=$1 + echo " Creating user and database '$database'" + psql -v ON_ERROR_STOP=1 --username "$POSTGRES_USER" <<-EOSQL + CREATE DATABASE $database; + GRANT ALL PRIVILEGES ON DATABASE $database TO $POSTGRES_USER; +EOSQL +} + +if [ -n "$POSTGRES_MULTIPLE_DATABASES" ]; then + echo "Multiple database creation requested: $POSTGRES_MULTIPLE_DATABASES" + for db in $(echo $POSTGRES_MULTIPLE_DATABASES | tr ',' ' '); do + create_user_and_database $db + done + echo "Multiple databases created" +fi \ No newline at end of file diff --git a/mod/livekit/livekit.yaml b/mod/livekit/livekit.yaml new file mode 100644 index 0000000..55653f3 --- /dev/null +++ b/mod/livekit/livekit.yaml @@ -0,0 +1,15 @@ +port: 7880 +log_level: debug +# when enabled, LiveKit will expose prometheus metrics on :6789/metrics +#prometheus_port: 6789 +rtc: + port_range_start: 16384 + port_range_end: 32768 + use_external_ip: false +redis: + # redis is recommended for production deploys + address: redis:6379 + +keys: + # TODO: change keys + TEST: TEST diff --git a/mod/nginx.conf b/mod/nginx.conf new file mode 100644 index 0000000..d1e9b3d --- /dev/null +++ b/mod/nginx.conf @@ -0,0 +1,33 @@ +user nginx; +worker_processes auto; + +error_log /var/log/nginx/error.log warn; +pid /var/run/nginx.pid; + +worker_rlimit_nofile 20000; + +events { + worker_connections 10000; +} + + +http { + include /etc/nginx/mime.types; + default_type application/octet-stream; + + log_format main '$remote_addr - $remote_user [$time_local] "$request" ' + '$status $body_bytes_sent "$http_referer" ' + '"$http_user_agent" "$http_x_forwarded_for"'; + + access_log /var/log/nginx/access.log main; + + sendfile on; + #tcp_nopush on; + + keepalive_timeout 65; + + #gzip on; + + include /etc/nginx/conf.d/*.conf; + +} diff --git a/mod/nginx/Dockerfile b/mod/nginx/Dockerfile new file mode 100644 index 0000000..dc92c41 --- /dev/null +++ b/mod/nginx/Dockerfile @@ -0,0 +1,49 @@ +ARG BBB_BUILD_TAG +FROM bigbluebutton/bbb-build:$BBB_BUILD_TAG AS builder-learning-dashboard +COPY --from=src-learning-dashboard / /bbb-learning-dashboard +RUN cd /bbb-learning-dashboard && npm ci && npm run build + + +FROM bigbluebutton/bbb-build:$BBB_BUILD_TAG AS builder-playback +COPY --from=src-playback / /bbb-playback +RUN cd /bbb-playback && npm install && npm run-script build + +FROM bigbluebutton/bbb-build:$BBB_BUILD_TAG AS builder-html5 +COPY --from=src-html5 / /source +RUN cd /source && CI=true npm ci +RUN cd /source && DISABLE_ESLINT_PLUGIN=true npm run build-safari && npm run build +RUN cd /source/dist && \ + HASH=$(ls | grep -Eo 'bundle\.[a-f0-9]{20}\.js' | head -n 1 | grep -Eo '[a-f0-9]{20}') && \ + if [ -z "$HASH" ]; then \ + echo "Bundle hash not found."; \ + else \ + for FILE in *.safari.js *.safari.js.map; do \ + if [[ "$FILE" == *"$HASH"* ]]; then \ + continue; \ + fi; \ + PREFIX="${FILE%%.safari.js*}"; \ + SUFFIX="${FILE#*.safari.js}"; \ + NEW_NAME="${PREFIX}.${HASH}.safari.js${SUFFIX}"; \ + echo "Renaming $FILE → $NEW_NAME"; \ + mv "$FILE" "$NEW_NAME"; \ + done; \ + fi + +RUN find /source/dist -name '*.js' -exec gzip -k -f -9 '{}' \; \ + && find /source/dist -name '*.css' -exec gzip -k -f -9 '{}' \; \ + && find /source/dist -name '*.wasm' -exec gzip -k -f -9 '{}' \; + +RUN sed -i "s/?v=VERSION/?v=$BBB_BUILD_TAG/g" /source/dist/index.html && \ + sed -i "s/?v=VERSION/?v=$BBB_BUILD_TAG/g" /source/dist/stylesheets/fonts.css + +# -------------------- + +FROM nginx:1.29-alpine + +COPY --from=builder-learning-dashboard /bbb-learning-dashboard/build /www/learning-analytics-dashboard/ +COPY --from=builder-playback /bbb-playback/build /www/playback/presentation/2.3 +COPY --from=builder-html5 /source/dist /usr/share/bigbluebutton/html5-client/ +COPY ./bbb /etc/nginx/bbb +COPY ./bigbluebutton /etc/nginx/conf.d/default.conf +COPY ./bbb-graphql-client-settings-cache.conf /etc/nginx/conf.d/bbb-graphql-client-settings-cache.conf +COPY ./nginx.conf /etc/nginx/nginx.conf diff --git a/mod/nginx/bbb-graphql-client-settings-cache.conf b/mod/nginx/bbb-graphql-client-settings-cache.conf new file mode 100644 index 0000000..fbea8b7 --- /dev/null +++ b/mod/nginx/bbb-graphql-client-settings-cache.conf @@ -0,0 +1 @@ +proxy_cache_path /tmp/hasura-client-settings-cache levels=1:2 keys_zone=client_settings_cache:64m inactive=2880m use_temp_path=off; \ No newline at end of file diff --git a/mod/nginx/bbb/bbb-html5.nginx b/mod/nginx/bbb/bbb-html5.nginx new file mode 100644 index 0000000..fc73a84 --- /dev/null +++ b/mod/nginx/bbb/bbb-html5.nginx @@ -0,0 +1,21 @@ +# running in production (static assets) +location /html5client { + gzip_static on; + alias /usr/share/bigbluebutton/html5-client/; + index index.html; + try_files $uri $uri/ =404; +} + +location /html5client/locales { + alias /usr/share/bigbluebutton/html5-client/locales; + autoindex on; + autoindex_format json; +} + +location /html5client/wasm { + types { + application/wasm wasm; + } + gzip_static on; + alias /usr/share/bigbluebutton/html5-client/wasm; +} \ No newline at end of file diff --git a/mod/nginx/bbb/demo.nginx b/mod/nginx/bbb/demo.nginx new file mode 100644 index 0000000..8b68775 --- /dev/null +++ b/mod/nginx/bbb/demo.nginx @@ -0,0 +1,26 @@ + # Forward request to /demo to tomcat. This is for + # the BigBlueButton api demos. + location = /demo/ { + return 301 /demo/demo1.jsp; + } + location /demo { + proxy_pass http://host.docker.internal:8001; + proxy_redirect default; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + + # Allow 30M uploaded presentation document. + client_max_body_size 30m; + client_body_buffer_size 128k; + + proxy_connect_timeout 90; + proxy_send_timeout 90; + proxy_read_timeout 90; + + proxy_buffer_size 4k; + proxy_buffers 4 32k; + proxy_busy_buffers_size 64k; + proxy_temp_file_write_size 64k; + + include fastcgi_params; + } + diff --git a/mod/nginx/bbb/graphql.nginx b/mod/nginx/bbb/graphql.nginx new file mode 100644 index 0000000..be1f8cd --- /dev/null +++ b/mod/nginx/bbb/graphql.nginx @@ -0,0 +1,71 @@ +# Websocket connection +location /graphql { + proxy_http_version 1.1; + proxy_set_header Upgrade $http_upgrade; + proxy_set_header Connection "Upgrade"; + proxy_set_header Host $host; + #proxy_pass http://bbb-graphql-server:8085; #Hasura (it requires to change the location to /v1/graphql) + proxy_pass http://bbb-graphql-middleware:8378; #Graphql Middleware +} + +#DEPRECATED: +#This endpoint is being replaced by /api/rest/meetingStaticData (which contain clientSettings and more) +#It will be removed in BBB 3.1 +location /api/rest/clientSettings { + auth_request /bigbluebutton/connection/checkGraphqlAuthorization; + auth_request_set $meeting_id $sent_http_meeting_id; + + proxy_cache client_settings_cache; + proxy_cache_key "$uri|$meeting_id"; + proxy_cache_use_stale updating; + proxy_cache_valid 24h; + proxy_cache_lock on; + proxy_cache_lock_timeout 5s; # how long other requests may wait for the first one holding the cache lock + proxy_cache_lock_age 10s; # consider the lock stale after this time (prevents a stuck lock if upstream hangs) + # proxy_cache_background_update on; # optional: serve stale while refreshing the cache in background + + add_header X-Cached $upstream_cache_status; + + proxy_http_version 1.1; + proxy_set_header Connection ""; + proxy_connect_timeout 3s; # max time to establish TCP connection to Hasura + proxy_send_timeout 15s; # max time to send the request to Hasura + proxy_read_timeout 30s; # max time to wait for Hasura’s response + proxy_set_header Host $host; + proxy_pass http://bbb-graphql-server:8085; #Hasura +} + +#Set cache system for meeting static data +location /api/rest/meetingStaticData { + auth_request /bigbluebutton/connection/checkGraphqlAuthorization; + auth_request_set $meeting_id $sent_http_meeting_id; + + proxy_cache client_settings_cache; + proxy_cache_key "$uri|$meeting_id"; + proxy_cache_use_stale updating; + proxy_cache_valid 24h; + proxy_cache_lock on; + proxy_cache_lock_timeout 5s; # how long other requests may wait for the first one holding the cache lock + proxy_cache_lock_age 10s; # consider the lock stale after this time (prevents a stuck lock if upstream hangs) + # proxy_cache_background_update on; # optional: serve stale while refreshing the cache in background + + add_header X-Cached $upstream_cache_status; + + proxy_http_version 1.1; + proxy_set_header Connection ""; + proxy_connect_timeout 3s; # max time to establish TCP connection to Hasura + proxy_send_timeout 15s; # max time to send the request to Hasura + proxy_read_timeout 30s; # max time to wait for Hasura’s response + proxy_set_header Host $host; + proxy_pass http://bbb-graphql-server:8085; #Hasura +} + +location /api/rest/userMetadata { + auth_request /bigbluebutton/connection/checkGraphqlAuthorization; + auth_request_set $meeting_id $sent_http_meeting_id; + + proxy_http_version 1.1; + proxy_set_header Connection ""; + proxy_set_header Host $host; + proxy_pass http://bbb-graphql-server:8085; #Hasura +} diff --git a/mod/nginx/bbb/learning-dashboard.nginx b/mod/nginx/bbb/learning-dashboard.nginx new file mode 100644 index 0000000..f0b4fc3 --- /dev/null +++ b/mod/nginx/bbb/learning-dashboard.nginx @@ -0,0 +1,4 @@ +location /learning-analytics-dashboard/ { + alias /www/learning-analytics-dashboard/; + autoindex off; +} diff --git a/mod/nginx/bbb/livekit.nginx b/mod/nginx/bbb/livekit.nginx new file mode 100644 index 0000000..c295562 --- /dev/null +++ b/mod/nginx/bbb/livekit.nginx @@ -0,0 +1,11 @@ +location /livekit/ { + proxy_pass http://127.0.0.1:7880/; + proxy_http_version 1.1; + proxy_set_header Upgrade $http_upgrade; + proxy_set_header Connection "Upgrade"; + + proxy_read_timeout 60s; + proxy_send_timeout 60s; + client_body_timeout 60s; + send_timeout 60s; +} diff --git a/mod/nginx/bbb/loadbalancer.nginx b/mod/nginx/bbb/loadbalancer.nginx new file mode 100644 index 0000000..e254118 --- /dev/null +++ b/mod/nginx/bbb/loadbalancer.nginx @@ -0,0 +1,4 @@ +# If you run this BBB node behind a proxy loadbalancer set the hostname of +# the loadbalancer here. This will be used to add CORS headers so requesting +# slides and API calls won't fail +set $bbb_loadbalancer_node ''; diff --git a/mod/nginx/bbb/notes-playback.nginx b/mod/nginx/bbb/notes-playback.nginx new file mode 100644 index 0000000..16f7c43 --- /dev/null +++ b/mod/nginx/bbb/notes-playback.nginx @@ -0,0 +1,4 @@ +location /notes { + root /var/bigbluebutton/published; + index index.html index.htm; +} \ No newline at end of file diff --git a/mod/nginx/bbb/notes.nginx b/mod/nginx/bbb/notes.nginx new file mode 100644 index 0000000..4965d2d --- /dev/null +++ b/mod/nginx/bbb/notes.nginx @@ -0,0 +1,77 @@ +# https://github.com/ether/etherpad-lite/wiki/How-to-put-Etherpad-Lite-behind-a-reverse-Proxy +location /pad/p/ { + # Avoid setting the user name from the embedded URL + if ($arg_userName) { + return 401; + } + + rewrite /pad/p/(.*) /p/$1 break; + rewrite ^/pad/p$ /pad/p/ permanent; + proxy_pass http://etherpad:9001/p; + proxy_pass_header Server; + proxy_redirect /p /pad/p; + proxy_set_header Host $host; + proxy_buffering off; + + proxy_set_header X-Real-IP $remote_addr; # http://wiki.nginx.org/HttpProxyModule + proxy_set_header X-Forwarded-For $remote_addr; # EP logs to show the actual remote IP + proxy_set_header X-Forwarded-Proto $real_scheme; # for EP to set secure cookie flag when https is used + proxy_http_version 1.1; + + auth_request /bigbluebutton/connection/checkAuthorization; + auth_request_set $auth_status $upstream_status; +} + +location /pad/auth_session { + rewrite /pad/auth_session(.*) /auth_session$1 break; + proxy_pass http://etherpad:9001/; + proxy_pass_header Server; + proxy_set_header Host $host; + proxy_buffering off; + auth_request /bigbluebutton/connection/checkAuthorization; + auth_request_set $auth_status $upstream_status; +} + +location /pad { + rewrite /pad/(.*) /$1 break; + rewrite ^/pad$ /pad/ permanent; + proxy_pass http://etherpad:9001/; + proxy_pass_header Server; + proxy_redirect / /pad/; + proxy_set_header Host $host; + proxy_buffering off; +} + +location /pad/socket.io/socket.io.js { + rewrite /pad/socket.io/socket.io.js /socket.io/socket.io.js break; + proxy_pass http://etherpad:9001/; + proxy_set_header Host $host; + proxy_buffering off; +} + +location /pad/socket.io { + rewrite /pad/socket.io/(.*) /socket.io/$1 break; + proxy_pass http://etherpad:9001/; + proxy_redirect / /pad/; + proxy_set_header Host $host; + proxy_buffering off; + proxy_set_header X-Real-IP $remote_addr; # http://wiki.nginx.org/HttpProxyModule + proxy_set_header X-Forwarded-For $remote_addr; # EP logs to show the actual remote IP + proxy_set_header X-Forwarded-Proto $real_scheme; # for EP to set secure cookie flag when https is used + proxy_set_header Host $host; # pass the host header + proxy_http_version 1.1; # recommended with keepalive connections + # WebSocket proxying - from http://nginx.org/en/docs/http/websocket.html + proxy_set_header Upgrade $http_upgrade; + proxy_set_header Connection "Upgrade"; + + auth_request /bigbluebutton/connection/checkAuthorization; + auth_request_set $auth_status $upstream_status; +} + +location /static { + rewrite /static/(.*) /static/$1 break; + proxy_pass http://etherpad:9001/; + proxy_set_header Host $host; + proxy_buffering off; +} + diff --git a/mod/nginx/bbb/playback-video.nginx b/mod/nginx/bbb/playback-video.nginx new file mode 100644 index 0000000..39d0c68 --- /dev/null +++ b/mod/nginx/bbb/playback-video.nginx @@ -0,0 +1,21 @@ +# This file is part of BigBlueButton. +# +# Copyright © BigBlueButton Inc. and by respective authors. +# +# BigBlueButton is free software: you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by the +# Free Software Foundation, either version 3.0 of the License, or (at your +# option) any later version. +# +# BigBlueButton is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +# FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more +# details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with BigBlueButton. If not, see . + +location /playback/video/ { + alias /var/bigbluebutton/published/video/; + index index.html index.htm; +} diff --git a/mod/nginx/bbb/podcast.nginx b/mod/nginx/bbb/podcast.nginx new file mode 100644 index 0000000..7d8c0a8 --- /dev/null +++ b/mod/nginx/bbb/podcast.nginx @@ -0,0 +1,22 @@ +# +# BigBlueButton open source conferencing system - http://www.bigbluebutton.org/ +# +# Copyright (c) 2012 BigBlueButton Inc. and by respective authors (see below). +# +# This program is free software; you can redistribute it and/or modify it under the +# terms of the GNU Lesser General Public License as published by the Free Software +# Foundation; either version 3.0 of the License, or (at your option) any later +# version. +# +# BigBlueButton is distributed in the hope that it will be useful, but WITHOUT ANY +# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A +# PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License along +# with BigBlueButton; if not, see . +# + + location /podcast { + root /var/bigbluebutton/published; + index index.html index.htm; + } \ No newline at end of file diff --git a/mod/nginx/bbb/presentation-slides.nginx b/mod/nginx/bbb/presentation-slides.nginx new file mode 100644 index 0000000..d90adeb --- /dev/null +++ b/mod/nginx/bbb/presentation-slides.nginx @@ -0,0 +1,46 @@ +# +# BigBlueButton open source conferencing system - http://www.bigbluebutton.org/ +# +# Copyright (c) 2012 BigBlueButton Inc. and by respective authors (see below). +# +# This program is free software; you can redistribute it and/or modify it under the +# terms of the GNU Lesser General Public License as published by the Free Software +# Foundation; either version 3.0 of the License, or (at your option) any later +# version. +# +# BigBlueButton is distributed in the hope that it will be useful, but WITHOUT ANY +# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A +# PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License along +# with BigBlueButton; if not, see . +# + +# Have nginx serve the presentation slides instead of tomcat as large files +# causes tomcat to OOM. (ralam sept 20, 2018) + + location ~^\/bigbluebutton\/presentation\/(?[A-Za-z0-9\-]+)\/(?[A-Za-z0-9\-]+)\/(?[A-Za-z0-9\-]+)\/svg\/(?\d+)$ { + default_type image/svg+xml; + alias /var/bigbluebutton/$meeting_id_2/$meeting_id_2/$pres_id/svgs/slide$page_num.svg; + add_header 'Access-Control-Allow-Origin' '*' always; + } + + location ~^\/bigbluebutton\/presentation\/(?[A-Za-z0-9\-]+)\/(?[A-Za-z0-9\-]+)\/(?[A-Za-z0-9\-]+)\/pdf\/(?[A-Za-z0-9]+)\/annotated_slides.pdf$ { + default_type application/pdf; + alias /var/bigbluebutton/$meeting_id_2/$meeting_id_2/$pres_id/pdfs/$job_id/annotated_slides.pdf; + add_header 'Access-Control-Allow-Origin' '*' always; + } + + location ~^\/bigbluebutton\/presentation\/(?[A-Za-z0-9\-]+)\/(?[A-Za-z0-9\-]+)\/(?[A-Za-z0-9\-]+)\/thumbnail\/(?\d+)$ { + default_type image/png; + alias /var/bigbluebutton/$meeting_id_2/$meeting_id_2/$pres_id/thumbnails/thumb-$page_num.png; + add_header 'Access-Control-Allow-Origin' '*' always; + } + + location ~^\/bigbluebutton\/presentation\/(?[A-Za-z0-9\-]+)\/(?[A-Za-z0-9\-]+)\/(?[A-Za-z0-9\-]+)\/textfiles\/(?\d+)$ { + default_type text/plain; + alias /var/bigbluebutton/$meeting_id_2/$meeting_id_2/$pres_id/textfiles/slide-$page_num.txt; + add_header 'Access-Control-Allow-Origin' '*' always; + } + + diff --git a/mod/nginx/bbb/presentation.nginx b/mod/nginx/bbb/presentation.nginx new file mode 100644 index 0000000..2199332 --- /dev/null +++ b/mod/nginx/bbb/presentation.nginx @@ -0,0 +1,28 @@ +# +# BigBlueButton open source conferencing system - http://www.bigbluebutton.org/ +# +# Copyright (c) 2012 BigBlueButton Inc. and by respective authors (see below). +# +# This program is free software; you can redistribute it and/or modify it under the +# terms of the GNU Lesser General Public License as published by the Free Software +# Foundation; either version 3.0 of the License, or (at your option) any later +# version. +# +# BigBlueButton is distributed in the hope that it will be useful, but WITHOUT ANY +# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A +# PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License along +# with BigBlueButton; if not, see . +# + location /playback/presentation/2.0/playback.html { + return 301 /playback/presentation/2.3/$arg_meetingId?$query_string; + } + location /playback/presentation/2.3 { + try_files $uri /playback/presentation/2.3/index.html; + } + + location /presentation { + root /var/bigbluebutton/published; + index index.html index.htm; + } diff --git a/mod/nginx/bbb/recording-screenshare.nginx b/mod/nginx/bbb/recording-screenshare.nginx new file mode 100644 index 0000000..d639110 --- /dev/null +++ b/mod/nginx/bbb/recording-screenshare.nginx @@ -0,0 +1,22 @@ +# +# BigBlueButton open source conferencing system - http://www.bigbluebutton.org/ +# +# Copyright (c) 2012 BigBlueButton Inc. and by respective authors (see below). +# +# This program is free software; you can redistribute it and/or modify it under the +# terms of the GNU Lesser General Public License as published by the Free Software +# Foundation; either version 3.0 of the License, or (at your option) any later +# version. +# +# BigBlueButton is distributed in the hope that it will be useful, but WITHOUT ANY +# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A +# PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License along +# with BigBlueButton; if not, see . +# + + location /recording/screenshare { + alias /var/bigbluebutton/published/screenshare; + index index.html index.htm; + } \ No newline at end of file diff --git a/mod/nginx/bbb/slides.nginx b/mod/nginx/bbb/slides.nginx new file mode 100644 index 0000000..1c3d170 --- /dev/null +++ b/mod/nginx/bbb/slides.nginx @@ -0,0 +1,28 @@ + +# +# BigBlueButton open source conferencing system - http://www.bigbluebutton.org/ +# +# Copyright (c) 2012 BigBlueButton Inc. and by respective authors (see below). +# +# This program is free software; you can redistribute it and/or modify it under the +# terms of the GNU Lesser General Public License as published by the Free Software +# Foundation; either version 3.0 of the License, or (at your option) any later +# version. +# +# BigBlueButton is distributed in the hope that it will be useful, but WITHOUT ANY +# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A +# PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License along +# with BigBlueButton; if not, see . +# + location /playback/slides { + root /var/bigbluebutton; + index index.html index.htm; + } + + location /slides { + root /var/bigbluebutton/published; + index index.html index.htm; + } + diff --git a/mod/nginx/bbb/web.nginx b/mod/nginx/bbb/web.nginx new file mode 100644 index 0000000..d854219 --- /dev/null +++ b/mod/nginx/bbb/web.nginx @@ -0,0 +1,186 @@ + # Handle request to bbb-web running within a SpringBoot Tomcat embedded servlet container. This is for BBB-API and Presentation. + location /bigbluebutton { + proxy_http_version 1.1; + + location /bigbluebutton { + proxy_pass http://bbb-web:8090; + proxy_redirect default; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + + # Workaround IE refusal to set cookies in iframe + add_header P3P 'CP="No P3P policy available"'; + } + + + location ~ "^\/bigbluebutton\/presentation\/(?[a-zA-Z0-9_-]+)/upload$" { + proxy_pass http://bbb-web:8090; + proxy_redirect default; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + + # Workaround IE refusal to set cookies in iframe + add_header P3P 'CP="No P3P policy available"'; + + # high limit for presentation as bbb-web will reject upload if larger than configured + client_max_body_size 1000m; + client_body_buffer_size 128k; + + proxy_connect_timeout 90; + proxy_send_timeout 90; + proxy_read_timeout 90; + + proxy_buffer_size 4k; + proxy_buffers 4 32k; + proxy_busy_buffers_size 64k; + proxy_temp_file_write_size 64k; + + include fastcgi_params; + + proxy_request_buffering off; + + # Send a sub-request to allow bbb-web to refuse before loading + # If file is larger than configured bbb-web will return with code 403 and Header: x-file-too-large = 1 + auth_request /bigbluebutton/presentation/checkPresentation; + error_page 403 = @error403; + auth_request_set $file_too_large_header $upstream_http_x_file_too_large; + + } + + location /bigbluebutton/presentation/download { + return 404; + } + + location ~ "^/bigbluebutton/presentation/download\/[0-9a-f]+-[0-9]+/[0-9a-f]+-[0-9]+$" { + if ($arg_presFilename !~ "^[0-9a-f]+-[0-9]+\.[0-9a-zA-Z]+$") { + return 404; + } + proxy_pass http://bbb-web:8090$uri$is_args$args; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + # Workaround IE refusal to set cookies in iframe + add_header P3P 'CP="No P3P policy available"'; + } + + location = /bigbluebutton/presentation/checkPresentation { + proxy_pass http://bbb-web:8090; + proxy_redirect default; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + + proxy_set_header X-Presentation-Token $prestoken; + proxy_set_header X-Original-URI $request_uri; + proxy_set_header Content-Length ""; + proxy_set_header X-Original-Content-Length $http_content_length; + proxy_set_header X-Original-Method $request_method; + + # high limit for presentation as bbb-web will reject upload if larger than configured + client_max_body_size 1000m; + client_body_buffer_size 128k; + + proxy_pass_request_body off; + proxy_request_buffering off; + + } + + # To check connection authentication, include: + # auth_request /bigbluebutton/connection/checkAuthorization; + # auth_request_set $auth_status $upstream_status; + # + # and make sure to add sessionToken param in the request URI + location = /bigbluebutton/connection/checkAuthorization { + internal; + proxy_pass http://bbb-web:8090; + proxy_pass_request_body off; + proxy_set_header Content-Length ""; + proxy_set_header X-Original-URI $request_uri; + } + + location = /bigbluebutton/connection/checkGraphqlAuthorization { + internal; + proxy_pass http://bbb-web:8090; + proxy_pass_request_body off; + proxy_set_header Content-Length ""; + proxy_set_header X-Original-URI $request_uri; + # this is required for CORS preflight checks in cluster setup + proxy_set_header X-Original-Method $request_method; + } + + location = /bigbluebutton/connection/legacyCheckAuthorization { + internal; + proxy_pass http://bbb-web:8090; + proxy_pass_request_body off; + proxy_set_header Content-Length ""; + proxy_set_header X-Original-URI $request_uri; + } + + location = /bigbluebutton/connection/validatePad { + internal; + proxy_pass http://bbb-web:8090; + proxy_pass_request_body off; + proxy_set_header Content-Length ""; + proxy_set_header X-Original-URI $request_uri; + } + + location ~ "^/bigbluebutton\/textTrack\/(?[a-zA-Z0-9]+)\/(?[a-zA-Z0-9_-]+)\/(?.+)$" { + # Workaround IE refusal to set cookies in iframe + add_header P3P 'CP="No P3P policy available"'; + + # Allow 30M uploaded presentation document. + client_max_body_size 30m; + client_body_buffer_size 128k; + + proxy_connect_timeout 90; + proxy_send_timeout 90; + proxy_read_timeout 90; + + proxy_buffer_size 4k; + proxy_buffers 4 32k; + proxy_busy_buffers_size 64k; + proxy_temp_file_write_size 64k; + + include fastcgi_params; + + proxy_request_buffering off; + + # Send a sub-request to allow bbb-web to refuse before loading + auth_request /bigbluebutton/textTrack/validateAuthToken; + + default_type text/plain; + alias /var/bigbluebutton/captions/$recordId/$textTrack; + + } + + location = /bigbluebutton/textTrack/validateAuthToken { + internal; + proxy_pass http://bbb-web:8090; + proxy_redirect default; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + + proxy_set_header X-textTrack-token $textTrackToken; + proxy_set_header X-textTrack-recordId $recordId; + proxy_set_header X-textTrack-track $textTrack; + proxy_set_header X-Original-URI $request_uri; + } + + location /bigbluebutton/rtt-check { + default_type text/plain; + add_header Cache-Control "no-store, no-cache, must-revalidate, proxy-revalidate, max-age=0"; + add_header Pragma "no-cache"; + add_header Expires "0"; + add_header X-Server-Epoch-Msec $msec; + add_header X-Request-Id $request_id; + + # this Header is required for cluster setups as the ping check is a + # CORS request. No cookies are required so we can just allow anyone + # to use this endpoint. + add_header 'Access-Control-Allow-Origin' '*'; + add_header 'Access-Control-Expose-Headers' 'X-Server-Epoch-Msec,X-Request-Id'; + return 200 ""; + } + + } + + location @error403 { + if ($file_too_large_header = '1') { + return 413; + } + + return 403; + } diff --git a/mod/nginx/bigbluebutton b/mod/nginx/bigbluebutton new file mode 100644 index 0000000..a7ff2f0 --- /dev/null +++ b/mod/nginx/bigbluebutton @@ -0,0 +1,85 @@ +server { + # proxied from HAProxy + listen 48082 http2 proxy_protocol; + listen 48081 proxy_protocol; + + # optional ports for other reverse proxies + listen 48087 default_server; + listen [::]:48087 default_server; + + server_name _; + access_log /dev/stdout; + absolute_redirect off; + root /www/; + + # 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; + + # opt-out of google's floc tracking + # https://www.eff.org/deeplinks/2021/03/googles-floc-terrible-idea + add_header Permissions-Policy "interest-cohort=()"; + + + # Include specific rules for record and playback + include /etc/nginx/bbb/*.nginx; + + # redirect old greenlight v2 room links + location ~ "/b/([a-z0-9\-]+)" { + return 302 /rooms/$1; + } + + # serve default.pdf from /www/ + location = /default.pdf { + try_files $uri =404; + } + + location / { + proxy_pass http://greenlight:3000; + proxy_set_header Host $host; + proxy_set_header X-Forwarded-For "127.0.0.1"; + proxy_set_header X-Forwarded-Proto $scheme; + proxy_set_header X-Forwarded-Ssl on; + proxy_http_version 1.1; + client_max_body_size 1000m; + } + + location /cable { + proxy_pass http://greenlight:3000; + proxy_set_header Host $host; + proxy_set_header X-Forwarded-For "127.0.0.1"; + proxy_set_header X-Forwarded-Proto $scheme; + proxy_set_header X-Forwarded-Ssl on; + proxy_set_header Upgrade $http_upgrade; + proxy_set_header Connection "Upgrade"; + proxy_http_version 1.1; + proxy_read_timeout 6h; + proxy_send_timeout 6h; + client_body_timeout 6h; + send_timeout 6h; + } +} + +upstream hasura { + least_conn; + server bbb-graphql-server:8085; + # you might want to add more bbb-graphql-server@ instances to balance the + # load to multiple bbb-graphql-server instances. Execute + # `systemctl enable --now bbb-graphql-server@8086` and uncomment the + # following line: + # server 127.0.0.1:8086; +} +server { + listen 8185; + + root /var/www/html; + + location / { + proxy_http_version 1.1; + proxy_set_header Upgrade $http_upgrade; + proxy_set_header Connection "Upgrade"; + proxy_set_header Host $host; + + proxy_pass http://hasura; + } +} \ No newline at end of file diff --git a/mod/nginx/default.pdf b/mod/nginx/default.pdf new file mode 100644 index 0000000..8a23582 Binary files /dev/null and b/mod/nginx/default.pdf differ diff --git a/mod/periodic/Dockerfile b/mod/periodic/Dockerfile new file mode 100644 index 0000000..770dcaf --- /dev/null +++ b/mod/periodic/Dockerfile @@ -0,0 +1,10 @@ +FROM debian:bookworm-slim + +# -- install docker cli +COPY --from=library/docker:latest /usr/local/bin/docker /usr/bin/docker + +COPY bbb-remove-old-recordings bbb-resync-freeswitch entrypoint.sh / + +RUN chmod +x bbb-remove-old-recordings + +ENTRYPOINT ["/entrypoint.sh"] diff --git a/mod/periodic/bbb-remove-old-recordings b/mod/periodic/bbb-remove-old-recordings new file mode 100644 index 0000000..33f6c38 --- /dev/null +++ b/mod/periodic/bbb-remove-old-recordings @@ -0,0 +1,41 @@ +#!/bin/bash + +# Source: https://docs.bigbluebutton.org/admin/customize.html#delete-recordings-older-than-n-days + +set -e +LOGFILE=/var/log/bigbluebutton/bbb-recording-cleanup-$(date --iso-8601='seconds' -u).log +shopt -s nullglob +NOW=$(date +%s) + +echo "$(date --rfc-3339=seconds) Deleting recordings older than ${RECORDING_MAX_AGE_DAYS} days" >"${LOGFILE}" + +# Find the name of recordings container in order to access `bbb-record` utility +BBB_RECORDINGS_CONTAINER_NAME=$(docker ps --filter "name=recordings" --filter "status=running" --format "{{.Names}}") +if [ $BBB_RECORDINGS_CONTAINER_NAME == "" ]; then + echo "$(date --rfc-3339=seconds) ERROR: recordings container is not running" >>"${LOGFILE}" + exit 1 +fi + +for donefile in /var/bigbluebutton/recording/status/published/*-presentation.done ; do + MTIME=$(stat -c %Y "${donefile}") + # Check the age of the recording + if [ $(( ( $NOW - $MTIME ) / 86400 )) -gt $RECORDING_MAX_AGE_DAYS ]; then + MEETING_ID=$(basename "${donefile}") + MEETING_ID=${MEETING_ID%-presentation.done} + echo "${MEETING_ID}" >> "${LOGFILE}" + + docker exec "$BBB_RECORDINGS_CONTAINER_NAME" bbb-record --delete "${MEETING_ID}" >>"${LOGFILE}" + fi +done + +for eventsfile in /var/bigbluebutton/recording/raw/*/events.xml ; do + MTIME=$(stat -c %Y "${eventsfile}") + # Check the age of the recording + if [ $(( ( $NOW - $MTIME ) / 86400 )) -gt $RECORDING_MAX_AGE_DAYS ]; then + MEETING_ID="${eventsfile%/events.xml}" + MEETING_ID="${MEETING_ID##*/}" + echo "${MEETING_ID}" >> "${LOGFILE}" + + docker exec "$BBB_RECORDINGS_CONTAINER_NAME" bbb-record --delete "${MEETING_ID}" >>"${LOGFILE}" + fi +done diff --git a/mod/periodic/entrypoint.sh b/mod/periodic/entrypoint.sh new file mode 100644 index 0000000..cc3bbb0 --- /dev/null +++ b/mod/periodic/entrypoint.sh @@ -0,0 +1,23 @@ +#!/bin/bash + +# +# How N days back to keep files +# +history=5 + +while : +do + + # resync freeswitch + /bbb-resync-freeswitch + + # delete presentations older than N days + find /var/bigbluebutton/ -maxdepth 1 -type d -name "*-[0-9]*" -mtime +$history -exec rm -rf '{}' + + + # delete recordings older than $RECORDING_MAX_AGE_DAYS + if [ "$ENABLE_RECORDING" == true ] && [ "$REMOVE_OLD_RECORDING" == true ]; then + /bbb-remove-old-recordings + fi + + sleep 30m +done diff --git a/mod/recordings/Dockerfile b/mod/recordings/Dockerfile new file mode 100644 index 0000000..cd6fe87 --- /dev/null +++ b/mod/recordings/Dockerfile @@ -0,0 +1,126 @@ +ARG BBB_BUILD_TAG +FROM bigbluebutton/bbb-build:$BBB_BUILD_TAG AS builder + +# compile and install mkclean +RUN cd /tmp \ + && wget https://sourceforge.net/projects/matroska/files/mkclean/mkclean-0.8.10.tar.bz2/download -O mkclean-0.8.10.tar.bz2 \ + && tar -xf /tmp/mkclean-0.8.10.tar.bz2 \ + && cd /tmp/mkclean-0.8.10 \ + && sed -i 's/\r//g' ./mkclean/configure.compiled \ + && ./mkclean/configure.compiled \ + && make -C mkclean \ + && cp ./release/gcc_linux_x64/mkclean /usr/bin/mkclean \ + && rm -r /tmp/mkclean-* + + +FROM ruby:3.2-slim-trixie + +# install apt dependencies +RUN apt-get update && apt-get install -y \ + wget \ + subversion \ + rsync \ + build-essential \ + libsystemd-dev \ + python3 \ + python3-attr \ + python3-cairo \ + python3-gi \ + python3-gi-cairo \ + python3-lxml \ + python3-icu \ + python3-pyinotify \ + python3-sortedcollections \ + python3-packaging \ + gir1.2-pangocairo-1.0 \ + gir1.2-poppler-0.18 \ + gir1.2-gtk-3.0 \ + gir1.2-gdkpixbuf-2.0 \ + fonts-croscore \ + ffmpeg \ + poppler-utils \ + imagemagick \ + supervisor \ + unzip \ + locales \ + locales-all +# TODO: missing packages + +ENV LC_ALL en_US.UTF-8 +ENV LANG en_US.UTF-8 +ENV LANGUAGE en_US.UTF-8 + +COPY --from=builder /usr/bin/mkclean /usr/bin/mkclean + +# add dockerize +ENV DOCKERIZE_VERSION v0.6.1 +RUN wget -q https://github.com/jwilder/dockerize/releases/download/$DOCKERIZE_VERSION/dockerize-linux-amd64-$DOCKERIZE_VERSION.tar.gz \ + && tar -C /usr/local/bin -xzvf dockerize-linux-amd64-$DOCKERIZE_VERSION.tar.gz \ + && rm dockerize-linux-amd64-$DOCKERIZE_VERSION.tar.gz + +# add yq for bbb-record +RUN wget -q https://github.com/mikefarah/yq/releases/download/v4.45.1/yq_linux_amd64 -O /usr/bin/yq \ + && chmod +x /usr/bin/yq + +RUN mkdir -p \ + /usr/local/bigbluebutton \ + /usr/local/bigbluebutton/core \ + /etc/bigbluebutton \ + /etc/bigbluebutton/recording + +# dependency for bbb-presentation-video +RUN wget -q -O /tmp/python3-perfect-freehand.deb https://github.com/bigbluebutton/perfect-freehand-python/releases/download/1.2.0/python3-perfect-freehand_1.2.0_all.deb && \ + dpkg -i /tmp/python3-perfect-freehand.deb && \ + rm /tmp/python3-perfect-freehand.deb + +ARG TAG_RECORDINGS + +COPY --from=record-core / /usr/local/bigbluebutton/core + +# add bbb-playback-presentation scripts +COPY --from=presentation /scripts /usr/local/bigbluebutton/core/scripts/ + +# install ruby dependencies +RUN cd /usr/local/bigbluebutton/core \ + && gem install builder \ + && gem install bundler -v 2.4.22 --no-document \ + && bundle config set --local deployment true \ + && bundle install \ + && bundle clean \ + && rm -r vendor/bundle/ruby/*/cache \ + && find vendor/bundle -name '*.o' -delete + +# log to file instead of journald +RUN sed -i 's|Journald::Logger\.new.*|Logger.new("/var/log/bigbluebutton/recording.log")|g' /usr/local/bigbluebutton/core/lib/recordandplayback.rb && \ + sed -i 's|Journald::Logger\.new.*|Logger.new("/var/log/bigbluebutton/recording.log")|g' /usr/local/bigbluebutton/core/scripts/rap-caption-inbox.rb && \ + sed -i 's|Logger\.new.*|Logger.new("/var/log/bigbluebutton/recording.log")|g' /usr/local/bigbluebutton/core/scripts/rap-process-worker.rb && \ + sed -i 's|Logger\.new.*|Logger.new("/var/log/bigbluebutton/recording.log")|g' /usr/local/bigbluebutton/core/scripts/archive/archive.rb && \ + sed -i 's|Logger\.new.*|Logger.new("/var/log/bigbluebutton/recording.log")|g' /usr/local/bigbluebutton/core/scripts/publish/presentation.rb && \ + sed -i 's|Logger\.new.*|Logger.new("/var/log/bigbluebutton/recording.log")|g' /usr/local/bigbluebutton/core/scripts/utils/captions.rb && \ + sed -i 's|Logger\.new.*|Logger.new("/var/log/bigbluebutton/recording.log")|g' /usr/local/bigbluebutton/core/scripts/process/presentation.rb + +# add bbb-record with some adjustments so bbb-record works in this environment +COPY --from=bbb-conf /bin/bbb-record /usr/bin/bbb-record +RUN cd /usr/bin \ + && chmod +x /usr/bin/bbb-record \ + && sed -i 's/^BBB_WEB.*/BBB_WEB=""/' /usr/bin/bbb-record \ + && sed -i 's/systemctl.*//' /usr/bin/bbb-record \ + && echo "BIGBLUEBUTTON_RELEASE=$TAG_RECORDINGS" > /etc/bigbluebutton/bigbluebutton-release + +# change owner +# https://github.com/alangecker/bigbluebutton-docker/issues/63 +RUN chown -R 998:998 /usr/local/bigbluebutton + +ARG TAG_BBB_PRESENTATION_VIDEO +RUN cd /tmp/ && \ + wget -q -O bbb-presentation-video.zip "https://github.com/bigbluebutton/bbb-presentation-video/releases/download/${TAG_BBB_PRESENTATION_VIDEO}/ubuntu-22.04.zip" && \ + unzip -o bbb-presentation-video.zip -d bbb-presentation-video && \ + dpkg -i bbb-presentation-video/bbb-presentation-video*.deb && \ + rm -rf /tmp/* + +COPY bbb-web.properties /etc/bigbluebutton/bbb-web.properties.tmpl +COPY recording.yml /etc/bigbluebutton/recording/recording.yml.tmpl +COPY supervisord.conf /etc/supervisor/conf.d/supervisord.conf +COPY entrypoint.sh /entrypoint.sh + +ENTRYPOINT /entrypoint.sh diff --git a/mod/recordings/bbb-web.properties b/mod/recordings/bbb-web.properties new file mode 100644 index 0000000..e891d1c --- /dev/null +++ b/mod/recordings/bbb-web.properties @@ -0,0 +1,2 @@ +bigbluebutton.web.serverURL=https://{{ .Env.DOMAIN }} +securitySalt={{ .Env.SHARED_SECRET }} \ No newline at end of file diff --git a/mod/recordings/entrypoint.sh b/mod/recordings/entrypoint.sh new file mode 100644 index 0000000..2a70f42 --- /dev/null +++ b/mod/recordings/entrypoint.sh @@ -0,0 +1,16 @@ +#!/bin/bash + +touch /var/log/bigbluebutton/recording.log +touch /var/log/bigbluebutton/bbb-web.log +touch /var/log/bigbluebutton/sanity.log +touch /var/log/bigbluebutton/post_publish.log +mkdir -p /var/log/bigbluebutton/presentation +chown -R 998:998 /var/log/bigbluebutton + +dockerize \ + -template /etc/bigbluebutton/recording/recording.yml.tmpl:/etc/bigbluebutton/recording/recording.yml \ + -template /etc/bigbluebutton/bbb-web.properties.tmpl:/etc/bigbluebutton/bbb-web.properties \ + -stdout /var/log/bigbluebutton/recording.log \ + -stdout /var/log/bigbluebutton/post_publish.log \ + -stdout /var/log/bigbluebutton/sanity.log \ + /usr/bin/supervisord --nodaemon \ No newline at end of file diff --git a/mod/recordings/recording.yml b/mod/recordings/recording.yml new file mode 100644 index 0000000..2e79bdf --- /dev/null +++ b/mod/recordings/recording.yml @@ -0,0 +1,4 @@ +redis_host: redis +notes_endpoint: http://bbb-pads:9002/p +playback_host: {{ .Env.DOMAIN }} +playback_protocol: https diff --git a/mod/recordings/supervisord.conf b/mod/recordings/supervisord.conf new file mode 100644 index 0000000..168999d --- /dev/null +++ b/mod/recordings/supervisord.conf @@ -0,0 +1,34 @@ +[supervisord] +user=root + +[program:rasque_workers] +command=bundle exec rake -f Rakefile resque:workers +directory=/usr/local/bigbluebutton/core/scripts +environment=QUEUE="rap:archive,rap:publish,rap:process,rap:sanity,rap:captions,rap:events",COUNT="1",HOME="/tmp" +user=998 +stdout_logfile=/dev/fd/1 +stdout_logfile_maxbytes=0 +stderr_logfile=/dev/fd/2 +stderr_logfile_maxbytes=0 + + +[program:rap_starter] +command=bundle exec ruby /usr/local/bigbluebutton/core/scripts/rap-starter.rb +directory=/usr/local/bigbluebutton/core/scripts +environment=HOME="/tmp" +user=998 +stdout_logfile=/dev/fd/1 +stdout_logfile_maxbytes=0 +stderr_logfile=/dev/fd/2 +stderr_logfile_maxbytes=0 + + +[program:rap_caption_inbox] +command=bundle exec ruby /usr/local/bigbluebutton/core/scripts/rap-caption-inbox.rb +directory=/usr/local/bigbluebutton/core/scripts +environment=HOME="/tmp" +user=998 +stdout_logfile=/dev/fd/1 +stdout_logfile_maxbytes=0 +stderr_logfile=/dev/fd/2 +stderr_logfile_maxbytes=0 diff --git a/mod/webhooks.nginx b/mod/webhooks.nginx new file mode 100644 index 0000000..e2e3f5f --- /dev/null +++ b/mod/webhooks.nginx @@ -0,0 +1,5 @@ +# Webhooks service is not deployed; return 503 if the endpoint is hit. +# To enable webhooks, add a webhooks service and restore the proxy_pass block. +location /bigbluebutton/api/hooks { + return 503; +} diff --git a/mod/webhooks/Dockerfile b/mod/webhooks/Dockerfile new file mode 100644 index 0000000..e4b7ce3 --- /dev/null +++ b/mod/webhooks/Dockerfile @@ -0,0 +1,25 @@ +FROM node:22-bookworm-slim AS builder + + +RUN apt-get update && apt-get install -y git wget + +RUN wget -q https://github.com/mikefarah/yq/releases/download/v4.25.1/yq_linux_amd64 -O /usr/bin/yq \ + && chmod +x /usr/bin/yq + +COPY --from=src / /bbb-webhooks +RUN cd /bbb-webhooks && npm ci --omit=dev && rm -rf /bbb-webhooks./.git + +RUN chmod 777 /bbb-webhooks/config +# ------------------------------ + +FROM node:22-bookworm-slim +RUN useradd --uid 2004 --user-group bbb-webhooks + +COPY --from=builder /usr/bin/yq /usr/bin/yq +COPY --from=builder /bbb-webhooks /bbb-webhooks +COPY entrypoint.sh /entrypoint.sh + +RUN mkdir /bbb-webhooks/log && chmod 777 /bbb-webhooks/log +USER bbb-webhooks +ENTRYPOINT /entrypoint.sh + diff --git a/mod/webhooks/entrypoint.sh b/mod/webhooks/entrypoint.sh new file mode 100644 index 0000000..1cd89e0 --- /dev/null +++ b/mod/webhooks/entrypoint.sh @@ -0,0 +1,17 @@ +#!/bin/sh +set -e +TARGET=/bbb-webhooks/config/production.yml +cp /bbb-webhooks/config/default.example.yml $TARGET + +yq e -i ".hooks.getRaw = false" $TARGET +yq e -i '.modules."../out/webhooks/index.js".config.getRaw = false' $TARGET + +export NODE_ENV=production +export REDIS_HOST=redis +export SERVER_DOMAIN=$DOMAIN +export BEARER_AUTH=true +export SERVER_BIND_IP=0.0.0.0 + +cd /bbb-webhooks +node app.js + diff --git a/mod/webrtc-sfu-config.yaml b/mod/webrtc-sfu-config.yaml new file mode 100644 index 0000000..45c72da --- /dev/null +++ b/mod/webrtc-sfu-config.yaml @@ -0,0 +1,36 @@ +kurento: [] +redisHost: redis +clientHost: webrtc-sfu +recordingAdapter: bbb-webrtc-recorder + +mcs-host: webrtc-sfu +mcs-address: webrtc-sfu +freeswitch: + ip: freeswitch + sip_ip: freeswitch + port: 5066 + esl_ip: freeswitch + esl_port: 8021 + +log: + # trace|debug|info|warn|error + level: debug + # Whether to log to stdout + stdout: true + # Whether to log to a file + file: false + +mediasoup: + dedicatedMediaTypeWorkers: + audio: auto + workerBalancing: + strategy: least-loaded + worker: + # Must match the port range published in compose.yml (24577-24676) + rtcMinPort: 24577 + rtcMaxPort: 24676 + + plainRtp: + listenIp: + ip: "0.0.0.0" + announcedIp: "webrtc-sfu" \ No newline at end of file diff --git a/mod/webrtc-sfu.nginx b/mod/webrtc-sfu.nginx new file mode 100644 index 0000000..daa67bc --- /dev/null +++ b/mod/webrtc-sfu.nginx @@ -0,0 +1,25 @@ +location /bbb-webrtc-sfu { + auth_request /bigbluebutton/connection/checkAuthorization; + auth_request_set $auth_status $upstream_status; + # Extra variables are annotated by bbb-web as custom headers + auth_request_set $user_id $sent_http_user_id; + auth_request_set $meeting_id $sent_http_meeting_id; + auth_request_set $voice_bridge $sent_http_voice_bridge; + auth_request_set $user_name $sent_http_user_name; + + proxy_pass http://webrtc-sfu:3008; + proxy_http_version 1.1; + proxy_set_header Upgrade $http_upgrade; + proxy_set_header Connection "Upgrade"; + # Following custom headers are used by bbb-webrtc-sfu + proxy_set_header User-Id $user_id; + proxy_set_header Meeting-Id $meeting_id; + proxy_set_header Voice-Bridge $voice_bridge; + proxy_set_header User-Name $user_name; + + proxy_read_timeout 60s; + proxy_send_timeout 60s; + client_body_timeout 60s; + send_timeout 60s; +} + diff --git a/mod/webrtc-sfu/Dockerfile b/mod/webrtc-sfu/Dockerfile new file mode 100644 index 0000000..cd2ee00 --- /dev/null +++ b/mod/webrtc-sfu/Dockerfile @@ -0,0 +1,42 @@ +ARG BBB_BUILD_TAG +FROM bigbluebutton/bbb-build:$BBB_BUILD_TAG AS builder + + +RUN useradd --uid 2004 --user-group webrtc-sfu + +# cache packages +COPY --from=source /package.json /cache/package.json +RUN cd /cache && npm install --unsafe-perm + +COPY --from=source / /app + +ENV NODE_ENV production + + +RUN cd /app \ + && rm -rf /app/node_modules && cp -a /cache/node_modules /app/node_modules \ + && cp config/default.example.yml config/production.yml \ + && npm install --unsafe-perm \ + && npm cache clear --force \ + && rm -rf node_modules/mediasoup/worker/out/Release/subprojects \ + && rm -rf node_modules/mediasoup/worker/out/Release/mediasoup-worker.p \ + && rm -rf node_modules/mediasoup/worker/out/Release/deps + + +# ============================= +FROM node:22-bookworm-slim +RUN useradd --uid 2004 --user-group webrtc-sfu +ENV NODE_ENV production + +COPY --from=builder /app /app +RUN mkdir /home/webrtc-sfu && chown -R webrtc-sfu:webrtc-sfu /app/config /home/webrtc-sfu + +USER webrtc-sfu +WORKDIR /app + + +COPY config.yaml /etc/bigbluebutton/bbb-webrtc-sfu/production.yml +ENV NODE_ENV=production +ENV NODE_CONFIG_DIR=/app/config/:/etc/bigbluebutton/bbb-webrtc-sfu/ +ENV ALLOW_CONFIG_MUTATIONS=true +CMD [ "npm", "start" ] diff --git a/mongod.conf b/mongod.conf deleted file mode 100644 index f791065..0000000 --- a/mongod.conf +++ /dev/null @@ -1,33 +0,0 @@ -# mongod.conf - -# for documentation of all options, see: -# http://docs.mongodb.org/manual/reference/configuration-options/ - -storage: - dbPath: /data/db - journal: - enabled: true - wiredTiger: - engineConfig: - cacheSizeGB: 1 - journalCompressor: none - directoryForIndexes: true - collectionConfig: - blockCompressor: none - indexConfig: - prefixCompression: false - - -net: - port: 27017 - bindIp: 0.0.0.0 - - -replication: - replSetName: rs0 - -setParameter: - diagnosticDataCollectionEnabled: false - -security: - javascriptEnabled: false