mirror of
https://github.com/certbot/certbot.git
synced 2026-07-28 08:45:22 +02:00
Nginx improvements
Add a server_names_hash_bucket_size directive during challenges to fix an nginx crash on restart (Fixes #922). Use fullchain instead of chain (Fixes #610). Implement OCSP stapling (Fixes #937, Fixes #931). Hide Boulder output in integration tests to make them more readable.
This commit is contained in:
@@ -163,7 +163,8 @@ class ApacheConfigurator(augeas_configurator.AugeasConfigurator):
|
|||||||
|
|
||||||
temp_install(self.mod_ssl_conf)
|
temp_install(self.mod_ssl_conf)
|
||||||
|
|
||||||
def deploy_cert(self, domain, cert_path, key_path, chain_path=None):
|
def deploy_cert(self, domain, cert_path, key_path,
|
||||||
|
chain_path=None, fullchain_path=None): # pylint: disable=unused-argument
|
||||||
"""Deploys certificate to specified virtual host.
|
"""Deploys certificate to specified virtual host.
|
||||||
|
|
||||||
Currently tries to find the last directives to deploy the cert in
|
Currently tries to find the last directives to deploy the cert in
|
||||||
|
|||||||
+3
-2
@@ -175,12 +175,13 @@ class Proxy(configurators_common.Proxy):
|
|||||||
else:
|
else:
|
||||||
return {"example.com"}
|
return {"example.com"}
|
||||||
|
|
||||||
def deploy_cert(self, domain, cert_path, key_path, chain_path=None):
|
def deploy_cert(self, domain, cert_path, key_path, chain_path=None,
|
||||||
|
fullchain_path=None):
|
||||||
"""Installs cert"""
|
"""Installs cert"""
|
||||||
cert_path, key_path, chain_path = self.copy_certs_and_keys(
|
cert_path, key_path, chain_path = self.copy_certs_and_keys(
|
||||||
cert_path, key_path, chain_path)
|
cert_path, key_path, chain_path)
|
||||||
self._apache_configurator.deploy_cert(
|
self._apache_configurator.deploy_cert(
|
||||||
domain, cert_path, key_path, chain_path)
|
domain, cert_path, key_path, chain_path, fullchain_path)
|
||||||
|
|
||||||
|
|
||||||
def _is_apache_command(command):
|
def _is_apache_command(command):
|
||||||
|
|||||||
@@ -117,30 +117,44 @@ class NginxConfigurator(common.Plugin):
|
|||||||
temp_install(self.mod_ssl_conf)
|
temp_install(self.mod_ssl_conf)
|
||||||
|
|
||||||
# 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, chain_path=None):
|
def deploy_cert(self, domain, cert_path, key_path,
|
||||||
|
chain_path, fullchain_path):
|
||||||
# pylint: disable=unused-argument
|
# pylint: disable=unused-argument
|
||||||
"""Deploys certificate to specified virtual host.
|
"""Deploys certificate to specified virtual host.
|
||||||
|
|
||||||
.. note:: Aborts if the vhost is missing ssl_certificate or
|
.. note:: Aborts if the vhost is missing ssl_certificate or
|
||||||
ssl_certificate_key.
|
ssl_certificate_key.
|
||||||
|
|
||||||
.. note:: Nginx doesn't have a cert chain directive, so the last
|
.. note:: Nginx doesn't have a cert chain directive.
|
||||||
parameter is always ignored. It expects the cert file to have
|
It expects the cert file to have the concatenated chain.
|
||||||
the concatenated chain.
|
However, we use the chain file as input to the
|
||||||
|
ssl_trusted_certificate directive, used for verify OCSP responses.
|
||||||
|
|
||||||
.. note:: This doesn't save the config files!
|
.. note:: This doesn't save the config files!
|
||||||
|
|
||||||
"""
|
"""
|
||||||
vhost = self.choose_vhost(domain)
|
vhost = self.choose_vhost(domain)
|
||||||
directives = [['ssl_certificate', cert_path],
|
cert_directives = [['ssl_certificate', fullchain_path],
|
||||||
['ssl_certificate_key', key_path]]
|
['ssl_certificate_key', key_path]]
|
||||||
|
|
||||||
|
# OCSP stapling was introduced in Nginx 1.3.7. If we have that version
|
||||||
|
# or greater, add config settings for it.
|
||||||
|
stapling_directives = []
|
||||||
|
if self.version >= (1, 3, 7):
|
||||||
|
stapling_directives = [
|
||||||
|
['ssl_trusted_certificate', chain_path],
|
||||||
|
['ssl_stapling', 'on'],
|
||||||
|
['ssl_stapling_verify', 'on']]
|
||||||
|
|
||||||
try:
|
try:
|
||||||
self.parser.add_server_directives(vhost.filep, vhost.names,
|
self.parser.add_server_directives(vhost.filep, vhost.names,
|
||||||
directives, True)
|
cert_directives, True)
|
||||||
|
self.parser.add_server_directives(vhost.filep, vhost.names,
|
||||||
|
stapling_directives, False)
|
||||||
logger.info("Deployed Certificate to VirtualHost %s for %s",
|
logger.info("Deployed Certificate to VirtualHost %s for %s",
|
||||||
vhost.filep, vhost.names)
|
vhost.filep, vhost.names)
|
||||||
except errors.MisconfigurationError:
|
except errors.MisconfigurationError, e:
|
||||||
|
logger.debug(e)
|
||||||
logger.warn(
|
logger.warn(
|
||||||
"Cannot find a cert or key directive in %s for %s. "
|
"Cannot find a cert or key directive in %s for %s. "
|
||||||
"VirtualHost was not modified.", vhost.filep, vhost.names)
|
"VirtualHost was not modified.", vhost.filep, vhost.names)
|
||||||
|
|||||||
@@ -90,14 +90,23 @@ class NginxDvsni(common.Dvsni):
|
|||||||
# Add the 'include' statement for the challenges if it doesn't exist
|
# Add the 'include' statement for the challenges if it doesn't exist
|
||||||
# already in the main config
|
# already in the main config
|
||||||
included = False
|
included = False
|
||||||
directive = ['include', self.challenge_conf]
|
include_directive = ['include', self.challenge_conf]
|
||||||
root = self.configurator.parser.loc["root"]
|
root = self.configurator.parser.loc["root"]
|
||||||
|
|
||||||
|
bucket_directive = ['server_names_hash_bucket_size', '128']
|
||||||
|
|
||||||
main = self.configurator.parser.parsed[root]
|
main = self.configurator.parser.parsed[root]
|
||||||
for entry in main:
|
for key, value in main:
|
||||||
if entry[0] == ['http']:
|
if key == ['http']:
|
||||||
body = entry[1]
|
body = value
|
||||||
if directive not in body:
|
found_bucket = False
|
||||||
body.append(directive)
|
for key, value in body: # pylint: disable=unused-variable
|
||||||
|
if key == bucket_directive[0]:
|
||||||
|
found_bucket = True
|
||||||
|
if not found_bucket:
|
||||||
|
body.insert(0, bucket_directive)
|
||||||
|
if include_directive not in body:
|
||||||
|
body.insert(0, include_directive)
|
||||||
included = True
|
included = True
|
||||||
break
|
break
|
||||||
if not included:
|
if not included:
|
||||||
|
|||||||
@@ -476,8 +476,12 @@ def _add_directives(block, directives, replace=False):
|
|||||||
:param list directives: The new directives.
|
:param list directives: The new directives.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
if replace:
|
for directive in directives:
|
||||||
for directive in directives:
|
if not replace:
|
||||||
|
# We insert new directives at the top of the block, mostly
|
||||||
|
# to work around https://trac.nginx.org/nginx/ticket/810
|
||||||
|
block.insert(0, directive)
|
||||||
|
else:
|
||||||
changed = False
|
changed = False
|
||||||
if len(directive) == 0:
|
if len(directive) == 0:
|
||||||
continue
|
continue
|
||||||
@@ -489,5 +493,3 @@ def _add_directives(block, directives, replace=False):
|
|||||||
raise errors.MisconfigurationError(
|
raise errors.MisconfigurationError(
|
||||||
'LetsEncrypt expected directive for %s in the Nginx '
|
'LetsEncrypt expected directive for %s in the Nginx '
|
||||||
'config but did not find it.' % directive[0])
|
'config but did not find it.' % directive[0])
|
||||||
else:
|
|
||||||
block.extend(directives)
|
|
||||||
|
|||||||
@@ -63,11 +63,11 @@ class NginxConfiguratorTest(util.NginxTest):
|
|||||||
|
|
||||||
# pylint: disable=protected-access
|
# pylint: disable=protected-access
|
||||||
parsed = self.config.parser._parse_files(filep, override=True)
|
parsed = self.config.parser._parse_files(filep, override=True)
|
||||||
self.assertEqual([[['server'], [['listen', '69.50.225.155:9000'],
|
self.assertEqual([[['server'], [['listen', '5001 ssl'],
|
||||||
|
['listen', '69.50.225.155:9000'],
|
||||||
['listen', '127.0.0.1'],
|
['listen', '127.0.0.1'],
|
||||||
['server_name', '.example.com'],
|
['server_name', '.example.com'],
|
||||||
['server_name', 'example.*'],
|
['server_name', 'example.*']]]],
|
||||||
['listen', '5001 ssl']]]],
|
|
||||||
parsed[0])
|
parsed[0])
|
||||||
|
|
||||||
def test_choose_vhost(self):
|
def test_choose_vhost(self):
|
||||||
@@ -96,18 +96,49 @@ class NginxConfiguratorTest(util.NginxTest):
|
|||||||
def test_more_info(self):
|
def test_more_info(self):
|
||||||
self.assertTrue('nginx.conf' in self.config.more_info())
|
self.assertTrue('nginx.conf' in self.config.more_info())
|
||||||
|
|
||||||
|
def test_deploy_cert_stapling(self):
|
||||||
|
# Choose a version of Nginx greater than 1.3.7 so stapling code gets
|
||||||
|
# invoked.
|
||||||
|
self.config.version = (1, 9, 6)
|
||||||
|
example_conf = self.config.parser.abs_path('sites-enabled/example.com')
|
||||||
|
self.config.deploy_cert(
|
||||||
|
"www.example.com",
|
||||||
|
"example/cert.pem",
|
||||||
|
"example/key.pem",
|
||||||
|
"example/chain.pem",
|
||||||
|
"example/fullchain.pem")
|
||||||
|
self.config.save()
|
||||||
|
self.config.parser.load()
|
||||||
|
generated_conf = self.config.parser.parsed[example_conf]
|
||||||
|
|
||||||
|
self.assertTrue(util.contains_at_depth(generated_conf,
|
||||||
|
['ssl_stapling', 'on'], 2))
|
||||||
|
self.assertTrue(util.contains_at_depth(generated_conf,
|
||||||
|
['ssl_stapling_verify', 'on'], 2))
|
||||||
|
self.assertTrue(util.contains_at_depth(generated_conf,
|
||||||
|
['ssl_trusted_certificate', 'example/chain.pem'], 2))
|
||||||
|
|
||||||
def test_deploy_cert(self):
|
def test_deploy_cert(self):
|
||||||
server_conf = self.config.parser.abs_path('server.conf')
|
server_conf = self.config.parser.abs_path('server.conf')
|
||||||
nginx_conf = self.config.parser.abs_path('nginx.conf')
|
nginx_conf = self.config.parser.abs_path('nginx.conf')
|
||||||
example_conf = self.config.parser.abs_path('sites-enabled/example.com')
|
example_conf = self.config.parser.abs_path('sites-enabled/example.com')
|
||||||
|
# Choose a version of Nginx less than 1.3.7 so stapling code doesn't get
|
||||||
|
# invoked.
|
||||||
|
self.config.version = (1, 3, 1)
|
||||||
|
|
||||||
# Get the default SSL vhost
|
# Get the default SSL vhost
|
||||||
self.config.deploy_cert(
|
self.config.deploy_cert(
|
||||||
"www.example.com",
|
"www.example.com",
|
||||||
"example/cert.pem", "example/key.pem")
|
"example/cert.pem",
|
||||||
|
"example/key.pem",
|
||||||
|
"example/chain.pem",
|
||||||
|
"example/fullchain.pem")
|
||||||
self.config.deploy_cert(
|
self.config.deploy_cert(
|
||||||
"another.alias",
|
"another.alias",
|
||||||
"/etc/nginx/cert.pem", "/etc/nginx/key.pem")
|
"/etc/nginx/cert.pem",
|
||||||
|
"/etc/nginx/key.pem",
|
||||||
|
"/etc/nginx/chain.pem",
|
||||||
|
"/etc/nginx/fullchain.pem")
|
||||||
self.config.save()
|
self.config.save()
|
||||||
|
|
||||||
self.config.parser.load()
|
self.config.parser.load()
|
||||||
@@ -119,35 +150,34 @@ class NginxConfiguratorTest(util.NginxTest):
|
|||||||
access_log = os.path.join(self.work_dir, "access.log")
|
access_log = os.path.join(self.work_dir, "access.log")
|
||||||
error_log = os.path.join(self.work_dir, "error.log")
|
error_log = os.path.join(self.work_dir, "error.log")
|
||||||
self.assertEqual([[['server'],
|
self.assertEqual([[['server'],
|
||||||
[['listen', '69.50.225.155:9000'],
|
[['include', self.config.parser.loc["ssl_options"]],
|
||||||
|
['ssl_certificate_key', 'example/key.pem'],
|
||||||
|
['ssl_certificate', 'example/fullchain.pem'],
|
||||||
|
['error_log', error_log],
|
||||||
|
['access_log', access_log],
|
||||||
|
|
||||||
|
['listen', '5001 ssl'],
|
||||||
|
['listen', '69.50.225.155:9000'],
|
||||||
['listen', '127.0.0.1'],
|
['listen', '127.0.0.1'],
|
||||||
['server_name', '.example.com'],
|
['server_name', '.example.com'],
|
||||||
['server_name', 'example.*'],
|
['server_name', 'example.*']]]],
|
||||||
['listen', '5001 ssl'],
|
|
||||||
['access_log', access_log],
|
|
||||||
['error_log', error_log],
|
|
||||||
['ssl_certificate', 'example/cert.pem'],
|
|
||||||
['ssl_certificate_key', 'example/key.pem'],
|
|
||||||
['include',
|
|
||||||
self.config.parser.loc["ssl_options"]]]]],
|
|
||||||
parsed_example_conf)
|
parsed_example_conf)
|
||||||
self.assertEqual([['server_name', 'somename alias another.alias']],
|
self.assertEqual([['server_name', 'somename alias another.alias']],
|
||||||
parsed_server_conf)
|
parsed_server_conf)
|
||||||
self.assertEqual([['server'],
|
self.assertTrue(util.contains_at_depth(parsed_nginx_conf,
|
||||||
[['listen', '8000'],
|
[['server'],
|
||||||
['listen', 'somename:8080'],
|
[['include', self.config.parser.loc["ssl_options"]],
|
||||||
['include', 'server.conf'],
|
['ssl_certificate_key', '/etc/nginx/key.pem'],
|
||||||
[['location', '/'],
|
['ssl_certificate', '/etc/nginx/fullchain.pem'],
|
||||||
[['root', 'html'],
|
['error_log', error_log],
|
||||||
['index', 'index.html index.htm']]],
|
['access_log', access_log],
|
||||||
['listen', '5001 ssl'],
|
['listen', '5001 ssl'],
|
||||||
['access_log', access_log],
|
['listen', '8000'],
|
||||||
['error_log', error_log],
|
['listen', 'somename:8080'],
|
||||||
['ssl_certificate', '/etc/nginx/cert.pem'],
|
['include', 'server.conf'],
|
||||||
['ssl_certificate_key', '/etc/nginx/key.pem'],
|
[['location', '/'],
|
||||||
['include',
|
[['root', 'html'], ['index', 'index.html index.htm']]]]],
|
||||||
self.config.parser.loc["ssl_options"]]]],
|
2))
|
||||||
parsed_nginx_conf[-1][-1][-1])
|
|
||||||
|
|
||||||
def test_get_all_certs_keys(self):
|
def test_get_all_certs_keys(self):
|
||||||
nginx_conf = self.config.parser.abs_path('nginx.conf')
|
nginx_conf = self.config.parser.abs_path('nginx.conf')
|
||||||
@@ -156,16 +186,22 @@ class NginxConfiguratorTest(util.NginxTest):
|
|||||||
# Get the default SSL vhost
|
# Get the default SSL vhost
|
||||||
self.config.deploy_cert(
|
self.config.deploy_cert(
|
||||||
"www.example.com",
|
"www.example.com",
|
||||||
"example/cert.pem", "example/key.pem")
|
"example/cert.pem",
|
||||||
|
"example/key.pem",
|
||||||
|
"example/chain.pem",
|
||||||
|
"example/fullchain.pem")
|
||||||
self.config.deploy_cert(
|
self.config.deploy_cert(
|
||||||
"another.alias",
|
"another.alias",
|
||||||
"/etc/nginx/cert.pem", "/etc/nginx/key.pem")
|
"/etc/nginx/cert.pem",
|
||||||
|
"/etc/nginx/key.pem",
|
||||||
|
"/etc/nginx/chain.pem",
|
||||||
|
"/etc/nginx/fullchain.pem")
|
||||||
self.config.save()
|
self.config.save()
|
||||||
|
|
||||||
self.config.parser.load()
|
self.config.parser.load()
|
||||||
self.assertEqual(set([
|
self.assertEqual(set([
|
||||||
('example/cert.pem', 'example/key.pem', example_conf),
|
('example/fullchain.pem', 'example/key.pem', example_conf),
|
||||||
('/etc/nginx/cert.pem', '/etc/nginx/key.pem', nginx_conf),
|
('/etc/nginx/fullchain.pem', '/etc/nginx/key.pem', nginx_conf),
|
||||||
]), self.config.get_all_certs_keys())
|
]), self.config.get_all_certs_keys())
|
||||||
|
|
||||||
@mock.patch("letsencrypt_nginx.configurator.dvsni.NginxDvsni.perform")
|
@mock.patch("letsencrypt_nginx.configurator.dvsni.NginxDvsni.perform")
|
||||||
|
|||||||
@@ -85,7 +85,8 @@ class DvsniPerformTest(util.NginxTest):
|
|||||||
# Make sure challenge config is included in main config
|
# Make sure challenge config is included in main config
|
||||||
http = self.sni.configurator.parser.parsed[
|
http = self.sni.configurator.parser.parsed[
|
||||||
self.sni.configurator.parser.loc["root"]][-1]
|
self.sni.configurator.parser.loc["root"]][-1]
|
||||||
self.assertTrue(['include', self.sni.challenge_conf] in http[1])
|
self.assertTrue(
|
||||||
|
util.contains_at_depth(http, ['include', self.sni.challenge_conf], 1))
|
||||||
|
|
||||||
def test_perform2(self):
|
def test_perform2(self):
|
||||||
acme_responses = []
|
acme_responses = []
|
||||||
@@ -108,7 +109,8 @@ class DvsniPerformTest(util.NginxTest):
|
|||||||
http = self.sni.configurator.parser.parsed[
|
http = self.sni.configurator.parser.parsed[
|
||||||
self.sni.configurator.parser.loc["root"]][-1]
|
self.sni.configurator.parser.loc["root"]][-1]
|
||||||
self.assertTrue(['include', self.sni.challenge_conf] in http[1])
|
self.assertTrue(['include', self.sni.challenge_conf] in http[1])
|
||||||
self.assertTrue(['server_name', 'blah'] in http[1][-2][1])
|
self.assertTrue(
|
||||||
|
util.contains_at_depth(http, ['server_name', 'blah'], 3))
|
||||||
|
|
||||||
self.assertEqual(len(sni_responses), 3)
|
self.assertEqual(len(sni_responses), 3)
|
||||||
for i in xrange(3):
|
for i in xrange(3):
|
||||||
|
|||||||
@@ -128,18 +128,18 @@ class NginxParserTest(util.NginxTest):
|
|||||||
r'~^(www\.)?(example|bar)\.']),
|
r'~^(www\.)?(example|bar)\.']),
|
||||||
[['foo', 'bar'], ['ssl_certificate',
|
[['foo', 'bar'], ['ssl_certificate',
|
||||||
'/etc/ssl/cert.pem']])
|
'/etc/ssl/cert.pem']])
|
||||||
ssl_re = re.compile(r'foo bar;\n\s+ssl_certificate /etc/ssl/cert.pem')
|
ssl_re = re.compile(r'\n\s+ssl_certificate /etc/ssl/cert.pem')
|
||||||
self.assertEqual(1, len(re.findall(ssl_re, nginxparser.dumps(
|
dump = nginxparser.dumps(nparser.parsed[nparser.abs_path('nginx.conf')])
|
||||||
nparser.parsed[nparser.abs_path('nginx.conf')]))))
|
self.assertEqual(1, len(re.findall(ssl_re, dump)))
|
||||||
nparser.add_server_directives(nparser.abs_path('server.conf'),
|
nparser.add_server_directives(nparser.abs_path('server.conf'),
|
||||||
set(['alias', 'another.alias',
|
set(['alias', 'another.alias',
|
||||||
'somename']),
|
'somename']),
|
||||||
[['foo', 'bar'], ['ssl_certificate',
|
[['foo', 'bar'], ['ssl_certificate',
|
||||||
'/etc/ssl/cert2.pem']])
|
'/etc/ssl/cert2.pem']])
|
||||||
self.assertEqual(nparser.parsed[nparser.abs_path('server.conf')],
|
self.assertEqual(nparser.parsed[nparser.abs_path('server.conf')],
|
||||||
[['server_name', 'somename alias another.alias'],
|
[['ssl_certificate', '/etc/ssl/cert2.pem'],
|
||||||
['foo', 'bar'],
|
['foo', 'bar'],
|
||||||
['ssl_certificate', '/etc/ssl/cert2.pem']])
|
['server_name', 'somename alias another.alias']])
|
||||||
|
|
||||||
def test_add_http_directives(self):
|
def test_add_http_directives(self):
|
||||||
nparser = parser.NginxParser(self.config_path, self.ssl_options)
|
nparser = parser.NginxParser(self.config_path, self.ssl_options)
|
||||||
@@ -148,8 +148,9 @@ class NginxParserTest(util.NginxTest):
|
|||||||
[['listen', '80'],
|
[['listen', '80'],
|
||||||
['server_name', 'localhost']]]
|
['server_name', 'localhost']]]
|
||||||
nparser.add_http_directives(filep, block)
|
nparser.add_http_directives(filep, block)
|
||||||
self.assertEqual(nparser.parsed[filep][-1][0], ['http'])
|
root = nparser.parsed[filep]
|
||||||
self.assertEqual(nparser.parsed[filep][-1][1][-1], block)
|
self.assertTrue(util.contains_at_depth(root, ['http'], 1))
|
||||||
|
self.assertTrue(util.contains_at_depth(root, block, 2))
|
||||||
|
|
||||||
def test_replace_server_directives(self):
|
def test_replace_server_directives(self):
|
||||||
nparser = parser.NginxParser(self.config_path, self.ssl_options)
|
nparser = parser.NginxParser(self.config_path, self.ssl_options)
|
||||||
|
|||||||
@@ -85,3 +85,18 @@ def filter_comments(tree):
|
|||||||
yield [key, values]
|
yield [key, values]
|
||||||
|
|
||||||
return list(traverse(tree))
|
return list(traverse(tree))
|
||||||
|
|
||||||
|
def contains_at_depth(haystack, needle, n):
|
||||||
|
"""
|
||||||
|
Return true if the needle is present in one of the sub-iterables in haystack
|
||||||
|
at depth n. Haystack must be an iterable.
|
||||||
|
"""
|
||||||
|
if not hasattr(haystack, '__iter__'):
|
||||||
|
return False
|
||||||
|
if n == 0:
|
||||||
|
return needle in haystack
|
||||||
|
else:
|
||||||
|
for item in haystack:
|
||||||
|
if contains_at_depth(item, needle, n - 1):
|
||||||
|
return True
|
||||||
|
return False
|
||||||
|
|||||||
@@ -20,7 +20,6 @@ events {
|
|||||||
}
|
}
|
||||||
|
|
||||||
http {
|
http {
|
||||||
server_names_hash_bucket_size 2048;
|
|
||||||
# Set an array of temp and cache file options that will otherwise default to
|
# Set an array of temp and cache file options that will otherwise default to
|
||||||
# restricted locations accessible only to root.
|
# restricted locations accessible only to root.
|
||||||
client_body_temp_path $root/client_body;
|
client_body_temp_path $root/client_body;
|
||||||
|
|||||||
+6
-3
@@ -337,9 +337,9 @@ def run(args, config, plugins): # pylint: disable=too-many-branches,too-many-lo
|
|||||||
|
|
||||||
lineage = _auth_from_domains(le_client, config, domains, plugins)
|
lineage = _auth_from_domains(le_client, config, domains, plugins)
|
||||||
|
|
||||||
# TODO: We also need to pass the fullchain (for Nginx)
|
|
||||||
le_client.deploy_certificate(
|
le_client.deploy_certificate(
|
||||||
domains, lineage.privkey, lineage.cert, lineage.chain)
|
domains, lineage.privkey, lineage.cert,
|
||||||
|
lineage.chain, lineage.fullchain)
|
||||||
le_client.enhance_config(domains, args.redirect)
|
le_client.enhance_config(domains, args.redirect)
|
||||||
|
|
||||||
if len(lineage.available_versions("cert")) == 1:
|
if len(lineage.available_versions("cert")) == 1:
|
||||||
@@ -392,7 +392,8 @@ def install(args, config, plugins):
|
|||||||
args, config, authenticator=None, installer=installer)
|
args, config, authenticator=None, installer=installer)
|
||||||
assert args.cert_path is not None # required=True in the subparser
|
assert args.cert_path is not None # required=True in the subparser
|
||||||
le_client.deploy_certificate(
|
le_client.deploy_certificate(
|
||||||
domains, args.key_path, args.cert_path, args.chain_path)
|
domains, args.key_path, args.cert_path, args.chain_path,
|
||||||
|
args.fullchain_path)
|
||||||
le_client.enhance_config(domains, args.redirect)
|
le_client.enhance_config(domains, args.redirect)
|
||||||
|
|
||||||
|
|
||||||
@@ -803,6 +804,8 @@ def _paths_parser(helpful):
|
|||||||
default_cp = None
|
default_cp = None
|
||||||
if verb == "auth":
|
if verb == "auth":
|
||||||
default_cp = flag_default("auth_chain_path")
|
default_cp = flag_default("auth_chain_path")
|
||||||
|
add("paths", "--fullchain-path", default=default_cp,
|
||||||
|
help="Accompanying path to a full certificate chain (cert plus chain).")
|
||||||
add("paths", "--chain-path", default=default_cp,
|
add("paths", "--chain-path", default=default_cp,
|
||||||
help="Accompanying path to a certificate chain.")
|
help="Accompanying path to a certificate chain.")
|
||||||
add("paths", "--config-dir", default=flag_default("config_dir"),
|
add("paths", "--config-dir", default=flag_default("config_dir"),
|
||||||
|
|||||||
@@ -336,7 +336,8 @@ class Client(object):
|
|||||||
|
|
||||||
return os.path.abspath(act_cert_path), cert_chain_abspath
|
return os.path.abspath(act_cert_path), cert_chain_abspath
|
||||||
|
|
||||||
def deploy_certificate(self, domains, privkey_path, cert_path, chain_path):
|
def deploy_certificate(self, domains, privkey_path,
|
||||||
|
cert_path, chain_path, fullchain_path):
|
||||||
"""Install certificate
|
"""Install certificate
|
||||||
|
|
||||||
:param list domains: list of domains to install the certificate
|
:param list domains: list of domains to install the certificate
|
||||||
@@ -357,8 +358,10 @@ class Client(object):
|
|||||||
# TODO: Provide a fullchain reference for installers like
|
# TODO: Provide a fullchain reference for installers like
|
||||||
# nginx that want it
|
# nginx that want it
|
||||||
self.installer.deploy_cert(
|
self.installer.deploy_cert(
|
||||||
dom, os.path.abspath(cert_path),
|
domain=dom, cert_path=os.path.abspath(cert_path),
|
||||||
os.path.abspath(privkey_path), chain_path)
|
key_path=os.path.abspath(privkey_path),
|
||||||
|
chain_path=chain_path,
|
||||||
|
fullchain_path=fullchain_path)
|
||||||
|
|
||||||
self.installer.save("Deployed Let's Encrypt Certificate")
|
self.installer.save("Deployed Let's Encrypt Certificate")
|
||||||
# sites may have been enabled / final cleanup
|
# sites may have been enabled / final cleanup
|
||||||
|
|||||||
@@ -241,13 +241,15 @@ class IInstaller(IPlugin):
|
|||||||
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def deploy_cert(domain, cert_path, key_path, chain_path=None):
|
def deploy_cert(domain, cert_path, key_path, chain_path, fullchain_path):
|
||||||
"""Deploy certificate.
|
"""Deploy certificate.
|
||||||
|
|
||||||
:param str domain: domain to deploy certificate file
|
:param str domain: domain to deploy certificate file
|
||||||
:param str cert_path: absolute path to the certificate file
|
:param str cert_path: absolute path to the certificate file
|
||||||
:param str key_path: absolute path to the private key file
|
:param str key_path: absolute path to the private key file
|
||||||
:param str chain_path: absolute path to the certificate chain file
|
:param str chain_path: absolute path to the certificate chain file
|
||||||
|
:param str fullchain_path: absolute path to the certificate fullchain
|
||||||
|
file (cert plus chain)
|
||||||
|
|
||||||
:raises .PluginError: when cert cannot be deployed
|
:raises .PluginError: when cert cannot be deployed
|
||||||
|
|
||||||
|
|||||||
@@ -30,7 +30,8 @@ class Installer(common.Plugin):
|
|||||||
def get_all_names(self):
|
def get_all_names(self):
|
||||||
return []
|
return []
|
||||||
|
|
||||||
def deploy_cert(self, domain, cert_path, key_path, chain_path=None):
|
def deploy_cert(self, domain, cert_path, key_path,
|
||||||
|
chain_path=None, fullchain_path=None):
|
||||||
pass # pragma: no cover
|
pass # pragma: no cover
|
||||||
|
|
||||||
def enhance(self, domain, enhancement, options=None):
|
def enhance(self, domain, enhancement, options=None):
|
||||||
|
|||||||
@@ -177,15 +177,19 @@ class ClientTest(unittest.TestCase):
|
|||||||
|
|
||||||
def test_deploy_certificate(self):
|
def test_deploy_certificate(self):
|
||||||
self.assertRaises(errors.Error, self.client.deploy_certificate,
|
self.assertRaises(errors.Error, self.client.deploy_certificate,
|
||||||
["foo.bar"], "key", "cert", "chain")
|
["foo.bar"], "key", "cert", "chain", "fullchain")
|
||||||
|
|
||||||
installer = mock.MagicMock()
|
installer = mock.MagicMock()
|
||||||
self.client.installer = installer
|
self.client.installer = installer
|
||||||
|
|
||||||
self.client.deploy_certificate(["foo.bar"], "key", "cert", "chain")
|
self.client.deploy_certificate(["foo.bar"], "key", "cert", "chain",
|
||||||
|
"fullchain")
|
||||||
installer.deploy_cert.assert_called_once_with(
|
installer.deploy_cert.assert_called_once_with(
|
||||||
"foo.bar", os.path.abspath("cert"),
|
cert_path=os.path.abspath("cert"),
|
||||||
os.path.abspath("key"), os.path.abspath("chain"))
|
chain_path=os.path.abspath("chain"),
|
||||||
|
domain='foo.bar',
|
||||||
|
fullchain_path='fullchain',
|
||||||
|
key_path=os.path.abspath("key"))
|
||||||
self.assertEqual(installer.save.call_count, 1)
|
self.assertEqual(installer.save.call_count, 1)
|
||||||
installer.restart.assert_called_once_with()
|
installer.restart.assert_called_once_with()
|
||||||
|
|
||||||
|
|||||||
@@ -38,5 +38,5 @@ if ! go get bitbucket.org/liamstask/goose/cmd/goose ; then
|
|||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
./test/create_db.sh
|
./test/create_db.sh
|
||||||
./start.py &
|
./start.py > /dev/null &
|
||||||
# Hopefully start.py bootstraps before integration test is started...
|
# Hopefully start.py bootstraps before integration test is started...
|
||||||
|
|||||||
Reference in New Issue
Block a user