mirror of
https://github.com/certbot/certbot.git
synced 2026-05-28 04:34:11 -04:00
Merge pull request #2146 from letsencrypt/apache-redirect
Copy only relevant lines from http vhost to ssl vhost skeleton v2
This commit is contained in:
commit
ff7e765611
2 changed files with 96 additions and 3 deletions
|
|
@ -8,6 +8,7 @@ import shutil
|
|||
import socket
|
||||
import time
|
||||
|
||||
import zope.component
|
||||
import zope.interface
|
||||
|
||||
from acme import challenges
|
||||
|
|
@ -709,6 +710,39 @@ class ApacheConfigurator(augeas_configurator.AugeasConfigurator):
|
|||
else:
|
||||
return non_ssl_vh_fp + self.conf("le_vhost_ext")
|
||||
|
||||
def _sift_line(self, line):
|
||||
"""Decides whether a line should be copied to a SSL vhost.
|
||||
|
||||
A canonical example of when sifting a line is required:
|
||||
When the http vhost contains a RewriteRule that unconditionally
|
||||
redirects any request to the https version of the same site.
|
||||
e.g:
|
||||
RewriteRule ^ https://%{SERVER_NAME}%{REQUEST_URI} [L,QSA,R=permanent]
|
||||
Copying the above line to the ssl vhost would cause a
|
||||
redirection loop.
|
||||
|
||||
:param str line: a line extracted from the http vhost.
|
||||
|
||||
:returns: True - don't copy line from http vhost to SSL vhost.
|
||||
:rtype: bool
|
||||
|
||||
"""
|
||||
if not line.lstrip().startswith("RewriteRule"):
|
||||
return False
|
||||
|
||||
# According to: http://httpd.apache.org/docs/2.4/rewrite/flags.html
|
||||
# The syntax of a RewriteRule is:
|
||||
# RewriteRule pattern target [Flag1,Flag2,Flag3]
|
||||
# i.e. target is required, so it must exist.
|
||||
target = line.split()[2].strip()
|
||||
|
||||
# target may be surrounded with quotes
|
||||
if target[0] in ("'", '"') and target[0] == target[-1]:
|
||||
target = target[1:-1]
|
||||
|
||||
# Sift line if it redirects the request to a HTTPS site
|
||||
return target.startswith("https://")
|
||||
|
||||
def _copy_create_ssl_vhost_skeleton(self, avail_fp, ssl_fp):
|
||||
"""Copies over existing Vhost with IfModule mod_ssl.c> skeleton.
|
||||
|
||||
|
|
@ -721,18 +755,38 @@ class ApacheConfigurator(augeas_configurator.AugeasConfigurator):
|
|||
# First register the creation so that it is properly removed if
|
||||
# configuration is rolled back
|
||||
self.reverter.register_file_creation(False, ssl_fp)
|
||||
sift = False
|
||||
|
||||
try:
|
||||
with open(avail_fp, "r") as orig_file:
|
||||
with open(ssl_fp, "w") as new_file:
|
||||
new_file.write("<IfModule mod_ssl.c>\n")
|
||||
for line in orig_file:
|
||||
new_file.write(line)
|
||||
if self._sift_line(line):
|
||||
if not sift:
|
||||
new_file.write(
|
||||
"# Some rewrite rules in this file were "
|
||||
"were disabled on your HTTPS site,\n"
|
||||
"# because they have the potential to "
|
||||
"create redirection loops.\n")
|
||||
sift = True
|
||||
new_file.write("# " + line)
|
||||
else:
|
||||
new_file.write(line)
|
||||
new_file.write("</IfModule>\n")
|
||||
except IOError:
|
||||
logger.fatal("Error writing/reading to file in make_vhost_ssl")
|
||||
raise errors.PluginError("Unable to write/read in make_vhost_ssl")
|
||||
|
||||
if sift:
|
||||
reporter = zope.component.getUtility(interfaces.IReporter)
|
||||
reporter.add_message(
|
||||
"Some rewrite rules copied from {0} were disabled in the "
|
||||
"vhost for your HTTPS site located at {1} because they have "
|
||||
"the potential to create redirection loops.".format(avail_fp,
|
||||
ssl_fp),
|
||||
reporter.MEDIUM_PRIORITY)
|
||||
|
||||
def _update_ssl_vhosts_addrs(self, vh_path):
|
||||
ssl_addrs = set()
|
||||
ssl_addr_p = self.aug.match(vh_path + "/arg")
|
||||
|
|
|
|||
|
|
@ -847,7 +847,8 @@ class TwoVhost80Test(util.ApacheTest):
|
|||
|
||||
# Create a preexisting rewrite rule
|
||||
self.config.parser.add_dir(
|
||||
self.vh_truth[3].path, "RewriteRule", ["Unknown"])
|
||||
self.vh_truth[3].path, "RewriteRule", ["UnknownPattern",
|
||||
"UnknownTarget"])
|
||||
self.config.save()
|
||||
|
||||
# This will create an ssl vhost for letsencrypt.demo
|
||||
|
|
@ -862,7 +863,7 @@ class TwoVhost80Test(util.ApacheTest):
|
|||
|
||||
self.assertEqual(len(rw_engine), 1)
|
||||
# three args to rw_rule + 1 arg for the pre existing rewrite
|
||||
self.assertEqual(len(rw_rule), 4)
|
||||
self.assertEqual(len(rw_rule), 5)
|
||||
|
||||
self.assertTrue(rw_engine[0].startswith(self.vh_truth[3].path))
|
||||
self.assertTrue(rw_rule[0].startswith(self.vh_truth[3].path))
|
||||
|
|
@ -911,6 +912,44 @@ class TwoVhost80Test(util.ApacheTest):
|
|||
self.config._enable_redirect(self.vh_truth[1], "") # pylint: disable=protected-access
|
||||
self.assertEqual(len(self.config.vhosts), 7)
|
||||
|
||||
def test_sift_line(self):
|
||||
# pylint: disable=protected-access
|
||||
small_quoted_target = "RewriteRule ^ \"http://\""
|
||||
self.assertFalse(self.config._sift_line(small_quoted_target))
|
||||
|
||||
https_target = "RewriteRule ^ https://satoshi"
|
||||
self.assertTrue(self.config._sift_line(https_target))
|
||||
|
||||
normal_target = "RewriteRule ^/(.*) http://www.a.com:1234/$1 [L,R]"
|
||||
self.assertFalse(self.config._sift_line(normal_target))
|
||||
|
||||
@mock.patch("letsencrypt_apache.configurator.zope.component.getUtility")
|
||||
def test_make_vhost_ssl_with_existing_rewrite_rule(self, mock_get_utility):
|
||||
self.config.parser.modules.add("rewrite_module")
|
||||
|
||||
http_vhost = self.vh_truth[0]
|
||||
|
||||
self.config.parser.add_dir(
|
||||
http_vhost.path, "RewriteEngine", "on")
|
||||
|
||||
self.config.parser.add_dir(
|
||||
http_vhost.path, "RewriteRule",
|
||||
["^",
|
||||
"https://%{SERVER_NAME}%{REQUEST_URI}",
|
||||
"[L,QSA,R=permanent]"])
|
||||
self.config.save()
|
||||
|
||||
ssl_vhost = self.config.make_vhost_ssl(self.vh_truth[0])
|
||||
|
||||
self.assertTrue(self.config.parser.find_dir(
|
||||
"RewriteEngine", "on", ssl_vhost.path, False))
|
||||
|
||||
conf_text = open(ssl_vhost.filep).read()
|
||||
commented_rewrite_rule = \
|
||||
"# RewriteRule ^ https://%{SERVER_NAME}%{REQUEST_URI} [L,QSA,R=permanent]"
|
||||
self.assertTrue(commented_rewrite_rule in conf_text)
|
||||
mock_get_utility().add_message.assert_called_once_with(mock.ANY,
|
||||
mock.ANY)
|
||||
|
||||
def get_achalls(self):
|
||||
"""Return testing achallenges."""
|
||||
|
|
|
|||
Loading…
Reference in a new issue