Commit b71cf56e authored by Łukasz Nowak's avatar Łukasz Nowak

caddy-frontend: Implement Strict-Transport-Security

parent 7287fbd8
......@@ -14,7 +14,7 @@
# not need these here).
[template]
filename = instance.cfg.in
md5sum = 04015a7a552285984d091293ef573fb9
md5sum = de69a8c408ce4f228fc22eacb7e96657
[profile-common]
filename = instance-common.cfg.in
......@@ -26,11 +26,11 @@ md5sum = a6a626fd1579fd1d4b80ea67433ca16a
[profile-caddy-replicate]
filename = instance-apache-replicate.cfg.in
md5sum = acd4ab3d3df5ce46ece6f58b126f1665
md5sum = 7cb8157d2b368ab3b281ea42f743eb9c
[profile-slave-list]
_update_hash_filename_ = templates/apache-custom-slave-list.cfg.in
md5sum = c48c1be68d547540971f874e78e5c96d
md5sum = 772c04c165fdae91299fd909e061f926
[profile-replicate-publish-slave-information]
_update_hash_filename_ = templates/replicate-publish-slave-information.cfg.in
......@@ -46,7 +46,7 @@ md5sum = 88af61e7abbf30dc99a1a2526161128d
[template-default-slave-virtualhost]
_update_hash_filename_ = templates/default-virtualhost.conf.in
md5sum = 1eb9f415229aa74de83f6d8660cac5a8
md5sum = a0ae858a3db8825c22d33d323392f588
[template-backend-haproxy-configuration]
_update_hash_filename_ = templates/backend-haproxy.cfg.in
......
......@@ -21,6 +21,7 @@
'ciphers',
'request-timeout',
'authenticate-to-backend',
'strict-transport-security',
]
%}
{% set aikc_enabled = slapparameter_dict.get('automatic-internal-kedifa-caucase-csr', 'true').lower() in TRUE_VALUES %}
......@@ -167,6 +168,11 @@ context =
{% endif %}
{% endfor %}
{% endif %}
{# Check strict-transport-security #}
{% set strict_transport_security = (slave.get('strict-transport-security') or '0') | int(false) %}
{% if strict_transport_security is false or strict_transport_security < 0 %}
{% do slave_error_list.append('Wrong strict-transport-security %s' % (slave.get('strict-transport-security'),)) %}
{% endif %}
{% set custom_domain = slave.get('custom_domain') %}
{% if custom_domain and custom_domain in used_host_list %}
{% do slave_error_list.append('custom_domain %r clashes' % (custom_domain,)) %}
......
......@@ -107,6 +107,12 @@
],
"title": "Authenticate to backend",
"type": "string"
},
"strict-transport-security": {
"title": "Strict Transport Security",
"description": "Enables Strict Transport Security (HSTS) on the slave, the default 0 results with option disabled. More information: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Strict-Transport-Security",
"default": "0",
"type": "integer"
}
},
"title": "Input Parameters",
......
......@@ -285,6 +285,32 @@
"description": "Amount of bad responses from the backend to consider it down.",
"default": "1",
"type": "integer"
},
"strict-transport-security": {
"title": "Strict Transport Security",
"description": "Enables Strict Transport Security (HSTS) on the slave, the default 0 results with option disabled. More information: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Strict-Transport-Security",
"default": "0",
"type": "integer"
},
"strict-transport-security-sub-domains": {
"title": "Strict Transport Security Sub Domains",
"description": "Configures Strict Transport Security for sub domains.",
"enum": [
"false",
"true"
],
"type": "string",
"default": "false"
},
"strict-transport-security-preload": {
"title": "Strict Transport Security Preload",
"description": "Configures Strict Transport Security preload mechanism.",
"enum": [
"false",
"true"
],
"type": "string",
"default": "false"
}
},
"title": "Input Parameters",
......
......@@ -106,3 +106,4 @@ configuration.backend-haproxy-statistic-port = 21444
configuration.authenticate-to-backend = False
configuration.rotate-num = 4000
configuration.slave-introspection-https-port = 22443
configuration.strict-transport-security = 0
......@@ -62,7 +62,7 @@ context =
{%- for key in ['https-only', 'websocket-transparent'] %}
{%- do slave_instance.__setitem__(key, ('' ~ slave_instance.get(key, 'true')).lower() in TRUE_VALUES) %}
{%- endfor %}
{%- for key in ['enable_cache', 'disable-no-cache-request', 'disable-via-header', 'prefer-gzip-encoding-to-backend'] %}
{%- for key in ['enable_cache', 'disable-no-cache-request', 'disable-via-header', 'prefer-gzip-encoding-to-backend', 'strict-transport-security-sub-domains', 'strict-transport-security-preload'] %}
{%- do slave_instance.__setitem__(key, ('' ~ slave_instance.get(key, 'false')).lower() in TRUE_VALUES) %}
{%- endfor %}
{%- for key in ['disabled-cookie-list'] %}
......@@ -128,11 +128,12 @@ context =
{%- do part_list.extend([slave_section_title]) %}
{%- set slave_log_folder = '${logrotate-directory:logrotate-backup}/' + slave_reference + "-logs" %}
{#- Pass backend timeout values #}
{%- for key in ['backend-connect-timeout', 'backend-connect-retries', 'request-timeout', 'authenticate-to-backend'] %}
{%- for key in ['backend-connect-timeout', 'backend-connect-retries', 'request-timeout', 'authenticate-to-backend', 'strict-transport-security'] %}
{%- if slave_instance.get(key, '') == '' %}
{%- do slave_instance.__setitem__(key, configuration[key]) %}
{%- endif %}
{%- endfor %}
{%- do slave_instance.__setitem__('strict-transport-security', int(slave_instance['strict-transport-security'])) %}
{%- do slave_instance.__setitem__('authenticate-to-backend', ('' ~ slave_instance.get('authenticate-to-backend', '')).lower() in TRUE_VALUES) %}
{#- Setup active check #}
{%- do slave_instance.__setitem__('backend-active-check', ('' ~ slave_instance.get('backend-active-check', '')).lower() in TRUE_VALUES) %}
......
......@@ -18,6 +18,21 @@
try_interval 250ms
{%- endmacro %} {# proxy_header #}
{%- macro hsts_header(tls) %}
{%- if tls %}
{%- if slave_parameter['strict-transport-security'] > 0 %}
{%- set strict_transport_security = ['max-age=%i' % (slave_parameter['strict-transport-security'],)] %}
{%- if slave_parameter['strict-transport-security-sub-domains'] %}
{%- do strict_transport_security.append('; includeSubDomains') %}
{%- endif %}
{%- if slave_parameter['strict-transport-security-preload'] %}
{%- do strict_transport_security.append('; preload') %}
{%- endif %}
header_downstream Strict-Transport-Security "{{ ''.join(strict_transport_security) }}"
{%- endif %}
{%- endif %}
{%- endmacro %} {# hsts_header #}
{%- for tls in [True, False] %}
{%- if tls %}
{%- set backend_url = slave_parameter.get('backend-https-url', slave_parameter['backend-http-url']) %}
......@@ -82,6 +97,7 @@
# {{ proxy_comment }}
proxy "/{{ proxy_name }}" {{ backend_url }} {
{{ proxy_header() }}
{{ hsts_header(tls) }}
{%- if proxy_name == 'prefer-gzip' %}
without /prefer-gzip
header_upstream Accept-Encoding gzip
......@@ -147,6 +163,7 @@
{%- elif slave_parameter['type'] == 'notebook' %}
proxy / {{ backend_url }} {
{{ proxy_header() }}
{{ hsts_header(tls) }}
transparent
}
rewrite {
......@@ -155,6 +172,7 @@
}
proxy /proxy/ {{ backend_url }} {
{{ proxy_header() }}
{{ hsts_header(tls) }}
transparent
websocket
without /proxy/
......@@ -163,6 +181,7 @@
{%- if slave_parameter['websocket-path-list'] %}
proxy / {{ backend_url }} {
{{ proxy_header() }}
{{ hsts_header(tls) }}
{%- if slave_parameter['websocket-transparent'] %}
transparent
{%- else %}
......@@ -172,6 +191,7 @@
{%- for websocket_path in slave_parameter['websocket-path-list'] %}
proxy "/{{ websocket_path }}" {{ backend_url }} {
{{ proxy_header() }}
{{ hsts_header(tls) }}
websocket
{%- if slave_parameter['websocket-transparent'] %}
transparent
......@@ -183,6 +203,7 @@
{%- else %}
proxy / {{ backend_url }} {
{{ proxy_header() }}
{{ hsts_header(tls) }}
websocket
{%- if slave_parameter['websocket-transparent'] %}
transparent
......@@ -205,6 +226,7 @@
# {{ proxy_comment }}
proxy "/{{ proxy_name }}" {{ backend_url }} {
{{ proxy_header() }}
{{ hsts_header(tls) }}
{%- if proxy_name == 'prefer-gzip' %}
without /prefer-gzip
header_upstream Accept-Encoding gzip
......
......@@ -1247,18 +1247,26 @@ class TestSlave(SlaveHttpFrontendTestCase, TestDataMixin):
'backend-connect-timeout': 10,
'backend-connect-retries': 5,
'request-timeout': 15,
'strict-transport-security': '200',
'strict-transport-security-sub-domains': True,
'strict-transport-security-preload': True,
},
'server-alias': {
'url': cls.backend_url,
'server-alias': 'alias1.example.com alias2.example.com',
'strict-transport-security': '200',
},
'server-alias-empty': {
'url': cls.backend_url,
'server-alias': '',
'strict-transport-security': '200',
'strict-transport-security-sub-domains': True,
},
'server-alias-wildcard': {
'url': cls.backend_url,
'server-alias': '*.alias1.example.com',
'strict-transport-security': '200',
'strict-transport-security-preload': True,
},
'server-alias-duplicated': {
'url': cls.backend_url,
......@@ -1828,6 +1836,7 @@ class TestSlave(SlaveHttpFrontendTestCase, TestDataMixin):
self.certificate_pem,
der2pem(result.peercert))
self.assertNotIn('Strict-Transport-Security', result.headers)
self.assertEqualResultJson(result, 'Path', '/test-path/deeper')
try:
......@@ -2294,6 +2303,8 @@ class TestSlave(SlaveHttpFrontendTestCase, TestDataMixin):
self.certificate_pem,
der2pem(result.peercert))
self.assertEqual(
'max-age=200', result.headers['Strict-Transport-Security'])
self.assertEqualResultJson(result, 'Path', '/test-path/deeper')
result = fakeHTTPSResult(
......@@ -2304,12 +2315,16 @@ class TestSlave(SlaveHttpFrontendTestCase, TestDataMixin):
self.certificate_pem,
der2pem(result.peercert))
self.assertEqual(
'max-age=200', result.headers['Strict-Transport-Security'])
self.assertEqualResultJson(result, 'Path', '/test-path/deeper')
result = fakeHTTPSResult(
'alias2.example.com',
'test-path/deep/.././deeper')
self.assertEqual(
'max-age=200', result.headers['Strict-Transport-Security'])
self.assertEqual(
self.certificate_pem,
der2pem(result.peercert))
......@@ -2330,6 +2345,9 @@ class TestSlave(SlaveHttpFrontendTestCase, TestDataMixin):
self.certificate_pem,
der2pem(result.peercert))
self.assertEqual(
'max-age=200; includeSubDomains',
result.headers['Strict-Transport-Security'])
self.assertEqualResultJson(result, 'Path', '/test-path/deeper')
try:
......@@ -2369,6 +2387,9 @@ class TestSlave(SlaveHttpFrontendTestCase, TestDataMixin):
self.certificate_pem,
der2pem(result.peercert))
self.assertEqual(
'max-age=200; preload',
result.headers['Strict-Transport-Security'])
self.assertEqualResultJson(result, 'Path', '/test-path')
result = fakeHTTPSResult(
......@@ -2378,6 +2399,9 @@ class TestSlave(SlaveHttpFrontendTestCase, TestDataMixin):
self.certificate_pem,
der2pem(result.peercert))
self.assertEqual(
'max-age=200; preload',
result.headers['Strict-Transport-Security'])
self.assertEqualResultJson(result, 'Path', '/test-path')
def test_server_alias_duplicated(self):
......@@ -4621,6 +4645,10 @@ class TestSlave(SlaveHttpFrontendTestCase, TestDataMixin):
self.certificate_pem,
der2pem(result.peercert))
self.assertEqual(
'max-age=200; includeSubDomains; preload',
result.headers['Strict-Transport-Security'])
self.assertEqualResultJson(result, 'Path', '/https/test-path/deeper')
result_http = fakeHTTPResult(
......@@ -4632,6 +4660,8 @@ class TestSlave(SlaveHttpFrontendTestCase, TestDataMixin):
result_http.status_code
)
self.assertNotIn('Strict-Transport-Security', result_http.headers)
self.assertEqual(
'https://urlhttpsurl.example.com:%s/test-path/deeper' % (HTTP_PORT,),
result_http.headers['Location']
......@@ -6983,6 +7013,7 @@ class TestPassedRequestParameter(HttpFrontendTestCase):
'ciphers': 'ciphers',
'request-timeout': 100,
'authenticate-to-backend': True,
'strict-transport-security': 200,
# specific parameters
'-frontend-config-1-ram-cache-size': '512K',
'-frontend-config-2-ram-cache-size': '256K',
......@@ -7080,7 +7111,8 @@ class TestPassedRequestParameter(HttpFrontendTestCase):
u'ram-cache-size': u'512K',
u're6st-verification-url': u're6st-verification-url',
u'request-timeout': u'100',
u'slave-kedifa-information': u'{}'
u'slave-kedifa-information': u'{}',
u'strict-transport-security': u'200'
},
'caddy-frontend-2': {
'X-software_release_url': self.frontend_2_sr,
......@@ -7107,7 +7139,8 @@ class TestPassedRequestParameter(HttpFrontendTestCase):
u'ram-cache-size': u'256K',
u're6st-verification-url': u're6st-verification-url',
u'request-timeout': u'100',
u'slave-kedifa-information': u'{}'
u'slave-kedifa-information': u'{}',
u'strict-transport-security': u'200'
},
'caddy-frontend-3': {
'X-software_release_url': self.frontend_3_sr,
......@@ -7133,7 +7166,8 @@ class TestPassedRequestParameter(HttpFrontendTestCase):
u'port': u'11443',
u're6st-verification-url': u're6st-verification-url',
u'request-timeout': u'100',
u'slave-kedifa-information': u'{}'
u'slave-kedifa-information': u'{}',
u'strict-transport-security': u'200'
},
'kedifa': {
'X-software_release_url': self.kedifa_sr,
......@@ -7179,7 +7213,8 @@ class TestPassedRequestParameter(HttpFrontendTestCase):
'request-timeout': '100',
'root_instance_title': 'testing partition 0',
'slap_software_type': 'RootSoftwareInstance',
'slave_instance_list': []
'slave_instance_list': [],
'strict-transport-security': '200'
}
}
self.assertEqual(
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment