mirror of
https://git.lapiole.org/dani/ansible-roles.git
synced 2025-04-23 21:53:21 +02:00
Update to 2024-07-22 10:00
This commit is contained in:
parent
457d41ccf9
commit
f5421b17f0
@ -14,7 +14,7 @@ jitsi_jigasi_git_url: https://github.com/jitsi/jigasi.git
|
|||||||
jitsi_meet_git_url: https://github.com/jitsi/jitsi-meet.git
|
jitsi_meet_git_url: https://github.com/jitsi/jitsi-meet.git
|
||||||
|
|
||||||
# Should ansible handle upgrades, or only initial install ?
|
# Should ansible handle upgrades, or only initial install ?
|
||||||
jitsi_manage_upgrade: True
|
jitsi_manage_upgrade: true
|
||||||
|
|
||||||
# XMPP server to connect to. Default is the same machine
|
# XMPP server to connect to. Default is the same machine
|
||||||
jitsi_xmpp_server: "{{ inventory_hostname }}"
|
jitsi_xmpp_server: "{{ inventory_hostname }}"
|
||||||
@ -33,14 +33,26 @@ jitsi_stun_servers: []
|
|||||||
jitsi_turn_secret: "{{ turnserver_auth_secret | default('p@ssw0rd') }}"
|
jitsi_turn_secret: "{{ turnserver_auth_secret | default('p@ssw0rd') }}"
|
||||||
|
|
||||||
# Authentication. Can be set to
|
# Authentication. Can be set to
|
||||||
# * False : no authentication at all (can also be None)
|
# * false : no authentication at all (can also be None)
|
||||||
# * sso : In this case, you have to protect /login with your SSO system (through a reverse proxy)
|
# * sso : In this case, you have to protect /login with your SSO system (through a reverse proxy)
|
||||||
# And once authenticated, send the HTTP headers mail and displayName with the appropriate values
|
# And once authenticated, send the HTTP headers mail and displayName with the appropriate values
|
||||||
# Note that jitsi Android client does not support sso authentication, so mobile users will be able
|
# Note that jitsi Android client does not support sso authentication, so mobile users will be able
|
||||||
# to join an existing conf, but not create one easily
|
# to join an existing conf, but not create one easily
|
||||||
|
# * token : to use JWT Tokens
|
||||||
# * ldap : Will use an LDAP server for authentication. Works on mobile, but is a bit less convinient
|
# * ldap : Will use an LDAP server for authentication. Works on mobile, but is a bit less convinient
|
||||||
# than sso for desktop users. See all the jitsi_ldap_xxxx settings
|
# than sso for desktop users. See all the jitsi_ldap_xxxx settings
|
||||||
jitsi_auth: False
|
jitsi_auth: false
|
||||||
|
|
||||||
|
# If using token
|
||||||
|
jitsi_token_app_id: jitsi
|
||||||
|
|
||||||
|
# Either jitsi_token_app_secret or jitsi_token_asap_key_server must be set
|
||||||
|
# jitsi_token_app_secret: XXXX
|
||||||
|
# jitsi_token_asap_key_server: https://sso.example.org/jitsi/asap
|
||||||
|
|
||||||
|
jitsi_token_iss: https://sso.example.org
|
||||||
|
jitsi_token_aud: "{{ jitsi_token_app_id }}"
|
||||||
|
jitsi_token_auth_url: https://sso.example.org/jitsi/login?room={room}
|
||||||
|
|
||||||
jitsi_jicofo_xmpp_user: focus
|
jitsi_jicofo_xmpp_user: focus
|
||||||
jitsi_jicofo_xmpp_domain: "{{ jitsi_auth_domain }}"
|
jitsi_jicofo_xmpp_domain: "{{ jitsi_auth_domain }}"
|
||||||
@ -52,7 +64,7 @@ jitsi_auth_domain: auth.{{ jitsi_domain }}
|
|||||||
|
|
||||||
# Can be either true, in which case a cert will be automatically obtained using letsencrypt
|
# Can be either true, in which case a cert will be automatically obtained using letsencrypt
|
||||||
# or can be a name, in which case you have to configure letsencrypt to obtain the cert yourself
|
# or can be a name, in which case you have to configure letsencrypt to obtain the cert yourself
|
||||||
# jitsi_letsencrypt_cert: True
|
# jitsi_letsencrypt_cert: true
|
||||||
# or
|
# or
|
||||||
# jitsi_letsencrypt_cert: jitsi.example.com
|
# jitsi_letsencrypt_cert: jitsi.example.com
|
||||||
#
|
#
|
||||||
@ -71,33 +83,32 @@ jitsi_meet_conf_base:
|
|||||||
websocket: wss://{{ jitsi_domain }}/xmpp-websocket
|
websocket: wss://{{ jitsi_domain }}/xmpp-websocket
|
||||||
clientNode: http://jitsi.org/jitsimeet
|
clientNode: http://jitsi.org/jitsimeet
|
||||||
focusUserJid: "{{ jitsi_jicofo_xmpp_user }}@{{ jitsi_auth_domain }}"
|
focusUserJid: "{{ jitsi_jicofo_xmpp_user }}@{{ jitsi_auth_domain }}"
|
||||||
enableNoAudioDetection: True
|
enableNoAudioDetection: true
|
||||||
enableNoisyMicDetection: True
|
enableNoisyMicDetection: true
|
||||||
startAudioMuted: 10
|
startAudioMuted: 10
|
||||||
startVideoMuted: 10
|
startVideoMuted: 10
|
||||||
enableOpusRed: True
|
enableOpusRed: true
|
||||||
desktopSharingFrameRate:
|
#desktopSharingFrameRate:
|
||||||
min: 5
|
# min: 5
|
||||||
max: 30
|
# max: 30
|
||||||
channelLastN: 25
|
requireDisplayName: true
|
||||||
enableLayerSuspension: True
|
|
||||||
enableUnifiedOnChrome: True
|
|
||||||
requireDisplayName: False
|
|
||||||
prejoinConfig:
|
prejoinConfig:
|
||||||
enabled: True
|
enabled: true
|
||||||
enableInsecureRoomNameWarning: False
|
enableInsecureRoomNameWarning: false
|
||||||
disableThirdPartyRequests: True
|
disableThirdPartyRequests: true
|
||||||
welcomePage:
|
welcomePage:
|
||||||
disabled: False
|
disabled: false
|
||||||
|
lobby:
|
||||||
|
enableChat: true
|
||||||
localRecording:
|
localRecording:
|
||||||
notifyAllParticipants: True
|
notifyAllParticipants: true
|
||||||
recordingService:
|
recordingService:
|
||||||
enabled: "{{ (jitsi_jibri_recordings_base_url is defined) | ternary(True, False) }}"
|
enabled: "{{ (jitsi_jibri_recordings_base_url is defined) | ternary(true, false) }}"
|
||||||
p2p:
|
p2p:
|
||||||
enabled: False
|
enabled: false
|
||||||
enableUnifiedOnChrome: True
|
enableUnifiedOnChrome: true
|
||||||
analytics:
|
analytics:
|
||||||
disabled: True
|
disabled: true
|
||||||
toolbarButtons:
|
toolbarButtons:
|
||||||
- camera
|
- camera
|
||||||
- chat
|
- chat
|
||||||
@ -129,18 +140,22 @@ jitsi_meet_conf_base:
|
|||||||
dialInNumbersUrl: https://{{ jitsi_domain }}/phoneNumberList
|
dialInNumbersUrl: https://{{ jitsi_domain }}/phoneNumberList
|
||||||
dialInConfCodeUrl: https://{{ jitsi_domain }}/conferenceMapper
|
dialInConfCodeUrl: https://{{ jitsi_domain }}/conferenceMapper
|
||||||
screenshotCapture:
|
screenshotCapture:
|
||||||
enabled: True
|
enabled: true
|
||||||
transcription:
|
transcription:
|
||||||
enabled: False
|
enabled: false
|
||||||
useTurnUdp: True
|
useTurnUdp: true
|
||||||
defaultLanguage: fr
|
defaultLanguage: fr
|
||||||
gravatar:
|
gravatar:
|
||||||
disabled: True
|
disabled: true
|
||||||
giphy:
|
giphy:
|
||||||
enabled: True
|
enabled: true
|
||||||
|
breakoutRooms:
|
||||||
|
hideAddRoomButton: false
|
||||||
|
hideAutoAssignButton: true
|
||||||
|
hideJoinRoomButton: false
|
||||||
|
|
||||||
jitsi_meet_conf_extra: {}
|
jitsi_meet_conf_extra: {}
|
||||||
jitsi_meet_conf: "{{ jitsi_meet_conf_base | combine(jitsi_meet_conf_extra, recursive=True) }}"
|
jitsi_meet_conf: "{{ jitsi_meet_conf_base | combine(jitsi_meet_conf_extra, recursive=true) }}"
|
||||||
|
|
||||||
# Meet interface configuration. Will be converted to JSON
|
# Meet interface configuration. Will be converted to JSON
|
||||||
# See https://github.com/jitsi/jitsi-meet/blob/master/interface_config.js for available settings and their meaning
|
# See https://github.com/jitsi/jitsi-meet/blob/master/interface_config.js for available settings and their meaning
|
||||||
@ -150,29 +165,29 @@ jitsi_meet_interface_conf_base:
|
|||||||
AUDIO_LEVEL_SECONDARY_COLOR: 'rgba(255,255,255,0.2)'
|
AUDIO_LEVEL_SECONDARY_COLOR: 'rgba(255,255,255,0.2)'
|
||||||
AUTO_PIN_LATEST_SCREEN_SHARE: remote-only
|
AUTO_PIN_LATEST_SCREEN_SHARE: remote-only
|
||||||
BRAND_WATERMARK_LINK: https://www.ehtrace.com
|
BRAND_WATERMARK_LINK: https://www.ehtrace.com
|
||||||
CLOSE_PAGE_GUEST_HINT: False
|
CLOSE_PAGE_GUEST_HINT: false
|
||||||
DEFAULT_BACKGROUND: '#040404'
|
DEFAULT_BACKGROUND: '#040404'
|
||||||
DEFAULT_WELCOME_PAGE_LOGO_URL: 'images/watermark.svg'
|
DEFAULT_WELCOME_PAGE_LOGO_URL: 'images/watermark.svg'
|
||||||
DISABLE_DOMINANT_SPEAKER_INDICATOR: False
|
DISABLE_DOMINANT_SPEAKER_INDICATOR: false
|
||||||
DISABLE_JOIN_LEAVE_NOTIFICATIONS: False
|
DISABLE_JOIN_LEAVE_NOTIFICATIONS: false
|
||||||
DISABLE_PRESENCE_STATUS: False
|
DISABLE_PRESENCE_STATUS: false
|
||||||
DISABLE_RINGING: False
|
DISABLE_RINGING: false
|
||||||
DISABLE_TRANSCRIPTION_SUBTITLES: True
|
DISABLE_TRANSCRIPTION_SUBTITLES: true
|
||||||
DISABLE_VIDEO_BACKGROUND: False
|
DISABLE_VIDEO_BACKGROUND: false
|
||||||
DISPLAY_WELCOME_FOOTER: True
|
DISPLAY_WELCOME_FOOTER: true
|
||||||
DISPLAY_WELCOME_PAGE_ADDITIONAL_CARD: False
|
DISPLAY_WELCOME_PAGE_ADDITIONAL_CARD: false
|
||||||
DISPLAY_WELCOME_PAGE_CONTENT: False
|
DISPLAY_WELCOME_PAGE_CONTENT: false
|
||||||
DISPLAY_WELCOME_PAGE_TOOLBAR_ADDITIONAL_CONTENT: False
|
DISPLAY_WELCOME_PAGE_TOOLBAR_ADDITIONAL_CONTENT: false
|
||||||
ENABLE_DIAL_OUT: "{{ (jitsi_jigasi_sip_server is defined) | ternary(True, False) }}"
|
ENABLE_DIAL_OUT: "{{ (jitsi_jigasi_sip_server is defined) | ternary(true, false) }}"
|
||||||
ENABLE_FEEDBACK_ANIMATION: False
|
ENABLE_FEEDBACK_ANIMATION: false
|
||||||
FILM_STRIP_MAX_HEIGHT: 120
|
FILM_STRIP_MAX_HEIGHT: 120
|
||||||
GENERATE_ROOMNAMES_ON_WELCOME_PAGE: True
|
GENERATE_ROOMNAMES_ON_WELCOME_PAGE: true
|
||||||
HIDE_INVITE_MORE_HEADER: False
|
HIDE_INVITE_MORE_HEADER: false
|
||||||
JITSI_WATERMARK_LINK: https://www.ehtrace.com
|
JITSI_WATERMARK_LINK: https://www.ehtrace.com
|
||||||
LANG_DETECTION: True
|
LANG_DETECTION: true
|
||||||
LOCAL_THUMBNAIL_RATIO: 16 / 9
|
LOCAL_THUMBNAIL_RATIO: 16 / 9
|
||||||
MAXIMUM_ZOOMING_COEFFICIENT: 1.3
|
MAXIMUM_ZOOMING_COEFFICIENT: 1.3
|
||||||
MOBILE_APP_PROMO: True
|
MOBILE_APP_PROMO: true
|
||||||
OPTIMAL_BROWSERS:
|
OPTIMAL_BROWSERS:
|
||||||
- chrome
|
- chrome
|
||||||
- chromium
|
- chromium
|
||||||
@ -182,7 +197,7 @@ jitsi_meet_interface_conf_base:
|
|||||||
- safari
|
- safari
|
||||||
POLICY_LOGO: null
|
POLICY_LOGO: null
|
||||||
PROVIDER_NAME: Ehtrace
|
PROVIDER_NAME: Ehtrace
|
||||||
RECENT_LIST_ENABLED: True
|
RECENT_LIST_ENABLED: true
|
||||||
REMOTE_THUMBNAIL_RATIO: 1
|
REMOTE_THUMBNAIL_RATIO: 1
|
||||||
SETTINGS_SECTIONS:
|
SETTINGS_SECTIONS:
|
||||||
- devices
|
- devices
|
||||||
@ -191,19 +206,19 @@ jitsi_meet_interface_conf_base:
|
|||||||
- profile
|
- profile
|
||||||
- sounds
|
- sounds
|
||||||
- more
|
- more
|
||||||
SHOW_BRAND_WATERMARK: False
|
SHOW_BRAND_WATERMARK: false
|
||||||
SHOW_CHROME_EXTENSION_BANNER: False
|
SHOW_CHROME_EXTENSION_BANNER: false
|
||||||
SHOW_JITSI_WATERMARK: False
|
SHOW_JITSI_WATERMARK: false
|
||||||
SHOW_POWERED_BY: False
|
SHOW_POWERED_BY: false
|
||||||
SHOW_PROMOTIONAL_CLOSE_PAGE: False
|
SHOW_PROMOTIONAL_CLOSE_PAGE: false
|
||||||
SUPPORT_URL: 'mailto:support@ehtrace.com'
|
SUPPORT_URL: 'mailto:support@ehtrace.com'
|
||||||
UNSUPPORTED_BROWSERS: []
|
UNSUPPORTED_BROWSERS: []
|
||||||
VERTICAL_FILMSTRIP: True
|
VERTICAL_FILMSTRIP: true
|
||||||
VIDEO_LAYOUT_FIT: both
|
VIDEO_LAYOUT_FIT: both
|
||||||
VIDEO_QUALITY_LABEL_DISABLED: False
|
VIDEO_QUALITY_LABEL_DISABLED: false
|
||||||
|
|
||||||
jitsi_meet_interface_conf_extra: {}
|
jitsi_meet_interface_conf_extra: {}
|
||||||
jitsi_meet_interface_conf: "{{ jitsi_meet_interface_conf_base | combine(jitsi_meet_interface_conf_extra, recursive=True) }}"
|
jitsi_meet_interface_conf: "{{ jitsi_meet_interface_conf_base | combine(jitsi_meet_interface_conf_extra, recursive=true) }}"
|
||||||
|
|
||||||
# You can customize strings here (lang/main-XX.json)
|
# You can customize strings here (lang/main-XX.json)
|
||||||
jitsi_meet_custom_lang: {}
|
jitsi_meet_custom_lang: {}
|
||||||
@ -221,12 +236,12 @@ jitsi_meet_custom_lang: {}
|
|||||||
|
|
||||||
# If jitsi_auth is ldap
|
# If jitsi_auth is ldap
|
||||||
# We inherit values from prosody if available, or we try to get values from ad_auth or ldap_auth
|
# We inherit values from prosody if available, or we try to get values from ad_auth or ldap_auth
|
||||||
jitsi_ldap_base: "{{ prosody_ldap_base | default(ad_auth | default(False) | ternary((ad_ldap_user_search_base is defined) | ternary(ad_ldap_user_search_base,'DC=' + ad_realm | default(samba_realm) | default(ansible_domain) | regex_replace('\\.',',DC=')), ldap_user_base | default('ou=Users') + ',' + ldap_base | default(ansible_domain | regex_replace('\\.','dc=')))) }}"
|
jitsi_ldap_base: "{{ prosody_ldap_base | default(ad_auth | default(false) | ternary((ad_ldap_user_search_base is defined) | ternary(ad_ldap_user_search_base,'DC=' + ad_realm | default(samba_realm) | default(ansible_domain) | regex_replace('\\.',',DC=')), ldap_user_base | default('ou=Users') + ',' + ldap_base | default(ansible_domain | regex_replace('\\.','dc=')))) }}"
|
||||||
jitsi_ldap_servers: "{{ prosody_ldap_server | default(ad_ldap_servers | default([ad_auth | default(False) | ternary(ad_realm | default(samba_realm) | default(ansible_domain) | lower, ldap_uri | default('ldap://' + ansible_domain) | urlsplit('hostname'))]))}}"
|
jitsi_ldap_servers: "{{ prosody_ldap_server | default(ad_ldap_servers | default([ad_auth | default(false) | ternary(ad_realm | default(samba_realm) | default(ansible_domain) | lower, ldap_uri | default('ldap://' + ansible_domain) | urlsplit('hostname'))]))}}"
|
||||||
jitsi_ldap_bind_dn: "{{ prosody_ldap_bind_dn | default(None) }}"
|
jitsi_ldap_bind_dn: "{{ prosody_ldap_bind_dn | default(None) }}"
|
||||||
jitsi_ldap_bind_pass: "{{ prosody_ldap_bind_pass | default(None) }}"
|
jitsi_ldap_bind_pass: "{{ prosody_ldap_bind_pass | default(None) }}"
|
||||||
jitsi_ldap_filter: "{{ prosody_ldap_filter | default(ad_auth | default(False) | ternary('(&(objectClass=user)(sAMAccountName=%s))','(&(objectClass=inetOrgPerson)(uid=%s))')) }}"
|
jitsi_ldap_filter: "{{ prosody_ldap_filter | default(ad_auth | default(false) | ternary('(&(objectClass=user)(sAMAccountName=%s))','(&(objectClass=inetOrgPerson)(uid=%s))')) }}"
|
||||||
jitsi_ldap_starttls: "{{ prosody_ldap_starttls | default(True) }}"
|
jitsi_ldap_starttls: "{{ prosody_ldap_starttls | default(true) }}"
|
||||||
|
|
||||||
# Jigasi settings
|
# Jigasi settings
|
||||||
|
|
||||||
@ -267,7 +282,7 @@ jitsi_jigasi_sip_extra_conf: {}
|
|||||||
# ENCRYPTION_PROTOCOL_STATUS.ZRTP: 'false'
|
# ENCRYPTION_PROTOCOL_STATUS.ZRTP: 'false'
|
||||||
# IS_PRESENCE_ENABLED: 'true'
|
# IS_PRESENCE_ENABLED: 'true'
|
||||||
# SDES_CIPHER_SUITES: AES_CM_128_HMAC_SHA1_80,AES_CM_128_HMAC_SHA1_32
|
# SDES_CIPHER_SUITES: AES_CM_128_HMAC_SHA1_80,AES_CM_128_HMAC_SHA1_32
|
||||||
jitsi_jigasi_sip_conf: "{{ jitsi_jigasi_sip_base_conf | combine(jitsi_jigasi_sip_extra_conf, recursive=True) }}"
|
jitsi_jigasi_sip_conf: "{{ jitsi_jigasi_sip_base_conf | combine(jitsi_jigasi_sip_extra_conf, recursive=true) }}"
|
||||||
|
|
||||||
jitsi_jigasi_xmpp_user: jigasi
|
jitsi_jigasi_xmpp_user: jigasi
|
||||||
jitsi_jigasi_xmpp_domain: "{{ jitsi_auth_domain }}"
|
jitsi_jigasi_xmpp_domain: "{{ jitsi_auth_domain }}"
|
||||||
@ -291,7 +306,7 @@ jitsi_confmapper_conf_base:
|
|||||||
id_max_length: 4
|
id_max_length: 4
|
||||||
db_file: "{{ jitsi_root_dir }}/data/confmapper/confmapper.sqlite"
|
db_file: "{{ jitsi_root_dir }}/data/confmapper/confmapper.sqlite"
|
||||||
jitsi_confmapper_conf_extra: {}
|
jitsi_confmapper_conf_extra: {}
|
||||||
jitsi_confmapper_conf: "{{ jitsi_confmapper_conf_base | combine(jitsi_confmapper_conf_extra, recursive=True) }}"
|
jitsi_confmapper_conf: "{{ jitsi_confmapper_conf_base | combine(jitsi_confmapper_conf_extra, recursive=true) }}"
|
||||||
|
|
||||||
# This is for Jibri integration
|
# This is for Jibri integration
|
||||||
jitsi_jibri_xmpp_user: jibri
|
jitsi_jibri_xmpp_user: jibri
|
||||||
|
@ -74,9 +74,18 @@
|
|||||||
jitsi_anonymousdomain:
|
jitsi_anonymousdomain:
|
||||||
hosts:
|
hosts:
|
||||||
anonymousdomain: guest.{{ jitsi_domain }}
|
anonymousdomain: guest.{{ jitsi_domain }}
|
||||||
- set_fact: jitsi_meet_conf={{ jitsi_anonymousdomain | combine(jitsi_meet_conf, recursive=True) }}
|
- set_fact: jitsi_meet_conf={{ jitsi_meet_conf | combine(jitsi_anonymousdomain, recursive=True) }}
|
||||||
when: jitsi_auth == 'ldap'
|
when: jitsi_auth == 'ldap' or jitsi_auth == 'token'
|
||||||
tags: jisti
|
tags: jitsi
|
||||||
|
|
||||||
|
- name: Set authentication url for jitsi meet
|
||||||
|
block:
|
||||||
|
- set_fact:
|
||||||
|
jitsi_authurl:
|
||||||
|
tokenAuthUrl: '{{ jitsi_token_auth_url }}'
|
||||||
|
- set_fact: jitsi_meet_conf={{ jitsi_meet_conf | combine(jitsi_authurl, recursive=True) }}
|
||||||
|
when: jitsi_auth == 'token'
|
||||||
|
tags: jitsi
|
||||||
|
|
||||||
- name: Check if cert file exist
|
- name: Check if cert file exist
|
||||||
stat: path={{ jitsi_cert_path }}
|
stat: path={{ jitsi_cert_path }}
|
||||||
|
@ -2,8 +2,17 @@
|
|||||||
jicofo {
|
jicofo {
|
||||||
|
|
||||||
authentication {
|
authentication {
|
||||||
enabled = {{ (jitsi_auth == 'sso' or jitsi_auth == 'ldap') | ternary('true', 'false') }}
|
{% if jitsi_auth == 'sso' %}
|
||||||
type = {{ (jitsi_auth == 'ldap') | ternary('XMPP', 'SHIBBOLETH') }}
|
enabled = true
|
||||||
|
type = SHIBBOLETH
|
||||||
|
{% elif jitsi_auth == 'ldap' %}
|
||||||
|
enabled = true
|
||||||
|
type = XMPP
|
||||||
|
{% elif jitsi_auth == 'token' %}
|
||||||
|
enabled = true
|
||||||
|
type = JWT
|
||||||
|
login-url = {{ jitsi_domain }}
|
||||||
|
{% endif %}
|
||||||
}
|
}
|
||||||
|
|
||||||
bridge {
|
bridge {
|
||||||
@ -28,5 +37,6 @@ jicofo {
|
|||||||
password = "{{ jitsi_jicofo_xmpp_pass }}"
|
password = "{{ jitsi_jicofo_xmpp_pass }}"
|
||||||
client-proxy = focus.{{ jitsi_domain }}
|
client-proxy = focus.{{ jitsi_domain }}
|
||||||
}
|
}
|
||||||
|
trusted-domains = ["{{ jitsi_jibri_xmpp_domain | default('recorder.' ~ jitsi_jibri_domain) }}"]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -11,7 +11,10 @@ external_services = {
|
|||||||
{% for stun in jitsi_stun_servers %}
|
{% for stun in jitsi_stun_servers %}
|
||||||
{
|
{
|
||||||
type = "{{ stun | urlsplit('scheme') }}",
|
type = "{{ stun | urlsplit('scheme') }}",
|
||||||
host = "{{ stun | regex_replace('(turns?|stun):([^:]+)(:\d+)?.*', '\\2') }}{% if stun | regex_replace('(turns?|stun):.+:(\d+)?.*', '\\2') | int > 0 and stun | regex_replace('(turns?|stun):.+:(\d+)?.*', '\\2') | int < 65535 %}:{{ stun | regex_replace('(turns?|stun):.+:(\d+)?.*', '\\2') }}{% endif %}",
|
host = "{{ stun | regex_replace('(turns?|stun):([^:]+)(:\d+)?.*', '\\2') }}",
|
||||||
|
{% if stun | regex_replace('(turns?|stun):.+:(\d+)?.*', '\\2') | int > 0 and stun | regex_replace('(turns?|stun):.+:(\d+)?.*', '\\2') | int < 65535 %}
|
||||||
|
port = {{ stun | regex_replace('(turns?|stun):.+:(\d+)?.*', '\\2') }},
|
||||||
|
{% endif %}
|
||||||
{% if stun | urlsplit('query') is search('transport=') %}
|
{% if stun | urlsplit('query') is search('transport=') %}
|
||||||
transport = "{{ stun | urlsplit('query') | regex_replace('.*transport=(udp|tcp).*', '\\1') }}",
|
transport = "{{ stun | urlsplit('query') | regex_replace('.*transport=(udp|tcp).*', '\\1') }}",
|
||||||
{% endif %}
|
{% endif %}
|
||||||
@ -24,7 +27,7 @@ external_services = {
|
|||||||
{% endfor %}
|
{% endfor %}
|
||||||
};
|
};
|
||||||
|
|
||||||
cross_domain_bosh = false;
|
-- cross_domain_bosh = false;
|
||||||
cross_domain_websocket = true;
|
cross_domain_websocket = true;
|
||||||
consider_bosh_secure = true;
|
consider_bosh_secure = true;
|
||||||
|
|
||||||
@ -45,40 +48,59 @@ VirtualHost "{{ jitsi_domain }}"
|
|||||||
ldap_filter = "{{ jitsi_ldap_filter }}"
|
ldap_filter = "{{ jitsi_ldap_filter }}"
|
||||||
ldap_scope = "subtree"
|
ldap_scope = "subtree"
|
||||||
ldap_tls = {{ jitsi_ldap_starttls | ternary('true','false') }}
|
ldap_tls = {{ jitsi_ldap_starttls | ternary('true','false') }}
|
||||||
|
{% elif jitsi_auth == 'token' %}
|
||||||
|
authentication = "token"
|
||||||
|
app_id = "{{ jitsi_token_app_id }}";
|
||||||
|
asap_accepted_issuers = "{{ jitsi_token_iss }}";
|
||||||
|
asap_accepted_audiences = "{{ jitsi_token_aud }}";
|
||||||
|
{% if jitsi_token_app_secret is defined %}
|
||||||
|
app_secret = "{{ jitsi_token_app_secret }}";
|
||||||
|
{% elif jitsi_token_asap_key_server is defined %}
|
||||||
|
asap_key_server = "{{ jitsi_token_asap_key_server }}";
|
||||||
|
{% endif %}
|
||||||
|
allow_empty_token = false;
|
||||||
{% else %}
|
{% else %}
|
||||||
authentication = "anonymous"
|
authentication = "jitsi-anonymous"
|
||||||
{% endif %}
|
{% endif %}
|
||||||
ssl = {
|
ssl = {
|
||||||
key = "{{ jitsi_key_path }}";
|
key = "{{ jitsi_key_path }}";
|
||||||
certificate = "{{ jitsi_cert_path }}";
|
certificate = "{{ jitsi_cert_path }}";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
c2s_require_encryption = false
|
||||||
|
allow_unencrypted_plain_auth = true
|
||||||
|
av_moderation_component = "avmoderation.{{ jitsi_domain }}"
|
||||||
|
speakerstats_component = "speakerstats.{{ jitsi_domain }}"
|
||||||
|
end_conference_component = "endconference.{{ jitsi_domain }}"
|
||||||
|
|
||||||
modules_enabled = {
|
modules_enabled = {
|
||||||
"bosh";
|
"bosh";
|
||||||
"pubsub";
|
"pubsub";
|
||||||
"ping";
|
|
||||||
"websocket";
|
"websocket";
|
||||||
"external_services";
|
"ping";
|
||||||
"speakerstats";
|
"speakerstats";
|
||||||
|
"external_services";
|
||||||
"conference_duration";
|
"conference_duration";
|
||||||
|
"end_conference";
|
||||||
"muc_lobby_rooms";
|
"muc_lobby_rooms";
|
||||||
"participant_metadata";
|
|
||||||
"muc_breakout_rooms";
|
"muc_breakout_rooms";
|
||||||
"av_moderation";
|
"av_moderation";
|
||||||
|
"room_metadata";
|
||||||
|
"participant_metadata";
|
||||||
|
"presence_identity";
|
||||||
}
|
}
|
||||||
c2s_require_encryption = false
|
|
||||||
allow_unencrypted_plain_auth = true
|
|
||||||
speakerstats_component = "speakerstats.{{ jitsi_domain }}"
|
|
||||||
conference_duration_component = "conferenceduration.{{ jitsi_domain }}"
|
conference_duration_component = "conferenceduration.{{ jitsi_domain }}"
|
||||||
lobby_muc = "lobby.{{ jitsi_domain }}"
|
lobby_muc = "lobby.{{ jitsi_domain }}"
|
||||||
breakout_rooms_muc = "breakout.{{ jitsi_domain }}"
|
breakout_rooms_muc = "breakout.{{ jitsi_domain }}"
|
||||||
|
room_metadata_component = "metadata.{{ jitsi_domain }}"
|
||||||
main_muc = "conference.{{ jitsi_domain }}"
|
main_muc = "conference.{{ jitsi_domain }}"
|
||||||
muc_lobby_whitelist = { "recorder.{{ jitsi_domain }}" }
|
muc_lobby_whitelist = { "recorder.{{ jitsi_domain }}" }
|
||||||
|
|
||||||
{% if jitsi_auth == 'ldap' %}
|
{% if jitsi_auth == 'ldap' or jitsi_auth == 'token' %}
|
||||||
-- Guest virtual domain
|
-- Guest virtual domain
|
||||||
VirtualHost "guest.{{ jitsi_domain }}"
|
VirtualHost "guest.{{ jitsi_domain }}"
|
||||||
authentication = "anonymous"
|
authentication = "jitsi-anonymous"
|
||||||
c2s_require_encryption = false
|
c2s_require_encryption = false
|
||||||
modules_enabled = {
|
modules_enabled = {
|
||||||
"participant_metadata";
|
"participant_metadata";
|
||||||
@ -101,10 +123,12 @@ VirtualHost "recorder.{{ jitsi_domain }}"
|
|||||||
c2s_require_encryption = false
|
c2s_require_encryption = false
|
||||||
|
|
||||||
Component "conference.{{ jitsi_domain }}" "muc"
|
Component "conference.{{ jitsi_domain }}" "muc"
|
||||||
|
restrict_room_creation = true
|
||||||
storage = "memory"
|
storage = "memory"
|
||||||
modules_enabled = {
|
modules_enabled = {
|
||||||
"ping";
|
"ping";
|
||||||
"jibri_bypass_pwd";
|
"jibri_bypass_pwd";
|
||||||
|
"muc_hide_all";
|
||||||
"muc_meeting_id";
|
"muc_meeting_id";
|
||||||
"muc_domain_mapper";
|
"muc_domain_mapper";
|
||||||
"polls";
|
"polls";
|
||||||
@ -113,11 +137,19 @@ Component "conference.{{ jitsi_domain }}" "muc"
|
|||||||
admins = { "{{ jitsi_jicofo_xmpp_user }}@{{ jitsi_auth_domain }}" }
|
admins = { "{{ jitsi_jicofo_xmpp_user }}@{{ jitsi_auth_domain }}" }
|
||||||
muc_room_locking = false
|
muc_room_locking = false
|
||||||
muc_room_default_public_jids = true
|
muc_room_default_public_jids = true
|
||||||
|
muc_password_whitelist = {
|
||||||
|
"{{ jitsi_jicofo_xmpp_user }}@{{ jitsi_auth_domain }}"
|
||||||
|
}
|
||||||
|
|
||||||
Component "internal.{{ jitsi_auth_domain }}" "muc"
|
Component "internal.{{ jitsi_auth_domain }}" "muc"
|
||||||
storage = "memory"
|
storage = "memory"
|
||||||
modules_enabled = { "ping"; }
|
modules_enabled = {
|
||||||
muc_room_cache_size = 1000
|
"muc_hide_all";
|
||||||
|
"ping";
|
||||||
|
}
|
||||||
|
admins = { "{{ jitsi_jicofo_xmpp_user }}@{{ jitsi_auth_domain }}" }
|
||||||
|
muc_room_locking = false
|
||||||
|
muc_room_default_public_jids = true
|
||||||
|
|
||||||
Component "focus.{{ jitsi_domain }}" "client_proxy"
|
Component "focus.{{ jitsi_domain }}" "client_proxy"
|
||||||
target_address = "{{ jitsi_jicofo_xmpp_user }}@{{ jitsi_auth_domain }}"
|
target_address = "{{ jitsi_jicofo_xmpp_user }}@{{ jitsi_auth_domain }}"
|
||||||
@ -128,6 +160,9 @@ Component "speakerstats.{{ jitsi_domain }}" "speakerstats_component"
|
|||||||
Component "conferenceduration.{{ jitsi_domain }}" "conference_duration_component"
|
Component "conferenceduration.{{ jitsi_domain }}" "conference_duration_component"
|
||||||
muc_component = "conference.{{ jitsi_domain }}"
|
muc_component = "conference.{{ jitsi_domain }}"
|
||||||
|
|
||||||
|
Component "endconference.{{ jitsi_domain }}" "end_conference"
|
||||||
|
muc_component = "conference.{{ jitsi_domain }}"
|
||||||
|
|
||||||
Component "avmoderation.{{ jitsi_domain }}" "av_moderation_component"
|
Component "avmoderation.{{ jitsi_domain }}" "av_moderation_component"
|
||||||
muc_component = "conference.{{ jitsi_domain }}"
|
muc_component = "conference.{{ jitsi_domain }}"
|
||||||
|
|
||||||
@ -137,16 +172,24 @@ Component "lobby.{{ jitsi_domain }}" "muc"
|
|||||||
muc_room_locking = false
|
muc_room_locking = false
|
||||||
muc_room_default_public_jids = true
|
muc_room_default_public_jids = true
|
||||||
modules_enabled = {
|
modules_enabled = {
|
||||||
|
"muc_hide_all";
|
||||||
"muc_rate_limit";
|
"muc_rate_limit";
|
||||||
|
"polls";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Component "metadata.{{ jitsi_domain }}" "room_metadata_component"
|
||||||
|
muc_component = "conference.{{ jitsi_domain }}"
|
||||||
|
breakout_rooms_component = "breakout.{{ jitsi_domain }}"
|
||||||
|
|
||||||
Component "breakout.{{ jitsi_domain }}" "muc"
|
Component "breakout.{{ jitsi_domain }}" "muc"
|
||||||
restrict_room_creation = true
|
restrict_room_creation = true
|
||||||
storage = "memory"
|
storage = "memory"
|
||||||
modules_enabled = {
|
modules_enabled = {
|
||||||
|
"muc_hide_all";
|
||||||
"muc_meeting_id";
|
"muc_meeting_id";
|
||||||
"muc_domain_mapper";
|
"muc_domain_mapper";
|
||||||
"muc_rate_limit";
|
"muc_rate_limit";
|
||||||
|
"polls";
|
||||||
}
|
}
|
||||||
admins = { "{{ jitsi_jicofo_xmpp_user }}@{{ jitsi_auth_domain }}" }
|
admins = { "{{ jitsi_jicofo_xmpp_user }}@{{ jitsi_auth_domain }}" }
|
||||||
muc_room_locking = false
|
muc_room_locking = false
|
||||||
|
@ -19,6 +19,24 @@ prosody_base_modules:
|
|||||||
- name: mod_auth_ldap
|
- name: mod_auth_ldap
|
||||||
- name: util.lib
|
- name: util.lib
|
||||||
url: https://raw.githubusercontent.com/jitsi/jitsi-meet/master/resources/prosody-plugins/util.lib.lua
|
url: https://raw.githubusercontent.com/jitsi/jitsi-meet/master/resources/prosody-plugins/util.lib.lua
|
||||||
|
- name: mod_auth_jitsi-anonymous
|
||||||
|
url: https://raw.githubusercontent.com/jitsi/jitsi-meet/master/resources/prosody-plugins/mod_auth_jitsi-anonymous.lua
|
||||||
|
- name: mod_end_conference
|
||||||
|
url: https://raw.githubusercontent.com/jitsi/jitsi-meet/master/resources/prosody-plugins/mod_end_conference.lua
|
||||||
|
- name: mod_room_metadata
|
||||||
|
url: https://raw.githubusercontent.com/jitsi/jitsi-meet/master/resources/prosody-plugins/mod_room_metadata.lua
|
||||||
|
- name: mod_room_metadata_component
|
||||||
|
url: https://raw.githubusercontent.com/jitsi/jitsi-meet/master/resources/prosody-plugins/mod_room_metadata_component.lua
|
||||||
|
- name: mod_muc_hide_all
|
||||||
|
url: https://raw.githubusercontent.com/jitsi/jitsi-meet/master/resources/prosody-plugins/mod_muc_hide_all.lua
|
||||||
|
- name: mod_room_destroy
|
||||||
|
url: https://raw.githubusercontent.com/jitsi/jitsi-meet/master/resources/prosody-plugins/mod_room_destroy.lua
|
||||||
|
- name: mod_presence_identity
|
||||||
|
url: https://raw.githubusercontent.com/jitsi/jitsi-meet/master/resources/prosody-plugins/mod_presence_identity.lua
|
||||||
|
- name: luajwtjitsi.lib
|
||||||
|
url: https://raw.githubusercontent.com/jitsi/jitsi-meet/master/resources/prosody-plugins/luajwtjitsi.lib.lua
|
||||||
|
- name: mod_auth_token
|
||||||
|
url: https://raw.githubusercontent.com/jitsi/jitsi-meet/master/resources/prosody-plugins/mod_auth_token.lua
|
||||||
- name: mod_speakerstats
|
- name: mod_speakerstats
|
||||||
url: https://raw.githubusercontent.com/jitsi/jitsi-meet/master/resources/prosody-plugins/mod_speakerstats.lua
|
url: https://raw.githubusercontent.com/jitsi/jitsi-meet/master/resources/prosody-plugins/mod_speakerstats.lua
|
||||||
- name: mod_speakerstats_component
|
- name: mod_speakerstats_component
|
||||||
|
11
roles/prosody/files/prosody-ansible.te
Normal file
11
roles/prosody/files/prosody-ansible.te
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
|
||||||
|
module prosody-ansible 1.0;
|
||||||
|
|
||||||
|
require {
|
||||||
|
type unlabeled_t;
|
||||||
|
type prosody_t;
|
||||||
|
class dir search;
|
||||||
|
}
|
||||||
|
|
||||||
|
#============= prosody_t ==============
|
||||||
|
allow prosody_t unlabeled_t:dir search;
|
539
roles/prosody/files/token/util.lib.lua
Normal file
539
roles/prosody/files/token/util.lib.lua
Normal file
@ -0,0 +1,539 @@
|
|||||||
|
-- Token authentication
|
||||||
|
-- Copyright (C) 2021-present 8x8, Inc.
|
||||||
|
|
||||||
|
local basexx = require "basexx";
|
||||||
|
local have_async, async = pcall(require, "util.async");
|
||||||
|
local hex = require "util.hex";
|
||||||
|
local jwt = module:require "luajwtjitsi";
|
||||||
|
local jid = require "util.jid";
|
||||||
|
local json_safe = require "cjson.safe";
|
||||||
|
local path = require "util.paths";
|
||||||
|
local sha256 = require "util.hashes".sha256;
|
||||||
|
local main_util = module:require "util";
|
||||||
|
local ends_with = main_util.ends_with;
|
||||||
|
local http_get_with_retry = main_util.http_get_with_retry;
|
||||||
|
local extract_subdomain = main_util.extract_subdomain;
|
||||||
|
local starts_with = main_util.starts_with;
|
||||||
|
local table_shallow_copy = main_util.table_shallow_copy;
|
||||||
|
local cjson_safe = require 'cjson.safe'
|
||||||
|
local timer = require "util.timer";
|
||||||
|
local async = require "util.async";
|
||||||
|
-- local inspect = require 'inspect';
|
||||||
|
|
||||||
|
local nr_retries = 3;
|
||||||
|
local ssl = require "ssl";
|
||||||
|
|
||||||
|
-- TODO: Figure out a less arbitrary default cache size.
|
||||||
|
local cacheSize = module:get_option_number("jwt_pubkey_cache_size", 128);
|
||||||
|
|
||||||
|
-- the cache for generated asap jwt tokens
|
||||||
|
local jwtKeyCache = require 'util.cache'.new(cacheSize);
|
||||||
|
|
||||||
|
local ASAPTTL_THRESHOLD = module:get_option_number('asap_ttl_threshold', 600);
|
||||||
|
local ASAPTTL = module:get_option_number('asap_ttl', 3600);
|
||||||
|
local ASAPIssuer = module:get_option_string('asap_issuer', 'jitsi');
|
||||||
|
local ASAPAudience = module:get_option_string('asap_audience', 'jitsi');
|
||||||
|
local ASAPKeyId = module:get_option_string('asap_key_id', 'jitsi');
|
||||||
|
local ASAPKeyPath = module:get_option_string('asap_key_path', '/etc/prosody/certs/asap.key');
|
||||||
|
|
||||||
|
local ASAPKey;
|
||||||
|
local f = io.open(ASAPKeyPath, 'r');
|
||||||
|
|
||||||
|
if f then
|
||||||
|
ASAPKey = f:read('*all');
|
||||||
|
f:close();
|
||||||
|
end
|
||||||
|
|
||||||
|
local Util = {}
|
||||||
|
Util.__index = Util
|
||||||
|
|
||||||
|
--- Constructs util class for token verifications.
|
||||||
|
-- Constructor that uses the passed module to extract all the
|
||||||
|
-- needed configurations.
|
||||||
|
-- If configuration is missing returns nil
|
||||||
|
-- @param module the module in which options to check for configs.
|
||||||
|
-- @return the new instance or nil
|
||||||
|
function Util.new(module)
|
||||||
|
local self = setmetatable({}, Util)
|
||||||
|
|
||||||
|
self.appId = module:get_option_string("app_id");
|
||||||
|
self.appSecret = module:get_option_string("app_secret");
|
||||||
|
self.asapKeyServer = module:get_option_string("asap_key_server");
|
||||||
|
-- A URL that will return json file with a mapping between kids and public keys
|
||||||
|
-- If the response Cache-Control header we will respect it and refresh it
|
||||||
|
self.cacheKeysUrl = module:get_option_string("cache_keys_url");
|
||||||
|
self.signatureAlgorithm = module:get_option_string("signature_algorithm");
|
||||||
|
self.allowEmptyToken = module:get_option_boolean("allow_empty_token");
|
||||||
|
|
||||||
|
self.cache = require"util.cache".new(cacheSize);
|
||||||
|
|
||||||
|
--[[
|
||||||
|
Multidomain can be supported in some deployments. In these deployments
|
||||||
|
there is a virtual conference muc, which address contains the subdomain
|
||||||
|
to use. Those deployments are accessible
|
||||||
|
by URL https://domain/subdomain.
|
||||||
|
Then the address of the room will be:
|
||||||
|
roomName@conference.subdomain.domain. This is like a virtual address
|
||||||
|
where there is only one muc configured by default with address:
|
||||||
|
conference.domain and the actual presentation of the room in that muc
|
||||||
|
component is [subdomain]roomName@conference.domain.
|
||||||
|
These setups relay on configuration 'muc_domain_base' which holds
|
||||||
|
the main domain and we use it to subtract subdomains from the
|
||||||
|
virtual addresses.
|
||||||
|
The following confgurations are for multidomain setups and domain name
|
||||||
|
verification:
|
||||||
|
--]]
|
||||||
|
|
||||||
|
-- optional parameter for custom muc component prefix,
|
||||||
|
-- defaults to "conference"
|
||||||
|
self.muc_domain_prefix = module:get_option_string(
|
||||||
|
"muc_mapper_domain_prefix", "conference");
|
||||||
|
-- domain base, which is the main domain used in the deployment,
|
||||||
|
-- the main VirtualHost for the deployment
|
||||||
|
self.muc_domain_base = module:get_option_string("muc_mapper_domain_base");
|
||||||
|
-- The "real" MUC domain that we are proxying to
|
||||||
|
if self.muc_domain_base then
|
||||||
|
self.muc_domain = module:get_option_string(
|
||||||
|
"muc_mapper_domain",
|
||||||
|
self.muc_domain_prefix.."."..self.muc_domain_base);
|
||||||
|
end
|
||||||
|
-- whether domain name verification is enabled, by default it is enabled
|
||||||
|
-- when disabled checking domain name and tenant if available will be skipped, we will check only room name.
|
||||||
|
self.enableDomainVerification = module:get_option_boolean('enable_domain_verification', true);
|
||||||
|
|
||||||
|
if self.allowEmptyToken == true then
|
||||||
|
module:log("warn", "WARNING - empty tokens allowed");
|
||||||
|
end
|
||||||
|
|
||||||
|
if self.appId == nil then
|
||||||
|
module:log("error", "'app_id' must not be empty");
|
||||||
|
return nil;
|
||||||
|
end
|
||||||
|
|
||||||
|
if self.appSecret == nil and self.asapKeyServer == nil then
|
||||||
|
module:log("error", "'app_secret' or 'asap_key_server' must be specified");
|
||||||
|
return nil;
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Set defaults for signature algorithm
|
||||||
|
if self.signatureAlgorithm == nil then
|
||||||
|
if self.asapKeyServer ~= nil then
|
||||||
|
self.signatureAlgorithm = "RS256"
|
||||||
|
elseif self.appSecret ~= nil then
|
||||||
|
self.signatureAlgorithm = "HS256"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
--array of accepted issuers: by default only includes our appId
|
||||||
|
self.acceptedIssuers = module:get_option_array('asap_accepted_issuers',{self.appId})
|
||||||
|
|
||||||
|
--array of accepted audiences: by default only includes our appId
|
||||||
|
self.acceptedAudiences = module:get_option_array('asap_accepted_audiences',{'*'})
|
||||||
|
|
||||||
|
self.requireRoomClaim = module:get_option_boolean('asap_require_room_claim', true);
|
||||||
|
|
||||||
|
if self.asapKeyServer and not have_async then
|
||||||
|
module:log("error", "requires a version of Prosody with util.async");
|
||||||
|
return nil;
|
||||||
|
end
|
||||||
|
|
||||||
|
if self.cacheKeysUrl then
|
||||||
|
self.cachedKeys = {};
|
||||||
|
local update_keys_cache;
|
||||||
|
update_keys_cache = async.runner(function (name)
|
||||||
|
local content, code, cache_for;
|
||||||
|
content, code, cache_for = http_get_with_retry(self.cacheKeysUrl, nr_retries);
|
||||||
|
if content ~= nil then
|
||||||
|
local keys_to_delete = table_shallow_copy(self.cachedKeys);
|
||||||
|
-- Let's convert any certificate to public key
|
||||||
|
for k, v in pairs(cjson_safe.decode(content)) do
|
||||||
|
if starts_with(v, '-----BEGIN CERTIFICATE-----') then
|
||||||
|
self.cachedKeys[k] = ssl.loadcertificate(v):pubkey();
|
||||||
|
-- do not clean this key if it already exists
|
||||||
|
keys_to_delete[k] = nil;
|
||||||
|
end
|
||||||
|
end
|
||||||
|
-- let's schedule the clean in an hour and a half, current tokens will be valid for an hour
|
||||||
|
timer.add_task(90*60, function ()
|
||||||
|
for k, _ in pairs(keys_to_delete) do
|
||||||
|
self.cachedKeys[k] = nil;
|
||||||
|
end
|
||||||
|
end);
|
||||||
|
|
||||||
|
if cache_for then
|
||||||
|
cache_for = tonumber(cache_for);
|
||||||
|
-- let's schedule new update 60 seconds before the cache expiring
|
||||||
|
if cache_for > 60 then
|
||||||
|
cache_for = cache_for - 60;
|
||||||
|
end
|
||||||
|
timer.add_task(cache_for, function ()
|
||||||
|
update_keys_cache:run("update_keys_cache");
|
||||||
|
end);
|
||||||
|
else
|
||||||
|
-- no cache header let's consider updating in 6hours
|
||||||
|
timer.add_task(6*60*60, function ()
|
||||||
|
update_keys_cache:run("update_keys_cache");
|
||||||
|
end);
|
||||||
|
end
|
||||||
|
else
|
||||||
|
module:log('warn', 'Failed to retrieve cached public keys code:%s', code);
|
||||||
|
-- failed let's retry in 30 seconds
|
||||||
|
timer.add_task(30, function ()
|
||||||
|
update_keys_cache:run("update_keys_cache");
|
||||||
|
end);
|
||||||
|
end
|
||||||
|
end);
|
||||||
|
update_keys_cache:run("update_keys_cache");
|
||||||
|
end
|
||||||
|
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
|
function Util:set_asap_key_server(asapKeyServer)
|
||||||
|
self.asapKeyServer = asapKeyServer;
|
||||||
|
end
|
||||||
|
|
||||||
|
function Util:set_asap_accepted_issuers(acceptedIssuers)
|
||||||
|
self.acceptedIssuers = acceptedIssuers;
|
||||||
|
end
|
||||||
|
|
||||||
|
function Util:set_asap_accepted_audiences(acceptedAudiences)
|
||||||
|
self.acceptedAudiences = acceptedAudiences;
|
||||||
|
end
|
||||||
|
|
||||||
|
function Util:set_asap_require_room_claim(checkRoom)
|
||||||
|
self.requireRoomClaim = checkRoom;
|
||||||
|
end
|
||||||
|
|
||||||
|
function Util:clear_asap_cache()
|
||||||
|
self.cache = require"util.cache".new(cacheSize);
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Returns the public key by keyID
|
||||||
|
-- @param keyId the key ID to request
|
||||||
|
-- @return the public key (the content of requested resource) or nil
|
||||||
|
function Util:get_public_key(keyId)
|
||||||
|
local content = self.cache:get(keyId);
|
||||||
|
local code;
|
||||||
|
if content == nil then
|
||||||
|
-- If the key is not found in the cache.
|
||||||
|
-- module:log("debug", "Cache miss for key: %s", keyId);
|
||||||
|
local keyurl = path.join(self.asapKeyServer, hex.to(sha256(keyId))..'.pem');
|
||||||
|
-- module:log("debug", "Fetching public key from: %s", keyurl);
|
||||||
|
content, code = http_get_with_retry(keyurl, nr_retries);
|
||||||
|
if content ~= nil then
|
||||||
|
self.cache:set(keyId, content);
|
||||||
|
else
|
||||||
|
if code == nil then
|
||||||
|
-- this is timout after nr_retries retries
|
||||||
|
module:log('warn', 'Timeout retrieving %s from %s', keyId, keyurl);
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return content;
|
||||||
|
else
|
||||||
|
-- If the key is in the cache, use it.
|
||||||
|
-- module:log("debug", "Cache hit for key: %s", keyId);
|
||||||
|
return content;
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Verifies token and process needed values to be stored in the session.
|
||||||
|
-- Token is obtained from session.auth_token.
|
||||||
|
-- Stores in session the following values:
|
||||||
|
-- session.jitsi_meet_room - the room name value from the token
|
||||||
|
-- session.jitsi_meet_domain - the domain name value from the token
|
||||||
|
-- session.jitsi_meet_context_user - the user details from the token
|
||||||
|
-- session.jitsi_meet_context_room - the room details from the token
|
||||||
|
-- session.jitsi_meet_context_group - the group value from the token
|
||||||
|
-- session.jitsi_meet_context_features - the features value from the token
|
||||||
|
-- @param session the current session
|
||||||
|
-- @return false and error
|
||||||
|
function Util:process_and_verify_token(session)
|
||||||
|
if session.auth_token == nil then
|
||||||
|
if self.allowEmptyToken then
|
||||||
|
return true;
|
||||||
|
else
|
||||||
|
return false, "not-allowed", "token required";
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
local key;
|
||||||
|
if session.public_key then
|
||||||
|
-- We're using an public key stored in the session
|
||||||
|
-- module:log("debug","Public key was found on the session");
|
||||||
|
key = session.public_key;
|
||||||
|
elseif self.asapKeyServer and session.auth_token ~= nil then
|
||||||
|
-- We're fetching an public key from an ASAP server
|
||||||
|
local dotFirst = session.auth_token:find("%.");
|
||||||
|
if not dotFirst then return false, "not-allowed", "Invalid token" end
|
||||||
|
local header, err = json_safe.decode(basexx.from_url64(session.auth_token:sub(1,dotFirst-1)));
|
||||||
|
if err then
|
||||||
|
return false, "not-allowed", "bad token format";
|
||||||
|
end
|
||||||
|
local kid = header["kid"];
|
||||||
|
if kid == nil then
|
||||||
|
return false, "not-allowed", "'kid' claim is missing";
|
||||||
|
end
|
||||||
|
local alg = header["alg"];
|
||||||
|
if alg == nil then
|
||||||
|
return false, "not-allowed", "'alg' claim is missing";
|
||||||
|
end
|
||||||
|
if alg.sub(alg,1,2) ~= "RS" then
|
||||||
|
return false, "not-allowed", "'kid' claim only support with RS family";
|
||||||
|
end
|
||||||
|
|
||||||
|
if self.cachedKeys and self.cachedKeys[kid] then
|
||||||
|
key = self.cachedKeys[kid];
|
||||||
|
else
|
||||||
|
key = self:get_public_key(kid);
|
||||||
|
end
|
||||||
|
|
||||||
|
if key == nil then
|
||||||
|
return false, "not-allowed", "could not obtain public key";
|
||||||
|
end
|
||||||
|
elseif self.appSecret ~= nil then
|
||||||
|
-- We're using a symmetric secret
|
||||||
|
key = self.appSecret
|
||||||
|
end
|
||||||
|
|
||||||
|
if key == nil then
|
||||||
|
return false, "not-allowed", "signature verification key is missing";
|
||||||
|
end
|
||||||
|
|
||||||
|
-- now verify the whole token
|
||||||
|
local claims, msg = jwt.verify(
|
||||||
|
session.auth_token,
|
||||||
|
self.signatureAlgorithm,
|
||||||
|
key,
|
||||||
|
self.acceptedIssuers,
|
||||||
|
self.acceptedAudiences
|
||||||
|
)
|
||||||
|
if claims ~= nil then
|
||||||
|
if self.requireRoomClaim then
|
||||||
|
local roomClaim = claims["room"];
|
||||||
|
if roomClaim == nil then
|
||||||
|
return false, "'room' claim is missing";
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Binds room name to the session which is later checked on MUC join
|
||||||
|
session.jitsi_meet_room = claims["room"];
|
||||||
|
-- Binds domain name to the session
|
||||||
|
session.jitsi_meet_domain = claims["sub"];
|
||||||
|
|
||||||
|
-- Binds the user details to the session if available
|
||||||
|
if claims["context"] ~= nil then
|
||||||
|
session.jitsi_meet_str_tenant = claims["context"]["tenant"];
|
||||||
|
|
||||||
|
if claims["context"]["user"] ~= nil then
|
||||||
|
session.jitsi_meet_context_user = claims["context"]["user"];
|
||||||
|
end
|
||||||
|
|
||||||
|
if claims["context"]["group"] ~= nil then
|
||||||
|
-- Binds any group details to the session
|
||||||
|
session.jitsi_meet_context_group = claims["context"]["group"];
|
||||||
|
end
|
||||||
|
|
||||||
|
if claims["context"]["features"] ~= nil then
|
||||||
|
-- Binds any features details to the session
|
||||||
|
session.jitsi_meet_context_features = claims["context"]["features"];
|
||||||
|
end
|
||||||
|
if claims["context"]["room"] ~= nil then
|
||||||
|
session.jitsi_meet_context_room = claims["context"]["room"]
|
||||||
|
end
|
||||||
|
elseif claims["user_id"] then
|
||||||
|
session.jitsi_meet_context_user = {};
|
||||||
|
session.jitsi_meet_context_user.id = claims["user_id"];
|
||||||
|
end
|
||||||
|
|
||||||
|
-- fire event that token has been verified and pass the session and the decoded token
|
||||||
|
prosody.events.fire_event('jitsi-authentication-token-verified', {
|
||||||
|
session = session;
|
||||||
|
claims = claims;
|
||||||
|
});
|
||||||
|
|
||||||
|
if session.contextRequired and claims["context"] == nil then
|
||||||
|
return false, "not-allowed", 'jwt missing required context claim';
|
||||||
|
end
|
||||||
|
|
||||||
|
return true;
|
||||||
|
else
|
||||||
|
return false, "not-allowed", msg;
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Verifies room name and domain if necessary.
|
||||||
|
-- Checks configs and if necessary checks the room name extracted from
|
||||||
|
-- room_address against the one saved in the session when token was verified.
|
||||||
|
-- Also verifies domain name from token against the domain in the room_address,
|
||||||
|
-- if enableDomainVerification is enabled.
|
||||||
|
-- @param session the current session
|
||||||
|
-- @param room_address the whole room address as received
|
||||||
|
-- @return returns true in case room was verified or there is no need to verify
|
||||||
|
-- it and returns false in case verification was processed
|
||||||
|
-- and was not successful
|
||||||
|
function Util:verify_room(session, room_address)
|
||||||
|
if self.allowEmptyToken and session.auth_token == nil then
|
||||||
|
--module:log("debug", "Skipped room token verification - empty tokens are allowed");
|
||||||
|
return true;
|
||||||
|
end
|
||||||
|
|
||||||
|
-- extract room name using all chars, except the not allowed ones
|
||||||
|
local room,_,_ = jid.split(room_address);
|
||||||
|
if room == nil then
|
||||||
|
log("error",
|
||||||
|
"Unable to get name of the MUC room ? to: %s", room_address);
|
||||||
|
return true;
|
||||||
|
end
|
||||||
|
|
||||||
|
local auth_room = session.jitsi_meet_room;
|
||||||
|
if auth_room then
|
||||||
|
if type(auth_room) == 'string' then
|
||||||
|
auth_room = string.lower(auth_room);
|
||||||
|
else
|
||||||
|
module:log('warn', 'session.jitsi_meet_room not string: %s', auth_room);
|
||||||
|
end
|
||||||
|
end
|
||||||
|
if not self.enableDomainVerification then
|
||||||
|
-- if auth_room is missing, this means user is anonymous (no token for
|
||||||
|
-- its domain) we let it through, jicofo is verifying creation domain
|
||||||
|
if auth_room and (room ~= auth_room and not ends_with(room, ']'..auth_room)) and auth_room ~= '*' then
|
||||||
|
return false;
|
||||||
|
end
|
||||||
|
|
||||||
|
return true;
|
||||||
|
end
|
||||||
|
|
||||||
|
local room_address_to_verify = jid.bare(room_address);
|
||||||
|
local room_node = jid.node(room_address);
|
||||||
|
-- parses bare room address, for multidomain expected format is:
|
||||||
|
-- [subdomain]roomName@conference.domain
|
||||||
|
local target_subdomain, target_room = extract_subdomain(room_node);
|
||||||
|
|
||||||
|
-- if we have '*' as room name in token, this means all rooms are allowed
|
||||||
|
-- so we will use the actual name of the room when constructing strings
|
||||||
|
-- to verify subdomains and domains to simplify checks
|
||||||
|
local room_to_check;
|
||||||
|
if auth_room == '*' then
|
||||||
|
-- authorized for accessing any room assign to room_to_check the actual
|
||||||
|
-- room name
|
||||||
|
if target_room ~= nil then
|
||||||
|
-- we are in multidomain mode and we were able to extract room name
|
||||||
|
room_to_check = target_room;
|
||||||
|
else
|
||||||
|
-- no target_room, room_address_to_verify does not contain subdomain
|
||||||
|
-- so we get just the node which is the room name
|
||||||
|
room_to_check = room_node;
|
||||||
|
end
|
||||||
|
else
|
||||||
|
-- no wildcard, so check room against authorized room from the token
|
||||||
|
if session.jitsi_meet_context_room and (session.jitsi_meet_context_room["regex"] == true or session.jitsi_meet_context_room["regex"] == "true") then
|
||||||
|
if target_room ~= nil then
|
||||||
|
-- room with subdomain
|
||||||
|
room_to_check = target_room:match(auth_room);
|
||||||
|
else
|
||||||
|
room_to_check = room_node:match(auth_room);
|
||||||
|
end
|
||||||
|
else
|
||||||
|
-- not a regex
|
||||||
|
room_to_check = auth_room;
|
||||||
|
end
|
||||||
|
-- module:log("debug", "room to check: %s", room_to_check)
|
||||||
|
if not room_to_check then
|
||||||
|
if not self.requireRoomClaim then
|
||||||
|
-- if we do not require to have the room claim, and it is missing
|
||||||
|
-- there is no point of continue and verifying the roomName and the tenant
|
||||||
|
return true;
|
||||||
|
end
|
||||||
|
|
||||||
|
return false;
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
if session.jitsi_meet_str_tenant
|
||||||
|
and string.lower(session.jitsi_meet_str_tenant) ~= session.jitsi_web_query_prefix then
|
||||||
|
module:log('warn', 'Tenant differs for user:%s group:%s url_tenant:%s token_tenant:%s',
|
||||||
|
session.jitsi_meet_context_user and session.jitsi_meet_context_user.id or '',
|
||||||
|
session.jitsi_meet_context_group,
|
||||||
|
session.jitsi_web_query_prefix, session.jitsi_meet_str_tenant);
|
||||||
|
session.jitsi_meet_tenant_mismatch = true;
|
||||||
|
end
|
||||||
|
|
||||||
|
local auth_domain = string.lower(session.jitsi_meet_domain);
|
||||||
|
local subdomain_to_check;
|
||||||
|
if target_subdomain then
|
||||||
|
if auth_domain == '*' then
|
||||||
|
-- check for wildcard in JWT claim, allow access if found
|
||||||
|
subdomain_to_check = target_subdomain;
|
||||||
|
else
|
||||||
|
-- no wildcard in JWT claim, so check subdomain against sub in token
|
||||||
|
subdomain_to_check = auth_domain;
|
||||||
|
end
|
||||||
|
-- from this point we depend on muc_domain_base,
|
||||||
|
-- deny access if option is missing
|
||||||
|
if not self.muc_domain_base then
|
||||||
|
module:log("warn", "No 'muc_domain_base' option set, denying access!");
|
||||||
|
return false;
|
||||||
|
end
|
||||||
|
|
||||||
|
return room_address_to_verify == jid.join(
|
||||||
|
"["..subdomain_to_check.."]"..room_to_check, self.muc_domain);
|
||||||
|
else
|
||||||
|
if auth_domain == '*' then
|
||||||
|
-- check for wildcard in JWT claim, allow access if found
|
||||||
|
subdomain_to_check = self.muc_domain;
|
||||||
|
else
|
||||||
|
-- no wildcard in JWT claim, so check subdomain against sub in token
|
||||||
|
subdomain_to_check = self.muc_domain_prefix.."."..auth_domain;
|
||||||
|
end
|
||||||
|
-- we do not have a domain part (multidomain is not enabled)
|
||||||
|
-- verify with info from the token
|
||||||
|
return room_address_to_verify == jid.join(room_to_check, subdomain_to_check);
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function Util:generateAsapToken(audience)
|
||||||
|
if not ASAPKey then
|
||||||
|
module:log('warn', 'No ASAP Key read, asap key generation is disabled');
|
||||||
|
return ''
|
||||||
|
end
|
||||||
|
|
||||||
|
audience = audience or ASAPAudience
|
||||||
|
local t = os.time()
|
||||||
|
local err
|
||||||
|
local exp_key = 'asap_exp.'..audience
|
||||||
|
local token_key = 'asap_token.'..audience
|
||||||
|
local exp = jwtKeyCache:get(exp_key)
|
||||||
|
local token = jwtKeyCache:get(token_key)
|
||||||
|
|
||||||
|
--if we find a token and it isn't too far from expiry, then use it
|
||||||
|
if token ~= nil and exp ~= nil then
|
||||||
|
exp = tonumber(exp)
|
||||||
|
if (exp - t) > ASAPTTL_THRESHOLD then
|
||||||
|
return token
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
--expiry is the current time plus TTL
|
||||||
|
exp = t + ASAPTTL
|
||||||
|
local payload = {
|
||||||
|
iss = ASAPIssuer,
|
||||||
|
aud = audience,
|
||||||
|
nbf = t,
|
||||||
|
exp = exp,
|
||||||
|
}
|
||||||
|
|
||||||
|
-- encode
|
||||||
|
local alg = 'RS256'
|
||||||
|
token, err = jwt.encode(payload, ASAPKey, alg, { kid = ASAPKeyId })
|
||||||
|
if not err then
|
||||||
|
token = 'Bearer '..token
|
||||||
|
jwtKeyCache:set(exp_key, exp)
|
||||||
|
jwtKeyCache:set(token_key, token)
|
||||||
|
return token
|
||||||
|
else
|
||||||
|
return ''
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
return Util;
|
@ -1,18 +1,22 @@
|
|||||||
---
|
---
|
||||||
|
|
||||||
- name: Install prosody
|
- name: Install prosody
|
||||||
yum:
|
package:
|
||||||
name:
|
name:
|
||||||
- prosody
|
- prosody
|
||||||
- lua-ldap
|
- lua-ldap
|
||||||
- lua-cyrussasl
|
- lua-cyrussasl
|
||||||
|
- lua-cjson
|
||||||
|
- lua-basexx
|
||||||
|
- lua-luaossl
|
||||||
|
- libjwt
|
||||||
tags: prosody
|
tags: prosody
|
||||||
|
|
||||||
- name: Create systemd unit snippet dir
|
- name: Create systemd unit snippet dir
|
||||||
file: path=/etc/systemd/system/prosody.service.d state=directory
|
file: path=/etc/systemd/system/prosody.service.d state=directory
|
||||||
tags: prosody
|
tags: prosody
|
||||||
|
|
||||||
- name: Install modules
|
- name: Install remote modules
|
||||||
get_url:
|
get_url:
|
||||||
url: "{{ item.url | default('https://raw.githubusercontent.com/prosody-modules/' ~ item.name ~ '/master/' ~ item.name ~ '.lua') }}"
|
url: "{{ item.url | default('https://raw.githubusercontent.com/prosody-modules/' ~ item.name ~ '/master/' ~ item.name ~ '.lua') }}"
|
||||||
dest: /opt/prosody/modules/{{ item.name }}.lua
|
dest: /opt/prosody/modules/{{ item.name }}.lua
|
||||||
@ -20,11 +24,14 @@
|
|||||||
notify: restart prosody
|
notify: restart prosody
|
||||||
tags: prosody
|
tags: prosody
|
||||||
|
|
||||||
- name: Install Participan Metadata module
|
- name: Install additional modules
|
||||||
copy:
|
copy:
|
||||||
src: mod_participant_metadata.lua
|
src: "{{ item }}"
|
||||||
dest: /opt/prosody/modules/
|
dest: /opt/prosody/modules/
|
||||||
notify: restart prosody
|
notify: restart prosody
|
||||||
|
loop:
|
||||||
|
- mod_participant_metadata.lua
|
||||||
|
- token
|
||||||
tags: prosody
|
tags: prosody
|
||||||
|
|
||||||
- name: Remove useless unit override
|
- name: Remove useless unit override
|
||||||
|
@ -9,6 +9,10 @@
|
|||||||
- include_tasks: facts.yml
|
- include_tasks: facts.yml
|
||||||
tags: always
|
tags: always
|
||||||
|
|
||||||
|
- include_tasks: selinux.yml
|
||||||
|
when: ansible_selinux.status == 'enabled'
|
||||||
|
tags: always
|
||||||
|
|
||||||
- include_tasks: conf.yml
|
- include_tasks: conf.yml
|
||||||
tags: always
|
tags: always
|
||||||
|
|
||||||
|
28
roles/prosody/tasks/selinux.yml
Normal file
28
roles/prosody/tasks/selinux.yml
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
---
|
||||||
|
|
||||||
|
- name: Set correct SELinux context
|
||||||
|
sefcontext:
|
||||||
|
target: "/opt/prosody(/.*)?"
|
||||||
|
setype: lib_t
|
||||||
|
seuser: system_u
|
||||||
|
state: present
|
||||||
|
tags: prosody
|
||||||
|
|
||||||
|
- name: Restore SELinux context
|
||||||
|
command: restorecon -R /opt/prosody/modules
|
||||||
|
changed_when: false
|
||||||
|
tags: prosody
|
||||||
|
|
||||||
|
- name: Copy SELinux policy
|
||||||
|
copy: src=prosody-ansible.te dest=/etc/selinux/targeted/local/
|
||||||
|
register: prosody_selinux_policy
|
||||||
|
tags: prosody
|
||||||
|
|
||||||
|
- name: Compile and load SELinux policy
|
||||||
|
shell: |
|
||||||
|
cd /etc/selinux/targeted/local/
|
||||||
|
checkmodule -M -m -o prosody-ansible.mod prosody-ansible.te
|
||||||
|
semodule_package -o prosody-ansible.pp -m prosody-ansible.mod
|
||||||
|
semodule -i /etc/selinux/targeted/local/prosody-ansible.pp
|
||||||
|
when: prosody_selinux_policy.changed
|
||||||
|
tags: prosody
|
Loading…
x
Reference in New Issue
Block a user