From 77fdb4d7d6194989dcc775f2e0ad81b6147c2359 Mon Sep 17 00:00:00 2001 From: Brad Warren Date: Wed, 7 Mar 2018 10:25:42 -0800 Subject: [PATCH 01/56] Release 0.22.0 --- acme/setup.py | 2 +- certbot-apache/setup.py | 2 +- certbot-auto | 104 +++++++++++------- certbot-compatibility-test/setup.py | 2 +- certbot-dns-cloudflare/setup.py | 2 +- certbot-dns-cloudxns/setup.py | 2 +- certbot-dns-digitalocean/setup.py | 2 +- certbot-dns-dnsimple/setup.py | 2 +- certbot-dns-dnsmadeeasy/setup.py | 2 +- certbot-dns-google/setup.py | 2 +- certbot-dns-luadns/setup.py | 2 +- certbot-dns-nsone/setup.py | 2 +- certbot-dns-rfc2136/setup.py | 2 +- certbot-dns-route53/setup.py | 2 +- certbot-nginx/setup.py | 2 +- certbot/__init__.py | 2 +- docs/cli-help.txt | 12 +- letsencrypt-auto | 104 +++++++++++------- letsencrypt-auto-source/certbot-auto.asc | 16 +-- letsencrypt-auto-source/letsencrypt-auto | 26 ++--- letsencrypt-auto-source/letsencrypt-auto.sig | Bin 256 -> 256 bytes .../pieces/certbot-requirements.txt | 24 ++-- 22 files changed, 186 insertions(+), 130 deletions(-) diff --git a/acme/setup.py b/acme/setup.py index 071b56ab3..93458785c 100644 --- a/acme/setup.py +++ b/acme/setup.py @@ -4,7 +4,7 @@ from setuptools import setup from setuptools import find_packages -version = '0.22.0.dev0' +version = '0.22.0' # Please update tox.ini when modifying dependency version requirements install_requires = [ diff --git a/certbot-apache/setup.py b/certbot-apache/setup.py index 7608c0647..3f64eadb7 100644 --- a/certbot-apache/setup.py +++ b/certbot-apache/setup.py @@ -4,7 +4,7 @@ from setuptools import setup from setuptools import find_packages -version = '0.22.0.dev0' +version = '0.22.0' # Remember to update local-oldest-requirements.txt when changing the minimum # acme/certbot version. diff --git a/certbot-auto b/certbot-auto index d3a5c23e5..343f56013 100755 --- a/certbot-auto +++ b/certbot-auto @@ -31,7 +31,7 @@ if [ -z "$VENV_PATH" ]; then fi VENV_BIN="$VENV_PATH/bin" BOOTSTRAP_VERSION_PATH="$VENV_PATH/certbot-auto-bootstrap-version.txt" -LE_AUTO_VERSION="0.21.1" +LE_AUTO_VERSION="0.22.0" BASENAME=$(basename $0) USAGE="Usage: $BASENAME [OPTIONS] A self-updating wrapper script for the Certbot ACME client. When run, updates @@ -47,6 +47,7 @@ Help for certbot itself cannot be provided until it is installed. --no-bootstrap do not install OS dependencies --no-self-upgrade do not download updates --os-packages-only install OS dependencies and exit + --install-only install certbot, upgrade if needed, and exit -v, --verbose provide more output -q, --quiet provide only update/error output; implies --non-interactive @@ -60,6 +61,8 @@ for arg in "$@" ; do DEBUG=1;; --os-packages-only) OS_PACKAGES_ONLY=1;; + --install-only) + INSTALL_ONLY=1;; --no-self-upgrade) # Do not upgrade this script (also prevents client upgrades, because each # copy of the script pins a hash of the python client) @@ -246,7 +249,7 @@ DeprecationBootstrap() { fi } -MIN_PYTHON_VERSION="2.6" +MIN_PYTHON_VERSION="2.7" MIN_PYVER=$(echo "$MIN_PYTHON_VERSION" | sed 's/\.//') # Sets LE_PYTHON to Python version string and PYVER to the first two # digits of the python version @@ -1196,24 +1199,24 @@ letsencrypt==0.7.0 \ --hash=sha256:105a5fb107e45bcd0722eb89696986dcf5f08a86a321d6aef25a0c7c63375ade \ --hash=sha256:c36e532c486a7e92155ee09da54b436a3c420813ec1c590b98f635d924720de9 -certbot==0.21.1 \ - --hash=sha256:08f026078807fbcfd7bfab44c4d827ee287738fefcc86fbe1493ce752d2fdccb \ - --hash=sha256:e6c8e9b0b5e38834330831d5a91e1c08accdb9b4923855d14d524e7327e6c4ea -acme==0.21.1 \ - --hash=sha256:4b2b5ef80c755dfa30eb5c67ab4b4e66e7f205ad922b43170502c5f8d8ef1242 \ - --hash=sha256:296e8abf4f5a69af1a892416faceea90e15f39e2920bf87beeaad1d6ce70a60b -certbot-apache==0.21.1 \ - --hash=sha256:faa4af1033564a0e676d16940775593fb849527b494a15f6a816ad0ed4fa273c \ - --hash=sha256:0bce4419d4fdabbdda2223cff8db6794c5717632fb9511b00498ec00982a3fa5 -certbot-nginx==0.21.1 \ - --hash=sha256:3fad3b4722544558ce03132f853e18da5e516013086aaa40f1036aa6667c70a9 \ - --hash=sha256:55a32afe0950ff49d3118f93035463a46c85c2f399d261123f5fe973afdd4f64 +certbot==0.22.0 \ + --hash=sha256:ebfeaf9737dc440a9f263099487523ab4c8d8da9def31a71327439d9186e00fa \ + --hash=sha256:ee307dd8f194bd710a3326aa4bacf95d358877498c0b9aa187eff0dc211dcbb3 +acme==0.22.0 \ + --hash=sha256:37e6d8e4eb7dd18edac96de209f451300e04074f14be7fce713db6931a0e4a20 \ + --hash=sha256:4a2cd52db32e914b68d8446c8e788f507c20edebbd1c36d4f3eda7b47c555fe8 +certbot-apache==0.22.0 \ + --hash=sha256:e91f6ec8203b636fa44f01017646fca68406224ee327fd56017103b78bc65539 \ + --hash=sha256:8fbab1a358ec131996d1c00f7d0ed18ee3624f8469cab3962dfd8ba40ca3e7cd +certbot-nginx==0.22.0 \ + --hash=sha256:d67210cf73cf44e8aeff04f6f228d8bde74444703ce3ccd929a450685b58c30b \ + --hash=sha256:b2b26bf9112062b02518407704cad09f7136322163d529a2dde3b6e1578ecb8c UNLIKELY_EOF # ------------------------------------------------------------------------- cat << "UNLIKELY_EOF" > "$TEMP_DIR/pipstrap.py" #!/usr/bin/env python -"""A small script that can act as a trust root for installing pip 8 +"""A small script that can act as a trust root for installing pip >=8 Embed this in your project, and your VCS checkout is all you have to trust. In a post-peep era, this lets you claw your way to a hash-checking version of pip, @@ -1237,6 +1240,7 @@ anything goes wrong, it will exit with a non-zero status code. from __future__ import print_function from distutils.version import StrictVersion from hashlib import sha256 +from os import environ from os.path import join from pipes import quote from shutil import rmtree @@ -1270,14 +1274,14 @@ except ImportError: from urllib.parse import urlparse # 3.4 -__version__ = 1, 3, 0 +__version__ = 1, 5, 1 PIP_VERSION = '9.0.1' +DEFAULT_INDEX_BASE = 'https://pypi.python.org' # wheel has a conditional dependency on argparse: maybe_argparse = ( - [('https://pypi.python.org/packages/18/dd/' - 'e617cfc3f6210ae183374cd9f6a26b20514bbb5a792af97949c5aacddf0f/' + [('18/dd/e617cfc3f6210ae183374cd9f6a26b20514bbb5a792af97949c5aacddf0f/' 'argparse-1.4.0.tar.gz', '62b089a55be1d8949cd2bc7e0df0bddb9e028faefc8c32038cc84862aefdd6e4')] if version_info < (2, 7, 0) else []) @@ -1285,18 +1289,14 @@ maybe_argparse = ( PACKAGES = maybe_argparse + [ # Pip has no dependencies, as it vendors everything: - ('https://pypi.python.org/packages/11/b6/' - 'abcb525026a4be042b486df43905d6893fb04f05aac21c32c638e939e447/' - 'pip-{0}.tar.gz' - .format(PIP_VERSION), + ('11/b6/abcb525026a4be042b486df43905d6893fb04f05aac21c32c638e939e447/' + 'pip-{0}.tar.gz'.format(PIP_VERSION), '09f243e1a7b461f654c26a725fa373211bb7ff17a9300058b205c61658ca940d'), # This version of setuptools has only optional dependencies: - ('https://pypi.python.org/packages/69/65/' - '4c544cde88d4d876cdf5cbc5f3f15d02646477756d89547e9a7ecd6afa76/' - 'setuptools-20.2.2.tar.gz', - '24fcfc15364a9fe09a220f37d2dcedc849795e3de3e4b393ee988e66a9cbd85a'), - ('https://pypi.python.org/packages/c9/1d/' - 'bd19e691fd4cfe908c76c429fe6e4436c9e83583c4414b54f6c85471954a/' + ('59/88/2f3990916931a5de6fa9706d6d75eb32ee8b78627bb2abaab7ed9e6d0622/' + 'setuptools-29.0.1.tar.gz', + 'b539118819a4857378398891fa5366e090690e46b3e41421a1e07d6e9fd8feb0'), + ('c9/1d/bd19e691fd4cfe908c76c429fe6e4436c9e83583c4414b54f6c85471954a/' 'wheel-0.29.0.tar.gz', '1ebb8ad7e26b448e9caa4773d2357849bf80ff9e313964bcaf79cbf0201a1648') ] @@ -1317,12 +1317,13 @@ def hashed_download(url, temp, digest): # >=2.7.9 verifies HTTPS certs itself, and, in any case, the cert # authenticity has only privacy (not arbitrary code execution) # implications, since we're checking hashes. - def opener(): + def opener(using_https=True): opener = build_opener(HTTPSHandler()) - # Strip out HTTPHandler to prevent MITM spoof: - for handler in opener.handlers: - if isinstance(handler, HTTPHandler): - opener.handlers.remove(handler) + if using_https: + # Strip out HTTPHandler to prevent MITM spoof: + for handler in opener.handlers: + if isinstance(handler, HTTPHandler): + opener.handlers.remove(handler) return opener def read_chunks(response, chunk_size): @@ -1332,8 +1333,9 @@ def hashed_download(url, temp, digest): break yield chunk - response = opener().open(url) - path = join(temp, urlparse(url).path.split('/')[-1]) + parsed_url = urlparse(url) + response = opener(using_https=parsed_url.scheme == 'https').open(url) + path = join(temp, parsed_url.path.split('/')[-1]) actual_hash = sha256() with open(path, 'wb') as file: for chunk in read_chunks(response, 4096): @@ -1346,6 +1348,24 @@ def hashed_download(url, temp, digest): return path +def get_index_base(): + """Return the URL to the dir containing the "packages" folder. + + Try to wring something out of PIP_INDEX_URL, if set. Hack "/simple" off the + end if it's there; that is likely to give us the right dir. + + """ + env_var = environ.get('PIP_INDEX_URL', '').rstrip('/') + if env_var: + SIMPLE = '/simple' + if env_var.endswith(SIMPLE): + return env_var[:-len(SIMPLE)] + else: + return env_var + else: + return DEFAULT_INDEX_BASE + + def main(): pip_version = StrictVersion(check_output(['pip', '--version']) .decode('utf-8').split()[1]) @@ -1353,11 +1373,13 @@ def main(): if pip_version >= min_pip_version: return 0 has_pip_cache = pip_version >= StrictVersion('6.0') - + index_base = get_index_base() temp = mkdtemp(prefix='pipstrap-') try: - downloads = [hashed_download(url, temp, digest) - for url, digest in PACKAGES] + downloads = [hashed_download(index_base + '/packages/' + path, + temp, + digest) + for path, digest in PACKAGES] check_output('pip install --no-index --no-deps -U ' + # Disable cache since we're not using it and it otherwise # sometimes throws permission warnings: @@ -1428,6 +1450,12 @@ UNLIKELY_EOF say "Installation succeeded." fi + + if [ "$INSTALL_ONLY" = 1 ]; then + say "Certbot is installed." + exit 0 + fi + "$VENV_BIN/letsencrypt" "$@" else diff --git a/certbot-compatibility-test/setup.py b/certbot-compatibility-test/setup.py index 861921ef7..c4073ed39 100644 --- a/certbot-compatibility-test/setup.py +++ b/certbot-compatibility-test/setup.py @@ -4,7 +4,7 @@ from setuptools import setup from setuptools import find_packages -version = '0.22.0.dev0' +version = '0.22.0' install_requires = [ 'certbot', diff --git a/certbot-dns-cloudflare/setup.py b/certbot-dns-cloudflare/setup.py index 4ed8e796d..780ea6db6 100644 --- a/certbot-dns-cloudflare/setup.py +++ b/certbot-dns-cloudflare/setup.py @@ -4,7 +4,7 @@ from setuptools import setup from setuptools import find_packages -version = '0.22.0.dev0' +version = '0.22.0' # Remember to update local-oldest-requirements.txt when changing the minimum # acme/certbot version. diff --git a/certbot-dns-cloudxns/setup.py b/certbot-dns-cloudxns/setup.py index 7f973709c..8c6e64af5 100644 --- a/certbot-dns-cloudxns/setup.py +++ b/certbot-dns-cloudxns/setup.py @@ -4,7 +4,7 @@ from setuptools import setup from setuptools import find_packages -version = '0.22.0.dev0' +version = '0.22.0' # Remember to update local-oldest-requirements.txt when changing the minimum # acme/certbot version. diff --git a/certbot-dns-digitalocean/setup.py b/certbot-dns-digitalocean/setup.py index 0ce91e64e..7dbf9dab5 100644 --- a/certbot-dns-digitalocean/setup.py +++ b/certbot-dns-digitalocean/setup.py @@ -4,7 +4,7 @@ from setuptools import setup from setuptools import find_packages -version = '0.22.0.dev0' +version = '0.22.0' # Remember to update local-oldest-requirements.txt when changing the minimum # acme/certbot version. diff --git a/certbot-dns-dnsimple/setup.py b/certbot-dns-dnsimple/setup.py index d12b26d83..c8593b7f7 100644 --- a/certbot-dns-dnsimple/setup.py +++ b/certbot-dns-dnsimple/setup.py @@ -4,7 +4,7 @@ from setuptools import setup from setuptools import find_packages -version = '0.22.0.dev0' +version = '0.22.0' # Remember to update local-oldest-requirements.txt when changing the minimum # acme/certbot version. diff --git a/certbot-dns-dnsmadeeasy/setup.py b/certbot-dns-dnsmadeeasy/setup.py index 856eaba0f..ec2cfd3ad 100644 --- a/certbot-dns-dnsmadeeasy/setup.py +++ b/certbot-dns-dnsmadeeasy/setup.py @@ -4,7 +4,7 @@ from setuptools import setup from setuptools import find_packages -version = '0.22.0.dev0' +version = '0.22.0' # Remember to update local-oldest-requirements.txt when changing the minimum # acme/certbot version. diff --git a/certbot-dns-google/setup.py b/certbot-dns-google/setup.py index 0dfff0402..900e9e4b2 100644 --- a/certbot-dns-google/setup.py +++ b/certbot-dns-google/setup.py @@ -4,7 +4,7 @@ from setuptools import setup from setuptools import find_packages -version = '0.22.0.dev0' +version = '0.22.0' # Remember to update local-oldest-requirements.txt when changing the minimum # acme/certbot version. diff --git a/certbot-dns-luadns/setup.py b/certbot-dns-luadns/setup.py index b255691dc..36b80bf19 100644 --- a/certbot-dns-luadns/setup.py +++ b/certbot-dns-luadns/setup.py @@ -4,7 +4,7 @@ from setuptools import setup from setuptools import find_packages -version = '0.22.0.dev0' +version = '0.22.0' # Remember to update local-oldest-requirements.txt when changing the minimum # acme/certbot version. diff --git a/certbot-dns-nsone/setup.py b/certbot-dns-nsone/setup.py index 68d8f6cdb..a5bfe256b 100644 --- a/certbot-dns-nsone/setup.py +++ b/certbot-dns-nsone/setup.py @@ -4,7 +4,7 @@ from setuptools import setup from setuptools import find_packages -version = '0.22.0.dev0' +version = '0.22.0' # Remember to update local-oldest-requirements.txt when changing the minimum # acme/certbot version. diff --git a/certbot-dns-rfc2136/setup.py b/certbot-dns-rfc2136/setup.py index 3d6b3799b..442a1e4af 100644 --- a/certbot-dns-rfc2136/setup.py +++ b/certbot-dns-rfc2136/setup.py @@ -4,7 +4,7 @@ from setuptools import setup from setuptools import find_packages -version = '0.22.0.dev0' +version = '0.22.0' # Remember to update local-oldest-requirements.txt when changing the minimum # acme/certbot version. diff --git a/certbot-dns-route53/setup.py b/certbot-dns-route53/setup.py index ad20725b5..b85c13dcd 100644 --- a/certbot-dns-route53/setup.py +++ b/certbot-dns-route53/setup.py @@ -3,7 +3,7 @@ import sys from distutils.core import setup from setuptools import find_packages -version = '0.22.0.dev0' +version = '0.22.0' # Remember to update local-oldest-requirements.txt when changing the minimum # acme/certbot version. diff --git a/certbot-nginx/setup.py b/certbot-nginx/setup.py index bb71cf19a..e2715adda 100644 --- a/certbot-nginx/setup.py +++ b/certbot-nginx/setup.py @@ -4,7 +4,7 @@ from setuptools import setup from setuptools import find_packages -version = '0.22.0.dev0' +version = '0.22.0' # Remember to update local-oldest-requirements.txt when changing the minimum # acme/certbot version. diff --git a/certbot/__init__.py b/certbot/__init__.py index 2869d29b0..97fdb75d7 100644 --- a/certbot/__init__.py +++ b/certbot/__init__.py @@ -1,4 +1,4 @@ """Certbot client.""" # version number like 1.2.3a0, must have at least 2 parts, like 1.2 -__version__ = '0.22.0.dev0' +__version__ = '0.22.0' diff --git a/docs/cli-help.txt b/docs/cli-help.txt index abebdb9c9..65f623d79 100644 --- a/docs/cli-help.txt +++ b/docs/cli-help.txt @@ -107,9 +107,9 @@ optional arguments: case, and to know when to deprecate support for past Python versions and flags. If you wish to hide this information from the Let's Encrypt server, set this to - "". (default: CertbotACMEClient/0.21.1 (certbot; + "". (default: CertbotACMEClient/0.22.0 (certbot; darwin 10.13.3) Authenticator/XXX Installer/YYY - (SUBCOMMAND; flags: FLAGS) Py/2.7.14). The flags + (SUBCOMMAND; flags: FLAGS) Py/3.6.4). The flags encoded in the user agent are: --duplicate, --force- renew, --allow-subset-of-names, -n, and whether any hooks are set. @@ -199,8 +199,8 @@ testing: --test-cert, --staging Use the staging server to obtain or revoke test - (invalid) certificates; equivalent to --server https - ://acme-staging.api.letsencrypt.org/directory + (invalid) certificates; equivalent to --server + https://acme-staging.api.letsencrypt.org/directory (default: False) --debug Show tracebacks in case of errors, and allow certbot- auto execution on experimental platforms (default: @@ -308,8 +308,8 @@ renew: of renewed certificate domains (for example, "example.com www.example.com" (default: None) --disable-hook-validation - Ordinarily the commands specified for --pre-hook - /--post-hook/--deploy-hook will be checked for + Ordinarily the commands specified for --pre- + hook/--post-hook/--deploy-hook will be checked for validity, to see if the programs being run are in the $PATH, so that mistakes can be caught early, even when the hooks aren't being run just yet. The validation is diff --git a/letsencrypt-auto b/letsencrypt-auto index d3a5c23e5..343f56013 100755 --- a/letsencrypt-auto +++ b/letsencrypt-auto @@ -31,7 +31,7 @@ if [ -z "$VENV_PATH" ]; then fi VENV_BIN="$VENV_PATH/bin" BOOTSTRAP_VERSION_PATH="$VENV_PATH/certbot-auto-bootstrap-version.txt" -LE_AUTO_VERSION="0.21.1" +LE_AUTO_VERSION="0.22.0" BASENAME=$(basename $0) USAGE="Usage: $BASENAME [OPTIONS] A self-updating wrapper script for the Certbot ACME client. When run, updates @@ -47,6 +47,7 @@ Help for certbot itself cannot be provided until it is installed. --no-bootstrap do not install OS dependencies --no-self-upgrade do not download updates --os-packages-only install OS dependencies and exit + --install-only install certbot, upgrade if needed, and exit -v, --verbose provide more output -q, --quiet provide only update/error output; implies --non-interactive @@ -60,6 +61,8 @@ for arg in "$@" ; do DEBUG=1;; --os-packages-only) OS_PACKAGES_ONLY=1;; + --install-only) + INSTALL_ONLY=1;; --no-self-upgrade) # Do not upgrade this script (also prevents client upgrades, because each # copy of the script pins a hash of the python client) @@ -246,7 +249,7 @@ DeprecationBootstrap() { fi } -MIN_PYTHON_VERSION="2.6" +MIN_PYTHON_VERSION="2.7" MIN_PYVER=$(echo "$MIN_PYTHON_VERSION" | sed 's/\.//') # Sets LE_PYTHON to Python version string and PYVER to the first two # digits of the python version @@ -1196,24 +1199,24 @@ letsencrypt==0.7.0 \ --hash=sha256:105a5fb107e45bcd0722eb89696986dcf5f08a86a321d6aef25a0c7c63375ade \ --hash=sha256:c36e532c486a7e92155ee09da54b436a3c420813ec1c590b98f635d924720de9 -certbot==0.21.1 \ - --hash=sha256:08f026078807fbcfd7bfab44c4d827ee287738fefcc86fbe1493ce752d2fdccb \ - --hash=sha256:e6c8e9b0b5e38834330831d5a91e1c08accdb9b4923855d14d524e7327e6c4ea -acme==0.21.1 \ - --hash=sha256:4b2b5ef80c755dfa30eb5c67ab4b4e66e7f205ad922b43170502c5f8d8ef1242 \ - --hash=sha256:296e8abf4f5a69af1a892416faceea90e15f39e2920bf87beeaad1d6ce70a60b -certbot-apache==0.21.1 \ - --hash=sha256:faa4af1033564a0e676d16940775593fb849527b494a15f6a816ad0ed4fa273c \ - --hash=sha256:0bce4419d4fdabbdda2223cff8db6794c5717632fb9511b00498ec00982a3fa5 -certbot-nginx==0.21.1 \ - --hash=sha256:3fad3b4722544558ce03132f853e18da5e516013086aaa40f1036aa6667c70a9 \ - --hash=sha256:55a32afe0950ff49d3118f93035463a46c85c2f399d261123f5fe973afdd4f64 +certbot==0.22.0 \ + --hash=sha256:ebfeaf9737dc440a9f263099487523ab4c8d8da9def31a71327439d9186e00fa \ + --hash=sha256:ee307dd8f194bd710a3326aa4bacf95d358877498c0b9aa187eff0dc211dcbb3 +acme==0.22.0 \ + --hash=sha256:37e6d8e4eb7dd18edac96de209f451300e04074f14be7fce713db6931a0e4a20 \ + --hash=sha256:4a2cd52db32e914b68d8446c8e788f507c20edebbd1c36d4f3eda7b47c555fe8 +certbot-apache==0.22.0 \ + --hash=sha256:e91f6ec8203b636fa44f01017646fca68406224ee327fd56017103b78bc65539 \ + --hash=sha256:8fbab1a358ec131996d1c00f7d0ed18ee3624f8469cab3962dfd8ba40ca3e7cd +certbot-nginx==0.22.0 \ + --hash=sha256:d67210cf73cf44e8aeff04f6f228d8bde74444703ce3ccd929a450685b58c30b \ + --hash=sha256:b2b26bf9112062b02518407704cad09f7136322163d529a2dde3b6e1578ecb8c UNLIKELY_EOF # ------------------------------------------------------------------------- cat << "UNLIKELY_EOF" > "$TEMP_DIR/pipstrap.py" #!/usr/bin/env python -"""A small script that can act as a trust root for installing pip 8 +"""A small script that can act as a trust root for installing pip >=8 Embed this in your project, and your VCS checkout is all you have to trust. In a post-peep era, this lets you claw your way to a hash-checking version of pip, @@ -1237,6 +1240,7 @@ anything goes wrong, it will exit with a non-zero status code. from __future__ import print_function from distutils.version import StrictVersion from hashlib import sha256 +from os import environ from os.path import join from pipes import quote from shutil import rmtree @@ -1270,14 +1274,14 @@ except ImportError: from urllib.parse import urlparse # 3.4 -__version__ = 1, 3, 0 +__version__ = 1, 5, 1 PIP_VERSION = '9.0.1' +DEFAULT_INDEX_BASE = 'https://pypi.python.org' # wheel has a conditional dependency on argparse: maybe_argparse = ( - [('https://pypi.python.org/packages/18/dd/' - 'e617cfc3f6210ae183374cd9f6a26b20514bbb5a792af97949c5aacddf0f/' + [('18/dd/e617cfc3f6210ae183374cd9f6a26b20514bbb5a792af97949c5aacddf0f/' 'argparse-1.4.0.tar.gz', '62b089a55be1d8949cd2bc7e0df0bddb9e028faefc8c32038cc84862aefdd6e4')] if version_info < (2, 7, 0) else []) @@ -1285,18 +1289,14 @@ maybe_argparse = ( PACKAGES = maybe_argparse + [ # Pip has no dependencies, as it vendors everything: - ('https://pypi.python.org/packages/11/b6/' - 'abcb525026a4be042b486df43905d6893fb04f05aac21c32c638e939e447/' - 'pip-{0}.tar.gz' - .format(PIP_VERSION), + ('11/b6/abcb525026a4be042b486df43905d6893fb04f05aac21c32c638e939e447/' + 'pip-{0}.tar.gz'.format(PIP_VERSION), '09f243e1a7b461f654c26a725fa373211bb7ff17a9300058b205c61658ca940d'), # This version of setuptools has only optional dependencies: - ('https://pypi.python.org/packages/69/65/' - '4c544cde88d4d876cdf5cbc5f3f15d02646477756d89547e9a7ecd6afa76/' - 'setuptools-20.2.2.tar.gz', - '24fcfc15364a9fe09a220f37d2dcedc849795e3de3e4b393ee988e66a9cbd85a'), - ('https://pypi.python.org/packages/c9/1d/' - 'bd19e691fd4cfe908c76c429fe6e4436c9e83583c4414b54f6c85471954a/' + ('59/88/2f3990916931a5de6fa9706d6d75eb32ee8b78627bb2abaab7ed9e6d0622/' + 'setuptools-29.0.1.tar.gz', + 'b539118819a4857378398891fa5366e090690e46b3e41421a1e07d6e9fd8feb0'), + ('c9/1d/bd19e691fd4cfe908c76c429fe6e4436c9e83583c4414b54f6c85471954a/' 'wheel-0.29.0.tar.gz', '1ebb8ad7e26b448e9caa4773d2357849bf80ff9e313964bcaf79cbf0201a1648') ] @@ -1317,12 +1317,13 @@ def hashed_download(url, temp, digest): # >=2.7.9 verifies HTTPS certs itself, and, in any case, the cert # authenticity has only privacy (not arbitrary code execution) # implications, since we're checking hashes. - def opener(): + def opener(using_https=True): opener = build_opener(HTTPSHandler()) - # Strip out HTTPHandler to prevent MITM spoof: - for handler in opener.handlers: - if isinstance(handler, HTTPHandler): - opener.handlers.remove(handler) + if using_https: + # Strip out HTTPHandler to prevent MITM spoof: + for handler in opener.handlers: + if isinstance(handler, HTTPHandler): + opener.handlers.remove(handler) return opener def read_chunks(response, chunk_size): @@ -1332,8 +1333,9 @@ def hashed_download(url, temp, digest): break yield chunk - response = opener().open(url) - path = join(temp, urlparse(url).path.split('/')[-1]) + parsed_url = urlparse(url) + response = opener(using_https=parsed_url.scheme == 'https').open(url) + path = join(temp, parsed_url.path.split('/')[-1]) actual_hash = sha256() with open(path, 'wb') as file: for chunk in read_chunks(response, 4096): @@ -1346,6 +1348,24 @@ def hashed_download(url, temp, digest): return path +def get_index_base(): + """Return the URL to the dir containing the "packages" folder. + + Try to wring something out of PIP_INDEX_URL, if set. Hack "/simple" off the + end if it's there; that is likely to give us the right dir. + + """ + env_var = environ.get('PIP_INDEX_URL', '').rstrip('/') + if env_var: + SIMPLE = '/simple' + if env_var.endswith(SIMPLE): + return env_var[:-len(SIMPLE)] + else: + return env_var + else: + return DEFAULT_INDEX_BASE + + def main(): pip_version = StrictVersion(check_output(['pip', '--version']) .decode('utf-8').split()[1]) @@ -1353,11 +1373,13 @@ def main(): if pip_version >= min_pip_version: return 0 has_pip_cache = pip_version >= StrictVersion('6.0') - + index_base = get_index_base() temp = mkdtemp(prefix='pipstrap-') try: - downloads = [hashed_download(url, temp, digest) - for url, digest in PACKAGES] + downloads = [hashed_download(index_base + '/packages/' + path, + temp, + digest) + for path, digest in PACKAGES] check_output('pip install --no-index --no-deps -U ' + # Disable cache since we're not using it and it otherwise # sometimes throws permission warnings: @@ -1428,6 +1450,12 @@ UNLIKELY_EOF say "Installation succeeded." fi + + if [ "$INSTALL_ONLY" = 1 ]; then + say "Certbot is installed." + exit 0 + fi + "$VENV_BIN/letsencrypt" "$@" else diff --git a/letsencrypt-auto-source/certbot-auto.asc b/letsencrypt-auto-source/certbot-auto.asc index f28fd9893..e9dd75a11 100644 --- a/letsencrypt-auto-source/certbot-auto.asc +++ b/letsencrypt-auto-source/certbot-auto.asc @@ -1,11 +1,11 @@ -----BEGIN PGP SIGNATURE----- -iQEzBAABCAAdFiEEos+1H6J1pyhiNOeyTRfJlc2XdfIFAlpqMlYACgkQTRfJlc2X -dfKHfQgAnZQJ34jFoVqEodT0EjvkFKZif4V/zXTsVwTHn107BcLCpH/9gjANrSo3 -JpvseH2q0odhOAZA4rZKH4Geh+5fsUl3Ew9YB28RXeyqEfCATUqPq6q+jAi55SLc -a064Ux5N7eOIh9gxvpDKBeSFD0eNB8IDtPQhUspr+WnoycawrJHNGawL8WIfrWY3 -0ZPF981iPCWCdN3woDP9wHA2QtBClAk2pQ1aMgdkK9r/QLO+DY92xmT/Uu4ik2jR -zv+QplsQLftjD+bRar5R9jiCWV5phPqrOF3ypMiU0K5bsnrZfGBzBcoEyfKuB+UR -F/j/631OC6yLRasr+xcL1gc+SCryfA== -=tkZT +iQEzBAABCAAdFiEEos+1H6J1pyhiNOeyTRfJlc2XdfIFAlqgLj0ACgkQTRfJlc2X +dfIAtAf/YwRvn17fdJOSXr08LP/qDPefz8AFefUJdKoOa+ikWIOWZTh5hskGtXO0 +e894FNcZqbg6NQu/KUBQAz/nBDRz8IOaWN30MFq5B4V2A3In5rn59PNaCDSKSBbC +auyU24gYkBxbDPjMpuode7yCsvHxTsB5sLNmHByMyMTBmQaiT5odAjr7PztTP52S +s/29/WOCJAYzBBFFJ9d0QD0drVSIcDM5JCuUK2vXgPuPVD4f3GankgP1nnAJ5ADV +acJp3cQ3OsofeE/HTw0qq7TiL0dGYf8yhRFovFve7tX+oujMIRALQJW6K9Qi7KTv +777V6xHuphrA+1qIrg2H8czOBDclFQ== +=Ngvl -----END PGP SIGNATURE----- diff --git a/letsencrypt-auto-source/letsencrypt-auto b/letsencrypt-auto-source/letsencrypt-auto index f97dc078d..343f56013 100755 --- a/letsencrypt-auto-source/letsencrypt-auto +++ b/letsencrypt-auto-source/letsencrypt-auto @@ -31,7 +31,7 @@ if [ -z "$VENV_PATH" ]; then fi VENV_BIN="$VENV_PATH/bin" BOOTSTRAP_VERSION_PATH="$VENV_PATH/certbot-auto-bootstrap-version.txt" -LE_AUTO_VERSION="0.22.0.dev0" +LE_AUTO_VERSION="0.22.0" BASENAME=$(basename $0) USAGE="Usage: $BASENAME [OPTIONS] A self-updating wrapper script for the Certbot ACME client. When run, updates @@ -1199,18 +1199,18 @@ letsencrypt==0.7.0 \ --hash=sha256:105a5fb107e45bcd0722eb89696986dcf5f08a86a321d6aef25a0c7c63375ade \ --hash=sha256:c36e532c486a7e92155ee09da54b436a3c420813ec1c590b98f635d924720de9 -certbot==0.21.1 \ - --hash=sha256:08f026078807fbcfd7bfab44c4d827ee287738fefcc86fbe1493ce752d2fdccb \ - --hash=sha256:e6c8e9b0b5e38834330831d5a91e1c08accdb9b4923855d14d524e7327e6c4ea -acme==0.21.1 \ - --hash=sha256:4b2b5ef80c755dfa30eb5c67ab4b4e66e7f205ad922b43170502c5f8d8ef1242 \ - --hash=sha256:296e8abf4f5a69af1a892416faceea90e15f39e2920bf87beeaad1d6ce70a60b -certbot-apache==0.21.1 \ - --hash=sha256:faa4af1033564a0e676d16940775593fb849527b494a15f6a816ad0ed4fa273c \ - --hash=sha256:0bce4419d4fdabbdda2223cff8db6794c5717632fb9511b00498ec00982a3fa5 -certbot-nginx==0.21.1 \ - --hash=sha256:3fad3b4722544558ce03132f853e18da5e516013086aaa40f1036aa6667c70a9 \ - --hash=sha256:55a32afe0950ff49d3118f93035463a46c85c2f399d261123f5fe973afdd4f64 +certbot==0.22.0 \ + --hash=sha256:ebfeaf9737dc440a9f263099487523ab4c8d8da9def31a71327439d9186e00fa \ + --hash=sha256:ee307dd8f194bd710a3326aa4bacf95d358877498c0b9aa187eff0dc211dcbb3 +acme==0.22.0 \ + --hash=sha256:37e6d8e4eb7dd18edac96de209f451300e04074f14be7fce713db6931a0e4a20 \ + --hash=sha256:4a2cd52db32e914b68d8446c8e788f507c20edebbd1c36d4f3eda7b47c555fe8 +certbot-apache==0.22.0 \ + --hash=sha256:e91f6ec8203b636fa44f01017646fca68406224ee327fd56017103b78bc65539 \ + --hash=sha256:8fbab1a358ec131996d1c00f7d0ed18ee3624f8469cab3962dfd8ba40ca3e7cd +certbot-nginx==0.22.0 \ + --hash=sha256:d67210cf73cf44e8aeff04f6f228d8bde74444703ce3ccd929a450685b58c30b \ + --hash=sha256:b2b26bf9112062b02518407704cad09f7136322163d529a2dde3b6e1578ecb8c UNLIKELY_EOF # ------------------------------------------------------------------------- diff --git a/letsencrypt-auto-source/letsencrypt-auto.sig b/letsencrypt-auto-source/letsencrypt-auto.sig index 8dd6837754301180039bdcd2192ec2ffa9911f1e..21cbf1a09de3849414c6c163dced1ead3fb2e131 100644 GIT binary patch literal 256 zcmV+b0ssC1UnH~cd*`0q_j<H6AHDK;n~Qf+SKW+Xy3TJcAQX-?GAmynNPos)KcguU5#wAO5CBQ$`2#CmQb9#87kxckZun8=50Gy6T(2&LYe?9T(@a* Date: Wed, 7 Mar 2018 10:26:08 -0800 Subject: [PATCH 02/56] Bump version to 0.23.0 --- acme/setup.py | 2 +- certbot-apache/setup.py | 2 +- certbot-compatibility-test/setup.py | 2 +- certbot-dns-cloudflare/setup.py | 2 +- certbot-dns-cloudxns/setup.py | 2 +- certbot-dns-digitalocean/setup.py | 2 +- certbot-dns-dnsimple/setup.py | 2 +- certbot-dns-dnsmadeeasy/setup.py | 2 +- certbot-dns-google/setup.py | 2 +- certbot-dns-luadns/setup.py | 2 +- certbot-dns-nsone/setup.py | 2 +- certbot-dns-rfc2136/setup.py | 2 +- certbot-dns-route53/setup.py | 2 +- certbot-nginx/setup.py | 2 +- certbot/__init__.py | 2 +- letsencrypt-auto-source/letsencrypt-auto | 2 +- 16 files changed, 16 insertions(+), 16 deletions(-) diff --git a/acme/setup.py b/acme/setup.py index 93458785c..5660cf424 100644 --- a/acme/setup.py +++ b/acme/setup.py @@ -4,7 +4,7 @@ from setuptools import setup from setuptools import find_packages -version = '0.22.0' +version = '0.23.0.dev0' # Please update tox.ini when modifying dependency version requirements install_requires = [ diff --git a/certbot-apache/setup.py b/certbot-apache/setup.py index 3f64eadb7..f00b6d95d 100644 --- a/certbot-apache/setup.py +++ b/certbot-apache/setup.py @@ -4,7 +4,7 @@ from setuptools import setup from setuptools import find_packages -version = '0.22.0' +version = '0.23.0.dev0' # Remember to update local-oldest-requirements.txt when changing the minimum # acme/certbot version. diff --git a/certbot-compatibility-test/setup.py b/certbot-compatibility-test/setup.py index c4073ed39..17abe65ec 100644 --- a/certbot-compatibility-test/setup.py +++ b/certbot-compatibility-test/setup.py @@ -4,7 +4,7 @@ from setuptools import setup from setuptools import find_packages -version = '0.22.0' +version = '0.23.0.dev0' install_requires = [ 'certbot', diff --git a/certbot-dns-cloudflare/setup.py b/certbot-dns-cloudflare/setup.py index 780ea6db6..956e37f79 100644 --- a/certbot-dns-cloudflare/setup.py +++ b/certbot-dns-cloudflare/setup.py @@ -4,7 +4,7 @@ from setuptools import setup from setuptools import find_packages -version = '0.22.0' +version = '0.23.0.dev0' # Remember to update local-oldest-requirements.txt when changing the minimum # acme/certbot version. diff --git a/certbot-dns-cloudxns/setup.py b/certbot-dns-cloudxns/setup.py index 8c6e64af5..df7dcc59a 100644 --- a/certbot-dns-cloudxns/setup.py +++ b/certbot-dns-cloudxns/setup.py @@ -4,7 +4,7 @@ from setuptools import setup from setuptools import find_packages -version = '0.22.0' +version = '0.23.0.dev0' # Remember to update local-oldest-requirements.txt when changing the minimum # acme/certbot version. diff --git a/certbot-dns-digitalocean/setup.py b/certbot-dns-digitalocean/setup.py index 7dbf9dab5..f136c7161 100644 --- a/certbot-dns-digitalocean/setup.py +++ b/certbot-dns-digitalocean/setup.py @@ -4,7 +4,7 @@ from setuptools import setup from setuptools import find_packages -version = '0.22.0' +version = '0.23.0.dev0' # Remember to update local-oldest-requirements.txt when changing the minimum # acme/certbot version. diff --git a/certbot-dns-dnsimple/setup.py b/certbot-dns-dnsimple/setup.py index c8593b7f7..a327edf93 100644 --- a/certbot-dns-dnsimple/setup.py +++ b/certbot-dns-dnsimple/setup.py @@ -4,7 +4,7 @@ from setuptools import setup from setuptools import find_packages -version = '0.22.0' +version = '0.23.0.dev0' # Remember to update local-oldest-requirements.txt when changing the minimum # acme/certbot version. diff --git a/certbot-dns-dnsmadeeasy/setup.py b/certbot-dns-dnsmadeeasy/setup.py index ec2cfd3ad..9ff317ee1 100644 --- a/certbot-dns-dnsmadeeasy/setup.py +++ b/certbot-dns-dnsmadeeasy/setup.py @@ -4,7 +4,7 @@ from setuptools import setup from setuptools import find_packages -version = '0.22.0' +version = '0.23.0.dev0' # Remember to update local-oldest-requirements.txt when changing the minimum # acme/certbot version. diff --git a/certbot-dns-google/setup.py b/certbot-dns-google/setup.py index 900e9e4b2..6c25ed452 100644 --- a/certbot-dns-google/setup.py +++ b/certbot-dns-google/setup.py @@ -4,7 +4,7 @@ from setuptools import setup from setuptools import find_packages -version = '0.22.0' +version = '0.23.0.dev0' # Remember to update local-oldest-requirements.txt when changing the minimum # acme/certbot version. diff --git a/certbot-dns-luadns/setup.py b/certbot-dns-luadns/setup.py index 36b80bf19..7b8ffd84f 100644 --- a/certbot-dns-luadns/setup.py +++ b/certbot-dns-luadns/setup.py @@ -4,7 +4,7 @@ from setuptools import setup from setuptools import find_packages -version = '0.22.0' +version = '0.23.0.dev0' # Remember to update local-oldest-requirements.txt when changing the minimum # acme/certbot version. diff --git a/certbot-dns-nsone/setup.py b/certbot-dns-nsone/setup.py index a5bfe256b..53b091065 100644 --- a/certbot-dns-nsone/setup.py +++ b/certbot-dns-nsone/setup.py @@ -4,7 +4,7 @@ from setuptools import setup from setuptools import find_packages -version = '0.22.0' +version = '0.23.0.dev0' # Remember to update local-oldest-requirements.txt when changing the minimum # acme/certbot version. diff --git a/certbot-dns-rfc2136/setup.py b/certbot-dns-rfc2136/setup.py index 442a1e4af..2cbc29e6d 100644 --- a/certbot-dns-rfc2136/setup.py +++ b/certbot-dns-rfc2136/setup.py @@ -4,7 +4,7 @@ from setuptools import setup from setuptools import find_packages -version = '0.22.0' +version = '0.23.0.dev0' # Remember to update local-oldest-requirements.txt when changing the minimum # acme/certbot version. diff --git a/certbot-dns-route53/setup.py b/certbot-dns-route53/setup.py index b85c13dcd..3f21c4dc5 100644 --- a/certbot-dns-route53/setup.py +++ b/certbot-dns-route53/setup.py @@ -3,7 +3,7 @@ import sys from distutils.core import setup from setuptools import find_packages -version = '0.22.0' +version = '0.23.0.dev0' # Remember to update local-oldest-requirements.txt when changing the minimum # acme/certbot version. diff --git a/certbot-nginx/setup.py b/certbot-nginx/setup.py index e2715adda..25023b307 100644 --- a/certbot-nginx/setup.py +++ b/certbot-nginx/setup.py @@ -4,7 +4,7 @@ from setuptools import setup from setuptools import find_packages -version = '0.22.0' +version = '0.23.0.dev0' # Remember to update local-oldest-requirements.txt when changing the minimum # acme/certbot version. diff --git a/certbot/__init__.py b/certbot/__init__.py index 97fdb75d7..ebc8d5343 100644 --- a/certbot/__init__.py +++ b/certbot/__init__.py @@ -1,4 +1,4 @@ """Certbot client.""" # version number like 1.2.3a0, must have at least 2 parts, like 1.2 -__version__ = '0.22.0' +__version__ = '0.23.0.dev0' diff --git a/letsencrypt-auto-source/letsencrypt-auto b/letsencrypt-auto-source/letsencrypt-auto index 343f56013..0ba318140 100755 --- a/letsencrypt-auto-source/letsencrypt-auto +++ b/letsencrypt-auto-source/letsencrypt-auto @@ -31,7 +31,7 @@ if [ -z "$VENV_PATH" ]; then fi VENV_BIN="$VENV_PATH/bin" BOOTSTRAP_VERSION_PATH="$VENV_PATH/certbot-auto-bootstrap-version.txt" -LE_AUTO_VERSION="0.22.0" +LE_AUTO_VERSION="0.23.0.dev0" BASENAME=$(basename $0) USAGE="Usage: $BASENAME [OPTIONS] A self-updating wrapper script for the Certbot ACME client. When run, updates From 2e6d65d9ecb5f2416413597d74c3599b470a5bd4 Mon Sep 17 00:00:00 2001 From: Brad Warren Date: Thu, 8 Mar 2018 17:24:30 -0800 Subject: [PATCH 03/56] Add readthedocs requirements files (#5696) * Add readthedocs requirements files. * Only install docs extras for plugin. --- .../readthedocs.org.requirements.txt | 12 ++++++++++++ .../readthedocs.org.requirements.txt | 12 ++++++++++++ .../readthedocs.org.requirements.txt | 12 ++++++++++++ .../readthedocs.org.requirements.txt | 12 ++++++++++++ .../readthedocs.org.requirements.txt | 12 ++++++++++++ certbot-dns-google/readthedocs.org.requirements.txt | 12 ++++++++++++ certbot-dns-luadns/readthedocs.org.requirements.txt | 12 ++++++++++++ certbot-dns-nsone/readthedocs.org.requirements.txt | 12 ++++++++++++ certbot-dns-rfc2136/readthedocs.org.requirements.txt | 12 ++++++++++++ certbot-dns-route53/readthedocs.org.requirements.txt | 12 ++++++++++++ 10 files changed, 120 insertions(+) create mode 100644 certbot-dns-cloudflare/readthedocs.org.requirements.txt create mode 100644 certbot-dns-cloudxns/readthedocs.org.requirements.txt create mode 100644 certbot-dns-digitalocean/readthedocs.org.requirements.txt create mode 100644 certbot-dns-dnsimple/readthedocs.org.requirements.txt create mode 100644 certbot-dns-dnsmadeeasy/readthedocs.org.requirements.txt create mode 100644 certbot-dns-google/readthedocs.org.requirements.txt create mode 100644 certbot-dns-luadns/readthedocs.org.requirements.txt create mode 100644 certbot-dns-nsone/readthedocs.org.requirements.txt create mode 100644 certbot-dns-rfc2136/readthedocs.org.requirements.txt create mode 100644 certbot-dns-route53/readthedocs.org.requirements.txt diff --git a/certbot-dns-cloudflare/readthedocs.org.requirements.txt b/certbot-dns-cloudflare/readthedocs.org.requirements.txt new file mode 100644 index 000000000..b18901111 --- /dev/null +++ b/certbot-dns-cloudflare/readthedocs.org.requirements.txt @@ -0,0 +1,12 @@ +# readthedocs.org gives no way to change the install command to "pip +# install -e .[docs]" (that would in turn install documentation +# dependencies), but it allows to specify a requirements.txt file at +# https://readthedocs.org/dashboard/letsencrypt/advanced/ (c.f. #259) + +# Although ReadTheDocs certainly doesn't need to install the project +# in --editable mode (-e), just "pip install .[docs]" does not work as +# expected and "pip install -e .[docs]" must be used instead + +-e acme +-e . +-e certbot-dns-cloudflare[docs] diff --git a/certbot-dns-cloudxns/readthedocs.org.requirements.txt b/certbot-dns-cloudxns/readthedocs.org.requirements.txt new file mode 100644 index 000000000..ae2ff8165 --- /dev/null +++ b/certbot-dns-cloudxns/readthedocs.org.requirements.txt @@ -0,0 +1,12 @@ +# readthedocs.org gives no way to change the install command to "pip +# install -e .[docs]" (that would in turn install documentation +# dependencies), but it allows to specify a requirements.txt file at +# https://readthedocs.org/dashboard/letsencrypt/advanced/ (c.f. #259) + +# Although ReadTheDocs certainly doesn't need to install the project +# in --editable mode (-e), just "pip install .[docs]" does not work as +# expected and "pip install -e .[docs]" must be used instead + +-e acme +-e . +-e certbot-dns-cloudxns[docs] diff --git a/certbot-dns-digitalocean/readthedocs.org.requirements.txt b/certbot-dns-digitalocean/readthedocs.org.requirements.txt new file mode 100644 index 000000000..08d973ab3 --- /dev/null +++ b/certbot-dns-digitalocean/readthedocs.org.requirements.txt @@ -0,0 +1,12 @@ +# readthedocs.org gives no way to change the install command to "pip +# install -e .[docs]" (that would in turn install documentation +# dependencies), but it allows to specify a requirements.txt file at +# https://readthedocs.org/dashboard/letsencrypt/advanced/ (c.f. #259) + +# Although ReadTheDocs certainly doesn't need to install the project +# in --editable mode (-e), just "pip install .[docs]" does not work as +# expected and "pip install -e .[docs]" must be used instead + +-e acme +-e . +-e certbot-dns-digitalocean[docs] diff --git a/certbot-dns-dnsimple/readthedocs.org.requirements.txt b/certbot-dns-dnsimple/readthedocs.org.requirements.txt new file mode 100644 index 000000000..fef73916c --- /dev/null +++ b/certbot-dns-dnsimple/readthedocs.org.requirements.txt @@ -0,0 +1,12 @@ +# readthedocs.org gives no way to change the install command to "pip +# install -e .[docs]" (that would in turn install documentation +# dependencies), but it allows to specify a requirements.txt file at +# https://readthedocs.org/dashboard/letsencrypt/advanced/ (c.f. #259) + +# Although ReadTheDocs certainly doesn't need to install the project +# in --editable mode (-e), just "pip install .[docs]" does not work as +# expected and "pip install -e .[docs]" must be used instead + +-e acme +-e . +-e certbot-dns-dnsimple[docs] diff --git a/certbot-dns-dnsmadeeasy/readthedocs.org.requirements.txt b/certbot-dns-dnsmadeeasy/readthedocs.org.requirements.txt new file mode 100644 index 000000000..8f8c6c731 --- /dev/null +++ b/certbot-dns-dnsmadeeasy/readthedocs.org.requirements.txt @@ -0,0 +1,12 @@ +# readthedocs.org gives no way to change the install command to "pip +# install -e .[docs]" (that would in turn install documentation +# dependencies), but it allows to specify a requirements.txt file at +# https://readthedocs.org/dashboard/letsencrypt/advanced/ (c.f. #259) + +# Although ReadTheDocs certainly doesn't need to install the project +# in --editable mode (-e), just "pip install .[docs]" does not work as +# expected and "pip install -e .[docs]" must be used instead + +-e acme +-e . +-e certbot-dns-dnsmadeeasy[docs] diff --git a/certbot-dns-google/readthedocs.org.requirements.txt b/certbot-dns-google/readthedocs.org.requirements.txt new file mode 100644 index 000000000..6ea393f86 --- /dev/null +++ b/certbot-dns-google/readthedocs.org.requirements.txt @@ -0,0 +1,12 @@ +# readthedocs.org gives no way to change the install command to "pip +# install -e .[docs]" (that would in turn install documentation +# dependencies), but it allows to specify a requirements.txt file at +# https://readthedocs.org/dashboard/letsencrypt/advanced/ (c.f. #259) + +# Although ReadTheDocs certainly doesn't need to install the project +# in --editable mode (-e), just "pip install .[docs]" does not work as +# expected and "pip install -e .[docs]" must be used instead + +-e acme +-e . +-e certbot-dns-google[docs] diff --git a/certbot-dns-luadns/readthedocs.org.requirements.txt b/certbot-dns-luadns/readthedocs.org.requirements.txt new file mode 100644 index 000000000..acb51e4ef --- /dev/null +++ b/certbot-dns-luadns/readthedocs.org.requirements.txt @@ -0,0 +1,12 @@ +# readthedocs.org gives no way to change the install command to "pip +# install -e .[docs]" (that would in turn install documentation +# dependencies), but it allows to specify a requirements.txt file at +# https://readthedocs.org/dashboard/letsencrypt/advanced/ (c.f. #259) + +# Although ReadTheDocs certainly doesn't need to install the project +# in --editable mode (-e), just "pip install .[docs]" does not work as +# expected and "pip install -e .[docs]" must be used instead + +-e acme +-e . +-e certbot-dns-luadns[docs] diff --git a/certbot-dns-nsone/readthedocs.org.requirements.txt b/certbot-dns-nsone/readthedocs.org.requirements.txt new file mode 100644 index 000000000..dbdee4480 --- /dev/null +++ b/certbot-dns-nsone/readthedocs.org.requirements.txt @@ -0,0 +1,12 @@ +# readthedocs.org gives no way to change the install command to "pip +# install -e .[docs]" (that would in turn install documentation +# dependencies), but it allows to specify a requirements.txt file at +# https://readthedocs.org/dashboard/letsencrypt/advanced/ (c.f. #259) + +# Although ReadTheDocs certainly doesn't need to install the project +# in --editable mode (-e), just "pip install .[docs]" does not work as +# expected and "pip install -e .[docs]" must be used instead + +-e acme +-e . +-e certbot-dns-nsone[docs] diff --git a/certbot-dns-rfc2136/readthedocs.org.requirements.txt b/certbot-dns-rfc2136/readthedocs.org.requirements.txt new file mode 100644 index 000000000..df89018ce --- /dev/null +++ b/certbot-dns-rfc2136/readthedocs.org.requirements.txt @@ -0,0 +1,12 @@ +# readthedocs.org gives no way to change the install command to "pip +# install -e .[docs]" (that would in turn install documentation +# dependencies), but it allows to specify a requirements.txt file at +# https://readthedocs.org/dashboard/letsencrypt/advanced/ (c.f. #259) + +# Although ReadTheDocs certainly doesn't need to install the project +# in --editable mode (-e), just "pip install .[docs]" does not work as +# expected and "pip install -e .[docs]" must be used instead + +-e acme +-e . +-e certbot-dns-rfc2136[docs] diff --git a/certbot-dns-route53/readthedocs.org.requirements.txt b/certbot-dns-route53/readthedocs.org.requirements.txt new file mode 100644 index 000000000..660a90d0e --- /dev/null +++ b/certbot-dns-route53/readthedocs.org.requirements.txt @@ -0,0 +1,12 @@ +# readthedocs.org gives no way to change the install command to "pip +# install -e .[docs]" (that would in turn install documentation +# dependencies), but it allows to specify a requirements.txt file at +# https://readthedocs.org/dashboard/letsencrypt/advanced/ (c.f. #259) + +# Although ReadTheDocs certainly doesn't need to install the project +# in --editable mode (-e), just "pip install .[docs]" does not work as +# expected and "pip install -e .[docs]" must be used instead + +-e acme +-e . +-e certbot-dns-route53[docs] From f13fdccf04f7bcbe9a5fa73d449fcb04abf86a56 Mon Sep 17 00:00:00 2001 From: Brad Warren Date: Mon, 12 Mar 2018 10:51:45 -0700 Subject: [PATCH 04/56] document resps param (#5695) --- certbot/auth_handler.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/certbot/auth_handler.py b/certbot/auth_handler.py index 51cdf09ee..4b8160ef9 100644 --- a/certbot/auth_handler.py +++ b/certbot/auth_handler.py @@ -161,6 +161,13 @@ class AuthHandler(object): :param aauthzrs: authorizations and the selected annotated challenges to try and perform :type aauthzrs: `list` of `AnnotatedAuthzr` + :param resps: challenge responses from the authenticator where + each response at index i corresponds to the annotated + challenge at index i in the list returned by + :func:`_get_all_achalls` + :type resps: `collections.abc.Iterable` of + :class:`~acme.challenges.ChallengeResponse` or `False` or + `None` :param dict chall_update: parameter that is updated to hold aauthzr index to list of outstanding solved annotated challenges From 64d647774e75c2f1ae2fcc9b7ba3855aed1178fc Mon Sep 17 00:00:00 2001 From: Brad Warren Date: Mon, 12 Mar 2018 10:57:46 -0700 Subject: [PATCH 05/56] Update the changelog to reflect 0.22.0 (#5691) --- CHANGELOG.md | 56 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 56 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1369b0907..2ac87b0f9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,62 @@ Certbot adheres to [Semantic Versioning](http://semver.org/). +## 0.22.0 - 2018-03-07 + +### Added + +* Support for obtaining wildcard certificates and a newer version of the ACME + protocol such as the one implemented by Let's Encrypt's upcoming ACMEv2 + endpoint was added to Certbot and its ACME library. Certbot still works with + older ACME versions and will automatically change the version of the protocol + used based on the version the ACME CA implements. +* The Apache and Nginx plugins are now able to automatically install a wildcard + certificate to multiple virtual hosts that you select from your server + configuration. +* The `certbot install` command now accepts the `--cert-name` flag for + selecting a certificate. +* `acme.client.BackwardsCompatibleClientV2` was added to Certbot's ACME library + which automatically handles most of the differences between new and old ACME + versions. `acme.client.ClientV2` is also available for people who only want + to support one version of the protocol or want to handle the differences + between versions themselves. +* certbot-auto now supports the flag --install-only which has the script + install Certbot and its dependencies and exit without invoking Certbot. +* Support for issuing a single certificate for a wildcard and base domain was + added to our Google Cloud DNS plugin. To do this, we now require your API + credentials have additional permissions, however, your credentials will + already have these permissions unless you defined a custom role with fewer + permissions than the standard DNS administrator role provided by Google. + These permissions are also only needed for the case described above so it + will continue to work for existing users. For more information about the + permissions changes, see the documentation in the plugin. + +### Changed + +* We have broken lockstep between our ACME library, Certbot, and its plugins. + This means that the different components do not need to be the same version + to work together like they did previously. This makes packaging easier + because not every piece of Certbot needs to be repackaged to ship a change to + a subset of its components. +* Support for Python 2.6 and Python 3.3 has been removed from ACME, Certbot, + Certbot's plugins, and certbot-auto. If you are using certbot-auto on a RHEL + 6 based system, it will walk you through the process of installing Certbot + with Python 3 and refuse to upgrade to a newer version of Certbot until you + have done so. +* Certbot's components now work with older versions of setuptools to simplify + packaging for EPEL 7. + +### Fixed + +* Issues caused by Certbot's Nginx plugin adding multiple ipv6only directives + has been resolved. +* A problem where Certbot's Apache plugin would add redundant include + directives for the TLS configuration managed by Certbot has been fixed. +* Certbot's webroot plugin now properly deletes any directories it creates. + +More details about these changes can be found on our GitHub repo: +https://github.com/certbot/certbot/milestone/48?closed=1 + ## 0.21.1 - 2018-01-25 ### Fixed From d310ad18c716f64bd295ce951494b1bc0cc4122d Mon Sep 17 00:00:00 2001 From: Brad Warren Date: Mon, 12 Mar 2018 17:10:23 -0700 Subject: [PATCH 06/56] Put API link at the bottom of DNS plugin docs (#5699) * Put link to API at the bottom for future docs. * Put API link at the bottom of existing docs. --- certbot-dns-cloudflare/docs/index.rst | 6 +++--- certbot-dns-cloudxns/docs/index.rst | 6 +++--- certbot-dns-digitalocean/docs/index.rst | 6 +++--- certbot-dns-dnsimple/docs/index.rst | 6 +++--- certbot-dns-dnsmadeeasy/docs/index.rst | 6 +++--- certbot-dns-google/docs/index.rst | 6 +++--- certbot-dns-luadns/docs/index.rst | 6 +++--- certbot-dns-nsone/docs/index.rst | 6 +++--- certbot-dns-rfc2136/docs/index.rst | 6 +++--- certbot-dns-route53/docs/index.rst | 6 +++--- tools/sphinx-quickstart.sh | 2 +- 11 files changed, 31 insertions(+), 31 deletions(-) diff --git a/certbot-dns-cloudflare/docs/index.rst b/certbot-dns-cloudflare/docs/index.rst index e75106a01..f2d59baea 100644 --- a/certbot-dns-cloudflare/docs/index.rst +++ b/certbot-dns-cloudflare/docs/index.rst @@ -10,14 +10,14 @@ Welcome to certbot-dns-cloudflare's documentation! :maxdepth: 2 :caption: Contents: +.. automodule:: certbot_dns_cloudflare + :members: + .. toctree:: :maxdepth: 1 api -.. automodule:: certbot_dns_cloudflare - :members: - Indices and tables ================== diff --git a/certbot-dns-cloudxns/docs/index.rst b/certbot-dns-cloudxns/docs/index.rst index 41ea250cd..83c6ca18d 100644 --- a/certbot-dns-cloudxns/docs/index.rst +++ b/certbot-dns-cloudxns/docs/index.rst @@ -10,14 +10,14 @@ Welcome to certbot-dns-cloudxns's documentation! :maxdepth: 2 :caption: Contents: +.. automodule:: certbot_dns_cloudxns + :members: + .. toctree:: :maxdepth: 1 api -.. automodule:: certbot_dns_cloudxns - :members: - Indices and tables diff --git a/certbot-dns-digitalocean/docs/index.rst b/certbot-dns-digitalocean/docs/index.rst index 9f66382ee..5e30f2d2c 100644 --- a/certbot-dns-digitalocean/docs/index.rst +++ b/certbot-dns-digitalocean/docs/index.rst @@ -10,14 +10,14 @@ Welcome to certbot-dns-digitalocean's documentation! :maxdepth: 2 :caption: Contents: +.. automodule:: certbot_dns_digitalocean + :members: + .. toctree:: :maxdepth: 1 api -.. automodule:: certbot_dns_digitalocean - :members: - Indices and tables diff --git a/certbot-dns-dnsimple/docs/index.rst b/certbot-dns-dnsimple/docs/index.rst index 4ff1e59eb..a565ba919 100644 --- a/certbot-dns-dnsimple/docs/index.rst +++ b/certbot-dns-dnsimple/docs/index.rst @@ -10,14 +10,14 @@ Welcome to certbot-dns-dnsimple's documentation! :maxdepth: 2 :caption: Contents: +.. automodule:: certbot_dns_dnsimple + :members: + .. toctree:: :maxdepth: 1 api -.. automodule:: certbot_dns_dnsimple - :members: - Indices and tables diff --git a/certbot-dns-dnsmadeeasy/docs/index.rst b/certbot-dns-dnsmadeeasy/docs/index.rst index 2e9aef36b..dddf0e745 100644 --- a/certbot-dns-dnsmadeeasy/docs/index.rst +++ b/certbot-dns-dnsmadeeasy/docs/index.rst @@ -10,14 +10,14 @@ Welcome to certbot-dns-dnsmadeeasy's documentation! :maxdepth: 2 :caption: Contents: +.. automodule:: certbot_dns_dnsmadeeasy + :members: + .. toctree:: :maxdepth: 1 api -.. automodule:: certbot_dns_dnsmadeeasy - :members: - Indices and tables diff --git a/certbot-dns-google/docs/index.rst b/certbot-dns-google/docs/index.rst index a8a322f97..6bb82c76a 100644 --- a/certbot-dns-google/docs/index.rst +++ b/certbot-dns-google/docs/index.rst @@ -10,14 +10,14 @@ Welcome to certbot-dns-google's documentation! :maxdepth: 2 :caption: Contents: +.. automodule:: certbot_dns_google + :members: + .. toctree:: :maxdepth: 1 api -.. automodule:: certbot_dns_google - :members: - Indices and tables diff --git a/certbot-dns-luadns/docs/index.rst b/certbot-dns-luadns/docs/index.rst index 589e925c0..d8cbdeb3f 100644 --- a/certbot-dns-luadns/docs/index.rst +++ b/certbot-dns-luadns/docs/index.rst @@ -10,14 +10,14 @@ Welcome to certbot-dns-luadns's documentation! :maxdepth: 2 :caption: Contents: +.. automodule:: certbot_dns_luadns + :members: + .. toctree:: :maxdepth: 1 api -.. automodule:: certbot_dns_luadns - :members: - Indices and tables diff --git a/certbot-dns-nsone/docs/index.rst b/certbot-dns-nsone/docs/index.rst index 6abba81ec..bc204a6ff 100644 --- a/certbot-dns-nsone/docs/index.rst +++ b/certbot-dns-nsone/docs/index.rst @@ -10,14 +10,14 @@ Welcome to certbot-dns-nsone's documentation! :maxdepth: 2 :caption: Contents: +.. automodule:: certbot_dns_nsone + :members: + .. toctree:: :maxdepth: 1 api -.. automodule:: certbot_dns_nsone - :members: - Indices and tables diff --git a/certbot-dns-rfc2136/docs/index.rst b/certbot-dns-rfc2136/docs/index.rst index 71705cb7f..c2d4daafe 100644 --- a/certbot-dns-rfc2136/docs/index.rst +++ b/certbot-dns-rfc2136/docs/index.rst @@ -10,14 +10,14 @@ Welcome to certbot-dns-rfc2136's documentation! :maxdepth: 2 :caption: Contents: +.. automodule:: certbot_dns_rfc2136 + :members: + .. toctree:: :maxdepth: 1 api -.. automodule:: certbot_dns_rfc2136 - :members: - Indices and tables diff --git a/certbot-dns-route53/docs/index.rst b/certbot-dns-route53/docs/index.rst index bacf73150..fe5adfad5 100644 --- a/certbot-dns-route53/docs/index.rst +++ b/certbot-dns-route53/docs/index.rst @@ -10,14 +10,14 @@ Welcome to certbot-dns-route53's documentation! :maxdepth: 2 :caption: Contents: +.. automodule:: certbot_dns_route53 + :members: + .. toctree:: :maxdepth: 1 api -.. automodule:: certbot_dns_route53 - :members: - Indices and tables diff --git a/tools/sphinx-quickstart.sh b/tools/sphinx-quickstart.sh index d67c45b6f..72dc9e200 100755 --- a/tools/sphinx-quickstart.sh +++ b/tools/sphinx-quickstart.sh @@ -25,7 +25,7 @@ API Documentation :glob: api/**" > api.rst -sed -i -e "s| :caption: Contents:| :caption: Contents:\n\n.. toctree::\n :maxdepth: 1\n\n api\n\n.. automodule:: ${PROJECT//-/_}\n :members:|" index.rst +sed -i -e "s| :caption: Contents:| :caption: Contents:\n\n.. automodule:: ${PROJECT//-/_}\n :members:\n\n.. toctree::\n :maxdepth: 1\n\n api|" index.rst echo "Suggested next steps: * Add API docs to: $PROJECT/docs/api/ From 1d0e3b1bfa5afaa861adc1a4157e6a94d34320a2 Mon Sep 17 00:00:00 2001 From: Brad Warren Date: Tue, 13 Mar 2018 07:08:01 -0700 Subject: [PATCH 07/56] Add documentation about DNS plugins and Docker (#5710) * make binding port optional * Add DNS docker docs * add basic DNS plugin docs * Add link to DNS plugin docs from Docker docs * Shrink table size --- docs/install.rst | 22 ++++++++++++++++++---- docs/using.rst | 36 ++++++++++++++++++++++++++++++++++++ 2 files changed, 54 insertions(+), 4 deletions(-) diff --git a/docs/install.rst b/docs/install.rst index aec885b62..07af41fbd 100644 --- a/docs/install.rst +++ b/docs/install.rst @@ -115,13 +115,17 @@ these make much sense to you, you should definitely use the certbot-auto_ method, which enables you to use installer plugins that cover both of those hard topics. -If you're still not convinced and have decided to use this method, -from the server that the domain you're requesting a certficate for resolves -to, `install Docker`_, then issue the following command: +If you're still not convinced and have decided to use this method, from +the server that the domain you're requesting a certficate for resolves +to, `install Docker`_, then issue a command like the one found below. If +you are using Certbot with the :ref:`Standalone` plugin, you will need +to make the port it uses accessible from outside of the container by +including something like ``-p 80:80`` or ``-p 443:443`` on the command +line before ``certbot/certbot``. .. code-block:: shell - sudo docker run -it --rm -p 443:443 -p 80:80 --name certbot \ + sudo docker run -it --rm --name certbot \ -v "/etc/letsencrypt:/etc/letsencrypt" \ -v "/var/lib/letsencrypt:/var/lib/letsencrypt" \ certbot/certbot certonly @@ -131,6 +135,16 @@ Running Certbot with the ``certonly`` command will obtain a certificate and plac within Docker, you must install the certificate manually according to the procedure recommended by the provider of your webserver. +There are also Docker images for each of Certbot's DNS plugins available +at https://hub.docker.com/u/certbot which automate doing domain +validation over DNS for popular providers. To use one, just replace +``certbot/certbot`` in the command above with the name of the image you +want to use. For example, to use Certbot's plugin for Amazon Route 53, +you'd use ``certbot/dns-route53``. You may also need to add flags to +Certbot and/or mount additional directories to provide access to your +DNS API credentials. See the :ref:`DNS plugin documentation +` for more info. + For more information about the layout of the ``/etc/letsencrypt`` directory, see :ref:`where-certs`. diff --git a/docs/using.rst b/docs/using.rst index e8f84e2d7..f26ec2563 100644 --- a/docs/using.rst +++ b/docs/using.rst @@ -54,12 +54,19 @@ standalone_ Y N | Uses a "standalone" webserver to obtain a certificate. | Requires port 80 or 443 to be available. This is useful on tls-sni-01_ (443) | systems with no webserver, or when direct integration with | the local webserver is not supported or not desired. +|dns_plugs| Y N | This category of plugins automates obtaining a certificate by dns-01_ (53) + | modifying DNS records to prove you have control over a + | domain. Doing domain validation in this way is + | the only way to obtain wildcard certificates from Let's + | Encrypt. manual_ Y N | Helps you obtain a certificate by giving you instructions to http-01_ (80), | perform domain validation yourself. Additionally allows you dns-01_ (53) or | to specify scripts to automate the validation task in a tls-sni-01_ (443) | customized way. =========== ==== ==== =============================================================== ============================= +.. |dns_plugs| replace:: :ref:`DNS plugins ` + Under the hood, plugins use one of several ACME protocol challenges_ to prove you control a domain. The options are http-01_ (which uses port 80), tls-sni-01_ (port 443) and dns-01_ (requiring configuration of a DNS server on @@ -141,6 +148,8 @@ the ``--nginx`` flag on the commandline. certbot --nginx +.. _standalone: + Standalone ---------- @@ -164,6 +173,33 @@ the Internet on the specified port using each requested domain name. .. note:: The ``--standalone-supported-challenges`` option has been deprecated since ``certbot`` version 0.9.0. +.. _dns_plugins: + +DNS Plugins +----------- + +If you'd like to obtain a wildcard certificate from Let's Encrypt or run +``certbot`` on a machine other than your target webserver, you can use one of +Certbot's DNS plugins. + +These plugins are still in the process of being packaged +by many distributions and cannot currently be installed with ``certbot-auto``. +If, however, you are comfortable installing the certificates yourself, +you can run these plugins with :ref:`Docker `. + +Once installed, you can find documentation on how to use each plugin at: + +* `certbot-dns-cloudflare `_ +* `certbot-dns-cloudxns `_ +* `certbot-dns-digitalocean `_ +* `certbot-dns-dnsimple `_ +* `certbot-dns-dnsmadeeasy `_ +* `certbot-dns-google `_ +* `certbot-dns-luadns `_ +* `certbot-dns-nsone `_ +* `certbot-dns-rfc2136 `_ +* `certbot-dns-route53 `_ + Manual ------ From 9ea14d2e2b9709ac260bcbfd0a39bf4587e0e2f7 Mon Sep 17 00:00:00 2001 From: Brad Warren Date: Wed, 14 Mar 2018 08:48:40 -0700 Subject: [PATCH 08/56] Add docs about --server (#5713) * Add docs about --server * address review comments * mention server in Docker docs * correct server URL * Use prod ACMEv2 example --- docs/install.rst | 7 +++++-- docs/using.rst | 21 +++++++++++++++++++++ 2 files changed, 26 insertions(+), 2 deletions(-) diff --git a/docs/install.rst b/docs/install.rst index 07af41fbd..67889d8f7 100644 --- a/docs/install.rst +++ b/docs/install.rst @@ -142,8 +142,11 @@ validation over DNS for popular providers. To use one, just replace want to use. For example, to use Certbot's plugin for Amazon Route 53, you'd use ``certbot/dns-route53``. You may also need to add flags to Certbot and/or mount additional directories to provide access to your -DNS API credentials. See the :ref:`DNS plugin documentation -` for more info. +DNS API credentials as specified in the :ref:`DNS plugin documentation +`. If you would like to obtain a wildcard certificate from +Let's Encrypt's ACMEv2 server, you'll need to include ``--server +https://acme-v02.api.letsencrypt.org/directory`` on the command line as +well. For more information about the layout of the ``/etc/letsencrypt`` directory, see :ref:`where-certs`. diff --git a/docs/using.rst b/docs/using.rst index f26ec2563..a40532998 100644 --- a/docs/using.rst +++ b/docs/using.rst @@ -837,6 +837,27 @@ Example usage for DNS-01 (Cloudflare API v4) (for example purposes only, do not .. _lock-files: +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 +different CA by providing ``--server`` on the command line or in a +:ref:`configuration 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. +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 ========== From e405aaa4c1474c76c2270885267f5fab1f38e21c Mon Sep 17 00:00:00 2001 From: cclauss Date: Wed, 14 Mar 2018 17:37:29 +0100 Subject: [PATCH 09/56] Fix print() and xrange() for Python 3 (#5590) --- .../certbot_compatibility_test/test_driver.py | 2 ++ .../certbot_compatibility_test/validator.py | 1 + certbot-compatibility-test/nginx/roundtrip.py | 2 +- letsencrypt-auto-source/tests/auto_test.py | 1 + tools/simple_http_server.py | 2 +- 5 files changed, 6 insertions(+), 2 deletions(-) diff --git a/certbot-compatibility-test/certbot_compatibility_test/test_driver.py b/certbot-compatibility-test/certbot_compatibility_test/test_driver.py index 71a0ba574..2c6c917b3 100644 --- a/certbot-compatibility-test/certbot_compatibility_test/test_driver.py +++ b/certbot-compatibility-test/certbot_compatibility_test/test_driver.py @@ -10,6 +10,8 @@ import sys import OpenSSL +from six.moves import xrange # pylint: disable=import-error,redefined-builtin + from acme import challenges from acme import crypto_util from acme import messages diff --git a/certbot-compatibility-test/certbot_compatibility_test/validator.py b/certbot-compatibility-test/certbot_compatibility_test/validator.py index 0fd6efab5..791fe0da2 100644 --- a/certbot-compatibility-test/certbot_compatibility_test/validator.py +++ b/certbot-compatibility-test/certbot_compatibility_test/validator.py @@ -5,6 +5,7 @@ import requests import zope.interface import six +from six.moves import xrange # pylint: disable=import-error,redefined-builtin from acme import crypto_util from acme import errors as acme_errors diff --git a/certbot-compatibility-test/nginx/roundtrip.py b/certbot-compatibility-test/nginx/roundtrip.py index 852221df5..85d283c78 100644 --- a/certbot-compatibility-test/nginx/roundtrip.py +++ b/certbot-compatibility-test/nginx/roundtrip.py @@ -8,7 +8,7 @@ from certbot_nginx import nginxparser def roundtrip(stuff): success = True for t in stuff: - print t + print(t) if not os.path.isfile(t): continue with open(t, "r") as f: diff --git a/letsencrypt-auto-source/tests/auto_test.py b/letsencrypt-auto-source/tests/auto_test.py index d187452a1..c5109e208 100644 --- a/letsencrypt-auto-source/tests/auto_test.py +++ b/letsencrypt-auto-source/tests/auto_test.py @@ -18,6 +18,7 @@ from threading import Thread from unittest import TestCase from pytest import mark +from six.moves import xrange # pylint: disable=redefined-builtin @mark.skip diff --git a/tools/simple_http_server.py b/tools/simple_http_server.py index 26bf231b7..14ac9a3d3 100755 --- a/tools/simple_http_server.py +++ b/tools/simple_http_server.py @@ -14,7 +14,7 @@ def serve_forever(port=0): """ server = HTTPServer(('', port), SimpleHTTPRequestHandler) - print 'Serving HTTP on {0} port {1} ...'.format(*server.server_address) + print('Serving HTTP on {0} port {1} ...'.format(*server.server_address)) sys.stdout.flush() server.serve_forever() From 065e923bc9b75f1a7e59164da0541e5f0540e1b4 Mon Sep 17 00:00:00 2001 From: Spencer Eick Date: Wed, 14 Mar 2018 15:59:13 -0400 Subject: [PATCH 10/56] Improve "cannot find cert of key directive" error (#5525) (#5679) - Fix code to log separate error messages when either SSLCertificateFile or SSLCertificateKeyFile - directives are not found. - Update the section in install.rst where the relevant error is referenced. - Edit a docstring where 'cert' previously referred to certificate. - Edit test_deploy_cert_invalid_vhost in the test suite to cover changes. Fixes #5525. --- certbot-apache/certbot_apache/configurator.py | 22 ++++++++------ .../certbot_apache/tests/configurator_test.py | 30 +++++++++++++++++-- docs/install.rst | 10 +++---- 3 files changed, 45 insertions(+), 17 deletions(-) diff --git a/certbot-apache/certbot_apache/configurator.py b/certbot-apache/certbot_apache/configurator.py index 6377bb114..8b996c675 100644 --- a/certbot-apache/certbot_apache/configurator.py +++ b/certbot-apache/certbot_apache/configurator.py @@ -285,8 +285,8 @@ class ApacheConfigurator(augeas_configurator.AugeasConfigurator): chain_path=None, fullchain_path=None): """Deploys certificate to specified virtual host. - Currently tries to find the last directives to deploy the cert in - the VHost associated with the given domain. If it can't find the + Currently tries to find the last directives to deploy the certificate + in the VHost associated with the given domain. If it can't find the directives, it searches the "included" confs. The function verifies that it has located the three directives and finally modifies them to point to the correct destination. After the certificate is @@ -424,14 +424,20 @@ class ApacheConfigurator(augeas_configurator.AugeasConfigurator): path["chain_path"] = self.parser.find_dir( "SSLCertificateChainFile", None, vhost.path) - if not path["cert_path"] or not path["cert_key"]: - # Throw some can't find all of the directives error" + # Handle errors when certificate/key directives cannot be found + if not path["cert_path"]: logger.warning( - "Cannot find a cert or key directive in %s. " + "Cannot find an SSLCertificateFile directive in %s. " "VirtualHost was not modified", vhost.path) - # Presumably break here so that the virtualhost is not modified raise errors.PluginError( - "Unable to find cert and/or key directives") + "Unable to find an SSLCertificateFile directive") + elif not path["cert_key"]: + logger.warning( + "Cannot find an SSLCertificateKeyFile directive for " + "certificate in %s. VirtualHost was not modified", vhost.path) + raise errors.PluginError( + "Unable to find an SSLCertificateKeyFile directive for " + "certificate") logger.info("Deploying Certificate to VirtualHost %s", vhost.filep) @@ -2117,5 +2123,3 @@ class ApacheConfigurator(augeas_configurator.AugeasConfigurator): # to be modified. return common.install_version_controlled_file(options_ssl, options_ssl_digest, self.constant("MOD_SSL_CONF_SRC"), constants.ALL_SSL_OPTIONS_HASHES) - - diff --git a/certbot-apache/certbot_apache/tests/configurator_test.py b/certbot-apache/certbot_apache/tests/configurator_test.py index c9bf9a63f..7b1e4fa86 100644 --- a/certbot-apache/certbot_apache/tests/configurator_test.py +++ b/certbot-apache/certbot_apache/tests/configurator_test.py @@ -441,13 +441,37 @@ class MultipleVhostsTest(util.ApacheTest): self.vh_truth[1].path)) def test_deploy_cert_invalid_vhost(self): + """For test cases where the `ApacheConfigurator` class' `_deploy_cert` + method is called with an invalid vhost parameter. Currently this tests + that a PluginError is appropriately raised when important directives + are missing in an SSL module.""" self.config.parser.modules.add("ssl_module") - mock_find = mock.MagicMock() - mock_find.return_value = [] - self.config.parser.find_dir = mock_find + self.config.parser.modules.add("mod_ssl.c") + self.config.parser.modules.add("socache_shmcb_module") + + def side_effect(*args): + """Mocks case where an SSLCertificateFile directive can be found + but an SSLCertificateKeyFile directive is missing.""" + if "SSLCertificateFile" in args: + return ["example/cert.pem"] + else: + return [] + + mock_find_dir = mock.MagicMock(return_value=[]) + mock_find_dir.side_effect = side_effect + + self.config.parser.find_dir = mock_find_dir # Get the default 443 vhost self.config.assoc["random.demo"] = self.vh_truth[1] + + self.assertRaises( + errors.PluginError, self.config.deploy_cert, "random.demo", + "example/cert.pem", "example/key.pem", "example/cert_chain.pem") + + # Remove side_effect to mock case where both SSLCertificateFile + # and SSLCertificateKeyFile directives are missing + self.config.parser.find_dir.side_effect = None self.assertRaises( errors.PluginError, self.config.deploy_cert, "random.demo", "example/cert.pem", "example/key.pem", "example/cert_chain.pem") diff --git a/docs/install.rst b/docs/install.rst index 67889d8f7..45b0b7785 100644 --- a/docs/install.rst +++ b/docs/install.rst @@ -204,10 +204,11 @@ want to use the Apache plugin, it has to be installed separately: emerge -av app-crypt/certbot emerge -av app-crypt/certbot-apache -When using the Apache plugin, you will run into a "cannot find a cert or key -directive" error if you're sporting the default Gentoo ``httpd.conf``. -You can fix this by commenting out two lines in ``/etc/apache2/httpd.conf`` -as follows: +When using the Apache plugin, you will run into a "cannot find an +SSLCertificateFile directive" or "cannot find an SSLCertificateKeyFile +directive for certificate" error if you're sporting the default Gentoo +``httpd.conf``. You can fix this by commenting out two lines in +``/etc/apache2/httpd.conf`` as follows: Change @@ -257,4 +258,3 @@ whole process is described in the :doc:`contributing`. e.g. ``sudo python setup.py install``, ``sudo pip install``, ``sudo ./venv/bin/...``. These modes of operation might corrupt your operating system and are **not supported** by the Certbot team! - From b3e73bd2ab07c09c881693b8326cf75c7b5a119f Mon Sep 17 00:00:00 2001 From: Brad Warren Date: Wed, 14 Mar 2018 17:38:37 -0700 Subject: [PATCH 11/56] removes blank line from chain.pem (#5730) --- certbot/crypto_util.py | 2 +- certbot/tests/crypto_util_test.py | 8 +++++--- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/certbot/crypto_util.py b/certbot/crypto_util.py index 37118c591..bd4e7fcfc 100644 --- a/certbot/crypto_util.py +++ b/certbot/crypto_util.py @@ -445,5 +445,5 @@ def cert_and_chain_from_fullchain(fullchain_pem): """ cert = OpenSSL.crypto.dump_certificate(OpenSSL.crypto.FILETYPE_PEM, OpenSSL.crypto.load_certificate(OpenSSL.crypto.FILETYPE_PEM, fullchain_pem)).decode() - chain = fullchain_pem[len(cert):] + chain = fullchain_pem[len(cert):].lstrip() return (cert, chain) diff --git a/certbot/tests/crypto_util_test.py b/certbot/tests/crypto_util_test.py index 480139378..2fe0e3d30 100644 --- a/certbot/tests/crypto_util_test.py +++ b/certbot/tests/crypto_util_test.py @@ -380,10 +380,12 @@ class CertAndChainFromFullchainTest(unittest.TestCase): cert_pem = CERT.decode() chain_pem = cert_pem + SS_CERT.decode() fullchain_pem = cert_pem + chain_pem + spacey_fullchain_pem = cert_pem + u'\n' + chain_pem from certbot.crypto_util import cert_and_chain_from_fullchain - cert_out, chain_out = cert_and_chain_from_fullchain(fullchain_pem) - self.assertEqual(cert_out, cert_pem) - self.assertEqual(chain_out, chain_pem) + for fullchain in (fullchain_pem, spacey_fullchain_pem): + cert_out, chain_out = cert_and_chain_from_fullchain(fullchain) + self.assertEqual(cert_out, cert_pem) + self.assertEqual(chain_out, chain_pem) if __name__ == '__main__': From 5ecb68f2ed41474d65f70d309d2bd05c61fd6faf Mon Sep 17 00:00:00 2001 From: ohemorange Date: Fri, 16 Mar 2018 15:24:55 -0700 Subject: [PATCH 12/56] Update instances of acme-staging url to acme-staging-v02 (#5734) * update instances of acme-staging url to acme-staging-v02 * keep example client as v1 * keep deactivate script as v1 --- certbot/constants.py | 2 +- certbot/tests/storage_test.py | 2 +- certbot/tests/testdata/sample-renewal.conf | 2 +- docs/contributing.rst | 10 +++++----- examples/dev-cli.ini | 2 +- 5 files changed, 9 insertions(+), 9 deletions(-) diff --git a/certbot/constants.py b/certbot/constants.py index a6878824b..9dfc00c6b 100644 --- a/certbot/constants.py +++ b/certbot/constants.py @@ -107,7 +107,7 @@ CLI_DEFAULTS = dict( dns_route53=False ) -STAGING_URI = "https://acme-staging.api.letsencrypt.org/directory" +STAGING_URI = "https://acme-staging-v02.api.letsencrypt.org/directory" # The set of reasons for revoking a certificate is defined in RFC 5280 in # section 5.3.1. The reasons that users are allowed to submit are restricted to diff --git a/certbot/tests/storage_test.py b/certbot/tests/storage_test.py index 6c0970e72..09c752ebe 100644 --- a/certbot/tests/storage_test.py +++ b/certbot/tests/storage_test.py @@ -726,7 +726,7 @@ class RenewableCertTests(BaseRenewableCertTest): self.test_rc.configuration["renewalparams"] = {} rp = self.test_rc.configuration["renewalparams"] self.assertEqual(self.test_rc.is_test_cert, False) - rp["server"] = "https://acme-staging.api.letsencrypt.org/directory" + rp["server"] = "https://acme-staging-v02.api.letsencrypt.org/directory" self.assertEqual(self.test_rc.is_test_cert, True) rp["server"] = "https://staging.someotherca.com/directory" self.assertEqual(self.test_rc.is_test_cert, True) diff --git a/certbot/tests/testdata/sample-renewal.conf b/certbot/tests/testdata/sample-renewal.conf index 52b3ec45c..04f9ae8ca 100644 --- a/certbot/tests/testdata/sample-renewal.conf +++ b/certbot/tests/testdata/sample-renewal.conf @@ -61,7 +61,7 @@ chain_path = /home/ubuntu/letsencrypt/chain.pem break_my_certs = False standalone = True manual = False -server = https://acme-staging.api.letsencrypt.org/directory +server = https://acme-staging-v02.api.letsencrypt.org/directory standalone_supported_challenges = "tls-sni-01,http-01" webroot = False os_packages_only = False diff --git a/docs/contributing.rst b/docs/contributing.rst index 654528e3d..45cd2e9f2 100644 --- a/docs/contributing.rst +++ b/docs/contributing.rst @@ -43,7 +43,7 @@ each shell where you're working: .. code-block:: shell source ./venv/bin/activate - export SERVER=https://acme-staging.api.letsencrypt.org/directory + export SERVER=https://acme-staging-v02.api.letsencrypt.org/directory source tests/integration/_common.sh After that, your shell will be using the virtual environment, your copy of @@ -443,10 +443,10 @@ For squeeze you will need to: FreeBSD ------- -Packages can be installed on FreeBSD using ``pkg``, -or any other port-management tool (``portupgrade``, ``portmanager``, etc.) -from the pre-built package or can be built and installed from ports. -Either way will ensure proper installation of all the dependencies required +Packages can be installed on FreeBSD using ``pkg``, +or any other port-management tool (``portupgrade``, ``portmanager``, etc.) +from the pre-built package or can be built and installed from ports. +Either way will ensure proper installation of all the dependencies required for the package. FreeBSD by default uses ``tcsh``. In order to activate virtualenv (see diff --git a/examples/dev-cli.ini b/examples/dev-cli.ini index c02038ca1..a405a0aef 100644 --- a/examples/dev-cli.ini +++ b/examples/dev-cli.ini @@ -1,5 +1,5 @@ # Always use the staging/testing server - avoids rate limiting -server = https://acme-staging.api.letsencrypt.org/directory +server = https://acme-staging-v02.api.letsencrypt.org/directory # This is an example configuration file for developers config-dir = /tmp/le/conf From 79d90d67452b708eb69e66a057b3b40add7af8e4 Mon Sep 17 00:00:00 2001 From: sydneyli Date: Fri, 16 Mar 2018 15:27:39 -0700 Subject: [PATCH 13/56] feat(nginx plugin): add HSTS enhancement (#5463) * feat(nginx plugin): add HSTS enhancement * chore(nginx): factor out block-splitting code from redirect & hsts enhancements! * chore(nginx): merge fixes * address comments * fix linter: remove a space * fix(config): remove SSL directives in HTTP block after block split, and remove_directive removes 'Managed by certbot' comment * chore(nginx-hsts): Move added SSL directives to a constant on Configurator class * fix(nginx-hsts): rebase on wildcard cert changes --- certbot-nginx/certbot_nginx/configurator.py | 94 +++++++++++++++---- certbot-nginx/certbot_nginx/constants.py | 4 + certbot-nginx/certbot_nginx/obj.py | 26 ++++- certbot-nginx/certbot_nginx/parser.py | 7 ++ .../certbot_nginx/tests/configurator_test.py | 50 +++++++++- certbot-nginx/certbot_nginx/tests/obj_test.py | 15 +++ .../certbot_nginx/tests/parser_test.py | 26 +++++ certbot/constants.py | 4 +- 8 files changed, 204 insertions(+), 22 deletions(-) diff --git a/certbot-nginx/certbot_nginx/configurator.py b/certbot-nginx/certbot_nginx/configurator.py index 83e308bac..41ca52d13 100644 --- a/certbot-nginx/certbot_nginx/configurator.py +++ b/certbot-nginx/certbot_nginx/configurator.py @@ -61,6 +61,9 @@ class NginxConfigurator(common.Installer): DEFAULT_LISTEN_PORT = '80' + # SSL directives that Certbot can add when installing a new certificate. + SSL_DIRECTIVES = ['ssl_certificate', 'ssl_certificate_key', 'ssl_dhparam'] + @classmethod def add_parser_arguments(cls, add): add("server-root", default=constants.CLI_DEFAULTS["server_root"], @@ -105,6 +108,7 @@ class NginxConfigurator(common.Installer): self.parser = None self.version = version self._enhance_func = {"redirect": self._enable_redirect, + "ensure-http-header": self._set_http_header, "staple-ocsp": self._enable_ocsp_stapling} self.reverter.recovery_routine() @@ -621,7 +625,7 @@ class NginxConfigurator(common.Installer): ################################## def supported_enhancements(self): # pylint: disable=no-self-use """Returns currently supported enhancements.""" - return ['redirect', 'staple-ocsp'] + return ['redirect', 'ensure-http-header', 'staple-ocsp'] def enhance(self, domain, enhancement, options=None): """Enhance configuration. @@ -647,6 +651,40 @@ class NginxConfigurator(common.Installer): test_redirect_block = _test_block_from_block(_redirect_block_for_domain(domain)) return vhost.contains_list(test_redirect_block) + def _set_http_header(self, domain, header_substring): + """Enables header identified by header_substring on domain. + + If the vhost is listening plaintextishly, separates out the relevant + directives into a new server block, and only add header directive to + HTTPS block. + + :param str domain: the domain to enable header for. + :param str header_substring: String to uniquely identify a header. + e.g. Strict-Transport-Security, Upgrade-Insecure-Requests + :returns: Success + :raises .errors.PluginError: If no viable HTTPS host can be created or + set with header header_substring. + """ + vhosts = self.choose_vhosts(domain) + if not vhosts: + raise errors.PluginError( + "Unable to find corresponding HTTPS host for enhancement.") + for vhost in vhosts: + if vhost.has_header(header_substring): + raise errors.PluginEnhancementAlreadyPresent( + "Existing %s header" % (header_substring)) + + # if there is no separate SSL block, break the block into two and + # choose the SSL block. + if vhost.ssl and any([not addr.ssl for addr in vhost.addrs]): + _, vhost = self._split_block(vhost) + + header_directives = [ + ['\n ', 'add_header', ' ', header_substring, ' '] + + constants.HEADER_ARGS[header_substring], + ['\n']] + self.parser.add_server_directives(vhost, header_directives, replace=False) + def _add_redirect_block(self, vhost, domain): """Add redirect directive to vhost """ @@ -655,6 +693,39 @@ class NginxConfigurator(common.Installer): self.parser.add_server_directives( vhost, redirect_block, replace=False, insert_at_top=True) + def _split_block(self, vhost, only_directives=None): + """Splits this "virtual host" (i.e. this nginx server block) into + separate HTTP and HTTPS blocks. + + :param vhost: The server block to break up into two. + :param list only_directives: If this exists, only duplicate these directives + when splitting the block. + :type vhost: :class:`~certbot_nginx.obj.VirtualHost` + :returns: tuple (http_vhost, https_vhost) + :rtype: tuple of type :class:`~certbot_nginx.obj.VirtualHost` + """ + http_vhost = self.parser.duplicate_vhost(vhost, only_directives=only_directives) + + def _ssl_match_func(directive): + return 'ssl' in directive + + def _ssl_config_match_func(directive): + return self.mod_ssl_conf in directive + + def _no_ssl_match_func(directive): + return 'ssl' not in directive + + # remove all ssl addresses and related directives from the new block + for directive in self.SSL_DIRECTIVES: + self.parser.remove_server_directives(http_vhost, directive) + self.parser.remove_server_directives(http_vhost, 'listen', match_func=_ssl_match_func) + self.parser.remove_server_directives(http_vhost, 'include', + match_func=_ssl_config_match_func) + + # remove all non-ssl addresses from the existing block + self.parser.remove_server_directives(vhost, 'listen', match_func=_no_ssl_match_func) + return http_vhost, vhost + def _enable_redirect(self, domain, unused_options): """Redirect all equivalent HTTP traffic to ssl_vhost. @@ -694,28 +765,15 @@ class NginxConfigurator(common.Installer): :param `~obj.Vhost` vhost: vhost to enable redirect for """ - new_vhost = None + http_vhost = None if vhost.ssl: - new_vhost = self.parser.duplicate_vhost(vhost, - only_directives=['listen', 'server_name']) - - def _ssl_match_func(directive): - return 'ssl' in directive - - def _no_ssl_match_func(directive): - return 'ssl' not in directive - - # remove all ssl addresses from the new block - self.parser.remove_server_directives(new_vhost, 'listen', match_func=_ssl_match_func) - - # remove all non-ssl addresses from the existing block - self.parser.remove_server_directives(vhost, 'listen', match_func=_no_ssl_match_func) + http_vhost, _ = self._split_block(vhost, ['listen', 'server_name']) # Add this at the bottom to get the right order of directives return_404_directive = [['\n ', 'return', ' ', '404']] - self.parser.add_server_directives(new_vhost, return_404_directive, replace=False) + self.parser.add_server_directives(http_vhost, return_404_directive, replace=False) - vhost = new_vhost + vhost = http_vhost if self._has_certbot_redirect(vhost, domain): logger.info("Traffic on port %s already redirecting to ssl in %s", diff --git a/certbot-nginx/certbot_nginx/constants.py b/certbot-nginx/certbot_nginx/constants.py index 2e72b8686..3f263fea3 100644 --- a/certbot-nginx/certbot_nginx/constants.py +++ b/certbot-nginx/certbot_nginx/constants.py @@ -44,3 +44,7 @@ def os_constant(key): :return: value of constant for active os """ return CLI_DEFAULTS[key] + +HSTS_ARGS = ['\"max-age=31536000\"', ' ', 'always'] + +HEADER_ARGS = {'Strict-Transport-Security': HSTS_ARGS} diff --git a/certbot-nginx/certbot_nginx/obj.py b/certbot-nginx/certbot_nginx/obj.py index 3625a95b9..ea5c6e2f8 100644 --- a/certbot-nginx/certbot_nginx/obj.py +++ b/certbot-nginx/certbot_nginx/obj.py @@ -5,7 +5,7 @@ import six from certbot.plugins import common -REDIRECT_DIRECTIVES = ['return', 'rewrite'] +ADD_HEADER_DIRECTIVE = 'add_header' class Addr(common.Addr): r"""Represents an Nginx address, i.e. what comes after the 'listen' @@ -198,6 +198,14 @@ class VirtualHost(object): # pylint: disable=too-few-public-methods tuple(self.addrs), tuple(self.names), self.ssl, self.enabled)) + def has_header(self, header_name): + """Determine if this server block has a particular header set. + :param str header_name: The name of the header to check for, e.g. + 'Strict-Transport-Security' + """ + found = _find_directive(self.raw, ADD_HEADER_DIRECTIVE, header_name) + return found is not None + def contains_list(self, test): """Determine if raw server block contains test list at top level """ @@ -233,3 +241,19 @@ class VirtualHost(object): # pylint: disable=too-few-public-methods addrs=", ".join(str(addr) for addr in self.addrs), names=", ".join(self.names), https="Yes" if self.ssl else "No")) + +def _find_directive(directives, directive_name, match_content=None): + """Find a directive of type directive_name in directives. If match_content is given, + Searches for `match_content` in the directive arguments. + """ + if not directives or isinstance(directives, six.string_types) or len(directives) == 0: + return None + + # If match_content is None, just match on directive type. Otherwise, match on + # both directive type -and- the content! + if directives[0] == directive_name and \ + (match_content is None or match_content in directives): + return directives + + matches = (_find_directive(line, directive_name, match_content) for line in directives) + return next((m for m in matches if m is not None), None) diff --git a/certbot-nginx/certbot_nginx/parser.py b/certbot-nginx/certbot_nginx/parser.py index fbd6c0ade..e329307c0 100644 --- a/certbot-nginx/certbot_nginx/parser.py +++ b/certbot-nginx/certbot_nginx/parser.py @@ -377,6 +377,7 @@ class NginxParser(object): del directive[directive.index('default_server')] return new_vhost + def _parse_ssl_options(ssl_options): if ssl_options is not None: try: @@ -667,6 +668,9 @@ def _add_directive(block, directive, replace, insert_at_top): elif block[location] != directive: raise errors.MisconfigurationError(err_fmt.format(directive, block[location])) +def _is_certbot_comment(directive): + return '#' in directive and COMMENT in directive + def _remove_directives(directive_name, match_func, block): """Removes directives of name directive_name from a config block if match_func matches. """ @@ -674,6 +678,9 @@ def _remove_directives(directive_name, match_func, block): location = _find_location(block, directive_name, match_func=match_func) if location is None: return + # if the directive was made by us, remove the comment following + if location + 1 < len(block) and _is_certbot_comment(block[location + 1]): + del block[location + 1] del block[location] def _apply_global_addr_ssl(addr_to_ssl, parsed_server): diff --git a/certbot-nginx/certbot_nginx/tests/configurator_test.py b/certbot-nginx/certbot_nginx/tests/configurator_test.py index bffaef5e4..9489b534a 100644 --- a/certbot-nginx/certbot_nginx/tests/configurator_test.py +++ b/certbot-nginx/certbot_nginx/tests/configurator_test.py @@ -94,7 +94,7 @@ class NginxConfiguratorTest(util.NginxTest): "globalssl.com", "globalsslsetssl.com", "ipv6.com", "ipv6ssl.com"])) def test_supported_enhancements(self): - self.assertEqual(['redirect', 'staple-ocsp'], + self.assertEqual(['redirect', 'ensure-http-header', 'staple-ocsp'], self.config.supported_enhancements()) def test_enhance(self): @@ -510,6 +510,54 @@ class NginxConfiguratorTest(util.NginxTest): ['return', '404'], ['#', ' managed by Certbot'], [], [], []]]], generated_conf) + def test_split_for_headers(self): + example_conf = self.config.parser.abs_path('sites-enabled/example.com') + self.config.deploy_cert( + "example.org", + "example/cert.pem", + "example/key.pem", + "example/chain.pem", + "example/fullchain.pem") + self.config.enhance("www.example.com", "ensure-http-header", "Strict-Transport-Security") + generated_conf = self.config.parser.parsed[example_conf] + self.assertEqual( + [[['server'], [ + ['server_name', '.example.com'], + ['server_name', 'example.*'], [], + ['listen', '5001', 'ssl'], ['#', ' managed by Certbot'], + ['ssl_certificate', 'example/fullchain.pem'], ['#', ' managed by Certbot'], + ['ssl_certificate_key', 'example/key.pem'], ['#', ' managed by Certbot'], + ['include', self.config.mod_ssl_conf], ['#', ' managed by Certbot'], + ['ssl_dhparam', self.config.ssl_dhparams], ['#', ' managed by Certbot'], + [], [], + ['add_header', 'Strict-Transport-Security', '"max-age=31536000"', 'always'], + ['#', ' managed by Certbot'], + [], []]], + [['server'], [ + ['listen', '69.50.225.155:9000'], + ['listen', '127.0.0.1'], + ['server_name', '.example.com'], + ['server_name', 'example.*'], + [], [], []]]], + generated_conf) + + def test_http_header_hsts(self): + example_conf = self.config.parser.abs_path('sites-enabled/example.com') + self.config.enhance("www.example.com", "ensure-http-header", + "Strict-Transport-Security") + expected = ['add_header', 'Strict-Transport-Security', '"max-age=31536000"', 'always'] + generated_conf = self.config.parser.parsed[example_conf] + self.assertTrue(util.contains_at_depth(generated_conf, expected, 2)) + + def test_http_header_hsts_twice(self): + self.config.enhance("www.example.com", "ensure-http-header", + "Strict-Transport-Security") + self.assertRaises( + errors.PluginEnhancementAlreadyPresent, + self.config.enhance, "www.example.com", + "ensure-http-header", "Strict-Transport-Security") + + @mock.patch('certbot_nginx.obj.VirtualHost.contains_list') def test_certbot_redirect_exists(self, mock_contains_list): # Test that we add no redirect statement if there is already a diff --git a/certbot-nginx/certbot_nginx/tests/obj_test.py b/certbot-nginx/certbot_nginx/tests/obj_test.py index b30338b5b..929e7cdf0 100644 --- a/certbot-nginx/certbot_nginx/tests/obj_test.py +++ b/certbot-nginx/certbot_nginx/tests/obj_test.py @@ -143,6 +143,15 @@ class VirtualHostTest(unittest.TestCase): "filp", set([Addr.fromstring("localhost")]), False, False, set(['localhost']), raw4, []) + raw_has_hsts = [ + ['listen', '69.50.225.155:9000'], + ['server_name', 'return.com'], + ['add_header', 'always', 'set', 'Strict-Transport-Security', '\"max-age=31536000\"'], + ] + self.vhost_has_hsts = VirtualHost( + "filep", + set([Addr.fromstring("localhost")]), False, False, + set(['localhost']), raw_has_hsts, []) def test_eq(self): from certbot_nginx.obj import Addr @@ -162,6 +171,12 @@ class VirtualHostTest(unittest.TestCase): 'enabled: False']) self.assertEqual(stringified, str(self.vhost1)) + def test_has_header(self): + self.assertTrue(self.vhost_has_hsts.has_header('Strict-Transport-Security')) + self.assertFalse(self.vhost_has_hsts.has_header('Bogus-Header')) + self.assertFalse(self.vhost1.has_header('Strict-Transport-Security')) + self.assertFalse(self.vhost1.has_header('Bogus-Header')) + def test_contains_list(self): from certbot_nginx.obj import VirtualHost from certbot_nginx.obj import Addr diff --git a/certbot-nginx/certbot_nginx/tests/parser_test.py b/certbot-nginx/certbot_nginx/tests/parser_test.py index e21acb8ea..5fce6f25a 100644 --- a/certbot-nginx/certbot_nginx/tests/parser_test.py +++ b/certbot-nginx/certbot_nginx/tests/parser_test.py @@ -191,6 +191,32 @@ class NginxParserTest(util.NginxTest): #pylint: disable=too-many-public-methods ['server_name', '*.www.foo.com', '*.www.example.com']] self.assertTrue(nparser.has_ssl_on_directive(mock_vhost)) + + def test_remove_server_directives(self): + nparser = parser.NginxParser(self.config_path) + mock_vhost = obj.VirtualHost(nparser.abs_path('nginx.conf'), + None, None, None, + set(['localhost', + r'~^(www\.)?(example|bar)\.']), + None, [10, 1, 9]) + example_com = nparser.abs_path('sites-enabled/example.com') + names = set(['.example.com', 'example.*']) + mock_vhost.filep = example_com + mock_vhost.names = names + mock_vhost.path = [0] + nparser.add_server_directives(mock_vhost, + [['foo', 'bar'], ['ssl_certificate', + '/etc/ssl/cert2.pem']], + replace=False) + nparser.remove_server_directives(mock_vhost, 'foo') + nparser.remove_server_directives(mock_vhost, 'ssl_certificate') + self.assertEqual(nparser.parsed[example_com], + [[['server'], [['listen', '69.50.225.155:9000'], + ['listen', '127.0.0.1'], + ['server_name', '.example.com'], + ['server_name', 'example.*'], + []]]]) + def test_add_server_directives(self): nparser = parser.NginxParser(self.config_path) mock_vhost = obj.VirtualHost(nparser.abs_path('nginx.conf'), diff --git a/certbot/constants.py b/certbot/constants.py index 9dfc00c6b..0d0ee8d3f 100644 --- a/certbot/constants.py +++ b/certbot/constants.py @@ -135,13 +135,13 @@ RENEWER_DEFAULTS = dict( """Defaults for renewer script.""" -ENHANCEMENTS = ["redirect", "http-header", "ocsp-stapling", "spdy"] +ENHANCEMENTS = ["redirect", "ensure-http-header", "ocsp-stapling", "spdy"] """List of possible :class:`certbot.interfaces.IInstaller` enhancements. List of expected options parameters: - redirect: None -- http-header: TODO +- ensure-http-header: name of header (i.e. Strict-Transport-Security) - ocsp-stapling: certificate chain file path - spdy: TODO From ba6bdb50998bd55aeef7972a5c839560e02142f3 Mon Sep 17 00:00:00 2001 From: Brad Warren Date: Fri, 16 Mar 2018 17:45:46 -0700 Subject: [PATCH 14/56] Fix acme.client.Client.__init__ (#5747) * fixes #5738 * add test to prevent regressions --- acme/acme/client.py | 5 +++-- acme/acme/client_test.py | 10 ++++++++++ 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/acme/acme/client.py b/acme/acme/client.py index 9e2478afe..19615b087 100644 --- a/acme/acme/client.py +++ b/acme/acme/client.py @@ -259,11 +259,12 @@ class Client(ClientBase): """ # pylint: disable=too-many-arguments self.key = key - self.net = ClientNetwork(key, alg=alg, verify_ssl=verify_ssl) if net is None else net + if net is None: + net = ClientNetwork(key, alg=alg, verify_ssl=verify_ssl) if isinstance(directory, six.string_types): directory = messages.Directory.from_json( - self.net.get(directory).json()) + net.get(directory).json()) super(Client, self).__init__(directory=directory, net=net, acme_version=1) diff --git a/acme/acme/client_test.py b/acme/acme/client_test.py index 00b9e19dd..be08c2919 100644 --- a/acme/acme/client_test.py +++ b/acme/acme/client_test.py @@ -299,6 +299,16 @@ class ClientTest(ClientTestBase): directory=uri, key=KEY, alg=jose.RS256, net=self.net) self.net.get.assert_called_once_with(uri) + @mock.patch('acme.client.ClientNetwork') + def test_init_without_net(self, mock_net): + mock_net.return_value = mock.sentinel.net + alg = jose.RS256 + from acme.client import Client + self.client = Client( + directory=self.directory, key=KEY, alg=alg) + mock_net.called_once_with(KEY, alg=alg, verify_ssl=True) + self.assertEqual(self.client.net, mock.sentinel.net) + def test_register(self): # "Instance of 'Field' has no to_json/update member" bug: # pylint: disable=no-member From d4834da0f4bd26d1e989080805a2f1d7e442f10e Mon Sep 17 00:00:00 2001 From: Brad Warren Date: Fri, 16 Mar 2018 17:48:46 -0700 Subject: [PATCH 15/56] fix docker link --- docs/contributing.rst | 4 ++-- docs/install.rst | 2 ++ docs/using.rst | 2 +- 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/docs/contributing.rst b/docs/contributing.rst index 654528e3d..124cb398c 100644 --- a/docs/contributing.rst +++ b/docs/contributing.rst @@ -24,7 +24,7 @@ running: If you're on macOS, we recommend you skip the rest of this section and instead run Certbot in Docker. You can find instructions for how to do this :ref:`here -`. If you're running on Linux, you can run the following commands to +`. If you're running on Linux, you can run the following commands to install dependencies and set up a virtual environment where you can run Certbot. You will need to repeat this when Certbot's dependencies change or when a new plugin is introduced. @@ -377,7 +377,7 @@ This should generate documentation in the ``docs/_build/html`` directory. -.. _docker: +.. _docker-dev: Running the client with Docker ============================== diff --git a/docs/install.rst b/docs/install.rst index 45b0b7785..d47264545 100644 --- a/docs/install.rst +++ b/docs/install.rst @@ -94,6 +94,8 @@ Disable and remove the swapfile once the virtual environment is constructed:: user@webserver:~$ sudo swapoff /tmp/swapfile user@webserver:~$ sudo rm /tmp/swapfile +.. _docker-user: + Running with Docker ------------------- diff --git a/docs/using.rst b/docs/using.rst index a40532998..319651af0 100644 --- a/docs/using.rst +++ b/docs/using.rst @@ -185,7 +185,7 @@ Certbot's DNS plugins. These plugins are still in the process of being packaged by many distributions and cannot currently be installed with ``certbot-auto``. If, however, you are comfortable installing the certificates yourself, -you can run these plugins with :ref:`Docker `. +you can run these plugins with :ref:`Docker `. Once installed, you can find documentation on how to use each plugin at: From a26a78e84e0e93f5cf2122c18de7b6cad526f441 Mon Sep 17 00:00:00 2001 From: Edelita Valdez Date: Sat, 17 Mar 2018 19:23:53 -0700 Subject: [PATCH 16/56] Add note to developer guide docs about installing docs extras. (#4946) --- docs/contributing.rst | 3 +++ 1 file changed, 3 insertions(+) diff --git a/docs/contributing.rst b/docs/contributing.rst index d8985b8d2..7178e2bad 100644 --- a/docs/contributing.rst +++ b/docs/contributing.rst @@ -376,6 +376,9 @@ commands: This should generate documentation in the ``docs/_build/html`` directory. +.. note:: If you skipped the "Getting Started" instructions above, + run ``pip install -e .[docs]`` to install Certbot's docs extras modules. + .. _docker-dev: From 41ed6367b4eb82b9a0d5886853aaed1dfd130241 Mon Sep 17 00:00:00 2001 From: Gopal Adhikari Date: Mon, 19 Mar 2018 14:08:45 -0400 Subject: [PATCH 17/56] Fix typo: damain -> domain (#5756) Fix typo: damain -> domain in certbot/util.py:607 --- certbot/util.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/certbot/util.py b/certbot/util.py index f7ce6a3bc..b3973d96b 100644 --- a/certbot/util.py +++ b/certbot/util.py @@ -604,7 +604,7 @@ def enforce_domain_sanity(domain): def is_wildcard_domain(domain): """"Is domain a wildcard domain? - :param damain: domain to check + :param domain: domain to check :type domain: `bytes` or `str` or `unicode` :returns: True if domain is a wildcard, otherwise, False From 41ce1088812dc482d822f24aed2e7b42188a7bbb Mon Sep 17 00:00:00 2001 From: Brad Warren Date: Mon, 19 Mar 2018 16:51:01 -0700 Subject: [PATCH 18/56] Fix cleanup_challenges call (#5761) * fixes cleanup_challenges * add test to prevent regressions --- certbot/auth_handler.py | 15 +++++++-------- certbot/tests/auth_handler_test.py | 11 +++++++++++ 2 files changed, 18 insertions(+), 8 deletions(-) diff --git a/certbot/auth_handler.py b/certbot/auth_handler.py index 4b8160ef9..68389d1f8 100644 --- a/certbot/auth_handler.py +++ b/certbot/auth_handler.py @@ -118,7 +118,7 @@ class AuthHandler(object): """Get Responses for challenges from authenticators.""" resp = [] all_achalls = self._get_all_achalls(aauthzrs) - with error_handler.ErrorHandler(self._cleanup_challenges, all_achalls): + with error_handler.ErrorHandler(self._cleanup_challenges, aauthzrs, all_achalls): try: if all_achalls: resp = self.auth.perform(all_achalls) @@ -294,19 +294,18 @@ class AuthHandler(object): chall_prefs.extend(plugin_pref) return chall_prefs - def _cleanup_challenges(self, aauthzrs, achall_list=None): + def _cleanup_challenges(self, aauthzrs, achalls): """Cleanup challenges. - If achall_list is not provided, cleanup all achallenges. + :param aauthzrs: authorizations and their selected annotated + challenges + :type aauthzrs: `list` of `AnnotatedAuthzr` + :param achalls: annotated challenges to cleanup + :type achalls: `list` of :class:`certbot.achallenges.AnnotatedChallenge` """ logger.info("Cleaning up challenges") - if achall_list is None: - achalls = self._get_all_achalls(aauthzrs) - else: - achalls = achall_list - if achalls: self.auth.cleanup(achalls) for achall in achalls: diff --git a/certbot/tests/auth_handler_test.py b/certbot/tests/auth_handler_test.py index 54e284d9e..a4ac9eb73 100644 --- a/certbot/tests/auth_handler_test.py +++ b/certbot/tests/auth_handler_test.py @@ -278,6 +278,17 @@ class HandleAuthorizationsTest(unittest.TestCase): self.assertRaises( errors.AuthorizationError, self.handler.handle_authorizations, mock_order) + def test_perform_error(self): + self.mock_auth.perform.side_effect = errors.AuthorizationError + + authzr = gen_dom_authzr(domain="0", challs=acme_util.CHALLENGES, combos=True) + mock_order = mock.MagicMock(authorizations=[authzr]) + 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 From c0dc31fd8896618e32bba0d6628c1b0620827e43 Mon Sep 17 00:00:00 2001 From: noci2012 Date: Tue, 20 Mar 2018 21:29:24 +0100 Subject: [PATCH 19/56] Allow _acme-challenge as a zone (#5707) * Allow _acme-challenge as a zone Like described here: https://github.com/lukas2511/dehydrated/wiki/example-dns-01-nsupdate-script Not using this patch may be an issue if the parent zone has been (where a wildcard certificate has been requested.) signed by DNSSEC. Please consider this also for inclusion before dns-01 will be allowed for wildcards. * Update dns_rfc2136.py forgot one domain_name reference * Update dns_rfc2136.py moved domain up & added assignment. * Update dns_rfc2136_test.py tests adjusted to new calls. * Update dns_rfc2136_test.py Forgot on DOMAIN... * Update dns_rfc2136_test.py * Update dns_rfc2136.py pydoc updates. * Update dns_rfc2136.py --- .../certbot_dns_rfc2136/dns_rfc2136.py | 26 +++++++++---------- .../certbot_dns_rfc2136/dns_rfc2136_test.py | 16 ++++++------ 2 files changed, 20 insertions(+), 22 deletions(-) diff --git a/certbot-dns-rfc2136/certbot_dns_rfc2136/dns_rfc2136.py b/certbot-dns-rfc2136/certbot_dns_rfc2136/dns_rfc2136.py index 85a3bf9bb..127773469 100644 --- a/certbot-dns-rfc2136/certbot_dns_rfc2136/dns_rfc2136.py +++ b/certbot-dns-rfc2136/certbot_dns_rfc2136/dns_rfc2136.py @@ -70,11 +70,11 @@ class Authenticator(dns_common.DNSAuthenticator): self._validate_algorithm ) - def _perform(self, domain, validation_name, validation): - self._get_rfc2136_client().add_txt_record(domain, validation_name, validation, self.ttl) + def _perform(self, _domain, validation_name, validation): + self._get_rfc2136_client().add_txt_record(validation_name, validation, self.ttl) - def _cleanup(self, domain, validation_name, validation): - self._get_rfc2136_client().del_txt_record(domain, validation_name, validation) + def _cleanup(self, _domain, validation_name, validation): + self._get_rfc2136_client().del_txt_record(validation_name, validation) def _get_rfc2136_client(self): return _RFC2136Client(self.credentials.conf('server'), @@ -95,18 +95,17 @@ class _RFC2136Client(object): }) self.algorithm = key_algorithm - def add_txt_record(self, domain_name, record_name, record_content, record_ttl): + def add_txt_record(self, record_name, record_content, record_ttl): """ Add a TXT record using the supplied information. - :param str domain: The domain to use to find the closest SOA. :param str record_name: The record name (typically beginning with '_acme-challenge.'). :param str record_content: The record content (typically the challenge validation). :param int record_ttl: The record TTL (number of seconds that the record may be cached). :raises certbot.errors.PluginError: if an error occurs communicating with the DNS server """ - domain = self._find_domain(domain_name) + domain = self._find_domain(record_name) n = dns.name.from_text(record_name) o = dns.name.from_text(domain) @@ -131,18 +130,17 @@ class _RFC2136Client(object): raise errors.PluginError('Received response from server: {0}' .format(dns.rcode.to_text(rcode))) - def del_txt_record(self, domain_name, record_name, record_content): + def del_txt_record(self, record_name, record_content): """ Delete a TXT record using the supplied information. - :param str domain: The domain to use to find the closest SOA. :param str record_name: The record name (typically beginning with '_acme-challenge.'). :param str record_content: The record content (typically the challenge validation). :param int record_ttl: The record TTL (number of seconds that the record may be cached). :raises certbot.errors.PluginError: if an error occurs communicating with the DNS server """ - domain = self._find_domain(domain_name) + domain = self._find_domain(record_name) n = dns.name.from_text(record_name) o = dns.name.from_text(domain) @@ -167,17 +165,17 @@ class _RFC2136Client(object): raise errors.PluginError('Received response from server: {0}' .format(dns.rcode.to_text(rcode))) - def _find_domain(self, domain_name): + def _find_domain(self, record_name): """ Find the closest domain with an SOA record for a given domain name. - :param str domain_name: The domain name for which to find the closest SOA record. + :param str record_name: The record name for which to find the closest SOA record. :returns: The domain, if found. :rtype: str :raises certbot.errors.PluginError: if no SOA record can be found. """ - domain_name_guesses = dns_common.base_domain_name_guesses(domain_name) + domain_name_guesses = dns_common.base_domain_name_guesses(record_name) # Loop through until we find an authoritative SOA record for guess in domain_name_guesses: @@ -185,7 +183,7 @@ class _RFC2136Client(object): return guess raise errors.PluginError('Unable to determine base domain for {0} using names: {1}.' - .format(domain_name, domain_name_guesses)) + .format(record_name, domain_name_guesses)) def _query_soa(self, domain_name): """ diff --git a/certbot-dns-rfc2136/certbot_dns_rfc2136/dns_rfc2136_test.py b/certbot-dns-rfc2136/certbot_dns_rfc2136/dns_rfc2136_test.py index 54fdb8575..8a5166330 100644 --- a/certbot-dns-rfc2136/certbot_dns_rfc2136/dns_rfc2136_test.py +++ b/certbot-dns-rfc2136/certbot_dns_rfc2136/dns_rfc2136_test.py @@ -41,7 +41,7 @@ class AuthenticatorTest(test_util.TempDirTestCase, dns_test_common.BaseAuthentic def test_perform(self): self.auth.perform([self.achall]) - expected = [mock.call.add_txt_record(DOMAIN, '_acme-challenge.'+DOMAIN, mock.ANY, mock.ANY)] + expected = [mock.call.add_txt_record('_acme-challenge.'+DOMAIN, mock.ANY, mock.ANY)] self.assertEqual(expected, self.mock_client.mock_calls) def test_cleanup(self): @@ -49,7 +49,7 @@ class AuthenticatorTest(test_util.TempDirTestCase, dns_test_common.BaseAuthentic self.auth._attempt_cleanup = True self.auth.cleanup([self.achall]) - expected = [mock.call.del_txt_record(DOMAIN, '_acme-challenge.'+DOMAIN, mock.ANY)] + expected = [mock.call.del_txt_record('_acme-challenge.'+DOMAIN, mock.ANY)] self.assertEqual(expected, self.mock_client.mock_calls) def test_invalid_algorithm_raises(self): @@ -82,7 +82,7 @@ class RFC2136ClientTest(unittest.TestCase): # _find_domain | pylint: disable=protected-access self.rfc2136_client._find_domain = mock.MagicMock(return_value="example.com") - self.rfc2136_client.add_txt_record(DOMAIN, "bar", "baz", 42) + self.rfc2136_client.add_txt_record("bar", "baz", 42) query_mock.assert_called_with(mock.ANY, SERVER) self.assertTrue("bar. 42 IN TXT \"baz\"" in str(query_mock.call_args[0][0])) @@ -96,7 +96,7 @@ class RFC2136ClientTest(unittest.TestCase): self.assertRaises( errors.PluginError, self.rfc2136_client.add_txt_record, - DOMAIN, "bar", "baz", 42) + "bar", "baz", 42) @mock.patch("dns.query.tcp") def test_add_txt_record_server_error(self, query_mock): @@ -107,7 +107,7 @@ class RFC2136ClientTest(unittest.TestCase): self.assertRaises( errors.PluginError, self.rfc2136_client.add_txt_record, - DOMAIN, "bar", "baz", 42) + "bar", "baz", 42) @mock.patch("dns.query.tcp") def test_del_txt_record(self, query_mock): @@ -115,7 +115,7 @@ class RFC2136ClientTest(unittest.TestCase): # _find_domain | pylint: disable=protected-access self.rfc2136_client._find_domain = mock.MagicMock(return_value="example.com") - self.rfc2136_client.del_txt_record(DOMAIN, "bar", "baz") + self.rfc2136_client.del_txt_record("bar", "baz") query_mock.assert_called_with(mock.ANY, SERVER) self.assertTrue("bar. 0 NONE TXT \"baz\"" in str(query_mock.call_args[0][0])) @@ -129,7 +129,7 @@ class RFC2136ClientTest(unittest.TestCase): self.assertRaises( errors.PluginError, self.rfc2136_client.del_txt_record, - DOMAIN, "bar", "baz") + "bar", "baz") @mock.patch("dns.query.tcp") def test_del_txt_record_server_error(self, query_mock): @@ -140,7 +140,7 @@ class RFC2136ClientTest(unittest.TestCase): self.assertRaises( errors.PluginError, self.rfc2136_client.del_txt_record, - DOMAIN, "bar", "baz") + "bar", "baz") def test_find_domain(self): # _query_soa | pylint: disable=protected-access From f01aa1295f0b35771b9937c3ee51e5966776a927 Mon Sep 17 00:00:00 2001 From: Edelita Valdez Date: Tue, 20 Mar 2018 23:40:44 -0700 Subject: [PATCH 20/56] Add quotes to command for docs extras. --- docs/contributing.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/contributing.rst b/docs/contributing.rst index 7178e2bad..2098f7cdf 100644 --- a/docs/contributing.rst +++ b/docs/contributing.rst @@ -377,7 +377,7 @@ This should generate documentation in the ``docs/_build/html`` directory. .. note:: If you skipped the "Getting Started" instructions above, - run ``pip install -e .[docs]`` to install Certbot's docs extras modules. + run ``pip install -e ".[docs]"`` to install Certbot's docs extras modules. .. _docker-dev: From cbd827382e3ab311b3ecfef91cebff51e90c794a Mon Sep 17 00:00:00 2001 From: Harlan Lieberman-Berg Date: Wed, 21 Mar 2018 11:17:06 -0400 Subject: [PATCH 21/56] Documentation on cron renewal (#5460) --- docs/using.rst | 39 ++++++++++++++++++++++++++++++++++++++- 1 file changed, 38 insertions(+), 1 deletion(-) diff --git a/docs/using.rst b/docs/using.rst index 319651af0..83e27bf34 100644 --- a/docs/using.rst +++ b/docs/using.rst @@ -552,6 +552,12 @@ can run on a regular basis, like every week or every day). In that case, you are likely to want to use the ``-q`` or ``--quiet`` quiet flag to silence all output except errors. +.. seealso:: Many of the certbot clients obtained through a + distribution come with automatic renewal out of the box, + such as Debian and Ubuntu versions installed through `apt`, + CentOS/RHEL 7 through EPEL, etc. See `Automated Renewals`_ + for more details. + If you are manually renewing all of your certificates, the ``--force-renewal`` flag may be helpful; it causes the expiration time of the certificate(s) to be ignored when considering renewal, and attempts to @@ -647,6 +653,31 @@ The following commands could be used to specify where these files are located:: sed -i 's,/etc/letsencrypt/live/example.com,/home/user/me/certbot,g' /etc/letsencrypt/renewal/example.com.conf certbot update_symlinks +Automated Renewals +------------------ + +Many Linux distributions provide automated renewal when you use the +packages installed through their system package manager. The +following table is an *incomplete* list of distributions which do so, +as well as their methods for doing so. + +If you are not sure whether or not your system has this already +automated, refer to your distribution's documentation, or check your +system's crontab (typically in `/etc/crontab/` and `/etc/cron.*/*` and +systemd timers (`systemctl list-timers`). + +.. csv-table:: Distributions with Automated Renewal + :header: "Distribution Name", "Distribution Version", "Automation Method" + + "CentOS", "EPEL 7", "systemd" + "Debian", "jessie", "cron, systemd" + "Debian", "stretch", "cron, systemd" + "Debian", "testing/sid", "cron, systemd" + "Fedora", "26", "systemd" + "Fedora", "27", "systemd" + "RHEL", "EPEL 7", "systemd" + "Ubuntu", "17.10", "cron, systemd" + "Ubuntu", "certbot PPA", "cron, systemd" .. _where-certs: @@ -888,7 +919,7 @@ Certbot accepts a global configuration file that applies its options to all invo of Certbot. Certificate specific configuration choices should be set in the ``.conf`` files that can be found in ``/etc/letsencrypt/renewal``. -By default no cli.ini file is created, after creating one +By default no cli.ini file is created, after creating one it is possible to specify the location of this configuration file with ``certbot-auto --config cli.ini`` (or shorter ``-c cli.ini``). An example configuration file is shown below: @@ -924,6 +955,12 @@ the oldest one to make room for new logs. The number of subsequent logs can be changed by passing the desired number to the command line flag ``--max-log-backups``. +.. note:: Some distributions, including Debian and Ubuntu, disable + certbot's internal log rotation in favor of a more traditional + logrotate script. If you are using a distribution's packages and + want to alter the log rotation, check `/etc/logrotate.d/` for a + certbot rotation script. + .. _command-line: Certbot command-line options From fe8e0c98c564a3b2c09d08d31dc6cf0d6b03f3ba Mon Sep 17 00:00:00 2001 From: Sebastiaan Lokhorst Date: Wed, 21 Mar 2018 19:18:39 +0100 Subject: [PATCH 22/56] Update docs for Apache plugin (#5776) The supported OSs are now listed in another file. The table also contradicted the text below. --- docs/using.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/using.rst b/docs/using.rst index 83e27bf34..7a25a5cc2 100644 --- a/docs/using.rst +++ b/docs/using.rst @@ -45,7 +45,7 @@ a combination_ of distinct authenticator and installer plugins. Plugin Auth Inst Notes Challenge types (and port) =========== ==== ==== =============================================================== ============================= apache_ Y Y | Automates obtaining and installing a certificate with Apache tls-sni-01_ (443) - | 2.4 on Debian-based distributions with ``libaugeas0`` 1.0+. + | 2.4 on OSes with ``libaugeas0`` 1.0+. webroot_ Y N | Obtains a certificate by writing to the webroot directory of http-01_ (80) | an already running webserver. nginx_ Y Y | Automates obtaining and installing a certificate with Nginx. tls-sni-01_ (443) @@ -87,7 +87,7 @@ Apache The Apache plugin currently requires an OS with augeas version 1.0; currently `it supports -`_ +`_ modern OSes based on Debian, Fedora, SUSE, Gentoo and Darwin. This automates both obtaining *and* installing certificates on an Apache webserver. To specify this plugin on the command line, simply include From 3f291e51c6df96389c8aa3fae05847066a331c7f Mon Sep 17 00:00:00 2001 From: Brad Warren Date: Wed, 21 Mar 2018 11:21:09 -0700 Subject: [PATCH 23/56] Update certbot auto to reflect 0.22 point releases (#5768) * Release 0.22.1 (cherry picked from commit 05c75e34e274bd76b72113d9c832198c065caf86) * Bump version to 0.23.0 (cherry picked from commit 6fd3a577910f18f6c931d50f575466edeed93557) * Release 0.22.2 (cherry picked from commit ea445ed11ee3895e98f92debee541772455fe35b) * Bump version to 0.23.0 (cherry picked from commit cbe87d451c66931a084f4e513d899aae085a37d3) --- certbot-auto | 26 +++++++++---------- docs/cli-help.txt | 12 ++++----- letsencrypt-auto | 26 +++++++++---------- letsencrypt-auto-source/certbot-auto.asc | 16 ++++++------ letsencrypt-auto-source/letsencrypt-auto | 24 ++++++++--------- letsencrypt-auto-source/letsencrypt-auto.sig | 4 ++- .../pieces/certbot-requirements.txt | 24 ++++++++--------- 7 files changed, 67 insertions(+), 65 deletions(-) diff --git a/certbot-auto b/certbot-auto index 343f56013..8c9745a6f 100755 --- a/certbot-auto +++ b/certbot-auto @@ -31,7 +31,7 @@ if [ -z "$VENV_PATH" ]; then fi VENV_BIN="$VENV_PATH/bin" BOOTSTRAP_VERSION_PATH="$VENV_PATH/certbot-auto-bootstrap-version.txt" -LE_AUTO_VERSION="0.22.0" +LE_AUTO_VERSION="0.22.2" BASENAME=$(basename $0) USAGE="Usage: $BASENAME [OPTIONS] A self-updating wrapper script for the Certbot ACME client. When run, updates @@ -1199,18 +1199,18 @@ letsencrypt==0.7.0 \ --hash=sha256:105a5fb107e45bcd0722eb89696986dcf5f08a86a321d6aef25a0c7c63375ade \ --hash=sha256:c36e532c486a7e92155ee09da54b436a3c420813ec1c590b98f635d924720de9 -certbot==0.22.0 \ - --hash=sha256:ebfeaf9737dc440a9f263099487523ab4c8d8da9def31a71327439d9186e00fa \ - --hash=sha256:ee307dd8f194bd710a3326aa4bacf95d358877498c0b9aa187eff0dc211dcbb3 -acme==0.22.0 \ - --hash=sha256:37e6d8e4eb7dd18edac96de209f451300e04074f14be7fce713db6931a0e4a20 \ - --hash=sha256:4a2cd52db32e914b68d8446c8e788f507c20edebbd1c36d4f3eda7b47c555fe8 -certbot-apache==0.22.0 \ - --hash=sha256:e91f6ec8203b636fa44f01017646fca68406224ee327fd56017103b78bc65539 \ - --hash=sha256:8fbab1a358ec131996d1c00f7d0ed18ee3624f8469cab3962dfd8ba40ca3e7cd -certbot-nginx==0.22.0 \ - --hash=sha256:d67210cf73cf44e8aeff04f6f228d8bde74444703ce3ccd929a450685b58c30b \ - --hash=sha256:b2b26bf9112062b02518407704cad09f7136322163d529a2dde3b6e1578ecb8c +certbot==0.22.2 \ + --hash=sha256:c8c63bdf0fed6258bdbc892454314ec37bcd1c35a7f62524a083d93ccdfc420d \ + --hash=sha256:e6e3639293e78397f31f7d99e3c63aff82d91e2b0d50d146ee3c77f830464bef +acme==0.22.2 \ + --hash=sha256:59a55244612ee305d2caa6bb4cddd400fb60ec841bf011ed29a2899832a682c2 \ + --hash=sha256:0ecd0ea369f53d5bc744d6e72717f9af2e1ceb558d109dbd433148851027adb4 +certbot-apache==0.22.2 \ + --hash=sha256:b5340d4b9190358fde8eb6a5be0def37e32014b5142ee79ef5d2319ccbbde754 \ + --hash=sha256:3cd26912bb5732d917ddf7aad2fe870090d4ece9a408b2c2de8e9723ec99c759 +certbot-nginx==0.22.2 \ + --hash=sha256:91feef0d879496835d355e82841f92e5ecb5abbf6f23ea0ee5bbb8f5a92b278a \ + --hash=sha256:b10bf04c1a20cf878d5e0d1877deb0e0780bc31b0ffda08ce7199bbc39d0753b UNLIKELY_EOF # ------------------------------------------------------------------------- diff --git a/docs/cli-help.txt b/docs/cli-help.txt index 65f623d79..399adc194 100644 --- a/docs/cli-help.txt +++ b/docs/cli-help.txt @@ -107,9 +107,9 @@ optional arguments: case, and to know when to deprecate support for past Python versions and flags. If you wish to hide this information from the Let's Encrypt server, set this to - "". (default: CertbotACMEClient/0.22.0 (certbot; + "". (default: CertbotACMEClient/0.22.2 (certbot; darwin 10.13.3) Authenticator/XXX Installer/YYY - (SUBCOMMAND; flags: FLAGS) Py/3.6.4). The flags + (SUBCOMMAND; flags: FLAGS) Py/2.7.14). The flags encoded in the user agent are: --duplicate, --force- renew, --allow-subset-of-names, -n, and whether any hooks are set. @@ -199,8 +199,8 @@ testing: --test-cert, --staging Use the staging server to obtain or revoke test - (invalid) certificates; equivalent to --server - https://acme-staging.api.letsencrypt.org/directory + (invalid) certificates; equivalent to --server https + ://acme-staging-v02.api.letsencrypt.org/directory (default: False) --debug Show tracebacks in case of errors, and allow certbot- auto execution on experimental platforms (default: @@ -308,8 +308,8 @@ renew: of renewed certificate domains (for example, "example.com www.example.com" (default: None) --disable-hook-validation - Ordinarily the commands specified for --pre- - hook/--post-hook/--deploy-hook will be checked for + Ordinarily the commands specified for --pre-hook + /--post-hook/--deploy-hook will be checked for validity, to see if the programs being run are in the $PATH, so that mistakes can be caught early, even when the hooks aren't being run just yet. The validation is diff --git a/letsencrypt-auto b/letsencrypt-auto index 343f56013..8c9745a6f 100755 --- a/letsencrypt-auto +++ b/letsencrypt-auto @@ -31,7 +31,7 @@ if [ -z "$VENV_PATH" ]; then fi VENV_BIN="$VENV_PATH/bin" BOOTSTRAP_VERSION_PATH="$VENV_PATH/certbot-auto-bootstrap-version.txt" -LE_AUTO_VERSION="0.22.0" +LE_AUTO_VERSION="0.22.2" BASENAME=$(basename $0) USAGE="Usage: $BASENAME [OPTIONS] A self-updating wrapper script for the Certbot ACME client. When run, updates @@ -1199,18 +1199,18 @@ letsencrypt==0.7.0 \ --hash=sha256:105a5fb107e45bcd0722eb89696986dcf5f08a86a321d6aef25a0c7c63375ade \ --hash=sha256:c36e532c486a7e92155ee09da54b436a3c420813ec1c590b98f635d924720de9 -certbot==0.22.0 \ - --hash=sha256:ebfeaf9737dc440a9f263099487523ab4c8d8da9def31a71327439d9186e00fa \ - --hash=sha256:ee307dd8f194bd710a3326aa4bacf95d358877498c0b9aa187eff0dc211dcbb3 -acme==0.22.0 \ - --hash=sha256:37e6d8e4eb7dd18edac96de209f451300e04074f14be7fce713db6931a0e4a20 \ - --hash=sha256:4a2cd52db32e914b68d8446c8e788f507c20edebbd1c36d4f3eda7b47c555fe8 -certbot-apache==0.22.0 \ - --hash=sha256:e91f6ec8203b636fa44f01017646fca68406224ee327fd56017103b78bc65539 \ - --hash=sha256:8fbab1a358ec131996d1c00f7d0ed18ee3624f8469cab3962dfd8ba40ca3e7cd -certbot-nginx==0.22.0 \ - --hash=sha256:d67210cf73cf44e8aeff04f6f228d8bde74444703ce3ccd929a450685b58c30b \ - --hash=sha256:b2b26bf9112062b02518407704cad09f7136322163d529a2dde3b6e1578ecb8c +certbot==0.22.2 \ + --hash=sha256:c8c63bdf0fed6258bdbc892454314ec37bcd1c35a7f62524a083d93ccdfc420d \ + --hash=sha256:e6e3639293e78397f31f7d99e3c63aff82d91e2b0d50d146ee3c77f830464bef +acme==0.22.2 \ + --hash=sha256:59a55244612ee305d2caa6bb4cddd400fb60ec841bf011ed29a2899832a682c2 \ + --hash=sha256:0ecd0ea369f53d5bc744d6e72717f9af2e1ceb558d109dbd433148851027adb4 +certbot-apache==0.22.2 \ + --hash=sha256:b5340d4b9190358fde8eb6a5be0def37e32014b5142ee79ef5d2319ccbbde754 \ + --hash=sha256:3cd26912bb5732d917ddf7aad2fe870090d4ece9a408b2c2de8e9723ec99c759 +certbot-nginx==0.22.2 \ + --hash=sha256:91feef0d879496835d355e82841f92e5ecb5abbf6f23ea0ee5bbb8f5a92b278a \ + --hash=sha256:b10bf04c1a20cf878d5e0d1877deb0e0780bc31b0ffda08ce7199bbc39d0753b UNLIKELY_EOF # ------------------------------------------------------------------------- diff --git a/letsencrypt-auto-source/certbot-auto.asc b/letsencrypt-auto-source/certbot-auto.asc index e9dd75a11..3e1c4791c 100644 --- a/letsencrypt-auto-source/certbot-auto.asc +++ b/letsencrypt-auto-source/certbot-auto.asc @@ -1,11 +1,11 @@ -----BEGIN PGP SIGNATURE----- -iQEzBAABCAAdFiEEos+1H6J1pyhiNOeyTRfJlc2XdfIFAlqgLj0ACgkQTRfJlc2X -dfIAtAf/YwRvn17fdJOSXr08LP/qDPefz8AFefUJdKoOa+ikWIOWZTh5hskGtXO0 -e894FNcZqbg6NQu/KUBQAz/nBDRz8IOaWN30MFq5B4V2A3In5rn59PNaCDSKSBbC -auyU24gYkBxbDPjMpuode7yCsvHxTsB5sLNmHByMyMTBmQaiT5odAjr7PztTP52S -s/29/WOCJAYzBBFFJ9d0QD0drVSIcDM5JCuUK2vXgPuPVD4f3GankgP1nnAJ5ADV -acJp3cQ3OsofeE/HTw0qq7TiL0dGYf8yhRFovFve7tX+oujMIRALQJW6K9Qi7KTv -777V6xHuphrA+1qIrg2H8czOBDclFQ== -=Ngvl +iQEzBAABCAAdFiEEos+1H6J1pyhiNOeyTRfJlc2XdfIFAlqwWJwACgkQTRfJlc2X +dfIzmwgAghmc3W63/qpCtJdezYeGLJdu03LvKoWYc7dTNYj2+0P5qmAAgCvKNY34 +qYzXA1jfCOgILSzRNE5WY+rbgjcmxxsxH+luYm6Ik0909MaMQ0D3h+5cRFs/tTtd +5cX0gxL3RQQTBwpnwbAZibe7lhjs9pXBiob2ek67hVr+xEwem69BQMlOhtYJbOs1 +osccoKc4NqaKbrfgOjjtMaL8YoRPO9vJHS9rRr6hxRZlPsmvusAHAiCbIrbX4XKE +CgxJFnuHK+amtfRoZg/xCqIK3Z94yZXPezywsri/YvDteOIs+DZ2qG/StfUrNYFX +WYfFFFyld0xwQtb4Oi9u4mx4sPg7lw== +=jZDE -----END PGP SIGNATURE----- diff --git a/letsencrypt-auto-source/letsencrypt-auto b/letsencrypt-auto-source/letsencrypt-auto index 0ba318140..07b313528 100755 --- a/letsencrypt-auto-source/letsencrypt-auto +++ b/letsencrypt-auto-source/letsencrypt-auto @@ -1199,18 +1199,18 @@ letsencrypt==0.7.0 \ --hash=sha256:105a5fb107e45bcd0722eb89696986dcf5f08a86a321d6aef25a0c7c63375ade \ --hash=sha256:c36e532c486a7e92155ee09da54b436a3c420813ec1c590b98f635d924720de9 -certbot==0.22.0 \ - --hash=sha256:ebfeaf9737dc440a9f263099487523ab4c8d8da9def31a71327439d9186e00fa \ - --hash=sha256:ee307dd8f194bd710a3326aa4bacf95d358877498c0b9aa187eff0dc211dcbb3 -acme==0.22.0 \ - --hash=sha256:37e6d8e4eb7dd18edac96de209f451300e04074f14be7fce713db6931a0e4a20 \ - --hash=sha256:4a2cd52db32e914b68d8446c8e788f507c20edebbd1c36d4f3eda7b47c555fe8 -certbot-apache==0.22.0 \ - --hash=sha256:e91f6ec8203b636fa44f01017646fca68406224ee327fd56017103b78bc65539 \ - --hash=sha256:8fbab1a358ec131996d1c00f7d0ed18ee3624f8469cab3962dfd8ba40ca3e7cd -certbot-nginx==0.22.0 \ - --hash=sha256:d67210cf73cf44e8aeff04f6f228d8bde74444703ce3ccd929a450685b58c30b \ - --hash=sha256:b2b26bf9112062b02518407704cad09f7136322163d529a2dde3b6e1578ecb8c +certbot==0.22.2 \ + --hash=sha256:c8c63bdf0fed6258bdbc892454314ec37bcd1c35a7f62524a083d93ccdfc420d \ + --hash=sha256:e6e3639293e78397f31f7d99e3c63aff82d91e2b0d50d146ee3c77f830464bef +acme==0.22.2 \ + --hash=sha256:59a55244612ee305d2caa6bb4cddd400fb60ec841bf011ed29a2899832a682c2 \ + --hash=sha256:0ecd0ea369f53d5bc744d6e72717f9af2e1ceb558d109dbd433148851027adb4 +certbot-apache==0.22.2 \ + --hash=sha256:b5340d4b9190358fde8eb6a5be0def37e32014b5142ee79ef5d2319ccbbde754 \ + --hash=sha256:3cd26912bb5732d917ddf7aad2fe870090d4ece9a408b2c2de8e9723ec99c759 +certbot-nginx==0.22.2 \ + --hash=sha256:91feef0d879496835d355e82841f92e5ecb5abbf6f23ea0ee5bbb8f5a92b278a \ + --hash=sha256:b10bf04c1a20cf878d5e0d1877deb0e0780bc31b0ffda08ce7199bbc39d0753b UNLIKELY_EOF # ------------------------------------------------------------------------- diff --git a/letsencrypt-auto-source/letsencrypt-auto.sig b/letsencrypt-auto-source/letsencrypt-auto.sig index 21cbf1a09..088d5efa6 100644 --- a/letsencrypt-auto-source/letsencrypt-auto.sig +++ b/letsencrypt-auto-source/letsencrypt-auto.sig @@ -1 +1,3 @@ -_$³î{çžÜ÷zÆÖéúÚQ)6("Rmnåf$B|cs°;D†û¤…ë¶V,Š?ÎË8æ¨j„ê™Íàã|¨»GÍýâfÃb"ߘ§,²î*ÓÉã‹¿ì%‚ƒ™]¦6#Ÿ¢•nœ|\`+.Xû,œ)#àŸÝQB‚j@¼0GþXéz5Œ Ž42+_HÆ`?£.ÏòÚÐXÛçÝô›0ÿè/„ì<ÎtúpÜN)’¤ÃICñQõÇ&i 8Y¸Ü‚&¤R·6µ^í]l²JÜ¡¦Ê#¹–Pœ»*ãþnúæmN6B3…oæc{^r%(âÊdvœûƒ<)ï® \ No newline at end of file +ˆc÷œNdC…f’òï*2‰;û=uTM,žù]K†= jy!Ð5M÷=2D’}/Ú°3¨Û.>¢¶±ˆ:K‚†"Ótaæöç,ÙGe®ÖC‹ùÌÞr‹Fö¬¥q™î©8d³ mßq›S˜ +oGQìžÍÃY ”íé4&FöåË8çWâÕÑxUmè|¸ãè¦0wÙ”~£Ìþg†R…T)…¬Eö=· ¤Ú‡7£(ŠÕ¿,›(\e‘fÃŰ…®·å‰*¤ô! nÀã (ª‡ƒ¦)ªª6òrk:noô¨¶Ûàªô×Fœ»]òx%ϸ÷ +c)w2~³Ÿ¯ ìËÚxC \ No newline at end of file diff --git a/letsencrypt-auto-source/pieces/certbot-requirements.txt b/letsencrypt-auto-source/pieces/certbot-requirements.txt index ff7b80ec0..865c3f6bc 100644 --- a/letsencrypt-auto-source/pieces/certbot-requirements.txt +++ b/letsencrypt-auto-source/pieces/certbot-requirements.txt @@ -1,12 +1,12 @@ -certbot==0.22.0 \ - --hash=sha256:ebfeaf9737dc440a9f263099487523ab4c8d8da9def31a71327439d9186e00fa \ - --hash=sha256:ee307dd8f194bd710a3326aa4bacf95d358877498c0b9aa187eff0dc211dcbb3 -acme==0.22.0 \ - --hash=sha256:37e6d8e4eb7dd18edac96de209f451300e04074f14be7fce713db6931a0e4a20 \ - --hash=sha256:4a2cd52db32e914b68d8446c8e788f507c20edebbd1c36d4f3eda7b47c555fe8 -certbot-apache==0.22.0 \ - --hash=sha256:e91f6ec8203b636fa44f01017646fca68406224ee327fd56017103b78bc65539 \ - --hash=sha256:8fbab1a358ec131996d1c00f7d0ed18ee3624f8469cab3962dfd8ba40ca3e7cd -certbot-nginx==0.22.0 \ - --hash=sha256:d67210cf73cf44e8aeff04f6f228d8bde74444703ce3ccd929a450685b58c30b \ - --hash=sha256:b2b26bf9112062b02518407704cad09f7136322163d529a2dde3b6e1578ecb8c +certbot==0.22.2 \ + --hash=sha256:c8c63bdf0fed6258bdbc892454314ec37bcd1c35a7f62524a083d93ccdfc420d \ + --hash=sha256:e6e3639293e78397f31f7d99e3c63aff82d91e2b0d50d146ee3c77f830464bef +acme==0.22.2 \ + --hash=sha256:59a55244612ee305d2caa6bb4cddd400fb60ec841bf011ed29a2899832a682c2 \ + --hash=sha256:0ecd0ea369f53d5bc744d6e72717f9af2e1ceb558d109dbd433148851027adb4 +certbot-apache==0.22.2 \ + --hash=sha256:b5340d4b9190358fde8eb6a5be0def37e32014b5142ee79ef5d2319ccbbde754 \ + --hash=sha256:3cd26912bb5732d917ddf7aad2fe870090d4ece9a408b2c2de8e9723ec99c759 +certbot-nginx==0.22.2 \ + --hash=sha256:91feef0d879496835d355e82841f92e5ecb5abbf6f23ea0ee5bbb8f5a92b278a \ + --hash=sha256:b10bf04c1a20cf878d5e0d1877deb0e0780bc31b0ffda08ce7199bbc39d0753b From afb6260c346962571113d7f26df8c6939539d2fa Mon Sep 17 00:00:00 2001 From: Brad Warren Date: Wed, 21 Mar 2018 11:21:35 -0700 Subject: [PATCH 24/56] update changelog for 0.22.1 and 0.22.2 (#5770) --- CHANGELOG.md | 48 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 48 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2ac87b0f9..1906858dc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,54 @@ Certbot adheres to [Semantic Versioning](http://semver.org/). +## 0.22.2 - 2018-03-19 + +### Fixed + +* A type error introduced in 0.22.1 that would occur during challenge cleanup + when a Certbot plugin raises an exception while trying to complete the + challenge was fixed. + +Despite us having broken lockstep, we are continuing to release new versions of +all Certbot components during releases for the time being, however, the only +packages with changes other than their version number were: + +* certbot + +More details about these changes can be found on our GitHub repo: +https://github.com/certbot/certbot/milestone/53?closed=1 + +## 0.22.1 - 2018-03-19 + +### Changed + +* The ACME server used with Certbot's --dry-run and --staging flags is now + Let's Encrypt's ACMEv2 staging server which allows people to also test ACMEv2 + features with these flags. + +### Fixed + +* The HTTP Content-Type header is now set to the correct value during + certificate revocation with new versions of the ACME protocol. +* When using Certbot with Let's Encrypt's ACMEv2 server, it would add a blank + line to the top of chain.pem and between the certificates in fullchain.pem + for each lineage. These blank lines have been removed. +* Resolved a bug that caused Certbot's --allow-subset-of-names flag not to + work. +* Fixed a regression in acme.client.Client that caused the class to not work + when it was initialized without a ClientNetwork which is done by some of the + other projects using our ACME library. + +Despite us having broken lockstep, we are continuing to release new versions of +all Certbot components during releases for the time being, however, the only +packages with changes other than their version number were: + +* acme +* certbot + +More details about these changes can be found on our GitHub repo: +https://github.com/certbot/certbot/milestone/51?closed=1 + ## 0.22.0 - 2018-03-07 ### Added From bca0aa48c2d37142bf4ab06e3b5b79e68ffbd224 Mon Sep 17 00:00:00 2001 From: sydneyli Date: Wed, 21 Mar 2018 15:41:33 -0700 Subject: [PATCH 25/56] logging: log timestamps as local timezone instead of UTC (#5607) * logging: log timestamps as local timezone instead of UTC * test(logging): expect localtime instead of gmtime * linter fix in logging --- certbot/log.py | 2 -- certbot/tests/log_test.py | 2 +- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/certbot/log.py b/certbot/log.py index e0d2e8f11..face93cb3 100644 --- a/certbot/log.py +++ b/certbot/log.py @@ -19,7 +19,6 @@ import logging.handlers import os import sys import tempfile -import time import traceback from acme import messages @@ -148,7 +147,6 @@ def setup_log_file_handler(config, logfile, fmt): handler.doRollover() # TODO: creates empty letsencrypt.log.1 file handler.setLevel(logging.DEBUG) handler_formatter = logging.Formatter(fmt=fmt) - handler_formatter.converter = time.gmtime # don't use localtime handler.setFormatter(handler_formatter) return handler, log_file_path diff --git a/certbot/tests/log_test.py b/certbot/tests/log_test.py index 3b0e1c5f6..549d2c5e1 100644 --- a/certbot/tests/log_test.py +++ b/certbot/tests/log_test.py @@ -156,7 +156,7 @@ class SetupLogFileHandlerTest(test_util.ConfigTestCase): handler.close() self.assertEqual(handler.level, logging.DEBUG) - self.assertEqual(handler.formatter.converter, time.gmtime) + self.assertEqual(handler.formatter.converter, time.localtime) expected_path = os.path.join(self.config.logs_dir, log_file) self.assertEqual(log_path, expected_path) From 8e9a4447ff5598c303515f183698574318499598 Mon Sep 17 00:00:00 2001 From: Delan Azabani Date: Fri, 23 Mar 2018 06:24:53 +1100 Subject: [PATCH 26/56] make pip_install.sh compatible with POSIX sh(1) again (#5622) --- tools/pip_install.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/pip_install.sh b/tools/pip_install.sh index b385c5482..78e2afa17 100755 --- a/tools/pip_install.sh +++ b/tools/pip_install.sh @@ -1,4 +1,4 @@ -#!/bin/bash -e +#!/bin/sh -e # pip installs packages using pinned package versions. If CERTBOT_OLDEST is set # to 1, a combination of tools/oldest_constraints.txt, # tools/dev_constraints.txt, and local-oldest-requirements.txt contained in the From 693cb1d162ae37aa184aa022803a1dec9403bcad Mon Sep 17 00:00:00 2001 From: Alokin Software Pvt Ltd Date: Fri, 23 Mar 2018 06:20:05 +0530 Subject: [PATCH 27/56] Support Openresty in the NGINX plugin (#5467) * fixes #4919 openresty_support * making the regex more general * reformatting warning to pass lint * Fix string formatting in logging function * Fix LE_AUTO_VERSION --- certbot-nginx/certbot_nginx/configurator.py | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/certbot-nginx/certbot_nginx/configurator.py b/certbot-nginx/certbot_nginx/configurator.py index 41ca52d13..4b039417d 100644 --- a/certbot-nginx/certbot_nginx/configurator.py +++ b/certbot-nginx/certbot_nginx/configurator.py @@ -895,7 +895,7 @@ class NginxConfigurator(common.Installer): raise errors.PluginError( "Unable to run %s -V" % self.conf('ctl')) - version_regex = re.compile(r"nginx/([0-9\.]*)", re.IGNORECASE) + version_regex = re.compile(r"nginx version: ([^/]+)/([0-9\.]*)", re.IGNORECASE) version_matches = version_regex.findall(text) sni_regex = re.compile(r"TLS SNI support enabled", re.IGNORECASE) @@ -912,7 +912,12 @@ class NginxConfigurator(common.Installer): if not sni_matches: raise errors.PluginError("Nginx build doesn't support SNI") - nginx_version = tuple([int(i) for i in version_matches[0].split(".")]) + product_name, product_version = version_matches[0] + if product_name is not 'nginx': + logger.warning("NGINX derivative %s is not officially supported by" + " certbot", product_name) + + nginx_version = tuple([int(i) for i in product_version.split(".")]) # nginx < 0.8.48 uses machine hostname as default server_name instead of # the empty string From 8d0d42a739893fba38cefa33fc60f8d70fbf60dc Mon Sep 17 00:00:00 2001 From: ohemorange Date: Fri, 23 Mar 2018 16:30:13 -0700 Subject: [PATCH 28/56] Refactor _add_directive into separate functions (#5786) * Refactor _add_directive to separate functions * UnspacedList isn't idempotent * refactor parser in add_server_directives and update_or_add_server_directives * update parser tests * remove replace=False and add to update_or_add for replace=True in configurator * remove replace=False and add to update_or_add for replace=True in http01 * update documentation --- certbot-nginx/certbot_nginx/configurator.py | 18 +-- certbot-nginx/certbot_nginx/http_01.py | 4 +- certbot-nginx/certbot_nginx/parser.py | 109 +++++++++++------- .../certbot_nginx/tests/configurator_test.py | 9 +- .../certbot_nginx/tests/parser_test.py | 29 ++--- 5 files changed, 92 insertions(+), 77 deletions(-) diff --git a/certbot-nginx/certbot_nginx/configurator.py b/certbot-nginx/certbot_nginx/configurator.py index 4b039417d..1073bff4f 100644 --- a/certbot-nginx/certbot_nginx/configurator.py +++ b/certbot-nginx/certbot_nginx/configurator.py @@ -192,8 +192,8 @@ class NginxConfigurator(common.Installer): cert_directives = [['\n ', 'ssl_certificate', ' ', fullchain_path], ['\n ', 'ssl_certificate_key', ' ', key_path]] - self.parser.add_server_directives(vhost, - cert_directives, replace=True) + self.parser.update_or_add_server_directives(vhost, + cert_directives) logger.info("Deploying Certificate to VirtualHost %s", vhost.filep) self.save_notes += ("Changed vhost at %s with addresses of %s\n" % @@ -344,7 +344,7 @@ class NginxConfigurator(common.Installer): for name in vhost.names: name_block[0].append(' ') name_block[0].append(name) - self.parser.add_server_directives(vhost, name_block, replace=True) + self.parser.update_or_add_server_directives(vhost, name_block) def _get_default_vhost(self, port): vhost_list = self.parser.get_vhosts() @@ -584,7 +584,7 @@ class NginxConfigurator(common.Installer): # have it continue to do so. if len(vhost.addrs) == 0: listen_block = [['\n ', 'listen', ' ', self.DEFAULT_LISTEN_PORT]] - self.parser.add_server_directives(vhost, listen_block, replace=False) + self.parser.add_server_directives(vhost, listen_block) if vhost.ipv6_enabled(): ipv6_block = ['\n ', @@ -618,7 +618,7 @@ class NginxConfigurator(common.Installer): ]) self.parser.add_server_directives( - vhost, ssl_block, replace=False) + vhost, ssl_block) ################################## # enhancement methods (IInstaller) @@ -683,7 +683,7 @@ class NginxConfigurator(common.Installer): ['\n ', 'add_header', ' ', header_substring, ' '] + constants.HEADER_ARGS[header_substring], ['\n']] - self.parser.add_server_directives(vhost, header_directives, replace=False) + self.parser.add_server_directives(vhost, header_directives) def _add_redirect_block(self, vhost, domain): """Add redirect directive to vhost @@ -691,7 +691,7 @@ class NginxConfigurator(common.Installer): redirect_block = _redirect_block_for_domain(domain) self.parser.add_server_directives( - vhost, redirect_block, replace=False, insert_at_top=True) + vhost, redirect_block, insert_at_top=True) def _split_block(self, vhost, only_directives=None): """Splits this "virtual host" (i.e. this nginx server block) into @@ -771,7 +771,7 @@ class NginxConfigurator(common.Installer): # Add this at the bottom to get the right order of directives return_404_directive = [['\n ', 'return', ' ', '404']] - self.parser.add_server_directives(http_vhost, return_404_directive, replace=False) + self.parser.add_server_directives(http_vhost, return_404_directive) vhost = http_vhost @@ -821,7 +821,7 @@ class NginxConfigurator(common.Installer): try: self.parser.add_server_directives(vhost, - stapling_directives, replace=False) + stapling_directives) except errors.MisconfigurationError as error: logger.debug(error) raise errors.PluginError("An error occurred while enabling OCSP " diff --git a/certbot-nginx/certbot_nginx/http_01.py b/certbot-nginx/certbot_nginx/http_01.py index 0b1b2bfe0..93c7bfc90 100644 --- a/certbot-nginx/certbot_nginx/http_01.py +++ b/certbot-nginx/certbot_nginx/http_01.py @@ -199,9 +199,9 @@ class NginxHttp01(common.ChallengePerformer): ['return', ' ', '200', ' ', validation]]]] self.configurator.parser.add_server_directives(vhost, - location_directive, replace=False) + location_directive) rewrite_directive = [['rewrite', ' ', '^(/.well-known/acme-challenge/.*)', ' ', '$1', ' ', 'break']] self.configurator.parser.add_server_directives(vhost, - rewrite_directive, replace=False, insert_at_top=True) + rewrite_directive, insert_at_top=True) diff --git a/certbot-nginx/certbot_nginx/parser.py b/certbot-nginx/certbot_nginx/parser.py index e329307c0..3dc70f19b 100644 --- a/certbot-nginx/certbot_nginx/parser.py +++ b/certbot-nginx/certbot_nginx/parser.py @@ -276,15 +276,13 @@ class NginxParser(object): return False - def add_server_directives(self, vhost, directives, replace, insert_at_top=False): - """Add or replace directives in the server block identified by vhost. + def add_server_directives(self, vhost, directives, insert_at_top=False): + """Add directives to the server block identified by vhost. This method modifies vhost to be fully consistent with the new directives. - ..note :: If replace is True and the directive already exists, the first - instance will be replaced. Otherwise, the directive is added. - ..note :: If replace is False nothing gets added if an identical - block exists already. + ..note :: It's an error to try and add a nonrepeatable directive that already + exists in the config block with a conflicting value. ..todo :: Doesn't match server blocks whose server_name directives are split across multiple conf files. @@ -292,13 +290,34 @@ class NginxParser(object): :param :class:`~certbot_nginx.obj.VirtualHost` vhost: The vhost whose information we use to match on :param list directives: The directives to add - :param bool replace: Whether to only replace existing directives :param bool insert_at_top: True if the directives need to be inserted at the top of the server block instead of the bottom """ self._modify_server_directives(vhost, - functools.partial(_add_directives, directives, replace, insert_at_top)) + functools.partial(_add_directives, directives, insert_at_top)) + + def update_or_add_server_directives(self, vhost, directives, insert_at_top=False): + """Add or replace directives in the server block identified by vhost. + + This method modifies vhost to be fully consistent with the new directives. + + ..note :: When a directive with the same name already exists in the + config block, the first instance will be replaced. Otherwise, the directive + will be appended/prepended to the config block as in add_server_directives. + + ..todo :: Doesn't match server blocks whose server_name directives are + split across multiple conf files. + + :param :class:`~certbot_nginx.obj.VirtualHost` vhost: The vhost + whose information we use to match on + :param list directives: The directives to add + :param bool insert_at_top: True if the directives need to be inserted at the top + of the server block instead of the bottom + + """ + self._modify_server_directives(vhost, + functools.partial(_update_or_add_directives, directives, insert_at_top)) def remove_server_directives(self, vhost, directive_name, match_func=None): """Remove all directives of type directive_name. @@ -524,26 +543,17 @@ def _is_ssl_on_directive(entry): len(entry) == 2 and entry[0] == 'ssl' and entry[1] == 'on') -def _add_directives(directives, replace, insert_at_top, block): - """Adds or replaces directives in a config block. - - When replace=False, it's an error to try and add a nonrepeatable directive that already - exists in the config block with a conflicting value. - - When replace=True and a directive with the same name already exists in the - config block, the first instance will be replaced. Otherwise, the directive - will be added to the config block. - - ..todo :: Find directives that are in included files. - - :param list directives: The new directives. - :param bool replace: Described above. - :param bool insert_at_top: Described above. - :param list block: The block to replace in - - """ +def _add_directives(directives, insert_at_top, block): + """Adds directives to a config block.""" for directive in directives: - _add_directive(block, directive, replace, insert_at_top) + _add_directive(block, directive, insert_at_top) + if block and '\n' not in block[-1]: # could be " \n " or ["\n"] ! + block.append(nginxparser.UnspacedList('\n')) + +def _update_or_add_directives(directives, insert_at_top, block): + """Adds or replaces directives in a config block.""" + for directive in directives: + _update_or_add_directive(block, directive, insert_at_top) if block and '\n' not in block[-1]: # could be " \n " or ["\n"] ! block.append(nginxparser.UnspacedList('\n')) @@ -601,28 +611,20 @@ def _find_location(block, directive_name, match_func=None): return next((index for index, line in enumerate(block) \ if line and line[0] == directive_name and (match_func is None or match_func(line))), None) -def _add_directive(block, directive, replace, insert_at_top): - """Adds or replaces a single directive in a config block. +def _is_whitespace_or_comment(directive): + """Is this directive either a whitespace or comment directive?""" + return len(directive) == 0 or directive[0] == '#' - See _add_directives for more documentation. - - """ - directive = nginxparser.UnspacedList(directive) - def is_whitespace_or_comment(directive): - """Is this directive either a whitespace or comment directive?""" - return len(directive) == 0 or directive[0] == '#' - if is_whitespace_or_comment(directive): +def _add_directive(block, directive, insert_at_top): + if not isinstance(directive, nginxparser.UnspacedList): + directive = nginxparser.UnspacedList(directive) + if _is_whitespace_or_comment(directive): # whitespace or comment block.append(directive) return location = _find_location(block, directive[0]) - if replace: - if location is not None: - block[location] = directive - comment_directive(block, location) - return # Append or prepend directive. Fail if the name is not a repeatable directive name, # and there is already a copy of that directive with a different value # in the config file. @@ -647,7 +649,7 @@ def _add_directive(block, directive, replace, insert_at_top): for included_directive in included_directives: included_dir_loc = _find_location(block, included_directive[0]) included_dir_name = included_directive[0] - if not is_whitespace_or_comment(included_directive) \ + if not _is_whitespace_or_comment(included_directive) \ and not can_append(included_dir_loc, included_dir_name): if block[included_dir_loc] != included_directive: raise errors.MisconfigurationError(err_fmt.format(included_directive, @@ -668,6 +670,27 @@ def _add_directive(block, directive, replace, insert_at_top): elif block[location] != directive: raise errors.MisconfigurationError(err_fmt.format(directive, block[location])) +def _update_directive(block, directive, location): + block[location] = directive + comment_directive(block, location) + +def _update_or_add_directive(block, directive, insert_at_top): + if not isinstance(directive, nginxparser.UnspacedList): + directive = nginxparser.UnspacedList(directive) + if _is_whitespace_or_comment(directive): + # whitespace or comment + block.append(directive) + return + + location = _find_location(block, directive[0]) + + # we can update directive + if location is not None: + _update_directive(block, directive, location) + return + + _add_directive(block, directive, insert_at_top) + def _is_certbot_comment(directive): return '#' in directive and COMMENT in directive diff --git a/certbot-nginx/certbot_nginx/tests/configurator_test.py b/certbot-nginx/certbot_nginx/tests/configurator_test.py index 9489b534a..34abf2f0d 100644 --- a/certbot-nginx/certbot_nginx/tests/configurator_test.py +++ b/certbot-nginx/certbot_nginx/tests/configurator_test.py @@ -113,8 +113,7 @@ class NginxConfiguratorTest(util.NginxTest): None, [0]) self.config.parser.add_server_directives( mock_vhost, - [['listen', ' ', '5001', ' ', 'ssl']], - replace=False) + [['listen', ' ', '5001', ' ', 'ssl']]) self.config.save() # pylint: disable=protected-access @@ -206,9 +205,9 @@ class NginxConfiguratorTest(util.NginxTest): "example/chain.pem", None) - @mock.patch('certbot_nginx.parser.NginxParser.add_server_directives') - def test_deploy_cert_raise_on_add_error(self, mock_add_server_directives): - mock_add_server_directives.side_effect = errors.MisconfigurationError() + @mock.patch('certbot_nginx.parser.NginxParser.update_or_add_server_directives') + def test_deploy_cert_raise_on_add_error(self, mock_update_or_add_server_directives): + mock_update_or_add_server_directives.side_effect = errors.MisconfigurationError() self.assertRaises( errors.PluginError, self.config.deploy_cert, diff --git a/certbot-nginx/certbot_nginx/tests/parser_test.py b/certbot-nginx/certbot_nginx/tests/parser_test.py index 5fce6f25a..9db251d59 100644 --- a/certbot-nginx/certbot_nginx/tests/parser_test.py +++ b/certbot-nginx/certbot_nginx/tests/parser_test.py @@ -206,8 +206,7 @@ class NginxParserTest(util.NginxTest): #pylint: disable=too-many-public-methods mock_vhost.path = [0] nparser.add_server_directives(mock_vhost, [['foo', 'bar'], ['ssl_certificate', - '/etc/ssl/cert2.pem']], - replace=False) + '/etc/ssl/cert2.pem']]) nparser.remove_server_directives(mock_vhost, 'foo') nparser.remove_server_directives(mock_vhost, 'ssl_certificate') self.assertEqual(nparser.parsed[example_com], @@ -226,8 +225,7 @@ class NginxParserTest(util.NginxTest): #pylint: disable=too-many-public-methods None, [10, 1, 9]) nparser.add_server_directives(mock_vhost, [['foo', 'bar'], ['\n ', 'ssl_certificate', ' ', - '/etc/ssl/cert.pem']], - replace=False) + '/etc/ssl/cert.pem']]) ssl_re = re.compile(r'\n\s+ssl_certificate /etc/ssl/cert.pem') dump = nginxparser.dumps(nparser.parsed[nparser.abs_path('nginx.conf')]) self.assertEqual(1, len(re.findall(ssl_re, dump))) @@ -239,10 +237,8 @@ class NginxParserTest(util.NginxTest): #pylint: disable=too-many-public-methods mock_vhost.path = [0] nparser.add_server_directives(mock_vhost, [['foo', 'bar'], ['ssl_certificate', - '/etc/ssl/cert2.pem']], - replace=False) - nparser.add_server_directives(mock_vhost, [['foo', 'bar']], - replace=False) + '/etc/ssl/cert2.pem']]) + nparser.add_server_directives(mock_vhost, [['foo', 'bar']]) from certbot_nginx.parser import COMMENT self.assertEqual(nparser.parsed[example_com], [[['server'], [['listen', '69.50.225.155:9000'], @@ -264,8 +260,7 @@ class NginxParserTest(util.NginxTest): #pylint: disable=too-many-public-methods nparser.add_server_directives, mock_vhost, [['foo', 'bar'], - ['ssl_certificate', '/etc/ssl/cert2.pem']], - replace=False) + ['ssl_certificate', '/etc/ssl/cert2.pem']]) def test_comment_is_repeatable(self): nparser = parser.NginxParser(self.config_path) @@ -275,12 +270,10 @@ class NginxParserTest(util.NginxTest): #pylint: disable=too-many-public-methods set(['.example.com', 'example.*']), None, [0]) nparser.add_server_directives(mock_vhost, - [['\n ', '#', ' ', 'what a nice comment']], - replace=False) + [['\n ', '#', ' ', 'what a nice comment']]) nparser.add_server_directives(mock_vhost, [['\n ', 'include', ' ', - nparser.abs_path('comment_in_file.conf')]], - replace=False) + nparser.abs_path('comment_in_file.conf')]]) from certbot_nginx.parser import COMMENT self.assertEqual(nparser.parsed[example_com], [[['server'], [['listen', '69.50.225.155:9000'], @@ -299,8 +292,8 @@ class NginxParserTest(util.NginxTest): #pylint: disable=too-many-public-methods target = set(['.example.com', 'example.*']) filep = nparser.abs_path('sites-enabled/example.com') mock_vhost = obj.VirtualHost(filep, None, None, None, target, None, [0]) - nparser.add_server_directives( - mock_vhost, [['server_name', 'foobar.com']], replace=True) + nparser.update_or_add_server_directives( + mock_vhost, [['server_name', 'foobar.com']]) from certbot_nginx.parser import COMMENT self.assertEqual( nparser.parsed[filep], @@ -310,8 +303,8 @@ class NginxParserTest(util.NginxTest): #pylint: disable=too-many-public-methods ['server_name', 'example.*'], [] ]]]) mock_vhost.names = set(['foobar.com', 'example.*']) - nparser.add_server_directives( - mock_vhost, [['ssl_certificate', 'cert.pem']], replace=True) + nparser.update_or_add_server_directives( + mock_vhost, [['ssl_certificate', 'cert.pem']]) self.assertEqual( nparser.parsed[filep], [[['server'], [['listen', '69.50.225.155:9000'], From e9707ebc26008a1422e80fcafed8e2bc0dc471cf Mon Sep 17 00:00:00 2001 From: ohemorange Date: Mon, 26 Mar 2018 14:56:31 -0700 Subject: [PATCH 29/56] Allow 'default' along with 'default_server' in Nginx (#5788) * test default detection * Allow 'default' along with 'default_server' in Nginx * Test that default gets written out as default_server in canonical string * remove superfulous parens --- certbot-nginx/certbot_nginx/obj.py | 2 ++ certbot-nginx/certbot_nginx/parser.py | 8 +++++--- certbot-nginx/certbot_nginx/tests/obj_test.py | 7 ++++++- 3 files changed, 13 insertions(+), 4 deletions(-) diff --git a/certbot-nginx/certbot_nginx/obj.py b/certbot-nginx/certbot_nginx/obj.py index ea5c6e2f8..eb94c138e 100644 --- a/certbot-nginx/certbot_nginx/obj.py +++ b/certbot-nginx/certbot_nginx/obj.py @@ -88,6 +88,8 @@ class Addr(common.Addr): ssl = True elif nextpart == 'default_server': default = True + elif nextpart == 'default': + default = True elif nextpart == "ipv6only=on": ipv6only = True diff --git a/certbot-nginx/certbot_nginx/parser.py b/certbot-nginx/certbot_nginx/parser.py index 3dc70f19b..85a239c85 100644 --- a/certbot-nginx/certbot_nginx/parser.py +++ b/certbot-nginx/certbot_nginx/parser.py @@ -391,9 +391,11 @@ class NginxParser(object): for addr in new_vhost.addrs: addr.default = False for directive in enclosing_block[new_vhost.path[-1]][1]: - if (len(directive) > 0 and directive[0] == 'listen' - and 'default_server' in directive): - del directive[directive.index('default_server')] + 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')] return new_vhost diff --git a/certbot-nginx/certbot_nginx/tests/obj_test.py b/certbot-nginx/certbot_nginx/tests/obj_test.py index 929e7cdf0..9e5853c4a 100644 --- a/certbot-nginx/certbot_nginx/tests/obj_test.py +++ b/certbot-nginx/certbot_nginx/tests/obj_test.py @@ -14,6 +14,7 @@ class AddrTest(unittest.TestCase): self.addr5 = Addr.fromstring("myhost") self.addr6 = Addr.fromstring("80 default_server spdy") self.addr7 = Addr.fromstring("unix:/var/run/nginx.sock") + self.addr8 = Addr.fromstring("*:80 default ssl") def test_fromstring(self): self.assertEqual(self.addr1.get_addr(), "192.168.1.1") @@ -46,6 +47,8 @@ class AddrTest(unittest.TestCase): self.assertFalse(self.addr6.ssl) self.assertTrue(self.addr6.default) + self.assertTrue(self.addr8.default) + self.assertEqual(None, self.addr7) def test_str(self): @@ -55,6 +58,7 @@ class AddrTest(unittest.TestCase): self.assertEqual(str(self.addr4), "*:80 default_server ssl") self.assertEqual(str(self.addr5), "myhost") self.assertEqual(str(self.addr6), "80 default_server") + self.assertEqual(str(self.addr8), "*:80 default_server ssl") def test_to_string(self): self.assertEqual(self.addr1.to_string(), "192.168.1.1") @@ -77,7 +81,8 @@ class AddrTest(unittest.TestCase): from certbot_nginx.obj import Addr any_addresses = ("0.0.0.0:80 default_server ssl", "80 default_server ssl", - "*:80 default_server ssl") + "*:80 default_server ssl", + "80 default ssl") for first, second in itertools.combinations(any_addresses, 2): self.assertEqual(Addr.fromstring(first), Addr.fromstring(second)) From 8cdb213a613ade9a5b7a58f393a391a65ec097bc Mon Sep 17 00:00:00 2001 From: Andrew Starr-Bochicchio Date: Mon, 26 Mar 2018 19:12:55 -0400 Subject: [PATCH 30/56] Google DNS: Mock API discovery to run tests without internet connection. (#5791) * Google DNS: Mock API discovery to run tests without internet connection. * Allow test to pass when run from main cerbot package. --- .../certbot_dns_google/dns_google.py | 9 +- .../certbot_dns_google/dns_google_test.py | 10 +- .../testdata/discovery.json | 1401 +++++++++++++++++ 3 files changed, 1417 insertions(+), 3 deletions(-) create mode 100644 certbot-dns-google/certbot_dns_google/testdata/discovery.json diff --git a/certbot-dns-google/certbot_dns_google/dns_google.py b/certbot-dns-google/certbot_dns_google/dns_google.py index e2088b357..c204cb0ca 100644 --- a/certbot-dns-google/certbot_dns_google/dns_google.py +++ b/certbot-dns-google/certbot_dns_google/dns_google.py @@ -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): """ diff --git a/certbot-dns-google/certbot_dns_google/dns_google_test.py b/certbot-dns-google/certbot_dns_google/dns_google_test.py index 72b8be8af..b6f6e08b6 100644 --- a/certbot-dns-google/certbot_dns_google/dns_google_test.py +++ b/certbot-dns-google/certbot_dns_google/dns_google_test.py @@ -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() diff --git a/certbot-dns-google/certbot_dns_google/testdata/discovery.json b/certbot-dns-google/certbot_dns_google/testdata/discovery.json new file mode 100644 index 000000000..79a406645 --- /dev/null +++ b/certbot-dns-google/certbot_dns_google/testdata/discovery.json @@ -0,0 +1,1401 @@ +{ + "kind": "discovery#restDescription", + "etag": "\"-iA1DTNe4s-I6JZXPt1t1Ypy8IU/gSzgHqX4Zwypnde2YApimTf_qmE\"", + "discoveryVersion": "v1", + "id": "dns:v1", + "name": "dns", + "version": "v1", + "revision": "20180314", + "title": "Google Cloud DNS API", + "description": "Configures and serves authoritative DNS records.", + "ownerDomain": "google.com", + "ownerName": "Google", + "icons": { + "x16": "https://www.gstatic.com/images/branding/product/1x/googleg_16dp.png", + "x32": "https://www.gstatic.com/images/branding/product/1x/googleg_32dp.png" + }, + "documentationLink": "https://developers.google.com/cloud-dns", + "protocol": "rest", + "baseUrl": "https://www.googleapis.com/dns/v1/projects/", + "basePath": "/dns/v1/projects/", + "rootUrl": "https://www.googleapis.com/", + "servicePath": "dns/v1/projects/", + "batchPath": "batch/dns/v1", + "parameters": { + "alt": { + "type": "string", + "description": "Data format for the response.", + "default": "json", + "enum": [ + "json" + ], + "enumDescriptions": [ + "Responses with Content-Type of application/json" + ], + "location": "query" + }, + "fields": { + "type": "string", + "description": "Selector specifying which fields to include in a partial response.", + "location": "query" + }, + "key": { + "type": "string", + "description": "API key. Your API key identifies your project and provides you with API access, quota, and reports. Required unless you provide an OAuth 2.0 token.", + "location": "query" + }, + "oauth_token": { + "type": "string", + "description": "OAuth 2.0 token for the current user.", + "location": "query" + }, + "prettyPrint": { + "type": "boolean", + "description": "Returns response with indentations and line breaks.", + "default": "true", + "location": "query" + }, + "quotaUser": { + "type": "string", + "description": "Available to use for quota purposes for server-side applications. Can be any arbitrary string assigned to a user, but should not exceed 40 characters. Overrides userIp if both are provided.", + "location": "query" + }, + "userIp": { + "type": "string", + "description": "IP address of the site where the request originates. Use this if you want to enforce per-user limits.", + "location": "query" + } + }, + "auth": { + "oauth2": { + "scopes": { + "https://www.googleapis.com/auth/cloud-platform": { + "description": "View and manage your data across Google Cloud Platform services" + }, + "https://www.googleapis.com/auth/cloud-platform.read-only": { + "description": "View your data across Google Cloud Platform services" + }, + "https://www.googleapis.com/auth/ndev.clouddns.readonly": { + "description": "View your DNS records hosted by Google Cloud DNS" + }, + "https://www.googleapis.com/auth/ndev.clouddns.readwrite": { + "description": "View and manage your DNS records hosted by Google Cloud DNS" + } + } + } + }, + "schemas": { + "Change": { + "id": "Change", + "type": "object", + "description": "An atomic update to a collection of ResourceRecordSets.", + "properties": { + "additions": { + "type": "array", + "description": "Which ResourceRecordSets to add?", + "items": { + "$ref": "ResourceRecordSet" + } + }, + "deletions": { + "type": "array", + "description": "Which ResourceRecordSets to remove? Must match existing data exactly.", + "items": { + "$ref": "ResourceRecordSet" + } + }, + "id": { + "type": "string", + "description": "Unique identifier for the resource; defined by the server (output only)." + }, + "isServing": { + "type": "boolean", + "description": "If the DNS queries for the zone will be served." + }, + "kind": { + "type": "string", + "description": "Identifies what kind of resource this is. Value: the fixed string \"dns#change\".", + "default": "dns#change" + }, + "startTime": { + "type": "string", + "description": "The time that this operation was started by the server (output only). This is in RFC3339 text format." + }, + "status": { + "type": "string", + "description": "Status of the operation (output only).", + "enum": [ + "done", + "pending" + ], + "enumDescriptions": [ + "", + "" + ] + } + } + }, + "ChangesListResponse": { + "id": "ChangesListResponse", + "type": "object", + "description": "The response to a request to enumerate Changes to a ResourceRecordSets collection.", + "properties": { + "changes": { + "type": "array", + "description": "The requested changes.", + "items": { + "$ref": "Change" + } + }, + "header": { + "$ref": "ResponseHeader" + }, + "kind": { + "type": "string", + "description": "Type of resource.", + "default": "dns#changesListResponse" + }, + "nextPageToken": { + "type": "string", + "description": "The presence of this field indicates that there exist more results following your last page of results in pagination order. To fetch them, make another list request using this value as your pagination token.\n\nIn this way you can retrieve the complete contents of even very large collections one page at a time. However, if the contents of the collection change between the first and last paginated list request, the set of all elements returned will be an inconsistent view of the collection. There is no way to retrieve a \"snapshot\" of collections larger than the maximum page size." + } + } + }, + "DnsKey": { + "id": "DnsKey", + "type": "object", + "description": "A DNSSEC key pair.", + "properties": { + "algorithm": { + "type": "string", + "description": "String mnemonic specifying the DNSSEC algorithm of this key. Immutable after creation time.", + "enum": [ + "ecdsap256sha256", + "ecdsap384sha384", + "rsasha1", + "rsasha256", + "rsasha512" + ], + "enumDescriptions": [ + "", + "", + "", + "", + "" + ] + }, + "creationTime": { + "type": "string", + "description": "The time that this resource was created in the control plane. This is in RFC3339 text format. Output only." + }, + "description": { + "type": "string", + "description": "A mutable string of at most 1024 characters associated with this resource for the user's convenience. Has no effect on the resource's function." + }, + "digests": { + "type": "array", + "description": "Cryptographic hashes of the DNSKEY resource record associated with this DnsKey. These digests are needed to construct a DS record that points at this DNS key. Output only.", + "items": { + "$ref": "DnsKeyDigest" + } + }, + "id": { + "type": "string", + "description": "Unique identifier for the resource; defined by the server (output only)." + }, + "isActive": { + "type": "boolean", + "description": "Active keys will be used to sign subsequent changes to the ManagedZone. Inactive keys will still be present as DNSKEY Resource Records for the use of resolvers validating existing signatures." + }, + "keyLength": { + "type": "integer", + "description": "Length of the key in bits. Specified at creation time then immutable.", + "format": "uint32" + }, + "keyTag": { + "type": "integer", + "description": "The key tag is a non-cryptographic hash of the a DNSKEY resource record associated with this DnsKey. The key tag can be used to identify a DNSKEY more quickly (but it is not a unique identifier). In particular, the key tag is used in a parent zone's DS record to point at the DNSKEY in this child ManagedZone. The key tag is a number in the range [0, 65535] and the algorithm to calculate it is specified in RFC4034 Appendix B. Output only.", + "format": "int32" + }, + "kind": { + "type": "string", + "description": "Identifies what kind of resource this is. Value: the fixed string \"dns#dnsKey\".", + "default": "dns#dnsKey" + }, + "publicKey": { + "type": "string", + "description": "Base64 encoded public half of this key. Output only." + }, + "type": { + "type": "string", + "description": "One of \"KEY_SIGNING\" or \"ZONE_SIGNING\". Keys of type KEY_SIGNING have the Secure Entry Point flag set and, when active, will be used to sign only resource record sets of type DNSKEY. Otherwise, the Secure Entry Point flag will be cleared and this key will be used to sign only resource record sets of other types. Immutable after creation time.", + "enum": [ + "keySigning", + "zoneSigning" + ], + "enumDescriptions": [ + "", + "" + ] + } + } + }, + "DnsKeyDigest": { + "id": "DnsKeyDigest", + "type": "object", + "properties": { + "digest": { + "type": "string", + "description": "The base-16 encoded bytes of this digest. Suitable for use in a DS resource record." + }, + "type": { + "type": "string", + "description": "Specifies the algorithm used to calculate this digest.", + "enum": [ + "sha1", + "sha256", + "sha384" + ], + "enumDescriptions": [ + "", + "", + "" + ] + } + } + }, + "DnsKeySpec": { + "id": "DnsKeySpec", + "type": "object", + "description": "Parameters for DnsKey key generation. Used for generating initial keys for a new ManagedZone and as default when adding a new DnsKey.", + "properties": { + "algorithm": { + "type": "string", + "description": "String mnemonic specifying the DNSSEC algorithm of this key.", + "enum": [ + "ecdsap256sha256", + "ecdsap384sha384", + "rsasha1", + "rsasha256", + "rsasha512" + ], + "enumDescriptions": [ + "", + "", + "", + "", + "" + ] + }, + "keyLength": { + "type": "integer", + "description": "Length of the keys in bits.", + "format": "uint32" + }, + "keyType": { + "type": "string", + "description": "One of \"KEY_SIGNING\" or \"ZONE_SIGNING\". Keys of type KEY_SIGNING have the Secure Entry Point flag set and, when active, will be used to sign only resource record sets of type DNSKEY. Otherwise, the Secure Entry Point flag will be cleared and this key will be used to sign only resource record sets of other types.", + "enum": [ + "keySigning", + "zoneSigning" + ], + "enumDescriptions": [ + "", + "" + ] + }, + "kind": { + "type": "string", + "description": "Identifies what kind of resource this is. Value: the fixed string \"dns#dnsKeySpec\".", + "default": "dns#dnsKeySpec" + } + } + }, + "DnsKeysListResponse": { + "id": "DnsKeysListResponse", + "type": "object", + "description": "The response to a request to enumerate DnsKeys in a ManagedZone.", + "properties": { + "dnsKeys": { + "type": "array", + "description": "The requested resources.", + "items": { + "$ref": "DnsKey" + } + }, + "header": { + "$ref": "ResponseHeader" + }, + "kind": { + "type": "string", + "description": "Type of resource.", + "default": "dns#dnsKeysListResponse" + }, + "nextPageToken": { + "type": "string", + "description": "The presence of this field indicates that there exist more results following your last page of results in pagination order. To fetch them, make another list request using this value as your pagination token.\n\nIn this way you can retrieve the complete contents of even very large collections one page at a time. However, if the contents of the collection change between the first and last paginated list request, the set of all elements returned will be an inconsistent view of the collection. There is no way to retrieve a \"snapshot\" of collections larger than the maximum page size." + } + } + }, + "ManagedZone": { + "id": "ManagedZone", + "type": "object", + "description": "A zone is a subtree of the DNS namespace under one administrative responsibility. A ManagedZone is a resource that represents a DNS zone hosted by the Cloud DNS service.", + "properties": { + "creationTime": { + "type": "string", + "description": "The time that this resource was created on the server. This is in RFC3339 text format. Output only." + }, + "description": { + "type": "string", + "description": "A mutable string of at most 1024 characters associated with this resource for the user's convenience. Has no effect on the managed zone's function." + }, + "dnsName": { + "type": "string", + "description": "The DNS name of this managed zone, for instance \"example.com.\"." + }, + "dnssecConfig": { + "$ref": "ManagedZoneDnsSecConfig", + "description": "DNSSEC configuration." + }, + "id": { + "type": "string", + "description": "Unique identifier for the resource; defined by the server (output only)", + "format": "uint64" + }, + "kind": { + "type": "string", + "description": "Identifies what kind of resource this is. Value: the fixed string \"dns#managedZone\".", + "default": "dns#managedZone" + }, + "labels": { + "type": "object", + "description": "User labels.", + "additionalProperties": { + "type": "string" + } + }, + "name": { + "type": "string", + "description": "User assigned name for this resource. Must be unique within the project. The name must be 1-63 characters long, must begin with a letter, end with a letter or digit, and only contain lowercase letters, digits or dashes." + }, + "nameServerSet": { + "type": "string", + "description": "Optionally specifies the NameServerSet for this ManagedZone. A NameServerSet is a set of DNS name servers that all host the same ManagedZones. Most users will leave this field unset." + }, + "nameServers": { + "type": "array", + "description": "Delegate your managed_zone to these virtual name servers; defined by the server (output only)", + "items": { + "type": "string" + } + } + } + }, + "ManagedZoneDnsSecConfig": { + "id": "ManagedZoneDnsSecConfig", + "type": "object", + "properties": { + "defaultKeySpecs": { + "type": "array", + "description": "Specifies parameters that will be used for generating initial DnsKeys for this ManagedZone. Output only while state is not OFF.", + "items": { + "$ref": "DnsKeySpec" + } + }, + "kind": { + "type": "string", + "description": "Identifies what kind of resource this is. Value: the fixed string \"dns#managedZoneDnsSecConfig\".", + "default": "dns#managedZoneDnsSecConfig" + }, + "nonExistence": { + "type": "string", + "description": "Specifies the mechanism used to provide authenticated denial-of-existence responses. Output only while state is not OFF.", + "enum": [ + "nsec", + "nsec3" + ], + "enumDescriptions": [ + "", + "" + ] + }, + "state": { + "type": "string", + "description": "Specifies whether DNSSEC is enabled, and what mode it is in.", + "enum": [ + "off", + "on", + "transfer" + ], + "enumDescriptions": [ + "", + "", + "" + ] + } + } + }, + "ManagedZoneOperationsListResponse": { + "id": "ManagedZoneOperationsListResponse", + "type": "object", + "properties": { + "header": { + "$ref": "ResponseHeader" + }, + "kind": { + "type": "string", + "description": "Type of resource.", + "default": "dns#managedZoneOperationsListResponse" + }, + "nextPageToken": { + "type": "string", + "description": "The presence of this field indicates that there exist more results following your last page of results in pagination order. To fetch them, make another list request using this value as your page token.\n\nIn this way you can retrieve the complete contents of even very large collections one page at a time. However, if the contents of the collection change between the first and last paginated list request, the set of all elements returned will be an inconsistent view of the collection. There is no way to retrieve a consistent snapshot of a collection larger than the maximum page size." + }, + "operations": { + "type": "array", + "description": "The operation resources.", + "items": { + "$ref": "Operation" + } + } + } + }, + "ManagedZonesListResponse": { + "id": "ManagedZonesListResponse", + "type": "object", + "properties": { + "header": { + "$ref": "ResponseHeader" + }, + "kind": { + "type": "string", + "description": "Type of resource.", + "default": "dns#managedZonesListResponse" + }, + "managedZones": { + "type": "array", + "description": "The managed zone resources.", + "items": { + "$ref": "ManagedZone" + } + }, + "nextPageToken": { + "type": "string", + "description": "The presence of this field indicates that there exist more results following your last page of results in pagination order. To fetch them, make another list request using this value as your page token.\n\nIn this way you can retrieve the complete contents of even very large collections one page at a time. However, if the contents of the collection change between the first and last paginated list request, the set of all elements returned will be an inconsistent view of the collection. There is no way to retrieve a consistent snapshot of a collection larger than the maximum page size." + } + } + }, + "Operation": { + "id": "Operation", + "type": "object", + "description": "An operation represents a successful mutation performed on a Cloud DNS resource. Operations provide: - An audit log of server resource mutations. - A way to recover/retry API calls in the case where the response is never received by the caller. Use the caller specified client_operation_id.", + "properties": { + "dnsKeyContext": { + "$ref": "OperationDnsKeyContext", + "description": "Only populated if the operation targeted a DnsKey (output only)." + }, + "id": { + "type": "string", + "description": "Unique identifier for the resource. This is the client_operation_id if the client specified it when the mutation was initiated, otherwise, it is generated by the server. The name must be 1-63 characters long and match the regular expression [-a-z0-9]? (output only)" + }, + "kind": { + "type": "string", + "description": "Identifies what kind of resource this is. Value: the fixed string \"dns#operation\".", + "default": "dns#operation" + }, + "startTime": { + "type": "string", + "description": "The time that this operation was started by the server. This is in RFC3339 text format (output only)." + }, + "status": { + "type": "string", + "description": "Status of the operation. Can be one of the following: \"PENDING\" or \"DONE\" (output only).", + "enum": [ + "done", + "pending" + ], + "enumDescriptions": [ + "", + "" + ] + }, + "type": { + "type": "string", + "description": "Type of the operation. Operations include insert, update, and delete (output only)." + }, + "user": { + "type": "string", + "description": "User who requested the operation, for example: user@example.com. cloud-dns-system for operations automatically done by the system. (output only)" + }, + "zoneContext": { + "$ref": "OperationManagedZoneContext", + "description": "Only populated if the operation targeted a ManagedZone (output only)." + } + } + }, + "OperationDnsKeyContext": { + "id": "OperationDnsKeyContext", + "type": "object", + "properties": { + "newValue": { + "$ref": "DnsKey", + "description": "The post-operation DnsKey resource." + }, + "oldValue": { + "$ref": "DnsKey", + "description": "The pre-operation DnsKey resource." + } + } + }, + "OperationManagedZoneContext": { + "id": "OperationManagedZoneContext", + "type": "object", + "properties": { + "newValue": { + "$ref": "ManagedZone", + "description": "The post-operation ManagedZone resource." + }, + "oldValue": { + "$ref": "ManagedZone", + "description": "The pre-operation ManagedZone resource." + } + } + }, + "Project": { + "id": "Project", + "type": "object", + "description": "A project resource. The project is a top level container for resources including Cloud DNS ManagedZones. Projects can be created only in the APIs console.", + "properties": { + "id": { + "type": "string", + "description": "User assigned unique identifier for the resource (output only)." + }, + "kind": { + "type": "string", + "description": "Identifies what kind of resource this is. Value: the fixed string \"dns#project\".", + "default": "dns#project" + }, + "number": { + "type": "string", + "description": "Unique numeric identifier for the resource; defined by the server (output only).", + "format": "uint64" + }, + "quota": { + "$ref": "Quota", + "description": "Quotas assigned to this project (output only)." + } + } + }, + "Quota": { + "id": "Quota", + "type": "object", + "description": "Limits associated with a Project.", + "properties": { + "dnsKeysPerManagedZone": { + "type": "integer", + "description": "Maximum allowed number of DnsKeys per ManagedZone.", + "format": "int32" + }, + "kind": { + "type": "string", + "description": "Identifies what kind of resource this is. Value: the fixed string \"dns#quota\".", + "default": "dns#quota" + }, + "managedZones": { + "type": "integer", + "description": "Maximum allowed number of managed zones in the project.", + "format": "int32" + }, + "resourceRecordsPerRrset": { + "type": "integer", + "description": "Maximum allowed number of ResourceRecords per ResourceRecordSet.", + "format": "int32" + }, + "rrsetAdditionsPerChange": { + "type": "integer", + "description": "Maximum allowed number of ResourceRecordSets to add per ChangesCreateRequest.", + "format": "int32" + }, + "rrsetDeletionsPerChange": { + "type": "integer", + "description": "Maximum allowed number of ResourceRecordSets to delete per ChangesCreateRequest.", + "format": "int32" + }, + "rrsetsPerManagedZone": { + "type": "integer", + "description": "Maximum allowed number of ResourceRecordSets per zone in the project.", + "format": "int32" + }, + "totalRrdataSizePerChange": { + "type": "integer", + "description": "Maximum allowed size for total rrdata in one ChangesCreateRequest in bytes.", + "format": "int32" + }, + "whitelistedKeySpecs": { + "type": "array", + "description": "DNSSEC algorithm and key length types that can be used for DnsKeys.", + "items": { + "$ref": "DnsKeySpec" + } + } + } + }, + "ResourceRecordSet": { + "id": "ResourceRecordSet", + "type": "object", + "description": "A unit of data that will be returned by the DNS servers.", + "properties": { + "kind": { + "type": "string", + "description": "Identifies what kind of resource this is. Value: the fixed string \"dns#resourceRecordSet\".", + "default": "dns#resourceRecordSet" + }, + "name": { + "type": "string", + "description": "For example, www.example.com." + }, + "rrdatas": { + "type": "array", + "description": "As defined in RFC 1035 (section 5) and RFC 1034 (section 3.6.1).", + "items": { + "type": "string" + } + }, + "signatureRrdatas": { + "type": "array", + "description": "As defined in RFC 4034 (section 3.2).", + "items": { + "type": "string" + } + }, + "ttl": { + "type": "integer", + "description": "Number of seconds that this ResourceRecordSet can be cached by resolvers.", + "format": "int32" + }, + "type": { + "type": "string", + "description": "The identifier of a supported record type, for example, A, AAAA, MX, TXT, and so on." + } + } + }, + "ResourceRecordSetsListResponse": { + "id": "ResourceRecordSetsListResponse", + "type": "object", + "properties": { + "header": { + "$ref": "ResponseHeader" + }, + "kind": { + "type": "string", + "description": "Type of resource.", + "default": "dns#resourceRecordSetsListResponse" + }, + "nextPageToken": { + "type": "string", + "description": "The presence of this field indicates that there exist more results following your last page of results in pagination order. To fetch them, make another list request using this value as your pagination token.\n\nIn this way you can retrieve the complete contents of even very large collections one page at a time. However, if the contents of the collection change between the first and last paginated list request, the set of all elements returned will be an inconsistent view of the collection. There is no way to retrieve a consistent snapshot of a collection larger than the maximum page size." + }, + "rrsets": { + "type": "array", + "description": "The resource record set resources.", + "items": { + "$ref": "ResourceRecordSet" + } + } + } + }, + "ResponseHeader": { + "id": "ResponseHeader", + "type": "object", + "description": "Elements common to every response.", + "properties": { + "operationId": { + "type": "string", + "description": "For mutating operation requests that completed successfully. This is the client_operation_id if the client specified it, otherwise it is generated by the server (output only)." + } + } + } + }, + "resources": { + "changes": { + "methods": { + "create": { + "id": "dns.changes.create", + "path": "{project}/managedZones/{managedZone}/changes", + "httpMethod": "POST", + "description": "Atomically update the ResourceRecordSet collection.", + "parameters": { + "clientOperationId": { + "type": "string", + "description": "For mutating operation requests only. An optional identifier specified by the client. Must be unique for operation resources in the Operations collection.", + "location": "query" + }, + "managedZone": { + "type": "string", + "description": "Identifies the managed zone addressed by this request. Can be the managed zone name or id.", + "required": true, + "location": "path" + }, + "project": { + "type": "string", + "description": "Identifies the project addressed by this request.", + "required": true, + "location": "path" + } + }, + "parameterOrder": [ + "project", + "managedZone" + ], + "request": { + "$ref": "Change" + }, + "response": { + "$ref": "Change" + }, + "scopes": [ + "https://www.googleapis.com/auth/cloud-platform", + "https://www.googleapis.com/auth/ndev.clouddns.readwrite" + ] + }, + "get": { + "id": "dns.changes.get", + "path": "{project}/managedZones/{managedZone}/changes/{changeId}", + "httpMethod": "GET", + "description": "Fetch the representation of an existing Change.", + "parameters": { + "changeId": { + "type": "string", + "description": "The identifier of the requested change, from a previous ResourceRecordSetsChangeResponse.", + "required": true, + "location": "path" + }, + "clientOperationId": { + "type": "string", + "description": "For mutating operation requests only. An optional identifier specified by the client. Must be unique for operation resources in the Operations collection.", + "location": "query" + }, + "managedZone": { + "type": "string", + "description": "Identifies the managed zone addressed by this request. Can be the managed zone name or id.", + "required": true, + "location": "path" + }, + "project": { + "type": "string", + "description": "Identifies the project addressed by this request.", + "required": true, + "location": "path" + } + }, + "parameterOrder": [ + "project", + "managedZone", + "changeId" + ], + "response": { + "$ref": "Change" + }, + "scopes": [ + "https://www.googleapis.com/auth/cloud-platform", + "https://www.googleapis.com/auth/cloud-platform.read-only", + "https://www.googleapis.com/auth/ndev.clouddns.readonly", + "https://www.googleapis.com/auth/ndev.clouddns.readwrite" + ] + }, + "list": { + "id": "dns.changes.list", + "path": "{project}/managedZones/{managedZone}/changes", + "httpMethod": "GET", + "description": "Enumerate Changes to a ResourceRecordSet collection.", + "parameters": { + "managedZone": { + "type": "string", + "description": "Identifies the managed zone addressed by this request. Can be the managed zone name or id.", + "required": true, + "location": "path" + }, + "maxResults": { + "type": "integer", + "description": "Optional. Maximum number of results to be returned. If unspecified, the server will decide how many results to return.", + "format": "int32", + "location": "query" + }, + "pageToken": { + "type": "string", + "description": "Optional. A tag returned by a previous list request that was truncated. Use this parameter to continue a previous list request.", + "location": "query" + }, + "project": { + "type": "string", + "description": "Identifies the project addressed by this request.", + "required": true, + "location": "path" + }, + "sortBy": { + "type": "string", + "description": "Sorting criterion. The only supported value is change sequence.", + "default": "changeSequence", + "enum": [ + "changeSequence" + ], + "enumDescriptions": [ + "" + ], + "location": "query" + }, + "sortOrder": { + "type": "string", + "description": "Sorting order direction: 'ascending' or 'descending'.", + "location": "query" + } + }, + "parameterOrder": [ + "project", + "managedZone" + ], + "response": { + "$ref": "ChangesListResponse" + }, + "scopes": [ + "https://www.googleapis.com/auth/cloud-platform", + "https://www.googleapis.com/auth/cloud-platform.read-only", + "https://www.googleapis.com/auth/ndev.clouddns.readonly", + "https://www.googleapis.com/auth/ndev.clouddns.readwrite" + ] + } + } + }, + "dnsKeys": { + "methods": { + "get": { + "id": "dns.dnsKeys.get", + "path": "{project}/managedZones/{managedZone}/dnsKeys/{dnsKeyId}", + "httpMethod": "GET", + "description": "Fetch the representation of an existing DnsKey.", + "parameters": { + "clientOperationId": { + "type": "string", + "description": "For mutating operation requests only. An optional identifier specified by the client. Must be unique for operation resources in the Operations collection.", + "location": "query" + }, + "digestType": { + "type": "string", + "description": "An optional comma-separated list of digest types to compute and display for key signing keys. If omitted, the recommended digest type will be computed and displayed.", + "location": "query" + }, + "dnsKeyId": { + "type": "string", + "description": "The identifier of the requested DnsKey.", + "required": true, + "location": "path" + }, + "managedZone": { + "type": "string", + "description": "Identifies the managed zone addressed by this request. Can be the managed zone name or id.", + "required": true, + "location": "path" + }, + "project": { + "type": "string", + "description": "Identifies the project addressed by this request.", + "required": true, + "location": "path" + } + }, + "parameterOrder": [ + "project", + "managedZone", + "dnsKeyId" + ], + "response": { + "$ref": "DnsKey" + }, + "scopes": [ + "https://www.googleapis.com/auth/cloud-platform", + "https://www.googleapis.com/auth/cloud-platform.read-only", + "https://www.googleapis.com/auth/ndev.clouddns.readonly", + "https://www.googleapis.com/auth/ndev.clouddns.readwrite" + ] + }, + "list": { + "id": "dns.dnsKeys.list", + "path": "{project}/managedZones/{managedZone}/dnsKeys", + "httpMethod": "GET", + "description": "Enumerate DnsKeys to a ResourceRecordSet collection.", + "parameters": { + "digestType": { + "type": "string", + "description": "An optional comma-separated list of digest types to compute and display for key signing keys. If omitted, the recommended digest type will be computed and displayed.", + "location": "query" + }, + "managedZone": { + "type": "string", + "description": "Identifies the managed zone addressed by this request. Can be the managed zone name or id.", + "required": true, + "location": "path" + }, + "maxResults": { + "type": "integer", + "description": "Optional. Maximum number of results to be returned. If unspecified, the server will decide how many results to return.", + "format": "int32", + "location": "query" + }, + "pageToken": { + "type": "string", + "description": "Optional. A tag returned by a previous list request that was truncated. Use this parameter to continue a previous list request.", + "location": "query" + }, + "project": { + "type": "string", + "description": "Identifies the project addressed by this request.", + "required": true, + "location": "path" + } + }, + "parameterOrder": [ + "project", + "managedZone" + ], + "response": { + "$ref": "DnsKeysListResponse" + }, + "scopes": [ + "https://www.googleapis.com/auth/cloud-platform", + "https://www.googleapis.com/auth/cloud-platform.read-only", + "https://www.googleapis.com/auth/ndev.clouddns.readonly", + "https://www.googleapis.com/auth/ndev.clouddns.readwrite" + ] + } + } + }, + "managedZoneOperations": { + "methods": { + "get": { + "id": "dns.managedZoneOperations.get", + "path": "{project}/managedZones/{managedZone}/operations/{operation}", + "httpMethod": "GET", + "description": "Fetch the representation of an existing Operation.", + "parameters": { + "clientOperationId": { + "type": "string", + "description": "For mutating operation requests only. An optional identifier specified by the client. Must be unique for operation resources in the Operations collection.", + "location": "query" + }, + "managedZone": { + "type": "string", + "description": "Identifies the managed zone addressed by this request.", + "required": true, + "location": "path" + }, + "operation": { + "type": "string", + "description": "Identifies the operation addressed by this request.", + "required": true, + "location": "path" + }, + "project": { + "type": "string", + "description": "Identifies the project addressed by this request.", + "required": true, + "location": "path" + } + }, + "parameterOrder": [ + "project", + "managedZone", + "operation" + ], + "response": { + "$ref": "Operation" + }, + "scopes": [ + "https://www.googleapis.com/auth/cloud-platform", + "https://www.googleapis.com/auth/cloud-platform.read-only", + "https://www.googleapis.com/auth/ndev.clouddns.readonly", + "https://www.googleapis.com/auth/ndev.clouddns.readwrite" + ] + }, + "list": { + "id": "dns.managedZoneOperations.list", + "path": "{project}/managedZones/{managedZone}/operations", + "httpMethod": "GET", + "description": "Enumerate Operations for the given ManagedZone.", + "parameters": { + "managedZone": { + "type": "string", + "description": "Identifies the managed zone addressed by this request.", + "required": true, + "location": "path" + }, + "maxResults": { + "type": "integer", + "description": "Optional. Maximum number of results to be returned. If unspecified, the server will decide how many results to return.", + "format": "int32", + "location": "query" + }, + "pageToken": { + "type": "string", + "description": "Optional. A tag returned by a previous list request that was truncated. Use this parameter to continue a previous list request.", + "location": "query" + }, + "project": { + "type": "string", + "description": "Identifies the project addressed by this request.", + "required": true, + "location": "path" + }, + "sortBy": { + "type": "string", + "description": "Sorting criterion. The only supported values are START_TIME and ID.", + "default": "startTime", + "enum": [ + "id", + "startTime" + ], + "enumDescriptions": [ + "", + "" + ], + "location": "query" + } + }, + "parameterOrder": [ + "project", + "managedZone" + ], + "response": { + "$ref": "ManagedZoneOperationsListResponse" + }, + "scopes": [ + "https://www.googleapis.com/auth/cloud-platform", + "https://www.googleapis.com/auth/cloud-platform.read-only", + "https://www.googleapis.com/auth/ndev.clouddns.readonly", + "https://www.googleapis.com/auth/ndev.clouddns.readwrite" + ] + } + } + }, + "managedZones": { + "methods": { + "create": { + "id": "dns.managedZones.create", + "path": "{project}/managedZones", + "httpMethod": "POST", + "description": "Create a new ManagedZone.", + "parameters": { + "clientOperationId": { + "type": "string", + "description": "For mutating operation requests only. An optional identifier specified by the client. Must be unique for operation resources in the Operations collection.", + "location": "query" + }, + "project": { + "type": "string", + "description": "Identifies the project addressed by this request.", + "required": true, + "location": "path" + } + }, + "parameterOrder": [ + "project" + ], + "request": { + "$ref": "ManagedZone" + }, + "response": { + "$ref": "ManagedZone" + }, + "scopes": [ + "https://www.googleapis.com/auth/cloud-platform", + "https://www.googleapis.com/auth/ndev.clouddns.readwrite" + ] + }, + "delete": { + "id": "dns.managedZones.delete", + "path": "{project}/managedZones/{managedZone}", + "httpMethod": "DELETE", + "description": "Delete a previously created ManagedZone.", + "parameters": { + "clientOperationId": { + "type": "string", + "description": "For mutating operation requests only. An optional identifier specified by the client. Must be unique for operation resources in the Operations collection.", + "location": "query" + }, + "managedZone": { + "type": "string", + "description": "Identifies the managed zone addressed by this request. Can be the managed zone name or id.", + "required": true, + "location": "path" + }, + "project": { + "type": "string", + "description": "Identifies the project addressed by this request.", + "required": true, + "location": "path" + } + }, + "parameterOrder": [ + "project", + "managedZone" + ], + "scopes": [ + "https://www.googleapis.com/auth/cloud-platform", + "https://www.googleapis.com/auth/ndev.clouddns.readwrite" + ] + }, + "get": { + "id": "dns.managedZones.get", + "path": "{project}/managedZones/{managedZone}", + "httpMethod": "GET", + "description": "Fetch the representation of an existing ManagedZone.", + "parameters": { + "clientOperationId": { + "type": "string", + "description": "For mutating operation requests only. An optional identifier specified by the client. Must be unique for operation resources in the Operations collection.", + "location": "query" + }, + "managedZone": { + "type": "string", + "description": "Identifies the managed zone addressed by this request. Can be the managed zone name or id.", + "required": true, + "location": "path" + }, + "project": { + "type": "string", + "description": "Identifies the project addressed by this request.", + "required": true, + "location": "path" + } + }, + "parameterOrder": [ + "project", + "managedZone" + ], + "response": { + "$ref": "ManagedZone" + }, + "scopes": [ + "https://www.googleapis.com/auth/cloud-platform", + "https://www.googleapis.com/auth/cloud-platform.read-only", + "https://www.googleapis.com/auth/ndev.clouddns.readonly", + "https://www.googleapis.com/auth/ndev.clouddns.readwrite" + ] + }, + "list": { + "id": "dns.managedZones.list", + "path": "{project}/managedZones", + "httpMethod": "GET", + "description": "Enumerate ManagedZones that have been created but not yet deleted.", + "parameters": { + "dnsName": { + "type": "string", + "description": "Restricts the list to return only zones with this domain name.", + "location": "query" + }, + "maxResults": { + "type": "integer", + "description": "Optional. Maximum number of results to be returned. If unspecified, the server will decide how many results to return.", + "format": "int32", + "location": "query" + }, + "pageToken": { + "type": "string", + "description": "Optional. A tag returned by a previous list request that was truncated. Use this parameter to continue a previous list request.", + "location": "query" + }, + "project": { + "type": "string", + "description": "Identifies the project addressed by this request.", + "required": true, + "location": "path" + } + }, + "parameterOrder": [ + "project" + ], + "response": { + "$ref": "ManagedZonesListResponse" + }, + "scopes": [ + "https://www.googleapis.com/auth/cloud-platform", + "https://www.googleapis.com/auth/cloud-platform.read-only", + "https://www.googleapis.com/auth/ndev.clouddns.readonly", + "https://www.googleapis.com/auth/ndev.clouddns.readwrite" + ] + }, + "patch": { + "id": "dns.managedZones.patch", + "path": "{project}/managedZones/{managedZone}", + "httpMethod": "PATCH", + "description": "Update an existing ManagedZone. This method supports patch semantics.", + "parameters": { + "clientOperationId": { + "type": "string", + "description": "For mutating operation requests only. An optional identifier specified by the client. Must be unique for operation resources in the Operations collection.", + "location": "query" + }, + "managedZone": { + "type": "string", + "description": "Identifies the managed zone addressed by this request. Can be the managed zone name or id.", + "required": true, + "location": "path" + }, + "project": { + "type": "string", + "description": "Identifies the project addressed by this request.", + "required": true, + "location": "path" + } + }, + "parameterOrder": [ + "project", + "managedZone" + ], + "request": { + "$ref": "ManagedZone" + }, + "response": { + "$ref": "Operation" + }, + "scopes": [ + "https://www.googleapis.com/auth/cloud-platform", + "https://www.googleapis.com/auth/ndev.clouddns.readwrite" + ] + }, + "update": { + "id": "dns.managedZones.update", + "path": "{project}/managedZones/{managedZone}", + "httpMethod": "PUT", + "description": "Update an existing ManagedZone.", + "parameters": { + "clientOperationId": { + "type": "string", + "description": "For mutating operation requests only. An optional identifier specified by the client. Must be unique for operation resources in the Operations collection.", + "location": "query" + }, + "managedZone": { + "type": "string", + "description": "Identifies the managed zone addressed by this request. Can be the managed zone name or id.", + "required": true, + "location": "path" + }, + "project": { + "type": "string", + "description": "Identifies the project addressed by this request.", + "required": true, + "location": "path" + } + }, + "parameterOrder": [ + "project", + "managedZone" + ], + "request": { + "$ref": "ManagedZone" + }, + "response": { + "$ref": "Operation" + }, + "scopes": [ + "https://www.googleapis.com/auth/cloud-platform", + "https://www.googleapis.com/auth/ndev.clouddns.readwrite" + ] + } + } + }, + "projects": { + "methods": { + "get": { + "id": "dns.projects.get", + "path": "{project}", + "httpMethod": "GET", + "description": "Fetch the representation of an existing Project.", + "parameters": { + "clientOperationId": { + "type": "string", + "description": "For mutating operation requests only. An optional identifier specified by the client. Must be unique for operation resources in the Operations collection.", + "location": "query" + }, + "project": { + "type": "string", + "description": "Identifies the project addressed by this request.", + "required": true, + "location": "path" + } + }, + "parameterOrder": [ + "project" + ], + "response": { + "$ref": "Project" + }, + "scopes": [ + "https://www.googleapis.com/auth/cloud-platform", + "https://www.googleapis.com/auth/cloud-platform.read-only", + "https://www.googleapis.com/auth/ndev.clouddns.readonly", + "https://www.googleapis.com/auth/ndev.clouddns.readwrite" + ] + } + } + }, + "resourceRecordSets": { + "methods": { + "list": { + "id": "dns.resourceRecordSets.list", + "path": "{project}/managedZones/{managedZone}/rrsets", + "httpMethod": "GET", + "description": "Enumerate ResourceRecordSets that have been created but not yet deleted.", + "parameters": { + "managedZone": { + "type": "string", + "description": "Identifies the managed zone addressed by this request. Can be the managed zone name or id.", + "required": true, + "location": "path" + }, + "maxResults": { + "type": "integer", + "description": "Optional. Maximum number of results to be returned. If unspecified, the server will decide how many results to return.", + "format": "int32", + "location": "query" + }, + "name": { + "type": "string", + "description": "Restricts the list to return only records with this fully qualified domain name.", + "location": "query" + }, + "pageToken": { + "type": "string", + "description": "Optional. A tag returned by a previous list request that was truncated. Use this parameter to continue a previous list request.", + "location": "query" + }, + "project": { + "type": "string", + "description": "Identifies the project addressed by this request.", + "required": true, + "location": "path" + }, + "type": { + "type": "string", + "description": "Restricts the list to return only records of this type. If present, the \"name\" parameter must also be present.", + "location": "query" + } + }, + "parameterOrder": [ + "project", + "managedZone" + ], + "response": { + "$ref": "ResourceRecordSetsListResponse" + }, + "scopes": [ + "https://www.googleapis.com/auth/cloud-platform", + "https://www.googleapis.com/auth/cloud-platform.read-only", + "https://www.googleapis.com/auth/ndev.clouddns.readonly", + "https://www.googleapis.com/auth/ndev.clouddns.readwrite" + ] + } + } + } + } +} From 804fd4b78a2bcf7d962fce6953103daa62ed420c Mon Sep 17 00:00:00 2001 From: ohemorange Date: Mon, 26 Mar 2018 16:28:30 -0700 Subject: [PATCH 31/56] factor out location_directive_for_achall (#5794) --- certbot-nginx/certbot_nginx/http_01.py | 27 +++++++++++++------------- 1 file changed, 14 insertions(+), 13 deletions(-) diff --git a/certbot-nginx/certbot_nginx/http_01.py b/certbot-nginx/certbot_nginx/http_01.py index 93c7bfc90..d08a3b1cb 100644 --- a/certbot-nginx/certbot_nginx/http_01.py +++ b/certbot-nginx/certbot_nginx/http_01.py @@ -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) From af2cce4ca8c55c37a18197b565b67bdbf5c83879 Mon Sep 17 00:00:00 2001 From: sydneyli Date: Mon, 26 Mar 2018 17:09:02 -0700 Subject: [PATCH 32/56] fix(auth_handler): cleanup is always called (#5779) * fix(auth_handler): cleanup is always called * test(auth_handler): tests for various error cases --- certbot/auth_handler.py | 42 ++++++++++++++--------------- certbot/error_handler.py | 19 +++++++++++-- certbot/tests/auth_handler_test.py | 26 ++++++++++++++++++ certbot/tests/error_handler_test.py | 30 ++++++++++++++++++++- 4 files changed, 92 insertions(+), 25 deletions(-) diff --git a/certbot/auth_handler.py b/certbot/auth_handler.py index 68389d1f8..9d7c75f57 100644 --- a/certbot/auth_handler.py +++ b/certbot/auth_handler.py @@ -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: diff --git a/certbot/error_handler.py b/certbot/error_handler.py index 842243f70..e2737711e 100644 --- a/certbot/error_handler.py +++ b/certbot/error_handler.py @@ -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 + diff --git a/certbot/tests/auth_handler_test.py b/certbot/tests/auth_handler_test.py index a4ac9eb73..9a8a13498 100644 --- a/certbot/tests/auth_handler_test.py +++ b/certbot/tests/auth_handler_test.py @@ -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 diff --git a/certbot/tests/error_handler_test.py b/certbot/tests/error_handler_test.py index 60dcf5e99..d4c48c242 100644 --- a/certbot/tests/error_handler_test.py +++ b/certbot/tests/error_handler_test.py @@ -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 From 4d082e22e61be36cebc4b22e8c7f507347b72e67 Mon Sep 17 00:00:00 2001 From: ohemorange Date: Tue, 27 Mar 2018 15:11:39 -0700 Subject: [PATCH 33/56] Remove ipv6only=on from duplicated vhosts (#5793) * rename delete_default to remove_singleton_listen_params * update docstring * add documentation to obj.py * add test for remove duplicate ipv6only * Remove ipv6only=on from duplicated vhosts * add test to make sure ipv6only=on is not erroneously removed --- certbot-nginx/certbot_nginx/configurator.py | 3 ++- certbot-nginx/certbot_nginx/obj.py | 2 ++ certbot-nginx/certbot_nginx/parser.py | 12 ++++++---- .../certbot_nginx/tests/parser_test.py | 24 ++++++++++++++++++- 4 files changed, 35 insertions(+), 6 deletions(-) diff --git a/certbot-nginx/certbot_nginx/configurator.py b/certbot-nginx/certbot_nginx/configurator.py index 1073bff4f..13fe493fc 100644 --- a/certbot-nginx/certbot_nginx/configurator.py +++ b/certbot-nginx/certbot_nginx/configurator.py @@ -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) diff --git a/certbot-nginx/certbot_nginx/obj.py b/certbot-nginx/certbot_nginx/obj.py index eb94c138e..8868fcfad 100644 --- a/certbot-nginx/certbot_nginx/obj.py +++ b/certbot-nginx/certbot_nginx/obj.py @@ -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') diff --git a/certbot-nginx/certbot_nginx/parser.py b/certbot-nginx/certbot_nginx/parser.py index 85a239c85..23eacf70a 100644 --- a/certbot-nginx/certbot_nginx/parser.py +++ b/certbot-nginx/certbot_nginx/parser.py @@ -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 diff --git a/certbot-nginx/certbot_nginx/tests/parser_test.py b/certbot-nginx/certbot_nginx/tests/parser_test.py index 9db251d59..1e9703185 100644 --- a/certbot-nginx/certbot_nginx/tests/parser_test.py +++ b/certbot-nginx/certbot_nginx/tests/parser_test.py @@ -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 From 669312d248bc11bc981472f1e4d0e0dfe0c9e4a7 Mon Sep 17 00:00:00 2001 From: ohemorange Date: Tue, 27 Mar 2018 15:25:34 -0700 Subject: [PATCH 34/56] We don't try to add location blocks through a mechanism that checks REPEATABLE_DIRECTIVES, and it wouldn't work as an accurate check even if we did, so just remove it (#5787) --- certbot-nginx/certbot_nginx/parser.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/certbot-nginx/certbot_nginx/parser.py b/certbot-nginx/certbot_nginx/parser.py index 23eacf70a..577e783fc 100644 --- a/certbot-nginx/certbot_nginx/parser.py +++ b/certbot-nginx/certbot_nginx/parser.py @@ -565,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] From a779e06d472b71de17ca0c63de6c6913a95fa55a Mon Sep 17 00:00:00 2001 From: ohemorange Date: Tue, 27 Mar 2018 17:33:48 -0700 Subject: [PATCH 35/56] Add integration tests for nginx plugin (#5441) * Add a rewrite directive for the .well-known location so we don't hit existing rewrites * add comment * Add (nonexistent) document root so we don't use the default value * Add integration tests for nginx plugin * add a sleep 5 to test on travis * put sleep 5 in the right spot * test return status of grep respecting -e and note that we're actually not posix compliant * redelete newline --- .../tests/boulder-integration.conf.sh | 35 +++++++++++++++++-- certbot-nginx/tests/boulder-integration.sh | 34 ++++++++++++++---- 2 files changed, 61 insertions(+), 8 deletions(-) diff --git a/certbot-nginx/tests/boulder-integration.conf.sh b/certbot-nginx/tests/boulder-integration.conf.sh index c38180698..4374f9094 100755 --- a/certbot-nginx/tests/boulder-integration.conf.sh +++ b/certbot-nginx/tests/boulder-integration.conf.sh @@ -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 diff --git a/certbot-nginx/tests/boulder-integration.sh b/certbot-nginx/tests/boulder-integration.sh index f236fb103..d6bd767ce 100755 --- a/certbot-nginx/tests/boulder-integration.sh +++ b/certbot-nginx/tests/boulder-integration.sh @@ -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 From 336950c0b906c4830a15536ad2e8216efa0d08d1 Mon Sep 17 00:00:00 2001 From: Brad Warren Date: Wed, 28 Mar 2018 08:37:00 -0700 Subject: [PATCH 36/56] Update oldest tests to test against 0.22.0 versions (#5800) --- certbot-nginx/local-oldest-requirements.txt | 4 ++-- local-oldest-requirements.txt | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/certbot-nginx/local-oldest-requirements.txt b/certbot-nginx/local-oldest-requirements.txt index 65f5a758e..4b88f0288 100644 --- a/certbot-nginx/local-oldest-requirements.txt +++ b/certbot-nginx/local-oldest-requirements.txt @@ -1,2 +1,2 @@ --e acme[dev] --e .[dev] +acme[dev]==0.22.0 +certbot[dev]==0.22.0 diff --git a/local-oldest-requirements.txt b/local-oldest-requirements.txt index 2346300a3..37bef2083 100644 --- a/local-oldest-requirements.txt +++ b/local-oldest-requirements.txt @@ -1 +1 @@ --e acme[dev] +acme[dev]==0.22.0 From 7630550ac47f2191069bd7482d0c0473be8b4551 Mon Sep 17 00:00:00 2001 From: Brad Warren Date: Thu, 29 Mar 2018 14:15:59 -0700 Subject: [PATCH 37/56] Revert "Update oldest tests to test against 0.22.0 versions (#5800)" (#5809) This reverts commit 336950c0b906c4830a15536ad2e8216efa0d08d1. --- certbot-nginx/local-oldest-requirements.txt | 4 ++-- local-oldest-requirements.txt | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/certbot-nginx/local-oldest-requirements.txt b/certbot-nginx/local-oldest-requirements.txt index 4b88f0288..65f5a758e 100644 --- a/certbot-nginx/local-oldest-requirements.txt +++ b/certbot-nginx/local-oldest-requirements.txt @@ -1,2 +1,2 @@ -acme[dev]==0.22.0 -certbot[dev]==0.22.0 +-e acme[dev] +-e .[dev] diff --git a/local-oldest-requirements.txt b/local-oldest-requirements.txt index 37bef2083..2346300a3 100644 --- a/local-oldest-requirements.txt +++ b/local-oldest-requirements.txt @@ -1 +1 @@ -acme[dev]==0.22.0 +-e acme[dev] From 5ff7f2211edde280a50087d3a593557d6b90405d Mon Sep 17 00:00:00 2001 From: ohemorange Date: Thu, 29 Mar 2018 15:34:38 -0700 Subject: [PATCH 38/56] Explicitly add six as a dependency in letsencrypt-auto-source dockerfiles (#5808) * update documentation * explicitly add six as a dependency in letsencrypt-auto-source dockerfiles * pin six version --- letsencrypt-auto-source/Dockerfile.centos6 | 2 +- letsencrypt-auto-source/Dockerfile.precise | 2 +- letsencrypt-auto-source/Dockerfile.trusty | 2 +- letsencrypt-auto-source/Dockerfile.wheezy | 2 +- letsencrypt-auto-source/tests/__init__.py | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/letsencrypt-auto-source/Dockerfile.centos6 b/letsencrypt-auto-source/Dockerfile.centos6 index 47eb48f50..92fec168b 100644 --- a/letsencrypt-auto-source/Dockerfile.centos6 +++ b/letsencrypt-auto-source/Dockerfile.centos6 @@ -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 diff --git a/letsencrypt-auto-source/Dockerfile.precise b/letsencrypt-auto-source/Dockerfile.precise index 71d572315..39a167c14 100644 --- a/letsencrypt-auto-source/Dockerfile.precise +++ b/letsencrypt-auto-source/Dockerfile.precise @@ -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 \ diff --git a/letsencrypt-auto-source/Dockerfile.trusty b/letsencrypt-auto-source/Dockerfile.trusty index e0aacd118..3de88f9af 100644 --- a/letsencrypt-auto-source/Dockerfile.trusty +++ b/letsencrypt-auto-source/Dockerfile.trusty @@ -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 diff --git a/letsencrypt-auto-source/Dockerfile.wheezy b/letsencrypt-auto-source/Dockerfile.wheezy index 56948d22a..f4f3fea15 100644 --- a/letsencrypt-auto-source/Dockerfile.wheezy +++ b/letsencrypt-auto-source/Dockerfile.wheezy @@ -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 \ diff --git a/letsencrypt-auto-source/tests/__init__.py b/letsencrypt-auto-source/tests/__init__.py index 45db90444..8a1613aa5 100644 --- a/letsencrypt-auto-source/tests/__init__.py +++ b/letsencrypt-auto-source/tests/__init__.py @@ -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. && docker run --rm -t -i lea """ From 8231b1a19cc62262f099dc17609d4d1041e2e5da Mon Sep 17 00:00:00 2001 From: sydneyli Date: Thu, 29 Mar 2018 17:09:21 -0700 Subject: [PATCH 39/56] Pin Lexicon version to 2.2.1 (#5803) --- certbot-dns-cloudxns/setup.py | 2 +- certbot-dns-dnsimple/setup.py | 2 +- certbot-dns-dnsmadeeasy/setup.py | 2 +- certbot-dns-luadns/setup.py | 2 +- certbot-dns-nsone/setup.py | 2 +- tools/dev_constraints.txt | 2 +- 6 files changed, 6 insertions(+), 6 deletions(-) diff --git a/certbot-dns-cloudxns/setup.py b/certbot-dns-cloudxns/setup.py index df7dcc59a..3493638a0 100644 --- a/certbot-dns-cloudxns/setup.py +++ b/certbot-dns-cloudxns/setup.py @@ -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', diff --git a/certbot-dns-dnsimple/setup.py b/certbot-dns-dnsimple/setup.py index a327edf93..f2887d371 100644 --- a/certbot-dns-dnsimple/setup.py +++ b/certbot-dns-dnsimple/setup.py @@ -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', diff --git a/certbot-dns-dnsmadeeasy/setup.py b/certbot-dns-dnsmadeeasy/setup.py index 9ff317ee1..a3ee12cf0 100644 --- a/certbot-dns-dnsmadeeasy/setup.py +++ b/certbot-dns-dnsmadeeasy/setup.py @@ -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', diff --git a/certbot-dns-luadns/setup.py b/certbot-dns-luadns/setup.py index 7b8ffd84f..f872d7093 100644 --- a/certbot-dns-luadns/setup.py +++ b/certbot-dns-luadns/setup.py @@ -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', diff --git a/certbot-dns-nsone/setup.py b/certbot-dns-nsone/setup.py index 53b091065..102ed48c2 100644 --- a/certbot-dns-nsone/setup.py +++ b/certbot-dns-nsone/setup.py @@ -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', diff --git a/tools/dev_constraints.txt b/tools/dev_constraints.txt index 47440241d..d02204215 100644 --- a/tools/dev_constraints.txt +++ b/tools/dev_constraints.txt @@ -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 From 4d706ac77e3eabb3134d0ea75ab76fd58b412bb6 Mon Sep 17 00:00:00 2001 From: Joshua Bowman Date: Fri, 30 Mar 2018 17:16:48 -0700 Subject: [PATCH 40/56] Update default to ACMEv2 server (#5722) --- certbot/constants.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/certbot/constants.py b/certbot/constants.py index 0d0ee8d3f..9da5415d4 100644 --- a/certbot/constants.py +++ b/certbot/constants.py @@ -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, From 8fd3f6c64cb9fc456753c105664606fa89d0a0c4 Mon Sep 17 00:00:00 2001 From: Brad Warren Date: Tue, 3 Apr 2018 11:44:13 -0700 Subject: [PATCH 41/56] fixes #5380 (#5812) --- docs/api/constants.rst | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/docs/api/constants.rst b/docs/api/constants.rst index e225056a2..99ecc240a 100644 --- a/docs/api/constants.rst +++ b/docs/api/constants.rst @@ -3,3 +3,7 @@ .. automodule:: certbot.constants :members: + :exclude-members: SSL_DHPARAMS_SRC + +.. autodata:: SSL_DHPARAMS_SRC + :annotation: = '/path/to/certbot/ssl-dhparams.pem' From f5ad08047bbdf1ba230a6c7933eb7f923fe07938 Mon Sep 17 00:00:00 2001 From: Joona Hoikkala Date: Tue, 3 Apr 2018 22:04:57 +0300 Subject: [PATCH 42/56] Fix comparison to check values (#5815) --- certbot-nginx/certbot_nginx/configurator.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/certbot-nginx/certbot_nginx/configurator.py b/certbot-nginx/certbot_nginx/configurator.py index 13fe493fc..3ba8bcb06 100644 --- a/certbot-nginx/certbot_nginx/configurator.py +++ b/certbot-nginx/certbot_nginx/configurator.py @@ -914,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) From bdaccb645b5af0bb1d14f2cc2ff3e40b7cde9b54 Mon Sep 17 00:00:00 2001 From: ohemorange Date: Tue, 3 Apr 2018 12:14:23 -0700 Subject: [PATCH 43/56] Support quoted server names in Nginx (#5811) * Support quoted server names in Nginx * add unit test to check that we strip quotes * update configurator test --- certbot-nginx/certbot_nginx/parser.py | 2 +- certbot-nginx/certbot_nginx/tests/configurator_test.py | 2 +- .../tests/testdata/etc_nginx/sites-enabled/default | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/certbot-nginx/certbot_nginx/parser.py b/certbot-nginx/certbot_nginx/parser.py index 577e783fc..f06cd17a7 100644 --- a/certbot-nginx/certbot_nginx/parser.py +++ b/certbot-nginx/certbot_nginx/parser.py @@ -743,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 diff --git a/certbot-nginx/certbot_nginx/tests/configurator_test.py b/certbot-nginx/certbot_nginx/tests/configurator_test.py index 34abf2f0d..e88dcb8e0 100644 --- a/certbot-nginx/certbot_nginx/tests/configurator_test.py +++ b/certbot-nginx/certbot_nginx/tests/configurator_test.py @@ -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']]]]], diff --git a/certbot-nginx/certbot_nginx/tests/testdata/etc_nginx/sites-enabled/default b/certbot-nginx/certbot_nginx/tests/testdata/etc_nginx/sites-enabled/default index 4f67fa7d1..e167761d1 100644 --- a/certbot-nginx/certbot_nginx/tests/testdata/etc_nginx/sites-enabled/default +++ b/certbot-nginx/certbot_nginx/tests/testdata/etc_nginx/sites-enabled/default @@ -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; From 2c502e6f8b09898e958f96cff3ef47ba1b8afdc4 Mon Sep 17 00:00:00 2001 From: Brad Warren Date: Tue, 3 Apr 2018 14:04:51 -0700 Subject: [PATCH 44/56] document default is ACMEv2 (#5818) --- docs/using.rst | 16 ++++------------ 1 file changed, 4 insertions(+), 12 deletions(-) diff --git a/docs/using.rst b/docs/using.rst index 7a25a5cc2..f478eb550 100644 --- a/docs/using.rst +++ b/docs/using.rst @@ -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 ` 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 ========== From 9996730fb1a3eded162b54c6ca97731a145e9169 Mon Sep 17 00:00:00 2001 From: Joona Hoikkala Date: Wed, 4 Apr 2018 00:05:37 +0300 Subject: [PATCH 45/56] If restart fails, try alternative restart command if available (#5500) * Use alternative restart command if available in distro overrides --- certbot-apache/certbot_apache/configurator.py | 19 ++++++++++++++++++- .../certbot_apache/override_centos.py | 1 + .../certbot_apache/override_gentoo.py | 1 + .../certbot_apache/tests/centos_test.py | 14 ++++++++++++++ .../certbot_apache/tests/gentoo_test.py | 8 ++++++++ 5 files changed, 42 insertions(+), 1 deletion(-) diff --git a/certbot-apache/certbot_apache/configurator.py b/certbot-apache/certbot_apache/configurator.py index 8b996c675..722e94e18 100644 --- a/certbot-apache/certbot_apache/configurator.py +++ b/certbot-apache/certbot_apache/configurator.py @@ -2000,10 +2000,27 @@ class ApacheConfigurator(augeas_configurator.AugeasConfigurator): :raises .errors.MisconfigurationError: If reload fails """ + error = "" try: util.run_script(self.constant("restart_cmd")) except errors.SubprocessError as err: - raise errors.MisconfigurationError(str(err)) + logger.info("Unable to restart apache using %s", + self.constant("restart_cmd")) + alt_restart = self.constant("restart_cmd_alt") + if alt_restart: + logger.debug("Trying alternative restart command: %s", + alt_restart) + # There is an alternative restart command available + # This usually is "restart" verb while original is "graceful" + try: + util.run_script(self.constant( + "restart_cmd_alt")) + return + except errors.SubprocessError as secerr: + error = str(secerr) + else: + error = str(err) + raise errors.MisconfigurationError(error) def config_test(self): # pylint: disable=no-self-use """Check the configuration of Apache for errors. diff --git a/certbot-apache/certbot_apache/override_centos.py b/certbot-apache/certbot_apache/override_centos.py index db6cd6fba..6e75e361d 100644 --- a/certbot-apache/certbot_apache/override_centos.py +++ b/certbot-apache/certbot_apache/override_centos.py @@ -21,6 +21,7 @@ class CentOSConfigurator(configurator.ApacheConfigurator): version_cmd=['apachectl', '-v'], apache_cmd="apachectl", restart_cmd=['apachectl', 'graceful'], + restart_cmd_alt=['apachectl', 'restart'], conftest_cmd=['apachectl', 'configtest'], enmod=None, dismod=None, diff --git a/certbot-apache/certbot_apache/override_gentoo.py b/certbot-apache/certbot_apache/override_gentoo.py index 92f1d4a20..165e44c96 100644 --- a/certbot-apache/certbot_apache/override_gentoo.py +++ b/certbot-apache/certbot_apache/override_gentoo.py @@ -21,6 +21,7 @@ class GentooConfigurator(configurator.ApacheConfigurator): version_cmd=['/usr/sbin/apache2', '-v'], apache_cmd="apache2ctl", restart_cmd=['apache2ctl', 'graceful'], + restart_cmd_alt=['apache2ctl', 'restart'], conftest_cmd=['apache2ctl', 'configtest'], enmod=None, dismod=None, diff --git a/certbot-apache/certbot_apache/tests/centos_test.py b/certbot-apache/certbot_apache/tests/centos_test.py index d7a2a2fd9..4ee8b5dcf 100644 --- a/certbot-apache/certbot_apache/tests/centos_test.py +++ b/certbot-apache/certbot_apache/tests/centos_test.py @@ -4,6 +4,8 @@ import unittest import mock +from certbot import errors + from certbot_apache import obj from certbot_apache import override_centos from certbot_apache.tests import util @@ -121,5 +123,17 @@ class MultipleVhostsTestCentOS(util.ApacheTest): self.assertTrue("MOCK_NOSEP" in self.config.parser.variables.keys()) self.assertEqual("NOSEP_VAL", self.config.parser.variables["NOSEP_TWO"]) + @mock.patch("certbot_apache.configurator.util.run_script") + def test_alt_restart_works(self, mock_run_script): + mock_run_script.side_effect = [None, errors.SubprocessError, None] + self.config.restart() + self.assertEquals(mock_run_script.call_count, 3) + + @mock.patch("certbot_apache.configurator.util.run_script") + def test_alt_restart_errors(self, mock_run_script): + mock_run_script.side_effect = [None, + errors.SubprocessError, + errors.SubprocessError] + self.assertRaises(errors.MisconfigurationError, self.config.restart) if __name__ == "__main__": unittest.main() # pragma: no cover diff --git a/certbot-apache/certbot_apache/tests/gentoo_test.py b/certbot-apache/certbot_apache/tests/gentoo_test.py index cfbaffac7..d32551267 100644 --- a/certbot-apache/certbot_apache/tests/gentoo_test.py +++ b/certbot-apache/certbot_apache/tests/gentoo_test.py @@ -4,6 +4,8 @@ import unittest import mock +from certbot import errors + from certbot_apache import override_gentoo from certbot_apache import obj from certbot_apache.tests import util @@ -123,5 +125,11 @@ class MultipleVhostsTestGentoo(util.ApacheTest): self.assertEquals(len(self.config.parser.modules), 4) self.assertTrue("mod_another.c" in self.config.parser.modules) + @mock.patch("certbot_apache.configurator.util.run_script") + def test_alt_restart_works(self, mock_run_script): + mock_run_script.side_effect = [None, errors.SubprocessError, None] + self.config.restart() + self.assertEquals(mock_run_script.call_count, 3) + if __name__ == "__main__": unittest.main() # pragma: no cover From b24d9dddc33c0e5d92c22e3a148ba061570ba70a Mon Sep 17 00:00:00 2001 From: Brad Warren Date: Tue, 3 Apr 2018 17:55:12 -0700 Subject: [PATCH 46/56] Revert ACMEv2 default (#5819) * Revert "document default is ACMEv2 (#5818)" This reverts commit 2c502e6f8b09898e958f96cff3ef47ba1b8afdc4. * Revert "Update default to ACMEv2 server (#5722)" This reverts commit 4d706ac77e3eabb3134d0ea75ab76fd58b412bb6. --- certbot/constants.py | 2 +- docs/using.rst | 16 ++++++++++++---- 2 files changed, 13 insertions(+), 5 deletions(-) diff --git a/certbot/constants.py b/certbot/constants.py index 9da5415d4..0d0ee8d3f 100644 --- a/certbot/constants.py +++ b/certbot/constants.py @@ -84,7 +84,7 @@ CLI_DEFAULTS = dict( config_dir="/etc/letsencrypt", work_dir="/var/lib/letsencrypt", logs_dir="/var/log/letsencrypt", - server="https://acme-v02.api.letsencrypt.org/directory", + server="https://acme-v01.api.letsencrypt.org/directory", # Plugins parsers configurator=None, diff --git a/docs/using.rst b/docs/using.rst index f478eb550..7a25a5cc2 100644 --- a/docs/using.rst +++ b/docs/using.rst @@ -871,16 +871,24 @@ 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 ACMEv2 production server at -https://acme-v02.api.letsencrypt.org/. You can tell Certbot to use a +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 different CA by providing ``--server`` on the command line or in a :ref:`configuration file ` with the URL of the server's ACME directory. For example, if you would like to use Let's Encrypt's -initial ACMEv1 server, you would add ``--server -https://acme-v01.api.letsencrypt.org/directory`` to the command line. +new ACMEv2 server, you would add ``--server +https://acme-v02.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 ========== From b6afba0d64c7d0798afb8df609ca2ba4233b0816 Mon Sep 17 00:00:00 2001 From: Brad Warren Date: Wed, 4 Apr 2018 14:33:41 -0700 Subject: [PATCH 47/56] Include testdata (#5827) --- certbot-dns-google/MANIFEST.in | 1 + 1 file changed, 1 insertion(+) diff --git a/certbot-dns-google/MANIFEST.in b/certbot-dns-google/MANIFEST.in index 18f018c08..c91330e38 100644 --- a/certbot-dns-google/MANIFEST.in +++ b/certbot-dns-google/MANIFEST.in @@ -1,3 +1,4 @@ include LICENSE.txt include README.rst recursive-include docs * +recursive-include certbot_dns_google/testdata * From 16b2539f72f1ac83cea731b7068b69fe8c822a8e Mon Sep 17 00:00:00 2001 From: Brad Warren Date: Wed, 4 Apr 2018 15:04:43 -0700 Subject: [PATCH 48/56] Release 0.23.0 --- acme/setup.py | 2 +- certbot-apache/setup.py | 2 +- certbot-auto | 26 +++++++++--------- certbot-compatibility-test/setup.py | 2 +- certbot-dns-cloudflare/setup.py | 2 +- certbot-dns-cloudxns/setup.py | 2 +- certbot-dns-digitalocean/setup.py | 2 +- certbot-dns-dnsimple/setup.py | 2 +- certbot-dns-dnsmadeeasy/setup.py | 2 +- certbot-dns-google/setup.py | 2 +- certbot-dns-luadns/setup.py | 2 +- certbot-dns-nsone/setup.py | 2 +- certbot-dns-rfc2136/setup.py | 2 +- certbot-dns-route53/setup.py | 2 +- certbot-nginx/setup.py | 2 +- certbot/__init__.py | 2 +- docs/cli-help.txt | 4 +-- letsencrypt-auto | 26 +++++++++--------- letsencrypt-auto-source/certbot-auto.asc | 16 +++++------ letsencrypt-auto-source/letsencrypt-auto | 26 +++++++++--------- letsencrypt-auto-source/letsencrypt-auto.sig | Bin 256 -> 256 bytes .../pieces/certbot-requirements.txt | 24 ++++++++-------- 22 files changed, 76 insertions(+), 76 deletions(-) diff --git a/acme/setup.py b/acme/setup.py index 5660cf424..f54ed0e61 100644 --- a/acme/setup.py +++ b/acme/setup.py @@ -4,7 +4,7 @@ from setuptools import setup from setuptools import find_packages -version = '0.23.0.dev0' +version = '0.23.0' # Please update tox.ini when modifying dependency version requirements install_requires = [ diff --git a/certbot-apache/setup.py b/certbot-apache/setup.py index f00b6d95d..f382ae7c7 100644 --- a/certbot-apache/setup.py +++ b/certbot-apache/setup.py @@ -4,7 +4,7 @@ from setuptools import setup from setuptools import find_packages -version = '0.23.0.dev0' +version = '0.23.0' # Remember to update local-oldest-requirements.txt when changing the minimum # acme/certbot version. diff --git a/certbot-auto b/certbot-auto index 8c9745a6f..061b0c8f3 100755 --- a/certbot-auto +++ b/certbot-auto @@ -31,7 +31,7 @@ if [ -z "$VENV_PATH" ]; then fi VENV_BIN="$VENV_PATH/bin" BOOTSTRAP_VERSION_PATH="$VENV_PATH/certbot-auto-bootstrap-version.txt" -LE_AUTO_VERSION="0.22.2" +LE_AUTO_VERSION="0.23.0" BASENAME=$(basename $0) USAGE="Usage: $BASENAME [OPTIONS] A self-updating wrapper script for the Certbot ACME client. When run, updates @@ -1199,18 +1199,18 @@ letsencrypt==0.7.0 \ --hash=sha256:105a5fb107e45bcd0722eb89696986dcf5f08a86a321d6aef25a0c7c63375ade \ --hash=sha256:c36e532c486a7e92155ee09da54b436a3c420813ec1c590b98f635d924720de9 -certbot==0.22.2 \ - --hash=sha256:c8c63bdf0fed6258bdbc892454314ec37bcd1c35a7f62524a083d93ccdfc420d \ - --hash=sha256:e6e3639293e78397f31f7d99e3c63aff82d91e2b0d50d146ee3c77f830464bef -acme==0.22.2 \ - --hash=sha256:59a55244612ee305d2caa6bb4cddd400fb60ec841bf011ed29a2899832a682c2 \ - --hash=sha256:0ecd0ea369f53d5bc744d6e72717f9af2e1ceb558d109dbd433148851027adb4 -certbot-apache==0.22.2 \ - --hash=sha256:b5340d4b9190358fde8eb6a5be0def37e32014b5142ee79ef5d2319ccbbde754 \ - --hash=sha256:3cd26912bb5732d917ddf7aad2fe870090d4ece9a408b2c2de8e9723ec99c759 -certbot-nginx==0.22.2 \ - --hash=sha256:91feef0d879496835d355e82841f92e5ecb5abbf6f23ea0ee5bbb8f5a92b278a \ - --hash=sha256:b10bf04c1a20cf878d5e0d1877deb0e0780bc31b0ffda08ce7199bbc39d0753b +certbot==0.23.0 \ + --hash=sha256:66c42cf780ddbf582ecc52aa6a61242450a2650227b436ad0d260685c4ef8a49 \ + --hash=sha256:6cff4c5da1228661ccaf95195064cb29e6cdf80913193bdb2eb20e164c76053e +acme==0.23.0 \ + --hash=sha256:02e9b596bd3bf8f0733d6d43ec2464ac8185a000acb58d2b4fd9e19223bbbf0b \ + --hash=sha256:08c16635578507f526c338b3418c1147a9f015bf2d366abd51f38918703b4550 +certbot-apache==0.23.0 \ + --hash=sha256:50077742d2763b7600dfda618eb89c870aeea5e6a4c00f60157877f7a7d81f7c \ + --hash=sha256:6b7acec243e224de5268d46c2597277586dffa55e838c252b6931c30d549028e +certbot-nginx==0.23.0 \ + --hash=sha256:f12c21bbe3eb955ca533f1da96d28c6310378b138e844d83253562e18b6cbb32 \ + --hash=sha256:cadf14e4bd504d9ce5987a5ec6dbd8e136638e55303ad5dc81dcb723ddd64324 UNLIKELY_EOF # ------------------------------------------------------------------------- diff --git a/certbot-compatibility-test/setup.py b/certbot-compatibility-test/setup.py index 17abe65ec..a628c6530 100644 --- a/certbot-compatibility-test/setup.py +++ b/certbot-compatibility-test/setup.py @@ -4,7 +4,7 @@ from setuptools import setup from setuptools import find_packages -version = '0.23.0.dev0' +version = '0.23.0' install_requires = [ 'certbot', diff --git a/certbot-dns-cloudflare/setup.py b/certbot-dns-cloudflare/setup.py index 956e37f79..e3bd566fa 100644 --- a/certbot-dns-cloudflare/setup.py +++ b/certbot-dns-cloudflare/setup.py @@ -4,7 +4,7 @@ from setuptools import setup from setuptools import find_packages -version = '0.23.0.dev0' +version = '0.23.0' # Remember to update local-oldest-requirements.txt when changing the minimum # acme/certbot version. diff --git a/certbot-dns-cloudxns/setup.py b/certbot-dns-cloudxns/setup.py index 3493638a0..a81759271 100644 --- a/certbot-dns-cloudxns/setup.py +++ b/certbot-dns-cloudxns/setup.py @@ -4,7 +4,7 @@ from setuptools import setup from setuptools import find_packages -version = '0.23.0.dev0' +version = '0.23.0' # Remember to update local-oldest-requirements.txt when changing the minimum # acme/certbot version. diff --git a/certbot-dns-digitalocean/setup.py b/certbot-dns-digitalocean/setup.py index f136c7161..ed6247f5c 100644 --- a/certbot-dns-digitalocean/setup.py +++ b/certbot-dns-digitalocean/setup.py @@ -4,7 +4,7 @@ from setuptools import setup from setuptools import find_packages -version = '0.23.0.dev0' +version = '0.23.0' # Remember to update local-oldest-requirements.txt when changing the minimum # acme/certbot version. diff --git a/certbot-dns-dnsimple/setup.py b/certbot-dns-dnsimple/setup.py index f2887d371..db894857c 100644 --- a/certbot-dns-dnsimple/setup.py +++ b/certbot-dns-dnsimple/setup.py @@ -4,7 +4,7 @@ from setuptools import setup from setuptools import find_packages -version = '0.23.0.dev0' +version = '0.23.0' # Remember to update local-oldest-requirements.txt when changing the minimum # acme/certbot version. diff --git a/certbot-dns-dnsmadeeasy/setup.py b/certbot-dns-dnsmadeeasy/setup.py index a3ee12cf0..80ac7becc 100644 --- a/certbot-dns-dnsmadeeasy/setup.py +++ b/certbot-dns-dnsmadeeasy/setup.py @@ -4,7 +4,7 @@ from setuptools import setup from setuptools import find_packages -version = '0.23.0.dev0' +version = '0.23.0' # Remember to update local-oldest-requirements.txt when changing the minimum # acme/certbot version. diff --git a/certbot-dns-google/setup.py b/certbot-dns-google/setup.py index 6c25ed452..166b09a29 100644 --- a/certbot-dns-google/setup.py +++ b/certbot-dns-google/setup.py @@ -4,7 +4,7 @@ from setuptools import setup from setuptools import find_packages -version = '0.23.0.dev0' +version = '0.23.0' # Remember to update local-oldest-requirements.txt when changing the minimum # acme/certbot version. diff --git a/certbot-dns-luadns/setup.py b/certbot-dns-luadns/setup.py index f872d7093..bca54f8d4 100644 --- a/certbot-dns-luadns/setup.py +++ b/certbot-dns-luadns/setup.py @@ -4,7 +4,7 @@ from setuptools import setup from setuptools import find_packages -version = '0.23.0.dev0' +version = '0.23.0' # Remember to update local-oldest-requirements.txt when changing the minimum # acme/certbot version. diff --git a/certbot-dns-nsone/setup.py b/certbot-dns-nsone/setup.py index 102ed48c2..b395a3327 100644 --- a/certbot-dns-nsone/setup.py +++ b/certbot-dns-nsone/setup.py @@ -4,7 +4,7 @@ from setuptools import setup from setuptools import find_packages -version = '0.23.0.dev0' +version = '0.23.0' # Remember to update local-oldest-requirements.txt when changing the minimum # acme/certbot version. diff --git a/certbot-dns-rfc2136/setup.py b/certbot-dns-rfc2136/setup.py index 2cbc29e6d..989ad29af 100644 --- a/certbot-dns-rfc2136/setup.py +++ b/certbot-dns-rfc2136/setup.py @@ -4,7 +4,7 @@ from setuptools import setup from setuptools import find_packages -version = '0.23.0.dev0' +version = '0.23.0' # Remember to update local-oldest-requirements.txt when changing the minimum # acme/certbot version. diff --git a/certbot-dns-route53/setup.py b/certbot-dns-route53/setup.py index 3f21c4dc5..ef04bab8e 100644 --- a/certbot-dns-route53/setup.py +++ b/certbot-dns-route53/setup.py @@ -3,7 +3,7 @@ import sys from distutils.core import setup from setuptools import find_packages -version = '0.23.0.dev0' +version = '0.23.0' # Remember to update local-oldest-requirements.txt when changing the minimum # acme/certbot version. diff --git a/certbot-nginx/setup.py b/certbot-nginx/setup.py index 25023b307..1c019290d 100644 --- a/certbot-nginx/setup.py +++ b/certbot-nginx/setup.py @@ -4,7 +4,7 @@ from setuptools import setup from setuptools import find_packages -version = '0.23.0.dev0' +version = '0.23.0' # Remember to update local-oldest-requirements.txt when changing the minimum # acme/certbot version. diff --git a/certbot/__init__.py b/certbot/__init__.py index ebc8d5343..53ba17e38 100644 --- a/certbot/__init__.py +++ b/certbot/__init__.py @@ -1,4 +1,4 @@ """Certbot client.""" # version number like 1.2.3a0, must have at least 2 parts, like 1.2 -__version__ = '0.23.0.dev0' +__version__ = '0.23.0' diff --git a/docs/cli-help.txt b/docs/cli-help.txt index 399adc194..218e21a88 100644 --- a/docs/cli-help.txt +++ b/docs/cli-help.txt @@ -107,8 +107,8 @@ optional arguments: case, and to know when to deprecate support for past Python versions and flags. If you wish to hide this information from the Let's Encrypt server, set this to - "". (default: CertbotACMEClient/0.22.2 (certbot; - darwin 10.13.3) Authenticator/XXX Installer/YYY + "". (default: CertbotACMEClient/0.23.0 (certbot; + darwin 10.13.4) Authenticator/XXX Installer/YYY (SUBCOMMAND; flags: FLAGS) Py/2.7.14). The flags encoded in the user agent are: --duplicate, --force- renew, --allow-subset-of-names, -n, and whether any diff --git a/letsencrypt-auto b/letsencrypt-auto index 8c9745a6f..061b0c8f3 100755 --- a/letsencrypt-auto +++ b/letsencrypt-auto @@ -31,7 +31,7 @@ if [ -z "$VENV_PATH" ]; then fi VENV_BIN="$VENV_PATH/bin" BOOTSTRAP_VERSION_PATH="$VENV_PATH/certbot-auto-bootstrap-version.txt" -LE_AUTO_VERSION="0.22.2" +LE_AUTO_VERSION="0.23.0" BASENAME=$(basename $0) USAGE="Usage: $BASENAME [OPTIONS] A self-updating wrapper script for the Certbot ACME client. When run, updates @@ -1199,18 +1199,18 @@ letsencrypt==0.7.0 \ --hash=sha256:105a5fb107e45bcd0722eb89696986dcf5f08a86a321d6aef25a0c7c63375ade \ --hash=sha256:c36e532c486a7e92155ee09da54b436a3c420813ec1c590b98f635d924720de9 -certbot==0.22.2 \ - --hash=sha256:c8c63bdf0fed6258bdbc892454314ec37bcd1c35a7f62524a083d93ccdfc420d \ - --hash=sha256:e6e3639293e78397f31f7d99e3c63aff82d91e2b0d50d146ee3c77f830464bef -acme==0.22.2 \ - --hash=sha256:59a55244612ee305d2caa6bb4cddd400fb60ec841bf011ed29a2899832a682c2 \ - --hash=sha256:0ecd0ea369f53d5bc744d6e72717f9af2e1ceb558d109dbd433148851027adb4 -certbot-apache==0.22.2 \ - --hash=sha256:b5340d4b9190358fde8eb6a5be0def37e32014b5142ee79ef5d2319ccbbde754 \ - --hash=sha256:3cd26912bb5732d917ddf7aad2fe870090d4ece9a408b2c2de8e9723ec99c759 -certbot-nginx==0.22.2 \ - --hash=sha256:91feef0d879496835d355e82841f92e5ecb5abbf6f23ea0ee5bbb8f5a92b278a \ - --hash=sha256:b10bf04c1a20cf878d5e0d1877deb0e0780bc31b0ffda08ce7199bbc39d0753b +certbot==0.23.0 \ + --hash=sha256:66c42cf780ddbf582ecc52aa6a61242450a2650227b436ad0d260685c4ef8a49 \ + --hash=sha256:6cff4c5da1228661ccaf95195064cb29e6cdf80913193bdb2eb20e164c76053e +acme==0.23.0 \ + --hash=sha256:02e9b596bd3bf8f0733d6d43ec2464ac8185a000acb58d2b4fd9e19223bbbf0b \ + --hash=sha256:08c16635578507f526c338b3418c1147a9f015bf2d366abd51f38918703b4550 +certbot-apache==0.23.0 \ + --hash=sha256:50077742d2763b7600dfda618eb89c870aeea5e6a4c00f60157877f7a7d81f7c \ + --hash=sha256:6b7acec243e224de5268d46c2597277586dffa55e838c252b6931c30d549028e +certbot-nginx==0.23.0 \ + --hash=sha256:f12c21bbe3eb955ca533f1da96d28c6310378b138e844d83253562e18b6cbb32 \ + --hash=sha256:cadf14e4bd504d9ce5987a5ec6dbd8e136638e55303ad5dc81dcb723ddd64324 UNLIKELY_EOF # ------------------------------------------------------------------------- diff --git a/letsencrypt-auto-source/certbot-auto.asc b/letsencrypt-auto-source/certbot-auto.asc index 3e1c4791c..620739670 100644 --- a/letsencrypt-auto-source/certbot-auto.asc +++ b/letsencrypt-auto-source/certbot-auto.asc @@ -1,11 +1,11 @@ -----BEGIN PGP SIGNATURE----- -iQEzBAABCAAdFiEEos+1H6J1pyhiNOeyTRfJlc2XdfIFAlqwWJwACgkQTRfJlc2X -dfIzmwgAghmc3W63/qpCtJdezYeGLJdu03LvKoWYc7dTNYj2+0P5qmAAgCvKNY34 -qYzXA1jfCOgILSzRNE5WY+rbgjcmxxsxH+luYm6Ik0909MaMQ0D3h+5cRFs/tTtd -5cX0gxL3RQQTBwpnwbAZibe7lhjs9pXBiob2ek67hVr+xEwem69BQMlOhtYJbOs1 -osccoKc4NqaKbrfgOjjtMaL8YoRPO9vJHS9rRr6hxRZlPsmvusAHAiCbIrbX4XKE -CgxJFnuHK+amtfRoZg/xCqIK3Z94yZXPezywsri/YvDteOIs+DZ2qG/StfUrNYFX -WYfFFFyld0xwQtb4Oi9u4mx4sPg7lw== -=jZDE +iQEzBAABCAAdFiEEos+1H6J1pyhiNOeyTRfJlc2XdfIFAlrFS/EACgkQTRfJlc2X +dfK+rQf8DcKY5bMi5eJnwwAlui6WIyWSrf1KAKt09tEGZSHQ1fcyCPrGVhk7VVDg +NJ1/XiYBquPW+7mYUcHrIRsiKYbTUcmVjyqP6tZd67IxRH9ToNqBzA6kq99T+IPd +iTGdczHMSPcxM6/Fa5PYMHXy2+ctTr/8+gnsxth9QfcM62Yd6ecfqIdoId3vk9Aw +UBMENZhUasIvgZDWuow+1XVZ/DAmdvj2Xl/E3sA9i2ArREJhkhVegtdrHkwSY+Hm +MKfZGqNVse6ZAF/8YdEVBum0OngMMs63DwucwFxmw5DqWtmnXm6awLNW/LQ/3R5L +xuKjcVaAT1h5TgIyRT6opH8JBKmLpg== +=Ouj4 -----END PGP SIGNATURE----- diff --git a/letsencrypt-auto-source/letsencrypt-auto b/letsencrypt-auto-source/letsencrypt-auto index 07b313528..061b0c8f3 100755 --- a/letsencrypt-auto-source/letsencrypt-auto +++ b/letsencrypt-auto-source/letsencrypt-auto @@ -31,7 +31,7 @@ if [ -z "$VENV_PATH" ]; then fi VENV_BIN="$VENV_PATH/bin" BOOTSTRAP_VERSION_PATH="$VENV_PATH/certbot-auto-bootstrap-version.txt" -LE_AUTO_VERSION="0.23.0.dev0" +LE_AUTO_VERSION="0.23.0" BASENAME=$(basename $0) USAGE="Usage: $BASENAME [OPTIONS] A self-updating wrapper script for the Certbot ACME client. When run, updates @@ -1199,18 +1199,18 @@ letsencrypt==0.7.0 \ --hash=sha256:105a5fb107e45bcd0722eb89696986dcf5f08a86a321d6aef25a0c7c63375ade \ --hash=sha256:c36e532c486a7e92155ee09da54b436a3c420813ec1c590b98f635d924720de9 -certbot==0.22.2 \ - --hash=sha256:c8c63bdf0fed6258bdbc892454314ec37bcd1c35a7f62524a083d93ccdfc420d \ - --hash=sha256:e6e3639293e78397f31f7d99e3c63aff82d91e2b0d50d146ee3c77f830464bef -acme==0.22.2 \ - --hash=sha256:59a55244612ee305d2caa6bb4cddd400fb60ec841bf011ed29a2899832a682c2 \ - --hash=sha256:0ecd0ea369f53d5bc744d6e72717f9af2e1ceb558d109dbd433148851027adb4 -certbot-apache==0.22.2 \ - --hash=sha256:b5340d4b9190358fde8eb6a5be0def37e32014b5142ee79ef5d2319ccbbde754 \ - --hash=sha256:3cd26912bb5732d917ddf7aad2fe870090d4ece9a408b2c2de8e9723ec99c759 -certbot-nginx==0.22.2 \ - --hash=sha256:91feef0d879496835d355e82841f92e5ecb5abbf6f23ea0ee5bbb8f5a92b278a \ - --hash=sha256:b10bf04c1a20cf878d5e0d1877deb0e0780bc31b0ffda08ce7199bbc39d0753b +certbot==0.23.0 \ + --hash=sha256:66c42cf780ddbf582ecc52aa6a61242450a2650227b436ad0d260685c4ef8a49 \ + --hash=sha256:6cff4c5da1228661ccaf95195064cb29e6cdf80913193bdb2eb20e164c76053e +acme==0.23.0 \ + --hash=sha256:02e9b596bd3bf8f0733d6d43ec2464ac8185a000acb58d2b4fd9e19223bbbf0b \ + --hash=sha256:08c16635578507f526c338b3418c1147a9f015bf2d366abd51f38918703b4550 +certbot-apache==0.23.0 \ + --hash=sha256:50077742d2763b7600dfda618eb89c870aeea5e6a4c00f60157877f7a7d81f7c \ + --hash=sha256:6b7acec243e224de5268d46c2597277586dffa55e838c252b6931c30d549028e +certbot-nginx==0.23.0 \ + --hash=sha256:f12c21bbe3eb955ca533f1da96d28c6310378b138e844d83253562e18b6cbb32 \ + --hash=sha256:cadf14e4bd504d9ce5987a5ec6dbd8e136638e55303ad5dc81dcb723ddd64324 UNLIKELY_EOF # ------------------------------------------------------------------------- diff --git a/letsencrypt-auto-source/letsencrypt-auto.sig b/letsencrypt-auto-source/letsencrypt-auto.sig index 088d5efa63c963d88dae21aa13b48cfaf0540917..738fa36e074721d2d7ea6d34b8dafc5937bbf2d1 100644 GIT binary patch literal 256 zcmV+b0ssCViCd%$o-hEy?<8CQwD8ADUBTS{idqO_gjrF5G{ zVRP?&R3bm%6xg|eMaq^mxiODsYes`VLVP%?>YI`_vpQkyqmump!{L*LC1Srlny=7{ zo3Y>fVHPDtRA0e84`j9mZXT*pgZ6cioA*IXpnwBfgM|DH+$uv9hdg9*BU!VA0HaH%xvk86LU? z;y5! literal 256 zcmV+b0ssDpWA~g+WJ85!lJf5bDl&;X`#p73O)Q@I8C^?;Js@g%A<#8V9``*mM3Q|k z+ORXI+b%w$fwmg4h&oGxh9VZz2M%;$=Jw|-*+*rr) Date: Wed, 4 Apr 2018 15:05:08 -0700 Subject: [PATCH 49/56] Bump version to 0.24.0 --- acme/setup.py | 2 +- certbot-apache/setup.py | 2 +- certbot-compatibility-test/setup.py | 2 +- certbot-dns-cloudflare/setup.py | 2 +- certbot-dns-cloudxns/setup.py | 2 +- certbot-dns-digitalocean/setup.py | 2 +- certbot-dns-dnsimple/setup.py | 2 +- certbot-dns-dnsmadeeasy/setup.py | 2 +- certbot-dns-google/setup.py | 2 +- certbot-dns-luadns/setup.py | 2 +- certbot-dns-nsone/setup.py | 2 +- certbot-dns-rfc2136/setup.py | 2 +- certbot-dns-route53/setup.py | 2 +- certbot-nginx/setup.py | 2 +- certbot/__init__.py | 2 +- letsencrypt-auto-source/letsencrypt-auto | 2 +- 16 files changed, 16 insertions(+), 16 deletions(-) diff --git a/acme/setup.py b/acme/setup.py index f54ed0e61..9cc616c36 100644 --- a/acme/setup.py +++ b/acme/setup.py @@ -4,7 +4,7 @@ from setuptools import setup from setuptools import find_packages -version = '0.23.0' +version = '0.24.0.dev0' # Please update tox.ini when modifying dependency version requirements install_requires = [ diff --git a/certbot-apache/setup.py b/certbot-apache/setup.py index f382ae7c7..1ad3b7e43 100644 --- a/certbot-apache/setup.py +++ b/certbot-apache/setup.py @@ -4,7 +4,7 @@ from setuptools import setup from setuptools import find_packages -version = '0.23.0' +version = '0.24.0.dev0' # Remember to update local-oldest-requirements.txt when changing the minimum # acme/certbot version. diff --git a/certbot-compatibility-test/setup.py b/certbot-compatibility-test/setup.py index a628c6530..93495d22e 100644 --- a/certbot-compatibility-test/setup.py +++ b/certbot-compatibility-test/setup.py @@ -4,7 +4,7 @@ from setuptools import setup from setuptools import find_packages -version = '0.23.0' +version = '0.24.0.dev0' install_requires = [ 'certbot', diff --git a/certbot-dns-cloudflare/setup.py b/certbot-dns-cloudflare/setup.py index e3bd566fa..7f7eab517 100644 --- a/certbot-dns-cloudflare/setup.py +++ b/certbot-dns-cloudflare/setup.py @@ -4,7 +4,7 @@ from setuptools import setup from setuptools import find_packages -version = '0.23.0' +version = '0.24.0.dev0' # Remember to update local-oldest-requirements.txt when changing the minimum # acme/certbot version. diff --git a/certbot-dns-cloudxns/setup.py b/certbot-dns-cloudxns/setup.py index a81759271..6a411dedf 100644 --- a/certbot-dns-cloudxns/setup.py +++ b/certbot-dns-cloudxns/setup.py @@ -4,7 +4,7 @@ from setuptools import setup from setuptools import find_packages -version = '0.23.0' +version = '0.24.0.dev0' # Remember to update local-oldest-requirements.txt when changing the minimum # acme/certbot version. diff --git a/certbot-dns-digitalocean/setup.py b/certbot-dns-digitalocean/setup.py index ed6247f5c..0195fb0da 100644 --- a/certbot-dns-digitalocean/setup.py +++ b/certbot-dns-digitalocean/setup.py @@ -4,7 +4,7 @@ from setuptools import setup from setuptools import find_packages -version = '0.23.0' +version = '0.24.0.dev0' # Remember to update local-oldest-requirements.txt when changing the minimum # acme/certbot version. diff --git a/certbot-dns-dnsimple/setup.py b/certbot-dns-dnsimple/setup.py index db894857c..e004ef92c 100644 --- a/certbot-dns-dnsimple/setup.py +++ b/certbot-dns-dnsimple/setup.py @@ -4,7 +4,7 @@ from setuptools import setup from setuptools import find_packages -version = '0.23.0' +version = '0.24.0.dev0' # Remember to update local-oldest-requirements.txt when changing the minimum # acme/certbot version. diff --git a/certbot-dns-dnsmadeeasy/setup.py b/certbot-dns-dnsmadeeasy/setup.py index 80ac7becc..75f588825 100644 --- a/certbot-dns-dnsmadeeasy/setup.py +++ b/certbot-dns-dnsmadeeasy/setup.py @@ -4,7 +4,7 @@ from setuptools import setup from setuptools import find_packages -version = '0.23.0' +version = '0.24.0.dev0' # Remember to update local-oldest-requirements.txt when changing the minimum # acme/certbot version. diff --git a/certbot-dns-google/setup.py b/certbot-dns-google/setup.py index 166b09a29..d8a8025ef 100644 --- a/certbot-dns-google/setup.py +++ b/certbot-dns-google/setup.py @@ -4,7 +4,7 @@ from setuptools import setup from setuptools import find_packages -version = '0.23.0' +version = '0.24.0.dev0' # Remember to update local-oldest-requirements.txt when changing the minimum # acme/certbot version. diff --git a/certbot-dns-luadns/setup.py b/certbot-dns-luadns/setup.py index bca54f8d4..cebe69b42 100644 --- a/certbot-dns-luadns/setup.py +++ b/certbot-dns-luadns/setup.py @@ -4,7 +4,7 @@ from setuptools import setup from setuptools import find_packages -version = '0.23.0' +version = '0.24.0.dev0' # Remember to update local-oldest-requirements.txt when changing the minimum # acme/certbot version. diff --git a/certbot-dns-nsone/setup.py b/certbot-dns-nsone/setup.py index b395a3327..a86d12819 100644 --- a/certbot-dns-nsone/setup.py +++ b/certbot-dns-nsone/setup.py @@ -4,7 +4,7 @@ from setuptools import setup from setuptools import find_packages -version = '0.23.0' +version = '0.24.0.dev0' # Remember to update local-oldest-requirements.txt when changing the minimum # acme/certbot version. diff --git a/certbot-dns-rfc2136/setup.py b/certbot-dns-rfc2136/setup.py index 989ad29af..82f813197 100644 --- a/certbot-dns-rfc2136/setup.py +++ b/certbot-dns-rfc2136/setup.py @@ -4,7 +4,7 @@ from setuptools import setup from setuptools import find_packages -version = '0.23.0' +version = '0.24.0.dev0' # Remember to update local-oldest-requirements.txt when changing the minimum # acme/certbot version. diff --git a/certbot-dns-route53/setup.py b/certbot-dns-route53/setup.py index ef04bab8e..b584b872f 100644 --- a/certbot-dns-route53/setup.py +++ b/certbot-dns-route53/setup.py @@ -3,7 +3,7 @@ import sys from distutils.core import setup from setuptools import find_packages -version = '0.23.0' +version = '0.24.0.dev0' # Remember to update local-oldest-requirements.txt when changing the minimum # acme/certbot version. diff --git a/certbot-nginx/setup.py b/certbot-nginx/setup.py index 1c019290d..77ea4170a 100644 --- a/certbot-nginx/setup.py +++ b/certbot-nginx/setup.py @@ -4,7 +4,7 @@ from setuptools import setup from setuptools import find_packages -version = '0.23.0' +version = '0.24.0.dev0' # Remember to update local-oldest-requirements.txt when changing the minimum # acme/certbot version. diff --git a/certbot/__init__.py b/certbot/__init__.py index 53ba17e38..9dbab3b70 100644 --- a/certbot/__init__.py +++ b/certbot/__init__.py @@ -1,4 +1,4 @@ """Certbot client.""" # version number like 1.2.3a0, must have at least 2 parts, like 1.2 -__version__ = '0.23.0' +__version__ = '0.24.0.dev0' diff --git a/letsencrypt-auto-source/letsencrypt-auto b/letsencrypt-auto-source/letsencrypt-auto index 061b0c8f3..89072ec88 100755 --- a/letsencrypt-auto-source/letsencrypt-auto +++ b/letsencrypt-auto-source/letsencrypt-auto @@ -31,7 +31,7 @@ if [ -z "$VENV_PATH" ]; then fi VENV_BIN="$VENV_PATH/bin" BOOTSTRAP_VERSION_PATH="$VENV_PATH/certbot-auto-bootstrap-version.txt" -LE_AUTO_VERSION="0.23.0" +LE_AUTO_VERSION="0.24.0.dev0" BASENAME=$(basename $0) USAGE="Usage: $BASENAME [OPTIONS] A self-updating wrapper script for the Certbot ACME client. When run, updates From db938dcc0eb9c6a4341ea89dad5c791771cb7e28 Mon Sep 17 00:00:00 2001 From: Peter Linss Date: Fri, 6 Apr 2018 05:39:35 +0900 Subject: [PATCH 50/56] Update messages.py (#5759) Add wildcard field to AuthorizationResource --- acme/acme/messages.py | 1 + 1 file changed, 1 insertion(+) diff --git a/acme/acme/messages.py b/acme/acme/messages.py index 23cd66c63..a69b3bbc4 100644 --- a/acme/acme/messages.py +++ b/acme/acme/messages.py @@ -435,6 +435,7 @@ class Authorization(ResourceBody): # be absent'... then acme-spec gives example with 'expires' # present... That's confusing! expires = fields.RFC3339Field('expires', omitempty=True) + wildcard = jose.Field('wildcard', omitempty=True) @challenges.decoder def challenges(value): # pylint: disable=missing-docstring,no-self-argument From 58626c3197096969e98e687eb97e3e622c6d0ce1 Mon Sep 17 00:00:00 2001 From: Brad Warren Date: Mon, 9 Apr 2018 16:58:58 -0700 Subject: [PATCH 51/56] Double max_rounds (#5842) --- certbot/auth_handler.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/certbot/auth_handler.py b/certbot/auth_handler.py index 9d7c75f57..caf112c61 100644 --- a/certbot/auth_handler.py +++ b/certbot/auth_handler.py @@ -189,7 +189,7 @@ class AuthHandler(object): return active_achalls def _poll_challenges(self, aauthzrs, chall_update, - best_effort, min_sleep=3, max_rounds=15): + best_effort, min_sleep=3, max_rounds=30): """Wait for all challenge results to be determined.""" indices_to_check = set(chall_update.keys()) comp_indices = set() From 4a8e35289c967ccf47c7438954d83613b4a751c7 Mon Sep 17 00:00:00 2001 From: Joona Hoikkala Date: Wed, 11 Apr 2018 18:54:55 +0300 Subject: [PATCH 52/56] PluginStorage to store variables between invocations. (#5468) The base class for Installer plugins `certbot.plugins.common.Installer` now provides functionality of `PluginStorage` to all installer plugins. This allows a plugin to save and retrieve variables in between of invocations. The on disk storage is basically a JSON file at `config_dir`/`.pluginstorage.json`, usually `/etc/letsencrypt/.pluginstorage.json`. The JSON structure is automatically namespaced using the internal plugin name as a namespace key. Because the actual storage is JSON, the supported data types are: dict, list, tuple, str, unicode, int, long, float, boolean and nonetype. To add a variable from inside the plugin class: `self.storage.put("my_variable_name", my_var)` To fetch a variable from inside the plugin class: `my_var = self.storage.fetch("my_variable_key")` The storage state isn't written on disk automatically, but needs to be called: `self.storage.save()` * Plugin storage implementation * Added config_dir to existing test mocks * PluginStorage test cases * Saner handling of bad config_dir paths * Storage moved to Installer and not initialized on plugin __init__ * Finetuning and renaming --- certbot/errors.py | 4 ++ certbot/plugins/common.py | 4 +- certbot/plugins/storage.py | 117 ++++++++++++++++++++++++++++++++ certbot/plugins/storage_test.py | 117 ++++++++++++++++++++++++++++++++ 4 files changed, 241 insertions(+), 1 deletion(-) create mode 100644 certbot/plugins/storage.py create mode 100644 certbot/plugins/storage_test.py diff --git a/certbot/errors.py b/certbot/errors.py index e9c4a0806..48aebc267 100644 --- a/certbot/errors.py +++ b/certbot/errors.py @@ -87,6 +87,10 @@ class NotSupportedError(PluginError): """Certbot Plugin function not supported error.""" +class PluginStorageError(PluginError): + """Certbot Plugin Storage error.""" + + class StandaloneBindError(Error): """Standalone plugin bind error.""" diff --git a/certbot/plugins/common.py b/certbot/plugins/common.py index c281534ca..147d9e21a 100644 --- a/certbot/plugins/common.py +++ b/certbot/plugins/common.py @@ -18,6 +18,8 @@ from certbot import interfaces from certbot import reverter from certbot import util +from certbot.plugins.storage import PluginStorage + logger = logging.getLogger(__name__) @@ -99,7 +101,6 @@ class Plugin(object): def conf(self, var): """Find a configuration value for variable ``var``.""" return getattr(self.config, self.dest(var)) -# other class Installer(Plugin): @@ -110,6 +111,7 @@ class Installer(Plugin): """ def __init__(self, *args, **kwargs): super(Installer, self).__init__(*args, **kwargs) + self.storage = PluginStorage(self.config, self.name) self.reverter = reverter.Reverter(self.config) def add_to_checkpoint(self, save_files, save_notes, temporary=False): diff --git a/certbot/plugins/storage.py b/certbot/plugins/storage.py new file mode 100644 index 000000000..a0c3f8564 --- /dev/null +++ b/certbot/plugins/storage.py @@ -0,0 +1,117 @@ +"""Plugin storage class.""" +import json +import logging +import os + +from certbot import errors + +logger = logging.getLogger(__name__) + +class PluginStorage(object): + """Class implementing storage functionality for plugins""" + + def __init__(self, config, classkey): + """Initializes PluginStorage object storing required configuration + options. + + :param .configuration.NamespaceConfig config: Configuration object + :param str classkey: class name to use as root key in storage file + + """ + + self._config = config + self._classkey = classkey + self._initialized = False + self._data = None + self._storagepath = None + + def _initialize_storage(self): + """Initializes PluginStorage data and reads current state from the disk + if the storage json exists.""" + + self._storagepath = os.path.join(self._config.config_dir, ".pluginstorage.json") + self._load() + self._initialized = True + + def _load(self): + """Reads PluginStorage content from the disk to a dict structure + + :raises .errors.PluginStorageError: when unable to open or read the file + """ + data = dict() + filedata = "" + try: + with open(self._storagepath, 'r') as fh: + filedata = fh.read() + except IOError as e: + errmsg = "Could not read PluginStorage data file: {0} : {1}".format( + self._storagepath, str(e)) + if os.path.isfile(self._storagepath): + # Only error out if file exists, but cannot be read + logger.error(errmsg) + raise errors.PluginStorageError(errmsg) + try: + data = json.loads(filedata) + except ValueError: + if not filedata: + logger.debug("Plugin storage file %s was empty, no values loaded", + self._storagepath) + else: + errmsg = "PluginStorage file {0} is corrupted.".format( + self._storagepath) + logger.error(errmsg) + raise errors.PluginStorageError(errmsg) + self._data = data + + def save(self): + """Saves PluginStorage content to disk + + :raises .errors.PluginStorageError: when unable to serialize the data + or write it to the filesystem + """ + if not self._initialized: + errmsg = "Unable to save, no values have been added to PluginStorage." + logger.error(errmsg) + raise errors.PluginStorageError(errmsg) + + try: + serialized = json.dumps(self._data) + except TypeError as e: + errmsg = "Could not serialize PluginStorage data: {0}".format( + str(e)) + logger.error(errmsg) + raise errors.PluginStorageError(errmsg) + try: + with os.fdopen(os.open(self._storagepath, + os.O_WRONLY | os.O_CREAT, 0o600), 'w') as fh: + fh.write(serialized) + except IOError as e: + errmsg = "Could not write PluginStorage data to file {0} : {1}".format( + self._storagepath, str(e)) + logger.error(errmsg) + raise errors.PluginStorageError(errmsg) + + def put(self, key, value): + """Put configuration value to PluginStorage + + :param str key: Key to store the value to + :param value: Data to store + """ + if not self._initialized: + self._initialize_storage() + + if not self._classkey in self._data.keys(): + self._data[self._classkey] = dict() + self._data[self._classkey][key] = value + + def fetch(self, key): + """Get configuration value from PluginStorage + + :param str key: Key to get value from the storage + + :raises KeyError: If the key doesn't exist in the storage + """ + if not self._initialized: + self._initialize_storage() + + return self._data[self._classkey][key] diff --git a/certbot/plugins/storage_test.py b/certbot/plugins/storage_test.py new file mode 100644 index 000000000..8d96f400c --- /dev/null +++ b/certbot/plugins/storage_test.py @@ -0,0 +1,117 @@ +"""Tests for certbot.plugins.storage.PluginStorage""" +import json +import mock +import os +import unittest + +from certbot import errors + +from certbot.plugins import common +from certbot.tests import util as test_util + +class PluginStorageTest(test_util.ConfigTestCase): + """Test for certbot.plugins.storage.PluginStorage""" + + def setUp(self): + super(PluginStorageTest, self).setUp() + self.plugin_cls = common.Installer + os.mkdir(self.config.config_dir) + with mock.patch("certbot.reverter.util"): + self.plugin = self.plugin_cls(config=self.config, name="mockplugin") + + def test_load_errors_cant_read(self): + with open(os.path.join(self.config.config_dir, + ".pluginstorage.json"), "w") as fh: + fh.write("dummy") + # When unable to read file that exists + mock_open = mock.mock_open() + mock_open.side_effect = IOError + self.plugin.storage.storagepath = os.path.join(self.config.config_dir, + ".pluginstorage.json") + with mock.patch("six.moves.builtins.open", mock_open): + with mock.patch('os.path.isfile', return_value=True): + with mock.patch("certbot.reverter.util"): + self.assertRaises(errors.PluginStorageError, + self.plugin.storage._load) # pylint: disable=protected-access + + def test_load_errors_empty(self): + with open(os.path.join(self.config.config_dir, ".pluginstorage.json"), "w") as fh: + fh.write('') + with mock.patch("certbot.plugins.storage.logger.debug") as mock_log: + # Should not error out but write a debug log line instead + with mock.patch("certbot.reverter.util"): + nocontent = self.plugin_cls(self.config, "mockplugin") + self.assertRaises(KeyError, + nocontent.storage.fetch, "value") + self.assertTrue(mock_log.called) + self.assertTrue("no values loaded" in mock_log.call_args[0][0]) + + def test_load_errors_corrupted(self): + with open(os.path.join(self.config.config_dir, + ".pluginstorage.json"), "w") as fh: + fh.write('invalid json') + with mock.patch("certbot.plugins.storage.logger.error") as mock_log: + with mock.patch("certbot.reverter.util"): + corrupted = self.plugin_cls(self.config, "mockplugin") + self.assertRaises(errors.PluginError, + corrupted.storage.fetch, + "value") + self.assertTrue("is corrupted" in mock_log.call_args[0][0]) + + def test_save_errors_cant_serialize(self): + with mock.patch("certbot.plugins.storage.logger.error") as mock_log: + # Set data as something that can't be serialized + self.plugin.storage._initialized = True # pylint: disable=protected-access + self.plugin.storage.storagepath = "/tmp/whatever" + self.plugin.storage._data = self.plugin_cls # pylint: disable=protected-access + self.assertRaises(errors.PluginStorageError, + self.plugin.storage.save) + self.assertTrue("Could not serialize" in mock_log.call_args[0][0]) + + def test_save_errors_unable_to_write_file(self): + mock_open = mock.mock_open() + mock_open.side_effect = IOError + with mock.patch("os.open", mock_open): + with mock.patch("certbot.plugins.storage.logger.error") as mock_log: + self.plugin.storage._data = {"valid": "data"} # pylint: disable=protected-access + self.plugin.storage._initialized = True # pylint: disable=protected-access + self.assertRaises(errors.PluginStorageError, + self.plugin.storage.save) + self.assertTrue("Could not write" in mock_log.call_args[0][0]) + + def test_save_uninitialized(self): + with mock.patch("certbot.reverter.util"): + self.assertRaises(errors.PluginStorageError, + self.plugin_cls(self.config, "x").storage.save) + + def test_namespace_isolation(self): + with mock.patch("certbot.reverter.util"): + plugin1 = self.plugin_cls(self.config, "first") + plugin2 = self.plugin_cls(self.config, "second") + plugin1.storage.put("first_key", "first_value") + self.assertRaises(KeyError, + plugin2.storage.fetch, "first_key") + self.assertRaises(KeyError, + plugin2.storage.fetch, "first") + self.assertEqual(plugin1.storage.fetch("first_key"), "first_value") + + + def test_saved_state(self): + self.plugin.storage.put("testkey", "testvalue") + # Write to disk + self.plugin.storage.save() + with mock.patch("certbot.reverter.util"): + another = self.plugin_cls(self.config, "mockplugin") + self.assertEqual(another.storage.fetch("testkey"), "testvalue") + + with open(os.path.join(self.config.config_dir, + ".pluginstorage.json"), 'r') as fh: + psdata = fh.read() + psjson = json.loads(psdata) + self.assertTrue("mockplugin" in psjson.keys()) + self.assertEqual(len(psjson), 1) + self.assertEqual(psjson["mockplugin"]["testkey"], "testvalue") + + +if __name__ == "__main__": + unittest.main() # pragma: no cover From e7db97df87f317ca959fe92b454d83997195563c Mon Sep 17 00:00:00 2001 From: Brad Warren Date: Wed, 11 Apr 2018 11:16:12 -0700 Subject: [PATCH 53/56] Update CHANGELOG for 0.23.0 (#5822) * Update CHANGELOG for 0.23.0 * correct date --- CHANGELOG.md | 43 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 43 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1906858dc..89c7c41a9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,49 @@ Certbot adheres to [Semantic Versioning](http://semver.org/). +## 0.23.0 - 2018-04-04 + +### Added + +* Support for OpenResty was added to the Nginx plugin. + +### Changed + +* The timestamps in Certbot's logfiles now use the system's local time zone + rather than UTC. +* Certbot's DNS plugins that use Lexicon now rely on Lexicon>=2.2.1 to be able + to create and delete multiple TXT records on a single domain. +* certbot-dns-google's test suite now works without an internet connection. + +### Fixed + +* Removed a small window that if during which an error occurred, Certbot + wouldn't clean up performed challenges. +* The parameters `default` and `ipv6only` are now removed from `listen` + directives when creating a new server block in the Nginx plugin. +* `server_name` directives enclosed in quotation marks in Nginx are now properly + supported. +* Resolved an issue preventing the Apache plugin from starting Apache when it's + not currently running on RHEL and Gentoo based systems. + +Despite us having broken lockstep, we are continuing to release new versions of +all Certbot components during releases for the time being, however, the only +packages with changes other than their version number were: + +* certbot +* certbot-apache +* certbot-dns-cloudxns +* certbot-dns-dnsimple +* certbot-dns-dnsmadeeasy +* certbot-dns-google +* certbot-dns-luadns +* certbot-dns-nsone +* certbot-dns-rfc2136 +* certbot-nginx + +More details about these changes can be found on our GitHub repo: +https://github.com/certbot/certbot/milestone/50?closed=1 + ## 0.22.2 - 2018-03-19 ### Fixed From 88ceaa38d5f21bb81c4852fe1eb75ba049dfdf3f Mon Sep 17 00:00:00 2001 From: sydneyli Date: Wed, 11 Apr 2018 14:36:53 -0700 Subject: [PATCH 54/56] Bump certbot's acme depenency to 0.22.1 (#5826) --- setup.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/setup.py b/setup.py index 3667a6976..ba521ed2a 100644 --- a/setup.py +++ b/setup.py @@ -34,9 +34,7 @@ version = meta['version'] # specified here to avoid masking the more specific request requirements in # acme. See https://github.com/pypa/pip/issues/988 for more info. install_requires = [ - # Remember to update local-oldest-requirements.txt when changing the - # minimum acme version. - 'acme>0.21.1', + 'acme>=0.22.1', # We technically need ConfigArgParse 0.10.0 for Python 2.6 support, but # saying so here causes a runtime error against our temporary fork of 0.9.3 # in which we added 2.6 support (see #2243), so we relax the requirement. From 6b29d159a2f221c3437770bdb43924ee6f953c4b Mon Sep 17 00:00:00 2001 From: Brad Warren Date: Wed, 11 Apr 2018 16:14:55 -0700 Subject: [PATCH 55/56] use older boulder version (#5852) --- tests/boulder-fetch.sh | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tests/boulder-fetch.sh b/tests/boulder-fetch.sh index fc9cbaae7..53485ffc0 100755 --- a/tests/boulder-fetch.sh +++ b/tests/boulder-fetch.sh @@ -7,10 +7,11 @@ set -xe export GOPATH=${GOPATH:-$HOME/gopath} BOULDERPATH=${BOULDERPATH:-$GOPATH/src/github.com/letsencrypt/boulder} if [ ! -d ${BOULDERPATH} ]; then - git clone --depth=1 https://github.com/letsencrypt/boulder ${BOULDERPATH} + git clone https://github.com/letsencrypt/boulder ${BOULDERPATH} fi cd ${BOULDERPATH} +git checkout fa5c9176655d9fa8dfca188de08bd5373aca422f FAKE_DNS=$(ifconfig docker0 | grep "inet addr:" | cut -d: -f2 | awk '{ print $1}') [ -z "$FAKE_DNS" ] && FAKE_DNS=$(ifconfig docker0 | grep "inet " | xargs | cut -d ' ' -f 2) [ -z "$FAKE_DNS" ] && FAKE_DNS=$(ip addr show dev docker0 | grep "inet " | xargs | cut -d ' ' -f 2 | cut -d '/' -f 1) From e4062efd9fb6ab01d4423a29a37a2e0a5d9e48a3 Mon Sep 17 00:00:00 2001 From: Brad Warren Date: Wed, 11 Apr 2018 16:58:41 -0700 Subject: [PATCH 56/56] Revert "Fix pytest on macOS in Travis (#5360)" This reverts commit 5388842e5b3868e29caf545fb771a23e7fce4143. --- pytest.ini | 2 -- tools/install_and_test.sh | 2 +- tools/pytest.sh | 15 --------------- tox.cover.sh | 3 +-- tox.ini | 6 ------ 5 files changed, 2 insertions(+), 26 deletions(-) delete mode 100644 pytest.ini delete mode 100755 tools/pytest.sh diff --git a/pytest.ini b/pytest.ini deleted file mode 100644 index b64550cb7..000000000 --- a/pytest.ini +++ /dev/null @@ -1,2 +0,0 @@ -[pytest] -addopts = --quiet diff --git a/tools/install_and_test.sh b/tools/install_and_test.sh index f0385470b..59832cbc3 100755 --- a/tools/install_and_test.sh +++ b/tools/install_and_test.sh @@ -19,5 +19,5 @@ for requirement in "$@" ; do if [ $pkg = "." ]; then pkg="certbot" fi - "$(dirname $0)/pytest.sh" --pyargs $pkg + pytest --numprocesses auto --quiet --pyargs $pkg done diff --git a/tools/pytest.sh b/tools/pytest.sh deleted file mode 100755 index 8e3619d5d..000000000 --- a/tools/pytest.sh +++ /dev/null @@ -1,15 +0,0 @@ -#!/bin/bash -# Runs pytest with the provided arguments, adding --numprocesses to the command -# line. This argument is set to "auto" if the environmnent variable TRAVIS is -# not set, otherwise, it is set to 2. This works around -# https://github.com/pytest-dev/pytest-xdist/issues/9. Currently every Travis -# environnment provides two cores. See -# https://docs.travis-ci.com/user/reference/overview/#Virtualization-environments. - -if ${TRAVIS:-false}; then - NUMPROCESSES="2" -else - NUMPROCESSES="auto" -fi - -pytest --numprocesses "$NUMPROCESSES" "$@" diff --git a/tox.cover.sh b/tox.cover.sh index bc0e5a8bf..2b5a3cf19 100755 --- a/tox.cover.sh +++ b/tox.cover.sh @@ -51,8 +51,7 @@ cover () { fi pkg_dir=$(echo "$1" | tr _ -) - pytest="$(dirname $0)/tools/pytest.sh" - "$pytest" --cov "$pkg_dir" --cov-append --cov-report= --pyargs "$1" + pytest --cov "$pkg_dir" --cov-append --cov-report= --numprocesses auto --pyargs "$1" coverage report --fail-under="$min" --include="$pkg_dir/*" --show-missing } diff --git a/tox.ini b/tox.ini index 049220bbb..98fe8fca9 100644 --- a/tox.ini +++ b/tox.ini @@ -60,8 +60,6 @@ commands = setenv = PYTHONPATH = {toxinidir} PYTHONHASHSEED = 0 -passenv = - TRAVIS [testenv:py27-oldest] commands = @@ -69,8 +67,6 @@ commands = setenv = {[testenv]setenv} CERTBOT_OLDEST=1 -passenv = - {[testenv]passenv} [testenv:py27-acme-oldest] commands = @@ -123,8 +119,6 @@ basepython = python2.7 commands = {[base]install_packages} ./tox.cover.sh -passenv = - {[testenv]passenv} [testenv:lint] basepython = python2.7