mirror of
https://github.com/certbot/certbot.git
synced 2026-06-03 22:08:07 -04:00
Generalized http-header enhancement
This commit is contained in:
parent
2988a09087
commit
04136cfbf2
3 changed files with 56 additions and 23 deletions
|
|
@ -123,7 +123,7 @@ class ApacheConfigurator(augeas_configurator.AugeasConfigurator):
|
|||
self.version = version
|
||||
self.vhosts = None
|
||||
self._enhance_func = {"redirect": self._enable_redirect,
|
||||
"hsts": self._enable_hsts}
|
||||
"http-header": self._set_http_header}
|
||||
|
||||
@property
|
||||
def mod_ssl_conf(self):
|
||||
|
|
@ -682,7 +682,7 @@ class ApacheConfigurator(augeas_configurator.AugeasConfigurator):
|
|||
############################################################################
|
||||
def supported_enhancements(self): # pylint: disable=no-self-use
|
||||
"""Returns currently supported enhancements."""
|
||||
return ["redirect", "hsts"]
|
||||
return ["redirect", "http-header"]
|
||||
|
||||
def enhance(self, domain, enhancement, options=None):
|
||||
"""Enhance configuration.
|
||||
|
|
@ -709,7 +709,8 @@ class ApacheConfigurator(augeas_configurator.AugeasConfigurator):
|
|||
logger.warn("Failed %s for %s", enhancement, domain)
|
||||
raise
|
||||
|
||||
def _enable_hsts(self, ssl_vhost, unused_options):
|
||||
def _set_http_header(self, ssl_vhost, header_name):
|
||||
# TODO REWRITE COMMENT
|
||||
"""Enables the HSTS header on all HTTP responses.
|
||||
|
||||
.. note:: HSTS defends against SSL stripping attacks.
|
||||
|
|
@ -736,18 +737,22 @@ class ApacheConfigurator(augeas_configurator.AugeasConfigurator):
|
|||
if "headers_module" not in self.parser.modules:
|
||||
self.enable_mod("headers")
|
||||
|
||||
# Check if HSTS header is already set
|
||||
self._verify_no_hsts_header(ssl_vhost)
|
||||
# Check if selected header is already set
|
||||
self._verify_no_http_header(ssl_vhost, header_name)
|
||||
|
||||
# Add directives to server
|
||||
self.parser.add_dir(ssl_vhost.path, "Header", constants.HSTS_ARGS)
|
||||
self.save_notes += ("Adding HSTS header to every response from ssl "
|
||||
"vhost in %s\n" % (ssl_vhost.filep))
|
||||
self.parser.add_dir(ssl_vhost.path, "Header",
|
||||
constants.HEADER_ARGS[header_name])
|
||||
|
||||
self.save_notes += ("Adding %s header to ssl vhost in %s\n" %
|
||||
(header_name, ssl_vhost.filep))
|
||||
|
||||
self.save()
|
||||
logger.info("Adding HSTS header to every response from ssl vhost in %s",
|
||||
logger.info("Adding %s header to ssl vhost in %s", header_name,
|
||||
ssl_vhost.filep)
|
||||
|
||||
def _verify_no_hsts_header(self, ssl_vhost):
|
||||
def _verify_no_http_header(self, ssl_vhost, header_name):
|
||||
# TODO revise comment
|
||||
"""Checks to see if existing HSTS settings is in place.
|
||||
|
||||
Checks to see if virtualhost already contains a HSTS header
|
||||
|
|
@ -765,15 +770,10 @@ class ApacheConfigurator(augeas_configurator.AugeasConfigurator):
|
|||
if header_path:
|
||||
# "Existing Header directive for virtualhost"
|
||||
for match in header_path:
|
||||
if match == "Strict-Transport-Security":
|
||||
raise errors.PluginError("Existing HSTS header")
|
||||
|
||||
for match, arg in itertools.izip(header_path, constants.HSTS_ARGS):
|
||||
if self.aug.get(match) != arg:
|
||||
raise errors.PluginError("Unknown Existing HSTS header")
|
||||
raise errors.PluginError("Let's Encrypt has already enabled HSTS")
|
||||
|
||||
|
||||
if self.aug.get(match) == header_name.lower():
|
||||
raise errors.PluginError("Existing %s header" %
|
||||
(header_name))
|
||||
|
||||
def _enable_redirect(self, ssl_vhost, unused_options):
|
||||
"""Redirect all equivalent HTTP traffic to ssl_vhost.
|
||||
|
||||
|
|
|
|||
|
|
@ -28,8 +28,14 @@ REWRITE_HTTPS_ARGS = [
|
|||
"^", "https://%{SERVER_NAME}%{REQUEST_URI}", "[L,QSA,R=permanent]"]
|
||||
"""Apache rewrite rule arguments used for redirections to https vhost"""
|
||||
|
||||
HSTS_ARGS = [
|
||||
"always", "set", "Strict-Transport-Security",
|
||||
|
||||
HSTS_ARGS = ["always", "set", "Strict-Transport-Security",
|
||||
"\"max-age=31536000; includeSubDomains\""]
|
||||
"""Apache header arguments for HSTS"""
|
||||
|
||||
UIR_ARGS = ["always", "set", "Content-Security-Policy",
|
||||
"upgrade-insecure-requests"]
|
||||
|
||||
HEADER_ARGS = {"Strict-Transport-Security" : HSTS_ARGS,
|
||||
"Upgrade-Insecure-Requests" : UIR_ARGS}
|
||||
|
||||
|
|
|
|||
|
|
@ -15,6 +15,7 @@ from letsencrypt import errors
|
|||
from letsencrypt.tests import acme_util
|
||||
|
||||
from letsencrypt_apache import configurator
|
||||
from letsencrypt_apache import constants
|
||||
from letsencrypt_apache import obj
|
||||
|
||||
from letsencrypt_apache.tests import util
|
||||
|
|
@ -509,13 +510,14 @@ class TwoVhost80Test(util.ApacheTest):
|
|||
|
||||
@mock.patch("letsencrypt.le_util.run_script")
|
||||
@mock.patch("letsencrypt.le_util.exe_exists")
|
||||
def test_hsts(self, mock_exe, _):
|
||||
def test_http_header_hsts(self, mock_exe, _):
|
||||
self.config.parser.update_runtime_variables = mock.Mock()
|
||||
self.config.parser.modules.add("mod_ssl.c")
|
||||
mock_exe.return_value = True
|
||||
|
||||
# This will create an ssl vhost for letsencrypt.demo
|
||||
self.config.enhance("letsencrypt.demo", "hsts")
|
||||
self.config.enhance("letsencrypt.demo", "http-header",
|
||||
"Strict-Transport-Security")
|
||||
|
||||
self.assertTrue("headers_module" in self.config.parser.modules)
|
||||
|
||||
|
|
@ -526,6 +528,31 @@ class TwoVhost80Test(util.ApacheTest):
|
|||
# load(). They must be found in sites-available
|
||||
hsts_header = self.config.parser.find_dir(
|
||||
"Header", None, ssl_vhost.path)
|
||||
|
||||
# four args to HSTS header
|
||||
self.assertEqual(len(hsts_header), 4)
|
||||
|
||||
@mock.patch("letsencrypt.le_util.run_script")
|
||||
@mock.patch("letsencrypt.le_util.exe_exists")
|
||||
def test_http_header_hsts_with_conflict(self, mock_exe, _):
|
||||
mock_exe.return_value = True
|
||||
self.config.parser.update_runtime_variables = mock.Mock()
|
||||
self.config.parser.modules.add("mod_ssl.c")
|
||||
|
||||
ssl_vhost = self.config.make_vhost_ssl(self.vh_truth[3])
|
||||
self.config.parser.add_dir(
|
||||
ssl_vhost.path, "Header", constants.HEADER_ARGS[
|
||||
"Strict-Transport-Security"])
|
||||
|
||||
# This will create an ssl vhost for letsencrypt.demo
|
||||
self.config.enhance(self.vh_truth[3].name, "http-header",
|
||||
"Strict-Transport-Security")
|
||||
|
||||
# These are not immediately available in find_dir even with save() and
|
||||
# load(). They must be found in sites-available
|
||||
hsts_header = self.config.parser.find_dir(
|
||||
"Header", None, ssl_vhost.path)
|
||||
|
||||
# four args to HSTS header
|
||||
self.assertEqual(len(hsts_header), 4)
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue