import os import re import uuid import testinfra.utils.ansible_runner testinfra_hosts = testinfra.utils.ansible_runner.AnsibleRunner( os.environ['MOLECULE_INVENTORY_FILE']).get_hosts('parameters-*') def test_installed_packages(host): """ Tests if the necessary packages have been installed. """ assert host.package('rsync').is_installed assert host.package('dovecot-imapd').is_installed assert host.package('dovecot-ldap').is_installed assert host.package('dovecot-sieve').is_installed assert host.package('dovecot-managesieved').is_installed assert host.package('postfix').is_installed assert host.package('postfix-ldap').is_installed assert host.package('swaks').is_installed assert host.package('clamav-milter').is_installed def test_removed_packages(host): """ Tests if certain packages have been removed from the system. """ assert not host.package('exim4').is_installed def test_postfix_user(host): """ Tests if Postfix user has been added to correct group for traversing the TLS private key directory. """ assert "ssl-cert" in host.user('postfix').groups def test_dovecot_user(host): """ Tests if Dovecot user has been added to correct group for traversing the TLS private key directory. """ assert "ssl-cert" in host.user('dovecot').groups def test_clamav_milter_configuration(host): """ Tests if ClamAV Milter configuration has been deployed correctly. """ config = host.file('/etc/clamav/clamav-milter.conf') assert config.is_file assert config.user == 'root' assert config.group == 'root' assert config.mode == 0o644 def test_clamav_milter(host): """ Tests if ClamAV milter is blocking viruses. """ server_did_not_accept_mail = 26 eicar = 'X5O!P%@AP[4\\PZX54(P^)7CC)7}$EICAR-STANDARD-ANTIVIRUS-TEST-FILE!$H+H*' send_mail = host.run("swaks --to john.doe@domain1 --server localhost --attach '%s'" % eicar) assert send_mail.rc == server_did_not_accept_mail assert 'Your message has been rejected due to a possible virus' in send_mail.stdout def test_postfix_chroot_directories(host): """ Tests if Postfix chroot directories have been set-up with correct permissions. """ directory = host.file('/var/spool/postfix/var') assert directory.is_directory assert directory.user == 'root' assert directory.group == 'root' assert directory.mode == 0o755 directory = host.file('/var/spool/postfix/var/run') assert directory.is_directory assert directory.user == 'root' assert directory.group == 'root' assert directory.mode == 0o755 directory = host.file('/var/spool/postfix/var/run/clamav') assert directory.is_directory assert directory.user == 'clamav' assert directory.group == 'clamav' assert directory.mode == 0o755 def test_ldap_tls_truststore_file(host): """ Tests if the LDAP TLS truststore file has been deployed correctly. """ tls_file = host.file('/etc/ssl/certs/mail_ldap_tls_truststore.pem') assert tls_file.is_file assert tls_file.user == 'root' assert tls_file.group == 'root' assert tls_file.mode == 0o644 assert tls_file.content_string == open("tests/data/x509/ca.cert.pem", "r").read().rstrip() tls_file = host.file('/var/spool/postfix/etc/ssl/certs/mail_ldap_tls_truststore.pem') assert tls_file.is_file assert tls_file.user == 'root' assert tls_file.group == 'root' assert tls_file.mode == 0o644 assert tls_file.content_string == open("tests/data/x509/ca.cert.pem", "r").read().rstrip() def test_mailname_file(host): """ Tests the system mail name file permissions. """ mailname = host.file('/etc/mailname') assert mailname.is_file assert mailname.user == 'root' assert mailname.group == 'root' assert mailname.mode == 0o644 def test_postfix_ldap_configuration_files(host): """ Tests if Postfix LDAP configuration files have been deployed correctly. """ for config_file_path in ['/etc/postfix/ldap-virtual-alias-maps.cf', '/etc/postfix/ldap-virtual-mailbox-domains.cf', '/etc/postfix/ldap-virtual-mailbox-maps.cf']: config = host.file(config_file_path) assert config.is_file assert config.user == 'root' assert config.group == 'postfix' assert config.mode == 0o640 def test_postfix_ldap_configuration(host): """ Tests if LDAP configuration can be used to fetch correct query results. """ with host.sudo(): # Test for valid domains. command = host.run("postmap -q domain1 ldap:/etc/postfix/ldap-virtual-mailbox-domains.cf") assert command.rc == 0 assert command.stdout == "domain1\n" command = host.run("postmap -q domain2 ldap:/etc/postfix/ldap-virtual-mailbox-domains.cf") assert command.rc == 0 assert command.stdout == "domain2\n" # Test for invalid domains. command = host.run("postmap -q domain3 ldap:/etc/postfix/ldap-virtual-mailbox-domains.cf") assert command.rc == 1 assert command.stdout == "" # Test for valid mail addresses. command = host.run("postmap -q 'john.doe@domain1' ldap:/etc/postfix/ldap-virtual-mailbox-maps.cf") assert command.rc == 0 assert command.stdout == 'john.doe@domain1\n' command = host.run("postmap -q 'jane.doe@domain2' ldap:/etc/postfix/ldap-virtual-mailbox-maps.cf") assert command.rc == 0 assert command.stdout == 'jane.doe@domain2\n' # Test for invalid mail addresses. command = host.run("postmap -q 'jane.doe@domain1' ldap:/etc/postfix/ldap-virtual-mailbox-maps.cf") assert command.rc == 1 assert command.stdout == '' command = host.run("postmap -q 'john.doe@domain2' ldap:/etc/postfix/ldap-virtual-mailbox-maps.cf") assert command.rc == 1 assert command.stdout == '' # Test for valid mail address that's not allowed by LDAP group membership. command = host.run("postmap -q 'nomail@domain1' ldap:/etc/postfix/ldap-virtual-mailbox-maps.cf") assert command.rc == 1 assert command.stdout == '' # Test for valid mail aliases. command = host.run("postmap -q postmaster@domain1 ldap:/etc/postfix/ldap-virtual-alias-maps.cf") assert command.rc == 0 assert command.stdout == "john.doe@domain1\n" command = host.run("postmap -q webmaster@domain2 ldap:/etc/postfix/ldap-virtual-alias-maps.cf") assert command.rc == 0 assert command.stdout == "jane.doe@domain2\n" # Test for invalid mail aliases. command = host.run("postmap -q postmaster@domain2 ldap:/etc/postfix/ldap-virtual-alias-maps.cf") assert command.rc == 1 assert command.stdout == "" command = host.run("postmap -q webmaster@domain1 ldap:/etc/postfix/ldap-virtual-alias-maps.cf") assert command.rc == 1 assert command.stdout == "" def test_postfix_main_cf_file(host): """ Tests Postfix main configuration file permissions. """ config = host.file('/etc/postfix/main.cf') assert config.is_file assert config.user == 'root' assert config.group == 'root' assert config.mode == 0o644 def test_postfix_delivery_to_dovecot(host): """ Tests if mail received by Postfix is properly delivered to Dovecot. """ hostname = host.run('hostname').stdout.strip() message_id = str(uuid.uuid4()) # Virtual account. send = host.run('swaks --header %s --suppress-data --to john.doe@domain1 --server %s', "Message-Id: <%s>" % message_id, hostname) assert send.rc == 0 with host.sudo(): mail_log = host.file('/var/log/mail.log') pattern = r"dovecot: lda\(john.doe@domain1\): msgid=<%s>: saved mail to INBOX" % message_id assert re.search(pattern, mail_log.content_string) is not None def test_dovecot_system_authentication_is_disabled(host): """ Tests if Dovecot system-based authentication has been disabled. """ config = host.file("/etc/dovecot/conf.d/10-auth.conf") assert "!include auth-system.conf.ext" not in config.content_string def test_dovecot_overrides_configuration_file(host): """ Tests if Dovecot configuration file with overrides has been deployed and has correct permissions. """ config = host.file("/etc/dovecot/conf.d/99-local.conf") assert config.is_file assert config.user == 'root' assert config.group == 'root' assert config.mode == 0o644 def test_dovecot_imap_ldap_configuration(host): """ Tests if Dovecot LDAP configuration is correct. """ with host.sudo(): user_does_not_exist = 67 # Test for valid mail addresses. command = host.run("doveadm user john.doe@domain1") assert command.rc == 0 command = host.run("doveadm user jane.doe@domain2") assert command.rc == 0 # Test for invalid mail addresses. command = host.run("doveadm user john.doe@domain2") assert command.rc == user_does_not_exist command = host.run("doveadm user jane.doe@domain1") assert command.rc == user_does_not_exist # Test for mail addresses present in LDAP, but entry not in mail group. command = host.run("doveadm user nomail@domain1") assert command.rc == user_does_not_exist def test_postfix_master_file(host): """ Tests permissions for Postfix master.cf configuration file. """ config = host.file('/etc/postfix/master.cf') assert config.is_file assert config.user == 'root' assert config.group == 'root' assert config.mode == 0o644 def test_services(host): """ Tests if all the mail-related servieces are up and running. """ for service_name in ["clamav-daemon", "clamav-freshclam", "clamav-milter", "postfix", "dovecot"]: service = host.service(service_name) assert service.is_running assert service.is_enabled def test_clamav_database_presence(host): """ Tests if ClamAV database is present. """ for database_file in ["/var/lib/clamav/bytecode", "/var/lib/clamav/daily", "/var/lib/clamav/main"]: database_cvd = host.file(database_file + '.cvd') database_cld = host.file(database_file + '.cld') assert database_cvd.is_file or database_cld.is_file def test_firewall_configuration_file(host): """ Tests if firewall configuration file has been deployed correctly. """ with host.sudo(): config = host.file('/etc/ferm/conf.d/20-mail.conf') assert config.is_file assert config.user == 'root' assert config.group == 'root' assert config.mode == 0o640 def test_smtp_server_dh_parameter_file(host): """ Tests if the Diffie-Hellman parameter file has been generated correctly. """ hostname = host.run('hostname').stdout.strip() dhparam_file_path = '/etc/ssl/private/%s_smtp.dh.pem' % hostname with host.sudo(): dhparam_file = host.file(dhparam_file_path) assert dhparam_file.is_file assert dhparam_file.user == 'root' assert dhparam_file.group == 'root' assert dhparam_file.mode == 0o640 dhparam_info = host.run("openssl dhparam -noout -text -in %s", dhparam_file_path) assert "DH Parameters: (2048 bit)" in dhparam_info.stdout def test_smtp_server_uses_correct_dh_parameters(host): """ Tests if the SMTP server uses the generated Diffie-Hellman parameter. """ hostname = host.run('hostname').stdout.strip() with host.sudo(): expected_dhparam = host.file('/etc/ssl/private/%s_smtp.dh.pem' % hostname).content_string.rstrip() connection = host.run("gnutls-cli --no-ca-verification --starttls-proto=smtp --port 25 " "--priority 'NONE:+VERS-TLS1.2:+CTYPE-X509:+COMP-NULL:+SIGN-RSA-SHA384:+DHE-RSA:+SHA384:+AEAD:+AES-256-GCM' --verbose localhost") output = connection.stdout begin_marker = "-----BEGIN DH PARAMETERS-----" end_marker = "-----END DH PARAMETERS-----" used_dhparam = output[output.find(begin_marker):output.find(end_marker) + len(end_marker)] assert used_dhparam == expected_dhparam def test_imap_server_uses_correct_dh_parameters(host): """ Tests if the IMAP server uses correct Diffie-Hellman parameters. """ connection = host.run("gnutls-cli --no-ca-verification --starttls-proto=imap --port 143 " "--priority 'NONE:+VERS-TLS1.2:+CTYPE-X509:+COMP-NULL:+SIGN-RSA-SHA384:+DHE-RSA:+SHA384:+AEAD:+AES-256-GCM' localhost") assert " - Using prime: 2048 bits" in connection.stdout def test_imap_and_smtp_tls_files(host): """ Tests if IMAP and SMTP TLS private keys and certificates have been deployed correctly. """ hostname = host.run('hostname').stdout.strip() with host.sudo(): tls_file = host.file('/etc/ssl/private/%s_smtp.key' % hostname) assert tls_file.is_file assert tls_file.user == 'root' assert tls_file.group == 'root' assert tls_file.mode == 0o640 assert tls_file.content_string == open("tests/data/x509/%s_smtp.key.pem" % hostname, "r").read().rstrip() tls_file = host.file('/etc/ssl/certs/%s_smtp.pem' % hostname) assert tls_file.is_file assert tls_file.user == 'root' assert tls_file.group == 'root' assert tls_file.mode == 0o644 assert tls_file.content_string == open("tests/data/x509/%s_smtp.cert.pem" % hostname, "r").read().rstrip() tls_file = host.file('/etc/ssl/private/%s_imap.key' % hostname) assert tls_file.is_file assert tls_file.user == 'root' assert tls_file.group == 'root' assert tls_file.mode == 0o640 assert tls_file.content_string == open("tests/data/x509/%s_imap.key.pem" % hostname, "r").read().rstrip() tls_file = host.file('/etc/ssl/certs/%s_imap.pem' % hostname) assert tls_file.is_file assert tls_file.user == 'root' assert tls_file.group == 'root' assert tls_file.mode == 0o644 assert tls_file.content_string == open("tests/data/x509/%s_imap.cert.pem" % hostname, "r").read().rstrip()