Allow to configure max-age for HSTS(HTTP Strict Transport Security)

HSTS is helpful to bring more protection to users, but on the other
hand, it locks down users to use HTTPS only until max-age expires. To
enable HSTS, admins must enable enforce-ssl option and set non-zero
value to hsts-max-age-seconds explicitly.

Content Security Policy (CSP) is not enabled this time. Horizon upstream
may need some work: https://bugs.launchpad.net/horizon/+bug/1618024

Change-Id: I7fd774ba9a1c292d51625d6d36a086b2a531ae75
Partial-Bug: #1713202
This commit is contained in:
Nobuto Murata 2017-10-13 10:53:09 -04:00
parent 47396b52de
commit 1d45c57fd2
4 changed files with 36 additions and 4 deletions

View File

@ -301,6 +301,22 @@ options:
description: |
If True, redirects plain http requests to https port 443. For this option
to have an effect, SSL must be configured.
hsts-max-age-seconds:
type: int
default: 0
description: |
"max-age" parameter for HSTS(HTTP Strict Transport Security)
header. Use with caution since once you set this option, browsers
will remember it so they can only use HTTPS (HTTP connection won't
be allowed) until max-age expires.
.
An example value is one year (31536000). However, a shorter
max-age such as 24 hours (86400) is recommended during initial
rollout in case of any mistakes. For more details on HSTS, refer to:
https://developer.mozilla.org/docs/Web/Security/HTTP_strict_transport_security
.
For this option to have an effect, SSL must be configured and
enforce-ssl option must be true.
database-user:
type: string
default: horizon

View File

@ -209,7 +209,8 @@ class ApacheContext(OSContextGenerator):
ctxt = {
'http_port': 70,
'https_port': 433,
'enforce_ssl': False
'enforce_ssl': False,
'hsts_max_age_seconds': config('hsts-max-age-seconds')
}
if config('enforce-ssl'):

View File

@ -33,6 +33,9 @@
{% if ssl_configured %}
SSLCertificateFile {{ ssl_cert }}
SSLCertificateKeyFile {{ ssl_key }}
{% if enforce_ssl %}
Header set Strict-Transport-Security "max-age={{ hsts_max_age_seconds }}"
{% endif %}
{% else %}
SSLCertificateFile /etc/ssl/certs/ssl-cert-snakeoil.pem
SSLCertificateKeyFile /etc/ssl/private/ssl-cert-snakeoil.key

View File

@ -63,21 +63,33 @@ class TestHorizonContexts(CharmTestCase):
def test_Apachecontext(self):
self.assertEqual(horizon_contexts.ApacheContext()(),
{'http_port': 70, 'https_port': 433,
'enforce_ssl': False})
'enforce_ssl': False,
'hsts_max_age_seconds': 0})
def test_Apachecontext_enforce_ssl(self):
self.test_config.set('enforce-ssl', True)
self.get_cert.return_value = ('cert', 'key')
self.assertEquals(horizon_contexts.ApacheContext()(),
{'http_port': 70, 'https_port': 433,
'enforce_ssl': True})
'enforce_ssl': True,
'hsts_max_age_seconds': 0})
def test_Apachecontext_enforce_ssl_no_cert(self):
self.test_config.set('enforce-ssl', True)
self.get_cert.return_value = (None, 'key')
self.assertEquals(horizon_contexts.ApacheContext()(),
{'http_port': 70, 'https_port': 433,
'enforce_ssl': False})
'enforce_ssl': False,
'hsts_max_age_seconds': 0})
def test_Apachecontext_hsts_max_age_seconds(self):
self.test_config.set('enforce-ssl', True)
self.get_cert.return_value = ('cert', 'key')
self.test_config.set('hsts-max-age-seconds', 15768000)
self.assertEquals(horizon_contexts.ApacheContext()(),
{'http_port': 70, 'https_port': 433,
'enforce_ssl': True,
'hsts_max_age_seconds': 15768000})
@patch.object(horizon_contexts, 'get_ca_cert', lambda: None)
@patch('os.chmod')