Changeset - 28de9251c7aa
[Not reviewed]
roles/xmpp_server/molecule/default/host_vars/ldap-server.yml
Show inline comments
 
---
 

	
 
# ldap_server role.
 
ldap_admin_password: admin
 

	
 
ldap_server_consumers:
 
  - name: prosody
 
    password: prosodypassword
 

	
 
ldap_server_domain: "local"
 
ldap_server_groups:
 
  - name: xmpp
 
ldap_server_organization: "Example"
 
ldap_server_tls_certificate: "{{ lookup('file', 'tests/data/x509/server/ldap-server_ldap.cert.pem') }}"
 
ldap_server_tls_key: "{{ lookup('file', 'tests/data/x509/server/ldap-server_ldap.key.pem') }}"
 

	
 
# common
 
ca_certificates:
 
  testca: "{{ lookup('file', 'tests/data/x509/ca/level1.cert.pem') }}"
 

	
 
# ldap_client
 
ldap_client_config:
 
  - comment: CA truststore
 
    option: TLS_CACERT
 
    value: /etc/ssl/certs/testca.cert.pem
 
  - comment: Ensure TLS is enforced
 
    option: TLS_REQCERT
 
    value: demand
 
  - comment: Base DN
 
    option: BASE
 
    value: dc=local
 
  - comment: URI
 
    option: URI
 
    value: ldapi:///
 

	
 
# backup_server role.
 
backup_host_ssh_private_keys:
 
  rsa: "{{ lookup('file', 'tests/data/ssh/server_rsa') }}"
 
  ed25519: "{{ lookup('file', 'tests/data/ssh/server_ed25519') }}"
 
  ecdsa: "{{ lookup('file', 'tests/data/ssh/server_ecdsa') }}"
 
backup_clients:
 
  - server: parameters-optional-bullseye
 
    ip: 192.168.56.52
 
    public_key: "{{ lookup('file', 'tests/data/ssh/parameters-optional.pub') }}"
 
  - server: parameters-optional-bookworm
 
    ip: 192.168.56.32
 
    public_key: "{{ lookup('file', 'tests/data/ssh/parameters-optional.pub') }}"
roles/xmpp_server/molecule/default/molecule.yml
Show inline comments
 
---
 

	
 
dependency: {}
 

	
 
driver:
 
  name: vagrant
 
  provider:
 
    name: virtualbox
 

	
 
lint:
 
  name: yamllint
 
  options:
 
    config-file: ../../.yamllint.yml
 

	
 
platforms:
 

	
 

	
 
  # Helpers
 
  # =======
 

	
 
  - name: ldap-server
 
    box: debian/bookworm64
 
    memory: 512
 
    cpus: 1
 
    provider_raw_config_args:
 
      - "customize ['modifyvm', :id, '--paravirtprovider', 'minimal']"
 
    interfaces:
 
      - auto_config: true
 
        ip: 192.168.56.11
 
        network_name: private_network
 
        type: static
 

	
 

	
 
  # Debian 11 Bullseye
 
  # ================
 

	
 
  - name: client-bullseye
 
    groups:
 
      - clients
 
      - bullseye
 
    # Use Bookworm client box for testing Bullseye servers to avoid
 
    # duplication of test code in test_client.py due to missing
 
    # functional build of go-sendxmpp for the Bullseye release (glibc
 
    # mismatch in prebuilt package).
 
    box: debian/bookworm64
 
    memory: 256
 
    cpus: 1
 
    provider_raw_config_args:
 
      - "customize ['modifyvm', :id, '--paravirtprovider', 'minimal']"
 
    interfaces:
 
      - auto_config: true
 
        ip: 192.168.56.41
 
        network_name: private_network
 
        type: static
 

	
 
  - name: parameters-mandatory-bullseye
 
    groups:
 
      - parameters-mandatory
 
      - bullseye
 
    box: debian/bullseye64
 
    memory: 512
 
    cpus: 1
 
    provider_raw_config_args:
 
      - "customize ['modifyvm', :id, '--paravirtprovider', 'minimal']"
 
    interfaces:
 
      - auto_config: true
 
        ip: 192.168.56.51
 
        network_name: private_network
 
        type: static
 

	
 
  - name: parameters-optional-bullseye
 
    groups:
 
      - parameters-optional
 
      - bullseye
 
    box: debian/bullseye64
 
    memory: 512
 
    cpus: 1
 
    provider_raw_config_args:
 
      - "customize ['modifyvm', :id, '--paravirtprovider', 'minimal']"
 
    interfaces:
 
      - auto_config: true
 
        ip: 192.168.56.52
 
        network_name: private_network
 
        type: static
 

	
 

	
 
  # Debian 11 Bookworm
 
  # ==================
 

	
 
  - name: client-bookworm
 
    groups:
 
      - clients
 
      - bookworm
 
    box: debian/bookworm64
 
    memory: 256
 
    cpus: 1
 
    provider_raw_config_args:
 
      - "customize ['modifyvm', :id, '--paravirtprovider', 'minimal']"
 
    interfaces:
 
      - auto_config: true
 
        ip: 192.168.56.21
 
        network_name: private_network
 
        type: static
 

	
 
  - name: parameters-mandatory-bookworm
 
    groups:
 
      - parameters-mandatory
 
      - bookworm
 
    box: debian/bookworm64
 
    memory: 512
 
    cpus: 1
 
    provider_raw_config_args:
 
      - "customize ['modifyvm', :id, '--paravirtprovider', 'minimal']"
 
    interfaces:
 
      - auto_config: true
 
        ip: 192.168.56.31
 
        network_name: private_network
 
        type: static
 

	
 
  - name: parameters-optional-bookworm
 
    groups:
 
      - parameters-optional
 
      - bookworm
 
    box: debian/bookworm64
 
    memory: 512
 
    cpus: 1
 
    provider_raw_config_args:
 
      - "customize ['modifyvm', :id, '--paravirtprovider', 'minimal']"
 
    interfaces:
 
      - auto_config: true
 
        ip: 192.168.56.32
 
        network_name: private_network
 
        type: static
 

	
 

	
 
provisioner:
 
  name: ansible
 
  playbooks:
 
    cleanup: cleanup.yml
 
  config_options:
 
    defaults:
 
      force_valid_group_names: "ignore"
 
      interpreter_python: "/usr/bin/python3"
 
    ssh_connection:
 
      pipelining: "True"
 
  lint:
 
    name: ansible-lint
 

	
 
scenario:
 
  name: default
 

	
 
verifier:
 
  name: testinfra
 
  lint:
 
    name: flake8
roles/xmpp_server/molecule/default/prepare.yml
Show inline comments
 
---
 

	
 
- name: Set-up fixtures
 
  hosts: localhost
 
  connection: local
 
  gather_facts: false
 
  tasks:
 

	
 
    - name: Initialise CA hierarchy
 
      command: "gimmecert init"
 
      args:
 
        creates: ".gimmecert/ca/level1.cert.pem"
 
        chdir: "tests/data/"
 

	
 
    - name: Generate server private keys and certificates
 
      command:
 
      args:
 
        chdir: "tests/data/"
 
        creates: ".gimmecert/server/{{ item.name }}.cert.pem"
 
        argv: "{{ ['gimmecert', 'server', item.name] + item.fqdn }}"
 
      with_items:
 
        - name: ldap-server_ldap
 
          fqdn:
 
            - ldap-server
 

	
 
        - name: parameters-mandatory-bullseye_xmpp
 
          fqdn:
 
            - parameters-mandatory
 
            - domain1
 
            - proxy.domain1
 
            - conference.domain1
 
        - name: parameters-optional-bullseye_xmpp
 
          fqdn:
 
            - parameters-optional
 
            - domain2
 
            - proxy.domain2
 
            - conference.domain2
 
            - domain3
 
            - proxy.domain3
 
            - conference.domain3
 
        - name: parameters-mandatory-bookworm_xmpp
 
          fqdn:
 
            - parameters-mandatory
 
            - domain1
 
            - proxy.domain1
 
            - conference.domain1
 
        - name: parameters-optional-bookworm_xmpp
 
          fqdn:
 
            - parameters-optional
 
            - domain2
 
            - proxy.domain2
 
            - conference.domain2
 
            - domain3
 
            - proxy.domain3
 
            - conference.domain3
 

	
 
    - name: Set-up link to generated X.509 material
 
      file:
 
        src: ".gimmecert"
 
        dest: "tests/data/x509"
 
        state: link
 

	
 
- name: Prepare
 
  hosts: all
 
  gather_facts: false
 
  tasks:
 
    - name: Install python for Ansible
 
      raw: test -e /usr/bin/python3 || (apt -y update && apt install -y python3-minimal)
 
      become: true
 
      changed_when: false
 

	
 
- hosts: all
 
  become: true
 
  tasks:
 

	
 
    - name: Update all caches to avoid errors due to missing remote archives
 
      apt:
 
        update_cache: true
 
      changed_when: false
 

	
 
    - name: Install tools for testing
 
      apt:
 
        name:
 
          - gnutls-bin
 
          - nmap
 
        state: present
 

	
 
    - name: Use name provided via CLI when running STARTTLS handshake for XMPP via nmap
 
      replace:
 
        path: "/usr/share/nmap/nselib/sslcert.lua"
 
        regexp: "host\\.name\\)"
 
        replace: "host.targetname)"
 

	
 
- hosts: bullseye
 
  become: true
 
  tasks:
 

	
 
    - name: Enable TLSv1.0+ in global OpenSSL configuration file in order to be able to test the xmpp_server_tls_protocol parameter
 
      lineinfile:
 
        path: "/etc/ssl/openssl.cnf"
 
        regexp: "^MinProtocol ="
 
        line: "MinProtocol = TLSv1.0"
 
        owner: root
 
        group: root
 
        mode: 0644
 
        state: present
 

	
 
    - name: Set-up the hosts file
 
      lineinfile:
 
        path: /etc/hosts
 
        regexp: "^{{ item.key }}"
 
        line: "{{ item.key }} {{ item.value }}"
 
        owner: root
 
        group: root
 
        mode: 0644
 
        state: present
 
      with_dict:
 
        192.168.56.11: "ldap-server backup-server"
 
        192.168.56.41: "client-bullseye"
 
        192.168.56.51: "parameters-mandatory domain1 proxy.domain1 conference.domain1"
 
        192.168.56.52: "parameters-optional domain2 proxy.domain2 conference.domain2 domain3 proxy.domain3 conference.domain3"
 

	
 
- hosts: bookworm
 
  become: true
 
  tasks:
 

	
 
    - name: Enable TLSv1.0+ in global OpenSSL configuration file in order to be able to test the web_server_tls_protocols parameter
 
      blockinfile:
 
        path: "/etc/ssl/openssl.cnf"
 
        block: |
 
          [openssl_init]
 
          ssl_conf = ssl_sect
 

	
 
          [ssl_sect]
 
          system_default = system_default_sect
 

	
 
          [system_default_sect]
 
          MinProtocol = TLSv1.1
 
          CipherString = DEFAULT@SECLEVEL=0
 
        owner: root
 
        group: root
 
        mode: 0644
 
        state: present
 

	
 
    - name: Set-up the hosts file
 
      lineinfile:
 
        path: /etc/hosts
 
        regexp: "^{{ item.key }}"
 
        line: "{{ item.key }} {{ item.value }}"
 
        owner: root
 
        group: root
 
        mode: 0644
 
        state: present
 
      with_dict:
 
        192.168.56.11: "ldap-server backup-server"
 
        192.168.56.21: "client-bookworm"
 
        192.168.56.31: "parameters-mandatory domain1 proxy.domain1 conference.domain1"
 
        192.168.56.32: "parameters-optional domain2 proxy.domain2 conference.domain2 domain3 proxy.domain3 conference.domain3"
 

	
 
- hosts: clients
 
  become: true
 
  tasks:
 

	
 
    - name: Install tool for testing TCP connectivity
 
      apt:
 
        name: hping3
 
        state: present
 

	
 
    - name: Deploy CA certificate
 
      copy:
 
        src: tests/data/x509/ca/level1.cert.pem
 
        dest: /usr/local/share/ca-certificates/testca.crt
 
        owner: root
 
        group: root
 
        mode: 0644
 
      notify:
 
        - Update CA certificate cache
 

	
 
    - name: Install console-based XMPP client (for interactive testing)
 
      apt:
 
        name: mcabber
 
        state: present
 

	
 
    - name: Install console-based XMPP tool (for non-interactive testing)
 
      apt:
 
        name: go-sendxmpp
 
        state: present
 

	
 
    - name: Create dedicated group for testing
 
      group:
 
        name: user
 
        state: present
 

	
 
    - name: Create dedicated user for testing
 
      user:
 
        name: user
 
        group: user
 
        shell: /bin/bash
 

	
 
    - name: Deploy mcabber configuration files
 
      template:
 
        src: tests/data/mcabber.cfg.j2
 
        dest: "~user/{{ item.jid }}.cfg"
 
        owner: user
 
        group: user
 
        mode: 0600
 
      with_items:
 
        - jid: john.doe@domain1
 
          password: johnpassword
 
          server: domain1
 
          security: tls
 
          nickname: john.doe
 
        - jid: jane.doe@domain2
 
          password: janepassword
 
          server: domain2
 
          security: ssl
 
          nickname: jane.doe
 
        - jid: mick.doe@domain3
 
          password: mickpassword
 
          server: domain3
 
          security: tls
 
          nickname: mick.doe
 
        - jid: noxmpp@domain1
 
          password: noxmpppassword
 
          server: domain1
 
          security: tls
 
          nickname: noxmpp
 

	
 
  handlers:
 

	
 
    - name: Update CA certificate cache
 
      command: /usr/sbin/update-ca-certificates --fresh
 

	
 
- hosts: ldap-server
 
  become: true
 
  roles:
 
    - ldap_server
 
    - backup_server
 

	
 
- hosts: ldap-server
 
  become: true
 
  tasks:
 

	
 
    - name: Create LDAP accounts for testing
 
      ldap_entry:
 
        dn: "{{ item.dn }}"
 
        objectClass: "{{ item.objectClass }}"
 
        attributes: "{{ item.attributes }}"
 
      with_items:
 
        - dn: uid=john,ou=people,dc=local
 
          objectClass:
 
            - inetOrgPerson
 
            - simpleSecurityObject
 
          attributes:
 
            userPassword: johnpassword
 
            uid: john
 
            cn: John Doe
 
            sn: Doe
 
            mail: john.doe@domain1
 

	
 
        - dn: uid=jane,ou=people,dc=local
 
          objectClass:
 
            - inetOrgPerson
 
            - simpleSecurityObject
 
          attributes:
 
            userPassword: janepassword
 
            uid: jane
 
            cn: Jane Doe
 
            sn: Doe
 
            mail: jane.doe@domain2
 

	
 
        - dn: uid=mick,ou=people,dc=local
 
          objectClass:
 
            - inetOrgPerson
 
            - simpleSecurityObject
 
          attributes:
 
            userPassword: mickpassword
 
            uid: mick
 
            cn: Mick Doe
 
            sn: Doe
 
            mail: mick.doe@domain3
 

	
 
        - dn: uid=noxmpp,ou=people,dc=local
 
          objectClass:
 
            - inetOrgPerson
 
            - simpleSecurityObject
 
          attributes:
 
            userPassword: noxmpppassword
 
            uid: noxmpp
 
            cn: No XMPP
 
            sn: XMPP
 
            mail: noxmpp@domain1
 

	
 
    - name: Add test accounts to correct group
 
      ldap_attr:
 
        dn: "cn=xmpp,ou=groups,dc=local"
 
        name: uniqueMember
 
        state: exact
 
        values:
 
          - uid=john,ou=people,dc=local
 
          - uid=jane,ou=people,dc=local
 
          - uid=mick,ou=people,dc=local
 
          - uid=eve,ou=people,dc=local
 

	
 
- hosts: parameters-mandatory,parameters-optional
 
  become: true
 
  tasks:
 

	
 
    - name: Install console-based XMPP tool (for non-interactive testing)
 
      apt:
 
        name: "{{ sendxmpp_package }}"
 
        state: present
 
      vars:
 
        sendxmpp_package: "{% if ansible_distribution_release == 'bullseye' %}sendxmpp{% else %}go-sendxmpp{% endif %}"
 
        sendxmpp_package: "go-sendxmpp"
 

	
 
    - name: Deploy small Lua script for listing the enabled modules in Prosody
 
      copy:
 
        src: list_prosody_modules.lua
 
        dest: "/usr/local/bin/list_prosody_modules.lua"
 
        owner: root
 
        group: root
 
        mode: 0755
roles/xmpp_server/molecule/default/tests/test_backup.py
Show inline comments
 
import os
 
import uuid
 

	
 
import testinfra.utils.ansible_runner
 

	
 

	
 
testinfra_hosts = testinfra.utils.ansible_runner.AnsibleRunner(
 
    os.environ['MOLECULE_INVENTORY_FILE']).get_hosts('parameters-optional')
 

	
 

	
 
def test_backup(host):
 
    """
 
    Tests if Prosody data directory is correctly backed-up.
 
    """
 

	
 
    distribution_release = host.ansible("setup")["ansible_facts"]["ansible_distribution_release"]
 

	
 
    # Deliver a couple of messages in order to make sure the directory structure
 
    # is created.
 
    message = str(uuid.uuid1())
 

	
 
    if distribution_release == "bullseye":
 
        send = host.run("echo '%s' | sendxmpp --tls-ca-path /usr/local/share/ca-certificates/testca.crt "
 
                        "-t -u jane.doe -p janepassword -j domain2:5222 mick.doe@domain3", message)
 
        assert send.rc == 0
 
    else:
 
        send = host.run("echo '%s' | go-sendxmpp --debug "
 
                        "--username jane.doe@domain2 --password janepassword --jserver domain3:5222 "
 
                        "mick.doe@domain3", message)
 
        assert send.rc == 0
 
    send = host.run("echo '%s' | go-sendxmpp --debug "
 
                    "--username jane.doe@domain2 --password janepassword --jserver domain3:5222 "
 
                    "mick.doe@domain3", message)
 
    assert send.rc == 0
 

	
 
    with host.sudo():
 

	
 
        # Remove restore directory in order to make sure restore has worked
 
        # correctly.
 
        host.run("rm -rf /root/restore")
 

	
 
        backup_run = host.run('duply main backup')
 
        assert backup_run.rc == 0
 

	
 
        restore_run = host.run('duply main restore /root/restore')
 
        assert restore_run.rc == 0
 

	
 
        directory = host.file("/root/restore/var/lib/prosody/domain3")
 
        assert directory.is_directory
 
        assert directory.user == "prosody"
 
        assert directory.group == "prosody"
 
        assert directory.mode == 0o750
 

	
 
        directory = host.file("/root/restore/var/lib/prosody/domain3/offline")
 
        assert directory.is_directory
 
        assert directory.user == "prosody"
 
        assert directory.group == "prosody"
 
        assert directory.mode == 0o750
 

	
 
        offline_messages = host.file("/root/restore/var/lib/prosody/domain3/offline/mick%2edoe.list")
 
        assert offline_messages.is_file
 
        assert offline_messages.user == 'prosody'
 
        assert offline_messages.group == 'prosody'
 
        assert offline_messages.mode == 0o640
 
        assert message in offline_messages.content_string
roles/xmpp_server/molecule/default/tests/test_default_bullseye.py
Show inline comments
 
deleted file
roles/xmpp_server/molecule/default/tests/test_mandatory.py
Show inline comments
 
import os
 

	
 
import defusedxml.ElementTree as ElementTree
 

	
 
import pytest
 

	
 
import testinfra.utils.ansible_runner
 

	
 

	
 
testinfra_hosts = testinfra.utils.ansible_runner.AnsibleRunner(
 
    os.environ['MOLECULE_INVENTORY_FILE']).get_hosts('parameters-mandatory')
 

	
 

	
 
def test_prosody_configuration_file_content(host):
 
    """
 
    Tests if Prosody configuration file has correct content.
 
    """
 

	
 
    hostname = host.run('hostname').stdout.strip()
 

	
 
    with host.sudo():
 

	
 
        config = host.file('/etc/prosody/prosody.cfg.lua')
 

	
 
        assert "admins = { \"john.doe@domain1\",  }" in config.content_string
 
        assert "key = \"/etc/ssl/private/%s_xmpp.key\";" % hostname in config.content_string
 
        assert "certificate = \"/etc/ssl/certs/%s_xmpp.pem\";" % hostname in config.content_string
 
        assert "ldap_server = \"ldap-server\"" in config.content_string
 
        assert "ldap_rootdn = \"cn=prosody,ou=services,dc=local\"" in config.content_string
 
        assert "ldap_password = \"prosodypassword\"" in config.content_string
 
        assert "ldap_filter = \"(&(mail=$user@$host)(memberOf=cn=xmpp,ou=groups,dc=local))\"" in config.content_string
 
        assert "ldap_base = \"ou=people,dc=local\"" in config.content_string
 
        assert "archive_expires_after = \"never\"" in config.content_string
 

	
 
        assert """VirtualHost "domain1"
 
Component "conference.domain1" "muc"
 
  restrict_room_creation = "local"
 
Component "proxy.domain1" "proxy65"
 
  proxy65_acl = { "domain1" }""" in config.content_string
 

	
 

	
 
@pytest.mark.parametrize("port", [
 
    5222,
 
    5223
 
])
 
def test_xmpp_c2s_tls_version_and_ciphers(host, port):
 
    """
 
    Tests if the correct TLS version and ciphers have been enabled for
 
    XMPP C2S ports.
 
    """
 

	
 
    distribution_release = host.ansible("setup")["ansible_facts"]["ansible_distribution_release"]
 

	
 
    if distribution_release == "bullseye":
 
        expected_tls_versions = ["TLSv1.2"]
 
        expected_tls_ciphers = [
 
            "TLS_DHE_RSA_WITH_AES_128_GCM_SHA256",
 
            "TLS_DHE_RSA_WITH_AES_256_GCM_SHA384",
 
            "TLS_DHE_RSA_WITH_CHACHA20_POLY1305_SHA256",
 
            "TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256",
 
            "TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384",
 
            "TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256",
 
        ]
 
    else:
 
        expected_tls_versions = ["TLSv1.2", "TLSv1.3"]
 
        expected_tls_ciphers = [
 
            "TLS_AKE_WITH_AES_128_GCM_SHA256",
 
            "TLS_AKE_WITH_AES_256_GCM_SHA384",
 
            "TLS_AKE_WITH_CHACHA20_POLY1305_SHA256",
 
            "TLS_DHE_RSA_WITH_AES_128_GCM_SHA256",
 
            "TLS_DHE_RSA_WITH_AES_256_GCM_SHA384",
 
            "TLS_DHE_RSA_WITH_CHACHA20_POLY1305_SHA256",
 
            "TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256",
 
            "TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384",
 
            "TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256",
 
        ]
 
    expected_tls_versions = ["TLSv1.2", "TLSv1.3"]
 
    expected_tls_ciphers = [
 
        "TLS_AKE_WITH_AES_128_GCM_SHA256",
 
        "TLS_AKE_WITH_AES_256_GCM_SHA384",
 
        "TLS_AKE_WITH_CHACHA20_POLY1305_SHA256",
 
        "TLS_DHE_RSA_WITH_AES_128_GCM_SHA256",
 
        "TLS_DHE_RSA_WITH_AES_256_GCM_SHA384",
 
        "TLS_DHE_RSA_WITH_CHACHA20_POLY1305_SHA256",
 
        "TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256",
 
        "TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384",
 
        "TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256",
 
    ]
 

	
 
    # Run the nmap scanner against the server, and fetch the results.
 
    nmap = host.run("nmap -sV --script ssl-enum-ciphers -p %s domain1 -oX /tmp/report.xml", str(port))
 
    assert nmap.rc == 0
 
    report_content = host.file('/tmp/report.xml').content_string
 

	
 
    report_root = ElementTree.fromstring(report_content)
 

	
 
    tls_versions = []
 
    tls_ciphers = set()
 

	
 
    for child in report_root.findall("./host/ports/port/script[@id='ssl-enum-ciphers']/table"):
 
        tls_versions.append(child.attrib['key'])
 

	
 
    for child in report_root.findall(".//table[@key='ciphers']/table/elem[@key='name']"):
 
        tls_ciphers.add(child.text)
 

	
 
    tls_versions.sort()
 
    tls_ciphers = sorted(list(tls_ciphers))
 

	
 
    assert tls_versions == expected_tls_versions
 
    assert tls_ciphers == expected_tls_ciphers
roles/xmpp_server/molecule/default/tests/test_optional.py
Show inline comments
 
import os
 

	
 
import defusedxml.ElementTree as ElementTree
 

	
 
import pytest
 

	
 
import testinfra.utils.ansible_runner
 

	
 

	
 
testinfra_hosts = testinfra.utils.ansible_runner.AnsibleRunner(
 
    os.environ['MOLECULE_INVENTORY_FILE']).get_hosts('parameters-optional')
 

	
 

	
 
def test_prosody_configuration_file_content(host):
 
    """
 
    Tests if Prosody configuration file has correct content.
 
    """
 

	
 
    hostname = host.run('hostname').stdout.strip()
 

	
 
    with host.sudo():
 

	
 
        config = host.file('/etc/prosody/prosody.cfg.lua')
 

	
 
        assert "admins = { \"jane.doe@domain2\", \"mick.doe@domain3\",  }" in config.content_string
 
        assert "key = \"/etc/ssl/private/%s_xmpp.key\";" % hostname in config.content_string
 
        assert "certificate = \"/etc/ssl/certs/%s_xmpp.pem\";" % hostname in config.content_string
 
        assert "ldap_server = \"ldap-server\"" in config.content_string
 
        assert "ldap_rootdn = \"cn=prosody,ou=services,dc=local\"" in config.content_string
 
        assert "ldap_password = \"prosodypassword\"" in config.content_string
 
        assert "ldap_filter = \"(&(mail=$user@$host)(memberOf=cn=xmpp,ou=groups,dc=local))\"" in config.content_string
 
        assert "ldap_base = \"ou=people,dc=local\"" in config.content_string
 
        assert "archive_expires_after = \"1w\"" in config.content_string
 

	
 
        assert """VirtualHost "domain2"
 
Component "conference.domain2" "muc"
 
  restrict_room_creation = "local"
 
Component "proxy.domain2" "proxy65"
 
  proxy65_acl = { "domain2" }""" in config.content_string
 

	
 
        assert """VirtualHost "domain3"
 
Component "conference.domain3" "muc"
 
  restrict_room_creation = "local"
 
Component "proxy.domain3" "proxy65"
 
  proxy65_acl = { "domain3" }""" in config.content_string
 

	
 

	
 
@pytest.mark.parametrize("port", [
 
    5222,
 
    5223
 
])
 
def test_xmpp_c2s_tls_version_and_ciphers(host, port):
 
    """
 
    Tests if the correct TLS version and ciphers have been enabled for
 
    XMPP C2S ports.
 
    """
 

	
 
    distribution_release = host.ansible("setup")["ansible_facts"]["ansible_distribution_release"]
 

	
 
    if distribution_release == "bullseye":
 
        expected_tls_versions = ["TLSv1.0", "TLSv1.1", "TLSv1.2"]
 
        expected_tls_ciphers = [
 
            "TLS_DHE_RSA_WITH_AES_128_CBC_SHA256",
 
            "TLS_DHE_RSA_WITH_AES_128_GCM_SHA256",
 
            "TLS_DHE_RSA_WITH_AES_256_CBC_SHA256",
 
            "TLS_DHE_RSA_WITH_AES_256_GCM_SHA384",
 
            "TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA",
 
            "TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256",
 
            "TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256",
 
            "TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384",
 
            "TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384",
 
        ]
 
    else:
 
        expected_tls_versions = ["TLSv1.0", "TLSv1.1", "TLSv1.2", "TLSv1.3"]
 
        expected_tls_ciphers = [
 
            "TLS_AKE_WITH_AES_128_GCM_SHA256",
 
            "TLS_AKE_WITH_AES_256_GCM_SHA384",
 
            "TLS_AKE_WITH_CHACHA20_POLY1305_SHA256",
 
            "TLS_DHE_RSA_WITH_AES_128_CBC_SHA256",
 
            "TLS_DHE_RSA_WITH_AES_128_GCM_SHA256",
 
            "TLS_DHE_RSA_WITH_AES_256_CBC_SHA256",
 
            "TLS_DHE_RSA_WITH_AES_256_GCM_SHA384",
 
            "TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA",
 
            "TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256",
 
            "TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256",
 
            "TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384",
 
            "TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384",
 
        ]
 
    expected_tls_versions = ["TLSv1.0", "TLSv1.1", "TLSv1.2", "TLSv1.3"]
 
    expected_tls_ciphers = [
 
        "TLS_AKE_WITH_AES_128_GCM_SHA256",
 
        "TLS_AKE_WITH_AES_256_GCM_SHA384",
 
        "TLS_AKE_WITH_CHACHA20_POLY1305_SHA256",
 
        "TLS_DHE_RSA_WITH_AES_128_CBC_SHA256",
 
        "TLS_DHE_RSA_WITH_AES_128_GCM_SHA256",
 
        "TLS_DHE_RSA_WITH_AES_256_CBC_SHA256",
 
        "TLS_DHE_RSA_WITH_AES_256_GCM_SHA384",
 
        "TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA",
 
        "TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256",
 
        "TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256",
 
        "TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384",
 
        "TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384",
 
    ]
 

	
 
    # Run the nmap scanner against the server, and fetch the results.
 
    nmap = host.run("nmap -sV --script ssl-enum-ciphers -p %s domain2 -oX /tmp/report.xml", str(port))
 
    assert nmap.rc == 0
 
    report_content = host.file('/tmp/report.xml').content_string
 

	
 
    report_root = ElementTree.fromstring(report_content)
 

	
 
    tls_versions = []
 
    tls_ciphers = set()
 

	
 
    for child in report_root.findall("./host/ports/port/script[@id='ssl-enum-ciphers']/table"):
 
        tls_versions.append(child.attrib['key'])
 

	
 
    for child in report_root.findall(".//table[@key='ciphers']/table/elem[@key='name']"):
 
        tls_ciphers.add(child.text)
 

	
 
    tls_versions.sort()
 
    tls_ciphers = sorted(list(tls_ciphers))
 

	
 
    assert tls_versions == expected_tls_versions
 
    assert tls_ciphers == expected_tls_ciphers
roles/xmpp_server/tasks/main.yml
Show inline comments
 
---
 

	
 
# Main implementation
 
# ===================
 

	
 
- name: Set-up the Debian backports repository
 
  template:
 
    src: backports.list.j2
 
    dest: /etc/apt/sources.list.d/backports.list
 
    owner: root
 
    group: root
 
    mode: 0644
 
  register: backports_repository_configuration
 

	
 
- name: Update apt cache if backports repository configuration changed (for immediate use)  # noqa 503
 
  # [503] Tasks that run when changed should likely be handlers
 
  #   Since apt_repository module is not reliable (does not deploy
 
  #   change when changing distro version etc), we have to use
 
  #   template instead, but this also means we need to trigger the apt
 
  #   cache reload by hand.
 
  apt:
 
    update_cache: true
 
  when: backports_repository_configuration.changed
 

	
 
- name: Drop package pins to backports for Prosody on Debian 11 Bullseye
 
  file:
 
    path: /etc/apt/preferences.d/prosody
 
    state: absent
 
  when: ansible_distribution_release == 'bullseye'
 

	
 
- name: Install additional Prosody dependencies
 
  apt:
 
    name:
 
      - lua-ldap
 
      - prosody-modules
 
    state: present
 
  notify:
 
    - Restart Prosody
 

	
 
- name: Install Prosody
 
  apt:
 
    name: prosody
 
    state: present
 
  notify:
 
    - Restart Prosody
 

	
 
- name: Allow Prosody user to traverse the directory with TLS private keys
 
  user:
 
    name: prosody
 
    append: true
 
    groups: ssl-cert
 

	
 
- name: Deploy XMPP TLS private key
 
  copy:
 
    dest: "/etc/ssl/private/{{ ansible_fqdn }}_xmpp.key"
 
    content: "{{ xmpp_tls_key }}"
 
    owner: root
 
    group: prosody
 
    mode: 0640
 
  notify:
 
    - Restart Prosody
 

	
 
- name: Deploy XMPP TLS certificate
 
  copy:
 
    dest: "/etc/ssl/certs/{{ ansible_fqdn }}_xmpp.pem"
 
    content: "{{ xmpp_tls_certificate }}"
 
    owner: root
 
    group: root
 
    mode: 0644
 
  notify:
 
    - Restart Prosody
 

	
 
- name: Generate the XMPP server Diffie-Hellman parameter
 
  openssl_dhparam:
 
    owner: root
 
    group: prosody
 
    mode: 0640
 
    path: "/etc/ssl/private/{{ ansible_fqdn }}_xmpp.dh.pem"
 
    size: 2048
 
  notify:
 
    - Restart Prosody
 

	
 
- name: Deploy configuration file for checking certificate validity via cron
 
  copy:
 
    content: "/etc/ssl/certs/{{ ansible_fqdn }}_xmpp.pem"
 
    dest: "/etc/check_certificate/{{ ansible_fqdn }}_xmpp.conf"
 
    owner: root
 
    group: root
 
    mode: 0644
 

	
 
- name: Deploy script for validating Prosody certificate
 
  copy:
 
    src: "check_prosody_certificate.sh"
 
    dest: "/usr/local/bin/check_prosody_certificate.sh"
 
    owner: root
 
    group: root
 
    mode: 0755
 

	
 
- name: Set-up crontab task that runs the Prosody certificate checker script once a day
 
  copy:
 
    src: "cron_check_prosody_certificate"
 
    dest: "/etc/cron.d/check_prosody_certificate"
 
    owner: root
 
    group: root
 
    mode: 0644
 

	
 
- name: Deploy LDAP client configuration (for validating LDAP server certificate)
 
  copy:
 
    src: prosody_ldaprc
 
    dest: "/var/lib/prosody/.ldaprc"
 
    owner: root
 
    group: prosody
 
    mode: 0640
 
  notify:
 
    - Restart Prosody
 

	
 
- name: Deploy Prosody configuration file
 
  template:
 
    src: "prosody.cfg.lua.j2"
 
    dest: "/etc/prosody/prosody.cfg.lua"
 
    owner: root
 
    group: prosody
 
    mode: 0640
 
  notify:
 
    - Restart Prosody
 

	
roles/xmpp_server/templates/prosody.cfg.lua.j2
Show inline comments
 
-- List of server administrators.
 
admins = { {% for admin in xmpp_administrators %}"{{ admin }}", {% endfor %} }
 

	
 
-- List of modules to load on startup.
 
modules_enabled = {
 

	
 
  -- Generally required
 
    "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
 
    "posix"; -- POSIX functionality, sends server to background, enables syslog, etc.
 

	
 
  -- Not essential, but recommended
 
    "private"; -- Private XML storage (for room bookmarks, etc.)
 
    "blocklist"; -- Allow users to block communications with other users
 
    "vcard"; -- Allow users to set vCards
 
    "carbons"; -- Keep multiple clients in sync
 

	
 
  -- Nice to have
 
    "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
 
    "pep"; -- Enables users to publish their mood, activity, playing music and more
 
    "register"; -- Allow users to register on this server using a client and change passwords
 
    "mam"; -- Store messages in an archive and allow users to access it
 

	
 
  -- Admin interfaces
 
    "admin_adhoc"; -- Allows administration via an XMPP client that supports ad-hoc commands
 

	
 
  -- Other specific functionality
 
    "announce"; -- Send announcement to all online users
 
    "legacyauth"; -- Allow legacy authentication and SSL
 
};
 

	
 
-- Disable account creation by default, for security
 
-- For more information see http://prosody.im/doc/creating_accounts
 
allow_registration = false;
 

	
 
-- Set global settings for SSL/TLS.
 
ssl = {
 
  key = "/etc/ssl/private/{{ ansible_fqdn }}_xmpp.key";
 
  certificate = "/etc/ssl/certs/{{ ansible_fqdn }}_xmpp.pem";
 
  dhparam = "/etc/ssl/private/{{ ansible_fqdn }}_xmpp.dh.pem";
 
}
 

	
 
-- Configure TLS protocol and ciphers for client-to-server
 
-- connections (STARTTLS).
 
c2s_ssl = {
 
  protocol = "{{ xmpp_server_tls_protocol }}";
 
  ciphers = "{{ xmpp_server_tls_ciphers }}";
 
}
 

	
 
-- Configure TLS protocol and ciphers for client-to-server
 
-- connections (direct TLS).
 
{% if ansible_distribution_release == "bullseye" %}
 
legacy_ssl_ssl = {
 
  protocol = "{{ xmpp_server_tls_protocol }}";
 
  ciphers = "{{ xmpp_server_tls_ciphers }}";
 
}
 
{% else %}
 
c2s_direct_tls_ssl = {
 
  protocol = "{{ xmpp_server_tls_protocol }}";
 
  ciphers = "{{ xmpp_server_tls_ciphers }}";
 
  -- @WORKAROUND: No DHE ciphers because dhparam is getting reset
 
  --
 
  --    There is a bug in Prosody 0.12.3 resulting in dhparam value
 
  --    from from global config getting ignored when domain SNI
 
  --    context is initalised on TCP port 5223. Define the parameter
 
  --    in within this configuration context as well to fix the issue.
 
  dhparam = "/etc/ssl/private/{{ ansible_fqdn }}_xmpp.dh.pem";
 
}
 
{% endif %}
 

	
 
-- Ports on which to have direct TLS/SSL.
 
{% if ansible_distribution_release == "bullseye" %}
 
legacy_ssl_ports = { 5223 }
 
{% else %}
 
c2s_direct_tls_ports = { 5223 }
 
{% endif %}
 

	
 
-- Force clients to use encrypted connection.
 
c2s_require_encryption = true
 

	
 
-- Disable certificate validation for server-to-server connections.
 
s2s_secure_auth = false
 

	
 
-- Path to Prosody's PID file.
 
pidfile = "/run/prosody/prosody.pid"
 

	
 
-- Authentication backend.
 
authentication = "ldap"
 
ldap_server = "{{ xmpp_ldap_server }}"
 
ldap_rootdn = "cn=prosody,ou=services,{{ xmpp_ldap_base_dn }}"
 
ldap_password = "{{ xmpp_ldap_password }}"
 
ldap_filter = "(&(mail=$user@$host)(memberOf=cn=xmpp,ou=groups,{{xmpp_ldap_base_dn}}))"
 
ldap_scope = "onelevel"
 
ldap_tls = true
 
ldap_base = "ou=people,{{ xmpp_ldap_base_dn }}"
 

	
 
-- Message Archives (mod_mam) configuration.
 
archive_expires_after = "{{ xmpp_server_archive_expiration }}"
 

	
 
-- Storage backend.
 
storage = "internal"
 

	
 
-- Logging configuration.
 
log = {
 
  info = "/var/log/prosody/prosody.log"; -- Change 'info' to 'debug' for verbose logging
 
  error = "/var/log/prosody/prosody.err";
 
  "*syslog";
 
}
 

	
 
-- Domains which should be handled by Prosody, with dedicated MUC and file
 
-- proxying components.
 
{% for domain in xmpp_domains -%}
 
VirtualHost "{{ domain }}"
 
Component "conference.{{ domain }}" "muc"
 
  restrict_room_creation = "local"
 
Component "proxy.{{ domain }}" "proxy65"
 
  proxy65_acl = { "{{ domain }}" }
 
{% endfor -%}
0 comments (0 inline, 0 general)