mirror of
https://github.com/certbot/certbot.git
synced 2026-08-04 12:13:34 +02:00
Merge pull request #2537 from letsencrypt/issue_2240
Apache: handle wildcards when matching server names (Issue #2240)
This commit is contained in:
@@ -342,6 +342,21 @@ class ApacheConfigurator(augeas_configurator.AugeasConfigurator):
|
|||||||
self.assoc[target_name] = vhost
|
self.assoc[target_name] = vhost
|
||||||
return vhost
|
return vhost
|
||||||
|
|
||||||
|
def included_in_wildcard(self, names, target_name):
|
||||||
|
"""Helper function to see if alias is covered by wildcard"""
|
||||||
|
target_name = target_name.split(".")[::-1]
|
||||||
|
wildcards = [domain.split(".")[1:] for domain in names if domain.startswith("*")]
|
||||||
|
for wildcard in wildcards:
|
||||||
|
if len(wildcard) > len(target_name):
|
||||||
|
continue
|
||||||
|
for idx, segment in enumerate(wildcard[::-1]):
|
||||||
|
if segment != target_name[idx]:
|
||||||
|
break
|
||||||
|
else:
|
||||||
|
# https://docs.python.org/2/tutorial/controlflow.html#break-and-continue-statements-and-else-clauses-on-loops
|
||||||
|
return True
|
||||||
|
return False
|
||||||
|
|
||||||
def _find_best_vhost(self, target_name):
|
def _find_best_vhost(self, target_name):
|
||||||
"""Finds the best vhost for a target_name.
|
"""Finds the best vhost for a target_name.
|
||||||
|
|
||||||
@@ -351,16 +366,21 @@ class ApacheConfigurator(augeas_configurator.AugeasConfigurator):
|
|||||||
:returns: VHost or None
|
:returns: VHost or None
|
||||||
|
|
||||||
"""
|
"""
|
||||||
# Points 4 - Servername SSL
|
# Points 6 - Servername SSL
|
||||||
# Points 3 - Address name with SSL
|
# Points 5 - Wildcard SSL
|
||||||
# Points 2 - Servername no SSL
|
# Points 4 - Address name with SSL
|
||||||
|
# Points 3 - Servername no SSL
|
||||||
|
# Points 2 - Wildcard no SSL
|
||||||
# Points 1 - Address name with no SSL
|
# Points 1 - Address name with no SSL
|
||||||
best_candidate = None
|
best_candidate = None
|
||||||
best_points = 0
|
best_points = 0
|
||||||
for vhost in self.vhosts:
|
for vhost in self.vhosts:
|
||||||
if vhost.modmacro is True:
|
if vhost.modmacro is True:
|
||||||
continue
|
continue
|
||||||
if target_name in vhost.get_names():
|
names = vhost.get_names()
|
||||||
|
if target_name in names:
|
||||||
|
points = 3
|
||||||
|
elif self.included_in_wildcard(names, target_name):
|
||||||
points = 2
|
points = 2
|
||||||
elif any(addr.get_addr() == target_name for addr in vhost.addrs):
|
elif any(addr.get_addr() == target_name for addr in vhost.addrs):
|
||||||
points = 1
|
points = 1
|
||||||
@@ -370,7 +390,7 @@ class ApacheConfigurator(augeas_configurator.AugeasConfigurator):
|
|||||||
continue # pragma: no cover
|
continue # pragma: no cover
|
||||||
|
|
||||||
if vhost.ssl:
|
if vhost.ssl:
|
||||||
points += 2
|
points += 3
|
||||||
|
|
||||||
if points > best_points:
|
if points > best_points:
|
||||||
best_points = points
|
best_points = points
|
||||||
|
|||||||
@@ -85,7 +85,7 @@ class TwoVhost80Test(util.ApacheTest):
|
|||||||
mock_getutility.notification = mock.MagicMock(return_value=True)
|
mock_getutility.notification = mock.MagicMock(return_value=True)
|
||||||
names = self.config.get_all_names()
|
names = self.config.get_all_names()
|
||||||
self.assertEqual(names, set(
|
self.assertEqual(names, set(
|
||||||
["letsencrypt.demo", "encryption-example.demo", "ip-172-30-0-17"]))
|
["letsencrypt.demo", "encryption-example.demo", "ip-172-30-0-17", "*.blue.purple.com"]))
|
||||||
|
|
||||||
@mock.patch("zope.component.getUtility")
|
@mock.patch("zope.component.getUtility")
|
||||||
@mock.patch("letsencrypt_apache.configurator.socket.gethostbyaddr")
|
@mock.patch("letsencrypt_apache.configurator.socket.gethostbyaddr")
|
||||||
@@ -103,7 +103,7 @@ class TwoVhost80Test(util.ApacheTest):
|
|||||||
self.config.vhosts.append(vhost)
|
self.config.vhosts.append(vhost)
|
||||||
|
|
||||||
names = self.config.get_all_names()
|
names = self.config.get_all_names()
|
||||||
self.assertEqual(len(names), 5)
|
self.assertEqual(len(names), 6)
|
||||||
self.assertTrue("zombo.com" in names)
|
self.assertTrue("zombo.com" in names)
|
||||||
self.assertTrue("google.com" in names)
|
self.assertTrue("google.com" in names)
|
||||||
self.assertTrue("letsencrypt.demo" in names)
|
self.assertTrue("letsencrypt.demo" in names)
|
||||||
@@ -124,7 +124,7 @@ class TwoVhost80Test(util.ApacheTest):
|
|||||||
|
|
||||||
"""
|
"""
|
||||||
vhs = self.config.get_virtual_hosts()
|
vhs = self.config.get_virtual_hosts()
|
||||||
self.assertEqual(len(vhs), 6)
|
self.assertEqual(len(vhs), 7)
|
||||||
found = 0
|
found = 0
|
||||||
|
|
||||||
for vhost in vhs:
|
for vhost in vhs:
|
||||||
@@ -135,7 +135,7 @@ class TwoVhost80Test(util.ApacheTest):
|
|||||||
else:
|
else:
|
||||||
raise Exception("Missed: %s" % vhost) # pragma: no cover
|
raise Exception("Missed: %s" % vhost) # pragma: no cover
|
||||||
|
|
||||||
self.assertEqual(found, 6)
|
self.assertEqual(found, 7)
|
||||||
|
|
||||||
# Handle case of non-debian layout get_virtual_hosts
|
# Handle case of non-debian layout get_virtual_hosts
|
||||||
with mock.patch(
|
with mock.patch(
|
||||||
@@ -143,7 +143,7 @@ class TwoVhost80Test(util.ApacheTest):
|
|||||||
) as mock_conf:
|
) as mock_conf:
|
||||||
mock_conf.return_value = False
|
mock_conf.return_value = False
|
||||||
vhs = self.config.get_virtual_hosts()
|
vhs = self.config.get_virtual_hosts()
|
||||||
self.assertEqual(len(vhs), 6)
|
self.assertEqual(len(vhs), 7)
|
||||||
|
|
||||||
@mock.patch("letsencrypt_apache.display_ops.select_vhost")
|
@mock.patch("letsencrypt_apache.display_ops.select_vhost")
|
||||||
def test_choose_vhost_none_avail(self, mock_select):
|
def test_choose_vhost_none_avail(self, mock_select):
|
||||||
@@ -186,6 +186,20 @@ class TwoVhost80Test(util.ApacheTest):
|
|||||||
self.assertRaises(
|
self.assertRaises(
|
||||||
errors.PluginError, self.config.choose_vhost, "none.com")
|
errors.PluginError, self.config.choose_vhost, "none.com")
|
||||||
|
|
||||||
|
def test_choosevhost_select_vhost_with_wildcard(self):
|
||||||
|
chosen_vhost = self.config.choose_vhost("blue.purple.com", temp=True)
|
||||||
|
self.assertEqual(self.vh_truth[6], chosen_vhost)
|
||||||
|
|
||||||
|
def test_findbest_continues_on_short_domain(self):
|
||||||
|
# pylint: disable=protected-access
|
||||||
|
chosen_vhost = self.config._find_best_vhost("purple.com")
|
||||||
|
self.assertEqual(None, chosen_vhost)
|
||||||
|
|
||||||
|
def test_findbest_continues_on_long_domain(self):
|
||||||
|
# pylint: disable=protected-access
|
||||||
|
chosen_vhost = self.config._find_best_vhost("green.red.purple.com")
|
||||||
|
self.assertEqual(None, chosen_vhost)
|
||||||
|
|
||||||
def test_find_best_vhost(self):
|
def test_find_best_vhost(self):
|
||||||
# pylint: disable=protected-access
|
# pylint: disable=protected-access
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
@@ -211,6 +225,7 @@ class TwoVhost80Test(util.ApacheTest):
|
|||||||
self.config.vhosts = [
|
self.config.vhosts = [
|
||||||
vh for vh in self.config.vhosts
|
vh for vh in self.config.vhosts
|
||||||
if vh.name not in ["letsencrypt.demo", "encryption-example.demo"]
|
if vh.name not in ["letsencrypt.demo", "encryption-example.demo"]
|
||||||
|
and "*.blue.purple.com" not in vh.aliases
|
||||||
]
|
]
|
||||||
|
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
@@ -218,7 +233,7 @@ class TwoVhost80Test(util.ApacheTest):
|
|||||||
|
|
||||||
def test_non_default_vhosts(self):
|
def test_non_default_vhosts(self):
|
||||||
# pylint: disable=protected-access
|
# pylint: disable=protected-access
|
||||||
self.assertEqual(len(self.config._non_default_vhosts()), 4)
|
self.assertEqual(len(self.config._non_default_vhosts()), 5)
|
||||||
|
|
||||||
def test_is_site_enabled(self):
|
def test_is_site_enabled(self):
|
||||||
"""Test if site is enabled.
|
"""Test if site is enabled.
|
||||||
@@ -524,7 +539,7 @@ class TwoVhost80Test(util.ApacheTest):
|
|||||||
self.assertEqual(self.config.is_name_vhost(self.vh_truth[0]),
|
self.assertEqual(self.config.is_name_vhost(self.vh_truth[0]),
|
||||||
self.config.is_name_vhost(ssl_vhost))
|
self.config.is_name_vhost(ssl_vhost))
|
||||||
|
|
||||||
self.assertEqual(len(self.config.vhosts), 7)
|
self.assertEqual(len(self.config.vhosts), 8)
|
||||||
|
|
||||||
def test_clean_vhost_ssl(self):
|
def test_clean_vhost_ssl(self):
|
||||||
# pylint: disable=protected-access
|
# pylint: disable=protected-access
|
||||||
@@ -942,7 +957,7 @@ class TwoVhost80Test(util.ApacheTest):
|
|||||||
|
|
||||||
# pylint: disable=protected-access
|
# pylint: disable=protected-access
|
||||||
self.config._enable_redirect(self.vh_truth[1], "")
|
self.config._enable_redirect(self.vh_truth[1], "")
|
||||||
self.assertEqual(len(self.config.vhosts), 7)
|
self.assertEqual(len(self.config.vhosts), 8)
|
||||||
|
|
||||||
def test_create_own_redirect_for_old_apache_version(self):
|
def test_create_own_redirect_for_old_apache_version(self):
|
||||||
self.config.parser.modules.add("rewrite_module")
|
self.config.parser.modules.add("rewrite_module")
|
||||||
@@ -953,7 +968,7 @@ class TwoVhost80Test(util.ApacheTest):
|
|||||||
|
|
||||||
# pylint: disable=protected-access
|
# pylint: disable=protected-access
|
||||||
self.config._enable_redirect(self.vh_truth[1], "")
|
self.config._enable_redirect(self.vh_truth[1], "")
|
||||||
self.assertEqual(len(self.config.vhosts), 7)
|
self.assertEqual(len(self.config.vhosts), 8)
|
||||||
|
|
||||||
def test_sift_line(self):
|
def test_sift_line(self):
|
||||||
# pylint: disable=protected-access
|
# pylint: disable=protected-access
|
||||||
|
|||||||
+13
@@ -0,0 +1,13 @@
|
|||||||
|
<VirtualHost *:80>
|
||||||
|
|
||||||
|
ServerName ip-172-30-0-17
|
||||||
|
ServerAdmin webmaster@localhost
|
||||||
|
DocumentRoot /var/www/html
|
||||||
|
ServerAlias *.blue.purple.com
|
||||||
|
|
||||||
|
ErrorLog ${APACHE_LOG_DIR}/error.log
|
||||||
|
CustomLog ${APACHE_LOG_DIR}/access.log combined
|
||||||
|
|
||||||
|
</VirtualHost>
|
||||||
|
|
||||||
|
# vim: syntax=apache ts=4 sw=4 sts=4 sr noet
|
||||||
@@ -150,7 +150,12 @@ def get_vh_truth(temp_dir, config_name):
|
|||||||
os.path.join(prefix, "default-ssl-port-only.conf"),
|
os.path.join(prefix, "default-ssl-port-only.conf"),
|
||||||
os.path.join(aug_pre, ("default-ssl-port-only.conf/"
|
os.path.join(aug_pre, ("default-ssl-port-only.conf/"
|
||||||
"IfModule/VirtualHost")),
|
"IfModule/VirtualHost")),
|
||||||
set([obj.Addr.fromstring("_default_:443")]), True, False)
|
set([obj.Addr.fromstring("_default_:443")]), True, False),
|
||||||
|
obj.VirtualHost(
|
||||||
|
os.path.join(prefix, "wildcard.conf"),
|
||||||
|
os.path.join(aug_pre, "wildcard.conf/VirtualHost"),
|
||||||
|
set([obj.Addr.fromstring("*:80")]), False, False,
|
||||||
|
"ip-172-30-0-17", aliases=["*.blue.purple.com"])
|
||||||
]
|
]
|
||||||
return vh_truth
|
return vh_truth
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user