From f774e938a4ed462cdb65c2517cbca234d0e56d47 2017-07-06 22:06:28 From: Branko Majic Date: 2017-07-06 22:06:28 Subject: [PATCH] MAR-27: Implemented tests for mail_forwarder role: - Install hping3 (for testing connectivity) on mail-server test machine. - Changed syntax used for deploying the SMTP relay truststore in order to ensure that tabs are preserved. - Implemented tests for the mail_fotwarder role. - Updated tests for mail_server role to check for new setting added (smtp_host_lookup). --- diff --git a/roles/mail_forwarder/playbook.yml b/roles/mail_forwarder/playbook.yml index d9fdb2851794485c5b704c5efcf8fa32eb32b3dd..6367e9a7be32efbf1930114d1482092349c40a91 100644 --- a/roles/mail_forwarder/playbook.yml +++ b/roles/mail_forwarder/playbook.yml @@ -88,6 +88,11 @@ notify: - Restart Postfix + - name: Install tool for testing TCP connectivity + apt: + name: hping3 + state: installed + handlers: - name: Update CA certificate cache diff --git a/roles/mail_forwarder/tasks/main.yml b/roles/mail_forwarder/tasks/main.yml index d5384e75bb7ae7dd20be804f1bf36a29ac02ed54..9a3be1d672cd1dce33edc01eb629780cb1a872a4 100644 --- a/roles/mail_forwarder/tasks/main.yml +++ b/roles/mail_forwarder/tasks/main.yml @@ -7,8 +7,12 @@ apt: name="exim4*" state=absent purge=yes - name: Deploy the SMTP relay TLS truststore - copy: content="{{ smtp_relay_truststore }}" dest="/etc/ssl/certs/smtp_relay_truststore.pem" - owner=root group=root mode=0644 + copy: + content: "{{ smtp_relay_truststore }}" + dest: "/etc/ssl/certs/smtp_relay_truststore.pem" + owner: root + group: root + mode: 0644 - name: Configure visible mail name of the system copy: content="{{ inventory_hostname }}\n" dest="/etc/mailname" diff --git a/roles/mail_forwarder/tests/test_connectivity_from_client.py b/roles/mail_forwarder/tests/test_connectivity_from_client.py new file mode 100644 index 0000000000000000000000000000000000000000..1bdccffe8044535fd9700a7d30a82286f4327764 --- /dev/null +++ b/roles/mail_forwarder/tests/test_connectivity_from_client.py @@ -0,0 +1,20 @@ +import testinfra.utils.ansible_runner + + +testinfra_hosts = testinfra.utils.ansible_runner.AnsibleRunner( + '.molecule/ansible_inventory').get_hosts('client1') + + +def test_connectivity_from_client(Command, Sudo): + """ + Tests connectivity towards mail forwarder servers from client + (non-relay). Connectivity should fail for both. + """ + + with Sudo(): + + ping = Command('hping3 -S -p 25 -c 1 parameters-mandatory') + assert ping.rc != 0 + + ping = Command('hping3 -S -p 25 -c 1 parameters-optional') + assert ping.rc != 0 diff --git a/roles/mail_forwarder/tests/test_connectivity_from_relay.py b/roles/mail_forwarder/tests/test_connectivity_from_relay.py new file mode 100644 index 0000000000000000000000000000000000000000..8deb5b4d284f1e1ba326d1cf8c18a61c3350a231 --- /dev/null +++ b/roles/mail_forwarder/tests/test_connectivity_from_relay.py @@ -0,0 +1,42 @@ +import testinfra.utils.ansible_runner + + +testinfra_hosts = testinfra.utils.ansible_runner.AnsibleRunner( + '.molecule/ansible_inventory').get_hosts('mail-server') + + +def test_connectivity_from_relay(Command, Sudo): + """ + Tests connectivity towards mail forwarder servers from relay. Connection + towards parameters-mandatory should fail. + """ + + with Sudo(): + + ping = Command('hping3 -S -p 25 -c 1 parameters-mandatory') + assert ping.rc != 0 + + ping = Command('hping3 -S -p 25 -c 1 parameters-optional') + assert ping.rc == 0 + + +def test_mail_reception_from_relay(Command, Sudo): + """ + Tests if mails can be sent from relay to servers configured to use the + relay. + """ + + send = Command('swaks --suppress-data --to root@parameters-optional --server parameters-optional') + assert send.rc == 0 + + +def test_open_relay(Command): + """ + Tests if mail forwarder behaves as open relay. + """ + + no_recipients_accepted = 24 + + send = Command('swaks --suppress-data --to root@client1 --server parameters-optional') + assert send.rc == no_recipients_accepted + assert "Relay access denied" in send.stdout diff --git a/roles/mail_forwarder/tests/test_default.py b/roles/mail_forwarder/tests/test_default.py index ef04a32b8c6dc6cc2738e21edb2cf11d1c0c55c9..d36ac06de67fb7ae637acf46caccdb7d7d9bbc39 100644 --- a/roles/mail_forwarder/tests/test_default.py +++ b/roles/mail_forwarder/tests/test_default.py @@ -1,12 +1,83 @@ import testinfra.utils.ansible_runner + testinfra_hosts = testinfra.utils.ansible_runner.AnsibleRunner( - '.molecule/ansible_inventory').get_hosts('all') + '.molecule/ansible_inventory').get_hosts(['parameters-mandatory', 'parameters-optional']) + + +def test_installed_packages(Package): + """ + Tests if the necessary packages have been installed. + """ + + assert Package('postfix').is_installed + assert Package('swaks').is_installed + + +def test_removed_packages(Package): + """ + Tests if certain packages have been removed from the system. + """ + + assert not Package('exim4').is_installed + + +def test_smtp_relay_truststore_file(File): + """ + Tests if SMTP relay truststore has correct permissions + """ + + truststore = File('/etc/ssl/certs/smtp_relay_truststore.pem') + + assert truststore.is_file + assert truststore.user == 'root' + assert truststore.group == 'root' + assert truststore.mode == 0o644 + + +def test_smtp_mailname(File): + """ + Tests if SMTP mailname configuration file has correct permissions. + """ + + mailname = File('/etc/mailname') + + assert mailname.is_file + assert mailname.user == 'root' + assert mailname.group == 'root' + assert mailname.mode == 0o644 + + +def test_postfix_main_cf_file(File): + """ + Tests Postfix main configuration file permissions. + """ + + config = File('/etc/postfix/main.cf') + assert config.is_file + assert config.user == 'root' + assert config.group == 'root' + assert config.mode == 0o644 + + +def test_services(Service): + """ + Tests if all the necessary services are enabled and running. + """ + + service = Service('postfix') + assert service.is_running + assert service.is_enabled -def test_hosts_file(File): - f = File('/etc/hosts') +def test_firewall_configuration_file(File, Sudo): + """ + Tests if firewall configuration file has correct permissions. + """ - assert f.exists - assert f.user == 'root' - assert f.group == 'root' + with Sudo(): + config = File('/etc/ferm/conf.d/20-mail.conf') + assert config.is_file + assert config.user == 'root' + assert config.group == 'root' + assert config.mode == 0o640 diff --git a/roles/mail_forwarder/tests/test_mandatory.py b/roles/mail_forwarder/tests/test_mandatory.py new file mode 100644 index 0000000000000000000000000000000000000000..ef7d5eaa322e2680c648287ab681d8de541e504f --- /dev/null +++ b/roles/mail_forwarder/tests/test_mandatory.py @@ -0,0 +1,62 @@ +import re + + +import testinfra.utils.ansible_runner + + +testinfra_hosts = testinfra.utils.ansible_runner.AnsibleRunner( + '.molecule/ansible_inventory').get_hosts('parameters-mandatory') + + +def test_smtp_relay_truststore_file(File): + """ + Tests if SMTP relay truststore has correct content. + """ + + truststore = File('/etc/ssl/certs/smtp_relay_truststore.pem') + + assert truststore.content == open("tests/data/x509/truststore.pem", "r").read().rstrip() + + +def test_smtp_mailname(File): + """ + Tests if SMTP mailname configuration file has correct content. + """ + + mailname = File('/etc/mailname') + + assert mailname.content == "parameters-mandatory" + + +def test_postfix_main_cf_file_content(File): + """ + Tests if the Postfix main configuration file content is correct. + """ + + config = File('/etc/postfix/main.cf') + config_lines = config.content.split("\n") + + assert "myhostname = parameters-mandatory" in config_lines + assert "mydestination = parameters-mandatory, parameters-mandatory, localhost.localdomain, localhost" in config_lines + assert "relayhost = " in config_lines + assert "mynetworks = 127.0.0.0/8 [::ffff:127.0.0.0]/104 [::1]/128" in config_lines + assert "smtp_tls_security_level" not in config.content + assert "smtp_tls_CAfile" not in config.content + assert "smtp_host_lookup = dns, native" in config_lines + + +def test_direct_mail_sending(Command, File, Sudo): + """ + Tests if mails are sent correctly directly without relay if relay has not + been configured. + """ + + send = Command('swaks --suppress-data --to root@domain1 --server localhost') + assert send.rc == 0 + message_id = re.search('Ok: queued as (.*)', send.stdout).group(1) + + with Sudo(): + mail_log = File('/var/log/mail.log') + pattern = "%s: to=, relay=domain1.*status=sent" % message_id + + assert re.search(pattern, mail_log.content) is not None diff --git a/roles/mail_forwarder/tests/test_optional.py b/roles/mail_forwarder/tests/test_optional.py new file mode 100644 index 0000000000000000000000000000000000000000..5e9cb7db158c1bac841ebfc8979c582826d46aa1 --- /dev/null +++ b/roles/mail_forwarder/tests/test_optional.py @@ -0,0 +1,112 @@ +import re + + +import testinfra.utils.ansible_runner + + +testinfra_hosts = testinfra.utils.ansible_runner.AnsibleRunner( + '.molecule/ansible_inventory').get_hosts('parameters-optional') + + +def test_smtp_relay_truststore_file(File): + """ + Tests if SMTP relay truststore has correct content. + """ + + truststore = File('/etc/ssl/certs/smtp_relay_truststore.pem') + + assert truststore.content == open("tests/data/x509/ca.cert.pem", "r").read().rstrip() + + +def test_smtp_mailname(File): + """ + Tests if SMTP mailname has been configured correctly. + """ + + mailname = File('/etc/mailname') + + assert mailname.content == "parameters-optional" + + +def test_postfix_main_cf_file_content(File): + """ + Tests if the Postfix main configuration file content is correct. + """ + + config = File('/etc/postfix/main.cf') + config_lines = config.content.split("\n") + + assert "myhostname = parameters-optional" in config_lines + assert "mydestination = parameters-optional, parameters-optional, localhost.localdomain, localhost" in config_lines + assert "relayhost = mail-server" in config_lines + assert "mynetworks = 127.0.0.0/8 [::ffff:127.0.0.0]/104 [::1]/128" in config_lines + assert "smtp_tls_security_level=verify" in config_lines + assert "smtp_tls_CAfile=/etc/ssl/certs/smtp_relay_truststore.pem" in config_lines + assert "smtp_host_lookup = dns, native" in config_lines + + +def test_local_aliases(Command, File, Sudo): + """ + Tests if local aliases are configured correctly. + """ + + send = Command('swaks --suppress-data --to root@localhost') + assert send.rc == 0 + message_id = re.search('Ok: queued as (.*)', send.stdout).group(1) + + with Sudo(): + mail_log = File('/var/log/mail.log') + pattern1 = "%s: to=, orig_to=.*status=sent" % message_id + pattern2 = "%s: to=, orig_to=.*status=sent" % message_id + + assert re.search(pattern1, mail_log.content) is not None + assert re.search(pattern2, mail_log.content) is not None + + +def test_relay_mail_sending(Command, File, Sudo): + """ + Tests if mails are sent correctly via relay if relay has been configured. + """ + + send = Command('swaks --suppress-data --to root@domain1 --server localhost') + assert send.rc == 0 + message_id = re.search('Ok: queued as (.*)', send.stdout).group(1) + + with Sudo(): + mail_log = File('/var/log/mail.log') + pattern = "%s: to=, relay=mail-server.*status=sent" % message_id + + assert re.search(pattern, mail_log.content) is not None + + +def test_tls_enforced_towards_relay_mail_server(Command, File, Sudo): + """ + Tests if TLS verification is enfoced towards the relay mail server. + """ + + with Sudo(): + # Replace the relayhost with name that is not present in relay's + # certificate. + command = Command("sed -i -e s#relayhost\\ =\\ mail-server#relayhost\\ =\\ domain1# /etc/postfix/main.cf") + assert command.rc == 0 + command = Command("service postfix restart") + assert command.rc == 0 + + # Try to send out an e-mail + send = Command('swaks --suppress-data --to root@domain1 --server localhost') + + # Restore correct relay name in the configuration file. + command = Command("sed -i -e s#relayhost\\ =\\ domain1#relayhost\\ =\\ mail-server# /etc/postfix/main.cf") + assert command.rc == 0 + command = Command("service postfix restart") + assert command.rc == 0 + + # Finally check the results. + assert send.rc == 0 + message_id = re.search('Ok: queued as (.*)', send.stdout).group(1) + + with Sudo(): + mail_log = File('/var/log/mail.log') + pattern = "%s: to=, relay=domain1.*status=deferred \(Server certificate not verified\)" % message_id + + assert re.search(pattern, mail_log.content) is not None diff --git a/roles/mail_server/tests/test_mandatory.py b/roles/mail_server/tests/test_mandatory.py index 315c5cc6a1cc6835719d163183f9c84b2778cf39..c39b1e6968a00f4ad4b9894b1f760f843558e2d7 100644 --- a/roles/mail_server/tests/test_mandatory.py +++ b/roles/mail_server/tests/test_mandatory.py @@ -85,6 +85,7 @@ def test_postfix_main_cf_file_content(File): assert "smtpd_tls_cert_file = /etc/ssl/certs/parameters-mandatory_smtp.pem" in config_lines assert "smtpd_tls_key_file = /etc/ssl/private/parameters-mandatory_smtp.key" in config_lines assert "reject_rbl" not in config_lines + assert "smtp_host_lookup = dns, native" in config_lines def test_dovecot_mailbox_directories(Command, File, Sudo): diff --git a/roles/mail_server/tests/test_optional.py b/roles/mail_server/tests/test_optional.py index d6954ab278efb07f4b50b2205df65315cf72e17e..0b4370782317034e321716c6a66ddf6d6ba53c66 100644 --- a/roles/mail_server/tests/test_optional.py +++ b/roles/mail_server/tests/test_optional.py @@ -89,6 +89,7 @@ def test_postfix_main_cf_file_content(File): assert "smtpd_tls_key_file = /etc/ssl/private/parameters-optional_smtp.key" in config_lines assert " reject_rbl bl.spamcop.net" in config_lines assert " reject_rbl zen.spamhaus.org" in config_lines + assert "smtp_host_lookup = dns, native" in config_lines def test_local_aliases(Command, File, Sudo):