Skip to content

Commit

Permalink
Merge pull request certbot#2537 from letsencrypt/issue_2240
Browse files Browse the repository at this point in the history
Apache: handle wildcards when matching server names (Issue certbot#2240)
  • Loading branch information
bmw committed Feb 27, 2016
2 parents 21e02b1 + e46ce30 commit d674a74
Show file tree
Hide file tree
Showing 4 changed files with 68 additions and 15 deletions.
30 changes: 25 additions & 5 deletions letsencrypt-apache/letsencrypt_apache/configurator.py
Original file line number Diff line number Diff line change
Expand Up @@ -342,6 +342,21 @@ def _choose_vhost_from_list(self, target_name, temp=False):
self.assoc[target_name] = 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):
"""Finds the best vhost for a target_name.
Expand All @@ -351,16 +366,21 @@ def _find_best_vhost(self, target_name):
:returns: VHost or None
"""
# Points 4 - Servername SSL
# Points 3 - Address name with SSL
# Points 2 - Servername no SSL
# Points 6 - Servername SSL
# Points 5 - Wildcard SSL
# Points 4 - Address name with SSL
# Points 3 - Servername no SSL
# Points 2 - Wildcard no SSL
# Points 1 - Address name with no SSL
best_candidate = None
best_points = 0
for vhost in self.vhosts:
if vhost.modmacro is True:
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
elif any(addr.get_addr() == target_name for addr in vhost.addrs):
points = 1
Expand All @@ -370,7 +390,7 @@ def _find_best_vhost(self, target_name):
continue # pragma: no cover

if vhost.ssl:
points += 2
points += 3

if points > best_points:
best_points = points
Expand Down
33 changes: 24 additions & 9 deletions letsencrypt-apache/letsencrypt_apache/tests/configurator_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@ def test_get_all_names(self, mock_getutility):
mock_getutility.notification = mock.MagicMock(return_value=True)
names = self.config.get_all_names()
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("letsencrypt_apache.configurator.socket.gethostbyaddr")
Expand All @@ -103,7 +103,7 @@ def test_get_all_names_addrs(self, mock_gethost, mock_getutility):
self.config.vhosts.append(vhost)

names = self.config.get_all_names()
self.assertEqual(len(names), 5)
self.assertEqual(len(names), 6)
self.assertTrue("zombo.com" in names)
self.assertTrue("google.com" in names)
self.assertTrue("letsencrypt.demo" in names)
Expand All @@ -124,7 +124,7 @@ def test_get_virtual_hosts(self):
"""
vhs = self.config.get_virtual_hosts()
self.assertEqual(len(vhs), 6)
self.assertEqual(len(vhs), 7)
found = 0

for vhost in vhs:
Expand All @@ -135,15 +135,15 @@ def test_get_virtual_hosts(self):
else:
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
with mock.patch(
"letsencrypt_apache.configurator.ApacheConfigurator.conf"
) as mock_conf:
mock_conf.return_value = False
vhs = self.config.get_virtual_hosts()
self.assertEqual(len(vhs), 6)
self.assertEqual(len(vhs), 7)

@mock.patch("letsencrypt_apache.display_ops.select_vhost")
def test_choose_vhost_none_avail(self, mock_select):
Expand Down Expand Up @@ -186,6 +186,20 @@ def test_choose_vhost_select_vhost_conflicting_non_ssl(self, mock_select):
self.assertRaises(
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):
# pylint: disable=protected-access
self.assertEqual(
Expand All @@ -211,14 +225,15 @@ def test_find_best_vhost_default(self):
self.config.vhosts = [
vh for vh in self.config.vhosts
if vh.name not in ["letsencrypt.demo", "encryption-example.demo"]
and "*.blue.purple.com" not in vh.aliases
]

self.assertEqual(
self.config._find_best_vhost("example.demo"), self.vh_truth[2])

def test_non_default_vhosts(self):
# 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):
"""Test if site is enabled.
Expand Down Expand Up @@ -524,7 +539,7 @@ def test_make_vhost_ssl(self):
self.assertEqual(self.config.is_name_vhost(self.vh_truth[0]),
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):
# pylint: disable=protected-access
Expand Down Expand Up @@ -942,7 +957,7 @@ def test_create_own_redirect(self):

# pylint: disable=protected-access
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):
self.config.parser.modules.add("rewrite_module")
Expand All @@ -953,7 +968,7 @@ def test_create_own_redirect_for_old_apache_version(self):

# pylint: disable=protected-access
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):
# pylint: disable=protected-access
Expand Down
Original file line number Diff line number Diff line change
@@ -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
7 changes: 6 additions & 1 deletion letsencrypt-apache/letsencrypt_apache/tests/util.py
Original file line number Diff line number Diff line change
Expand Up @@ -150,7 +150,12 @@ def get_vh_truth(temp_dir, config_name):
os.path.join(prefix, "default-ssl-port-only.conf"),
os.path.join(aug_pre, ("default-ssl-port-only.conf/"
"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

Expand Down

0 comments on commit d674a74

Please sign in to comment.