Compare commits
24 Commits
travis-tes
...
nginx-acme
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
2f05226213 | ||
|
|
8153c987f4 | ||
|
|
c1da1f53d5 | ||
|
|
2934da5a5a | ||
|
|
38b2c8b251 | ||
|
|
31ce670c31 | ||
|
|
298809a860 | ||
|
|
346e320f4e | ||
|
|
82b93ff8cc | ||
|
|
8ddec361f5 | ||
|
|
c80595dc5c | ||
|
|
f9f2f9466d | ||
|
|
9db971e218 | ||
|
|
b98e603d95 | ||
|
|
11cb3afa7e | ||
|
|
11f71cb5be | ||
|
|
4436fd6b34 | ||
|
|
7294447c47 | ||
|
|
555f43f9e1 | ||
|
|
228f57a0a9 | ||
|
|
ec159ef72e | ||
|
|
dc98b03bee | ||
|
|
a934ddee86 | ||
|
|
fce6accc18 |
@@ -23,6 +23,7 @@ from certbot import util
|
|||||||
from certbot.plugins import common
|
from certbot.plugins import common
|
||||||
|
|
||||||
from certbot_nginx import constants
|
from certbot_nginx import constants
|
||||||
|
from certbot_nginx import display_ops
|
||||||
from certbot_nginx import nginxparser
|
from certbot_nginx import nginxparser
|
||||||
from certbot_nginx import parser
|
from certbot_nginx import parser
|
||||||
from certbot_nginx import tls_sni_01
|
from certbot_nginx import tls_sni_01
|
||||||
@@ -92,6 +93,11 @@ class NginxConfigurator(common.Installer):
|
|||||||
# For creating new vhosts if no names match
|
# For creating new vhosts if no names match
|
||||||
self.new_vhost = None
|
self.new_vhost = None
|
||||||
|
|
||||||
|
# List of vhosts configured per wildcard domain on this run.
|
||||||
|
# used by deploy_cert() and enhance()
|
||||||
|
self._wildcard_vhosts = {}
|
||||||
|
self._wildcard_redirect_vhosts = {}
|
||||||
|
|
||||||
# Add number of outstanding challenges
|
# Add number of outstanding challenges
|
||||||
self._chall_out = 0
|
self._chall_out = 0
|
||||||
|
|
||||||
@@ -146,6 +152,7 @@ class NginxConfigurator(common.Installer):
|
|||||||
raise errors.PluginError(
|
raise errors.PluginError(
|
||||||
'Unable to lock %s', self.conf('server-root'))
|
'Unable to lock %s', self.conf('server-root'))
|
||||||
|
|
||||||
|
|
||||||
# Entry point in main.py for installing cert
|
# Entry point in main.py for installing cert
|
||||||
def deploy_cert(self, domain, cert_path, key_path,
|
def deploy_cert(self, domain, cert_path, key_path,
|
||||||
chain_path=None, fullchain_path=None):
|
chain_path=None, fullchain_path=None):
|
||||||
@@ -166,14 +173,24 @@ class NginxConfigurator(common.Installer):
|
|||||||
"The nginx plugin currently requires --fullchain-path to "
|
"The nginx plugin currently requires --fullchain-path to "
|
||||||
"install a cert.")
|
"install a cert.")
|
||||||
|
|
||||||
vhost = self.choose_vhost(domain, create_if_no_match=True)
|
vhosts = self.choose_vhosts(domain, create_if_no_match=True)
|
||||||
|
for vhost in vhosts:
|
||||||
|
self._deploy_cert(vhost, cert_path, key_path, chain_path, fullchain_path)
|
||||||
|
|
||||||
|
def _deploy_cert(self, vhost, cert_path, key_path, chain_path, fullchain_path):
|
||||||
|
# pylint: disable=unused-argument
|
||||||
|
"""
|
||||||
|
Helper function for deploy_cert() that handles the actual deployment
|
||||||
|
this exists because we might want to do multiple deployments per
|
||||||
|
domain originally passed for deploy_cert(). This is especially true
|
||||||
|
with wildcard certificates
|
||||||
|
"""
|
||||||
cert_directives = [['\n ', 'ssl_certificate', ' ', fullchain_path],
|
cert_directives = [['\n ', 'ssl_certificate', ' ', fullchain_path],
|
||||||
['\n ', 'ssl_certificate_key', ' ', key_path]]
|
['\n ', 'ssl_certificate_key', ' ', key_path]]
|
||||||
|
|
||||||
self.parser.add_server_directives(vhost,
|
self.parser.add_server_directives(vhost,
|
||||||
cert_directives, replace=True)
|
cert_directives, replace=True)
|
||||||
logger.info("Deployed Certificate to VirtualHost %s for %s",
|
logger.info("Deploying Certificate to VirtualHost %s", vhost.filep)
|
||||||
vhost.filep, ", ".join(vhost.names))
|
|
||||||
|
|
||||||
self.save_notes += ("Changed vhost at %s with addresses of %s\n" %
|
self.save_notes += ("Changed vhost at %s with addresses of %s\n" %
|
||||||
(vhost.filep,
|
(vhost.filep,
|
||||||
@@ -181,10 +198,61 @@ class NginxConfigurator(common.Installer):
|
|||||||
self.save_notes += "\tssl_certificate %s\n" % fullchain_path
|
self.save_notes += "\tssl_certificate %s\n" % fullchain_path
|
||||||
self.save_notes += "\tssl_certificate_key %s\n" % key_path
|
self.save_notes += "\tssl_certificate_key %s\n" % key_path
|
||||||
|
|
||||||
|
def _choose_vhosts_wildcard(self, domain, prefer_ssl, no_ssl_filter_port=None):
|
||||||
|
"""Prompts user to choose vhosts to install a wildcard certificate for"""
|
||||||
|
if prefer_ssl:
|
||||||
|
vhosts_cache = self._wildcard_vhosts
|
||||||
|
preference_test = lambda x: x.ssl
|
||||||
|
else:
|
||||||
|
vhosts_cache = self._wildcard_redirect_vhosts
|
||||||
|
preference_test = lambda x: not x.ssl
|
||||||
|
|
||||||
|
# Caching!
|
||||||
|
if domain in vhosts_cache:
|
||||||
|
# Vhosts for a wildcard domain were already selected
|
||||||
|
return vhosts_cache[domain]
|
||||||
|
|
||||||
|
# Get all vhosts whether or not they are covered by the wildcard domain
|
||||||
|
vhosts = self.parser.get_vhosts()
|
||||||
|
|
||||||
|
# Go through the vhosts, making sure that we cover all the names
|
||||||
|
# present, but preferring the SSL or non-SSL vhosts
|
||||||
|
filtered_vhosts = {}
|
||||||
|
for vhost in vhosts:
|
||||||
|
# Ensure we're listening non-sslishly on no_ssl_filter_port
|
||||||
|
if no_ssl_filter_port is not None:
|
||||||
|
if not self._vhost_listening_on_port_no_ssl(vhost, no_ssl_filter_port):
|
||||||
|
continue
|
||||||
|
for name in vhost.names:
|
||||||
|
if preference_test(vhost):
|
||||||
|
# Prefer either SSL or non-SSL vhosts
|
||||||
|
filtered_vhosts[name] = vhost
|
||||||
|
elif name not in filtered_vhosts:
|
||||||
|
# Add if not in list previously
|
||||||
|
filtered_vhosts[name] = vhost
|
||||||
|
|
||||||
|
# Only unique VHost objects
|
||||||
|
dialog_input = set([vhost for vhost in filtered_vhosts.values()])
|
||||||
|
|
||||||
|
# Ask the user which of names to enable, expect list of names back
|
||||||
|
return_vhosts = display_ops.select_vhost_multiple(list(dialog_input))
|
||||||
|
|
||||||
|
for vhost in return_vhosts:
|
||||||
|
if domain not in vhosts_cache:
|
||||||
|
vhosts_cache[domain] = []
|
||||||
|
vhosts_cache[domain].append(vhost)
|
||||||
|
|
||||||
|
return return_vhosts
|
||||||
|
|
||||||
#######################
|
#######################
|
||||||
# Vhost parsing methods
|
# Vhost parsing methods
|
||||||
#######################
|
#######################
|
||||||
def choose_vhost(self, target_name, create_if_no_match=False):
|
def _choose_vhost_single(self, target_name):
|
||||||
|
matches = self._get_ranked_matches(target_name)
|
||||||
|
vhosts = [x for x in [self._select_best_name_match(matches)] if x is not None]
|
||||||
|
return vhosts
|
||||||
|
|
||||||
|
def choose_vhosts(self, target_name, create_if_no_match=False):
|
||||||
"""Chooses a virtual host based on the given domain name.
|
"""Chooses a virtual host based on the given domain name.
|
||||||
|
|
||||||
.. note:: This makes the vhost SSL-enabled if it isn't already. Follows
|
.. note:: This makes the vhost SSL-enabled if it isn't already. Follows
|
||||||
@@ -202,17 +270,19 @@ class NginxConfigurator(common.Installer):
|
|||||||
when there is no match found. If we can't choose a default, raise a
|
when there is no match found. If we can't choose a default, raise a
|
||||||
MisconfigurationError.
|
MisconfigurationError.
|
||||||
|
|
||||||
:returns: ssl vhost associated with name
|
:returns: ssl vhosts associated with name
|
||||||
:rtype: :class:`~certbot_nginx.obj.VirtualHost`
|
:rtype: list of :class:`~certbot_nginx.obj.VirtualHost`
|
||||||
|
|
||||||
"""
|
"""
|
||||||
vhost = None
|
if util.is_wildcard_domain(target_name):
|
||||||
|
# Ask user which VHosts to support.
|
||||||
matches = self._get_ranked_matches(target_name)
|
vhosts = self._choose_vhosts_wildcard(target_name, prefer_ssl=True)
|
||||||
vhost = self._select_best_name_match(matches)
|
else:
|
||||||
if not vhost:
|
vhosts = self._choose_vhost_single(target_name)
|
||||||
|
if not vhosts:
|
||||||
if create_if_no_match:
|
if create_if_no_match:
|
||||||
vhost = self._vhost_from_duplicated_default(target_name)
|
# result will not be [None] because it errors on failure
|
||||||
|
vhosts = [self._vhost_from_duplicated_default(target_name)]
|
||||||
else:
|
else:
|
||||||
# No matches. Raise a misconfiguration error.
|
# No matches. Raise a misconfiguration error.
|
||||||
raise errors.MisconfigurationError(
|
raise errors.MisconfigurationError(
|
||||||
@@ -222,10 +292,11 @@ class NginxConfigurator(common.Installer):
|
|||||||
"nginx configuration: "
|
"nginx configuration: "
|
||||||
"https://nginx.org/en/docs/http/server_names.html") % (target_name))
|
"https://nginx.org/en/docs/http/server_names.html") % (target_name))
|
||||||
# Note: if we are enhancing with ocsp, vhost should already be ssl.
|
# Note: if we are enhancing with ocsp, vhost should already be ssl.
|
||||||
if not vhost.ssl:
|
for vhost in vhosts:
|
||||||
self._make_server_ssl(vhost)
|
if not vhost.ssl:
|
||||||
|
self._make_server_ssl(vhost)
|
||||||
|
|
||||||
return vhost
|
return vhosts
|
||||||
|
|
||||||
def ipv6_info(self, port):
|
def ipv6_info(self, port):
|
||||||
"""Returns tuple of booleans (ipv6_active, ipv6only_present)
|
"""Returns tuple of booleans (ipv6_active, ipv6only_present)
|
||||||
@@ -359,7 +430,7 @@ class NginxConfigurator(common.Installer):
|
|||||||
return sorted(matches, key=lambda x: x['rank'])
|
return sorted(matches, key=lambda x: x['rank'])
|
||||||
|
|
||||||
|
|
||||||
def choose_redirect_vhost(self, target_name, port, create_if_no_match=False):
|
def choose_redirect_vhosts(self, target_name, port, create_if_no_match=False):
|
||||||
"""Chooses a single virtual host for redirect enhancement.
|
"""Chooses a single virtual host for redirect enhancement.
|
||||||
|
|
||||||
Chooses the vhost most closely matching target_name that is
|
Chooses the vhost most closely matching target_name that is
|
||||||
@@ -377,15 +448,20 @@ class NginxConfigurator(common.Installer):
|
|||||||
when there is no match found. If we can't choose a default, raise a
|
when there is no match found. If we can't choose a default, raise a
|
||||||
MisconfigurationError.
|
MisconfigurationError.
|
||||||
|
|
||||||
:returns: vhost associated with name
|
:returns: vhosts associated with name
|
||||||
:rtype: :class:`~certbot_nginx.obj.VirtualHost`
|
:rtype: list of :class:`~certbot_nginx.obj.VirtualHost`
|
||||||
|
|
||||||
"""
|
"""
|
||||||
matches = self._get_redirect_ranked_matches(target_name, port)
|
if util.is_wildcard_domain(target_name):
|
||||||
vhost = self._select_best_name_match(matches)
|
# Ask user which VHosts to enhance.
|
||||||
if not vhost and create_if_no_match:
|
vhosts = self._choose_vhosts_wildcard(target_name, prefer_ssl=False,
|
||||||
vhost = self._vhost_from_duplicated_default(target_name, port=port)
|
no_ssl_filter_port=port)
|
||||||
return vhost
|
else:
|
||||||
|
matches = self._get_redirect_ranked_matches(target_name, port)
|
||||||
|
vhosts = [x for x in [self._select_best_name_match(matches)]if x is not None]
|
||||||
|
if not vhosts and create_if_no_match:
|
||||||
|
vhosts = [self._vhost_from_duplicated_default(target_name, port=port)]
|
||||||
|
return vhosts
|
||||||
|
|
||||||
def _port_matches(self, test_port, matching_port):
|
def _port_matches(self, test_port, matching_port):
|
||||||
# test_port is a number, matching is a number or "" or None
|
# test_port is a number, matching is a number or "" or None
|
||||||
@@ -395,6 +471,23 @@ class NginxConfigurator(common.Installer):
|
|||||||
else:
|
else:
|
||||||
return test_port == matching_port
|
return test_port == matching_port
|
||||||
|
|
||||||
|
def _vhost_listening_on_port_no_ssl(self, vhost, port):
|
||||||
|
found_matching_port = False
|
||||||
|
if len(vhost.addrs) == 0:
|
||||||
|
# if there are no listen directives at all, Nginx defaults to
|
||||||
|
# listening on port 80.
|
||||||
|
found_matching_port = (port == self.DEFAULT_LISTEN_PORT)
|
||||||
|
else:
|
||||||
|
for addr in vhost.addrs:
|
||||||
|
if self._port_matches(port, addr.get_port()) and addr.ssl == False:
|
||||||
|
found_matching_port = True
|
||||||
|
|
||||||
|
if found_matching_port:
|
||||||
|
# make sure we don't have an 'ssl on' directive
|
||||||
|
return not self.parser.has_ssl_on_directive(vhost)
|
||||||
|
else:
|
||||||
|
return False
|
||||||
|
|
||||||
def _get_redirect_ranked_matches(self, target_name, port):
|
def _get_redirect_ranked_matches(self, target_name, port):
|
||||||
"""Gets a ranked list of plaintextish port-listening vhosts matching target_name
|
"""Gets a ranked list of plaintextish port-listening vhosts matching target_name
|
||||||
|
|
||||||
@@ -411,21 +504,7 @@ class NginxConfigurator(common.Installer):
|
|||||||
all_vhosts = self.parser.get_vhosts()
|
all_vhosts = self.parser.get_vhosts()
|
||||||
|
|
||||||
def _vhost_matches(vhost, port):
|
def _vhost_matches(vhost, port):
|
||||||
found_matching_port = False
|
return self._vhost_listening_on_port_no_ssl(vhost, port)
|
||||||
if len(vhost.addrs) == 0:
|
|
||||||
# if there are no listen directives at all, Nginx defaults to
|
|
||||||
# listening on port 80.
|
|
||||||
found_matching_port = (port == self.DEFAULT_LISTEN_PORT)
|
|
||||||
else:
|
|
||||||
for addr in vhost.addrs:
|
|
||||||
if self._port_matches(port, addr.get_port()) and addr.ssl == False:
|
|
||||||
found_matching_port = True
|
|
||||||
|
|
||||||
if found_matching_port:
|
|
||||||
# make sure we don't have an 'ssl on' directive
|
|
||||||
return not self.parser.has_ssl_on_directive(vhost)
|
|
||||||
else:
|
|
||||||
return False
|
|
||||||
|
|
||||||
matching_vhosts = [vhost for vhost in all_vhosts if _vhost_matches(vhost, port)]
|
matching_vhosts = [vhost for vhost in all_vhosts if _vhost_matches(vhost, port)]
|
||||||
|
|
||||||
@@ -587,17 +666,31 @@ class NginxConfigurator(common.Installer):
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
port = self.DEFAULT_LISTEN_PORT
|
port = self.DEFAULT_LISTEN_PORT
|
||||||
vhost = None
|
|
||||||
# If there are blocks listening plaintextishly on self.DEFAULT_LISTEN_PORT,
|
# If there are blocks listening plaintextishly on self.DEFAULT_LISTEN_PORT,
|
||||||
# choose the most name-matching one.
|
# choose the most name-matching one.
|
||||||
|
|
||||||
vhost = self.choose_redirect_vhost(domain, port)
|
vhosts = self.choose_redirect_vhosts(domain, port)
|
||||||
|
|
||||||
if vhost is None:
|
if not vhosts:
|
||||||
logger.info("No matching insecure server blocks listening on port %s found.",
|
logger.info("No matching insecure server blocks listening on port %s found.",
|
||||||
self.DEFAULT_LISTEN_PORT)
|
self.DEFAULT_LISTEN_PORT)
|
||||||
return
|
return
|
||||||
|
|
||||||
|
for vhost in vhosts:
|
||||||
|
self._enable_redirect_single(domain, vhost)
|
||||||
|
|
||||||
|
def _enable_redirect_single(self, domain, vhost):
|
||||||
|
"""Redirect all equivalent HTTP traffic to ssl_vhost.
|
||||||
|
|
||||||
|
If the vhost is listening plaintextishly, separate out the
|
||||||
|
relevant directives into a new server block and add a rewrite directive.
|
||||||
|
|
||||||
|
.. note:: This function saves the configuration
|
||||||
|
|
||||||
|
:param str domain: domain to enable redirect for
|
||||||
|
:param `~obj.Vhost` vhost: vhost to enable redirect for
|
||||||
|
"""
|
||||||
|
|
||||||
new_vhost = None
|
new_vhost = None
|
||||||
if vhost.ssl:
|
if vhost.ssl:
|
||||||
new_vhost = self.parser.duplicate_vhost(vhost,
|
new_vhost = self.parser.duplicate_vhost(vhost,
|
||||||
@@ -638,7 +731,18 @@ class NginxConfigurator(common.Installer):
|
|||||||
:type chain_path: `str` or `None`
|
:type chain_path: `str` or `None`
|
||||||
|
|
||||||
"""
|
"""
|
||||||
vhost = self.choose_vhost(domain)
|
vhosts = self.choose_vhosts(domain)
|
||||||
|
for vhost in vhosts:
|
||||||
|
self._enable_ocsp_stapling_single(vhost, chain_path)
|
||||||
|
|
||||||
|
def _enable_ocsp_stapling_single(self, vhost, chain_path):
|
||||||
|
"""Include OCSP response in TLS handshake
|
||||||
|
|
||||||
|
:param str vhost: vhost to enable OCSP response for
|
||||||
|
:param chain_path: chain file path
|
||||||
|
:type chain_path: `str` or `None`
|
||||||
|
|
||||||
|
"""
|
||||||
if self.version < (1, 3, 7):
|
if self.version < (1, 3, 7):
|
||||||
raise errors.PluginError("Version 1.3.7 or greater of nginx "
|
raise errors.PluginError("Version 1.3.7 or greater of nginx "
|
||||||
"is needed to enable OCSP stapling")
|
"is needed to enable OCSP stapling")
|
||||||
@@ -889,14 +993,23 @@ def _test_block_from_block(block):
|
|||||||
parser.comment_directive(test_block, 0)
|
parser.comment_directive(test_block, 0)
|
||||||
return test_block[:-1]
|
return test_block[:-1]
|
||||||
|
|
||||||
|
|
||||||
def _redirect_block_for_domain(domain):
|
def _redirect_block_for_domain(domain):
|
||||||
|
updated_domain = domain
|
||||||
|
match_symbol = '='
|
||||||
|
if util.is_wildcard_domain(domain):
|
||||||
|
match_symbol = '~'
|
||||||
|
updated_domain = updated_domain.replace('.', r'\.')
|
||||||
|
updated_domain = updated_domain.replace('*', '[^.]+')
|
||||||
|
updated_domain = '^' + updated_domain + '$'
|
||||||
redirect_block = [[
|
redirect_block = [[
|
||||||
['\n ', 'if', ' ', '($host', ' ', '=', ' ', '%s)' % domain, ' '],
|
['\n ', 'if', ' ', '($host', ' ', match_symbol, ' ', '%s)' % updated_domain, ' '],
|
||||||
[['\n ', 'return', ' ', '301', ' ', 'https://$host$request_uri'],
|
[['\n ', 'return', ' ', '301', ' ', 'https://$host$request_uri'],
|
||||||
'\n ']],
|
'\n ']],
|
||||||
['\n']]
|
['\n']]
|
||||||
return redirect_block
|
return redirect_block
|
||||||
|
|
||||||
|
|
||||||
def nginx_restart(nginx_ctl, nginx_conf):
|
def nginx_restart(nginx_ctl, nginx_conf):
|
||||||
"""Restarts the Nginx Server.
|
"""Restarts the Nginx Server.
|
||||||
|
|
||||||
|
|||||||
44
certbot-nginx/certbot_nginx/display_ops.py
Normal file
44
certbot-nginx/certbot_nginx/display_ops.py
Normal file
@@ -0,0 +1,44 @@
|
|||||||
|
"""Contains UI methods for Nginx operations."""
|
||||||
|
import logging
|
||||||
|
|
||||||
|
import zope.component
|
||||||
|
|
||||||
|
from certbot import interfaces
|
||||||
|
|
||||||
|
import certbot.display.util as display_util
|
||||||
|
|
||||||
|
|
||||||
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
def select_vhost_multiple(vhosts):
|
||||||
|
"""Select multiple Vhosts to install the certificate for
|
||||||
|
:param vhosts: Available Nginx VirtualHosts
|
||||||
|
:type vhosts: :class:`list` of type `~obj.Vhost`
|
||||||
|
:returns: List of VirtualHosts
|
||||||
|
:rtype: :class:`list`of type `~obj.Vhost`
|
||||||
|
"""
|
||||||
|
if not vhosts:
|
||||||
|
return list()
|
||||||
|
tags_list = [vhost.display_repr()+"\n" for vhost in vhosts]
|
||||||
|
# Remove the extra newline from the last entry
|
||||||
|
if len(tags_list):
|
||||||
|
tags_list[-1] = tags_list[-1][:-1]
|
||||||
|
code, names = zope.component.getUtility(interfaces.IDisplay).checklist(
|
||||||
|
"Which server blocks would you like to modify?",
|
||||||
|
tags=tags_list, force_interactive=True)
|
||||||
|
if code == display_util.OK:
|
||||||
|
return_vhosts = _reversemap_vhosts(names, vhosts)
|
||||||
|
return return_vhosts
|
||||||
|
return []
|
||||||
|
|
||||||
|
def _reversemap_vhosts(names, vhosts):
|
||||||
|
"""Helper function for select_vhost_multiple for mapping string
|
||||||
|
representations back to actual vhost objects"""
|
||||||
|
return_vhosts = list()
|
||||||
|
|
||||||
|
for selection in names:
|
||||||
|
for vhost in vhosts:
|
||||||
|
if vhost.display_repr().strip() == selection.strip():
|
||||||
|
return_vhosts.append(vhost)
|
||||||
|
return return_vhosts
|
||||||
@@ -179,13 +179,17 @@ class NginxHttp01(common.ChallengePerformer):
|
|||||||
|
|
||||||
"""
|
"""
|
||||||
try:
|
try:
|
||||||
vhost = self.configurator.choose_redirect_vhost(achall.domain,
|
vhosts = self.configurator.choose_redirect_vhosts(achall.domain,
|
||||||
'%i' % self.configurator.config.http01_port, create_if_no_match=True)
|
'%i' % self.configurator.config.http01_port, create_if_no_match=True)
|
||||||
except errors.MisconfigurationError:
|
except errors.MisconfigurationError:
|
||||||
# Couldn't find either a matching name+port server block
|
# Couldn't find either a matching name+port server block
|
||||||
# or a port+default_server block, so create a dummy block
|
# or a port+default_server block, so create a dummy block
|
||||||
return self._make_server_block(achall)
|
return self._make_server_block(achall)
|
||||||
|
|
||||||
|
# len is max 1 because Nginx doesn't authenticate wildcards
|
||||||
|
# if len were or vhosts None, we would have errored
|
||||||
|
vhost = vhosts[0]
|
||||||
|
|
||||||
# Modify existing server block
|
# Modify existing server block
|
||||||
validation = achall.validation(achall.account_key)
|
validation = achall.validation(achall.account_key)
|
||||||
validation_path = self._get_validation_path(achall)
|
validation_path = self._get_validation_path(achall)
|
||||||
|
|||||||
@@ -193,6 +193,11 @@ class VirtualHost(object): # pylint: disable=too-few-public-methods
|
|||||||
|
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
def __hash__(self):
|
||||||
|
return hash((self.filep, tuple(self.path),
|
||||||
|
tuple(self.addrs), tuple(self.names),
|
||||||
|
self.ssl, self.enabled))
|
||||||
|
|
||||||
def contains_list(self, test):
|
def contains_list(self, test):
|
||||||
"""Determine if raw server block contains test list at top level
|
"""Determine if raw server block contains test list at top level
|
||||||
"""
|
"""
|
||||||
@@ -216,3 +221,15 @@ class VirtualHost(object): # pylint: disable=too-few-public-methods
|
|||||||
for a in self.addrs:
|
for a in self.addrs:
|
||||||
if not a.ipv6:
|
if not a.ipv6:
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
def display_repr(self):
|
||||||
|
"""Return a representation of VHost to be used in dialog"""
|
||||||
|
return (
|
||||||
|
"File: {filename}\n"
|
||||||
|
"Addresses: {addrs}\n"
|
||||||
|
"Names: {names}\n"
|
||||||
|
"HTTPS: {https}\n".format(
|
||||||
|
filename=self.filep,
|
||||||
|
addrs=", ".join(str(addr) for addr in self.addrs),
|
||||||
|
names=", ".join(self.names),
|
||||||
|
https="Yes" if self.ssl else "No"))
|
||||||
|
|||||||
@@ -128,7 +128,7 @@ class NginxConfiguratorTest(util.NginxTest):
|
|||||||
['#', parser.COMMENT]]]],
|
['#', parser.COMMENT]]]],
|
||||||
parsed[0])
|
parsed[0])
|
||||||
|
|
||||||
def test_choose_vhost(self):
|
def test_choose_vhosts(self):
|
||||||
localhost_conf = set(['localhost', r'~^(www\.)?(example|bar)\.'])
|
localhost_conf = set(['localhost', r'~^(www\.)?(example|bar)\.'])
|
||||||
server_conf = set(['somename', 'another.alias', 'alias'])
|
server_conf = set(['somename', 'another.alias', 'alias'])
|
||||||
example_conf = set(['.example.com', 'example.*'])
|
example_conf = set(['.example.com', 'example.*'])
|
||||||
@@ -159,7 +159,7 @@ class NginxConfiguratorTest(util.NginxTest):
|
|||||||
'69.255.225.155']
|
'69.255.225.155']
|
||||||
|
|
||||||
for name in results:
|
for name in results:
|
||||||
vhost = self.config.choose_vhost(name)
|
vhost = self.config.choose_vhosts(name)[0]
|
||||||
path = os.path.relpath(vhost.filep, self.temp_dir)
|
path = os.path.relpath(vhost.filep, self.temp_dir)
|
||||||
|
|
||||||
self.assertEqual(results[name], vhost.names)
|
self.assertEqual(results[name], vhost.names)
|
||||||
@@ -173,7 +173,7 @@ class NginxConfiguratorTest(util.NginxTest):
|
|||||||
|
|
||||||
for name in bad_results:
|
for name in bad_results:
|
||||||
self.assertRaises(errors.MisconfigurationError,
|
self.assertRaises(errors.MisconfigurationError,
|
||||||
self.config.choose_vhost, name)
|
self.config.choose_vhosts, name)
|
||||||
|
|
||||||
def test_ipv6only(self):
|
def test_ipv6only(self):
|
||||||
# ipv6_info: (ipv6_active, ipv6only_present)
|
# ipv6_info: (ipv6_active, ipv6only_present)
|
||||||
@@ -702,6 +702,100 @@ class NginxConfiguratorTest(util.NginxTest):
|
|||||||
self.config.rollback_checkpoints()
|
self.config.rollback_checkpoints()
|
||||||
self.assertTrue(mock_parser_load.call_count == 3)
|
self.assertTrue(mock_parser_load.call_count == 3)
|
||||||
|
|
||||||
|
def test_choose_vhosts_wildcard(self):
|
||||||
|
# pylint: disable=protected-access
|
||||||
|
mock_path = "certbot_nginx.display_ops.select_vhost_multiple"
|
||||||
|
with mock.patch(mock_path) as mock_select_vhs:
|
||||||
|
vhost = [x for x in self.config.parser.get_vhosts()
|
||||||
|
if 'summer.com' in x.names][0]
|
||||||
|
mock_select_vhs.return_value = [vhost]
|
||||||
|
vhs = self.config._choose_vhosts_wildcard("*.com",
|
||||||
|
prefer_ssl=True)
|
||||||
|
# Check that the dialog was called with migration.com
|
||||||
|
self.assertTrue(vhost in mock_select_vhs.call_args[0][0])
|
||||||
|
|
||||||
|
# And the actual returned values
|
||||||
|
self.assertEquals(len(vhs), 1)
|
||||||
|
self.assertEqual(vhs[0], vhost)
|
||||||
|
|
||||||
|
def test_choose_vhosts_wildcard_redirect(self):
|
||||||
|
# pylint: disable=protected-access
|
||||||
|
mock_path = "certbot_nginx.display_ops.select_vhost_multiple"
|
||||||
|
with mock.patch(mock_path) as mock_select_vhs:
|
||||||
|
vhost = [x for x in self.config.parser.get_vhosts()
|
||||||
|
if 'summer.com' in x.names][0]
|
||||||
|
mock_select_vhs.return_value = [vhost]
|
||||||
|
vhs = self.config._choose_vhosts_wildcard("*.com",
|
||||||
|
prefer_ssl=False)
|
||||||
|
# Check that the dialog was called with migration.com
|
||||||
|
self.assertTrue(vhost in mock_select_vhs.call_args[0][0])
|
||||||
|
|
||||||
|
# And the actual returned values
|
||||||
|
self.assertEquals(len(vhs), 1)
|
||||||
|
self.assertEqual(vhs[0], vhost)
|
||||||
|
|
||||||
|
def test_deploy_cert_wildcard(self):
|
||||||
|
# pylint: disable=protected-access
|
||||||
|
mock_choose_vhosts = mock.MagicMock()
|
||||||
|
vhost = [x for x in self.config.parser.get_vhosts()
|
||||||
|
if 'geese.com' in x.names][0]
|
||||||
|
mock_choose_vhosts.return_value = [vhost]
|
||||||
|
self.config._choose_vhosts_wildcard = mock_choose_vhosts
|
||||||
|
mock_d = "certbot_nginx.configurator.NginxConfigurator._deploy_cert"
|
||||||
|
with mock.patch(mock_d) as mock_dep:
|
||||||
|
self.config.deploy_cert("*.com", "/tmp/path",
|
||||||
|
"/tmp/path", "/tmp/path", "/tmp/path")
|
||||||
|
self.assertTrue(mock_dep.called)
|
||||||
|
self.assertEquals(len(mock_dep.call_args_list), 1)
|
||||||
|
self.assertEqual(vhost, mock_dep.call_args_list[0][0][0])
|
||||||
|
|
||||||
|
@mock.patch("certbot_nginx.display_ops.select_vhost_multiple")
|
||||||
|
def test_deploy_cert_wildcard_no_vhosts(self, mock_dialog):
|
||||||
|
# pylint: disable=protected-access
|
||||||
|
mock_dialog.return_value = []
|
||||||
|
self.assertRaises(errors.PluginError,
|
||||||
|
self.config.deploy_cert,
|
||||||
|
"*.wild.cat", "/tmp/path", "/tmp/path",
|
||||||
|
"/tmp/path", "/tmp/path")
|
||||||
|
|
||||||
|
@mock.patch("certbot_nginx.display_ops.select_vhost_multiple")
|
||||||
|
def test_enhance_wildcard_ocsp_after_install(self, mock_dialog):
|
||||||
|
# pylint: disable=protected-access
|
||||||
|
vhost = [x for x in self.config.parser.get_vhosts()
|
||||||
|
if 'geese.com' in x.names][0]
|
||||||
|
self.config._wildcard_vhosts["*.com"] = [vhost]
|
||||||
|
self.config.enhance("*.com", "staple-ocsp", "example/chain.pem")
|
||||||
|
self.assertFalse(mock_dialog.called)
|
||||||
|
|
||||||
|
@mock.patch("certbot_nginx.display_ops.select_vhost_multiple")
|
||||||
|
def test_enhance_wildcard_redirect_or_ocsp_no_install(self, mock_dialog):
|
||||||
|
vhost = [x for x in self.config.parser.get_vhosts()
|
||||||
|
if 'summer.com' in x.names][0]
|
||||||
|
mock_dialog.return_value = [vhost]
|
||||||
|
self.config.enhance("*.com", "staple-ocsp", "example/chain.pem")
|
||||||
|
self.assertTrue(mock_dialog.called)
|
||||||
|
|
||||||
|
@mock.patch("certbot_nginx.display_ops.select_vhost_multiple")
|
||||||
|
def test_enhance_wildcard_double_redirect(self, mock_dialog):
|
||||||
|
# pylint: disable=protected-access
|
||||||
|
vhost = [x for x in self.config.parser.get_vhosts()
|
||||||
|
if 'summer.com' in x.names][0]
|
||||||
|
self.config._wildcard_redirect_vhosts["*.com"] = [vhost]
|
||||||
|
self.config.enhance("*.com", "redirect")
|
||||||
|
self.assertFalse(mock_dialog.called)
|
||||||
|
|
||||||
|
def test_choose_vhosts_wildcard_no_ssl_filter_port(self):
|
||||||
|
# pylint: disable=protected-access
|
||||||
|
mock_path = "certbot_nginx.display_ops.select_vhost_multiple"
|
||||||
|
with mock.patch(mock_path) as mock_select_vhs:
|
||||||
|
mock_select_vhs.return_value = []
|
||||||
|
self.config._choose_vhosts_wildcard("*.com",
|
||||||
|
prefer_ssl=False,
|
||||||
|
no_ssl_filter_port='80')
|
||||||
|
# Check that the dialog was called with only port 80 vhosts
|
||||||
|
self.assertEqual(len(mock_select_vhs.call_args[0][0]), 4)
|
||||||
|
|
||||||
|
|
||||||
class InstallSslOptionsConfTest(util.NginxTest):
|
class InstallSslOptionsConfTest(util.NginxTest):
|
||||||
"""Test that the options-ssl-nginx.conf file is installed and updated properly."""
|
"""Test that the options-ssl-nginx.conf file is installed and updated properly."""
|
||||||
|
|
||||||
|
|||||||
45
certbot-nginx/certbot_nginx/tests/display_ops_test.py
Normal file
45
certbot-nginx/certbot_nginx/tests/display_ops_test.py
Normal file
@@ -0,0 +1,45 @@
|
|||||||
|
"""Test certbot_apache.display_ops."""
|
||||||
|
import unittest
|
||||||
|
|
||||||
|
from certbot.display import util as display_util
|
||||||
|
|
||||||
|
from certbot.tests import util as certbot_util
|
||||||
|
|
||||||
|
from certbot_nginx import parser
|
||||||
|
|
||||||
|
from certbot_nginx.display_ops import select_vhost_multiple
|
||||||
|
from certbot_nginx.tests import util
|
||||||
|
|
||||||
|
|
||||||
|
class SelectVhostMultiTest(util.NginxTest):
|
||||||
|
"""Tests for certbot_nginx.display_ops.select_vhost_multiple."""
|
||||||
|
|
||||||
|
def setUp(self):
|
||||||
|
super(SelectVhostMultiTest, self).setUp()
|
||||||
|
nparser = parser.NginxParser(self.config_path)
|
||||||
|
self.vhosts = nparser.get_vhosts()
|
||||||
|
|
||||||
|
def test_select_no_input(self):
|
||||||
|
self.assertFalse(select_vhost_multiple([]))
|
||||||
|
|
||||||
|
@certbot_util.patch_get_utility()
|
||||||
|
def test_select_correct(self, mock_util):
|
||||||
|
mock_util().checklist.return_value = (
|
||||||
|
display_util.OK, [self.vhosts[3].display_repr(),
|
||||||
|
self.vhosts[2].display_repr()])
|
||||||
|
vhs = select_vhost_multiple([self.vhosts[3],
|
||||||
|
self.vhosts[2],
|
||||||
|
self.vhosts[1]])
|
||||||
|
self.assertTrue(self.vhosts[2] in vhs)
|
||||||
|
self.assertTrue(self.vhosts[3] in vhs)
|
||||||
|
self.assertFalse(self.vhosts[1] in vhs)
|
||||||
|
|
||||||
|
@certbot_util.patch_get_utility()
|
||||||
|
def test_select_cancel(self, mock_util):
|
||||||
|
mock_util().checklist.return_value = (display_util.CANCEL, "whatever")
|
||||||
|
vhs = select_vhost_multiple([self.vhosts[2], self.vhosts[3]])
|
||||||
|
self.assertFalse(vhs)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
unittest.main() # pragma: no cover
|
||||||
@@ -61,10 +61,10 @@ class TlsSniPerformTest(util.NginxTest):
|
|||||||
shutil.rmtree(self.work_dir)
|
shutil.rmtree(self.work_dir)
|
||||||
|
|
||||||
@mock.patch("certbot_nginx.configurator"
|
@mock.patch("certbot_nginx.configurator"
|
||||||
".NginxConfigurator.choose_vhost")
|
".NginxConfigurator.choose_vhosts")
|
||||||
def test_perform(self, mock_choose):
|
def test_perform(self, mock_choose):
|
||||||
self.sni.add_chall(self.achalls[1])
|
self.sni.add_chall(self.achalls[1])
|
||||||
mock_choose.return_value = None
|
mock_choose.return_value = []
|
||||||
result = self.sni.perform()
|
result = self.sni.perform()
|
||||||
self.assertFalse(result is None)
|
self.assertFalse(result is None)
|
||||||
|
|
||||||
|
|||||||
@@ -55,10 +55,11 @@ class NginxTlsSni01(common.TLSSNI01):
|
|||||||
self.configurator.config.tls_sni_01_port)
|
self.configurator.config.tls_sni_01_port)
|
||||||
|
|
||||||
for achall in self.achalls:
|
for achall in self.achalls:
|
||||||
vhost = self.configurator.choose_vhost(achall.domain, create_if_no_match=True)
|
vhosts = self.configurator.choose_vhosts(achall.domain, create_if_no_match=True)
|
||||||
|
|
||||||
if vhost is not None and vhost.addrs:
|
# len is max 1 because Nginx doesn't authenticate wildcards
|
||||||
addresses.append(list(vhost.addrs))
|
if vhosts and vhosts[0].addrs:
|
||||||
|
addresses.append(list(vhosts[0].addrs))
|
||||||
else:
|
else:
|
||||||
if ipv6:
|
if ipv6:
|
||||||
# If IPv6 is active in Nginx configuration
|
# If IPv6 is active in Nginx configuration
|
||||||
|
|||||||
Reference in New Issue
Block a user