diff --git a/webapps/jitsimeet/defaults/main.yml b/webapps/jitsimeet/defaults/main.yml index d5fc790f..dad86af6 100644 --- a/webapps/jitsimeet/defaults/main.yml +++ b/webapps/jitsimeet/defaults/main.yml @@ -4,6 +4,7 @@ system_dep: "['gnupg2', 'curl', 'apt-transport-https', 'default-jdk', 'lua5.2', 'lua-unbound', 'certbot', 'python3-certbot-nginx']" domains: ['jitsi.example.net'] +turn_domains: ['turn.jitsi.example.net'] certbot_admin_email: 'security@example.net' jitsi_meet_cert_choice: "Generate a new self-signed certificate (You will later get a chance to obtain a Let's encrypt certificate)" diff --git a/webapps/jitsimeet/tasks/main.yml b/webapps/jitsimeet/tasks/main.yml index 878ca4eb..4f26f55d 100644 --- a/webapps/jitsimeet/tasks/main.yml +++ b/webapps/jitsimeet/tasks/main.yml @@ -68,18 +68,23 @@ state: present install_recommends: yes +- name: Install stream module for nginx + ansible.builtin.apt: + name: libnginx-mod-stream + state: present + - name: Add certs dir for coturn/letsencrypt if needed file: - path: "{{ item }}" + path: "{{ item.path }}" state: directory - mode: '700' - owner: 'turnserver' - group: 'turnserver' + mode: "{{ item.mode }}" + owner: "{{ item.owner }}" + group: "{{ item.group }}" loop: - - /etc/coturn - - /etc/coturn/certs - - /etc/letsencrypt/renewal-hooks - - /etc/letsencrypt/renewal-hooks/deploy + - { path: '/etc/coturn', owner: "turnserver", group: "turnserver", mode: "0700" } + - { path: '/etc/coturn/certs', owner: "turnserver", group: "turnserver", mode: "0700" } + - { path: '/etc/letsencrypt/renewal-hooks', owner: "root", group: "root", mode: "0700" } + - { path: '/etc/letsencrypt/renewal-hooks/deploy', owner: "root", group: "root", mode: "0700" } - name: Template config files template: @@ -92,9 +97,11 @@ - { src: 'videobridge/jvb.conf.j2', dest: "/etc/jitsi/videobridge/jvb.conf", owner: "jvb", group: "jitsi", mode: "0640" } - { src: 'videobridge/sip-communicator.properties.j2', dest: "/etc/jitsi/videobridge/sip-communicator.properties", owner: "jvb", group: "jitsi", mode: "0640" } - { src: 'meet/config.js.j2', dest: "/etc/jitsi/meet/{{ domains | first }}-config.js", owner: "root", group: "root", mode: "0644" } + - { src: 'meet/interface_config.js.j2', dest: "/etc/jitsi/meet/{{ domains | first }}-interface_config.js", owner: "root", group: "root", mode: "0644" } + - { src: 'meet/welcomePageAdditionalContent.html.j2', dest: "/usr/share/jitsi-meet/static/welcomePageAdditionalContent.html", owner: "root", group: "root", mode: "0644" } - { src: 'prosody/virtualhost.cfg.lua.j2', dest: "/etc/prosody/conf.avail/{{ domains | first }}.cfg.lua", owner: "root", group: "root", mode: "0644" } - { src: 'coturn/turnserver.conf.j2', dest: "/etc/turnserver.conf", owner: "root", group: "turnserver", mode: "0640" } - - { src: 'certbot/coturn-certbot-deploy.sh.j2', dest: "/etc/letsencrypt/renewal-hooks/deploy/coturn-certbot-deploy.sh", owner: "root", group: "turnserver", mode: "0700" } + - { src: 'certbot/coturn-certbot-deploy.sh.j2', dest: "/etc/letsencrypt/renewal-hooks/deploy/coturn-certbot-deploy.sh", owner: "root", group: "root", mode: "0700" } - name: Add bloc to jicofo.conf to disable sctp ansible.builtin.blockinfile: @@ -153,7 +160,7 @@ state: directory mode: '0755' - name: Generate certificate with certbot - shell: certbot certonly --webroot --webroot-path /var/lib/letsencrypt --non-interactive --deploy-hook /etc/letsencrypt/renewal-hooks/deploy/coturn-certbot-deploy.sh --agree-tos --email {{ certbot_admin_email }} -d {{ domains |first }} + shell: certbot certonly --webroot --webroot-path /var/lib/letsencrypt --non-interactive --agree-tos --email {{ certbot_admin_email }} -d {{ domains |first }} when: ssl.stat.exists != true - name: (Re)check if SSL certificate is present and register result @@ -163,8 +170,17 @@ - name: (Re)template conf file for nginx vhost with SSL template: - src: "nginx/vhost.conf.j2" - dest: "/etc/nginx/sites-available/{{ domains |first }}.conf" + src: "{{ item.src }}" + dest: "{{ item.dest }}" + loop: + - { src: 'nginx/vhost.conf.j2', dest: "/etc/nginx/sites-available/{{ domains |first }}.conf" } + - { src: 'nginx/multiplex.conf.j2', dest: '/etc/nginx/modules-available/multiplex.conf' } + +- name: Enable multiplex module conf + file: + src: '/etc/nginx/modules-available/multiplex.conf' + dest: '/etc/nginx/modules-enabled/multiplex.conf' + state: link - name: Enable nginx vhost file: @@ -177,3 +193,11 @@ name: nginx state: reloaded +- name: Check if SSL certificate for coturn is present and register result + stat: + path: "/etc/coturn/certs/{{ turn_domains |first }}.crt" + register: ssl_coturn + +- name: Generate certificate for coturn with certbot + shell: certbot certonly --webroot --webroot-path /var/lib/letsencrypt --non-interactive --deploy-hook /etc/letsencrypt/renewal-hooks/deploy/coturn-certbot-deploy.sh --agree-tos --email {{ certbot_admin_email }} -d {{ turn_domains |first }} + when: ssl_coturn.stat.exists != true diff --git a/webapps/jitsimeet/templates/certbot/coturn-certbot-deploy.sh.j2 b/webapps/jitsimeet/templates/certbot/coturn-certbot-deploy.sh.j2 index de032ab5..9e34af55 100644 --- a/webapps/jitsimeet/templates/certbot/coturn-certbot-deploy.sh.j2 +++ b/webapps/jitsimeet/templates/certbot/coturn-certbot-deploy.sh.j2 @@ -6,7 +6,7 @@ set -e for domain in $RENEWED_DOMAINS; do case $domain in - {{ domains | first }}) + {{ turn_domains | first }}) daemon_cert_root=/etc/coturn/certs # Make sure the certificate and private key files are diff --git a/webapps/jitsimeet/templates/coturn/turnserver.conf.j2 b/webapps/jitsimeet/templates/coturn/turnserver.conf.j2 index ac9f0c1d..67ee20a3 100644 --- a/webapps/jitsimeet/templates/coturn/turnserver.conf.j2 +++ b/webapps/jitsimeet/templates/coturn/turnserver.conf.j2 @@ -2,9 +2,9 @@ use-auth-secret keep-address-family static-auth-secret={{ jitsi_meet_turn_secret }} -realm={{ domains | first }} -cert=/etc/coturn/certs/{{ domains | first }}.crt -pkey=/etc/coturn/certs/{{ domains | first }}.key +realm={{ turn_domains | first }} +cert=/etc/coturn/certs/{{ turn_domains | first }}.crt +pkey=/etc/coturn/certs/{{ turn_domains | first }}.key no-multicast-peers no-cli #no-loopback-peers diff --git a/webapps/jitsimeet/templates/meet/config.js.j2 b/webapps/jitsimeet/templates/meet/config.js.j2 index 6fa9886a..5000be95 100644 --- a/webapps/jitsimeet/templates/meet/config.js.j2 +++ b/webapps/jitsimeet/templates/meet/config.js.j2 @@ -574,13 +574,14 @@ var config = { // enableWelcomePage: true, // Configs for welcome page. - // welcomePage: { + welcomePage: { // // Whether to disable welcome page. In case it's disabled a random room // // will be joined when no room is specified. - // disabled: false, + disabled: false, + additionalContent: true // // If set,landing page will redirect to this URL. // customUrl: '' - // }, + }, // Configs for the lobby screen. // lobby { @@ -946,7 +947,7 @@ var config = { // The STUN servers that will be used in the peer to peer connections stunServers: [ - { urls: 'stun:{{ domains | first }}:3478' }, + { urls: 'stun:{{ turn_domains | first }}:3478' }, //{ urls: 'stun:meet-jit-si-turnrelay.jitsi.net:443' }, ], }, diff --git a/webapps/jitsimeet/templates/meet/interface_config.js.j2 b/webapps/jitsimeet/templates/meet/interface_config.js.j2 new file mode 100644 index 00000000..0b8d546e --- /dev/null +++ b/webapps/jitsimeet/templates/meet/interface_config.js.j2 @@ -0,0 +1,273 @@ +/* eslint-disable no-unused-vars, no-var, max-len */ +/* eslint sort-keys: ["error", "asc", {"caseSensitive": false}] */ + +/** + * !!!IMPORTANT!!! + * + * This file is considered deprecated. All options will eventually be moved to + * config.js, and no new options should be added here. + */ + +var interfaceConfig = { + APP_NAME: 'Jitsi Meet', + AUDIO_LEVEL_PRIMARY_COLOR: 'rgba(255,255,255,0.4)', + AUDIO_LEVEL_SECONDARY_COLOR: 'rgba(255,255,255,0.2)', + + /** + * A UX mode where the last screen share participant is automatically + * pinned. Valid values are the string "remote-only" so remote participants + * get pinned but not local, otherwise any truthy value for all participants, + * and any falsy value to disable the feature. + * + * Note: this mode is experimental and subject to breakage. + */ + AUTO_PIN_LATEST_SCREEN_SHARE: 'remote-only', + BRAND_WATERMARK_LINK: '', + + CLOSE_PAGE_GUEST_HINT: false, // A html text to be shown to guests on the close page, false disables it + + DEFAULT_BACKGROUND: '#040404', + DEFAULT_WELCOME_PAGE_LOGO_URL: 'images/watermark.svg', + + DISABLE_DOMINANT_SPEAKER_INDICATOR: false, + + /** + * If true, notifications regarding joining/leaving are no longer displayed. + */ + DISABLE_JOIN_LEAVE_NOTIFICATIONS: false, + + /** + * If true, presence status: busy, calling, connected etc. is not displayed. + */ + DISABLE_PRESENCE_STATUS: false, + + /** + * Whether the ringing sound in the call/ring overlay is disabled. If + * {@code undefined}, defaults to {@code false}. + * + * @type {boolean} + */ + DISABLE_RINGING: false, + + /** + * Whether the speech to text transcription subtitles panel is disabled. + * If {@code undefined}, defaults to {@code false}. + * + * @type {boolean} + */ + DISABLE_TRANSCRIPTION_SUBTITLES: false, + + /** + * Whether or not the blurred video background for large video should be + * displayed on browsers that can support it. + */ + DISABLE_VIDEO_BACKGROUND: false, + + DISPLAY_WELCOME_FOOTER: {{ welcome_footer }}, + DISPLAY_WELCOME_PAGE_ADDITIONAL_CARD: false, + DISPLAY_WELCOME_PAGE_CONTENT: true, + DISPLAY_WELCOME_PAGE_TOOLBAR_ADDITIONAL_CONTENT: false, + + ENABLE_DIAL_OUT: true, + + // DEPRECATED. Animation no longer supported. + // ENABLE_FEEDBACK_ANIMATION: false, + + FILM_STRIP_MAX_HEIGHT: 120, + + GENERATE_ROOMNAMES_ON_WELCOME_PAGE: true, + + /** + * Hide the invite prompt in the header when alone in the meeting. + */ + HIDE_INVITE_MORE_HEADER: false, + + JITSI_WATERMARK_LINK: 'https://jitsi.org', + + LANG_DETECTION: true, // Allow i18n to detect the system language + LOCAL_THUMBNAIL_RATIO: 16 / 9, // 16:9 + + /** + * Maximum coefficient of the ratio of the large video to the visible area + * after the large video is scaled to fit the window. + * + * @type {number} + */ + MAXIMUM_ZOOMING_COEFFICIENT: 1.3, + + /** + * Whether the mobile app Jitsi Meet is to be promoted to participants + * attempting to join a conference in a mobile Web browser. If + * {@code undefined}, defaults to {@code true}. + * + * @type {boolean} + */ + MOBILE_APP_PROMO: true, + + // Names of browsers which should show a warning stating the current browser + // has a suboptimal experience. Browsers which are not listed as optimal or + // unsupported are considered suboptimal. Valid values are: + // chrome, chromium, edge, electron, firefox, nwjs, opera, safari + OPTIMAL_BROWSERS: [ 'chrome', 'chromium', 'firefox', 'nwjs', 'electron', 'safari' ], + + POLICY_LOGO: null, + PROVIDER_NAME: 'Jitsi', + + /** + * If true, will display recent list + * + * @type {boolean} + */ + RECENT_LIST_ENABLED: true, + REMOTE_THUMBNAIL_RATIO: 1, // 1:1 + + SETTINGS_SECTIONS: [ 'devices', 'language', 'moderator', 'profile', 'calendar', 'sounds', 'more' ], + + /** + * Specify which sharing features should be displayed. If the value is not set + * all sharing features will be shown. You can set [] to disable all. + */ + // SHARING_FEATURES: ['email', 'url', 'dial-in', 'embed'], + + SHOW_BRAND_WATERMARK: false, + + /** + * Decides whether the chrome extension banner should be rendered on the landing page and during the meeting. + * If this is set to false, the banner will not be rendered at all. If set to true, the check for extension(s) + * being already installed is done before rendering. + */ + SHOW_CHROME_EXTENSION_BANNER: false, + + SHOW_JITSI_WATERMARK: true, + SHOW_POWERED_BY: false, + SHOW_PROMOTIONAL_CLOSE_PAGE: false, + + /* + * If indicated some of the error dialogs may point to the support URL for + * help. + */ + SUPPORT_URL: 'https://community.jitsi.org/', + + // Browsers, in addition to those which do not fully support WebRTC, that + // are not supported and should show the unsupported browser page. + UNSUPPORTED_BROWSERS: [], + + /** + * Whether to show thumbnails in filmstrip as a column instead of as a row. + */ + VERTICAL_FILMSTRIP: true, + + // Determines how the video would fit the screen. 'both' would fit the whole + // screen, 'height' would fit the original video height to the height of the + // screen, 'width' would fit the original video width to the width of the + // screen respecting ratio, 'nocrop' would make the video as large as + // possible and preserve aspect ratio without cropping. + VIDEO_LAYOUT_FIT: 'both', + + /** + * If true, hides the video quality label indicating the resolution status + * of the current large video. + * + * @type {boolean} + */ + VIDEO_QUALITY_LABEL_DISABLED: false, + + /** + * How many columns the tile view can expand to. The respected range is + * between 1 and 5. + */ + // TILE_VIEW_MAX_COLUMNS: 5, + + // List of undocumented settings + /** + INDICATOR_FONT_SIZES + PHONE_NUMBER_REGEX + */ + + // -----------------DEPRECATED CONFIGS BELOW THIS LINE----------------------------- + + /** + * Specify URL for downloading ios mobile app. + */ + // MOBILE_DOWNLOAD_LINK_IOS: 'https://itunes.apple.com/us/app/jitsi-meet/id1165103905', + + /** + * Specify custom URL for downloading android mobile app. + */ + // MOBILE_DOWNLOAD_LINK_ANDROID: 'https://play.google.com/store/apps/details?id=org.jitsi.meet', + + /** + * Specify mobile app scheme for opening the app from the mobile browser. + */ + // APP_SCHEME: 'org.jitsi.meet', + + // NATIVE_APP_NAME: 'Jitsi Meet', + + /** + * Specify Firebase dynamic link properties for the mobile apps. + */ + // MOBILE_DYNAMIC_LINK: { + // APN: 'org.jitsi.meet', + // APP_CODE: 'w2atb', + // CUSTOM_DOMAIN: undefined, + // IBI: 'com.atlassian.JitsiMeet.ios', + // ISI: '1165103905' + // }, + + /** + * Hide the logo on the deep linking pages. + */ + // HIDE_DEEP_LINKING_LOGO: false, + + /** + * Specify the Android app package name. + */ + // ANDROID_APP_PACKAGE: 'org.jitsi.meet', + + /** + * Specify custom URL for downloading f droid app. + */ + // MOBILE_DOWNLOAD_LINK_F_DROID: 'https://f-droid.org/en/packages/org.jitsi.meet/', + + // Connection indicators ( + // CONNECTION_INDICATOR_AUTO_HIDE_ENABLED, + // CONNECTION_INDICATOR_AUTO_HIDE_TIMEOUT, + // CONNECTION_INDICATOR_DISABLED) got moved to config.js. + + // Please use disableModeratorIndicator from config.js + // DISABLE_FOCUS_INDICATOR: false, + + // Please use defaultLocalDisplayName from config.js + // DEFAULT_LOCAL_DISPLAY_NAME: 'me', + + // Please use defaultLogoUrl from config.js + // DEFAULT_LOGO_URL: 'images/watermark.svg', + + // Please use defaultRemoteDisplayName from config.js + // DEFAULT_REMOTE_DISPLAY_NAME: 'Fellow Jitster', + + // Moved to config.js as `toolbarConfig.initialTimeout`. + // INITIAL_TOOLBAR_TIMEOUT: 20000, + + // Please use `liveStreaming.helpLink` from config.js + // Documentation reference for the live streaming feature. + // LIVE_STREAMING_HELP_LINK: 'https://jitsi.org/live', + + // Moved to config.js as `toolbarConfig.alwaysVisible`. + // TOOLBAR_ALWAYS_VISIBLE: false, + + // This config was moved to config.js as `toolbarButtons`. + // TOOLBAR_BUTTONS: [], + + // Moved to config.js as `toolbarConfig.timeout`. + // TOOLBAR_TIMEOUT: 4000, + + // Allow all above example options to include a trailing comma and + // prevent fear when commenting out the last value. + // eslint-disable-next-line sort-keys + makeJsonParserHappy: 'even if last key had a trailing comma' + + // No configuration value should follow this line. +}; + +/* eslint-enable no-unused-vars, no-var, max-len */ diff --git a/webapps/jitsimeet/templates/meet/welcomePageAdditionalContent.html.j2 b/webapps/jitsimeet/templates/meet/welcomePageAdditionalContent.html.j2 new file mode 100644 index 00000000..f91eb691 --- /dev/null +++ b/webapps/jitsimeet/templates/meet/welcomePageAdditionalContent.html.j2 @@ -0,0 +1,22 @@ + diff --git a/webapps/jitsimeet/templates/nginx/multiplex.conf.j2 b/webapps/jitsimeet/templates/nginx/multiplex.conf.j2 new file mode 100644 index 00000000..8a279fb9 --- /dev/null +++ b/webapps/jitsimeet/templates/nginx/multiplex.conf.j2 @@ -0,0 +1,27 @@ +stream { + map $ssl_preread_server_name $name { + {{ domains | first }} web_backend; + {{ turn_domains | first }} turn_backend; + } + + upstream web_backend { + server 127.0.0.1:8088; + } + + upstream turn_backend { + server {{ ansible_default_ipv4.address }}:5349; + } + + server { + listen 443; + listen [::]:443; + + # since 1.11.5 + ssl_preread on; + + proxy_pass $name; + + # Increase buffer to serve video + proxy_buffer_size 10m; + } +} diff --git a/webapps/jitsimeet/templates/nginx/vhost.conf.j2 b/webapps/jitsimeet/templates/nginx/vhost.conf.j2 index 919d0788..47156004 100644 --- a/webapps/jitsimeet/templates/nginx/vhost.conf.j2 +++ b/webapps/jitsimeet/templates/nginx/vhost.conf.j2 @@ -33,7 +33,7 @@ map $arg_vnode $prosody_node { server { listen 80; listen [::]:80; - server_name {{ domains | first }}; + server_name {{ domains | first }} {{ turn_domains | first }}; # For certbot location ~ /.well-known/acme-challenge { @@ -48,8 +48,8 @@ server { {% if ssl.stat.exists %} server { - listen 443 ssl http2; - listen [::]:443 ssl http2; + listen 8088 ssl http2; + listen [::]:8088 ssl http2; server_name {{ domains | first }}; access_log /var/log/nginx/{{ service }}.access.log; # reduce I/0 with buffer=10m flush=5m @@ -75,6 +75,7 @@ server { set $prefix ""; set $custom_index ""; set $config_js_location /etc/jitsi/meet/{{ domains | first }}-config.js; + set $interface_config_js_location /etc/jitsi/meet/{{ domains | first }}-interface_config.js; ## # Certificates @@ -103,6 +104,10 @@ server { location = /config.js { alias $config_js_location; } + + location = /interface_config.js { + alias $interface_config_js_location; + } location = /external_api.js { alias /usr/share/jitsi-meet/libs/external_api.min.js; diff --git a/webapps/jitsimeet/templates/prosody/virtualhost.cfg.lua.j2 b/webapps/jitsimeet/templates/prosody/virtualhost.cfg.lua.j2 index 506f4a14..cef3eaae 100644 --- a/webapps/jitsimeet/templates/prosody/virtualhost.cfg.lua.j2 +++ b/webapps/jitsimeet/templates/prosody/virtualhost.cfg.lua.j2 @@ -5,9 +5,9 @@ muc_mapper_domain_base = "{{ domains | first }}"; external_service_secret = "{{ jitsi_meet_turn_secret }}"; external_services = { - { type = "stun", host = "{{ domains | first }}", port = 3478 }, - { type = "turn", host = "{{ domains | first }}", port = 3478, transport = "udp", secret = true, ttl = 86400, algorithm = "turn" }, - { type = "turns", host = "{{ domains | first }}", port = 5349, transport = "tcp", secret = true, ttl = 86400, algorithm = "turn" } + { type = "stun", host = "{{ turn_domains | first }}", port = 3478 }, + { type = "turn", host = "{{ turn_domains | first }}", port = 3478, transport = "udp", secret = true, ttl = 86400, algorithm = "turn" }, + { type = "turns", host = "{{ turn_domains | first }}", port = 443, transport = "tcp", secret = true, ttl = 86400, algorithm = "turn" } }; cross_domain_bosh = false; diff --git a/webapps/jitsimeet/templates/videobridge/sip-communicator.properties.j2 b/webapps/jitsimeet/templates/videobridge/sip-communicator.properties.j2 index f8fca054..2b1dde4e 100644 --- a/webapps/jitsimeet/templates/videobridge/sip-communicator.properties.j2 +++ b/webapps/jitsimeet/templates/videobridge/sip-communicator.properties.j2 @@ -1,5 +1,5 @@ org.ice4j.ice.harvest.DISABLE_AWS_HARVESTER=true -org.ice4j.ice.harvest.STUN_MAPPING_HARVESTER_ADDRESSES={{ domains | first }}:3478 +org.ice4j.ice.harvest.STUN_MAPPING_HARVESTER_ADDRESSES={{ turn_domains | first }}:3478 org.jitsi.videobridge.ENABLE_STATISTICS=true org.jitsi.videobridge.STATISTICS_TRANSPORT=muc org.jitsi.videobridge.xmpp.user.shard.HOSTNAME=localhost @@ -8,3 +8,6 @@ org.jitsi.videobridge.xmpp.user.shard.USERNAME=jvb org.jitsi.videobridge.xmpp.user.shard.PASSWORD={{ jitsi_meet_jvb_secret }} org.jitsi.videobridge.xmpp.user.shard.MUC_JIDS=JvbBrewery@internal.auth.{{ domains | first }} org.jitsi.videobridge.xmpp.user.shard.MUC_NICKNAME={{ jitsi_meet_jvb_muc_nick }} +org.jitsi.videobridge.rest.jetty.ResourceHandler.alias./static/welcomePageAdditionalContent.html=/usr/share/jitsi-meet/static/welcomePageAdditionalContent.html +# Switches off the BWE mechanism. +#org.jitsi.videobridge.TRUST_BWE=false