Changeset - 351cd42e5f56
[Not reviewed]
0 3 3
Branko Majic (branko) - 7 years ago 2017-07-12 11:54:11
branko@majic.rs
MAR-32: Implemented tests for web_server role:

- Updated test playbook to deploy some additional test tools.
- Use expanded syntax for deploying TLS keys and certificates in order to
preserve tabs.
- Added a number of tests that should cover full functionality of the server.
6 files changed with 459 insertions and 10 deletions:
0 comments (0 inline, 0 general)
roles/web_server/playbook.yml
Show inline comments
 
@@ -75,3 +75,11 @@
 
      # common
 
      ca_certificates:
 
        testca: "{{ lookup('file', 'tests/data/x509/ca.cert.pem') }}"
 

	
 

	
 
- hosts: all
 
  tasks:
 
    - name: Install curl for testing redirects and webpage content
 
      apt:
 
        name: curl
 
        state: installed
roles/web_server/tasks/main.yml
Show inline comments
 
@@ -9,14 +9,22 @@
 
    - Restart nginx
 

	
 
- name: Deploy nginx TLS private key
 
  copy: dest="/etc/ssl/private/{{ ansible_fqdn }}_https.key" content="{{ default_https_tls_key }}"
 
        mode=0640 owner=root group=root
 
  copy:
 
    dest: "/etc/ssl/private/{{ ansible_fqdn }}_https.key"
 
    content: "{{ default_https_tls_key }}"
 
    mode: 0640
 
    owner: root
 
    group: root
 
  notify:
 
    - Restart nginx
 

	
 
- name: Deploy nginx TLS certificate
 
  copy: dest="/etc/ssl/certs/{{ ansible_fqdn }}_https.pem" content="{{ default_https_tls_certificate }}"
 
        mode=0644 owner=root group=root
 
  copy:
 
    dest: "/etc/ssl/certs/{{ ansible_fqdn }}_https.pem"
 
    content: "{{ default_https_tls_certificate }}"
 
    mode: 0644
 
    owner: root
 
    group: root
 
  notify:
 
    - Restart nginx
 

	
roles/web_server/tests/test_client.py
Show inline comments
 
new file 100644
 
import testinfra.utils.ansible_runner
 

	
 

	
 
testinfra_hosts = testinfra.utils.ansible_runner.AnsibleRunner(
 
    '.molecule/ansible_inventory').get_hosts('client1')
 

	
 

	
 
def test_connectivity(Command, Sudo):
 
    """
 
    Tests connectivity to the web server (ports that should be reachable).
 
    """
 

	
 
    with Sudo():
 

	
 
        for server in ["parameters-mandatory",
 
                       "parameters-optional"]:
 
            # HTTP, HTTPS.
 
            for port in [80, 443]:
 

	
 
                ping = Command('hping3 -S -p %d -c 1 %s' % (port, server))
 
                assert ping.rc == 0
roles/web_server/tests/test_default.py
Show inline comments
 
@@ -2,12 +2,235 @@ 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_hosts_file(File):
 
    f = File('/etc/hosts')
 
def test_installed_packages(Package):
 
    """
 
    Tests if the required packages have been installed.
 
    """
 

	
 
    assert f.exists
 
    assert f.user == 'root'
 
    assert f.group == 'root'
 
    assert Package('nginx').is_installed
 
    assert Package('virtualenv').is_installed
 
    assert Package('virtualenvwrapper').is_installed
 
    assert Package('php5-fpm').is_installed
 

	
 

	
 
def test_nginx_user(User):
 
    """
 
    Tests if Nginx user has been set-up correctly to traverse TLS directories.
 
    """
 

	
 
    assert 'ssl-cert' in User('www-data').groups
 

	
 

	
 
def test_default_tls_configuration_removed(File):
 
    """
 
    Tests if TLS configuration has been removed from the main (default)
 
    configuration file.
 
    """
 

	
 
    assert 'ssl_protocols' not in File('/etc/nginx/nginx.conf').content
 

	
 

	
 
def test_nginx_configuration_verification_script(File):
 
    """
 
    Tests if script used for verifying Nginx configuration is deployed
 
    correctly.
 
    """
 

	
 
    script = File('/usr/local/bin/nginx_verify_site.sh')
 

	
 
    assert script.is_file
 
    assert script.user == 'root'
 
    assert script.group == 'root'
 
    assert script.mode == 0o755
 

	
 

	
 
def test_tls_configuration_file(File):
 
    """
 
    Tests permissions of TLS configuration file.
 
    """
 

	
 
    config = File('/etc/nginx/conf.d/tls.conf')
 

	
 
    assert config.is_file
 
    assert config.user == 'root'
 
    assert config.group == 'root'
 
    assert config.mode == 0o644
 

	
 

	
 
def test_default_vhost_file(File):
 
    """
 
    Tests permissions of default vhost configuration file.
 
    """
 

	
 
    config = File('/etc/nginx/sites-available/default')
 

	
 
    assert config.is_file
 
    assert config.user == 'root'
 
    assert config.group == 'root'
 
    assert config.mode == 0o640
 

	
 

	
 
def test_default_website_enabled(File):
 
    """
 
    Tests if default website has been enabled.
 
    """
 

	
 
    config = File('/etc/nginx/sites-enabled/default')
 

	
 
    assert config.is_symlink
 
    assert config.linked_to == '/etc/nginx/sites-available/default'
 

	
 

	
 
def test_firewall_configuration_file(File, Sudo):
 
    """
 
    Tests if firewall configuration file has been deployed correctly.
 
    """
 

	
 
    with Sudo():
 

	
 
        config = File('/etc/ferm/conf.d/30-web.conf')
 

	
 
        assert config.is_file
 
        assert config.user == 'root'
 
        assert config.group == 'root'
 
        assert config.mode == 0o640
 

	
 

	
 
def test_default_debian_index_removed(File, Sudo):
 
    """
 
    Tests if default HTML pages provided by debian are removed.
 
    """
 

	
 
    with Sudo():
 
        assert not File('/var/www/html').exists
 

	
 

	
 
def test_default_vhost_root_directory(File, Sudo):
 
    """
 
    Tests if the default vhost root directory exists.
 
    """
 

	
 
    directory = File('/var/www/default')
 

	
 
    assert directory.is_directory
 
    assert directory.user == 'root'
 
    assert directory.group == 'www-data'
 
    assert directory.mode == 0o750
 

	
 

	
 
def test_default_vhost_index_page_file(File, Sudo):
 
    """
 
    Tests permissions of default vhost index page.
 
    """
 

	
 
    with Sudo():
 

	
 
        page = File('/var/www/default/index.html')
 

	
 
        assert page.is_file
 
        assert page.user == 'root'
 
        assert page.group == 'www-data'
 
        assert page.mode == 0o640
 

	
 

	
 
def test_services(Service):
 
    """
 
    Tests if services are enabled at boot and running.
 
    """
 

	
 
    service = Service('nginx')
 
    assert service.is_enabled
 
    assert service.is_running
 

	
 
    service = Service('php5-fpm')
 
    assert service.is_enabled
 
    assert service.is_running
 

	
 

	
 
def test_sockets(Socket):
 
    """
 
    Tests if web server is listening on correct ports.
 
    """
 

	
 
    assert Socket("tcp://80").is_listening
 
    assert Socket("tcp://443").is_listening
 

	
 

	
 
def test_socket_directories(File, Sudo):
 
    """
 
    Tests if directories containing sockets for WSGI and PHP apps are created
 
    correctly.
 
    """
 

	
 
    directory = File('/run/wsgi')
 
    assert directory.is_directory
 
    assert directory.user == 'root'
 
    assert directory.group == 'www-data'
 
    assert directory.mode == 0o750
 

	
 
    directory = File('/run/php5-fpm')
 
    assert directory.is_directory
 
    assert directory.user == 'root'
 
    assert directory.group == 'www-data'
 
    assert directory.mode == 0o750
 

	
 
    config = File('/etc/tmpfiles.d/wsgi.conf')
 
    assert config.is_file
 
    assert config.user == 'root'
 
    assert config.group == 'root'
 
    assert config.mode == 0o644
 
    assert 'd /run/wsgi/ 0750 root www-data - -' in config.content
 

	
 
    config = File('/etc/tmpfiles.d/php5-fpm.conf')
 
    assert config.is_file
 
    assert config.user == 'root'
 
    assert config.group == 'root'
 
    assert config.mode == 0o644
 
    assert 'd /run/php5-fpm/ 0750 root www-data - -' in config.content
 

	
 

	
 
def test_php5_fpm_service_overrides(File):
 
    """
 
    Tests if overrides for php5-fpm service are deployed correctly.
 
    """
 

	
 
    directory = File('/etc/systemd/system/php5-fpm.service.d')
 
    assert directory.is_directory
 
    assert directory.user == 'root'
 
    assert directory.group == 'root'
 
    assert directory.mode == 0o755
 

	
 
    config = File('/etc/systemd/system/php5-fpm.service.d/umask.conf')
 
    assert config.is_file
 
    assert config.user == 'root'
 
    assert config.group == 'root'
 
    assert config.mode == 0o644
 

	
 

	
 
def test_php_timezone_configuration(Command, File):
 
    """
 
    Tests if PHP timezone configuration has been set correctly.
 
    """
 

	
 
    config = File('/etc/php5/cli/conf.d/30-timezone.ini')
 
    assert config.is_file
 
    assert config.user == 'root'
 
    assert config.group == 'root'
 
    assert config.mode == 0o644
 

	
 
    config = File('/etc/php5/fpm/conf.d/30-timezone.ini')
 
    assert config.is_file
 
    assert config.user == 'root'
 
    assert config.group == 'root'
 
    assert config.mode == 0o644
 

	
 
    timezone = Command("php --php-ini /etc/php5/cli/php.ini -r 'echo ini_get(\"date.timezone\");'")
 
    assert timezone.rc == 0
 
    assert timezone.stdout == "GMT+0"
 

	
 
    timezone = Command("php --php-ini /etc/php5/fpm/php.ini -r 'echo ini_get(\"date.timezone\");'")
 
    assert timezone.rc == 0
 
    assert timezone.stdout == "GMT+0"
roles/web_server/tests/test_mandatory.py
Show inline comments
 
new file 100644
 
import testinfra.utils.ansible_runner
 

	
 

	
 
testinfra_hosts = testinfra.utils.ansible_runner.AnsibleRunner(
 
    '.molecule/ansible_inventory').get_hosts('parameters-mandatory')
 

	
 

	
 
def test_nginx_tls_files(File, Sudo):
 
    """
 
    Tests if TLS private key and certificate have been deployed correctly.
 
    """
 

	
 
    with Sudo():
 

	
 
        tls_file = File('/etc/ssl/private/parameters-mandatory_https.key')
 
        assert tls_file.is_file
 
        assert tls_file.user == 'root'
 
        assert tls_file.group == 'root'
 
        assert tls_file.mode == 0o640
 
        assert tls_file.content == open("tests/data/x509/parameters-mandatory_https.key", "r").read().rstrip()
 

	
 
        tls_file = File('/etc/ssl/certs/parameters-mandatory_https.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 == open("tests/data/x509/parameters-mandatory_https.pem", "r").read().rstrip()
 

	
 

	
 
def test_certificate_validity_check_configuration(File):
 
    """
 
    Tests if certificate validity check configuration file has been deployed
 
    correctly.
 
    """
 

	
 
    config = File('/etc/check_certificate/parameters-mandatory_https.conf')
 
    assert config.is_file
 
    assert config.user == 'root'
 
    assert config.group == 'root'
 
    assert config.mode == 0o644
 
    assert config.content == "/etc/ssl/certs/parameters-mandatory_https.pem"
 

	
 

	
 
def test_tls_configuration(Command):
 
    """
 
    Tests if the TLS has been configured correctly and works.
 
    """
 

	
 
    tls = Command('wget -q -O - https://parameters-mandatory/')
 
    assert tls.rc == 0
 

	
 
    old_tls_versions_disabled = Command("echo 'Q' | openssl s_client -no_tls1_2 -connect parameters-mandatory:443")
 
    assert old_tls_versions_disabled.rc != 0
 
    assert "CONNECTED" in old_tls_versions_disabled.stdout
 

	
 
    cipher = Command("echo 'Q' | openssl s_client -cipher ECDHE-RSA-AES128-SHA256 -connect parameters-mandatory:443")
 
    assert cipher.rc == 0
 
    assert "ECDHE-RSA-AES128-SHA256" in cipher.stdout
 

	
 
    cipher = Command("echo 'Q' | openssl s_client -cipher ECDHE-RSA-AES128-SHA -connect parameters-mandatory:443")
 
    assert cipher.rc != 0
 
    assert "ECDHE-RSA-AES128-SHA" not in cipher.stdout
 

	
 

	
 
def test_https_enforcement(Command):
 
    """
 
    Tests if HTTPS is being enforced.
 
    """
 

	
 
    https_enforcement = Command('curl -I http://parameters-mandatory/')
 

	
 
    assert https_enforcement.rc == 0
 
    assert 'HTTP/1.1 301 Moved Permanently' in https_enforcement.stdout
 
    assert 'Location: https://parameters-mandatory/' in https_enforcement.stdout
 

	
 
    https_enforcement = Command('curl -I https://parameters-mandatory/')
 

	
 
    assert https_enforcement.rc == 0
 
    assert 'Strict-Transport-Security: max-age=31536000; includeSubDomains' in https_enforcement.stdout
 

	
 

	
 
def test_default_vhost_index_page(Command):
 
    """
 
    Tests content of default vhost index page.
 
    """
 

	
 
    page = Command('curl https://parameters-mandatory/')
 

	
 
    assert page.rc == 0
 
    assert "<title>Welcome</title>" in page.stdout
 
    assert "<h1>Welcome</h1>" in page.stdout
 
    assert "<p>You are attempting to access the web server using a wrong name or an IP address. Please check your URL.</p>" in page.stdout
roles/web_server/tests/test_optional.py
Show inline comments
 
new file 100644
 
import testinfra.utils.ansible_runner
 

	
 

	
 
testinfra_hosts = testinfra.utils.ansible_runner.AnsibleRunner(
 
    '.molecule/ansible_inventory').get_hosts('parameters-optional')
 

	
 

	
 
def test_nginx_tls_files(File, Sudo):
 
    """
 
    Tests if TLS private key and certificate have been deployed correctly.
 
    """
 

	
 
    with Sudo():
 

	
 
        tls_file = File('/etc/ssl/private/parameters-optional_https.key')
 
        assert tls_file.is_file
 
        assert tls_file.user == 'root'
 
        assert tls_file.group == 'root'
 
        assert tls_file.mode == 0o640
 
        assert tls_file.content == open("tests/data/x509/parameters-optional_https.key.pem", "r").read().rstrip()
 

	
 
        tls_file = File('/etc/ssl/certs/parameters-optional_https.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 == open("tests/data/x509/parameters-optional_https.cert.pem", "r").read().rstrip()
 

	
 

	
 
def test_certificate_validity_check_configuration(File):
 
    """
 
    Tests if certificate validity check configuration file has been deployed
 
    correctly.
 
    """
 

	
 
    config = File('/etc/check_certificate/parameters-optional_https.conf')
 
    assert config.is_file
 
    assert config.user == 'root'
 
    assert config.group == 'root'
 
    assert config.mode == 0o644
 
    assert config.content == "/etc/ssl/certs/parameters-optional_https.pem"
 

	
 

	
 
def test_tls_configuration(Command):
 
    """
 
    Tests if the TLS has been configured correctly and works.
 
    """
 

	
 
    tls = Command('wget -q -O - https://parameters-optional/')
 
    assert tls.rc == 0
 

	
 
    old_tls_versions_disabled = Command("echo 'Q' | openssl s_client -no_tls1_1 -no_tls1_2 -connect parameters-optional:443")
 
    assert old_tls_versions_disabled.rc != 0
 
    assert "CONNECTED" in old_tls_versions_disabled.stdout
 

	
 
    newer_tls_versions_enabled = Command("echo 'Q' | openssl s_client -no_tls1_2 -connect parameters-optional:443")
 
    assert newer_tls_versions_enabled.rc == 0
 
    assert "CONNECTED" in newer_tls_versions_enabled.stdout
 

	
 
    cipher = Command("echo 'Q' | openssl s_client -cipher ECDHE-RSA-AES128-SHA256 -connect parameters-optional:443")
 
    assert cipher.rc == 0
 
    assert "ECDHE-RSA-AES128-SHA256" in cipher.stdout
 

	
 
    cipher = Command("echo 'Q' | openssl s_client -cipher ECDHE-RSA-AES128-SHA -connect parameters-optional:443")
 
    assert cipher.rc == 0
 
    assert "ECDHE-RSA-AES128-SHA" in cipher.stdout
 

	
 

	
 
def test_https_enforcement(Command):
 
    """
 
    Tests if HTTPS is (not) being enforced.
 
    """
 

	
 
    https_enforcement = Command('curl -I http://parameters-optional/')
 

	
 
    assert https_enforcement.rc == 0
 
    assert 'HTTP/1.1 200 OK' in https_enforcement.stdout
 
    assert 'HTTP/1.1 301 Moved Permanently' not in https_enforcement.stdout
 
    assert 'Location: https://parameters-optional/' not in https_enforcement.stdout
 

	
 
    https_enforcement = Command('curl -I https://parameters-optional/')
 

	
 
    assert https_enforcement.rc == 0
 
    assert 'Strict-Transport-Security' not in https_enforcement.stdout
 

	
 

	
 
def test_default_vhost_index_page(Command):
 
    """
 
    Tests content of default vhost index page.
 
    """
 

	
 
    page = Command('curl https://parameters-optional/')
 

	
 
    assert page.rc == 0
 
    assert "<title>Optional Welcome</title>" in page.stdout
 
    assert "<h1>Optional Welcome</h1>" in page.stdout
 
    assert "<p>Welcome to parameters-optional, default virtual host.</p>" in page.stdout
0 comments (0 inline, 0 general)