|
@@ -3,6 +3,10 @@ import re
|
|
|
import time
|
|
|
import uuid
|
|
|
|
|
|
import defusedxml.ElementTree as ElementTree
|
|
|
|
|
|
import pytest
|
|
|
|
|
|
import testinfra.utils.ansible_runner
|
|
|
|
|
|
|
|
@@ -129,12 +133,11 @@ def test_mail_owner(host):
|
|
|
assert user.groups == ["virtmail"]
|
|
|
|
|
|
|
|
|
def test_imap_tls_configuration(host):
|
|
|
def test_imap_tls_connectivity(host):
|
|
|
"""
|
|
|
Tests TLS configuration for IMAP in Dovecot.
|
|
|
Tests connectivity over STARTTLS/TLS towards IMAP server.
|
|
|
"""
|
|
|
|
|
|
# Test plain connectivity first.
|
|
|
starttls = host.run('echo "a0001 LOGOUT" | openssl s_client -quiet -starttls imap -connect parameters-optional:143')
|
|
|
assert starttls.rc == 0
|
|
|
assert '* BYE Logging out' in starttls.stdout
|
|
@@ -143,40 +146,53 @@ def test_imap_tls_configuration(host):
|
|
|
assert tls.rc == 0
|
|
|
assert '* BYE Logging out' in starttls.stdout
|
|
|
|
|
|
# Test TLS protocol versions.
|
|
|
starttls = host.run('echo "a0001 LOGOUT" | openssl s_client -quiet -starttls imap -no_tls1_2 -connect parameters-optional:143')
|
|
|
assert starttls.rc == 0
|
|
|
assert '* BYE Logging out' in starttls.stdout
|
|
|
|
|
|
tls = host.run('echo "a0001 LOGOUT" | openssl s_client -quiet -no_tls1_2 -connect parameters-optional:993')
|
|
|
assert tls.rc == 0
|
|
|
assert '* BYE Logging out' in starttls.stdout
|
|
|
@pytest.mark.parametrize("port", [
|
|
|
143,
|
|
|
993,
|
|
|
587,
|
|
|
])
|
|
|
def test_imap_and_smtp_submission_tls_version_and_ciphers(host, port):
|
|
|
"""
|
|
|
Tests if the correct TLS version and ciphers have been enabled for
|
|
|
IMAP and SMTP submission.
|
|
|
"""
|
|
|
|
|
|
expected_tls_versions = ["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",
|
|
|
]
|
|
|
|
|
|
starttls = host.run("echo 'a0001 LOGOUT' | openssl s_client -quiet -starttls imap -no_tls1_1 -no_tls1_2 -connect parameters-optional:143")
|
|
|
assert starttls.rc != 0
|
|
|
assert 'SSL alert number 70' in starttls.stderr
|
|
|
# Run the nmap scanner against the server, and fetch the results.
|
|
|
nmap = host.run("nmap -sV --script ssl-enum-ciphers -p %s localhost -oX /tmp/report.xml", str(port))
|
|
|
assert nmap.rc == 0
|
|
|
report_content = host.file('/tmp/report.xml').content_string
|
|
|
|
|
|
tls = host.run("echo 'a0001 LOGOUT' | openssl s_client -quiet -no_tls1_1 -no_tls1_2 -connect parameters-optional:993")
|
|
|
assert tls.rc != 0
|
|
|
assert 'SSL alert number 70' in tls.stderr
|
|
|
report_root = ElementTree.fromstring(report_content)
|
|
|
|
|
|
# Test at least one strong TLS cipher.
|
|
|
starttls_cipher = host.run("echo 'a0001 LOGOUT' | openssl s_client -starttls imap -cipher ECDHE-RSA-AES128-SHA256 -connect parameters-optional:143")
|
|
|
assert starttls_cipher.rc == 0
|
|
|
assert "ECDHE-RSA-AES128-SHA256" in starttls_cipher.stdout
|
|
|
tls_versions = []
|
|
|
tls_ciphers = set()
|
|
|
|
|
|
tls_cipher = host.run("echo 'a0001 LOGOUT' | openssl s_client -cipher ECDHE-RSA-AES128-SHA256 -connect parameters-optional:993")
|
|
|
assert tls_cipher.rc == 0
|
|
|
assert "ECDHE-RSA-AES128-SHA256" in tls_cipher.stdout
|
|
|
for child in report_root.findall("./host/ports/port/script/table"):
|
|
|
tls_versions.append(child.attrib['key'])
|
|
|
|
|
|
# Test weaker TLS cipher that was explicitly configured
|
|
|
starttls_cipher = host.run("echo 'a0001 LOGOUT' | openssl s_client -starttls imap -cipher ECDHE-RSA-AES128-SHA -connect parameters-optional:143")
|
|
|
assert starttls_cipher.rc == 0
|
|
|
assert "ECDHE-RSA-AES128-SHA" in starttls_cipher.stdout
|
|
|
for child in report_root.findall(".//table[@key='ciphers']/table/elem[@key='name']"):
|
|
|
tls_ciphers.add(child.text)
|
|
|
|
|
|
tls_cipher = host.run("echo 'a0001 LOGOUT' | openssl s_client -cipher ECDHE-RSA-AES128-SHA -connect parameters-optional:993")
|
|
|
assert tls_cipher.rc == 0
|
|
|
assert "ECDHE-RSA-AES128-SHA" in tls_cipher.stdout
|
|
|
tls_versions.sort()
|
|
|
tls_ciphers = sorted(list(tls_ciphers))
|
|
|
|
|
|
assert tls_versions == expected_tls_versions
|
|
|
assert tls_ciphers == expected_tls_ciphers
|
|
|
|
|
|
|
|
|
def test_dovecot_postmaster(host):
|
|
@@ -205,54 +221,106 @@ def test_imap_max_user_connections_per_ip(host):
|
|
|
assert " mail_max_userip_connections = 2" in config.stdout
|
|
|
|
|
|
|
|
|
def test_postfix_tls_configuration(host):
|
|
|
def test_smtp_tls_connectivity(host):
|
|
|
"""
|
|
|
Tests TLS configuration for SMTP in Postfix.
|
|
|
Tests connectivity over default/submission port towards SMTP
|
|
|
server.
|
|
|
"""
|
|
|
|
|
|
# Test TLS protocol versions for default port (all should be enabled).
|
|
|
starttls = host.run("echo 'QUIT' | openssl s_client -quiet -starttls smtp -no_tls1 -no_tls1_1 -connect parameters-optional:25")
|
|
|
assert starttls.rc == 0
|
|
|
assert '221 2.0.0 Bye' in starttls.stdout
|
|
|
|
|
|
starttls = host.run("echo 'QUIT' | openssl s_client -quiet -starttls smtp -no_tls1_2 -connect parameters-optional:25")
|
|
|
assert starttls.rc == 0
|
|
|
assert '221 2.0.0 Bye' in starttls.stdout
|
|
|
|
|
|
starttls = host.run("echo 'QUIT' | openssl s_client -quiet -starttls smtp -no_tls1_2 -no_tls1_1 -connect parameters-optional:25")
|
|
|
assert starttls.rc == 0
|
|
|
assert '221 2.0.0 Bye' in starttls.stdout
|
|
|
|
|
|
# Test TLS protocol versions for submission port (only TLS 1.1 and TLS 1.2 should be enabled).
|
|
|
starttls = host.run("echo 'QUIT' | openssl s_client -quiet -starttls smtp -connect parameters-optional:587")
|
|
|
starttls = host.run('echo "QUIT" | openssl s_client -quiet -starttls smtp -connect parameters-optional:25')
|
|
|
assert starttls.rc == 0
|
|
|
assert '221 2.0.0 Bye' in starttls.stdout
|
|
|
|
|
|
starttls = host.run("echo 'QUIT' | openssl s_client -quiet -starttls smtp -no_tls1_2 -connect parameters-optional:587")
|
|
|
assert starttls.rc == 0
|
|
|
tls = host.run('echo "QUIT" | openssl s_client -quiet -starttls smtp -connect parameters-optional:587')
|
|
|
assert tls.rc == 0
|
|
|
assert '221 2.0.0 Bye' in starttls.stdout
|
|
|
|
|
|
starttls = host.run("echo 'QUIT' | openssl s_client -quiet -starttls smtp -no_tls1_1 -no_tls1_2 -connect parameters-optional:587")
|
|
|
assert starttls.rc != 0
|
|
|
assert 'SSL alert number 70' in starttls.stderr
|
|
|
|
|
|
# Test ciphers for default port (less restrictive).
|
|
|
starttls_cipher = host.run("echo 'QUIT' | openssl s_client -starttls smtp -cipher ECDHE-RSA-AES128-SHA256 -connect parameters-optional:25")
|
|
|
assert starttls_cipher.rc == 0
|
|
|
assert "ECDHE-RSA-AES128-SHA256" in starttls_cipher.stdout
|
|
|
|
|
|
starttls_cipher = host.run("echo 'QUIT' | openssl s_client -starttls smtp -cipher ECDHE-RSA-AES128-SHA -connect parameters-optional:25")
|
|
|
assert starttls_cipher.rc == 0
|
|
|
assert "ECDHE-RSA-AES128-SHA" in starttls_cipher.stdout
|
|
|
|
|
|
# Test ciphers for submission port (at least one weak cipher was configured).
|
|
|
starttls_cipher = host.run("echo 'QUIT' | openssl s_client -starttls smtp -cipher ECDHE-RSA-AES128-SHA256 -connect parameters-optional:587")
|
|
|
assert starttls_cipher.rc == 0
|
|
|
assert "ECDHE-RSA-AES128-SHA256" in starttls_cipher.stdout
|
|
|
def test_smtp_default_port_tls_version_and_ciphers(host):
|
|
|
"""
|
|
|
Tests TLS configuration for SMTP default port (needs to be less
|
|
|
restrictive for interoperability purposes).
|
|
|
"""
|
|
|
|
|
|
starttls_cipher = host.run("echo 'QUIT' | openssl s_client -starttls smtp -cipher ECDHE-RSA-AES128-SHA -connect parameters-optional:587")
|
|
|
assert starttls_cipher.rc == 0
|
|
|
assert "ECDHE-RSA-AES128-SHA" in starttls_cipher.stdout
|
|
|
expected_tls_versions = ["TLSv1.0", "TLSv1.1", "TLSv1.2"]
|
|
|
|
|
|
expected_tls_ciphers = [
|
|
|
"TLS_DHE_RSA_WITH_AES_128_CBC_SHA",
|
|
|
"TLS_DHE_RSA_WITH_AES_128_CBC_SHA256",
|
|
|
"TLS_DHE_RSA_WITH_AES_128_CCM",
|
|
|
"TLS_DHE_RSA_WITH_AES_128_CCM_8",
|
|
|
"TLS_DHE_RSA_WITH_AES_128_GCM_SHA256",
|
|
|
"TLS_DHE_RSA_WITH_AES_256_CBC_SHA",
|
|
|
"TLS_DHE_RSA_WITH_AES_256_CBC_SHA256",
|
|
|
"TLS_DHE_RSA_WITH_AES_256_CCM",
|
|
|
"TLS_DHE_RSA_WITH_AES_256_CCM_8",
|
|
|
"TLS_DHE_RSA_WITH_AES_256_GCM_SHA384",
|
|
|
"TLS_DHE_RSA_WITH_CAMELLIA_128_CBC_SHA",
|
|
|
"TLS_DHE_RSA_WITH_CAMELLIA_128_CBC_SHA256",
|
|
|
"TLS_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA",
|
|
|
"TLS_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA256",
|
|
|
"TLS_DHE_RSA_WITH_CHACHA20_POLY1305_SHA256",
|
|
|
"TLS_DHE_RSA_WITH_SEED_CBC_SHA",
|
|
|
"TLS_DH_anon_WITH_AES_128_CBC_SHA",
|
|
|
"TLS_DH_anon_WITH_AES_128_CBC_SHA256",
|
|
|
"TLS_DH_anon_WITH_AES_128_GCM_SHA256",
|
|
|
"TLS_DH_anon_WITH_AES_256_CBC_SHA",
|
|
|
"TLS_DH_anon_WITH_AES_256_CBC_SHA256",
|
|
|
"TLS_DH_anon_WITH_AES_256_GCM_SHA384",
|
|
|
"TLS_DH_anon_WITH_CAMELLIA_128_CBC_SHA",
|
|
|
"TLS_DH_anon_WITH_CAMELLIA_128_CBC_SHA256",
|
|
|
"TLS_DH_anon_WITH_CAMELLIA_256_CBC_SHA",
|
|
|
"TLS_DH_anon_WITH_CAMELLIA_256_CBC_SHA256",
|
|
|
"TLS_DH_anon_WITH_SEED_CBC_SHA",
|
|
|
"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_SHA",
|
|
|
"TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384",
|
|
|
"TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384",
|
|
|
"TLS_ECDHE_RSA_WITH_CAMELLIA_128_CBC_SHA256",
|
|
|
"TLS_ECDHE_RSA_WITH_CAMELLIA_256_CBC_SHA384",
|
|
|
"TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256",
|
|
|
"TLS_ECDH_anon_WITH_AES_128_CBC_SHA",
|
|
|
"TLS_ECDH_anon_WITH_AES_256_CBC_SHA",
|
|
|
"TLS_RSA_WITH_AES_128_CBC_SHA",
|
|
|
"TLS_RSA_WITH_AES_128_CBC_SHA256",
|
|
|
"TLS_RSA_WITH_AES_128_CCM",
|
|
|
"TLS_RSA_WITH_AES_128_CCM_8",
|
|
|
"TLS_RSA_WITH_AES_128_GCM_SHA256",
|
|
|
"TLS_RSA_WITH_AES_256_CBC_SHA",
|
|
|
"TLS_RSA_WITH_AES_256_CBC_SHA256",
|
|
|
"TLS_RSA_WITH_AES_256_CCM",
|
|
|
"TLS_RSA_WITH_AES_256_CCM_8",
|
|
|
"TLS_RSA_WITH_AES_256_GCM_SHA384",
|
|
|
"TLS_RSA_WITH_CAMELLIA_128_CBC_SHA",
|
|
|
"TLS_RSA_WITH_CAMELLIA_128_CBC_SHA256",
|
|
|
"TLS_RSA_WITH_CAMELLIA_256_CBC_SHA",
|
|
|
"TLS_RSA_WITH_CAMELLIA_256_CBC_SHA256",
|
|
|
"TLS_RSA_WITH_SEED_CBC_SHA",
|
|
|
]
|
|
|
|
|
|
# Run the nmap scanner against the server, and fetch the results.
|
|
|
nmap = host.run("nmap -sV --script ssl-enum-ciphers -p 25 localhost -oX /tmp/report.xml")
|
|
|
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/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
|
|
|
|
|
|
|
|
|
def test_sieve_tls_configuration(host):
|