From fb49c9e2bc80598a8e53b8d477c795a396fadb79 Mon Sep 17 00:00:00 2001 From: Matthew Fennell Date: Thu, 12 Nov 2026 19:24:34 +0000 Subject: Use xmpp-server role from ansible-roles repo Now that the role has been migrated, we can remove the manual steps and just use it here. --- files/nginx_conf.j2 | 59 ------ files/prosody.cfg.lua.j2 | 165 ---------------- files/turnserver.conf.j2 | 12 -- files/virtual_host.cfg.lua.j2 | 107 ----------- playbook.yaml | 426 +----------------------------------------- 5 files changed, 1 insertion(+), 768 deletions(-) delete mode 100644 files/nginx_conf.j2 delete mode 100644 files/prosody.cfg.lua.j2 delete mode 100644 files/turnserver.conf.j2 delete mode 100644 files/virtual_host.cfg.lua.j2 diff --git a/files/nginx_conf.j2 b/files/nginx_conf.j2 deleted file mode 100644 index 29cd59d..0000000 --- a/files/nginx_conf.j2 +++ /dev/null @@ -1,59 +0,0 @@ -# SPDX-FileCopyrightText: 2024 Matthew Fennell -# -# SPDX-License-Identifier: AGPL-3.0-or-later - -server { - listen 443 ssl; - listen [::]:443 ssl; - - ssl_certificate /etc/prosody/certs/{{ virtual_host }}.crt; - ssl_certificate_key /etc/prosody/certs/{{ virtual_host }}.key; - - server_name chat.{{ virtual_host }}; - - location / { - proxy_pass https://localhost:5281; - proxy_set_header Host "chat.{{ virtual_host }}"; - proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; - proxy_set_header X-Forwarded-Proto $scheme; - proxy_buffering off; - tcp_nodelay on; - } -} - -server { - listen 80; - listen [::]:80; - - server_name chat.{{ virtual_host }}; - - return 301 https://$host$request_uri; -} - -server { - listen 443 ssl; - listen [::]:443 ssl; - - ssl_certificate /etc/prosody/certs/{{ virtual_host }}.crt; - ssl_certificate_key /etc/prosody/certs/{{ virtual_host }}.key; - - server_name {{ virtual_host }}; - - location / { - proxy_pass https://localhost:5281; - proxy_set_header Host "{{ virtual_host }}"; - proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; - proxy_set_header X-Forwarded-Proto $scheme; - proxy_buffering off; - tcp_nodelay on; - } -} - -server { - listen 80; - listen [::]:80; - - server_name {{ virtual_host }}; - - return 301 https://$host$request_uri; -} diff --git a/files/prosody.cfg.lua.j2 b/files/prosody.cfg.lua.j2 deleted file mode 100644 index 4b79a82..0000000 --- a/files/prosody.cfg.lua.j2 +++ /dev/null @@ -1,165 +0,0 @@ --- SPDX-FileCopyrightText: 2024 Matthew Fennell --- --- SPDX-License-Identifier: AGPL-3.0-or-later - -plugin_paths = { - "/usr/local/lib/prosody/modules" -} - -modules_enabled = { - - -- Core - "account_activity"; - "admin_adhoc"; - "admin_shell"; - "blocklist"; - "bookmarks"; - "bosh"; - "carbons"; - "cloud_notify"; - "csi_battery_saver"; - "disco"; - "external_services"; - "lastactivity"; - "lastlog2"; - "limits"; - "mam"; - "measure_active_users"; - "mimicking"; - "pep"; - "ping"; - "private"; - "roster"; - "sasl2"; - "sasl2_bind2"; - "sasl2_fast"; - "sasl2_sm"; - "sasl_ssdp"; - "saslauth"; - "server_contact_info"; - "server_info"; - "smacks"; - "stanza_debug"; - "time"; - "tls"; - "uptime"; - "vcard4"; - "vcard_legacy"; - "version"; - "websocket"; - -{% if not is_transport_server %} - -- Invites - "http"; - "http_libjs"; -{% endif %} - -{% if not is_transport_server %} - -- Turn - "turn_external"; -{% endif %} - -{% if not is_transport_server %} - -- Spam - "anti_spam"; - "report_forward"; - "spam_reporting"; - "watch_spam_reports"; -{% endif %} - -{% if not is_transport_server %} - -- S2S - "s2s_auth_dane_in"; - "s2s_bidi"; -{% endif %} - -{% if is_transport_server %} - -- Privileged components - "privilege"; -{% endif %} - -} - -modules_disabled = { - -{% if is_transport_server %} - "s2s"; -{% endif %} - -} - -pidfile = "/run/prosody/prosody.pid"; - -log = { - info = "/var/log/prosody/prosody.log"; - error = "/var/log/prosody/prosody.err"; - { - levels = { - "error" - }; - to = "syslog"; - }; -} - -{% if not is_transport_server %} -s2s_secure_auth = true -{% endif %} - -c2s_direct_tls_ports = { - 5223 -} - -{% if not is_transport_server %} -s2s_direct_tls_ports = { - 5270 -} -use_dane = true -{% endif %} - -{% if not is_transport_server %} -anti_spam_services = { - "xmppbl.org" -} -report_forward_to = { - "submit@reports.xmppbl.org"; -} -{% endif %} - -{% if not is_transport_server %} -turn_external_host = "{{ turn_server }}" -turn_external_secret = "{{ turn_secret }}" -{% endif %} - --- From Monal considerations for XMPP server admins -smacks_hibernation_time = 86400 - -{% if not is_transport_server %} -allow_registration = true -registration_invite_only = true -{% endif %} - -{% if not is_transport_server %} -firewall_scripts = { - "module:scripts/spam-blocking.pfw"; - "module:script/spam-blocklists.pfw"; -} -{% endif %} - -limits = { - c2s = { - rate = "10kb/s"; - }; - s2sin = { - rate = "30kb/s"; - }; -} - -storage = "sql" -sql = { - driver = "PostgreSQL"; - database = "prosody"; - username = "prosody"; - password = "{{ postgres_password }}"; -} - -Include "conf.d/*.cfg.lua" diff --git a/files/turnserver.conf.j2 b/files/turnserver.conf.j2 deleted file mode 100644 index de158d2..0000000 --- a/files/turnserver.conf.j2 +++ /dev/null @@ -1,12 +0,0 @@ -# SPDX-FileCopyrightText: 2024 Matthew Fennell -# -# SPDX-License-Identifier: AGPL-3.0-or-later - -syslog -no-rfc5780 -no-stun-backward-compatibility -response-origin-only-with-rfc5780 -realm={{ turn_server }} -use-auth-secret -static-auth-secret={{ turn_secret }} -external-ip={{ ipv4 }} diff --git a/files/virtual_host.cfg.lua.j2 b/files/virtual_host.cfg.lua.j2 deleted file mode 100644 index 389e98e..0000000 --- a/files/virtual_host.cfg.lua.j2 +++ /dev/null @@ -1,107 +0,0 @@ --- SPDX-FileCopyrightText: 2024 Matthew Fennell --- --- SPDX-License-Identifier: AGPL-3.0-or-later - -VirtualHost "{{ virtual_host }}" - -admins = { -{% for admin in admins %} - "{{ admin }}" -{% endfor %} -} - -modules_enabled = { - - -- Core - "groups"; - -{% if not is_transport_server %} - -- Invites - "invites"; - "invites_adhoc"; - "invites_api"; - "invites_page"; - "invites_register"; - "invites_register_web"; - "invites_tracking"; - "register"; - "register_apps"; -{% endif %} - -} - -{% if not is_transport_server %} -site_apps_show = { - "conversations"; - "dino"; - "gajim"; - "monal"; -} -{% endif %} - -{% if not is_transport_server %} -allow_user_invites = true -site_name = "{{ virtual_host }}" -{% endif %} - -privileged_entities = { -{% for transport in transports | default([]) %} - ["{{ transport.subdomain }}.{{ virtual_host }}"] = { - roster = "both"; - message = "outgoing"; - iq = { - ["http://jabber.org/protocol/pubsub"] = "both"; - ["http://jabber.org/protocol/pubsub#owner"] = "set"; - }; - }; -{% endfor %} -} - -Component "upload.{{ virtual_host }}" "http_file_share" -http_file_share_size_limit = 300 * 1024 * 1024 -http_file_share_global_quota = 5 * 1024 * 1024 * 1024 - -{% if is_transport_server %} -http_file_share_access = { -{% for transport in transports | default([]) %} - "{{ transport.subdomain }}.{{ virtual_host }}"; -{% endfor %} -} -{% endif %} - -Component "proxy.{{ virtual_host }}" "proxy65" - -proxy65_address = "chat.{{ virtual_host }}" - -modules_disabled = { - "s2s"; -} - -Component "conference.{{ virtual_host }}" "muc" -modules_enabled = { - "muc"; - "muc_mam"; - - -- Spam -{% if not is_transport_server %} - "muc_moderation"; - "muc_rtbl"; -{% endif %} -} - -log_all_rooms = true -restrict_room_creation = "local" -muc_room_default_public = false -muc_room_default_public_jids = true - -{% if not is_transport_server %} -muc_rtbl_jid = "xmppbl.org" -{% endif %} - -{% for transport in transports | default([]) %} -Component "{{ transport.subdomain }}.{{ virtual_host }}" -component_secret = "{{ transport.secret }}" -modules_enabled = { - "privilege"; -} -{% endfor %} diff --git a/playbook.yaml b/playbook.yaml index 5b6231a..d1aae6a 100644 --- a/playbook.yaml +++ b/playbook.yaml @@ -10,432 +10,8 @@ roles: - bootstrap - unattended-upgrades + - xmpp-server - role: munin-node vars: extra_profiles: - prosody - tasks: - - - name: Retrieve DANE hash - ansible.builtin.shell: - cmd: > - set -o pipefail && openssl x509 -in - ~/.lego/certificates/{{ virtual_host }}.crt -noout -pubkey | openssl - pkey -pubin -outform DER | openssl dgst -sha256 -binary | hexdump -ve - '/1 "%02x"' - register: dane_result - changed_when: false - delegate_to: localhost - - - name: Take note of DANE hash - ansible.builtin.set_fact: - dane_hash: "{{ dane_result.stdout }}" - tags: - - dns - - - name: Ensure common records exist - ansible.builtin.uri: - url: "https://api.mythic-beasts.com/dns/v2/zones/{{ domain }}/records\ - ?select=host%3Dchat{{ env_suffix }}%26type%3DA\ - &select=host%3Dchat{{ env_suffix }}%26type%3DAAAA\ - &select=host%3Dconference{{ env_suffix }}%26type%3DCNAME\ - &select=host%3Dupload{{ env_suffix }}%26type%3DCNAME\ - &select=host%3D_xmpp-client._tcp{{ env_suffix }}%26type%3DSRV\ - &select=host%3D_xmpps-client._tcp{{ env_suffix }}%26type%3DSRV\ - &select=host%3D_5222._tcp.chat{{ env_suffix }}%26type%3DTLSA\ - %26data%3D{{ dane_hash }}\ - &select=host%3D_5223._tcp.chat{{ env_suffix }}%26type%3DTLSA\ - %26data%3D{{ dane_hash }}" - method: PUT - body_format: json - body: - records: - - host: "chat{{ env_suffix }}" - type: "A" - data: "{{ ipv4 }}" - - host: "chat{{ env_suffix }}" - type: "AAAA" - data: "{{ ipv6 }}" - - host: "conference{{ env_suffix }}" - type: "CNAME" - data: "chat{{ env_suffix }}.{{ domain }}." - - host: "upload{{ env_suffix }}" - type: "CNAME" - data: "chat{{ env_suffix }}.{{ domain }}." - - host: "_xmpp-client._tcp{{ env_suffix }}" - type: "SRV" - data: "chat{{ env_suffix }}.{{ domain }}." - srv_priority: 0 - srv_weight: 5 - srv_port: 5222 - - host: "_xmpps-client._tcp{{ env_suffix }}" - type: "SRV" - data: "chat{{ env_suffix }}.{{ domain }}." - srv_priority: "0" - srv_weight: "5" - srv_port: "5223" - - host: "_5222._tcp.chat{{ env_suffix }}" - type: "TLSA" - data: "{{ dane_hash }}" - tlsa_usage: "3" - tlsa_selector: "1" - tlsa_matching: "1" - - host: "_5223._tcp.chat{{ env_suffix }}" - type: "TLSA" - data: "{{ dane_hash }}" - tlsa_usage: "3" - tlsa_selector: "1" - tlsa_matching: "1" - delegate_to: localhost - tags: - - dns - - - name: Ensure non-transport records exist - ansible.builtin.uri: - url: "https://api.mythic-beasts.com/dns/v2/zones/{{ domain }}/records\ - ?select=host%3D_xmpp-server._tcp{{ env_suffix }}%26type%3DSRV\ - &select=host%3D_xmpps-server._tcp{{ env_suffix }}%26type%3DSRV\ - &select=host%3D_xmpps-server._tcp.conference{{ env_suffix }}\ - %26type%3DSRV\ - &select=host%3D_xmpps-server._tcp.upload{{ env_suffix }}%26type%3DSRV\ - &select=host%3D_5269._tcp.chat{{ env_suffix }}%26type%3DTLSA\ - %26data%3D{{ dane_hash }}\ - &select=host%3D_5270._tcp.chat{{ env_suffix }}%26type%3DTLSA\ - %26data%3D{{ dane_hash }}" - method: PUT - body_format: json - body: - records: - - host: "_xmpp-server._tcp{{ env_suffix }}" - type: "SRV" - data: "chat{{ env_suffix }}.{{ domain }}." - srv_priority: "0" - srv_weight: "5" - srv_port: "5269" - - host: "_xmpps-server._tcp{{ env_suffix }}" - type: "SRV" - data: "chat{{ env_suffix }}.{{ domain }}." - srv_priority: "0" - srv_weight: "5" - srv_port: "5270" - - host: "_xmpps-server._tcp.conference{{ env_suffix }}" - type: "SRV" - data: "chat{{ env_suffix }}.{{ domain }}." - srv_priority: "0" - srv_weight: "5" - srv_port: "5270" - - host: "_xmpps-server._tcp.upload{{ env_suffix }}" - type: "SRV" - data: "chat{{ env_suffix }}.{{ domain }}." - srv_priority: "0" - srv_weight: "5" - srv_port: "5270" - - host: "_5269._tcp.chat{{ env_suffix }}" - type: "TLSA" - data: "{{ dane_hash }}" - tlsa_usage: "3" - tlsa_selector: "1" - tlsa_matching: "1" - - host: "_5270._tcp.chat{{ env_suffix }}" - type: "TLSA" - data: "{{ dane_hash }}" - tlsa_usage: "3" - tlsa_selector: "1" - tlsa_matching: "1" - delegate_to: localhost - when: not is_transport_server - tags: - - dns - - # We specifically use apt instead of the more general package module here, - # because we want to ensure the cache is updated before we try and install - # anything. This is needed because, on a freh Debian install on AWS - # Lightsail (as of 2024-02-08), nothing was returned after running apt - # search borgmatic. Updating the cache before running apt install solved - # this issue, but the package module does not support this functionality. - - name: Ensure required packages are installed - ansible.builtin.apt: - name: - - lua-dbi-postgresql # Prosody postgres connection - - lua-unbound # Prosody DNS resolution - - postgresql # Database - - prosody # XMPP server - - prosody-modules # Extra addons - - python3-psycopg2 # Used by ansible postgres role - state: present - update_cache: true - become: true - - - name: Ensure invite-specific packages are installed - ansible.builtin.apt: - name: - - libjs-bootstrap4 # Used by invite webpage - - libjs-jquery # Used by invite webpage - - nginx # Serve invite webpages - state: present - update_cache: true - become: true - when: not is_transport_server - - - name: Ensure turn-specific packages are installed - ansible.builtin.apt: - name: - - coturn # Audio / video calling server - state: present - update_cache: true - become: true - when: not is_transport_server - - - name: Ensure required ports with ufw applications are open - community.general.ufw: - rule: allow - name: "{{ item }}" - state: enabled - loop: - - OpenSSH - become: true - - - name: Ensure full XMPP ports are open - community.general.ufw: - rule: allow - name: "{{ item }}" - state: enabled - loop: - - XMPP - become: true - when: not is_transport_server - - - name: Ensure only c2s ports are open - community.general.ufw: - rule: allow - port: "{{ item }}" - proto: tcp - state: enabled - loop: - - 5222 - become: true - when: is_transport_server - - - name: Ensure invite-specific ports with ufw applications are open - community.general.ufw: - rule: allow - name: "{{ item }}" - state: enabled - loop: - - WWW Full - become: true - when: not is_transport_server - - - name: Ensure turn-specific ports with ufw applications are open - community.general.ufw: - rule: allow - name: "{{ item }}" - state: enabled - loop: - - Turnserver - become: true - when: not is_transport_server - - - name: Ensure other required tcp ports are open - community.general.ufw: - rule: allow - port: "{{ item }}" - proto: tcp - state: enabled - loop: - - 5000 # XEP-0065 - - 5223 # XEP-0368 - - 5270 # XEP-0368 - - 5280 # XEP-0363 - - 5281 # XEP-0363 - # - 5432 # Postgres - become: true - - - name: Ensure other udp ports are open - community.general.ufw: - rule: allow - port: "{{ item }}" - proto: udp - state: enabled - loop: - - 5000 # XEP-0065 - - 5280 # XEP-0363 - - 5281 # XEP-0363 - become: true - - - name: Ensure default nginx config is removed - ansible.builtin.file: - path: "/etc/nginx/sites-enabled/default" - state: absent - become: true - notify: Restart nginx - when: not is_transport_server - - - name: Ensure nginx config is installed - ansible.builtin.template: - src: "{{ playbook_dir }}/files/nginx_conf.j2" - dest: /etc/nginx/sites-available/{{ virtual_host }} - owner: root - group: root - mode: "0644" - become: true - notify: Restart nginx - when: not is_transport_server - - - name: Ensure nginx config is enabled - ansible.builtin.file: - src: /etc/nginx/sites-available/{{ virtual_host }} - dest: /etc/nginx/sites-enabled/{{ virtual_host }} - owner: root - group: root - state: link - become: true - notify: Restart nginx - when: not is_transport_server - - - name: Ensure turn is configured - ansible.builtin.template: - src: "{{ playbook_dir }}/files/turnserver.conf.j2" - dest: /etc/turnserver.conf - owner: root - group: prosody - mode: "0640" - become: true - notify: Restart coturn - when: not is_transport_server - - - name: Ensure prosody database is created - community.postgresql.postgresql_db: - name: prosody - become: true - become_user: postgres - - - name: Ensure prosody role is created - community.postgresql.postgresql_user: - login_db: prosody - name: prosody - become: true - become_user: postgres - - - name: Ensure prosody schema is created - community.postgresql.postgresql_schema: - login_db: prosody - name: prosody - owner: prosody - become: true - become_user: postgres - register: my_result - - - name: Ensure prosody user exists on database - community.postgresql.postgresql_user: - name: prosody - become: true - become_user: postgres - - - name: Ensure prosody user has permissions on database - community.postgresql.postgresql_privs: - type: database - login_db: prosody - privs: ALL - roles: prosody - become: true - become_user: postgres - - - name: Ensure prosody user has permissions on schema - community.postgresql.postgresql_privs: - type: table - login_db: prosody - objs: ALL_IN_SCHEMA - privs: ALL - roles: prosody - become: true - become_user: postgres - - - name: Ensure top-level prosody configuration is installed - ansible.builtin.template: - src: "{{ playbook_dir }}/files/prosody.cfg.lua.j2" - dest: /etc/prosody/prosody.cfg.lua - owner: root - group: prosody - mode: "0640" - become: true - notify: Restart prosody - - - name: Ensure host-specific prosody configuration is available - ansible.builtin.template: - src: "{{ playbook_dir }}/files/virtual_host.cfg.lua.j2" - dest: "/etc/prosody/conf.avail/{{ virtual_host }}.cfg.lua" - owner: root - group: prosody - mode: "0644" - become: true - notify: Restart prosody - - - name: Ensure host-specific prosody configuration is set - ansible.builtin.file: - src: "/etc/prosody/conf.avail/{{ virtual_host }}.cfg.lua" - dest: "/etc/prosody/conf.d/{{ virtual_host }}.cfg.lua" - owner: root - group: prosody - state: link - become: true - notify: Restart prosody - - - name: Ensure localhost prosody configuration is removed - ansible.builtin.file: - path: "/etc/prosody/conf.d/localhost.cfg.lua" - state: absent - become: true - notify: Restart prosody - - - name: Ensure localhost prosody configuration is not available - ansible.builtin.file: - path: "/etc/prosody/conf.avail/localhost.cfg.lua" - state: absent - become: true - - - name: Ensure example prosody configuration is not available - ansible.builtin.file: - path: "/etc/prosody/conf.avail/example.com.cfg.lua" - state: absent - become: true - - - name: Ensure prosody is enabled - ansible.builtin.service: - name: prosody - enabled: true - become: true - - handlers: - - - name: Restart prosody - ansible.builtin.service: - name: prosody - state: restarted - become: true - - - name: Restart coturn - ansible.builtin.service: - name: coturn - state: restarted - become: true - - - name: Restart nginx - ansible.builtin.service: - name: nginx - state: restarted - become: true - - vars: - - env_prefix: >- - {{ "" if env == "" else env + "." }} - - env_suffix: >- - {{ "" if env == "" else "." + env }} - - virtual_host: "{{ env_prefix }}{{ domain }}" - - turn_server: "chat.{{ env_prefix }}{{ turn_domain }}" - - is_transport_server: "{{ transports | default([]) | length > 0 }}" -- cgit v1.2.3