Changeset - f7c1f4c841f8
[Not reviewed]
1 3 0
Branko Majic (branko) - 4 years ago 2020-09-23 14:34:37
branko@majic.rs
MAR-158: Refactored web_server role TLS tests to use nmap.
4 files changed with 62 insertions and 165 deletions:
0 comments (0 inline, 0 general)
roles/web_server/molecule/default/prepare.yml
Show inline comments
 
@@ -54,7 +54,9 @@
 

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

	
 
- hosts: stretch
roles/web_server/molecule/default/tests/test_mandatory.py
Show inline comments
 
import os
 

	
 
import pytest
 
import defusedxml.ElementTree as ElementTree
 

	
 
import testinfra.utils.ansible_runner
 

	
 
from tls_ciphers import ALL_CIPHERS
 

	
 

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

	
 

	
 
def test_tls_version(host):
 
def test_tls_version_and_ciphers(host):
 
    """
 
    Tests if only the configured TLS protocol versions are allowed by
 
    the server.
 
    Tests if the correct TLS version and ciphers have been enabled.
 
    """
 

	
 
    old_tls_versions_disabled = host.run("echo 'Q' | openssl s_client -no_tls1_2 -connect parameters-mandatory:443")
 

	
 
    # Avoid false negatives by ensuring the client had actually
 
    # established the TCP connection.
 
    assert "CONNECTED" in old_tls_versions_disabled.stdout
 
    assert old_tls_versions_disabled.rc != 0
 

	
 

	
 
ENABLED_CIPHERS = [
 
    "DHE-RSA-AES128-GCM-SHA256",
 
    "DHE-RSA-AES256-GCM-SHA384",
 
    "DHE-RSA-CHACHA20-POLY1305",
 
    "ECDHE-RSA-AES128-GCM-SHA256",
 
    "ECDHE-RSA-AES256-GCM-SHA384",
 
    "ECDHE-RSA-CHACHA20-POLY1305",
 
]
 
    expected_tls_versions = ["TLSv1.2"]
 

	
 
DISABLED_CIPHERS = sorted(list(set(ALL_CIPHERS)-set(ENABLED_CIPHERS)))
 
    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",
 
    ]
 

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

	
 
@pytest.mark.parametrize("cipher", ENABLED_CIPHERS)
 
def test_enabled_tls_ciphers(host, cipher):
 
    """
 
    Tests available TLS ciphers on the server.
 
    """
 

	
 
    hostname = host.run('hostname').stdout.strip()
 
    fqdn = hostname[:hostname.rfind('-')]
 
    report_root = ElementTree.fromstring(report_content)
 

	
 
    client = host.run("echo 'Q' | openssl s_client -cipher %s -connect %s:443", cipher, fqdn)
 
    assert client.rc == 0
 
    assert cipher in client.stdout
 
    tls_versions = []
 
    tls_ciphers = set()
 

	
 
    for child in report_root.findall("./host/ports/port/script/table"):
 
        tls_versions.append(child.attrib['key'])
 

	
 
@pytest.mark.parametrize("cipher", DISABLED_CIPHERS)
 
def test_disabled_tls_ciphers(host, cipher):
 
    """
 
    Tests available TLS ciphers on the server.
 
    """
 
    for child in report_root.findall(".//table[@key='ciphers']/table/elem[@key='name']"):
 
        tls_ciphers.add(child.text)
 

	
 
    hostname = host.run('hostname').stdout.strip()
 
    fqdn = hostname[:hostname.rfind('-')]
 
    tls_versions.sort()
 
    tls_ciphers = sorted(list(tls_ciphers))
 

	
 
    client = host.run("echo 'Q' | openssl s_client -cipher %s -connect %s:443", cipher, fqdn)
 
    assert client.rc != 0
 
    assert cipher not in client.stdout
 
    assert tls_versions == expected_tls_versions
 
    assert tls_ciphers == expected_tls_ciphers
 

	
 

	
 
def test_https_enforcement(host):
roles/web_server/molecule/default/tests/test_optional.py
Show inline comments
 
import os
 

	
 
import pytest
 
import defusedxml.ElementTree as ElementTree
 

	
 
import testinfra.utils.ansible_runner
 

	
 
from tls_ciphers import ALL_CIPHERS
 

	
 

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

	
 

	
 
def test_tls_version(host):
 
def test_tls_version_and_ciphers(host):
 
    """
 
    Tests if only the configured TLS protocol versions are allowed by
 
    the server.
 
    Tests if the correct TLS version and ciphers have been enabled.
 
    """
 

	
 
    old_tls_versions_disabled = host.run("echo 'Q' | openssl s_client -no_tls1_1 -no_tls1_2 -connect parameters-optional:443")
 
    tls1_1_enabled = host.run("echo 'Q' | openssl s_client -tls1_1 -connect parameters-optional:443")
 
    tls1_2_enabled = host.run("echo 'Q' | openssl s_client -tls1_2 -connect parameters-optional:443")
 

	
 
    # Avoid false negatives by ensuring the client had actually
 
    # established the TCP connection.
 
    assert "CONNECTED" in old_tls_versions_disabled.stdout
 
    assert old_tls_versions_disabled.rc != 0
 

	
 
    # Avoid false negatives by ensuring the client had actually
 
    # established the TCP connection.
 
    assert "CONNECTED" in tls1_1_enabled.stdout
 
    assert tls1_1_enabled.rc == 0
 

	
 
    # Avoid false negatives by ensuring the client had actually
 
    # established the TCP connection.
 
    assert "CONNECTED" in tls1_2_enabled.stdout
 
    assert tls1_2_enabled.rc == 0
 

	
 
    expected_tls_versions = ["TLSv1.1", "TLSv1.2"]
 

	
 
ENABLED_CIPHERS = [
 
    "DHE-RSA-AES128-GCM-SHA256",
 
    "DHE-RSA-AES128-SHA256",
 
    "DHE-RSA-AES256-GCM-SHA384",
 
    "DHE-RSA-AES256-SHA256",
 
    "ECDHE-RSA-AES128-GCM-SHA256",
 
    "ECDHE-RSA-AES128-SHA",
 
    "ECDHE-RSA-AES128-SHA256",
 
    "ECDHE-RSA-AES256-GCM-SHA384",
 
    "ECDHE-RSA-AES256-SHA384",
 
]
 
    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",
 
    ]
 

	
 
DISABLED_CIPHERS = sorted(list(set(ALL_CIPHERS)-set(ENABLED_CIPHERS)))
 
    # Run the nmap scanner against the LDAP server, and fetch the
 
    # results.
 
    nmap = host.run("nmap -sV --script ssl-enum-ciphers -p 443 localhost -oX /tmp/report.xml")
 
    assert nmap.rc == 0
 
    report_content = host.file('/tmp/report.xml').content_string
 

	
 
    report_root = ElementTree.fromstring(report_content)
 

	
 
@pytest.mark.parametrize("cipher", ENABLED_CIPHERS)
 
def test_enabled_tls_ciphers(host, cipher):
 
    """
 
    Tests available TLS ciphers on the server.
 
    """
 

	
 
    hostname = host.run('hostname').stdout.strip()
 
    fqdn = hostname[:hostname.rfind('-')]
 
    tls_versions = []
 
    tls_ciphers = set()
 

	
 
    client = host.run("echo 'Q' | openssl s_client -cipher %s -connect %s:443", cipher, fqdn)
 
    assert client.rc == 0
 
    assert cipher in client.stdout
 
    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)
 

	
 
@pytest.mark.parametrize("cipher", DISABLED_CIPHERS)
 
def test_disabled_tls_ciphers(host, cipher):
 
    """
 
    Tests available TLS ciphers on the server.
 
    """
 

	
 
    hostname = host.run('hostname').stdout.strip()
 
    fqdn = hostname[:hostname.rfind('-')]
 
    tls_versions.sort()
 
    tls_ciphers = sorted(list(tls_ciphers))
 

	
 
    client = host.run("echo 'Q' | openssl s_client -cipher %s -connect %s:443", cipher, fqdn)
 
    assert client.rc != 0
 
    assert cipher not in client.stdout
 
    assert tls_versions == expected_tls_versions
 
    assert tls_ciphers == expected_tls_ciphers
 

	
 

	
 
def test_https_enforcement(host):
roles/web_server/molecule/default/tests/tls_ciphers.py
Show inline comments
 
deleted file
0 comments (0 inline, 0 general)