From 0261e875679f1bf63c8d689da7fc7e014597885d Mon Sep 17 00:00:00 2001 From: Stonewall Jackson Date: Sat, 4 Feb 2023 01:23:43 -0500 Subject: initial commit --- roles/prosody/defaults/main.yml | 21 ++++ .../systemd/system/prosody.service.d/override.conf | 6 ++ roles/prosody/handlers/main.yml | 4 + roles/prosody/meta/main.yml | 16 +++ roles/prosody/tasks/database.yml | 17 +++ roles/prosody/tasks/freeipa.yml | 64 +++++++++++ roles/prosody/tasks/main.yml | 97 +++++++++++++++++ .../templates/etc/prosody/prosody.cfg.lua.j2 | 119 +++++++++++++++++++++ .../usr/local/bin/prosody-update-roster.j2 | 56 ++++++++++ roles/prosody/vars/main.yml | 38 +++++++ 10 files changed, 438 insertions(+) create mode 100644 roles/prosody/defaults/main.yml create mode 100644 roles/prosody/files/etc/systemd/system/prosody.service.d/override.conf create mode 100644 roles/prosody/handlers/main.yml create mode 100644 roles/prosody/meta/main.yml create mode 100644 roles/prosody/tasks/database.yml create mode 100644 roles/prosody/tasks/freeipa.yml create mode 100644 roles/prosody/tasks/main.yml create mode 100644 roles/prosody/templates/etc/prosody/prosody.cfg.lua.j2 create mode 100644 roles/prosody/templates/usr/local/bin/prosody-update-roster.j2 create mode 100644 roles/prosody/vars/main.yml (limited to 'roles/prosody') diff --git a/roles/prosody/defaults/main.yml b/roles/prosody/defaults/main.yml new file mode 100644 index 0000000..df7ac3b --- /dev/null +++ b/roles/prosody/defaults/main.yml @@ -0,0 +1,21 @@ +prosody_admins: [] +prosody_vhosts: ['{{ email_domain }}'] +prosody_conference_vhosts: "{{ ['conference.'] | product(prosody_vhosts) | map('join') | list }}" +prosody_user: s-prosody +prosody_db_name: prosody +prosody_db_host: '{{ postgresql_host }}' +prosody_archive_expires_after: 4w +prosody_http_port: 5280 +prosody_http_host: '{{ ansible_fqdn }}' +prosody_sysaccount_username: prosody + +prosody_ldap_hosts: '{{ freeipa_hosts }}' +prosody_access_group: role-xmpp-access + +prosody_upload_file_size_limit: 52428800 # 50 MB +prosody_upload_expire_after: 604800 # 1 week +prosody_upload_quota: 10737418240 # 10 GiB + +prosody_turn_secret: '{{ coturn_auth_secret }}' +prosody_turn_host: '{{ coturn_realm }}' +prosody_turn_port: 3478 diff --git a/roles/prosody/files/etc/systemd/system/prosody.service.d/override.conf b/roles/prosody/files/etc/systemd/system/prosody.service.d/override.conf new file mode 100644 index 0000000..8ac7456 --- /dev/null +++ b/roles/prosody/files/etc/systemd/system/prosody.service.d/override.conf @@ -0,0 +1,6 @@ +[Unit] +Wants=gssproxy.service +After=network-online.target gssproxy.service + +[Service] +Environment=GSS_USE_PROXY=yes diff --git a/roles/prosody/handlers/main.yml b/roles/prosody/handlers/main.yml new file mode 100644 index 0000000..3d3cbf4 --- /dev/null +++ b/roles/prosody/handlers/main.yml @@ -0,0 +1,4 @@ +- name: restart prosody + systemd: + name: prosody + state: restarted diff --git a/roles/prosody/meta/main.yml b/roles/prosody/meta/main.yml new file mode 100644 index 0000000..8f5b990 --- /dev/null +++ b/roles/prosody/meta/main.yml @@ -0,0 +1,16 @@ +dependencies: + - role: yum + yum_repositories: epel + tags: yum + + - role: prosody_letsencrypt_proxy + + - role: freeipa_system_account + system_account_username: '{{ prosody_sysaccount_username }}' + system_account_password: '{{ prosody_sysaccount_password }}' + + - role: apache_vhost + apache_server_name: '{{ prosody_http_host }}' + apache_server_aliases: [] + apache_letsencrypt: yes + apache_config: '{{ prosody_apache_config }}' diff --git a/roles/prosody/tasks/database.yml b/roles/prosody/tasks/database.yml new file mode 100644 index 0000000..675ab11 --- /dev/null +++ b/roles/prosody/tasks/database.yml @@ -0,0 +1,17 @@ +- name: create database + postgresql_db: + name: '{{ prosody_db_name }}' + state: present + delegate_to: '{{ postgresql_inventory_host }}' + become: yes + become_user: postgres + +- name: create database user + postgresql_user: + name: '{{ prosody_user }}' + db: '{{ prosody_db_name }}' + priv: ALL + state: present + delegate_to: '{{ postgresql_inventory_host }}' + become: yes + become_user: postgres diff --git a/roles/prosody/tasks/freeipa.yml b/roles/prosody/tasks/freeipa.yml new file mode 100644 index 0000000..caff62a --- /dev/null +++ b/roles/prosody/tasks/freeipa.yml @@ -0,0 +1,64 @@ +- name: create user + ipauser: + ipaadmin_principal: '{{ ipa_user }}' + ipaadmin_password: '{{ ipa_pass }}' + name: '{{ prosody_user }}' + loginshell: /sbin/nologin + homedir: '{{ prosody_data_dir }}' + givenname: Prosody + sn: Service Account + state: present + run_once: yes + +- name: retrieve user keytab + include_role: + name: freeipa_keytab + vars: + keytab_principal: '{{ prosody_user }}' + keytab_path: '{{ prosody_keytab }}' + +- name: configure gssproxy for kerberized postgres + include_role: + name: gssproxy_client + vars: + gssproxy_name: prosody + gssproxy_section: service/prosody + gssproxy_client_keytab: '{{ prosody_keytab }}' + gssproxy_cred_usage: initiate + gssproxy_euid: prosody + +- name: create systemd override directory + file: + path: /etc/systemd/system/prosody.service.d + state: directory + +- name: create systemd override file + copy: + src: etc/systemd/system/prosody.service.d/override.conf + dest: /etc/systemd/system/prosody.service.d/override.conf + register: prosody_systemd_unit + notify: restart prosody + +- name: reload systemd units + systemd: + daemon_reload: yes + when: prosody_systemd_unit.changed + +- name: create SELinux policy for prosody to access gssproxy + include_role: + name: selinux_policy + apply: + tags: selinux + vars: + selinux_policy_name: prosody_gssproxy + selinux_policy_te: '{{ prosody_selinux_policy_te }}' + tags: selinux + +- name: create access group + ipagroup: + ipaadmin_principal: '{{ ipa_user }}' + ipaadmin_password: '{{ ipa_pass }}' + name: '{{ prosody_access_group }}' + nonposix: yes + state: present + run_once: yes diff --git a/roles/prosody/tasks/main.yml b/roles/prosody/tasks/main.yml new file mode 100644 index 0000000..c29dd38 --- /dev/null +++ b/roles/prosody/tasks/main.yml @@ -0,0 +1,97 @@ +- name: install prosody + dnf: + name: '{{ prosody_packages }}' + state: present + +- name: request conference vhost certificates + include_role: + name: certbot + vars: + certificate_sans: ['{{ item }}'] + certificate_path: '{{ prosody_certificate_dir }}/{{ item }}.crt' + certificate_key_path: '{{ prosody_certificate_dir }}/{{ item }}.key' + certificate_owner: prosody + certificate_hook: systemctl reload prosody + certificate_use_apache: yes + loop: '{{ prosody_conference_vhosts }}' + +- import_tasks: freeipa.yml + tags: freeipa + +- import_tasks: database.yml + tags: database + +- name: create module directory + file: + path: '{{ prosody_module_dir }}' + state: directory + +- name: clone module repository + hg: + repo: '{{ prosody_module_repo }}' + dest: '{{ prosody_module_dir }}' + +- name: generate configuration + template: + src: etc/prosody/prosody.cfg.lua.j2 + dest: /etc/prosody/prosody.cfg.lua + owner: root + group: prosody + mode: 0640 + notify: restart prosody + +- name: open firewall ports + firewalld: + permanent: yes + immediate: yes + service: '{{ item }}' + state: enabled + loop: + - xmpp-client + - xmpp-server + tags: firewalld + +- name: enable httpd_can_network_connect SELinux boolean + seboolean: + name: httpd_can_network_connect + state: yes + persistent: yes + tags: selinux + +- name: create roster file with correct permissions + copy: + content: '' + dest: '{{ prosody_groups_file }}' + owner: prosody + group: prosody + mode: 0640 + force: no + +- name: generate roster script + template: + src: usr/local/bin/prosody-update-roster.j2 + dest: /usr/local/bin/prosody-update-roster + mode: 0555 + +- name: create prosody-update-roster timer + include_role: + name: systemd_timer + vars: + timer_name: prosody-update-roster + timer_description: Update prosody shared roster + timer_after: network.target + timer_on_calendar: daily + timer_exec: /usr/local/bin/prosody-update-roster + timer_user: prosody + +- name: generate shared roster + systemd: + name: prosody-update-roster.service + state: started + changed_when: no + +- name: start prosody + systemd: + name: prosody + enabled: yes + state: started diff --git a/roles/prosody/templates/etc/prosody/prosody.cfg.lua.j2 b/roles/prosody/templates/etc/prosody/prosody.cfg.lua.j2 new file mode 100644 index 0000000..9a07f8e --- /dev/null +++ b/roles/prosody/templates/etc/prosody/prosody.cfg.lua.j2 @@ -0,0 +1,119 @@ +admins = { {% for admin in prosody_admins %}"{{ admin }}"{% if loop.last %},{% endif %}{% endfor %} } + +network_backend = "event" + +plugin_paths = { "{{ prosody_module_dir }}" } + +modules_enabled = { + -- required modules + "roster"; -- Allow users to have a roster. Recommended ;) + "saslauth"; -- Authentication for clients and servers. Recommended if you want to log in. + "tls"; -- Add support for secure TLS on c2s/s2s connections + "dialback"; -- s2s dialback support + "disco"; -- Service discovery + + -- optional modules + "csi"; -- Client state indication + "carbons"; -- Keep multiple clients in sync + "pep"; -- Enables users to publish their avatar, mood, activity, playing music and more + "private"; -- Private XML storage (for room bookmarks, etc.) + "blocklist"; -- Allow users to block communications with other users + "vcard4"; -- User profiles (stored in PEP) + "vcard_legacy"; -- Conversion between legacy vCard and PEP Avatar, vcard + "limits"; -- Enable bandwidth limiting for XMPP connections + + "version"; -- Replies to server version requests + "uptime"; -- Report how long server has been running + "time"; -- Let others know the time here on this server + "ping"; -- Replies to XMPP pings with pongs + "mam"; -- Store messages in an archive and allow users to access it + "admin_adhoc"; -- Allows administration via an XMPP client that supports ad-hoc commands + "groups"; -- Shared roster support + + -- community modules + "smacks"; -- Stream management / fast reconnects + "csi_battery_saver"; -- Mobile optimizations + "turn_external"; -- STUN/TURN server + "reload_modules"; -- Reload modules on config reload +} + +reload_modules = { "groups", "tls" } +pidfile = "/run/prosody/prosody.pid"; + +allow_registration = false +groups_file = "{{ prosody_groups_file }}" + +c2s_require_encryption = true +s2s_require_encryption = true +s2s_secure_auth = false + +-- Enable rate limits for incoming client and server connections +limits = { + c2s = { + rate = "10kb/s"; + }; + s2sin = { + rate = "30kb/s"; + }; +} + +-- Authentication +authentication = "ldap" +ldap_server = "{{ prosody_ldap_hosts | join(' ') }}" +ldap_rootdn = "uid={{ prosody_sysaccount_username }},{{ freeipa_sysaccount_basedn }}" +ldap_password = "{{ prosody_sysaccount_password }}" +ldap_base = "{{ freeipa_user_basedn }}" +ldap_filter = "(&(jid=$user@$host)(memberOf=cn={{ prosody_access_group }},{{ freeipa_group_basedn }}))" +ldap_tls = true + +-- Storage +storage = "sql" +sql = { + driver = "PostgreSQL", + database = "{{ prosody_db_name }}", + username = "{{ prosody_user }}", + host = "{{ prosody_db_host }}" +} + +archive_expires_after = "{{ prosody_archive_expires_after }}" + +-- Logging +log = { + info = "*console"; +} + +-- Certificates +certificates = "/etc/pki/prosody" + +-- HTTP +http_ports = { {{ prosody_http_port }} } +http_interfaces = { "127.0.0.1", "::1" } +https_interfaces = { } +https_ports = { } +http_external_url = "https://{{ prosody_http_host }}/" +https_external_url = "https://{{ prosody_http_host }}/" +http_max_content_size = {{ prosody_upload_file_size_limit }} +trusted_proxies = { "127.0.0.1", "::1" } + +Component "{{ prosody_http_host }}" "http_upload" + +http_upload_file_size_limit = {{ prosody_upload_file_size_limit }} +http_upload_expire_after = {{ prosody_upload_expire_after }} +http_upload_quota = {{ prosody_upload_quota }} + +-- Virtual hosts +{% for vhost in prosody_vhosts %} +VirtualHost "{{ vhost }}" +disco_items = { + { "{{ prosody_http_host }}" }, +} +turn_external_host = "{{ prosody_turn_host }}" +turn_external_port = {{ prosody_turn_port }} +turn_external_secret = "{{ prosody_turn_secret }}" + +{% endfor %} + +{% for vhost in prosody_conference_vhosts %} +Component "{{ vhost }}" "muc" + modules_enabled = { "muc_mam" } +{% endfor %} diff --git a/roles/prosody/templates/usr/local/bin/prosody-update-roster.j2 b/roles/prosody/templates/usr/local/bin/prosody-update-roster.j2 new file mode 100644 index 0000000..680ab91 --- /dev/null +++ b/roles/prosody/templates/usr/local/bin/prosody-update-roster.j2 @@ -0,0 +1,56 @@ +#!/usr/libexec/platform-python + +# Copyright (c) 2023 stonewall@sacredheartsc.com +# MIT License https://opensource.org/licenses/MIT +# +# Generates a shared roster file for Prosody from the given IPA group. + +import os +import sys +import ldap +import ldap.sasl +import ldap.filter +import hashlib +import subprocess + +LDAP_URI = '{{ freeipa_ldap_uri }}' +USER_BASEDN = '{{ freeipa_user_basedn }}' +GROUP_BASEDN = '{{ freeipa_group_basedn }}' + +PROSODY_GROUPS_FILE = '{{ prosody_groups_file }}' +PROSODY_ACCESS_GROUP = '{{ prosody_access_group }}' + +ROSTER_GROUP_NAME = 'Internal' + +os.environ['GSS_USE_PROXY'] = 'yes' +conn = ldap.initialize(LDAP_URI) +conn.protocol_version = ldap.VERSION3 +conn.sasl_interactive_bind_s('', ldap.sasl.sasl({}, 'GSSAPI')) + +users = conn.search_s( + USER_BASEDN, + ldap.SCOPE_SUBTREE, + ldap.filter.filter_format('memberOf=cn=%s,%s', [PROSODY_ACCESS_GROUP, GROUP_BASEDN]), + ['jid', 'displayName']) + +if not users: + exit(1) + +with open(PROSODY_GROUPS_FILE, 'rb') as f: + hash_before = hashlib.md5(f.read()).hexdigest() + f.close() + +with open(PROSODY_GROUPS_FILE, 'w') as f: + print(f'[{ROSTER_GROUP_NAME}]', file=f) + for user in users: + jid = user[1]['jid'][0].decode('utf-8') + displayName = user[1]['displayName'][0].decode('utf-8') + print(f'{jid}={displayName}', file=f) + f.close() + +with open(PROSODY_GROUPS_FILE, 'rb') as f: + hash_after = hashlib.md5(f.read()).hexdigest() + f.close() + +if hash_before != hash_after: + subprocess.run(['prosodyctl', 'reload']) diff --git a/roles/prosody/vars/main.yml b/roles/prosody/vars/main.yml new file mode 100644 index 0000000..d971fb7 --- /dev/null +++ b/roles/prosody/vars/main.yml @@ -0,0 +1,38 @@ +prosody_certificate_dir: /etc/pki/prosody +prosody_module_dir: /usr/local/lib64/prosody/modules +prosody_data_dir: /var/lib/prosody +prosody_keytab: /var/lib/gssproxy/clients/{{ prosody_user }}.keytab +prosody_groups_file: /etc/prosody/groups.ini + +prosody_module_repo: https://hg.prosody.im/prosody-modules/ + +prosody_packages: + - prosody + - lua-dbi + - lua-event + - lua-ldap + - lua-sec + - mercurial + +prosody_apache_config: | + {{ apache_proxy_config }} + ProxyPass / http://127.0.0.1:{{ prosody_http_port }}/ + ProxyPassReverse / http://127.0.0.1:{{ prosody_http_port }}/ + +prosody_selinux_policy_te: | + require { + type prosody_t; + type gssproxy_t; + type gssproxy_var_lib_t; + type ldap_port_t; + class dir search; + class sock_file write; + class unix_stream_socket connectto; + class tcp_socket name_connect; + } + + #============= prosody_t ============== + allow prosody_t gssproxy_var_lib_t:dir search; + allow prosody_t gssproxy_var_lib_t:sock_file write; + allow prosody_t gssproxy_t:unix_stream_socket connectto; + allow prosody_t ldap_port_t:tcp_socket name_connect; -- cgit