Compare commits
15 Commits
ipv6onlydu
...
update-ser
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d24b1f7310 | ||
|
|
bdaccb645b | ||
|
|
f5ad08047b | ||
|
|
8fd3f6c64c | ||
|
|
4d706ac77e | ||
|
|
8231b1a19c | ||
|
|
5ff7f2211e | ||
|
|
7630550ac4 | ||
|
|
336950c0b9 | ||
|
|
a779e06d47 | ||
|
|
669312d248 | ||
|
|
4d082e22e6 | ||
|
|
af2cce4ca8 | ||
|
|
804fd4b78a | ||
|
|
8cdb213a61 |
@@ -11,7 +11,7 @@ version = '0.23.0.dev0'
|
||||
install_requires = [
|
||||
'acme>=0.21.1',
|
||||
'certbot>=0.21.1',
|
||||
'dns-lexicon',
|
||||
'dns-lexicon>=2.2.1', # Support for >1 TXT record per name
|
||||
'mock',
|
||||
'setuptools',
|
||||
'zope.interface',
|
||||
|
||||
@@ -11,7 +11,7 @@ version = '0.23.0.dev0'
|
||||
install_requires = [
|
||||
'acme>=0.21.1',
|
||||
'certbot>=0.21.1',
|
||||
'dns-lexicon',
|
||||
'dns-lexicon>=2.2.1', # Support for >1 TXT record per name
|
||||
'mock',
|
||||
'setuptools',
|
||||
'zope.interface',
|
||||
|
||||
@@ -11,7 +11,7 @@ version = '0.23.0.dev0'
|
||||
install_requires = [
|
||||
'acme>=0.21.1',
|
||||
'certbot>=0.21.1',
|
||||
'dns-lexicon',
|
||||
'dns-lexicon>=2.2.1', # Support for >1 TXT record per name
|
||||
'mock',
|
||||
'setuptools',
|
||||
'zope.interface',
|
||||
|
||||
@@ -81,7 +81,7 @@ class _GoogleClient(object):
|
||||
Encapsulates all communication with the Google Cloud DNS API.
|
||||
"""
|
||||
|
||||
def __init__(self, account_json=None):
|
||||
def __init__(self, account_json=None, dns_api=None):
|
||||
|
||||
scopes = ['https://www.googleapis.com/auth/ndev.clouddns.readwrite']
|
||||
if account_json is not None:
|
||||
@@ -92,7 +92,12 @@ class _GoogleClient(object):
|
||||
credentials = None
|
||||
self.project_id = self.get_project_id()
|
||||
|
||||
self.dns = discovery.build('dns', 'v1', credentials=credentials, cache_discovery=False)
|
||||
if not dns_api:
|
||||
self.dns = discovery.build('dns', 'v1',
|
||||
credentials=credentials,
|
||||
cache_discovery=False)
|
||||
else:
|
||||
self.dns = dns_api
|
||||
|
||||
def add_txt_record(self, domain, record_name, record_content, record_ttl):
|
||||
"""
|
||||
|
||||
@@ -4,7 +4,9 @@ import os
|
||||
import unittest
|
||||
|
||||
import mock
|
||||
from googleapiclient import discovery
|
||||
from googleapiclient.errors import Error
|
||||
from googleapiclient.http import HttpMock
|
||||
from httplib2 import ServerNotFoundError
|
||||
|
||||
from certbot import errors
|
||||
@@ -68,7 +70,13 @@ class GoogleClientTest(unittest.TestCase):
|
||||
def _setUp_client_with_mock(self, zone_request_side_effect):
|
||||
from certbot_dns_google.dns_google import _GoogleClient
|
||||
|
||||
client = _GoogleClient(ACCOUNT_JSON_PATH)
|
||||
pwd = os.path.dirname(__file__)
|
||||
rel_path = 'testdata/discovery.json'
|
||||
discovery_file = os.path.join(pwd, rel_path)
|
||||
http_mock = HttpMock(discovery_file, {'status': '200'})
|
||||
dns_api = discovery.build('dns', 'v1', http=http_mock)
|
||||
|
||||
client = _GoogleClient(ACCOUNT_JSON_PATH, dns_api)
|
||||
|
||||
# Setup
|
||||
mock_mz = mock.MagicMock()
|
||||
|
||||
1401
certbot-dns-google/certbot_dns_google/testdata/discovery.json
vendored
Normal file
1401
certbot-dns-google/certbot_dns_google/testdata/discovery.json
vendored
Normal file
File diff suppressed because it is too large
Load Diff
@@ -11,7 +11,7 @@ version = '0.23.0.dev0'
|
||||
install_requires = [
|
||||
'acme>=0.21.1',
|
||||
'certbot>=0.21.1',
|
||||
'dns-lexicon',
|
||||
'dns-lexicon>=2.2.1', # Support for >1 TXT record per name
|
||||
'mock',
|
||||
'setuptools',
|
||||
'zope.interface',
|
||||
|
||||
@@ -11,7 +11,7 @@ version = '0.23.0.dev0'
|
||||
install_requires = [
|
||||
'acme>=0.21.1',
|
||||
'certbot>=0.21.1',
|
||||
'dns-lexicon',
|
||||
'dns-lexicon>=2.2.1', # Support for >1 TXT record per name
|
||||
'mock',
|
||||
'setuptools',
|
||||
'zope.interface',
|
||||
|
||||
@@ -332,7 +332,8 @@ class NginxConfigurator(common.Installer):
|
||||
def _vhost_from_duplicated_default(self, domain, port=None):
|
||||
if self.new_vhost is None:
|
||||
default_vhost = self._get_default_vhost(port)
|
||||
self.new_vhost = self.parser.duplicate_vhost(default_vhost, delete_default=True)
|
||||
self.new_vhost = self.parser.duplicate_vhost(default_vhost,
|
||||
remove_singleton_listen_params=True)
|
||||
self.new_vhost.names = set()
|
||||
|
||||
self._add_server_name_to_vhost(self.new_vhost, domain)
|
||||
@@ -913,7 +914,7 @@ class NginxConfigurator(common.Installer):
|
||||
raise errors.PluginError("Nginx build doesn't support SNI")
|
||||
|
||||
product_name, product_version = version_matches[0]
|
||||
if product_name is not 'nginx':
|
||||
if product_name != 'nginx':
|
||||
logger.warning("NGINX derivative %s is not officially supported by"
|
||||
" certbot", product_name)
|
||||
|
||||
|
||||
@@ -159,16 +159,22 @@ class NginxHttp01(common.ChallengePerformer):
|
||||
document_root = os.path.join(
|
||||
self.configurator.config.work_dir, "http_01_nonexistent")
|
||||
|
||||
block.extend([['server_name', ' ', achall.domain],
|
||||
['root', ' ', document_root],
|
||||
self._location_directive_for_achall(achall)
|
||||
])
|
||||
# TODO: do we want to return something else if they otherwise access this block?
|
||||
return [['server'], block]
|
||||
|
||||
def _location_directive_for_achall(self, achall):
|
||||
validation = achall.validation(achall.account_key)
|
||||
validation_path = self._get_validation_path(achall)
|
||||
|
||||
block.extend([['server_name', ' ', achall.domain],
|
||||
['root', ' ', document_root],
|
||||
[['location', ' ', '=', ' ', validation_path],
|
||||
[['default_type', ' ', 'text/plain'],
|
||||
['return', ' ', '200', ' ', validation]]]])
|
||||
# TODO: do we want to return something else if they otherwise access this block?
|
||||
return [['server'], block]
|
||||
location_directive = [['location', ' ', '=', ' ', validation_path],
|
||||
[['default_type', ' ', 'text/plain'],
|
||||
['return', ' ', '200', ' ', validation]]]
|
||||
return location_directive
|
||||
|
||||
|
||||
def _make_or_mod_server_block(self, achall):
|
||||
"""Modifies a server block to respond to a challenge.
|
||||
@@ -191,12 +197,7 @@ class NginxHttp01(common.ChallengePerformer):
|
||||
vhost = vhosts[0]
|
||||
|
||||
# Modify existing server block
|
||||
validation = achall.validation(achall.account_key)
|
||||
validation_path = self._get_validation_path(achall)
|
||||
|
||||
location_directive = [[['location', ' ', '=', ' ', validation_path],
|
||||
[['default_type', ' ', 'text/plain'],
|
||||
['return', ' ', '200', ' ', validation]]]]
|
||||
location_directive = [self._location_directive_for_achall(achall)]
|
||||
|
||||
self.configurator.parser.add_server_directives(vhost,
|
||||
location_directive)
|
||||
|
||||
@@ -29,6 +29,8 @@ class Addr(common.Addr):
|
||||
:param str port: port number or "\*" or ""
|
||||
:param bool ssl: Whether the directive includes 'ssl'
|
||||
:param bool default: Whether the directive includes 'default_server'
|
||||
:param bool default: Whether this is an IPv6 address
|
||||
:param bool ipv6only: Whether the directive includes 'ipv6only=on'
|
||||
|
||||
"""
|
||||
UNSPECIFIED_IPV4_ADDRESSES = ('', '*', '0.0.0.0')
|
||||
|
||||
@@ -354,13 +354,14 @@ class NginxParser(object):
|
||||
except errors.MisconfigurationError as err:
|
||||
raise errors.MisconfigurationError("Problem in %s: %s" % (filename, str(err)))
|
||||
|
||||
def duplicate_vhost(self, vhost_template, delete_default=False, only_directives=None):
|
||||
def duplicate_vhost(self, vhost_template, remove_singleton_listen_params=False,
|
||||
only_directives=None):
|
||||
"""Duplicate the vhost in the configuration files.
|
||||
|
||||
:param :class:`~certbot_nginx.obj.VirtualHost` vhost_template: The vhost
|
||||
whose information we copy
|
||||
:param bool delete_default: If we should remove default_server
|
||||
from listen directives in the block.
|
||||
:param bool remove_singleton_listen_params: If we should remove parameters
|
||||
from listen directives in the block that can only be used once per address
|
||||
:param list only_directives: If it exists, only duplicate the named directives. Only
|
||||
looks at first level of depth; does not expand includes.
|
||||
|
||||
@@ -387,15 +388,18 @@ class NginxParser(object):
|
||||
|
||||
enclosing_block.append(raw_in_parsed)
|
||||
new_vhost.path[-1] = len(enclosing_block) - 1
|
||||
if delete_default:
|
||||
if remove_singleton_listen_params:
|
||||
for addr in new_vhost.addrs:
|
||||
addr.default = False
|
||||
addr.ipv6only = False
|
||||
for directive in enclosing_block[new_vhost.path[-1]][1]:
|
||||
if len(directive) > 0 and directive[0] == 'listen':
|
||||
if 'default_server' in directive:
|
||||
del directive[directive.index('default_server')]
|
||||
if 'default' in directive:
|
||||
del directive[directive.index('default')]
|
||||
if 'ipv6only=on' in directive:
|
||||
del directive[directive.index('ipv6only=on')]
|
||||
return new_vhost
|
||||
|
||||
|
||||
@@ -561,7 +565,7 @@ def _update_or_add_directives(directives, insert_at_top, block):
|
||||
|
||||
|
||||
INCLUDE = 'include'
|
||||
REPEATABLE_DIRECTIVES = set(['server_name', 'listen', INCLUDE, 'location', 'rewrite'])
|
||||
REPEATABLE_DIRECTIVES = set(['server_name', 'listen', INCLUDE, 'rewrite'])
|
||||
COMMENT = ' managed by Certbot'
|
||||
COMMENT_BLOCK = [' ', '#', COMMENT]
|
||||
|
||||
@@ -739,7 +743,7 @@ def _parse_server_raw(server):
|
||||
if addr.ssl:
|
||||
parsed_server['ssl'] = True
|
||||
elif directive[0] == 'server_name':
|
||||
parsed_server['names'].update(directive[1:])
|
||||
parsed_server['names'].update(x.strip('"\'') for x in directive[1:])
|
||||
elif _is_ssl_on_directive(directive):
|
||||
parsed_server['ssl'] = True
|
||||
apply_ssl_to_all_addrs = True
|
||||
|
||||
@@ -639,7 +639,7 @@ class NginxConfiguratorTest(util.NginxTest):
|
||||
self.assertEqual([[['server'],
|
||||
[['listen', 'myhost', 'default_server'],
|
||||
['listen', 'otherhost', 'default_server'],
|
||||
['server_name', 'www.example.org'],
|
||||
['server_name', '"www.example.org"'],
|
||||
[['location', '/'],
|
||||
[['root', 'html'],
|
||||
['index', 'index.html', 'index.htm']]]]],
|
||||
|
||||
@@ -430,7 +430,7 @@ class NginxParserTest(util.NginxTest): #pylint: disable=too-many-public-methods
|
||||
|
||||
vhosts = nparser.get_vhosts()
|
||||
default = [x for x in vhosts if 'default' in x.filep][0]
|
||||
new_vhost = nparser.duplicate_vhost(default, delete_default=True)
|
||||
new_vhost = nparser.duplicate_vhost(default, remove_singleton_listen_params=True)
|
||||
nparser.filedump(ext='')
|
||||
|
||||
# check properties of new vhost
|
||||
@@ -448,6 +448,28 @@ class NginxParserTest(util.NginxTest): #pylint: disable=too-many-public-methods
|
||||
self.assertEqual(len(default.raw), len(new_vhost_parsed.raw))
|
||||
self.assertTrue(next(iter(default.addrs)).super_eq(next(iter(new_vhost_parsed.addrs))))
|
||||
|
||||
def test_duplicate_vhost_remove_ipv6only(self):
|
||||
nparser = parser.NginxParser(self.config_path)
|
||||
|
||||
vhosts = nparser.get_vhosts()
|
||||
ipv6ssl = [x for x in vhosts if 'ipv6ssl' in x.filep][0]
|
||||
new_vhost = nparser.duplicate_vhost(ipv6ssl, remove_singleton_listen_params=True)
|
||||
nparser.filedump(ext='')
|
||||
|
||||
for addr in new_vhost.addrs:
|
||||
self.assertFalse(addr.ipv6only)
|
||||
|
||||
identical_vhost = nparser.duplicate_vhost(ipv6ssl, remove_singleton_listen_params=False)
|
||||
nparser.filedump(ext='')
|
||||
|
||||
called = False
|
||||
for addr in identical_vhost.addrs:
|
||||
if addr.ipv6:
|
||||
self.assertTrue(addr.ipv6only)
|
||||
called = True
|
||||
self.assertTrue(called)
|
||||
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main() # pragma: no cover
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
server {
|
||||
listen myhost default_server;
|
||||
listen otherhost default_server;
|
||||
server_name www.example.org;
|
||||
server_name "www.example.org";
|
||||
|
||||
location / {
|
||||
root html;
|
||||
|
||||
@@ -49,9 +49,9 @@ http {
|
||||
|
||||
server {
|
||||
# IPv4.
|
||||
listen 5002;
|
||||
listen 5002 $default_server;
|
||||
# IPv6.
|
||||
listen [::]:5002 default ipv6only=on;
|
||||
listen [::]:5002 $default_server;
|
||||
server_name nginx.wtf nginx2.wtf;
|
||||
|
||||
root $root/webroot;
|
||||
@@ -62,5 +62,36 @@ http {
|
||||
try_files \$uri \$uri/ /index.html;
|
||||
}
|
||||
}
|
||||
|
||||
server {
|
||||
listen 5002;
|
||||
listen [::]:5002;
|
||||
server_name nginx3.wtf;
|
||||
|
||||
root $root/webroot;
|
||||
|
||||
location /.well-known/ {
|
||||
return 404;
|
||||
}
|
||||
|
||||
return 301 https://\$host\$request_uri;
|
||||
}
|
||||
|
||||
server {
|
||||
listen 8082;
|
||||
listen [::]:8082;
|
||||
server_name nginx4.wtf nginx5.wtf;
|
||||
}
|
||||
|
||||
server {
|
||||
listen 5002;
|
||||
listen [::]:5002;
|
||||
listen 5001 ssl;
|
||||
listen [::]:5001 ssl;
|
||||
if (\$scheme != "https") {
|
||||
return 301 https://\$host\$request_uri;
|
||||
}
|
||||
server_name nginx6.wtf nginx7.wtf;
|
||||
}
|
||||
}
|
||||
EOF
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
#!/bin/sh -xe
|
||||
#!/bin/bash -xe
|
||||
# prerequisite: apt-get install --no-install-recommends nginx-light openssl
|
||||
|
||||
. ./tests/integration/_common.sh
|
||||
@@ -6,13 +6,15 @@
|
||||
export PATH="/usr/sbin:$PATH" # /usr/sbin/nginx
|
||||
nginx_root="$root/nginx"
|
||||
mkdir $nginx_root
|
||||
original=$(root="$nginx_root" ./certbot-nginx/tests/boulder-integration.conf.sh)
|
||||
nginx_conf="$nginx_root/nginx.conf"
|
||||
echo "$original" > $nginx_conf
|
||||
|
||||
reload_nginx () {
|
||||
original=$(root="$nginx_root" ./certbot-nginx/tests/boulder-integration.conf.sh)
|
||||
nginx_conf="$nginx_root/nginx.conf"
|
||||
echo "$original" > $nginx_conf
|
||||
|
||||
killall nginx || true
|
||||
nginx -c $nginx_root/nginx.conf
|
||||
killall nginx || true
|
||||
nginx -c $nginx_root/nginx.conf
|
||||
}
|
||||
|
||||
certbot_test_nginx () {
|
||||
certbot_test \
|
||||
@@ -32,10 +34,30 @@ test_deployment_and_rollback() {
|
||||
diff -q <(echo "$original") $nginx_conf
|
||||
}
|
||||
|
||||
export default_server="default_server"
|
||||
reload_nginx
|
||||
certbot_test_nginx --domains nginx.wtf run
|
||||
test_deployment_and_rollback nginx.wtf
|
||||
certbot_test_nginx --domains nginx2.wtf --preferred-challenges http
|
||||
test_deployment_and_rollback nginx2.wtf
|
||||
# Overlapping location block and server-block-level return 301
|
||||
certbot_test_nginx --domains nginx3.wtf --preferred-challenges http
|
||||
test_deployment_and_rollback nginx3.wtf
|
||||
# No matching server block; default_server exists
|
||||
certbot_test_nginx --domains nginx4.wtf --preferred-challenges http
|
||||
test_deployment_and_rollback nginx4.wtf
|
||||
# No matching server block; default_server does not exist
|
||||
export default_server=""
|
||||
reload_nginx
|
||||
if nginx -c $nginx_root/nginx.conf -T 2>/dev/null | grep "default_server"; then
|
||||
echo "Failed to remove default_server"
|
||||
exit 1
|
||||
fi
|
||||
certbot_test_nginx --domains nginx5.wtf --preferred-challenges http
|
||||
test_deployment_and_rollback nginx5.wtf
|
||||
# Mutiple domains, mix of matching and not
|
||||
certbot_test_nginx --domains nginx6.wtf,nginx7.wtf --preferred-challenges http
|
||||
test_deployment_and_rollback nginx6.wtf
|
||||
|
||||
# note: not reached if anything above fails, hence "killall" at the
|
||||
# top
|
||||
|
||||
@@ -69,14 +69,15 @@ class AuthHandler(object):
|
||||
|
||||
# While there are still challenges remaining...
|
||||
while self._has_challenges(aauthzrs):
|
||||
resp = self._solve_challenges(aauthzrs)
|
||||
logger.info("Waiting for verification...")
|
||||
if config.debug_challenges:
|
||||
notify('Challenges loaded. Press continue to submit to CA. '
|
||||
'Pass "-v" for more info about challenges.', pause=True)
|
||||
with error_handler.ExitHandler(self._cleanup_challenges, aauthzrs):
|
||||
resp = self._solve_challenges(aauthzrs)
|
||||
logger.info("Waiting for verification...")
|
||||
if config.debug_challenges:
|
||||
notify('Challenges loaded. Press continue to submit to CA. '
|
||||
'Pass "-v" for more info about challenges.', pause=True)
|
||||
|
||||
# Send all Responses - this modifies achalls
|
||||
self._respond(aauthzrs, resp, best_effort)
|
||||
# Send all Responses - this modifies achalls
|
||||
self._respond(aauthzrs, resp, best_effort)
|
||||
|
||||
# Just make sure all decisions are complete.
|
||||
self.verify_authzr_complete(aauthzrs)
|
||||
@@ -118,14 +119,13 @@ class AuthHandler(object):
|
||||
"""Get Responses for challenges from authenticators."""
|
||||
resp = []
|
||||
all_achalls = self._get_all_achalls(aauthzrs)
|
||||
with error_handler.ErrorHandler(self._cleanup_challenges, aauthzrs, all_achalls):
|
||||
try:
|
||||
if all_achalls:
|
||||
resp = self.auth.perform(all_achalls)
|
||||
except errors.AuthorizationError:
|
||||
logger.critical("Failure in setting up challenges.")
|
||||
logger.info("Attempting to clean up outstanding challenges...")
|
||||
raise
|
||||
try:
|
||||
if all_achalls:
|
||||
resp = self.auth.perform(all_achalls)
|
||||
except errors.AuthorizationError:
|
||||
logger.critical("Failure in setting up challenges.")
|
||||
logger.info("Attempting to clean up outstanding challenges...")
|
||||
raise
|
||||
|
||||
assert len(resp) == len(all_achalls)
|
||||
|
||||
@@ -147,13 +147,10 @@ class AuthHandler(object):
|
||||
"""
|
||||
# TODO: chall_update is a dirty hack to get around acme-spec #105
|
||||
chall_update = dict()
|
||||
active_achalls = self._send_responses(aauthzrs, resp, chall_update)
|
||||
self._send_responses(aauthzrs, resp, chall_update)
|
||||
|
||||
# Check for updated status...
|
||||
try:
|
||||
self._poll_challenges(aauthzrs, chall_update, best_effort)
|
||||
finally:
|
||||
self._cleanup_challenges(aauthzrs, active_achalls)
|
||||
self._poll_challenges(aauthzrs, chall_update, best_effort)
|
||||
|
||||
def _send_responses(self, aauthzrs, resps, chall_update):
|
||||
"""Send responses and make sure errors are handled.
|
||||
@@ -294,7 +291,7 @@ class AuthHandler(object):
|
||||
chall_prefs.extend(plugin_pref)
|
||||
return chall_prefs
|
||||
|
||||
def _cleanup_challenges(self, aauthzrs, achalls):
|
||||
def _cleanup_challenges(self, aauthzrs, achalls=None):
|
||||
"""Cleanup challenges.
|
||||
|
||||
:param aauthzrs: authorizations and their selected annotated
|
||||
@@ -305,7 +302,8 @@ class AuthHandler(object):
|
||||
|
||||
"""
|
||||
logger.info("Cleaning up challenges")
|
||||
|
||||
if achalls is None:
|
||||
achalls = self._get_all_achalls(aauthzrs)
|
||||
if achalls:
|
||||
self.auth.cleanup(achalls)
|
||||
for achall in achalls:
|
||||
|
||||
@@ -84,7 +84,7 @@ CLI_DEFAULTS = dict(
|
||||
config_dir="/etc/letsencrypt",
|
||||
work_dir="/var/lib/letsencrypt",
|
||||
logs_dir="/var/log/letsencrypt",
|
||||
server="https://acme-v01.api.letsencrypt.org/directory",
|
||||
server="https://acme-v02.api.letsencrypt.org/directory",
|
||||
|
||||
# Plugins parsers
|
||||
configurator=None,
|
||||
|
||||
@@ -24,7 +24,6 @@ if os.name != "nt":
|
||||
if signal.getsignal(signal_code) != signal.SIG_IGN:
|
||||
_SIGNALS.append(signal_code)
|
||||
|
||||
|
||||
class ErrorHandler(object):
|
||||
"""Context manager for running code that must be cleaned up on failure.
|
||||
|
||||
@@ -55,6 +54,7 @@ class ErrorHandler(object):
|
||||
|
||||
"""
|
||||
def __init__(self, func=None, *args, **kwargs):
|
||||
self.call_on_regular_exit = False
|
||||
self.body_executed = False
|
||||
self.funcs = []
|
||||
self.prev_handlers = {}
|
||||
@@ -70,8 +70,11 @@ class ErrorHandler(object):
|
||||
self.body_executed = True
|
||||
retval = False
|
||||
# SystemExit is ignored to properly handle forks that don't exec
|
||||
if exec_type in (None, SystemExit):
|
||||
if exec_type is SystemExit:
|
||||
return retval
|
||||
elif exec_type is None:
|
||||
if not self.call_on_regular_exit:
|
||||
return retval
|
||||
elif exec_type is errors.SignalExit:
|
||||
logger.debug("Encountered signals: %s", self.received_signals)
|
||||
retval = True
|
||||
@@ -136,3 +139,15 @@ class ErrorHandler(object):
|
||||
for signum in self.received_signals:
|
||||
logger.debug("Calling signal %s", signum)
|
||||
os.kill(os.getpid(), signum)
|
||||
|
||||
class ExitHandler(ErrorHandler):
|
||||
"""Context manager for running code that must be cleaned up.
|
||||
|
||||
Subclass of ErrorHandler, with the same usage and parameters.
|
||||
In addition to cleaning up on all signals, also cleans up on
|
||||
regular exit.
|
||||
"""
|
||||
def __init__(self, func=None, *args, **kwargs):
|
||||
ErrorHandler.__init__(self, func, *args, **kwargs)
|
||||
self.call_on_regular_exit = True
|
||||
|
||||
|
||||
@@ -289,6 +289,32 @@ class HandleAuthorizationsTest(unittest.TestCase):
|
||||
self.assertEqual(
|
||||
self.mock_auth.cleanup.call_args[0][0][0].typ, "tls-sni-01")
|
||||
|
||||
@mock.patch("certbot.auth_handler.AuthHandler._respond")
|
||||
def test_respond_error(self, mock_respond):
|
||||
authzrs = [gen_dom_authzr(domain="0", challs=acme_util.CHALLENGES)]
|
||||
mock_order = mock.MagicMock(authorizations=authzrs)
|
||||
mock_respond.side_effect = errors.AuthorizationError
|
||||
|
||||
self.assertRaises(
|
||||
errors.AuthorizationError, self.handler.handle_authorizations, mock_order)
|
||||
self.assertEqual(self.mock_auth.cleanup.call_count, 1)
|
||||
self.assertEqual(
|
||||
self.mock_auth.cleanup.call_args[0][0][0].typ, "tls-sni-01")
|
||||
|
||||
@mock.patch("certbot.auth_handler.AuthHandler._poll_challenges")
|
||||
@mock.patch("certbot.auth_handler.AuthHandler.verify_authzr_complete")
|
||||
def test_incomplete_authzr_error(self, mock_verify, mock_poll):
|
||||
authzrs = [gen_dom_authzr(domain="0", challs=acme_util.CHALLENGES)]
|
||||
mock_order = mock.MagicMock(authorizations=authzrs)
|
||||
mock_verify.side_effect = errors.AuthorizationError
|
||||
mock_poll.side_effect = self._validate_all
|
||||
|
||||
self.assertRaises(
|
||||
errors.AuthorizationError, self.handler.handle_authorizations, mock_order)
|
||||
self.assertEqual(self.mock_auth.cleanup.call_count, 1)
|
||||
self.assertEqual(
|
||||
self.mock_auth.cleanup.call_args[0][0][0].typ, "tls-sni-01")
|
||||
|
||||
def _validate_all(self, aauthzrs, unused_1, unused_2):
|
||||
for i, aauthzr in enumerate(aauthzrs):
|
||||
azr = aauthzr.authzr
|
||||
|
||||
@@ -36,7 +36,7 @@ def send_signal(signum):
|
||||
|
||||
|
||||
class ErrorHandlerTest(unittest.TestCase):
|
||||
"""Tests for certbot.error_handler."""
|
||||
"""Tests for certbot.error_handler.ErrorHandler."""
|
||||
|
||||
def setUp(self):
|
||||
from certbot import error_handler
|
||||
@@ -47,6 +47,7 @@ class ErrorHandlerTest(unittest.TestCase):
|
||||
self.handler = error_handler.ErrorHandler(self.init_func,
|
||||
*self.init_args,
|
||||
**self.init_kwargs)
|
||||
|
||||
# pylint: disable=protected-access
|
||||
self.signals = error_handler._SIGNALS
|
||||
|
||||
@@ -113,6 +114,33 @@ class ErrorHandlerTest(unittest.TestCase):
|
||||
pass
|
||||
self.assertFalse(self.init_func.called)
|
||||
|
||||
def test_regular_exit(self):
|
||||
func = mock.MagicMock()
|
||||
self.handler.register(func)
|
||||
with self.handler:
|
||||
pass
|
||||
self.init_func.assert_not_called()
|
||||
func.assert_not_called()
|
||||
|
||||
|
||||
class ExitHandlerTest(ErrorHandlerTest):
|
||||
"""Tests for certbot.error_handler.ExitHandler."""
|
||||
|
||||
def setUp(self):
|
||||
from certbot import error_handler
|
||||
super(ExitHandlerTest, self).setUp()
|
||||
self.handler = error_handler.ExitHandler(self.init_func,
|
||||
*self.init_args,
|
||||
**self.init_kwargs)
|
||||
|
||||
def test_regular_exit(self):
|
||||
func = mock.MagicMock()
|
||||
self.handler.register(func)
|
||||
with self.handler:
|
||||
pass
|
||||
self.init_func.assert_called_once_with(*self.init_args,
|
||||
**self.init_kwargs)
|
||||
func.assert_called_once_with()
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main() # pragma: no cover
|
||||
|
||||
@@ -3,3 +3,7 @@
|
||||
|
||||
.. automodule:: certbot.constants
|
||||
:members:
|
||||
:exclude-members: SSL_DHPARAMS_SRC
|
||||
|
||||
.. autodata:: SSL_DHPARAMS_SRC
|
||||
:annotation: = '/path/to/certbot/ssl-dhparams.pem'
|
||||
|
||||
@@ -871,24 +871,16 @@ Example usage for DNS-01 (Cloudflare API v4) (for example purposes only, do not
|
||||
Changing the ACME Server
|
||||
========================
|
||||
|
||||
By default, Certbot uses Let's Encrypt's initial production server at
|
||||
https://acme-v01.api.letsencrypt.org/. You can tell Certbot to use a
|
||||
By default, Certbot uses Let's Encrypt's ACMEv2 production server at
|
||||
https://acme-v02.api.letsencrypt.org/. You can tell Certbot to use a
|
||||
different CA by providing ``--server`` on the command line or in a
|
||||
:ref:`configuration file <config-file>` with the URL of the server's
|
||||
ACME directory. For example, if you would like to use Let's Encrypt's
|
||||
new ACMEv2 server, you would add ``--server
|
||||
https://acme-v02.api.letsencrypt.org/directory`` to the command line.
|
||||
initial ACMEv1 server, you would add ``--server
|
||||
https://acme-v01.api.letsencrypt.org/directory`` to the command line.
|
||||
Certbot will automatically select which version of the ACME protocol to
|
||||
use based on the contents served at the provided URL.
|
||||
|
||||
If you use ``--server`` to specify an ACME CA that implements a newer
|
||||
version of the spec, you may be able to obtain a certificate for a
|
||||
wildcard domain. Some CAs (such as Let's Encrypt) require that domain
|
||||
validation for wildcard domains must be done through modifications to
|
||||
DNS records which means that the dns-01_ challenge type must be used. To
|
||||
see a list of Certbot plugins that support this challenge type and how
|
||||
to use them, see plugins_.
|
||||
|
||||
Lock Files
|
||||
==========
|
||||
|
||||
|
||||
@@ -11,7 +11,7 @@ RUN yum install -y python-pip sudo
|
||||
COPY ./pieces/pipstrap.py /opt
|
||||
RUN /opt/pipstrap.py
|
||||
# Pin pytest version for increased stability
|
||||
RUN pip install pytest==3.2.5
|
||||
RUN pip install pytest==3.2.5 six==1.10.0
|
||||
|
||||
# Add an unprivileged user:
|
||||
RUN useradd --create-home --home-dir /home/lea --shell /bin/bash --groups wheel --uid 1000 lea
|
||||
|
||||
@@ -15,7 +15,7 @@ RUN apt-get update && \
|
||||
COPY ./pieces/pipstrap.py /opt
|
||||
RUN /opt/pipstrap.py
|
||||
# Pin pytest version for increased stability
|
||||
RUN pip install pytest==3.2.5
|
||||
RUN pip install pytest==3.2.5 six==1.10.0
|
||||
|
||||
# Let that user sudo:
|
||||
RUN sed -i.bkp -e \
|
||||
|
||||
@@ -19,7 +19,7 @@ RUN apt-get update && \
|
||||
COPY ./pieces/pipstrap.py /opt
|
||||
RUN /opt/pipstrap.py
|
||||
# Pin pytest version for increased stability
|
||||
RUN pip install pytest==3.2.5
|
||||
RUN pip install pytest==3.2.5 six==1.10.0
|
||||
|
||||
RUN mkdir -p /home/lea/certbot
|
||||
|
||||
|
||||
@@ -14,7 +14,7 @@ RUN apt-get update && \
|
||||
COPY ./pieces/pipstrap.py /opt
|
||||
RUN /opt/pipstrap.py
|
||||
# Pin pytest version for increased stability
|
||||
RUN pip install pytest==3.2.5
|
||||
RUN pip install pytest==3.2.5 six==1.10.0
|
||||
|
||||
# Let that user sudo:
|
||||
RUN sed -i.bkp -e \
|
||||
|
||||
@@ -2,6 +2,6 @@
|
||||
|
||||
Run these locally by saying... ::
|
||||
|
||||
./build.py && docker build -t lea . && docker run --rm -t -i lea
|
||||
./build.py && docker build -t lea . -f Dockerfile.<distro> && docker run --rm -t -i lea
|
||||
|
||||
"""
|
||||
|
||||
@@ -12,7 +12,7 @@ botocore==1.7.41
|
||||
cloudflare==1.5.1
|
||||
coverage==4.4.2
|
||||
decorator==4.1.2
|
||||
dns-lexicon==2.1.14
|
||||
dns-lexicon==2.2.1
|
||||
dnspython==1.15.0
|
||||
docutils==0.14
|
||||
execnet==1.5.0
|
||||
|
||||
Reference in New Issue
Block a user