diff --git a/docs/releasenotes.rst b/docs/releasenotes.rst index 2aa625e33357952bb885b0f8c7e7d52c936a3b4b..e693cb5b778ff413c8a5abfbc43359d4b2434a41 100644 --- a/docs/releasenotes.rst +++ b/docs/releasenotes.rst @@ -60,6 +60,11 @@ Breaking changes: dropped. This could introduce incompatibility with older clients trying to connect to the IMAP/SMTP server. +* ``php_website`` role + + * Parameter ``enforce_https`` has been deprecated and + removed. HTTPS is now mandatory in all cases. + * ``preseed`` role * Parameter ``ansible_key`` is now mandatory. diff --git a/docs/rolereference.rst b/docs/rolereference.rst index 39efdcb270333faf9c32eccbbe886e271da00461..90e6924a5c788eee687d41d241c34974a6a92749 100644 --- a/docs/rolereference.rst +++ b/docs/rolereference.rst @@ -1474,6 +1474,12 @@ The role implements the following: The role is implemented with the following layout/logic in mind: +* Clients are served with ``Strict-Transport-Security`` header with + value of ``max-age=31536000; includeSubDomains``. This forces + compliant clients to always connect using HTTPS to the web server + when accessing its domain, as well as any subdomains served + by this web server or any other. The (client-side) cached header + value expires after one year. * Website users are named after the ``FQDN`` (fully qualified domain name) of website, in format of ``web-ESCAPEDFQDN``, where ``ESCAPEDFQDN`` is equal to ``FQDN`` where dots have been replaced by underscores (for example, @@ -1541,12 +1547,6 @@ Parameters compatible with regular expressions used by ``nginx`` for ``location ~`` syntax. -**enforce_https** (boolean, optional, ``True``) - Specify if HTTPS should be enforced for the website or not. If enforced, - clients connecting via plaintext will be redirected to HTTPS, and clients will - be served with ``Strict-Transport-Security`` header with value of - ``max-age=31536000; includeSubDomains``. - **environment_indicator** (dictionary, optional, ``null``) Specify configuration for including environment indicator on all HTML pages. Indicator is a simple strip at bottom of a page with custom background diff --git a/docs/usage.rst b/docs/usage.rst index c58df46e11035d91cf6872f19ceaaa6b8c281f94..5ede70652f3921d7850d236855348763a7481fcc 100644 --- a/docs/usage.rst +++ b/docs/usage.rst @@ -1237,8 +1237,7 @@ included, and also play nice with the web server role. As mentioned before, all roles will enforce TLS by default. The web server roles will additionaly implement HSTS policy by sending connecting clients ``Strict-Transport-Security`` header with value set to ``max-age=31536000; -includeSubDomains`` (if you disable enforcement of TLS, the header will not be -sent). +includeSubDomains``. With all the above noted, let us finally move on to the next step. diff --git a/roles/php_website/defaults/main.yml b/roles/php_website/defaults/main.yml index 8f990af2a73c683671de3dac3a8ed14b9d57c071..c56f66bbd2a58b040e14b478218e854c0de4faa2 100644 --- a/roles/php_website/defaults/main.yml +++ b/roles/php_website/defaults/main.yml @@ -2,7 +2,6 @@ additional_nginx_config: {} deny_files_regex: [] -enforce_https: true index: index.php packages: [] php_file_regex: \.php$ diff --git a/roles/php_website/molecule/default/playbook.yml b/roles/php_website/molecule/default/playbook.yml index a4fb3a5812e5ea3a49a0ffca9f21ddb15067a83e..fbb5302a7fa143541c0ea597c8dadbf9802a6310 100644 --- a/roles/php_website/molecule/default/playbook.yml +++ b/roles/php_website/molecule/default/playbook.yml @@ -27,7 +27,6 @@ admin_uid: 5000 deny_files_regex: - '^/secretfile.txt' - enforce_https: false environment_indicator: background_colour: "#ff0000" text_colour: "#00ff00" diff --git a/roles/php_website/molecule/default/tests/test_default.py b/roles/php_website/molecule/default/tests/test_default.py index 538fe398960d93bc2cba86b3e51de11c3b8a083a..6bc5496d3de89fc42a4412dcb2c6e2d8a5e1b87f 100644 --- a/roles/php_website/molecule/default/tests/test_default.py +++ b/roles/php_website/molecule/default/tests/test_default.py @@ -1,7 +1,30 @@ import os +import pytest + import testinfra.utils.ansible_runner testinfra_hosts = testinfra.utils.ansible_runner.AnsibleRunner( os.environ['MOLECULE_INVENTORY_FILE']).get_hosts('all') + + +@pytest.mark.parametrize('fqdn', [ + 'parameters-mandatory', + 'parameters-optional.local', +]) +def test_https_enforcement(host, fqdn): + """ + Tests if HTTPS is being enforced. + """ + + https_enforcement = host.run('curl -I http://%s/', fqdn) + + assert https_enforcement.rc == 0 + assert 'HTTP/1.1 301 Moved Permanently' in https_enforcement.stdout + assert 'Location: https://%s/' % fqdn in https_enforcement.stdout + + https_enforcement = host.run('curl -I https://%s/', fqdn) + + assert https_enforcement.rc == 0 + assert 'Strict-Transport-Security: max-age=31536000; includeSubDomains' in https_enforcement.stdout diff --git a/roles/php_website/molecule/default/tests/test_parameters_mandatory.py b/roles/php_website/molecule/default/tests/test_parameters_mandatory.py index d14b84f1943b574710b176957a98bbf6db4feb6c..fe95dce9fa87d94ee3068de463123a36f5d8e826 100644 --- a/roles/php_website/molecule/default/tests/test_parameters_mandatory.py +++ b/roles/php_website/molecule/default/tests/test_parameters_mandatory.py @@ -208,23 +208,6 @@ def test_website_enabled(host): assert config.linked_to == '/etc/nginx/sites-available/parameters-mandatory' -def test_https_enforcement(host): - """ - Tests if HTTPS is being enforced. - """ - - https_enforcement = host.run('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 = host.run('curl -I https://parameters-mandatory/') - - assert https_enforcement.rc == 0 - assert 'Strict-Transport-Security: max-age=31536000; includeSubDomains' in https_enforcement.stdout - - def test_index_page(host): """ Tests if index page is served correctly. diff --git a/roles/php_website/molecule/default/tests/test_parameters_optional.py b/roles/php_website/molecule/default/tests/test_parameters_optional.py index ca4d2fdfe71df227fae4144884a65dffa0cf2ce7..b1e22d7c8e208d84fed8979e507ab438628f0c8d 100644 --- a/roles/php_website/molecule/default/tests/test_parameters_optional.py +++ b/roles/php_website/molecule/default/tests/test_parameters_optional.py @@ -202,24 +202,6 @@ def test_website_enabled(host): assert config.linked_to == '/etc/nginx/sites-available/parameters-optional.local' -def test_https_enforcement(host): - """ - Tests if HTTPS is (not) being enforced. - """ - - https_enforcement = host.run('curl -I http://parameters-optional.local/') - - 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 = host.run('curl -I https://parameters-optional.local/') - - assert https_enforcement.rc == 0 - assert 'Strict-Transport-Security' not in https_enforcement.stdout - - def test_index_page(host): """ Tests if index page is served correctly (should be php file served statically). diff --git a/roles/php_website/templates/nginx_site.j2 b/roles/php_website/templates/nginx_site.j2 index dddef2f098f714bd41164cab5e7cc4c3d888d938..c0eaddf0e2d06f4cb4ce3c039f5d72db01a68b00 100644 --- a/roles/php_website/templates/nginx_site.j2 +++ b/roles/php_website/templates/nginx_site.j2 @@ -1,4 +1,3 @@ -{% if enforce_https -%} server { # HTTP (plaintext) configuration. listen 80; @@ -8,29 +7,21 @@ server { return 301 https://$host$request_uri; } -{% endif -%} server { # Base settings. root {{ home }}/htdocs/; index {{ index }}; server_name {{ fqdn }}; -{% if not enforce_https %} - # HTTP (plaintext) configuration. - listen 80; - -{% endif %} # HTTPS (TLS) configuration. listen 443 ssl; listen [::]:443 ssl; ssl_certificate_key /etc/ssl/private/{{ fqdn }}_https.key; ssl_certificate /etc/ssl/certs/{{ fqdn }}_https.pem; -{% if enforce_https -%} # Set-up HSTS header for preventing downgrades for users that visited the # site via HTTPS at least once. add_header Strict-Transport-Security "max-age=31536000; includeSubDomains"; -{% endif -%} {% for config in additional_nginx_config -%} # {{ config.comment }} diff --git a/testsite/playbooks/roles/phpinfo/meta/main.yml b/testsite/playbooks/roles/phpinfo/meta/main.yml index 8fe983bd10de76f1c5fc297fe0c304ea3968e179..73e84d882067d5a00d619984ca8d8af48e210191 100644 --- a/testsite/playbooks/roles/phpinfo/meta/main.yml +++ b/testsite/playbooks/roles/phpinfo/meta/main.yml @@ -7,7 +7,6 @@ dependencies: - ^(.*) /index.php admin_uid: 3000 uid: 2000 - enforce_https: false https_tls_key: "{{ lookup('file', inventory_dir + '/tls/phpinfo.' + testsite_domain + '_https.key') }}" https_tls_certificate: "{{ lookup('file', inventory_dir + '/tls/phpinfo.' + testsite_domain + '_https.pem') }}" additional_fpm_config: