Files @ a822861bea4e
Branch filter:

Location: majic-ansible-roles/roles/mail_server/molecule/default/tests/test_client2.py - annotation

branko
MAR-239: Dropped support for Debian 11 Bullseye from the mail_server role.
b0c92677ba93
17cf34f73ca6
17cf34f73ca6
774d8236fa12
774d8236fa12
277c561f3f52
277c561f3f52
17cf34f73ca6
277c561f3f52
d62b3adec462
277c561f3f52
277c561f3f52
d7f5980cc68a
277c561f3f52
277c561f3f52
277c561f3f52
277c561f3f52
277c561f3f52
277c561f3f52
d7f5980cc68a
277c561f3f52
277c561f3f52
277c561f3f52
d7f5980cc68a
277c561f3f52
277c561f3f52
277c561f3f52
d7f5980cc68a
1bb9f7ac1072
1bb9f7ac1072
1bb9f7ac1072
d7f5980cc68a
1bb9f7ac1072
1bb9f7ac1072
1bb9f7ac1072
277c561f3f52
d7f5980cc68a
277c561f3f52
277c561f3f52
277c561f3f52
277c561f3f52
277c561f3f52
277c561f3f52
277c561f3f52
277c561f3f52
d7f5980cc68a
277c561f3f52
277c561f3f52
277c561f3f52
d7f5980cc68a
277c561f3f52
277c561f3f52
277c561f3f52
d7f5980cc68a
277c561f3f52
277c561f3f52
277c561f3f52
d7f5980cc68a
277c561f3f52
277c561f3f52
277c561f3f52
277c561f3f52
d7f5980cc68a
277c561f3f52
277c561f3f52
277c561f3f52
d7f5980cc68a
277c561f3f52
277c561f3f52
277c561f3f52
d7f5980cc68a
277c561f3f52
277c561f3f52
277c561f3f52
d7f5980cc68a
277c561f3f52
277c561f3f52
277c561f3f52
17cf34f73ca6
d7f5980cc68a
17cf34f73ca6
17cf34f73ca6
17cf34f73ca6
d7f5980cc68a
17cf34f73ca6
17cf34f73ca6
17cf34f73ca6
277c561f3f52
d7f5980cc68a
277c561f3f52
277c561f3f52
277c561f3f52
d7f5980cc68a
277c561f3f52
277c561f3f52
277c561f3f52
d7f5980cc68a
277c561f3f52
277c561f3f52
277c561f3f52
d7f5980cc68a
277c561f3f52
277c561f3f52
277c561f3f52
277c561f3f52
d7f5980cc68a
277c561f3f52
277c561f3f52
277c561f3f52
d7f5980cc68a
277c561f3f52
277c561f3f52
277c561f3f52
d7f5980cc68a
277c561f3f52
277c561f3f52
277c561f3f52
d7f5980cc68a
277c561f3f52
277c561f3f52
277c561f3f52
277c561f3f52
d7f5980cc68a
277c561f3f52
277c561f3f52
277c561f3f52
277c561f3f52
277c561f3f52
ae1a499dbd62
ae1a499dbd62
17cf34f73ca6
17cf34f73ca6
17cf34f73ca6
ae1a499dbd62
ae1a499dbd62
ae1a499dbd62
ae1a499dbd62
ae1a499dbd62
ae1a499dbd62
ae1a499dbd62
ae1a499dbd62
ae1a499dbd62
ae1a499dbd62
ae1a499dbd62
ae1a499dbd62
ae1a499dbd62
ae1a499dbd62
ae1a499dbd62
ae1a499dbd62
ae1a499dbd62
ae1a499dbd62
ae1a499dbd62
277c561f3f52
277c561f3f52
277c561f3f52
277c561f3f52
d7f5980cc68a
277c561f3f52
277c561f3f52
277c561f3f52
277c561f3f52
277c561f3f52
277c561f3f52
ae1a499dbd62
ae1a499dbd62
17cf34f73ca6
17cf34f73ca6
17cf34f73ca6
ae1a499dbd62
ae1a499dbd62
277c561f3f52
17cf34f73ca6
277c561f3f52
277c561f3f52
d7f5980cc68a
277c561f3f52
277c561f3f52
277c561f3f52
277c561f3f52
277c561f3f52
277c561f3f52
ae1a499dbd62
ae1a499dbd62
17cf34f73ca6
17cf34f73ca6
17cf34f73ca6
ae1a499dbd62
ae1a499dbd62
17cf34f73ca6
17cf34f73ca6
17cf34f73ca6
ae1a499dbd62
ae1a499dbd62
277c561f3f52
17cf34f73ca6
277c561f3f52
ae1a499dbd62
ae1a499dbd62
277c561f3f52
17cf34f73ca6
17cf34f73ca6
17cf34f73ca6
d7f5980cc68a
17cf34f73ca6
17cf34f73ca6
17cf34f73ca6
17cf34f73ca6
6c1d08d39449
6c1d08d39449
17cf34f73ca6
d7f5980cc68a
17cf34f73ca6
17cf34f73ca6
d7f5980cc68a
17cf34f73ca6
17cf34f73ca6
17cf34f73ca6
774d8236fa12
774d8236fa12
774d8236fa12
774d8236fa12
774d8236fa12
17cf34f73ca6
17cf34f73ca6
17cf34f73ca6
17cf34f73ca6
774d8236fa12
17cf34f73ca6
774d8236fa12
774d8236fa12
17cf34f73ca6
17cf34f73ca6
17cf34f73ca6
17cf34f73ca6
774d8236fa12
17cf34f73ca6
17cf34f73ca6
17cf34f73ca6
17cf34f73ca6
774d8236fa12
17cf34f73ca6
17cf34f73ca6
17cf34f73ca6
17cf34f73ca6
d7f5980cc68a
17cf34f73ca6
17cf34f73ca6
17cf34f73ca6
17cf34f73ca6
17cf34f73ca6
d7f5980cc68a
17cf34f73ca6
17cf34f73ca6
17cf34f73ca6
d7f5980cc68a
17cf34f73ca6
17cf34f73ca6
17cf34f73ca6
17cf34f73ca6
226882a5ed41
226882a5ed41
226882a5ed41
226882a5ed41
226882a5ed41
226882a5ed41
226882a5ed41
226882a5ed41
226882a5ed41
226882a5ed41
226882a5ed41
226882a5ed41
17cf34f73ca6
17cf34f73ca6
226882a5ed41
17cf34f73ca6
17cf34f73ca6
17cf34f73ca6
90fcfc170214
90fcfc170214
90fcfc170214
90fcfc170214
90fcfc170214
90fcfc170214
90fcfc170214
90fcfc170214
90fcfc170214
90fcfc170214
90fcfc170214
90fcfc170214
90fcfc170214
90fcfc170214
17cf34f73ca6
17cf34f73ca6
17cf34f73ca6
17cf34f73ca6
d7f5980cc68a
90fcfc170214
90fcfc170214
17cf34f73ca6
17cf34f73ca6
d7f5980cc68a
17cf34f73ca6
1bb9f7ac1072
1bb9f7ac1072
17cf34f73ca6
17cf34f73ca6
1bb9f7ac1072
d7f5980cc68a
1bb9f7ac1072
1bb9f7ac1072
1bb9f7ac1072
d7f5980cc68a
1bb9f7ac1072
1bb9f7ac1072
1bb9f7ac1072
1bb9f7ac1072
ae1a499dbd62
ae1a499dbd62
17cf34f73ca6
17cf34f73ca6
17cf34f73ca6
ae1a499dbd62
ae1a499dbd62
17cf34f73ca6
17cf34f73ca6
17cf34f73ca6
17cf34f73ca6
d7f5980cc68a
17cf34f73ca6
17cf34f73ca6
17cf34f73ca6
17cf34f73ca6
17cf34f73ca6
226882a5ed41
17cf34f73ca6
17cf34f73ca6
226882a5ed41
17cf34f73ca6
17cf34f73ca6
17cf34f73ca6
e4de7ba32dc1
17cf34f73ca6
17cf34f73ca6
17cf34f73ca6
226882a5ed41
17cf34f73ca6
17cf34f73ca6
ae1a499dbd62
ae1a499dbd62
ae1a499dbd62
ae1a499dbd62
ae1a499dbd62
ae1a499dbd62
ae1a499dbd62
ae1a499dbd62
ae1a499dbd62
ae1a499dbd62
ae1a499dbd62
ae1a499dbd62
ae1a499dbd62
ae1a499dbd62
ae1a499dbd62
ae1a499dbd62
ae1a499dbd62
ae1a499dbd62
ae1a499dbd62
ae1a499dbd62
ae1a499dbd62
ae1a499dbd62
ae1a499dbd62
ae1a499dbd62
ae1a499dbd62
ae1a499dbd62
import os
import re

import pytest

import testinfra.utils.ansible_runner


testinfra_hosts = testinfra.utils.ansible_runner.AnsibleRunner(
    os.environ['MOLECULE_INVENTORY_FILE']).get_hosts('client-relay-forbidden')


def test_open_relay(host):
    """
    Tests if mail server behaves as open relay.
    """

    no_recipients_accepted = 24

    send = host.run('swaks --suppress-data --to root@client1 --server parameters-mandatory')
    assert send.rc == no_recipients_accepted
    assert "Relay access denied" in send.stdout

    send = host.run('swaks --suppress-data --to root@client1 --server parameters-optional')
    assert send.rc == no_recipients_accepted
    assert "Relay access denied" in send.stdout

    send = host.run('swaks --port 27 --suppress-data --to root@client1 --server parameters-mandatory')
    assert send.rc == no_recipients_accepted
    assert "Relay access denied" in send.stdout

    send = host.run('swaks --port 27 --suppress-data --to root@client1 --server parameters-optional')
    assert send.rc == no_recipients_accepted
    assert "Relay access denied" in send.stdout


def test_mail_delivery(host):
    """
    Tests if mails can be delivered to valid accounts. Has to be run on client
    with no unauthenticated relay permissions.
    """

    no_recipients_accepted = 24

    # Valid accounts.
    send = host.run('swaks --suppress-data --to john.doe@domain1 --server parameters-mandatory')
    assert send.rc == 0
    assert "Ok: queued as" in send.stdout

    send = host.run('swaks --suppress-data --to john.doe@domain1 --server parameters-optional')
    assert send.rc == 0
    assert "Ok: queued as" in send.stdout

    send = host.run('swaks --suppress-data --to jane.doe@domain2 --server parameters-mandatory')
    assert send.rc == 0
    assert "Ok: queued as" in send.stdout

    send = host.run('swaks --suppress-data --to jane.doe@domain2 --server parameters-optional')
    assert send.rc == 0
    assert "Ok: queued as" in send.stdout

    # Invalid accounts.
    send = host.run('swaks --suppress-data --to john.doe@domain2 --server parameters-mandatory')
    assert send.rc == no_recipients_accepted
    assert "Recipient address rejected: User unknown in virtual mailbox table" in send.stdout

    send = host.run('swaks --suppress-data --to john.doe@domain2 --server parameters-optional')
    assert send.rc == no_recipients_accepted
    assert "Recipient address rejected: User unknown in virtual mailbox table" in send.stdout

    send = host.run('swaks --suppress-data --to jane.doe@domain1 --server parameters-mandatory')
    assert send.rc == no_recipients_accepted
    assert "Recipient address rejected: User unknown in virtual mailbox table" in send.stdout

    send = host.run('swaks --suppress-data --to jane.doe@domain1 --server parameters-optional')
    assert send.rc == no_recipients_accepted
    assert "Recipient address rejected: User unknown in virtual mailbox table" in send.stdout

    # Test for valid mail address that's not allowed by LDAP group membership.
    send = host.run('swaks --suppress-data --to nomail@domain1 --server parameters-mandatory')
    assert send.rc == no_recipients_accepted
    assert "Recipient address rejected: User unknown in virtual mailbox table" in send.stdout

    send = host.run('swaks --suppress-data --to nomail@domain1 --server parameters-optional')
    assert send.rc == no_recipients_accepted
    assert "Recipient address rejected: User unknown in virtual mailbox table" in send.stdout

    # Valid aliases.
    send = host.run('swaks --suppress-data --to postmaster@domain1 --server parameters-mandatory')
    assert send.rc == 0
    assert "Ok: queued as" in send.stdout

    send = host.run('swaks --suppress-data --to postmaster@domain1 --server parameters-optional')
    assert send.rc == 0
    assert "Ok: queued as" in send.stdout

    send = host.run('swaks --suppress-data --to webmaster@domain2 --server parameters-mandatory')
    assert send.rc == 0
    assert "Ok: queued as" in send.stdout

    send = host.run('swaks --suppress-data --to webmaster@domain2 --server parameters-optional')
    assert send.rc == 0
    assert "Ok: queued as" in send.stdout

    # Invalid aliases.
    send = host.run('swaks --suppress-data --to postmaster@domain2 --server parameters-mandatory')
    assert send.rc == no_recipients_accepted
    assert "Recipient address rejected: User unknown in virtual mailbox table" in send.stdout

    send = host.run('swaks --suppress-data --to postmaster@domain2 --server parameters-optional')
    assert send.rc == no_recipients_accepted
    assert "Recipient address rejected: User unknown in virtual mailbox table" in send.stdout

    send = host.run('swaks --suppress-data --to webmaster@domain1 --server parameters-mandatory')
    assert send.rc == no_recipients_accepted
    assert "Recipient address rejected: User unknown in virtual mailbox table" in send.stdout

    send = host.run('swaks --suppress-data --to webmaster@domain1 --server parameters-optional')
    assert send.rc == no_recipients_accepted
    assert "Recipient address rejected: User unknown in virtual mailbox table" in send.stdout


def test_smtp_authentication(host):
    """
    Tests if SMTP authentication works via TLS and allows sending mails to
    anywhere.
    """

    send = host.run('swaks -tls --port 587 --auth-user john.doe@domain1 --auth-password johnpassword '
                    '--from john.doe@domain1 --to root@client1 --server parameters-mandatory')
    assert send.rc == 0
    assert "Ok: queued as" in send.stdout

    send = host.run('swaks -tls --port 587 --auth-user john.doe@domain1 --auth-password johnpassword '
                    '--from john.doe@domain1 --to root@client1 --server parameters-optional')
    assert send.rc == 0
    assert "Ok: queued as" in send.stdout


def test_smtp_authentication_with_alias_sender(host):
    """
    Tests if SMTP authentication works via TLS and allows sending mails to
    anywhere while using sender alias.
    """

    send = host.run('swaks -tls --port 587 --auth-user john.doe@domain1 --auth-password johnpassword '
                    '--from postmaster@domain1 --to root@client1 --server parameters-mandatory')
    assert send.rc == 0
    assert "Ok: queued as" in send.stdout

    send = host.run('swaks -tls --port 587 --auth-user john.doe@domain1 --auth-password johnpassword '
                    '--from postmaster@domain1 --to root@client1 --server parameters-optional')
    assert send.rc == 0
    assert "Ok: queued as" in send.stdout


def test_smtp_authentication_requires_tls(host):
    """
    Tests if SMTP authentication requires TLS.
    """

    auth_error = 28

    send = host.run('swaks --port 587 --auth-user john.doe@domain1 --auth-password johnpassword '
                    '--from john.doe@domain1 --to root@client1 --server parameters-mandatory')
    assert send.rc == auth_error
    assert "Host did not advertise authentication" in send.stderr

    send = host.run('swaks --port 587 --auth-user john.doe@domain1 --auth-password johnpassword '
                    '--from john.doe@domain1 --to root@client1 --server parameters-optional')
    assert send.rc == auth_error
    assert "Host did not advertise authentication" in send.stderr


def test_smtp_authentication_requires_submission_port(host):
    """
    Tests if SMTP authentication cannot be done on regular SMTP port.
    """

    auth_error = 28

    send = host.run('swaks --port 25 --auth-user john.doe@domain1 --auth-password johnpassword '
                    '--from john.doe@domain1 --to root@client1 --server parameters-mandatory')
    assert send.rc == auth_error
    assert "Host did not advertise authentication" in send.stderr

    send = host.run('swaks -tls --port 25 --auth-user john.doe@domain1 --auth-password johnpassword '
                    '--from john.doe@domain1 --to root@client1 --server parameters-mandatory')
    assert send.rc == auth_error
    assert "Host did not advertise authentication" in send.stderr

    send = host.run('swaks --port 25 --auth-user john.doe@domain1 --auth-password johnpassword '
                    '--from john.doe@domain1 --to root@client1 --server parameters-optional')
    assert send.rc == auth_error
    assert "Host did not advertise authentication" in send.stderr

    send = host.run('swaks -tls --port 25 --auth-user john.doe@domain1 --auth-password johnpassword '
                    '--from john.doe@domain1 --to root@client1 --server parameters-optional')
    assert send.rc == auth_error
    assert "Host did not advertise authentication" in send.stderr


def test_dovecot_inbox_separator(host):
    """
    Tests if inbox separator has been configured correctly.
    """

    pattern_slash_separator = re.compile(r'WARNING:imap-cli:Ignoring "LIST" response part : \([^)]*\) "/" INBOX')
    pattern_dot_separator = re.compile(r'WARNING:imap-cli:Ignoring "LIST" response part : \([^)]*\) "\." INBOX')

    status = host.run("imapcli status -c ~/imapcli-parameters-mandatory-john_doe.conf")
    assert pattern_slash_separator.search(status.stdout) is not None

    status = host.run("imapcli status -c ~/imapcli-parameters-optional-john_doe.conf")
    assert pattern_dot_separator.search(status.stdout) is not None


@pytest.mark.parametrize("server", [
    "parameters-mandatory",
    "parameters-optional"
])
def test_imap_authentication_requires_tls(host, server):
    """
    Tests if IMAP authentication requires TLS.
    """

    capabilities_command = "a0001 CAPABILITY\na0002 LOGOUT"

    # No TLS.
    command = host.run("echo %s | nc %s 143", capabilities_command, server)
    assert command.rc == 0
    assert "LOGINDISABLED" in command.stdout

    # STARTTLS.
    command = host.run("echo %s | openssl s_client -quiet -connect %s:143 -starttls imap", capabilities_command, server)
    assert command.rc == 0
    assert "LOGINDISABLED" not in command.stdout

    # TLS.
    command = host.run("echo %s | openssl s_client -quiet -connect %s:993", capabilities_command, server)
    assert command.rc == 0
    assert "LOGINDISABLED" not in command.stdout


def test_sieve_authentication_requires_tls(host):
    """
    Tests if SIEVE authentication requires TLS.
    """

    # No TLS.
    command = host.run("echo 'LOGOUT' | nc parameters-mandatory 4190")
    assert command.rc == 0
    assert "PLAIN LOGIN" not in command.stdout

    command = host.run("echo 'LOGOUT' | nc parameters-optional 4190")
    assert command.rc == 0
    assert "PLAIN LOGIN" not in command.stdout

    # STARTTLS
    # @TODO: In case of failed login (authentication rejected),
    #        sieve-connect will return error code 255. However,
    #        internally Testinfra treats error code as if it is a
    #        signal from the SSH (over which the command is being run)
    #        that the SSH itself has failed, and throws a runtime
    #        exception. The use of || //bin/false is a workaround for
    #        now. It would be a good idea to get in contact with
    #        Testinfra maintainer and raise an issue around
    #        this. There are more similar uses of sieve-connect in the
    #        test files, don't forget to update those as well if some
    #        kind of better solution pops-up.
    command = host.run("echo 'johnpassword' | sieve-connect -u john.doe@domain1 --password 0 --server parameters-mandatory --port 4190 --list || /bin/false")
    assert command.rc == 0

    command = host.run("echo 'johnpassword' | sieve-connect -u john.doe@domain1 --password 0 --server parameters-optional --port 4190 --list || /bin/false")
    assert command.rc == 0


@pytest.mark.parametrize("server", [
    "parameters-mandatory",
    "parameters-optional"
])
@pytest.mark.parametrize("port", [
    25,
    26,
    27,
    587,
    143,
    993,
    4190
])
def test_connectivity(host, server, port):
    """
    Tests connectivity to the mail server (ports that should be reachable).
    """

    with host.sudo():
        ping = host.run('hping3 -S -p %s -c 1 %s', str(port), server)
        assert ping.rc == 0


def test_port_forwarding(host):
    """
    Tests if port forwarding is set-up correctly for additional SMTP and
    submission ports.
    """

    # Regular SMTP.
    send = host.run('swaks -tls --port 27 --to john.doe@domain1 --server parameters-mandatory')
    assert send.rc == 0
    assert "Ok: queued as" in send.stdout

    send = host.run('swaks -tls --port 27 --to john.doe@domain1 --server parameters-optional')
    assert send.rc == 0
    assert "Ok: queued as" in send.stdout

    # Submission port.
    send = host.run('swaks -tls --port 26 --auth-user john.doe@domain1 --auth-password johnpassword '
                    '--from john.doe@domain1 --to root@client1 --server parameters-mandatory')
    assert send.rc == 0
    assert "Ok: queued as" in send.stdout

    send = host.run('swaks -tls --port 26 --auth-user john.doe@domain1 --auth-password johnpassword '
                    '--from john.doe@domain1 --to root@client1 --server parameters-optional')
    assert send.rc == 0
    assert "Ok: queued as" in send.stdout


def test_dovecot_sieve(host):
    """
    Tests if Sieve service is available.
    """

    # Test valid users.
    command = host.run('echo johnpassword | sieve-connect --list -s parameters-mandatory -p 4190 -u john.doe@domain1 --password 0 || /bin/false')
    assert command.rc == 0

    command = host.run('echo janepassword | sieve-connect --list -s parameters-optional -p 4190 -u jane.doe@domain2 --password 0 || /bin/false')
    assert command.rc == 0

    # Test invalid users.
    command = host.run('echo johnpassword | sieve-connect --list -s parameters-mandatory -p 4190 -u john.doe@domain2 --password 0 || /bin/false')
    assert command.rc != 0
    assert "Authentication refused by server" in command.stderr

    command = host.run('echo janepassword | sieve-connect --list -s parameters-optional -p 4190 -u jane.doe@domain1 --password 0 || /bin/false')
    assert command.rc != 0
    assert "Authentication refused by server" in command.stderr


def test_smtp_sender_forging(host):
    """
    Tests if SMTP sender forging is possible.
    """

    send = host.run('swaks -tls --port 587 --auth-user john.doe@domain1 --auth-password johnpassword '
                    '--from jane.doe@domain2 --to root@client1 --server parameters-mandatory')
    assert send.rc == 24
    assert "Sender address rejected: not owned by user john.doe@domain1" in send.stdout

    send = host.run('swaks -tls --port 587 --auth-user john.doe@domain1 --auth-password johnpassword '
                    '--from jane.doe@domain2 --to root@client1 --server parameters-optional')
    assert send.rc == 24
    assert "Sender address rejected: not owned by user john.doe@domain1" in send.stdout

    send = host.run('swaks -tls --port 587 --auth-user john.doe@domain1 --auth-password johnpassword '
                    '--from webmaster@domain2 --to root@client1 --server parameters-mandatory')
    assert send.rc == 24
    assert "Sender address rejected: not owned by user john.doe@domain1" in send.stdout

    send = host.run('swaks -tls --port 587 --auth-user john.doe@domain1 --auth-password johnpassword '
                    '--from webmaster@domain2 --to root@client1 --server parameters-optional')
    assert send.rc == 24
    assert "Sender address rejected: not owned by user john.doe@domain1" in send.stdout