diff --git a/.travis.yml b/.travis.yml index 8126b06b2..3edf9b167 100644 --- a/.travis.yml +++ b/.travis.yml @@ -169,7 +169,7 @@ notifications: email: false irc: channels: - - "chat.freenode.net#letsencrypt" + - "chat.freenode.net#letsencrypt-dev" on_success: never on_failure: always use_notice: true diff --git a/CHANGELOG.md b/CHANGELOG.md index 0a33b053a..377c472f8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,45 @@ Certbot adheres to [Semantic Versioning](http://semver.org/). +## 0.17.0 - 2017-08-02 + +### Added + +* Support in our nginx plugin for modifying SSL server blocks that do + not contain certificate or key directives. +* A `--max-log-backups` flag to allow users to configure or even completely + disable Certbot's built in log rotation. +* A `--user-agent-comment` flag to allow people who build tools around Certbot + to differentiate their user agent string by adding a comment to its default + value. + +### Changed + +* Due to some awesome work by + [cryptography project](https://github.com/pyca/cryptography), compilation can + now be avoided on most systems when using certbot-auto. This eliminates many + problems people have had in the past such as running out of memory, having + invalid headers/libraries, and changes to the OS packages on their system + after compilation breaking Certbot. +* The `--renew-hook` flag has been hidden in favor of `--deploy-hook`. This new + flag works exactly the same way except it is always run when a certificate is + issued rather than just when it is renewed. +* We have started printing deprecation warnings in certbot-auto for + experimentally supported systems with OS packages available. +* A certificate lineage's name is included in error messages during renewal. + +### Fixed + +* Encoding errors that could occur when parsing error messages from the ACME + server containing Unicode have been resolved. +* certbot-auto no longer prints misleading messages about there being a newer + pip version available when installation fails. +* Certbot's ACME library now properly extracts domains from critical SAN + extensions. + +More details about these changes can be found on our GitHub repo: +https://github.com/certbot/certbot/issues?q=is%3Aissue+milestone%3A0.17.0+is%3Aclosed + ## 0.16.0 - 2017-07-05 ### Added diff --git a/acme/setup.py b/acme/setup.py index 0b166dd91..dad845c04 100644 --- a/acme/setup.py +++ b/acme/setup.py @@ -4,7 +4,7 @@ from setuptools import setup from setuptools import find_packages -version = '0.17.0.dev0' +version = '0.18.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 7f61f9041..ea0085dfc 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.17.0.dev0' +version = '0.18.0.dev0' # Please update tox.ini when modifying dependency version requirements install_requires = [ diff --git a/certbot-auto b/certbot-auto index 599538891..b935ed447 100755 --- a/certbot-auto +++ b/certbot-auto @@ -28,7 +28,7 @@ if [ -z "$VENV_PATH" ]; then VENV_PATH="$XDG_DATA_HOME/$VENV_NAME" fi VENV_BIN="$VENV_PATH/bin" -LE_AUTO_VERSION="0.16.0" +LE_AUTO_VERSION="0.17.0" BASENAME=$(basename $0) USAGE="Usage: $BASENAME [OPTIONS] A self-updating wrapper script for the Certbot ACME client. When run, updates @@ -200,6 +200,25 @@ ExperimentalBootstrap() { fi } +DeprecationBootstrap() { + # Arguments: Platform name, bootstrap function name + if [ "$DEBUG" = 1 ]; then + if [ "$2" != "" ]; then + BootstrapMessage $1 + $2 + fi + else + error "WARNING: certbot-auto support for this $1 is DEPRECATED!" + error "Please visit certbot.eff.org to learn how to download a version of" + error "Certbot that is packaged for your system. While an existing version" + error "of certbot-auto may work currently, we have stopped supporting updating" + error "system packages for your system. Please switch to a packaged version" + error "as soon as possible." + exit 1 + fi +} + + DeterminePythonVersion() { for LE_PYTHON in "$LE_PYTHON" python2.7 python27 python2 python; do # Break (while keeping the LE_PYTHON value) if found. @@ -630,11 +649,11 @@ Bootstrap() { elif [ -f /etc/manjaro-release ]; then ExperimentalBootstrap "Manjaro Linux" BootstrapArchCommon elif [ -f /etc/gentoo-release ]; then - ExperimentalBootstrap "Gentoo" BootstrapGentooCommon + DeprecationBootstrap "Gentoo" BootstrapGentooCommon elif uname | grep -iq FreeBSD ; then - ExperimentalBootstrap "FreeBSD" BootstrapFreeBsd + DeprecationBootstrap "FreeBSD" BootstrapFreeBsd elif uname | grep -iq Darwin ; then - ExperimentalBootstrap "macOS" BootstrapMac + DeprecationBootstrap "macOS" BootstrapMac elif [ -f /etc/issue ] && grep -iq "Amazon Linux" /etc/issue ; then ExperimentalBootstrap "Amazon Linux" BootstrapRpmCommon elif [ -f /etc/product ] && grep -q "Joyent Instance" /etc/product ; then @@ -751,25 +770,37 @@ ConfigArgParse==0.10.0 \ --hash=sha256:3b50a83dd58149dfcee98cb6565265d10b53e9c0a2bca7eeef7fb5f5524890a7 configobj==5.0.6 \ --hash=sha256:a2f5650770e1c87fb335af19a9b7eb73fc05ccf22144eb68db7d00cd2bcb0902 -cryptography==1.9 \ - --hash=sha256:469a9d3d851038f1eb7d7f77bb08bb4775b41483372be450e25b293fe57bd59e \ - --hash=sha256:533143321d15c8743f91eec5c5f495c1b5cad9a25de8f6dab01eddd6b416903e \ - --hash=sha256:2230c186182d773064d06242e0fa604cd718bfff28aa9c5ae73d7e426e98a151 \ - --hash=sha256:3dc94ed5a26b8553a325767358f505c0a43e0c89df078647f77a4d022ddcdc57 \ - --hash=sha256:2eb8297b877cb6b56216750fa7017c9f5786bec8afd6a0f1aaace02cbfb6195f \ - --hash=sha256:025a96e48164106f2082b00f42bf430cf21f09e203e42585a712e420b75cbff0 \ - --hash=sha256:61eb3534f8ed2415dd708b28919205d523f220e4cd5b8165844edfdd4a649b8e \ - --hash=sha256:5474fe5ce6b517d3086e0231b6ad88f8978c551c4379f91c3d619c308490f0d7 \ - --hash=sha256:5ff169869624e23767d70db274f13a9ea4e97c029425a1224aa5e049e84ce2af \ - --hash=sha256:c26e057a2de13e97e708328d295c5ac4cd3eab4a5c42ce727dd1a53316034b8a \ - --hash=sha256:7db719432648f14ea33edffc5f75330c595804eac86ca916528b35ce50a8bfd6 \ - --hash=sha256:ca72537a7064bb80e34b6908e576ffc8e2c2cad29a7168f48d0999df089695c3 \ - --hash=sha256:2a5e577f5d2093e51486b4ec02b51bb5adb165b98fee99929d5af0813e90b469 \ - --hash=sha256:9d9da8bac2e31003d092f5ef6981a725c77c4e9a30638436884d61ad39f9a1ee \ - --hash=sha256:fab8ec6866e384d9827d5dc02a1383e991a6c05c54f818316c4b829e56ca2ba3 \ - --hash=sha256:365eb804362e581c9a02e7a610b30514f07dd77b2a38aed338eb5192446bbc58 \ - --hash=sha256:2908f709f02711dbb10561a9f154d2f7d1792385e2341e9708539cc4ecfb8667 \ - --hash=sha256:5518337022718029e367d982642f3e3523541e098ad671672a90b82474c84882 +cryptography==2.0.2 \ + --hash=sha256:187ae17358436d2c760f28c2aeb02fefa3f37647a9c5b6f7f7c3e83cd1c5a972 \ + --hash=sha256:19e43a13bbf52028dd1e810c803f2ad8880d0692d772f98d42e1eaf34bdee3d6 \ + --hash=sha256:da9291502cbc87dc0284a20c56876e4d2e68deac61cc43df4aec934e44ca97b1 \ + --hash=sha256:0954f8813095f581669330e0a2d5e726c33ac7f450c1458fac58bab54595e516 \ + --hash=sha256:d68b0cc40a8432ed3fc84876c519de704d6001800ec22b136e75ae841910c45b \ + --hash=sha256:2f8ad9580ab4da645cfea52a91d2da99a49a1e76616d8be68441a986fad652b0 \ + --hash=sha256:cc00b4511294f5f6b65c4e77a1a9c62f52490a63d2c120f3872176b40a82351e \ + --hash=sha256:cf896020f6a9f095a547b3d672c8db1ef2ed71fca11250731fa1d4a4cb8b1590 \ + --hash=sha256:e0fdb8322206fa02aa38f71519ff75dce2eb481b7e1110e2936795cb376bb6ee \ + --hash=sha256:277538466657ca5d6637f80be100242f9831d75138b788d718edd3aab34621f8 \ + --hash=sha256:2c77eb0560f54ce654ab82d6b2a64327a71ee969b29022bf9746ca311c9f5069 \ + --hash=sha256:755a7853b679e79d0a799351c092a9b0271f95ff54c8dd8823d8b527a2926a86 \ + --hash=sha256:77197a2d525e761cdd4c771180b4bd0d80703654c6385e4311cbbbe2beb56fa1 \ + --hash=sha256:eb8bb79d0ab00c931c8333b745f06fec481a51c52d70acd4ee95d6093ba5c386 \ + --hash=sha256:131f61de82ef28f3e20beb4bfc24f9692d28cecfd704e20e6c7f070f7793013a \ + --hash=sha256:ac35435974b2e27cd4520f29c191d7da36f4189aa3264e52c4c6c6d089ab6142 \ + --hash=sha256:04b6ea99daa2a8460728794213d76d45ad58ea247dc7e7ff148d7dd726e87863 \ + --hash=sha256:2b9442f8b4c3d575f6cc3db0e856034e0f5a9d55ecd636f52d8c496795b26952 \ + --hash=sha256:b3d3b3ecba1fe1bdb6f180770a137f877c8f07571f7b2934bb269475bcf0e5e8 \ + --hash=sha256:670a58c0d75cb0e78e73dd003bd96d4440bbb1f2bc041dcf7b81767ca4fb0ce9 \ + --hash=sha256:5af84d23bdb86b5e90aca263df1424b43f1748480bfcde3ac2a3cbe622612468 \ + --hash=sha256:ba22e8eefabdd7aca37d0c0c00d2274000d2cebb5cce9e5a710cb55bf8797b31 \ + --hash=sha256:b798b22fa7e92b439547323b8b719d217f1e1b7677585cfeeedf3b55c70bb7fb \ + --hash=sha256:59cff28af8cce96cb7e94a459726e1d88f6f5fa75097f9dcbebd99118d64ea4c \ + --hash=sha256:fe859e445abc9ba9e97950ddafb904e23234c4ecb76b0fae6c86e80592ce464a \ + --hash=sha256:655f3c474067f1e277430f23cc0549f0b1dc99b82aec6e53f80b9b2db7f76f11 \ + --hash=sha256:0ebc2be053c9a03a2f3e20a466e87bf12a51586b3c79bd2a22171b073a805346 \ + --hash=sha256:01e6e60654df64cca53733cda39446d67100c819c181d403afb120e0d2a71e1b \ + --hash=sha256:d46f4e5d455cb5563685c52ef212696f0a6cc1ea627603218eabbd8a095291d8 \ + --hash=sha256:3780b2663ee7ebb37cb83263326e3cd7f8b2ea439c448539d4b87de12c8d06ab enum34==1.1.2 \ --hash=sha256:2475d7fcddf5951e92ff546972758802de5260bf409319a9f1934e6bbc8b1dc7 \ --hash=sha256:35907defb0f992b75ab7788f65fedc1cf20ffa22688e0e6f6f12afc06b3ea501 @@ -876,18 +907,18 @@ letsencrypt==0.7.0 \ --hash=sha256:105a5fb107e45bcd0722eb89696986dcf5f08a86a321d6aef25a0c7c63375ade \ --hash=sha256:c36e532c486a7e92155ee09da54b436a3c420813ec1c590b98f635d924720de9 -certbot==0.16.0 \ - --hash=sha256:e1cc2479f4f149a7128b67192c3f5f6c111b6b9ddcac421ebee8ac5030d5b09b \ - --hash=sha256:795801fd6b06b32060e364ac045312e6b26c6272d5ca32878277e5a2afdee186 -acme==0.16.0 \ - --hash=sha256:31592a744f7a6e7cbb4c5daf2deebc9af6b03997d6f80e5becc203ab8694edcf \ - --hash=sha256:538b69134cc50bdcdcc081b844c85158509022c61d7abc1f44216beeb95de9cd -certbot-apache==0.16.0 \ - --hash=sha256:337f84f391c7d7c31d84376e7a8c882ac119112a4aabceadd1a7de6a2f01ca06 \ - --hash=sha256:f1f37b56e1ceb0a28ccf51d54ca34444e6a708448845ef25372e223b9a82c4d4 -certbot-nginx==0.16.0 \ - --hash=sha256:1d57428202f8e7abdd99c3ddbcf374aad5f69c4262fe8dae53d2016e4ae04ded \ - --hash=sha256:77711655514d707ddf728195f00b2f6cdd1ad9db52440276b4a67664479c6954 +certbot==0.17.0 \ + --hash=sha256:64c25c7123357feffded6408660bc6f5c7d493dd635ae172081d21473075a86a \ + --hash=sha256:43f5b26c3f314d14babf79a3bdf3522e4fc9eef867a0681c426f113c650a669c +acme==0.17.0 \ + --hash=sha256:501710171633af13fc52aa61d0277a6fe335f7477db5810e72239aaf4f3a09e7 \ + --hash=sha256:3ccbe4aaeb98c77b98ee4093b4e4adb76a1a24cbdfec0130c489c206f1d9b66e +certbot-apache==0.17.0 \ + --hash=sha256:17a7e8d7526d838610e68b96cf052af17c4055655b76b06d1cbc74857d90a216 \ + --hash=sha256:29b9e7bc5eaaff6dc4bce8398e35eeacdf346126aad68cac3d41bb87df20a6b9 +certbot-nginx==0.17.0 \ + --hash=sha256:980c9a33a79ab839a089a0085ff0c5414f01f47b6db26ed342df25916658cec9 \ + --hash=sha256:e573f8b4283172755c07b9cca8a8da7ef2d31b4df763881394b5339b2d42994a UNLIKELY_EOF # ------------------------------------------------------------------------- @@ -915,6 +946,7 @@ anything goes wrong, it will exit with a non-zero status code. # The above copyright notice and this permission notice shall be included in # all copies or substantial portions of the Software. from __future__ import print_function +from distutils.version import StrictVersion from hashlib import sha256 from os.path import join from pipes import quote @@ -949,12 +981,14 @@ except ImportError: from urllib.parse import urlparse # 3.4 -__version__ = 1, 1, 1 +__version__ = 1, 3, 0 +PIP_VERSION = '9.0.1' # wheel has a conditional dependency on argparse: maybe_argparse = ( - [('https://pypi.python.org/packages/source/a/argparse/' + [('https://pypi.python.org/packages/18/dd/' + 'e617cfc3f6210ae183374cd9f6a26b20514bbb5a792af97949c5aacddf0f/' 'argparse-1.4.0.tar.gz', '62b089a55be1d8949cd2bc7e0df0bddb9e028faefc8c32038cc84862aefdd6e4')] if version_info < (2, 7, 0) else []) @@ -962,13 +996,19 @@ maybe_argparse = ( PACKAGES = maybe_argparse + [ # Pip has no dependencies, as it vendors everything: - ('https://pypi.python.org/packages/source/p/pip/pip-8.0.3.tar.gz', - '30f98b66f3fe1069c529a491597d34a1c224a68640c82caf2ade5f88aa1405e8'), + ('https://pypi.python.org/packages/11/b6/' + 'abcb525026a4be042b486df43905d6893fb04f05aac21c32c638e939e447/' + 'pip-{0}.tar.gz' + .format(PIP_VERSION), + '09f243e1a7b461f654c26a725fa373211bb7ff17a9300058b205c61658ca940d'), # This version of setuptools has only optional dependencies: - ('https://pypi.python.org/packages/source/s/setuptools/' + ('https://pypi.python.org/packages/69/65/' + '4c544cde88d4d876cdf5cbc5f3f15d02646477756d89547e9a7ecd6afa76/' 'setuptools-20.2.2.tar.gz', '24fcfc15364a9fe09a220f37d2dcedc849795e3de3e4b393ee988e66a9cbd85a'), - ('https://pypi.python.org/packages/source/w/wheel/wheel-0.29.0.tar.gz', + ('https://pypi.python.org/packages/c9/1d/' + 'bd19e691fd4cfe908c76c429fe6e4436c9e83583c4414b54f6c85471954a/' + 'wheel-0.29.0.tar.gz', '1ebb8ad7e26b448e9caa4773d2357849bf80ff9e313964bcaf79cbf0201a1648') ] @@ -1018,11 +1058,21 @@ def hashed_download(url, temp, digest): def main(): + pip_version = StrictVersion(check_output(['pip', '--version']) + .decode('utf-8').split()[1]) + min_pip_version = StrictVersion(PIP_VERSION) + if pip_version >= min_pip_version: + return 0 + has_pip_cache = pip_version >= StrictVersion('6.0') + temp = mkdtemp(prefix='pipstrap-') try: downloads = [hashed_download(url, temp, digest) for url, 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: + ('--no-cache-dir ' if has_pip_cache else '') + ' '.join(quote(d) for d in downloads), shell=True) except HashError as exc: @@ -1045,9 +1095,9 @@ UNLIKELY_EOF PATH="$VENV_BIN:$PATH" "$VENV_BIN/python" "$TEMP_DIR/pipstrap.py" set +e if [ "$VERBOSE" = 1 ]; then - "$VENV_BIN/pip" install --no-cache-dir --require-hashes -r "$TEMP_DIR/letsencrypt-auto-requirements.txt" + "$VENV_BIN/pip" install --disable-pip-version-check --no-cache-dir --require-hashes -r "$TEMP_DIR/letsencrypt-auto-requirements.txt" else - PIP_OUT=`"$VENV_BIN/pip" install --no-cache-dir --require-hashes -r "$TEMP_DIR/letsencrypt-auto-requirements.txt" 2>&1` + PIP_OUT=`"$VENV_BIN/pip" install --disable-pip-version-check --no-cache-dir --require-hashes -r "$TEMP_DIR/letsencrypt-auto-requirements.txt" 2>&1` fi PIP_STATUS=$? set -e diff --git a/certbot-compatibility-test/Dockerfile b/certbot-compatibility-test/Dockerfile index bb9359ce8..fe55a68a6 100644 --- a/certbot-compatibility-test/Dockerfile +++ b/certbot-compatibility-test/Dockerfile @@ -8,8 +8,8 @@ MAINTAINER Brad Warren # TODO: Install non-default Python versions for tox. # TODO: Install Apache/Nginx for plugin development. -COPY certbot-auto /opt/certbot/src/certbot-auto -RUN /opt/certbot/src/certbot-auto -n --os-packages-only +COPY letsencrypt-auto-source /opt/certbot/src/letsencrypt-auto-source +RUN /opt/certbot/src/letsencrypt-auto-source/letsencrypt-auto --os-packages-only # the above is not likely to change, so by putting it further up the # Dockerfile we make sure we cache as much as possible @@ -29,16 +29,18 @@ COPY acme /opt/certbot/src/acme/ COPY certbot-apache /opt/certbot/src/certbot-apache/ COPY certbot-nginx /opt/certbot/src/certbot-nginx/ COPY certbot-compatibility-test /opt/certbot/src/certbot-compatibility-test/ +COPY tools /opt/certbot/src/tools RUN virtualenv --no-site-packages -p python2 /opt/certbot/venv && \ /opt/certbot/venv/bin/pip install -U setuptools && \ - /opt/certbot/venv/bin/pip install -U pip && \ - /opt/certbot/venv/bin/pip install \ - -e /opt/certbot/src/acme \ - -e /opt/certbot/src \ - -e /opt/certbot/src/certbot-apache \ - -e /opt/certbot/src/certbot-nginx \ - -e /opt/certbot/src/certbot-compatibility-test + /opt/certbot/venv/bin/pip install -U pip +ENV PATH /opt/certbot/venv/bin:$PATH +RUN /opt/certbot/src/tools/pip_install_editable.sh \ + /opt/certbot/src/acme \ + /opt/certbot/src \ + /opt/certbot/src/certbot-apache \ + /opt/certbot/src/certbot-nginx \ + /opt/certbot/src/certbot-compatibility-test # install in editable mode (-e) to save space: it's not possible to # "rm -rf /opt/certbot/src" (it's stays in the underlaying image); @@ -46,5 +48,3 @@ RUN virtualenv --no-site-packages -p python2 /opt/certbot/venv && \ # bash" and investigate, apply patches, etc. WORKDIR /opt/certbot/src/certbot-compatibility-test/certbot_compatibility_test/testdata - -ENV PATH /opt/certbot/venv/bin:$PATH diff --git a/certbot-compatibility-test/setup.py b/certbot-compatibility-test/setup.py index 071b97d22..9a348f1f9 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.17.0.dev0' +version = '0.18.0.dev0' install_requires = [ 'certbot', diff --git a/certbot-dns-cloudflare/setup.py b/certbot-dns-cloudflare/setup.py index 7162a1dcf..e301ae06f 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.17.0.dev0' +version = '0.18.0.dev0' # Please update tox.ini when modifying dependency version requirements install_requires = [ diff --git a/certbot-dns-cloudxns/setup.py b/certbot-dns-cloudxns/setup.py index 024e32918..61e741600 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.17.0.dev0' +version = '0.18.0.dev0' # Please update tox.ini when modifying dependency version requirements install_requires = [ diff --git a/certbot-dns-digitalocean/setup.py b/certbot-dns-digitalocean/setup.py index dc09fdd0b..113936945 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.17.0.dev0' +version = '0.18.0.dev0' # Please update tox.ini when modifying dependency version requirements install_requires = [ diff --git a/certbot-dns-dnsimple/setup.py b/certbot-dns-dnsimple/setup.py index 98d3e0a25..24d880bef 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.17.0.dev0' +version = '0.18.0.dev0' # Please update tox.ini when modifying dependency version requirements install_requires = [ diff --git a/certbot-dns-dnsmadeeasy/setup.py b/certbot-dns-dnsmadeeasy/setup.py index a3ab24684..cbae5303d 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.17.0.dev0' +version = '0.18.0.dev0' # Please update tox.ini when modifying dependency version requirements install_requires = [ diff --git a/certbot-dns-google/setup.py b/certbot-dns-google/setup.py index 4b7d21f2e..52ad15225 100644 --- a/certbot-dns-google/setup.py +++ b/certbot-dns-google/setup.py @@ -4,15 +4,17 @@ from setuptools import setup from setuptools import find_packages -version = '0.17.0.dev0' +version = '0.18.0.dev0' # Please update tox.ini when modifying dependency version requirements install_requires = [ 'acme=={0}'.format(version), 'certbot=={0}'.format(version), - 'google-api-python-client', + # 1.5 is the first version that supports oauth2client>=2.0 + 'google-api-python-client>=1.5', 'mock', - 'oauth2client', + # for oauth2client.service_account.ServiceAccountCredentials + 'oauth2client>=2.0', # For pkg_resources. >=1.0 so pip resolves it to a version cryptography # will tolerate; see #2599: 'setuptools>=1.0', diff --git a/certbot-dns-luadns/setup.py b/certbot-dns-luadns/setup.py index be0774e13..68061ced0 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.17.0.dev0' +version = '0.18.0.dev0' # Please update tox.ini when modifying dependency version requirements install_requires = [ diff --git a/certbot-dns-nsone/setup.py b/certbot-dns-nsone/setup.py index addb69b6d..0a562afec 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.17.0.dev0' +version = '0.18.0.dev0' # Please update tox.ini when modifying dependency version requirements install_requires = [ diff --git a/certbot-dns-rfc2136/setup.py b/certbot-dns-rfc2136/setup.py index 3b5409602..f225e6a89 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.17.0.dev0' +version = '0.18.0.dev0' # Please update tox.ini when modifying dependency version requirements install_requires = [ diff --git a/certbot-dns-route53/setup.py b/certbot-dns-route53/setup.py index 7a69f9a13..46fff80c6 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.17.0.dev0' +version = '0.18.0.dev0' install_requires = [ 'acme=={0}'.format(version), diff --git a/certbot-nginx/certbot_nginx/configurator.py b/certbot-nginx/certbot_nginx/configurator.py index 63f659453..fbe881bb0 100644 --- a/certbot-nginx/certbot_nginx/configurator.py +++ b/certbot-nginx/certbot_nginx/configurator.py @@ -87,8 +87,6 @@ class NginxConfigurator(common.Plugin): description = "Nginx Web Server plugin - Alpha" - hidden = True - DEFAULT_LISTEN_PORT = '80' @classmethod @@ -198,16 +196,10 @@ class NginxConfigurator(common.Plugin): cert_directives = [['\n', 'ssl_certificate', ' ', fullchain_path], ['\n', 'ssl_certificate_key', ' ', key_path]] - try: - self.parser.add_server_directives(vhost, - cert_directives, replace=True) - logger.info("Deployed Certificate to VirtualHost %s for %s", - vhost.filep, vhost.names) - except errors.MisconfigurationError as error: - logger.debug(error) - # Presumably break here so that the virtualhost is not modified - raise errors.PluginError("Cannot find a cert or key directive in {0} for {1}. " - "VirtualHost was not modified.".format(vhost.filep, vhost.names)) + self.parser.add_server_directives(vhost, + cert_directives, replace=True) + logger.info("Deployed Certificate to VirtualHost %s for %s", + vhost.filep, vhost.names) self.save_notes += ("Changed vhost at %s with addresses of %s\n" % (vhost.filep, diff --git a/certbot-nginx/certbot_nginx/parser.py b/certbot-nginx/certbot_nginx/parser.py index 4e4aa36ca..158cb9929 100644 --- a/certbot-nginx/certbot_nginx/parser.py +++ b/certbot-nginx/certbot_nginx/parser.py @@ -278,8 +278,8 @@ class NginxParser(object): This method modifies vhost to be fully consistent with the new directives. - ..note :: If replace is True, this raises a misconfiguration error - if the directive does not already exist. + ..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. @@ -464,8 +464,9 @@ def _add_directives(block, directives, replace): When replace=False, it's an error to try and add a directive that already exists in the config block with a conflicting value. - When replace=True, a directive with the same name MUST already exist in the - config block, and the first instance will be replaced. + 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. @@ -547,49 +548,46 @@ def _add_directive(block, directive, replace): location = find_location(directive) if replace: - if location is None: - raise errors.MisconfigurationError( - 'expected directive for {0} in the Nginx ' - 'config but did not find it.'.format(directive[0])) - block[location] = directive - _comment_directive(block, location) - else: - # Append 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. + if location is not None: + block[location] = directive + _comment_directive(block, location) + return + # Append 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. - # handle flat include files + # handle flat include files - directive_name = directive[0] - def can_append(loc, dir_name): - """ Can we append this directive to the block? """ - return loc is None or (isinstance(dir_name, str) and dir_name in REPEATABLE_DIRECTIVES) + directive_name = directive[0] + def can_append(loc, dir_name): + """ Can we append this directive to the block? """ + return loc is None or (isinstance(dir_name, str) and dir_name in REPEATABLE_DIRECTIVES) - err_fmt = 'tried to insert directive "{0}" but found conflicting "{1}".' + err_fmt = 'tried to insert directive "{0}" but found conflicting "{1}".' - # Give a better error message about the specific directive than Nginx's "fail to restart" - if directive_name == INCLUDE: - # in theory, we might want to do this recursively, but in practice, that's really not - # necessary because we know what file we're talking about (and if we don't recurse, we - # just give a worse error message) - included_directives = _parse_ssl_options(directive[1]) + # Give a better error message about the specific directive than Nginx's "fail to restart" + if directive_name == INCLUDE: + # in theory, we might want to do this recursively, but in practice, that's really not + # necessary because we know what file we're talking about (and if we don't recurse, we + # just give a worse error message) + included_directives = _parse_ssl_options(directive[1]) - for included_directive in included_directives: - included_dir_loc = find_location(included_directive) - included_dir_name = included_directive[0] - 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, - block[included_dir_loc])) - else: - _comment_out_directive(block, included_dir_loc, directive[1]) + for included_directive in included_directives: + included_dir_loc = find_location(included_directive) + included_dir_name = included_directive[0] + 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, + block[included_dir_loc])) + else: + _comment_out_directive(block, included_dir_loc, directive[1]) - if can_append(location, directive_name): - block.append(directive) - _comment_directive(block, len(block) - 1) - elif block[location] != directive: - raise errors.MisconfigurationError(err_fmt.format(directive, block[location])) + if can_append(location, directive_name): + block.append(directive) + _comment_directive(block, len(block) - 1) + elif block[location] != directive: + raise errors.MisconfigurationError(err_fmt.format(directive, block[location])) def _apply_global_addr_ssl(addr_to_ssl, parsed_server): """Apply global sslishness information to the parsed server block diff --git a/certbot-nginx/certbot_nginx/tests/parser_test.py b/certbot-nginx/certbot_nginx/tests/parser_test.py index 3877bf5d4..e655bc3e3 100644 --- a/certbot-nginx/certbot_nginx/tests/parser_test.py +++ b/certbot-nginx/certbot_nginx/tests/parser_test.py @@ -273,11 +273,16 @@ class NginxParserTest(util.NginxTest): #pylint: disable=too-many-public-methods ['server_name', 'example.*'], [] ]]]) mock_vhost.names = set(['foobar.com', 'example.*']) - self.assertRaises(errors.MisconfigurationError, - nparser.add_server_directives, - mock_vhost, - [['ssl_certificate', 'cert.pem']], - replace=True) + nparser.add_server_directives( + mock_vhost, [['ssl_certificate', 'cert.pem']], replace=True) + self.assertEqual( + nparser.parsed[filep], + [[['server'], [['listen', '69.50.225.155:9000'], + ['listen', '127.0.0.1'], + ['server_name', 'foobar.com'], ['#', COMMENT], + ['server_name', 'example.*'], [], + ['ssl_certificate', 'cert.pem'], ['#', COMMENT], [], + ]]]) def test_get_best_match(self): target_name = 'www.eff.org' diff --git a/certbot-nginx/setup.py b/certbot-nginx/setup.py index b501aecd9..f89307816 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.17.0.dev0' +version = '0.18.0.dev0' # Please update tox.ini when modifying dependency version requirements install_requires = [ diff --git a/certbot/__init__.py b/certbot/__init__.py index 23ed7eb7a..6c5eff32b 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.17.0.dev0' +__version__ = '0.18.0.dev0' diff --git a/certbot/cli.py b/certbot/cli.py index eaafce762..bb29a43ec 100644 --- a/certbot/cli.py +++ b/certbot/cli.py @@ -873,14 +873,21 @@ def prepare_and_parse_args(plugins, args, detect_defaults=False): # pylint: dis metavar="DOMAIN", action=_DomainsAction, default=[], help="Domain names to apply. For multiple domains you can use " "multiple -d flags or enter a comma separated list of domains " - "as a parameter. (default: Ask)") + "as a parameter. The first provided domain will be used in " + "some software user interfaces and file paths for the " + "certificate and related material unless otherwise " + "specified or you already have a certificate for the same " + "domains. (default: Ask)") helpful.add( [None, "run", "certonly", "manage", "delete", "certificates"], "--cert-name", dest="certname", metavar="CERTNAME", default=None, - help="Certificate name to apply. Only one certificate name can be used " - "per Certbot run. To see certificate names, run 'certbot certificates'. " - "When creating a new certificate, specifies the new certificate's name.") + help="Certificate name to apply. This name is used by Certbot for housekeeping " + "and in file paths; it doesn't affect the content of the certificate itself. " + "To see certificate names, run 'certbot certificates'. " + "When creating a new certificate, specifies the new certificate's name. " + "(default: the first provided domain or the name of an existing " + "certificate on your system for the same domains)") helpful.add( [None, "testing", "renew", "certonly"], "--dry-run", action="store_true", dest="dry_run", @@ -1145,15 +1152,21 @@ def _create_subparsers(helpful): '(default: {0}). The flags encoded in the user agent are: ' '--duplicate, --force-renew, --allow-subset-of-names, -n, and ' 'whether any hooks are set.'.format(sample_user_agent())) + helpful.add( + None, "--user-agent-comment", default=None, type=_user_agent_comment_type, + help="Add a comment to the default user agent string. May be used when repackaging Certbot " + "or calling it from another tool to allow additional statistical data to be collected." + " Ignored if --user-agent is set. (Example: Foo-Wrapper/1.0)") helpful.add("certonly", "--csr", type=read_file, help="Path to a Certificate Signing Request (CSR) in DER or PEM format." " Currently --csr only works with the 'certonly' subcommand.") helpful.add("revoke", "--reason", dest="reason", - choices=CaseInsensitiveList(constants.REVOCATION_REASONS.keys()), + choices=CaseInsensitiveList(sorted(constants.REVOCATION_REASONS, + key=constants.REVOCATION_REASONS.get)), action=_EncodeReasonAction, default=0, - help="Specify reason for revoking certificate.") + help="Specify reason for revoking certificate. (default: unspecified)") helpful.add("rollback", "--checkpoints", type=int, metavar="N", default=flag_default("rollback_checkpoints"), @@ -1360,6 +1373,10 @@ def parse_preferred_challenges(pref_challs): "Unrecognized challenges: {0}".format(unrecognized)) return challs +def _user_agent_comment_type(value): + if "(" in value or ")" in value: + raise argparse.ArgumentTypeError("may not contain parentheses") + return value class _DeployHookAction(argparse.Action): """Action class for parsing deploy hooks.""" diff --git a/certbot/client.py b/certbot/client.py index 6010dd0a0..ed70fda71 100644 --- a/certbot/client.py +++ b/certbot/client.py @@ -58,11 +58,12 @@ def determine_user_agent(config): # policy, talk to a core Certbot team member before making any # changes here. if config.user_agent is None: - ua = ("CertbotACMEClient/{0} ({1}; {2}) Authenticator/{3} Installer/{4} " + ua = ("CertbotACMEClient/{0} ({1}; {2}{8}) Authenticator/{3} Installer/{4} " "({5}; flags: {6}) Py/{7}") ua = ua.format(certbot.__version__, cli.cli_command, util.get_os_info_ua(), config.authenticator, config.installer, config.verb, - ua_flags(config), platform.python_version()) + ua_flags(config), platform.python_version(), + "; " + config.user_agent_comment if config.user_agent_comment else "") else: ua = config.user_agent return ua diff --git a/certbot/main.py b/certbot/main.py index a6e630540..d1ed6fe2b 100644 --- a/certbot/main.py +++ b/certbot/main.py @@ -645,6 +645,7 @@ def renew_cert(config, plugins, lineage): except errors.PluginSelectionError as e: logger.info("Could not choose appropriate plugin: %s", e) raise + le_client = _init_le_client(config, auth, installer) _get_and_save_cert(le_client, config, lineage=lineage) @@ -673,6 +674,7 @@ def certonly(config, plugins): except errors.PluginSelectionError as e: logger.info("Could not choose appropriate plugin: %s", e) raise + le_client = _init_le_client(config, auth, installer) if config.csr: diff --git a/certbot/plugins/common_test.py b/certbot/plugins/common_test.py index 411cbe651..409c05946 100644 --- a/certbot/plugins/common_test.py +++ b/certbot/plugins/common_test.py @@ -202,7 +202,7 @@ class TLSSNI01Test(unittest.TestCase): achall.chall.encode.return_value = "token" key = test_util.load_pyopenssl_private_key("rsa512_key.pem") achall.response_and_validation.return_value = ( - response, (test_util.load_cert("cert.pem"), key)) + response, (test_util.load_cert("cert_512.pem"), key)) with mock.patch("certbot.plugins.common.open", mock_open, create=True): @@ -215,7 +215,7 @@ class TLSSNI01Test(unittest.TestCase): # pylint: disable=no-member mock_open.assert_called_once_with(self.sni.get_cert_path(achall), "wb") mock_open.return_value.write.assert_called_once_with( - test_util.load_vector("cert.pem")) + test_util.load_vector("cert_512.pem")) mock_safe_open.assert_called_once_with( self.sni.get_key_path(achall), "wb", chmod=0o400) mock_safe_open.return_value.write.assert_called_once_with( diff --git a/certbot/plugins/selection.py b/certbot/plugins/selection.py index fdfbf2b15..5b1953187 100644 --- a/certbot/plugins/selection.py +++ b/certbot/plugins/selection.py @@ -108,11 +108,19 @@ def choose_plugin(prepared, question): opts = [plugin_ep.description_with_name + (" [Misconfigured]" if plugin_ep.misconfigured else "") for plugin_ep in prepared] + names = set(plugin_ep.name for plugin_ep in prepared) while True: disp = z_util(interfaces.IDisplay) - code, index = disp.menu( - question, opts, force_interactive=True) + if "CERTBOT_AUTO" in os.environ and names == set(("apache", "nginx")): + # The possibility of being offered exactly apache and nginx here + # is new interactivity brought by https://github.com/certbot/certbot/issues/4079, + # so set apache as a default for those kinds of non-interactive use + # (the user will get a warning to set --non-interactive or --force-interactive) + apache_idx = [n for n, p in enumerate(prepared) if p.name == "apache"][0] + code, index = disp.menu(question, opts, default=apache_idx) + else: + code, index = disp.menu(question, opts, force_interactive=True) if code == display_util.OK: plugin_ep = prepared[index] @@ -134,6 +142,8 @@ def record_chosen_plugins(config, plugins, auth, inst): "Update the config entries to reflect the plugins we actually selected." config.authenticator = plugins.find_init(auth).name if auth else "None" config.installer = plugins.find_init(inst).name if inst else "None" + logger.info("Plugins selected: Authenticator %s, Installer %s", + config.authenticator, config.installer) def choose_configurator_plugins(config, plugins, verb): diff --git a/certbot/plugins/selection_test.py b/certbot/plugins/selection_test.py index 9f0716905..4112810a2 100644 --- a/certbot/plugins/selection_test.py +++ b/certbot/plugins/selection_test.py @@ -1,4 +1,5 @@ """Tests for letsencrypt.plugins.selection""" +import os import sys import unittest @@ -115,6 +116,7 @@ class ChoosePluginTest(unittest.TestCase): False)) self.mock_apache = mock.Mock( description_with_name="a", misconfigured=True) + self.mock_apache.name = "apache" self.mock_stand = mock.Mock( description_with_name="s", misconfigured=False) self.mock_stand.init().more_info.return_value = "standalone" @@ -146,3 +148,26 @@ class ChoosePluginTest(unittest.TestCase): def test_no_choice(self, mock_util): mock_util().menu.return_value = (display_util.CANCEL, 0) self.assertTrue(self._call() is None) + + @test_util.patch_get_utility("certbot.plugins.selection.z_util") + def test_new_interaction_avoidance(self, mock_util): + mock_nginx = mock.Mock( + description_with_name="n", misconfigured=False) + mock_nginx.init().more_info.return_value = "nginx plugin" + mock_nginx.name = "nginx" + self.plugins[1] = mock_nginx + mock_util().menu.return_value = (display_util.CANCEL, 0) + + unset_cb_auto = os.environ.get("CERTBOT_AUTO") is None + if unset_cb_auto: + os.environ["CERTBOT_AUTO"] = "foo" + try: + self._call() + finally: + if unset_cb_auto: + del os.environ["CERTBOT_AUTO"] + + self.assertTrue("default" in mock_util().menu.call_args[1]) + +if __name__ == "__main__": + unittest.main() # pragma: no cover diff --git a/certbot/renewal.py b/certbot/renewal.py index 5c6697941..f3131c06f 100644 --- a/certbot/renewal.py +++ b/certbot/renewal.py @@ -389,14 +389,16 @@ def handle_renewal_request(config): disp = zope.component.getUtility(interfaces.IDisplay) disp.notification("Processing " + renewal_file, pause=False) lineage_config = copy.deepcopy(config) + lineagename = storage.lineagename_for_filename(renewal_file) # Note that this modifies config (to add back the configuration # elements from within the renewal configuration file). try: renewal_candidate = _reconstitute(lineage_config, renewal_file) except Exception as e: # pylint: disable=broad-except - logger.warning("Renewal configuration file %s produced an " - "unexpected error: %s. Skipping.", renewal_file, e) + logger.warning("Renewal configuration file %s (cert: %s) " + "produced an unexpected error: %s. Skipping.", + renewal_file, lineagename, e) logger.debug("Traceback was:\n%s", traceback.format_exc()) parse_failures.append(renewal_file) continue @@ -422,8 +424,9 @@ def handle_renewal_request(config): renew_skipped.append(renewal_candidate.fullchain) except Exception as e: # pylint: disable=broad-except # obtain_cert (presumably) encountered an unanticipated problem. - logger.warning("Attempting to renew cert from %s produced an " - "unexpected error: %s. Skipping.", renewal_file, e) + logger.warning("Attempting to renew cert (%s) from %s produced an " + "unexpected error: %s. Skipping.", lineagename, + renewal_file, e) logger.debug("Traceback was:\n%s", traceback.format_exc()) renew_failures.append(renewal_candidate.fullchain) diff --git a/certbot/tests/account_test.py b/certbot/tests/account_test.py index 5950dcda9..7245ad6a1 100644 --- a/certbot/tests/account_test.py +++ b/certbot/tests/account_test.py @@ -14,12 +14,10 @@ from acme import messages from certbot import errors -from certbot.tests import util - -from certbot.tests.util import TempDirTestCase +import certbot.tests.util as test_util -KEY = jose.JWKRSA.load(util.load_vector("rsa512_key_2.pem")) +KEY = jose.JWKRSA.load(test_util.load_vector("rsa512_key.pem")) class AccountTest(unittest.TestCase): @@ -48,22 +46,19 @@ class AccountTest(unittest.TestCase): def test_id(self): self.assertEqual( - self.acc.id, "bca5889f66457d5b62fbba7b25f9ab6f") + self.acc.id, "7adac10320f585ddf118429c0c4af2cd") def test_slug(self): self.assertEqual( - self.acc.slug, "test.certbot.org@2015-07-04T14:04:10Z (bca5)") + self.acc.slug, "test.certbot.org@2015-07-04T14:04:10Z (7ada)") def test_repr(self): self.assertTrue(repr(self.acc).startswith( - " csr_X.der] + +and for the certificate: + + openssl req -new -out cert_X.pem -key rsaX_key.pem -subj '/CN=example.com' -x509 [-outform DER > cert_X.der] \ No newline at end of file diff --git a/certbot/tests/testdata/cert-5sans.pem b/certbot/tests/testdata/cert-5sans_512.pem similarity index 100% rename from certbot/tests/testdata/cert-5sans.pem rename to certbot/tests/testdata/cert-5sans_512.pem diff --git a/certbot/tests/testdata/cert-san.pem b/certbot/tests/testdata/cert-san_512.pem similarity index 100% rename from certbot/tests/testdata/cert-san.pem rename to certbot/tests/testdata/cert-san_512.pem diff --git a/certbot/tests/testdata/cert.b64jose b/certbot/tests/testdata/cert.b64jose deleted file mode 100644 index fa1abdb9f..000000000 --- a/certbot/tests/testdata/cert.b64jose +++ /dev/null @@ -1 +0,0 @@ -MIIB3jCCAYigAwIBAgICBTkwDQYJKoZIhvcNAQELBQAwdzELMAkGA1UEBhMCVVMxETAPBgNVBAgMCE1pY2hpZ2FuMRIwEAYDVQQHDAlBbm4gQXJib3IxKzApBgNVBAoMIlVuaXZlcnNpdHkgb2YgTWljaGlnYW4gYW5kIHRoZSBFRkYxFDASBgNVBAMMC2V4YW1wbGUuY29tMB4XDTE0MTIxMTIyMzQ0NVoXDTE0MTIxODIyMzQ0NVowdzELMAkGA1UEBhMCVVMxETAPBgNVBAgMCE1pY2hpZ2FuMRIwEAYDVQQHDAlBbm4gQXJib3IxKzApBgNVBAoMIlVuaXZlcnNpdHkgb2YgTWljaGlnYW4gYW5kIHRoZSBFRkYxFDASBgNVBAMMC2V4YW1wbGUuY29tMFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBAKx1c7RR7R_drnBSQ_zfx1vQLHUbFLh1AQQQ5R8DZUXd36efNK79vukFhN9HFoHZiUvOjm0c-pVE6K-EdE_twuUCAwEAATANBgkqhkiG9w0BAQsFAANBAC24z0IdwIVKSlntksllvr6zJepBH5fMndfk3XJp10jT6VE-14KNtjh02a56GoraAvJAT5_H67E8GvJ_ocNnB_o \ No newline at end of file diff --git a/certbot/tests/testdata/cert.der b/certbot/tests/testdata/cert.der deleted file mode 100644 index 5f1018505..000000000 Binary files a/certbot/tests/testdata/cert.der and /dev/null differ diff --git a/certbot/tests/testdata/self_signed_cert.pem b/certbot/tests/testdata/cert_2048.pem similarity index 100% rename from certbot/tests/testdata/self_signed_cert.pem rename to certbot/tests/testdata/cert_2048.pem diff --git a/certbot/tests/testdata/cert.pem b/certbot/tests/testdata/cert_512.pem similarity index 100% rename from certbot/tests/testdata/cert.pem rename to certbot/tests/testdata/cert_512.pem diff --git a/certbot/tests/testdata/self_signed_cert_bad.pem b/certbot/tests/testdata/cert_512_bad.pem similarity index 100% rename from certbot/tests/testdata/self_signed_cert_bad.pem rename to certbot/tests/testdata/cert_512_bad.pem diff --git a/certbot/tests/testdata/self_signed_fullchain.pem b/certbot/tests/testdata/cert_fullchain_2048.pem similarity index 100% rename from certbot/tests/testdata/self_signed_fullchain.pem rename to certbot/tests/testdata/cert_fullchain_2048.pem diff --git a/certbot/tests/testdata/csr-6sans.pem b/certbot/tests/testdata/csr-6sans.pem deleted file mode 100644 index 8f6b52bd7..000000000 --- a/certbot/tests/testdata/csr-6sans.pem +++ /dev/null @@ -1,12 +0,0 @@ ------BEGIN CERTIFICATE REQUEST----- -MIIBuzCCAWUCAQAweTELMAkGA1UEBhMCVVMxETAPBgNVBAgTCE1pY2hpZ2FuMRIw -EAYDVQQHEwlBbm4gQXJib3IxDDAKBgNVBAoTA0VGRjEfMB0GA1UECxMWVW5pdmVy -c2l0eSBvZiBNaWNoaWdhbjEUMBIGA1UEAxMLZXhhbXBsZS5jb20wXDANBgkqhkiG -9w0BAQEFAANLADBIAkEA9LYRcVE3Nr+qleecEcX8JwVDnjeG1X7ucsCasuuZM0e0 -9cmYuUzxIkMjO/9x4AVcvXXRXPEV+LzWWkfkTlzRMwIDAQABoIGGMIGDBgkqhkiG -9w0BCQ4xdjB0MHIGA1UdEQRrMGmCC2V4YW1wbGUuY29tggtleGFtcGxlLm9yZ4IL -ZXhhbXBsZS5uZXSCDGV4YW1wbGUuaW5mb4IVc3ViZG9tYWluLmV4YW1wbGUuY29t -ghtvdGhlci5zdWJkb21haW4uZXhhbXBsZS5jb20wDQYJKoZIhvcNAQELBQADQQBd -k4BE5qvEvkYoZM/2++Xd9RrQ6wsdj0QiJQCozfsI4lQx6ZJnbtNc7HpDrX4W6XIv -IvzVBz/nD11drfz/RNuX ------END CERTIFICATE REQUEST----- diff --git a/certbot/tests/testdata/csr-6sans_512.conf b/certbot/tests/testdata/csr-6sans_512.conf new file mode 100644 index 000000000..fa7b3edc2 --- /dev/null +++ b/certbot/tests/testdata/csr-6sans_512.conf @@ -0,0 +1,29 @@ +[req] +distinguished_name = req_distinguished_name +req_extensions = v3_req + +[req_distinguished_name] +C=US +C_default = US +ST=Michigan +ST_default=Michigan +L=Ann Arbor +L_default=Ann Arbor +O=EFF +O_default=EFF +OU=University of Michigan +OU_default=University of Michigan +CN=example.com +CN_default=example.com + + +[ v3_req ] +subjectAltName = @alt_names + +[alt_names] +DNS.1 = example.com +DNS.2 = example.org +DNS.3 = example.net +DNS.4 = example.info +DNS.5 = subdomain.example.com +DNS.6 = other.subdomain.example.com \ No newline at end of file diff --git a/certbot/tests/testdata/csr-6sans_512.pem b/certbot/tests/testdata/csr-6sans_512.pem new file mode 100644 index 000000000..f72c0541d --- /dev/null +++ b/certbot/tests/testdata/csr-6sans_512.pem @@ -0,0 +1,12 @@ +-----BEGIN CERTIFICATE REQUEST----- +MIIBuzCCAWUCAQAweTELMAkGA1UEBhMCVVMxETAPBgNVBAgMCE1pY2hpZ2FuMRIw +EAYDVQQHDAlBbm4gQXJib3IxDDAKBgNVBAoMA0VGRjEfMB0GA1UECwwWVW5pdmVy +c2l0eSBvZiBNaWNoaWdhbjEUMBIGA1UEAwwLZXhhbXBsZS5jb20wXDANBgkqhkiG +9w0BAQEFAANLADBIAkEArHVztFHtH92ucFJD/N/HW9AsdRsUuHUBBBDlHwNlRd3f +p580rv2+6QWE30cWgdmJS86ObRz6lUTor4R0T+3C5QIDAQABoIGGMIGDBgkqhkiG +9w0BCQ4xdjB0MHIGA1UdEQRrMGmCC2V4YW1wbGUuY29tggtleGFtcGxlLm9yZ4IL +ZXhhbXBsZS5uZXSCDGV4YW1wbGUuaW5mb4IVc3ViZG9tYWluLmV4YW1wbGUuY29t +ghtvdGhlci5zdWJkb21haW4uZXhhbXBsZS5jb20wDQYJKoZIhvcNAQELBQADQQA+ +sU6T30n3SsdnHlj0Va8eECOWK7Lf8nUfxxgjPMQ7BoU8gbAnGfDmOlwDronTRqf1 +Me+nlYJU4TX1OiX10DYu +-----END CERTIFICATE REQUEST----- diff --git a/certbot/tests/testdata/csr-nonames.pem b/certbot/tests/testdata/csr-nonames_512.pem similarity index 100% rename from certbot/tests/testdata/csr-nonames.pem rename to certbot/tests/testdata/csr-nonames_512.pem diff --git a/certbot/tests/testdata/csr-nosans.pem b/certbot/tests/testdata/csr-nosans.pem deleted file mode 100644 index 813db67b0..000000000 --- a/certbot/tests/testdata/csr-nosans.pem +++ /dev/null @@ -1,8 +0,0 @@ ------BEGIN CERTIFICATE REQUEST----- -MIIBFTCBwAIBADBbMQswCQYDVQQGEwJBVTETMBEGA1UECAwKU29tZS1TdGF0ZTEh -MB8GA1UECgwYSW50ZXJuZXQgV2lkZ2l0cyBQdHkgTHRkMRQwEgYDVQQDDAtleGFt -cGxlLm9yZzBcMA0GCSqGSIb3DQEBAQUAA0sAMEgCQQD0thFxUTc2v6qV55wRxfwn -BUOeN4bVfu5ywJqy65kzR7T1yZi5TPEiQyM7/3HgBVy9ddFc8RX4vNZaR+ROXNEz -AgMBAAGgADANBgkqhkiG9w0BAQsFAANBAMikGL8Ch7hQCStXH7chhDp6+pt2+VSo -wgsrPQ2Bw4veDMlSemUrH+4e0TwbbntHfvXTDHWs9P3BiIDJLxFrjuA= ------END CERTIFICATE REQUEST----- diff --git a/certbot/tests/testdata/csr-nosans_512.conf b/certbot/tests/testdata/csr-nosans_512.conf new file mode 100644 index 000000000..1026cf9ad --- /dev/null +++ b/certbot/tests/testdata/csr-nosans_512.conf @@ -0,0 +1,16 @@ +[req] +distinguished_name = req_distinguished_name + +[req_distinguished_name] +C=US +C_default = US +ST=Michigan +ST_default=Michigan +L=Ann Arbor +L_default=Ann Arbor +O=EFF +O_default=EFF +OU=University of Michigan +OU_default=University of Michigan +CN=example.com +CN_default=example.com \ No newline at end of file diff --git a/certbot/tests/testdata/csr-nosans_512.pem b/certbot/tests/testdata/csr-nosans_512.pem new file mode 100644 index 000000000..5f02d7e97 --- /dev/null +++ b/certbot/tests/testdata/csr-nosans_512.pem @@ -0,0 +1,9 @@ +-----BEGIN CERTIFICATE REQUEST----- +MIIBMzCB3gIBADB5MQswCQYDVQQGEwJVUzERMA8GA1UECAwITWljaGlnYW4xEjAQ +BgNVBAcMCUFubiBBcmJvcjEMMAoGA1UECgwDRUZGMR8wHQYDVQQLDBZVbml2ZXJz +aXR5IHBmIE1pY2hpZ2FuMRQwEgYDVQQDDAtleGFtcGxlLmNvbTBcMA0GCSqGSIb3 +DQEBAQUAA0sAMEgCQQCsdXO0Ue0f3a5wUkP838db0Cx1GxS4dQEEEOUfA2VF3d+n +nzSu/b7pBYTfRxaB2YlLzo5tHPqVROivhHRP7cLlAgMBAAGgADANBgkqhkiG9w0B +AQsFAANBAG06jIPvSC6wiGLy7sUTaEX4UCE6Cztp3vh/uXN7Q++CGn6KiXNs/BRW +eFlcFPbvxbVG/ZZFR5aPs+Oy6RhqOjg= +-----END CERTIFICATE REQUEST----- diff --git a/certbot/tests/testdata/csr-san.pem b/certbot/tests/testdata/csr-san_512.pem similarity index 100% rename from certbot/tests/testdata/csr-san.pem rename to certbot/tests/testdata/csr-san_512.pem diff --git a/certbot/tests/testdata/csr.der b/certbot/tests/testdata/csr_512.der similarity index 100% rename from certbot/tests/testdata/csr.der rename to certbot/tests/testdata/csr_512.der diff --git a/certbot/tests/testdata/csr.pem b/certbot/tests/testdata/csr_512.pem similarity index 100% rename from certbot/tests/testdata/csr.pem rename to certbot/tests/testdata/csr_512.pem diff --git a/certbot/tests/testdata/dsa512_key.pem b/certbot/tests/testdata/dsa512_key.pem deleted file mode 100644 index 78e164712..000000000 --- a/certbot/tests/testdata/dsa512_key.pem +++ /dev/null @@ -1,14 +0,0 @@ ------BEGIN DSA PARAMETERS----- -MIGdAkEAwebEoGBfokKQeALHHnAZMQwYU35ILEBdV8oUmzv7qpSVUoHihyqfn6GC -OixAKSP8EJYcTilIqPbFbfFyOPlbLwIVANoFHEDiQgknAvKrG78pHzAJdQSPAkEA -qfka5Bnl+CeEMpzVZGrOVqZE/LFdZK9eT6YtWjzqtIkf3hwXUVxJsTnBG4xmrfvl -41pgNJpgu99YOYqPpS0g7A== ------END DSA PARAMETERS----- ------BEGIN DSA PRIVATE KEY----- -MIH5AgEAAkEAwebEoGBfokKQeALHHnAZMQwYU35ILEBdV8oUmzv7qpSVUoHihyqf -n6GCOixAKSP8EJYcTilIqPbFbfFyOPlbLwIVANoFHEDiQgknAvKrG78pHzAJdQSP -AkEAqfka5Bnl+CeEMpzVZGrOVqZE/LFdZK9eT6YtWjzqtIkf3hwXUVxJsTnBG4xm -rfvl41pgNJpgu99YOYqPpS0g7AJATQ2LUzjGQSM6UljcPY5I2OD9THkUR9kH2tth -zZd70UoI9btrVaTizgqYShuok94glSQNK0H92JgUk3scJPaAkAIVAMDn61h6vrCE -mNv063So6E+eYaIN ------END DSA PRIVATE KEY----- diff --git a/certbot/tests/testdata/dsa_cert.pem b/certbot/tests/testdata/dsa_cert.pem deleted file mode 100644 index ef94536e7..000000000 --- a/certbot/tests/testdata/dsa_cert.pem +++ /dev/null @@ -1,17 +0,0 @@ ------BEGIN CERTIFICATE----- -MIICuDCCAnWgAwIBAgIJAPjmErVMzwVLMAsGCWCGSAFlAwQDAjB3MQswCQYDVQQG -EwJVUzERMA8GA1UECAwITWljaGlnYW4xEjAQBgNVBAcMCUFubiBBcmJvcjErMCkG -A1UECgwiVW5pdmVyc2l0eSBvZiBNaWNoaWdhbiBhbmQgdGhlIEVGRjEUMBIGA1UE -AwwLZXhhbXBsZS5jb20wHhcNMTUwNTEyMTUzOTQzWhcNMTUwNjExMTUzOTQzWjB3 -MQswCQYDVQQGEwJVUzERMA8GA1UECAwITWljaGlnYW4xEjAQBgNVBAcMCUFubiBB -cmJvcjErMCkGA1UECgwiVW5pdmVyc2l0eSBvZiBNaWNoaWdhbiBhbmQgdGhlIEVG -RjEUMBIGA1UEAwwLZXhhbXBsZS5jb20wgfEwgakGByqGSM44BAEwgZ0CQQDB5sSg -YF+iQpB4AscecBkxDBhTfkgsQF1XyhSbO/uqlJVSgeKHKp+foYI6LEApI/wQlhxO -KUio9sVt8XI4+VsvAhUA2gUcQOJCCScC8qsbvykfMAl1BI8CQQCp+RrkGeX4J4Qy -nNVkas5WpkT8sV1kr15Ppi1aPOq0iR/eHBdRXEmxOcEbjGat++XjWmA0mmC731g5 -io+lLSDsA0MAAkBNDYtTOMZBIzpSWNw9jkjY4P1MeRRH2Qfa22HNl3vRSgj1u2tV -pOLOCphKG6iT3iCVJA0rQf3YmBSTexwk9oCQo1AwTjAdBgNVHQ4EFgQUZ2DlTDGU -PMwTUt0KztM6IyX61BcwHwYDVR0jBBgwFoAUZ2DlTDGUPMwTUt0KztM6IyX61Bcw -DAYDVR0TBAUwAwEB/zALBglghkgBZQMEAwIDMAAwLQIVAIbMgGx+KwBr4rgqZ2Lh -AAO8TegHAhQsuxpIIIphiReoWEtEJk4TqEIz/A== ------END CERTIFICATE----- diff --git a/certbot/tests/testdata/matching_cert.pem b/certbot/tests/testdata/matching_cert.pem deleted file mode 100644 index fda9cb1f4..000000000 --- a/certbot/tests/testdata/matching_cert.pem +++ /dev/null @@ -1,14 +0,0 @@ ------BEGIN CERTIFICATE----- -MIICNzCCAeGgAwIBAgIJALizm9Y3q620MA0GCSqGSIb3DQEBCwUAMHcxCzAJBgNV -BAYTAlVTMREwDwYDVQQIDAhNaWNoaWdhbjESMBAGA1UEBwwJQW5uIEFyYm9yMSsw -KQYDVQQKDCJVbml2ZXJzaXR5IG9mIE1pY2hpZ2FuIGFuZCB0aGUgRUZGMRQwEgYD -VQQDDAtleGFtcGxlLmNvbTAeFw0xNTA1MDkwMDI0NTJaFw0xNjA1MDgwMDI0NTJa -MHcxCzAJBgNVBAYTAlVTMREwDwYDVQQIDAhNaWNoaWdhbjESMBAGA1UEBwwJQW5u -IEFyYm9yMSswKQYDVQQKDCJVbml2ZXJzaXR5IG9mIE1pY2hpZ2FuIGFuZCB0aGUg -RUZGMRQwEgYDVQQDDAtleGFtcGxlLmNvbTBcMA0GCSqGSIb3DQEBAQUAA0sAMEgC -QQD0thFxUTc2v6qV55wRxfwnBUOeN4bVfu5ywJqy65kzR7T1yZi5TPEiQyM7/3Hg -BVy9ddFc8RX4vNZaR+ROXNEzAgMBAAGjUDBOMB0GA1UdDgQWBBRJieHEVSHKmBk0 -mTExx1erzlylCjAfBgNVHSMEGDAWgBRJieHEVSHKmBk0mTExx1erzlylCjAMBgNV -HRMEBTADAQH/MA0GCSqGSIb3DQEBCwUAA0EABT/nlpqOaanFSLZmWIrKv0zt63k4 -bmWNMA8fYT45KYpLomsW8qXdpC82IlVKfNk7fW0UYT3HOeDSJRcycxNCTQ== ------END CERTIFICATE----- diff --git a/certbot/tests/testdata/rsa512_key_2.pem b/certbot/tests/testdata/rsa512_key_2.pem deleted file mode 100644 index 709b6d8e3..000000000 --- a/certbot/tests/testdata/rsa512_key_2.pem +++ /dev/null @@ -1,9 +0,0 @@ ------BEGIN RSA PRIVATE KEY----- -MIIBOwIBAAJBAPS2EXFRNza/qpXnnBHF/CcFQ543htV+7nLAmrLrmTNHtPXJmLlM -8SJDIzv/ceAFXL110VzxFfi81lpH5E5c0TMCAwEAAQJBALmppYQ/JVARjWBcsEm/ -1/bXBJ127YLv4gQIY5baL4r6IdEE33OXMTTmD9wf+ajuq1eaH0htHkwhOvREu0sz -bskCIQD/Cg+xhEVLcwK3pFp3afPIhj1IPFiL3Uy/nqyMZ6O/RQIhAPWiDBofp7Cp -J4dGZs+hkRySq/IOeeRJlNK1Pq64nToXAiBZ7+te1100YSd5KT051SRB94zO13EG -SZESFduVW8rz3QIgK+tLiqg6TYYRQUi/PUTAM4GuKNuZw828RGiPyqHLywUCIQCd -pkZrNphL/y0D7HSbPIfZzD90M2V8tUjlK0BTqk1bHA== ------END RSA PRIVATE KEY----- diff --git a/certbot/tests/util.py b/certbot/tests/util.py index a36f0f6ac..4ecddc34f 100644 --- a/certbot/tests/util.py +++ b/certbot/tests/util.py @@ -22,6 +22,7 @@ from certbot import constants from certbot import interfaces from certbot import storage from certbot import util +from certbot import configuration from certbot.display import util as display_util @@ -246,6 +247,20 @@ class TempDirTestCase(unittest.TestCase): def tearDown(self): shutil.rmtree(self.tempdir) +class ConfigTestCase(TempDirTestCase): + """Test class which sets up a NamespaceConfig object. + + """ + def setUp(self): + super(ConfigTestCase, self).setUp() + self.config = configuration.NamespaceConfig( + mock.MagicMock( + config_dir=os.path.join(self.tempdir, 'config'), + work_dir=os.path.join(self.tempdir, 'work'), + logs_dir=os.path.join(self.tempdir, 'logs'), + server="example.com", + ) + ) def lock_and_call(func, lock_path): """Grab a lock for lock_path and call func. diff --git a/docs/challenges.rst b/docs/challenges.rst index 6e3e260dc..95c3b56a8 100644 --- a/docs/challenges.rst +++ b/docs/challenges.rst @@ -72,8 +72,8 @@ TLS-SNI-01 Challenge * When using the Apache plugin, make sure you are running Apache and no other web server on port 443. * When using the NGINX plugin, make sure you are running NGINX and no other web server on port 443. * With either the Apache or NGINX plugin, certbot modifies your web server configuration. If you get - an installation error then you have received a certificate but the plugin was unable to modify - your web server configuration, meaning that you'll have to install the certificate manually. + an error after successfully completing the challenge, then you have received a certificate but the + plugin was unable to modify your web server configuration, meaning that you'll have to install the certificate manually. In that case, please file a bug to help us improve certbot! * When using the Standalone plugin, make sure another program is not already listening to port 443 on the server. @@ -83,4 +83,3 @@ DNS-01 Challenge * When using the manual plugin, make sure your DNS records are correctly updated; you must be able to make appropriate changes to your DNS zone in order to pass the challenge. - diff --git a/docs/cli-help.txt b/docs/cli-help.txt index 6ff284788..a00fe6c65 100644 --- a/docs/cli-help.txt +++ b/docs/cli-help.txt @@ -39,6 +39,12 @@ optional arguments: -v, --verbose This flag can be used multiple times to incrementally increase the verbosity of output, e.g. -vvv. (default: -2) + --max-log-backups MAX_LOG_BACKUPS + Specifies the maximum number of backup logs that + should be kept by Certbot's built in log rotation. + Setting this flag to 0 disables log rotation entirely, + causing Certbot to always append to the same log file. + (default: 1000) -n, --non-interactive, --noninteractive Run without ever asking for user input. This may require additional command line flags; the client will @@ -68,8 +74,8 @@ optional arguments: reloads webservers to deploy and then roll back those changes. It also calls --pre-hook and --post-hook commands if they are defined because they may be - necessary to accurately simulate renewal. --renew-hook - commands are not called. (default: False) + necessary to accurately simulate renewal. --deploy- + hook commands are not called. (default: False) --debug-challenges After setting up challenges, wait for user input before submitting to CA (default: False) --preferred-challenges PREF_CHALLS @@ -89,12 +95,18 @@ 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.16.0 (certbot; - Ubuntu 16.04.2 LTS) Authenticator/XXX Installer/YYY + "". (default: CertbotACMEClient/0.17.0 (certbot; + Ubuntu 16.04.3 LTS) Authenticator/XXX Installer/YYY (SUBCOMMAND; flags: FLAGS) Py/2.7.12). The flags encoded in the user agent are: --duplicate, --force- renew, --allow-subset-of-names, -n, and whether any hooks are set. + --user-agent-comment USER_AGENT_COMMENT + Add a comment to the default user agent string. May be + used when repackaging Certbot or calling it from + another tool to allow additional statistical data to + be collected. Ignored if --user-agent is set. + (Example: Foo-Wrapper/1.0) (default: None) automation: Arguments for automating execution & other tweaks @@ -274,10 +286,10 @@ renew: attempt was made to obtain/renew a certificate. If multiple renewed certificates have identical post- hooks, only one will be run. (default: None) - --renew-hook RENEW_HOOK + --deploy-hook DEPLOY_HOOK Command to be run in a shell once for each - successfully renewed certificate. For this command, - the shell variable $RENEWED_LINEAGE will point to the + successfully issued certificate. For this command, the + shell variable $RENEWED_LINEAGE will point to the config live subdirectory (for example, "/etc/letsencrypt/live/example.com") containing the new certificates and keys; the shell variable @@ -286,7 +298,7 @@ renew: "example.com www.example.com" (default: None) --disable-hook-validation Ordinarily the commands specified for --pre-hook - /--post-hook/--renew-hook will be checked for + /--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/docs/install.rst b/docs/install.rst index e75a8b3e2..2b12edb47 100644 --- a/docs/install.rst +++ b/docs/install.rst @@ -30,7 +30,7 @@ configurations (if you use the ``apache`` or ``nginx`` plugins). If none of these apply to you, it is theoretically possible to run without root privileges, but for most users who want to avoid running an ACME client as root, either `letsencrypt-nosudo `_ or -`simp_le `_ are more appropriate choices. +`simp_le `_ are more appropriate choices. The Apache plugin currently requires an OS with augeas version 1.0; currently `it supports @@ -234,11 +234,10 @@ Installing from source Installation from source is only supported for developers and the whole process is described in the :doc:`contributing`. -.. warning:: Please do **not** use ``python setup.py install`` or - ``python pip install .``. Please do **not** attempt the - installation commands as superuser/root and/or without virtual - environment, 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! +.. warning:: Please do **not** use ``python setup.py install``, ``python pip + install .``, or ``easy_install .``. Please do **not** attempt the + installation commands as superuser/root and/or without virtual environment, + 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! diff --git a/docs/using.rst b/docs/using.rst index ef81f400e..11915d896 100644 --- a/docs/using.rst +++ b/docs/using.rst @@ -61,7 +61,7 @@ manual_ Y N | Helps you obtain a certificate by giving you instruction =========== ==== ==== =============================================================== ============================= 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), +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 port 53, though that's often not the same machine as your webserver). A few plugins support more than one challenge type, in which case you can choose one @@ -102,7 +102,7 @@ If you're getting a certificate for many domains at once, the plugin needs to know where each domain's files are served from, which could potentially be a separate directory for each domain. When requesting a certificate for multiple domains, each domain will use the most recently -specified ``--webroot-path``. So, for instance, +specified ``--webroot-path``. So, for instance, :: @@ -332,7 +332,7 @@ Example: .. code-block:: none - certbot --expand -d existing.com example.com,newdomain.com + certbot --expand -d existing.com,example.com,newdomain.com If you prefer, you can specify the domains individually like this: @@ -383,6 +383,12 @@ use the ``revoke`` command to do so. Note that the ``revoke`` command takes the certbot revoke --cert-path /etc/letsencrypt/live/CERTNAME/cert.pem +You can also specify the reason for revoking your certificate by using the ``reason`` flag. +Reasons include ``unspecified`` which is the default, as well as ``keycompromise``, +``affiliationchanged``, ``superseded``, and ``cessationofoperation``:: + + certbot revoke --cert-path /etc/letsencrypt/live/CERTNAME/cert.pem --reason keycompromise + Additionally, if a certificate is a test certificate obtained via the ``--staging`` or ``--test-cert`` flag, that flag must be passed to the ``revoke`` subcommand. @@ -538,8 +544,15 @@ commands into your individual environment. Modifying the Renewal Configuration File ---------------------------------------- +When a certificate is issued, by default Certbot creates a renewal configuration file that +tracks the options that were selected when Certbot was run. This allows Certbot +to use those same options again when it comes time for renewal. These renewal +configuration files are located at ``/etc/letsencrypt/renewal/CERTNAME``. + For advanced certificate management tasks, it is possible to manually modify the certificate's -renewal configuration file, located at ``/etc/letsencrypt/renewal/CERTNAME``. +renewal configuration file, but this is discouraged since it can easily break Certbot's +ability to renew your certificates. If you choose to modify the renewal configuration file +we advise you to test its validity with the ``certbot renew --dry-run`` command. .. warning:: Modifying any files in ``/etc/letsencrypt`` can damage them so Certbot can no longer properly manage its certificates, and we do not recommend doing so. @@ -790,7 +803,12 @@ of Certbot that you would like to run. Configuration file ================== -It is possible to specify configuration file with +Certbot accepts a global configuration file that applies its options to all invocations +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 +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: @@ -804,6 +822,13 @@ By default, the following locations are searched: ``~/.config/letsencrypt/cli.ini`` if ``$XDG_CONFIG_HOME`` is not set). +Since this configuration file applies to all invocations of certbot it is incorrect +to list domains in it. Listing domains in cli.ini may prevent renewal from working. +Additionally due to how arguments in cli.ini are parsed, options which wish to +not be set should not be listed. Options set to false will instead be read +as being set to true by older versions of Certbot, since they have been listed +in the config file. + .. keep it up to date with constants.py .. _log-rotation: @@ -823,7 +848,7 @@ changed by passing the desired number to the command line flag Certbot command-line options ============================ -Certbot supports a lot of command line options. Here's the full list, from +Certbot supports a lot of command line options. Here's the full list, from ``certbot --help all``: .. literalinclude:: cli-help.txt diff --git a/letsencrypt-auto b/letsencrypt-auto index 599538891..b935ed447 100755 --- a/letsencrypt-auto +++ b/letsencrypt-auto @@ -28,7 +28,7 @@ if [ -z "$VENV_PATH" ]; then VENV_PATH="$XDG_DATA_HOME/$VENV_NAME" fi VENV_BIN="$VENV_PATH/bin" -LE_AUTO_VERSION="0.16.0" +LE_AUTO_VERSION="0.17.0" BASENAME=$(basename $0) USAGE="Usage: $BASENAME [OPTIONS] A self-updating wrapper script for the Certbot ACME client. When run, updates @@ -200,6 +200,25 @@ ExperimentalBootstrap() { fi } +DeprecationBootstrap() { + # Arguments: Platform name, bootstrap function name + if [ "$DEBUG" = 1 ]; then + if [ "$2" != "" ]; then + BootstrapMessage $1 + $2 + fi + else + error "WARNING: certbot-auto support for this $1 is DEPRECATED!" + error "Please visit certbot.eff.org to learn how to download a version of" + error "Certbot that is packaged for your system. While an existing version" + error "of certbot-auto may work currently, we have stopped supporting updating" + error "system packages for your system. Please switch to a packaged version" + error "as soon as possible." + exit 1 + fi +} + + DeterminePythonVersion() { for LE_PYTHON in "$LE_PYTHON" python2.7 python27 python2 python; do # Break (while keeping the LE_PYTHON value) if found. @@ -630,11 +649,11 @@ Bootstrap() { elif [ -f /etc/manjaro-release ]; then ExperimentalBootstrap "Manjaro Linux" BootstrapArchCommon elif [ -f /etc/gentoo-release ]; then - ExperimentalBootstrap "Gentoo" BootstrapGentooCommon + DeprecationBootstrap "Gentoo" BootstrapGentooCommon elif uname | grep -iq FreeBSD ; then - ExperimentalBootstrap "FreeBSD" BootstrapFreeBsd + DeprecationBootstrap "FreeBSD" BootstrapFreeBsd elif uname | grep -iq Darwin ; then - ExperimentalBootstrap "macOS" BootstrapMac + DeprecationBootstrap "macOS" BootstrapMac elif [ -f /etc/issue ] && grep -iq "Amazon Linux" /etc/issue ; then ExperimentalBootstrap "Amazon Linux" BootstrapRpmCommon elif [ -f /etc/product ] && grep -q "Joyent Instance" /etc/product ; then @@ -751,25 +770,37 @@ ConfigArgParse==0.10.0 \ --hash=sha256:3b50a83dd58149dfcee98cb6565265d10b53e9c0a2bca7eeef7fb5f5524890a7 configobj==5.0.6 \ --hash=sha256:a2f5650770e1c87fb335af19a9b7eb73fc05ccf22144eb68db7d00cd2bcb0902 -cryptography==1.9 \ - --hash=sha256:469a9d3d851038f1eb7d7f77bb08bb4775b41483372be450e25b293fe57bd59e \ - --hash=sha256:533143321d15c8743f91eec5c5f495c1b5cad9a25de8f6dab01eddd6b416903e \ - --hash=sha256:2230c186182d773064d06242e0fa604cd718bfff28aa9c5ae73d7e426e98a151 \ - --hash=sha256:3dc94ed5a26b8553a325767358f505c0a43e0c89df078647f77a4d022ddcdc57 \ - --hash=sha256:2eb8297b877cb6b56216750fa7017c9f5786bec8afd6a0f1aaace02cbfb6195f \ - --hash=sha256:025a96e48164106f2082b00f42bf430cf21f09e203e42585a712e420b75cbff0 \ - --hash=sha256:61eb3534f8ed2415dd708b28919205d523f220e4cd5b8165844edfdd4a649b8e \ - --hash=sha256:5474fe5ce6b517d3086e0231b6ad88f8978c551c4379f91c3d619c308490f0d7 \ - --hash=sha256:5ff169869624e23767d70db274f13a9ea4e97c029425a1224aa5e049e84ce2af \ - --hash=sha256:c26e057a2de13e97e708328d295c5ac4cd3eab4a5c42ce727dd1a53316034b8a \ - --hash=sha256:7db719432648f14ea33edffc5f75330c595804eac86ca916528b35ce50a8bfd6 \ - --hash=sha256:ca72537a7064bb80e34b6908e576ffc8e2c2cad29a7168f48d0999df089695c3 \ - --hash=sha256:2a5e577f5d2093e51486b4ec02b51bb5adb165b98fee99929d5af0813e90b469 \ - --hash=sha256:9d9da8bac2e31003d092f5ef6981a725c77c4e9a30638436884d61ad39f9a1ee \ - --hash=sha256:fab8ec6866e384d9827d5dc02a1383e991a6c05c54f818316c4b829e56ca2ba3 \ - --hash=sha256:365eb804362e581c9a02e7a610b30514f07dd77b2a38aed338eb5192446bbc58 \ - --hash=sha256:2908f709f02711dbb10561a9f154d2f7d1792385e2341e9708539cc4ecfb8667 \ - --hash=sha256:5518337022718029e367d982642f3e3523541e098ad671672a90b82474c84882 +cryptography==2.0.2 \ + --hash=sha256:187ae17358436d2c760f28c2aeb02fefa3f37647a9c5b6f7f7c3e83cd1c5a972 \ + --hash=sha256:19e43a13bbf52028dd1e810c803f2ad8880d0692d772f98d42e1eaf34bdee3d6 \ + --hash=sha256:da9291502cbc87dc0284a20c56876e4d2e68deac61cc43df4aec934e44ca97b1 \ + --hash=sha256:0954f8813095f581669330e0a2d5e726c33ac7f450c1458fac58bab54595e516 \ + --hash=sha256:d68b0cc40a8432ed3fc84876c519de704d6001800ec22b136e75ae841910c45b \ + --hash=sha256:2f8ad9580ab4da645cfea52a91d2da99a49a1e76616d8be68441a986fad652b0 \ + --hash=sha256:cc00b4511294f5f6b65c4e77a1a9c62f52490a63d2c120f3872176b40a82351e \ + --hash=sha256:cf896020f6a9f095a547b3d672c8db1ef2ed71fca11250731fa1d4a4cb8b1590 \ + --hash=sha256:e0fdb8322206fa02aa38f71519ff75dce2eb481b7e1110e2936795cb376bb6ee \ + --hash=sha256:277538466657ca5d6637f80be100242f9831d75138b788d718edd3aab34621f8 \ + --hash=sha256:2c77eb0560f54ce654ab82d6b2a64327a71ee969b29022bf9746ca311c9f5069 \ + --hash=sha256:755a7853b679e79d0a799351c092a9b0271f95ff54c8dd8823d8b527a2926a86 \ + --hash=sha256:77197a2d525e761cdd4c771180b4bd0d80703654c6385e4311cbbbe2beb56fa1 \ + --hash=sha256:eb8bb79d0ab00c931c8333b745f06fec481a51c52d70acd4ee95d6093ba5c386 \ + --hash=sha256:131f61de82ef28f3e20beb4bfc24f9692d28cecfd704e20e6c7f070f7793013a \ + --hash=sha256:ac35435974b2e27cd4520f29c191d7da36f4189aa3264e52c4c6c6d089ab6142 \ + --hash=sha256:04b6ea99daa2a8460728794213d76d45ad58ea247dc7e7ff148d7dd726e87863 \ + --hash=sha256:2b9442f8b4c3d575f6cc3db0e856034e0f5a9d55ecd636f52d8c496795b26952 \ + --hash=sha256:b3d3b3ecba1fe1bdb6f180770a137f877c8f07571f7b2934bb269475bcf0e5e8 \ + --hash=sha256:670a58c0d75cb0e78e73dd003bd96d4440bbb1f2bc041dcf7b81767ca4fb0ce9 \ + --hash=sha256:5af84d23bdb86b5e90aca263df1424b43f1748480bfcde3ac2a3cbe622612468 \ + --hash=sha256:ba22e8eefabdd7aca37d0c0c00d2274000d2cebb5cce9e5a710cb55bf8797b31 \ + --hash=sha256:b798b22fa7e92b439547323b8b719d217f1e1b7677585cfeeedf3b55c70bb7fb \ + --hash=sha256:59cff28af8cce96cb7e94a459726e1d88f6f5fa75097f9dcbebd99118d64ea4c \ + --hash=sha256:fe859e445abc9ba9e97950ddafb904e23234c4ecb76b0fae6c86e80592ce464a \ + --hash=sha256:655f3c474067f1e277430f23cc0549f0b1dc99b82aec6e53f80b9b2db7f76f11 \ + --hash=sha256:0ebc2be053c9a03a2f3e20a466e87bf12a51586b3c79bd2a22171b073a805346 \ + --hash=sha256:01e6e60654df64cca53733cda39446d67100c819c181d403afb120e0d2a71e1b \ + --hash=sha256:d46f4e5d455cb5563685c52ef212696f0a6cc1ea627603218eabbd8a095291d8 \ + --hash=sha256:3780b2663ee7ebb37cb83263326e3cd7f8b2ea439c448539d4b87de12c8d06ab enum34==1.1.2 \ --hash=sha256:2475d7fcddf5951e92ff546972758802de5260bf409319a9f1934e6bbc8b1dc7 \ --hash=sha256:35907defb0f992b75ab7788f65fedc1cf20ffa22688e0e6f6f12afc06b3ea501 @@ -876,18 +907,18 @@ letsencrypt==0.7.0 \ --hash=sha256:105a5fb107e45bcd0722eb89696986dcf5f08a86a321d6aef25a0c7c63375ade \ --hash=sha256:c36e532c486a7e92155ee09da54b436a3c420813ec1c590b98f635d924720de9 -certbot==0.16.0 \ - --hash=sha256:e1cc2479f4f149a7128b67192c3f5f6c111b6b9ddcac421ebee8ac5030d5b09b \ - --hash=sha256:795801fd6b06b32060e364ac045312e6b26c6272d5ca32878277e5a2afdee186 -acme==0.16.0 \ - --hash=sha256:31592a744f7a6e7cbb4c5daf2deebc9af6b03997d6f80e5becc203ab8694edcf \ - --hash=sha256:538b69134cc50bdcdcc081b844c85158509022c61d7abc1f44216beeb95de9cd -certbot-apache==0.16.0 \ - --hash=sha256:337f84f391c7d7c31d84376e7a8c882ac119112a4aabceadd1a7de6a2f01ca06 \ - --hash=sha256:f1f37b56e1ceb0a28ccf51d54ca34444e6a708448845ef25372e223b9a82c4d4 -certbot-nginx==0.16.0 \ - --hash=sha256:1d57428202f8e7abdd99c3ddbcf374aad5f69c4262fe8dae53d2016e4ae04ded \ - --hash=sha256:77711655514d707ddf728195f00b2f6cdd1ad9db52440276b4a67664479c6954 +certbot==0.17.0 \ + --hash=sha256:64c25c7123357feffded6408660bc6f5c7d493dd635ae172081d21473075a86a \ + --hash=sha256:43f5b26c3f314d14babf79a3bdf3522e4fc9eef867a0681c426f113c650a669c +acme==0.17.0 \ + --hash=sha256:501710171633af13fc52aa61d0277a6fe335f7477db5810e72239aaf4f3a09e7 \ + --hash=sha256:3ccbe4aaeb98c77b98ee4093b4e4adb76a1a24cbdfec0130c489c206f1d9b66e +certbot-apache==0.17.0 \ + --hash=sha256:17a7e8d7526d838610e68b96cf052af17c4055655b76b06d1cbc74857d90a216 \ + --hash=sha256:29b9e7bc5eaaff6dc4bce8398e35eeacdf346126aad68cac3d41bb87df20a6b9 +certbot-nginx==0.17.0 \ + --hash=sha256:980c9a33a79ab839a089a0085ff0c5414f01f47b6db26ed342df25916658cec9 \ + --hash=sha256:e573f8b4283172755c07b9cca8a8da7ef2d31b4df763881394b5339b2d42994a UNLIKELY_EOF # ------------------------------------------------------------------------- @@ -915,6 +946,7 @@ anything goes wrong, it will exit with a non-zero status code. # The above copyright notice and this permission notice shall be included in # all copies or substantial portions of the Software. from __future__ import print_function +from distutils.version import StrictVersion from hashlib import sha256 from os.path import join from pipes import quote @@ -949,12 +981,14 @@ except ImportError: from urllib.parse import urlparse # 3.4 -__version__ = 1, 1, 1 +__version__ = 1, 3, 0 +PIP_VERSION = '9.0.1' # wheel has a conditional dependency on argparse: maybe_argparse = ( - [('https://pypi.python.org/packages/source/a/argparse/' + [('https://pypi.python.org/packages/18/dd/' + 'e617cfc3f6210ae183374cd9f6a26b20514bbb5a792af97949c5aacddf0f/' 'argparse-1.4.0.tar.gz', '62b089a55be1d8949cd2bc7e0df0bddb9e028faefc8c32038cc84862aefdd6e4')] if version_info < (2, 7, 0) else []) @@ -962,13 +996,19 @@ maybe_argparse = ( PACKAGES = maybe_argparse + [ # Pip has no dependencies, as it vendors everything: - ('https://pypi.python.org/packages/source/p/pip/pip-8.0.3.tar.gz', - '30f98b66f3fe1069c529a491597d34a1c224a68640c82caf2ade5f88aa1405e8'), + ('https://pypi.python.org/packages/11/b6/' + 'abcb525026a4be042b486df43905d6893fb04f05aac21c32c638e939e447/' + 'pip-{0}.tar.gz' + .format(PIP_VERSION), + '09f243e1a7b461f654c26a725fa373211bb7ff17a9300058b205c61658ca940d'), # This version of setuptools has only optional dependencies: - ('https://pypi.python.org/packages/source/s/setuptools/' + ('https://pypi.python.org/packages/69/65/' + '4c544cde88d4d876cdf5cbc5f3f15d02646477756d89547e9a7ecd6afa76/' 'setuptools-20.2.2.tar.gz', '24fcfc15364a9fe09a220f37d2dcedc849795e3de3e4b393ee988e66a9cbd85a'), - ('https://pypi.python.org/packages/source/w/wheel/wheel-0.29.0.tar.gz', + ('https://pypi.python.org/packages/c9/1d/' + 'bd19e691fd4cfe908c76c429fe6e4436c9e83583c4414b54f6c85471954a/' + 'wheel-0.29.0.tar.gz', '1ebb8ad7e26b448e9caa4773d2357849bf80ff9e313964bcaf79cbf0201a1648') ] @@ -1018,11 +1058,21 @@ def hashed_download(url, temp, digest): def main(): + pip_version = StrictVersion(check_output(['pip', '--version']) + .decode('utf-8').split()[1]) + min_pip_version = StrictVersion(PIP_VERSION) + if pip_version >= min_pip_version: + return 0 + has_pip_cache = pip_version >= StrictVersion('6.0') + temp = mkdtemp(prefix='pipstrap-') try: downloads = [hashed_download(url, temp, digest) for url, 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: + ('--no-cache-dir ' if has_pip_cache else '') + ' '.join(quote(d) for d in downloads), shell=True) except HashError as exc: @@ -1045,9 +1095,9 @@ UNLIKELY_EOF PATH="$VENV_BIN:$PATH" "$VENV_BIN/python" "$TEMP_DIR/pipstrap.py" set +e if [ "$VERBOSE" = 1 ]; then - "$VENV_BIN/pip" install --no-cache-dir --require-hashes -r "$TEMP_DIR/letsencrypt-auto-requirements.txt" + "$VENV_BIN/pip" install --disable-pip-version-check --no-cache-dir --require-hashes -r "$TEMP_DIR/letsencrypt-auto-requirements.txt" else - PIP_OUT=`"$VENV_BIN/pip" install --no-cache-dir --require-hashes -r "$TEMP_DIR/letsencrypt-auto-requirements.txt" 2>&1` + PIP_OUT=`"$VENV_BIN/pip" install --disable-pip-version-check --no-cache-dir --require-hashes -r "$TEMP_DIR/letsencrypt-auto-requirements.txt" 2>&1` fi PIP_STATUS=$? set -e diff --git a/letsencrypt-auto-source/certbot-auto.asc b/letsencrypt-auto-source/certbot-auto.asc index aa713c8c0..36afe0eba 100644 --- a/letsencrypt-auto-source/certbot-auto.asc +++ b/letsencrypt-auto-source/certbot-auto.asc @@ -1,11 +1,11 @@ -----BEGIN PGP SIGNATURE----- Version: GnuPG v2 -iQEcBAABCAAGBQJZXV5PAAoJEE0XyZXNl3Xy3IgH/1OiY21IcAGx13jW32KNIsf9 -/vqjR/fyPbraSzYDldCVIQxsaVA6oh2kNZgiB11OpPT20/WsDq1+Ymj+dMjonJfm -w3wjx5AnfR/YThQwNXUJ83XnPCA78CtYiXus9gyqk+10WpNXUkbdGOwM4eYOtb3o -fNjJXkA00pATIYXks6qV0WJVEDNYuHDfkDfxukVgU7HzjfayLQjo4Zbs7Qnp7oH9 -+Lizc11nTliUUo3hHyLziJsCfC+Irso5Q/fHM9rn9DS360PcW0uNjWeLQk0U1w1l -tuuObOsDi/7Rejk4uDu6qDVemdEoG1btCrZgzOvzi3NstWigrL76cZuoz0gaGu4= -=mN1c +iQEcBAABCAAGBQJZgRYdAAoJEE0XyZXNl3XyNskIAMh/M3tV8PTieSrMr3uzLua8 +R+tQJV31WlraoKGQAkZ9Ak+nEhJy0bOi3QAeOmEnS15sBM6ruD+UCfwUDrZxolfW +5Fnue2ocym+MhfDNKoerQNAmaaHY8sutoR+RNTegFyfyr92zMDZVzPm/DFAAHbK+ +eJltSx2Jleaig4V/RcKpkCwHErjQxn6Tn4jHlafAdNL28tEIGXcExpRj4raw3X1L +SoTq/yJiWe+M7t+1iBRVEMZHY1b47PbTo1ipKF/ZZ3Hrz5JKRhAKcA8diHlWp+1I +ujAfU4uu0hR+C3wcpeJ1i2YdS4S9y6uMGyIWU5toJfYdolTSGRZ2lPB+x5Um9pw= +=/7P7 -----END PGP SIGNATURE----- diff --git a/letsencrypt-auto-source/letsencrypt-auto b/letsencrypt-auto-source/letsencrypt-auto index 023d5044e..fe3f3b924 100755 --- a/letsencrypt-auto-source/letsencrypt-auto +++ b/letsencrypt-auto-source/letsencrypt-auto @@ -23,12 +23,15 @@ fi if [ -z "$XDG_DATA_HOME" ]; then XDG_DATA_HOME=~/.local/share fi -VENV_NAME="letsencrypt" if [ -z "$VENV_PATH" ]; then - VENV_PATH="$XDG_DATA_HOME/$VENV_NAME" + # We export these values so they are preserved properly if this script is + # rerun with sudo/su where $HOME/$XDG_DATA_HOME may have a different value. + export OLD_VENV_PATH="$XDG_DATA_HOME/letsencrypt" + export VENV_PATH="/opt/eff.org/certbot/venv" fi VENV_BIN="$VENV_PATH/bin" -LE_AUTO_VERSION="0.17.0.dev0" +BOOTSTRAP_VERSION_PATH="$VENV_PATH/certbot-auto-bootstrap-version.txt" +LE_AUTO_VERSION="0.18.0.dev0" BASENAME=$(basename $0) USAGE="Usage: $BASENAME [OPTIONS] A self-updating wrapper script for the Certbot ACME client. When run, updates @@ -49,6 +52,7 @@ Help for certbot itself cannot be provided until it is installed. implies --non-interactive All arguments are accepted and forwarded to the Certbot client when run." +export CERTBOT_AUTO="$0" for arg in "$@" ; do case "$arg" in @@ -77,7 +81,7 @@ for arg in "$@" ; do h) HELP=1;; n) - ASSUME_YES=1;; + NONINTERACTIVE=1;; q) QUIET=1;; v) @@ -93,8 +97,8 @@ if [ $BASENAME = "letsencrypt-auto" ]; then HELP=0 fi -# Set ASSUME_YES to 1 if QUIET (i.e. --quiet implies --non-interactive) -if [ "$QUIET" = 1 ]; then +# Set ASSUME_YES to 1 if QUIET or NONINTERACTIVE +if [ "$QUIET" = 1 -o "$NONINTERACTIVE" = 1 ]; then ASSUME_YES=1 fi @@ -119,16 +123,18 @@ else exit 1 fi -# certbot-auto needs root access to bootstrap OS dependencies, and -# certbot itself needs root access for almost all modes of operation -# The "normal" case is that sudo is used for the steps that need root, but -# this script *can* be run as root (not recommended), or fall back to using -# `su`. Auto-detection can be overridden by explicitly setting the -# environment variable LE_AUTO_SUDO to 'sudo', 'sudo_su' or '' as used below. +# Certbot itself needs root access for almost all modes of operation. +# certbot-auto needs root access to bootstrap OS dependencies and install +# Certbot at a protected path so it can be safely run as root. To accomplish +# this, this script will attempt to run itself as root if it doesn't have the +# necessary privileges by using `sudo` or falling back to `su` if it is not +# available. The mechanism used to obtain root access can be set explicitly by +# setting the environment variable LE_AUTO_SUDO to 'sudo', 'su', 'su_sudo', +# 'SuSudo', or '' as used below. # Because the parameters in `su -c` has to be a string, # we need to properly escape it. -su_sudo() { +SuSudo() { args="" # This `while` loop iterates over all parameters given to this function. # For each parameter, all `'` will be replace by `'"'"'`, and the escaped string @@ -147,34 +153,47 @@ su_sudo() { su root -c "$args" } -SUDO_ENV="" -export CERTBOT_AUTO="$0" -if [ -n "${LE_AUTO_SUDO+x}" ]; then - case "$LE_AUTO_SUDO" in - su_sudo|su) - SUDO=su_sudo - ;; - sudo) - SUDO=sudo - SUDO_ENV="CERTBOT_AUTO=$0" - ;; - '') ;; # Nothing to do for plain root method. - *) - error "Error: unknown root authorization mechanism '$LE_AUTO_SUDO'." - exit 1 - esac - say "Using preset root authorization mechanism '$LE_AUTO_SUDO'." -else - if test "`id -u`" -ne "0" ; then - if $EXISTS sudo 1>/dev/null 2>&1; then - SUDO=sudo - SUDO_ENV="CERTBOT_AUTO=$0" - else - say \"sudo\" is not available, will use \"su\" for installation steps... - SUDO=su_sudo - fi +# Sets the environment variable SUDO to be the name of the program or function +# to call to get root access. If this script already has root privleges, SUDO +# is set to an empty string. The value in SUDO should be run with the command +# to called with root privileges as arguments. +SetRootAuthMechanism() { + SUDO="" + if [ -n "${LE_AUTO_SUDO+x}" ]; then + case "$LE_AUTO_SUDO" in + SuSudo|su_sudo|su) + SUDO=SuSudo + ;; + sudo) + SUDO="sudo -E" + ;; + '') ;; # Nothing to do for plain root method. + *) + error "Error: unknown root authorization mechanism '$LE_AUTO_SUDO'." + exit 1 + esac + say "Using preset root authorization mechanism '$LE_AUTO_SUDO'." else - SUDO= + if test "`id -u`" -ne "0" ; then + if $EXISTS sudo 1>/dev/null 2>&1; then + SUDO="sudo -E" + else + say \"sudo\" is not available, will use \"su\" for installation steps... + SUDO=SuSudo + fi + fi + fi +} + +if [ "$1" = "--cb-auto-has-root" ]; then + shift 1 +elif [ "$1" != "--le-auto-phase2" ]; then + # if $1 is --le-auto-phase2, we've executed this branch before + SetRootAuthMechanism + if [ -n "$SUDO" ]; then + echo "Requesting to rerun $0 with root privileges..." + $SUDO "$0" --cb-auto-has-root "$@" + exit 0 fi fi @@ -238,6 +257,10 @@ DeterminePythonVersion() { fi } +# If new packages are installed by BootstrapDebCommon below, this version +# number must be increased. +BOOTSTRAP_DEB_COMMON_VERSION=1 + BootstrapDebCommon() { # Current version tested with: # @@ -261,7 +284,7 @@ BootstrapDebCommon() { QUIET_FLAG='-qq' fi - $SUDO apt-get $QUIET_FLAG update || error apt-get update hit problems but continuing anyway... + apt-get $QUIET_FLAG update || error apt-get update hit problems but continuing anyway... # virtualenv binary can be found in different packages depending on # distro version (#346) @@ -311,13 +334,13 @@ BootstrapDebCommon() { esac fi if [ "$add_backports" = 1 ]; then - $SUDO sh -c "echo $BACKPORT_SOURCELINE >> /etc/apt/sources.list.d/$BACKPORT_NAME.list" - $SUDO apt-get $QUIET_FLAG update + sh -c "echo $BACKPORT_SOURCELINE >> /etc/apt/sources.list.d/$BACKPORT_NAME.list" + apt-get $QUIET_FLAG update fi fi fi if [ "$add_backports" != 0 ]; then - $SUDO apt-get install $QUIET_FLAG $YES_FLAG --no-install-recommends -t "$BACKPORT_NAME" $augeas_pkg + apt-get install $QUIET_FLAG $YES_FLAG --no-install-recommends -t "$BACKPORT_NAME" $augeas_pkg augeas_pkg= fi } @@ -336,7 +359,7 @@ BootstrapDebCommon() { # XXX add a case for ubuntu PPAs fi - $SUDO apt-get install $QUIET_FLAG $YES_FLAG --no-install-recommends \ + apt-get install $QUIET_FLAG $YES_FLAG --no-install-recommends \ python \ python-dev \ $virtualenv \ @@ -354,6 +377,10 @@ BootstrapDebCommon() { fi } +# If new packages are installed by BootstrapRpmCommon below, this version +# number must be increased. +BOOTSTRAP_RPM_COMMON_VERSION=1 + BootstrapRpmCommon() { # Tested with: # - Fedora 20, 21, 22, 23 (x64) @@ -380,9 +407,9 @@ BootstrapRpmCommon() { QUIET_FLAG='--quiet' fi - if ! $SUDO $tool list *virtualenv >/dev/null 2>&1; then + if ! $tool list *virtualenv >/dev/null 2>&1; then echo "To use Certbot, packages from the EPEL repository need to be installed." - if ! $SUDO $tool list epel-release >/dev/null 2>&1; then + if ! $tool list epel-release >/dev/null 2>&1; then error "Enable the EPEL repository and try running Certbot again." exit 1 fi @@ -394,7 +421,7 @@ BootstrapRpmCommon() { /bin/echo -e "\e[0K\rEnabling the EPEL repository in 1 seconds..." sleep 1s fi - if ! $SUDO $tool install $yes_flag $QUIET_FLAG epel-release; then + if ! $tool install $yes_flag $QUIET_FLAG epel-release; then error "Could not enable EPEL. Aborting bootstrap!" exit 1 fi @@ -410,9 +437,8 @@ BootstrapRpmCommon() { ca-certificates " - # Some distros and older versions of current distros use a "python27" - # instead of "python" naming convention. Try both conventions. - if $SUDO $tool list python >/dev/null 2>&1; then + # Most RPM distros use the "python" or "python-" naming convention. Let's try that first. + if $tool list python >/dev/null 2>&1; then pkgs="$pkgs python python-devel @@ -420,6 +446,20 @@ BootstrapRpmCommon() { python-tools python-pip " + # Fedora 26 starts to use the prefix python2 for python2 based packages. + # this elseif is theoretically for any Fedora over version 26: + elif $tool list python2 >/dev/null 2>&1; then + pkgs="$pkgs + python2 + python2-libs + python2-setuptools + python2-devel + python2-virtualenv + python2-tools + python2-pip + " + # Some distros and older versions of current distros use a "python27" + # instead of the "python" or "python-" naming convention. else pkgs="$pkgs python27 @@ -430,18 +470,22 @@ BootstrapRpmCommon() { " fi - if $SUDO $tool list installed "httpd" >/dev/null 2>&1; then + if $tool list installed "httpd" >/dev/null 2>&1; then pkgs="$pkgs mod_ssl " fi - if ! $SUDO $tool install $yes_flag $QUIET_FLAG $pkgs; then + if ! $tool install $yes_flag $QUIET_FLAG $pkgs; then error "Could not install OS dependencies. Aborting bootstrap!" exit 1 fi } +# If new packages are installed by BootstrapSuseCommon below, this version +# number must be increased. +BOOTSTRAP_SUSE_COMMON_VERSION=1 + BootstrapSuseCommon() { # SLE12 don't have python-virtualenv @@ -454,7 +498,7 @@ BootstrapSuseCommon() { QUIET_FLAG='-qq' fi - $SUDO zypper $QUIET_FLAG $zypper_flags in $install_flags \ + zypper $QUIET_FLAG $zypper_flags in $install_flags \ python \ python-devel \ python-virtualenv \ @@ -465,6 +509,10 @@ BootstrapSuseCommon() { ca-certificates } +# If new packages are installed by BootstrapArchCommon below, this version +# number must be increased. +BOOTSTRAP_ARCH_COMMON_VERSION=1 + BootstrapArchCommon() { # Tested with: # - ArchLinux (x86_64) @@ -485,21 +533,25 @@ BootstrapArchCommon() { " # pacman -T exits with 127 if there are missing dependencies - missing=$($SUDO pacman -T $deps) || true + missing=$(pacman -T $deps) || true if [ "$ASSUME_YES" = 1 ]; then noconfirm="--noconfirm" fi if [ "$missing" ]; then - if [ "$QUIET" = 1]; then - $SUDO pacman -S --needed $missing $noconfirm > /dev/null + if [ "$QUIET" = 1 ]; then + pacman -S --needed $missing $noconfirm > /dev/null else - $SUDO pacman -S --needed $missing $noconfirm + pacman -S --needed $missing $noconfirm fi fi } +# If new packages are installed by BootstrapGentooCommon below, this version +# number must be increased. +BOOTSTRAP_GENTOO_COMMON_VERSION=1 + BootstrapGentooCommon() { PACKAGES=" dev-lang/python:2.7 @@ -517,29 +569,37 @@ BootstrapGentooCommon() { case "$PACKAGE_MANAGER" in (paludis) - $SUDO cave resolve --preserve-world --keep-targets if-possible $PACKAGES -x + cave resolve --preserve-world --keep-targets if-possible $PACKAGES -x ;; (pkgcore) - $SUDO pmerge --noreplace --oneshot $ASK_OPTION $PACKAGES + pmerge --noreplace --oneshot $ASK_OPTION $PACKAGES ;; (portage|*) - $SUDO emerge --noreplace --oneshot $ASK_OPTION $PACKAGES + emerge --noreplace --oneshot $ASK_OPTION $PACKAGES ;; esac } +# If new packages are installed by BootstrapFreeBsd below, this version number +# must be increased. +BOOTSTRAP_FREEBSD_VERSION=1 + BootstrapFreeBsd() { if [ "$QUIET" = 1 ]; then QUIET_FLAG="--quiet" fi - $SUDO pkg install -Ay $QUIET_FLAG \ + pkg install -Ay $QUIET_FLAG \ python \ py27-virtualenv \ augeas \ libffi } +# If new packages are installed by BootstrapMac below, this version number must +# be increased. +BOOTSTRAP_MAC_VERSION=1 + BootstrapMac() { if hash brew 2>/dev/null; then say "Using Homebrew to install dependencies..." @@ -548,7 +608,7 @@ BootstrapMac() { elif hash port 2>/dev/null; then say "Using MacPorts to install dependencies..." pkgman=port - pkgcmd="$SUDO port install" + pkgcmd="port install" else say "No Homebrew/MacPorts; installing Homebrew..." ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)" @@ -568,8 +628,8 @@ BootstrapMac() { # Workaround for _dlopen not finding augeas on macOS if [ "$pkgman" = "port" ] && ! [ -e "/usr/local/lib/libaugeas.dylib" ] && [ -e "/opt/local/lib/libaugeas.dylib" ]; then say "Applying augeas workaround" - $SUDO mkdir -p /usr/local/lib/ - $SUDO ln -s /opt/local/lib/libaugeas.dylib /usr/local/lib/ + mkdir -p /usr/local/lib/ + ln -s /opt/local/lib/libaugeas.dylib /usr/local/lib/ fi if ! hash pip 2>/dev/null; then @@ -585,17 +645,25 @@ BootstrapMac() { fi } +# If new packages are installed by BootstrapSmartOS below, this version number +# must be increased. +BOOTSTRAP_SMARTOS_VERSION=1 + BootstrapSmartOS() { pkgin update pkgin -y install 'gcc49' 'py27-augeas' 'py27-virtualenv' } +# If new packages are installed by BootstrapMageiaCommon below, this version +# number must be increased. +BOOTSTRAP_MAGEIA_COMMON_VERSION=1 + BootstrapMageiaCommon() { if [ "$QUIET" = 1 ]; then QUIET_FLAG='--quiet' fi - if ! $SUDO urpmi --force $QUIET_FLAG \ + if ! urpmi --force $QUIET_FLAG \ python \ libpython-devel \ python-virtualenv @@ -604,7 +672,7 @@ BootstrapMageiaCommon() { exit 1 fi - if ! $SUDO urpmi --force $QUIET_FLAG \ + if ! urpmi --force $QUIET_FLAG \ git \ gcc \ python-augeas \ @@ -618,23 +686,41 @@ BootstrapMageiaCommon() { } -# Install required OS packages: -Bootstrap() { - if [ "$NO_BOOTSTRAP" = 1 ]; then - return - elif [ -f /etc/debian_version ]; then +# Set Bootstrap to the function that installs OS dependencies on this system +# and BOOTSTRAP_VERSION to the unique identifier for the current version of +# that function. If Bootstrap is set to a function that doesn't install any +# packages (either because --no-bootstrap was included on the command line or +# we don't know how to bootstrap on this system), BOOTSTRAP_VERSION is not set. +if [ "$NO_BOOTSTRAP" = 1 ]; then + Bootstrap() { + : + } +elif [ -f /etc/debian_version ]; then + Bootstrap() { BootstrapMessage "Debian-based OSes" BootstrapDebCommon - elif [ -f /etc/mageia-release ]; then - # Mageia has both /etc/mageia-release and /etc/redhat-release + } + BOOTSTRAP_VERSION="BootstrapDebCommon $BOOTSTRAP_DEB_COMMON_VERSION" +elif [ -f /etc/mageia-release ]; then + # Mageia has both /etc/mageia-release and /etc/redhat-release + Bootstrap() { ExperimentalBootstrap "Mageia" BootstrapMageiaCommon - elif [ -f /etc/redhat-release ]; then + } + BOOTSTRAP_VERSION="BootstrapMageiaCommon $BOOTSTRAP_MAGEIA_COMMON_VERSION" +elif [ -f /etc/redhat-release ]; then + Bootstrap() { BootstrapMessage "RedHat-based OSes" BootstrapRpmCommon - elif [ -f /etc/os-release ] && `grep -q openSUSE /etc/os-release` ; then + } + BOOTSTRAP_VERSION="BootstrapRpmCommon $BOOTSTRAP_RPM_COMMON_VERSION" +elif [ -f /etc/os-release ] && `grep -q openSUSE /etc/os-release` ; then + Bootstrap() { BootstrapMessage "openSUSE-based OSes" BootstrapSuseCommon - elif [ -f /etc/arch-release ]; then + } + BOOTSTRAP_VERSION="BootstrapSuseCommon $BOOTSTRAP_SUSE_COMMON_VERSION" +elif [ -f /etc/arch-release ]; then + Bootstrap() { if [ "$DEBUG" = 1 ]; then BootstrapMessage "Archlinux" BootstrapArchCommon @@ -646,25 +732,76 @@ Bootstrap() { error "--debug flag." exit 1 fi - elif [ -f /etc/manjaro-release ]; then + } + BOOTSTRAP_VERSION="BootstrapArchCommon $BOOTSTRAP_ARCH_COMMON_VERSION" +elif [ -f /etc/manjaro-release ]; then + Bootstrap() { ExperimentalBootstrap "Manjaro Linux" BootstrapArchCommon - elif [ -f /etc/gentoo-release ]; then + } + BOOTSTRAP_VERSION="BootstrapArchCommon $BOOTSTRAP_ARCH_COMMON_VERSION" +elif [ -f /etc/gentoo-release ]; then + Bootstrap() { DeprecationBootstrap "Gentoo" BootstrapGentooCommon - elif uname | grep -iq FreeBSD ; then + } + BOOTSTRAP_VERSION="BootstrapGentooCommon $BOOTSTRAP_GENTOO_COMMON_VERSION" +elif uname | grep -iq FreeBSD ; then + Bootstrap() { DeprecationBootstrap "FreeBSD" BootstrapFreeBsd - elif uname | grep -iq Darwin ; then + } + BOOTSTRAP_VERSION="BootstrapFreeBsd $BOOTSTRAP_FREEBSD_VERSION" +elif uname | grep -iq Darwin ; then + Bootstrap() { DeprecationBootstrap "macOS" BootstrapMac - elif [ -f /etc/issue ] && grep -iq "Amazon Linux" /etc/issue ; then + } + BOOTSTRAP_VERSION="BootstrapMac $BOOTSTRAP_MAC_VERSION" +elif [ -f /etc/issue ] && grep -iq "Amazon Linux" /etc/issue ; then + Bootstrap() { ExperimentalBootstrap "Amazon Linux" BootstrapRpmCommon - elif [ -f /etc/product ] && grep -q "Joyent Instance" /etc/product ; then + } + BOOTSTRAP_VERSION="BootstrapRpmCommon $BOOTSTRAP_RPM_COMMON_VERSION" +elif [ -f /etc/product ] && grep -q "Joyent Instance" /etc/product ; then + Bootstrap() { ExperimentalBootstrap "Joyent SmartOS Zone" BootstrapSmartOS - else + } + BOOTSTRAP_VERSION="BootstrapSmartOS $BOOTSTRAP_SMARTOS_VERSION" +else + Bootstrap() { error "Sorry, I don't know how to bootstrap Certbot on your operating system!" error error "You will need to install OS dependencies, configure virtualenv, and run pip install manually." error "Please see https://letsencrypt.readthedocs.org/en/latest/contributing.html#prerequisites" error "for more info." exit 1 + } +fi + +# Sets PREV_BOOTSTRAP_VERSION to the identifier for the bootstrap script used +# to install OS dependencies on this system. PREV_BOOTSTRAP_VERSION isn't set +# if it is unknown how OS dependencies were installed on this system. +SetPrevBootstrapVersion() { + if [ -f $BOOTSTRAP_VERSION_PATH ]; then + PREV_BOOTSTRAP_VERSION=$(cat "$BOOTSTRAP_VERSION_PATH") + # The list below only contains bootstrap version strings that existed before + # we started writing them to disk. + # + # DO NOT MODIFY THIS LIST UNLESS YOU KNOW WHAT YOU'RE DOING! + elif grep -Fqx "$BOOTSTRAP_VERSION" << "UNLIKELY_EOF" +BootstrapDebCommon 1 +BootstrapMageiaCommon 1 +BootstrapRpmCommon 1 +BootstrapSuseCommon 1 +BootstrapArchCommon 1 +BootstrapGentooCommon 1 +BootstrapFreeBsd 1 +BootstrapMac 1 +BootstrapSmartOS 1 +UNLIKELY_EOF + then + # If there's no bootstrap version saved to disk, but the currently selected + # bootstrap script is from before we started saving the version number, + # return the currently selected version to prevent us from rebootstrapping + # unnecessarily. + PREV_BOOTSTRAP_VERSION="$BOOTSTRAP_VERSION" fi } @@ -678,18 +815,39 @@ if [ "$1" = "--le-auto-phase2" ]; then # Phase 2: Create venv, install LE, and run. shift 1 # the --le-auto-phase2 arg - if [ -f "$VENV_BIN/letsencrypt" ]; then - # --version output ran through grep due to python-cryptography DeprecationWarnings - # grep for both certbot and letsencrypt until certbot and shim packages have been released - INSTALLED_VERSION=$("$VENV_BIN/letsencrypt" --version 2>&1 | grep "^certbot\|^letsencrypt" | cut -d " " -f 2) - if [ -z "$INSTALLED_VERSION" ]; then - error "Error: couldn't get currently installed version for $VENV_BIN/letsencrypt: " 1>&2 - "$VENV_BIN/letsencrypt" --version - exit 1 + SetPrevBootstrapVersion + + INSTALLED_VERSION="none" + if [ -d "$VENV_PATH" ]; then + # If the selected Bootstrap function isn't a noop and it differs from the + # previously used version + if [ -n "$BOOTSTRAP_VERSION" -a "$BOOTSTRAP_VERSION" != "$PREV_BOOTSTRAP_VERSION" ]; then + # if non-interactive mode or stdin and stdout are connected to a terminal + if [ \( "$NONINTERACTIVE" = 1 \) -o \( \( -t 0 \) -a \( -t 1 \) \) ]; then + rm -rf "$VENV_PATH" + "$0" "$@" + exit 0 + else + error "Skipping upgrade because new OS dependencies may need to be installed." + error + error "To upgrade to a newer version, please run this script again manually so you can" + error "approve changes or with --non-interactive on the command line to automatically" + error "install any required packages." + # Set INSTALLED_VERSION to be the same so we don't update the venv + INSTALLED_VERSION="$LE_AUTO_VERSION" + fi + elif [ -f "$VENV_BIN/letsencrypt" ]; then + # --version output ran through grep due to python-cryptography DeprecationWarnings + # grep for both certbot and letsencrypt until certbot and shim packages have been released + INSTALLED_VERSION=$("$VENV_BIN/letsencrypt" --version 2>&1 | grep "^certbot\|^letsencrypt" | cut -d " " -f 2) + if [ -z "$INSTALLED_VERSION" ]; then + error "Error: couldn't get currently installed version for $VENV_BIN/letsencrypt: " 1>&2 + "$VENV_BIN/letsencrypt" --version + exit 1 + fi fi - else - INSTALLED_VERSION="none" fi + if [ "$LE_AUTO_VERSION" != "$INSTALLED_VERSION" ]; then say "Creating virtual environment..." DeterminePythonVersion @@ -700,6 +858,12 @@ if [ "$1" = "--le-auto-phase2" ]; then virtualenv --no-site-packages --python "$LE_PYTHON" "$VENV_PATH" > /dev/null fi + if [ -n "$BOOTSTRAP_VERSION" ]; then + echo "$BOOTSTRAP_VERSION" > "$BOOTSTRAP_VERSION_PATH" + elif [ -n "$PREV_BOOTSTRAP_VERSION" ]; then + echo "$PREV_BOOTSTRAP_VERSION" > "$BOOTSTRAP_VERSION_PATH" + fi + say "Installing Python packages..." TEMP_DIR=$(TempDir) trap 'rm -rf "$TEMP_DIR"' EXIT @@ -766,29 +930,41 @@ cffi==1.10.0 \ --hash=sha256:285ab352552f52f1398c912556d4d36d4ea9b8450e5c65d03809bf9886755533 \ --hash=sha256:5576644b859197da7bbd8f8c7c2fb5dcc6cd505cadb42992d5f104c013f8a214 \ --hash=sha256:b3b02911eb1f6ada203b0763ba924234629b51586f72a21faacc638269f4ced5 -ConfigArgParse==0.10.0 \ - --hash=sha256:3b50a83dd58149dfcee98cb6565265d10b53e9c0a2bca7eeef7fb5f5524890a7 +ConfigArgParse==0.12.0 \ + --hash=sha256:28cd7d67669651f2a4518367838c49539457504584a139709b2b8f6c208ef339 configobj==5.0.6 \ --hash=sha256:a2f5650770e1c87fb335af19a9b7eb73fc05ccf22144eb68db7d00cd2bcb0902 -cryptography==1.9 \ - --hash=sha256:469a9d3d851038f1eb7d7f77bb08bb4775b41483372be450e25b293fe57bd59e \ - --hash=sha256:533143321d15c8743f91eec5c5f495c1b5cad9a25de8f6dab01eddd6b416903e \ - --hash=sha256:2230c186182d773064d06242e0fa604cd718bfff28aa9c5ae73d7e426e98a151 \ - --hash=sha256:3dc94ed5a26b8553a325767358f505c0a43e0c89df078647f77a4d022ddcdc57 \ - --hash=sha256:2eb8297b877cb6b56216750fa7017c9f5786bec8afd6a0f1aaace02cbfb6195f \ - --hash=sha256:025a96e48164106f2082b00f42bf430cf21f09e203e42585a712e420b75cbff0 \ - --hash=sha256:61eb3534f8ed2415dd708b28919205d523f220e4cd5b8165844edfdd4a649b8e \ - --hash=sha256:5474fe5ce6b517d3086e0231b6ad88f8978c551c4379f91c3d619c308490f0d7 \ - --hash=sha256:5ff169869624e23767d70db274f13a9ea4e97c029425a1224aa5e049e84ce2af \ - --hash=sha256:c26e057a2de13e97e708328d295c5ac4cd3eab4a5c42ce727dd1a53316034b8a \ - --hash=sha256:7db719432648f14ea33edffc5f75330c595804eac86ca916528b35ce50a8bfd6 \ - --hash=sha256:ca72537a7064bb80e34b6908e576ffc8e2c2cad29a7168f48d0999df089695c3 \ - --hash=sha256:2a5e577f5d2093e51486b4ec02b51bb5adb165b98fee99929d5af0813e90b469 \ - --hash=sha256:9d9da8bac2e31003d092f5ef6981a725c77c4e9a30638436884d61ad39f9a1ee \ - --hash=sha256:fab8ec6866e384d9827d5dc02a1383e991a6c05c54f818316c4b829e56ca2ba3 \ - --hash=sha256:365eb804362e581c9a02e7a610b30514f07dd77b2a38aed338eb5192446bbc58 \ - --hash=sha256:2908f709f02711dbb10561a9f154d2f7d1792385e2341e9708539cc4ecfb8667 \ - --hash=sha256:5518337022718029e367d982642f3e3523541e098ad671672a90b82474c84882 +cryptography==2.0.2 \ + --hash=sha256:187ae17358436d2c760f28c2aeb02fefa3f37647a9c5b6f7f7c3e83cd1c5a972 \ + --hash=sha256:19e43a13bbf52028dd1e810c803f2ad8880d0692d772f98d42e1eaf34bdee3d6 \ + --hash=sha256:da9291502cbc87dc0284a20c56876e4d2e68deac61cc43df4aec934e44ca97b1 \ + --hash=sha256:0954f8813095f581669330e0a2d5e726c33ac7f450c1458fac58bab54595e516 \ + --hash=sha256:d68b0cc40a8432ed3fc84876c519de704d6001800ec22b136e75ae841910c45b \ + --hash=sha256:2f8ad9580ab4da645cfea52a91d2da99a49a1e76616d8be68441a986fad652b0 \ + --hash=sha256:cc00b4511294f5f6b65c4e77a1a9c62f52490a63d2c120f3872176b40a82351e \ + --hash=sha256:cf896020f6a9f095a547b3d672c8db1ef2ed71fca11250731fa1d4a4cb8b1590 \ + --hash=sha256:e0fdb8322206fa02aa38f71519ff75dce2eb481b7e1110e2936795cb376bb6ee \ + --hash=sha256:277538466657ca5d6637f80be100242f9831d75138b788d718edd3aab34621f8 \ + --hash=sha256:2c77eb0560f54ce654ab82d6b2a64327a71ee969b29022bf9746ca311c9f5069 \ + --hash=sha256:755a7853b679e79d0a799351c092a9b0271f95ff54c8dd8823d8b527a2926a86 \ + --hash=sha256:77197a2d525e761cdd4c771180b4bd0d80703654c6385e4311cbbbe2beb56fa1 \ + --hash=sha256:eb8bb79d0ab00c931c8333b745f06fec481a51c52d70acd4ee95d6093ba5c386 \ + --hash=sha256:131f61de82ef28f3e20beb4bfc24f9692d28cecfd704e20e6c7f070f7793013a \ + --hash=sha256:ac35435974b2e27cd4520f29c191d7da36f4189aa3264e52c4c6c6d089ab6142 \ + --hash=sha256:04b6ea99daa2a8460728794213d76d45ad58ea247dc7e7ff148d7dd726e87863 \ + --hash=sha256:2b9442f8b4c3d575f6cc3db0e856034e0f5a9d55ecd636f52d8c496795b26952 \ + --hash=sha256:b3d3b3ecba1fe1bdb6f180770a137f877c8f07571f7b2934bb269475bcf0e5e8 \ + --hash=sha256:670a58c0d75cb0e78e73dd003bd96d4440bbb1f2bc041dcf7b81767ca4fb0ce9 \ + --hash=sha256:5af84d23bdb86b5e90aca263df1424b43f1748480bfcde3ac2a3cbe622612468 \ + --hash=sha256:ba22e8eefabdd7aca37d0c0c00d2274000d2cebb5cce9e5a710cb55bf8797b31 \ + --hash=sha256:b798b22fa7e92b439547323b8b719d217f1e1b7677585cfeeedf3b55c70bb7fb \ + --hash=sha256:59cff28af8cce96cb7e94a459726e1d88f6f5fa75097f9dcbebd99118d64ea4c \ + --hash=sha256:fe859e445abc9ba9e97950ddafb904e23234c4ecb76b0fae6c86e80592ce464a \ + --hash=sha256:655f3c474067f1e277430f23cc0549f0b1dc99b82aec6e53f80b9b2db7f76f11 \ + --hash=sha256:0ebc2be053c9a03a2f3e20a466e87bf12a51586b3c79bd2a22171b073a805346 \ + --hash=sha256:01e6e60654df64cca53733cda39446d67100c819c181d403afb120e0d2a71e1b \ + --hash=sha256:d46f4e5d455cb5563685c52ef212696f0a6cc1ea627603218eabbd8a095291d8 \ + --hash=sha256:3780b2663ee7ebb37cb83263326e3cd7f8b2ea439c448539d4b87de12c8d06ab enum34==1.1.2 \ --hash=sha256:2475d7fcddf5951e92ff546972758802de5260bf409319a9f1934e6bbc8b1dc7 \ --hash=sha256:35907defb0f992b75ab7788f65fedc1cf20ffa22688e0e6f6f12afc06b3ea501 @@ -895,18 +1071,18 @@ letsencrypt==0.7.0 \ --hash=sha256:105a5fb107e45bcd0722eb89696986dcf5f08a86a321d6aef25a0c7c63375ade \ --hash=sha256:c36e532c486a7e92155ee09da54b436a3c420813ec1c590b98f635d924720de9 -certbot==0.16.0 \ - --hash=sha256:e1cc2479f4f149a7128b67192c3f5f6c111b6b9ddcac421ebee8ac5030d5b09b \ - --hash=sha256:795801fd6b06b32060e364ac045312e6b26c6272d5ca32878277e5a2afdee186 -acme==0.16.0 \ - --hash=sha256:31592a744f7a6e7cbb4c5daf2deebc9af6b03997d6f80e5becc203ab8694edcf \ - --hash=sha256:538b69134cc50bdcdcc081b844c85158509022c61d7abc1f44216beeb95de9cd -certbot-apache==0.16.0 \ - --hash=sha256:337f84f391c7d7c31d84376e7a8c882ac119112a4aabceadd1a7de6a2f01ca06 \ - --hash=sha256:f1f37b56e1ceb0a28ccf51d54ca34444e6a708448845ef25372e223b9a82c4d4 -certbot-nginx==0.16.0 \ - --hash=sha256:1d57428202f8e7abdd99c3ddbcf374aad5f69c4262fe8dae53d2016e4ae04ded \ - --hash=sha256:77711655514d707ddf728195f00b2f6cdd1ad9db52440276b4a67664479c6954 +certbot==0.17.0 \ + --hash=sha256:64c25c7123357feffded6408660bc6f5c7d493dd635ae172081d21473075a86a \ + --hash=sha256:43f5b26c3f314d14babf79a3bdf3522e4fc9eef867a0681c426f113c650a669c +acme==0.17.0 \ + --hash=sha256:501710171633af13fc52aa61d0277a6fe335f7477db5810e72239aaf4f3a09e7 \ + --hash=sha256:3ccbe4aaeb98c77b98ee4093b4e4adb76a1a24cbdfec0130c489c206f1d9b66e +certbot-apache==0.17.0 \ + --hash=sha256:17a7e8d7526d838610e68b96cf052af17c4055655b76b06d1cbc74857d90a216 \ + --hash=sha256:29b9e7bc5eaaff6dc4bce8398e35eeacdf346126aad68cac3d41bb87df20a6b9 +certbot-nginx==0.17.0 \ + --hash=sha256:980c9a33a79ab839a089a0085ff0c5414f01f47b6db26ed342df25916658cec9 \ + --hash=sha256:e573f8b4283172755c07b9cca8a8da7ef2d31b4df763881394b5339b2d42994a UNLIKELY_EOF # ------------------------------------------------------------------------- @@ -934,6 +1110,7 @@ anything goes wrong, it will exit with a non-zero status code. # The above copyright notice and this permission notice shall be included in # all copies or substantial portions of the Software. from __future__ import print_function +from distutils.version import StrictVersion from hashlib import sha256 from os.path import join from pipes import quote @@ -968,12 +1145,14 @@ except ImportError: from urllib.parse import urlparse # 3.4 -__version__ = 1, 1, 1 +__version__ = 1, 3, 0 +PIP_VERSION = '9.0.1' # wheel has a conditional dependency on argparse: maybe_argparse = ( - [('https://pypi.python.org/packages/source/a/argparse/' + [('https://pypi.python.org/packages/18/dd/' + 'e617cfc3f6210ae183374cd9f6a26b20514bbb5a792af97949c5aacddf0f/' 'argparse-1.4.0.tar.gz', '62b089a55be1d8949cd2bc7e0df0bddb9e028faefc8c32038cc84862aefdd6e4')] if version_info < (2, 7, 0) else []) @@ -981,13 +1160,19 @@ maybe_argparse = ( PACKAGES = maybe_argparse + [ # Pip has no dependencies, as it vendors everything: - ('https://pypi.python.org/packages/source/p/pip/pip-8.0.3.tar.gz', - '30f98b66f3fe1069c529a491597d34a1c224a68640c82caf2ade5f88aa1405e8'), + ('https://pypi.python.org/packages/11/b6/' + 'abcb525026a4be042b486df43905d6893fb04f05aac21c32c638e939e447/' + 'pip-{0}.tar.gz' + .format(PIP_VERSION), + '09f243e1a7b461f654c26a725fa373211bb7ff17a9300058b205c61658ca940d'), # This version of setuptools has only optional dependencies: - ('https://pypi.python.org/packages/source/s/setuptools/' + ('https://pypi.python.org/packages/69/65/' + '4c544cde88d4d876cdf5cbc5f3f15d02646477756d89547e9a7ecd6afa76/' 'setuptools-20.2.2.tar.gz', '24fcfc15364a9fe09a220f37d2dcedc849795e3de3e4b393ee988e66a9cbd85a'), - ('https://pypi.python.org/packages/source/w/wheel/wheel-0.29.0.tar.gz', + ('https://pypi.python.org/packages/c9/1d/' + 'bd19e691fd4cfe908c76c429fe6e4436c9e83583c4414b54f6c85471954a/' + 'wheel-0.29.0.tar.gz', '1ebb8ad7e26b448e9caa4773d2357849bf80ff9e313964bcaf79cbf0201a1648') ] @@ -1037,11 +1222,21 @@ def hashed_download(url, temp, digest): def main(): + pip_version = StrictVersion(check_output(['pip', '--version']) + .decode('utf-8').split()[1]) + min_pip_version = StrictVersion(PIP_VERSION) + if pip_version >= min_pip_version: + return 0 + has_pip_cache = pip_version >= StrictVersion('6.0') + temp = mkdtemp(prefix='pipstrap-') try: downloads = [hashed_download(url, temp, digest) for url, 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: + ('--no-cache-dir ' if has_pip_cache else '') + ' '.join(quote(d) for d in downloads), shell=True) except HashError as exc: @@ -1100,20 +1295,15 @@ UNLIKELY_EOF rm -rf "$VENV_PATH" exit 1 fi + + if [ -d "$OLD_VENV_PATH" -a ! -L "$OLD_VENV_PATH" ]; then + rm -rf "$OLD_VENV_PATH" + ln -s "$VENV_PATH" "$OLD_VENV_PATH" + fi + say "Installation succeeded." fi - if [ -n "$SUDO" ]; then - # SUDO is su wrapper or sudo - say "Requesting root privileges to run certbot..." - say " $VENV_BIN/letsencrypt" "$@" - fi - if [ -z "$SUDO_ENV" ] ; then - # SUDO is su wrapper / noop - $SUDO "$VENV_BIN/letsencrypt" "$@" - else - # sudo - $SUDO "$SUDO_ENV" "$VENV_BIN/letsencrypt" "$@" - fi + "$VENV_BIN/letsencrypt" "$@" else # Phase 1: Upgrade certbot-auto if necessary, then self-invoke. @@ -1124,12 +1314,14 @@ else # package). Phase 2 checks the version of the locally installed certbot. if [ ! -f "$VENV_BIN/letsencrypt" ]; then - if [ "$HELP" = 1 ]; then - echo "$USAGE" - exit 0 + if [ -z "$OLD_VENV_PATH" -o ! -f "$OLD_VENV_PATH/bin/letsencrypt" ]; then + if [ "$HELP" = 1 ]; then + echo "$USAGE" + exit 0 + fi + # If it looks like we've never bootstrapped before, bootstrap: + Bootstrap fi - # If it looks like we've never bootstrapped before, bootstrap: - Bootstrap fi if [ "$OS_PACKAGES_ONLY" = 1 ]; then say "OS packages installed." @@ -1289,13 +1481,13 @@ UNLIKELY_EOF say "Replacing certbot-auto..." # Clone permissions with cp. chmod and chown don't have a --reference # option on macOS or BSD, and stat -c on Linux is stat -f on macOS and BSD: - $SUDO cp -p "$0" "$TEMP_DIR/letsencrypt-auto.permission-clone" - $SUDO cp "$TEMP_DIR/letsencrypt-auto" "$TEMP_DIR/letsencrypt-auto.permission-clone" + cp -p "$0" "$TEMP_DIR/letsencrypt-auto.permission-clone" + cp "$TEMP_DIR/letsencrypt-auto" "$TEMP_DIR/letsencrypt-auto.permission-clone" # Using mv rather than cp leaves the old file descriptor pointing to the # original copy so the shell can continue to read it unmolested. mv across # filesystems is non-atomic, doing `rm dest, cp src dest, rm src`, but the - # cp is unlikely to fail (esp. under sudo) if the rm doesn't. - $SUDO mv -f "$TEMP_DIR/letsencrypt-auto.permission-clone" "$0" + # cp is unlikely to fail if the rm doesn't. + mv -f "$TEMP_DIR/letsencrypt-auto.permission-clone" "$0" fi # A newer version is available. fi # Self-upgrading is allowed. diff --git a/letsencrypt-auto-source/letsencrypt-auto.sig b/letsencrypt-auto-source/letsencrypt-auto.sig index 72ade22dc..a8885d19a 100644 Binary files a/letsencrypt-auto-source/letsencrypt-auto.sig and b/letsencrypt-auto-source/letsencrypt-auto.sig differ diff --git a/letsencrypt-auto-source/letsencrypt-auto.template b/letsencrypt-auto-source/letsencrypt-auto.template index 284241a82..eb2b82776 100755 --- a/letsencrypt-auto-source/letsencrypt-auto.template +++ b/letsencrypt-auto-source/letsencrypt-auto.template @@ -23,11 +23,14 @@ fi if [ -z "$XDG_DATA_HOME" ]; then XDG_DATA_HOME=~/.local/share fi -VENV_NAME="letsencrypt" if [ -z "$VENV_PATH" ]; then - VENV_PATH="$XDG_DATA_HOME/$VENV_NAME" + # We export these values so they are preserved properly if this script is + # rerun with sudo/su where $HOME/$XDG_DATA_HOME may have a different value. + export OLD_VENV_PATH="$XDG_DATA_HOME/letsencrypt" + export VENV_PATH="/opt/eff.org/certbot/venv" fi VENV_BIN="$VENV_PATH/bin" +BOOTSTRAP_VERSION_PATH="$VENV_PATH/certbot-auto-bootstrap-version.txt" LE_AUTO_VERSION="{{ LE_AUTO_VERSION }}" BASENAME=$(basename $0) USAGE="Usage: $BASENAME [OPTIONS] @@ -49,6 +52,7 @@ Help for certbot itself cannot be provided until it is installed. implies --non-interactive All arguments are accepted and forwarded to the Certbot client when run." +export CERTBOT_AUTO="$0" for arg in "$@" ; do case "$arg" in @@ -77,7 +81,7 @@ for arg in "$@" ; do h) HELP=1;; n) - ASSUME_YES=1;; + NONINTERACTIVE=1;; q) QUIET=1;; v) @@ -93,8 +97,8 @@ if [ $BASENAME = "letsencrypt-auto" ]; then HELP=0 fi -# Set ASSUME_YES to 1 if QUIET (i.e. --quiet implies --non-interactive) -if [ "$QUIET" = 1 ]; then +# Set ASSUME_YES to 1 if QUIET or NONINTERACTIVE +if [ "$QUIET" = 1 -o "$NONINTERACTIVE" = 1 ]; then ASSUME_YES=1 fi @@ -119,16 +123,18 @@ else exit 1 fi -# certbot-auto needs root access to bootstrap OS dependencies, and -# certbot itself needs root access for almost all modes of operation -# The "normal" case is that sudo is used for the steps that need root, but -# this script *can* be run as root (not recommended), or fall back to using -# `su`. Auto-detection can be overridden by explicitly setting the -# environment variable LE_AUTO_SUDO to 'sudo', 'sudo_su' or '' as used below. +# Certbot itself needs root access for almost all modes of operation. +# certbot-auto needs root access to bootstrap OS dependencies and install +# Certbot at a protected path so it can be safely run as root. To accomplish +# this, this script will attempt to run itself as root if it doesn't have the +# necessary privileges by using `sudo` or falling back to `su` if it is not +# available. The mechanism used to obtain root access can be set explicitly by +# setting the environment variable LE_AUTO_SUDO to 'sudo', 'su', 'su_sudo', +# 'SuSudo', or '' as used below. # Because the parameters in `su -c` has to be a string, # we need to properly escape it. -su_sudo() { +SuSudo() { args="" # This `while` loop iterates over all parameters given to this function. # For each parameter, all `'` will be replace by `'"'"'`, and the escaped string @@ -147,34 +153,47 @@ su_sudo() { su root -c "$args" } -SUDO_ENV="" -export CERTBOT_AUTO="$0" -if [ -n "${LE_AUTO_SUDO+x}" ]; then - case "$LE_AUTO_SUDO" in - su_sudo|su) - SUDO=su_sudo - ;; - sudo) - SUDO=sudo - SUDO_ENV="CERTBOT_AUTO=$0" - ;; - '') ;; # Nothing to do for plain root method. - *) - error "Error: unknown root authorization mechanism '$LE_AUTO_SUDO'." - exit 1 - esac - say "Using preset root authorization mechanism '$LE_AUTO_SUDO'." -else - if test "`id -u`" -ne "0" ; then - if $EXISTS sudo 1>/dev/null 2>&1; then - SUDO=sudo - SUDO_ENV="CERTBOT_AUTO=$0" - else - say \"sudo\" is not available, will use \"su\" for installation steps... - SUDO=su_sudo - fi +# Sets the environment variable SUDO to be the name of the program or function +# to call to get root access. If this script already has root privleges, SUDO +# is set to an empty string. The value in SUDO should be run with the command +# to called with root privileges as arguments. +SetRootAuthMechanism() { + SUDO="" + if [ -n "${LE_AUTO_SUDO+x}" ]; then + case "$LE_AUTO_SUDO" in + SuSudo|su_sudo|su) + SUDO=SuSudo + ;; + sudo) + SUDO="sudo -E" + ;; + '') ;; # Nothing to do for plain root method. + *) + error "Error: unknown root authorization mechanism '$LE_AUTO_SUDO'." + exit 1 + esac + say "Using preset root authorization mechanism '$LE_AUTO_SUDO'." else - SUDO= + if test "`id -u`" -ne "0" ; then + if $EXISTS sudo 1>/dev/null 2>&1; then + SUDO="sudo -E" + else + say \"sudo\" is not available, will use \"su\" for installation steps... + SUDO=SuSudo + fi + fi + fi +} + +if [ "$1" = "--cb-auto-has-root" ]; then + shift 1 +elif [ "$1" != "--le-auto-phase2" ]; then + # if $1 is --le-auto-phase2, we've executed this branch before + SetRootAuthMechanism + if [ -n "$SUDO" ]; then + echo "Requesting to rerun $0 with root privileges..." + $SUDO "$0" --cb-auto-has-root "$@" + exit 0 fi fi @@ -248,23 +267,41 @@ DeterminePythonVersion() { {{ bootstrappers/smartos.sh }} {{ bootstrappers/mageia_common.sh }} -# Install required OS packages: -Bootstrap() { - if [ "$NO_BOOTSTRAP" = 1 ]; then - return - elif [ -f /etc/debian_version ]; then +# Set Bootstrap to the function that installs OS dependencies on this system +# and BOOTSTRAP_VERSION to the unique identifier for the current version of +# that function. If Bootstrap is set to a function that doesn't install any +# packages (either because --no-bootstrap was included on the command line or +# we don't know how to bootstrap on this system), BOOTSTRAP_VERSION is not set. +if [ "$NO_BOOTSTRAP" = 1 ]; then + Bootstrap() { + : + } +elif [ -f /etc/debian_version ]; then + Bootstrap() { BootstrapMessage "Debian-based OSes" BootstrapDebCommon - elif [ -f /etc/mageia-release ]; then - # Mageia has both /etc/mageia-release and /etc/redhat-release + } + BOOTSTRAP_VERSION="BootstrapDebCommon $BOOTSTRAP_DEB_COMMON_VERSION" +elif [ -f /etc/mageia-release ]; then + # Mageia has both /etc/mageia-release and /etc/redhat-release + Bootstrap() { ExperimentalBootstrap "Mageia" BootstrapMageiaCommon - elif [ -f /etc/redhat-release ]; then + } + BOOTSTRAP_VERSION="BootstrapMageiaCommon $BOOTSTRAP_MAGEIA_COMMON_VERSION" +elif [ -f /etc/redhat-release ]; then + Bootstrap() { BootstrapMessage "RedHat-based OSes" BootstrapRpmCommon - elif [ -f /etc/os-release ] && `grep -q openSUSE /etc/os-release` ; then + } + BOOTSTRAP_VERSION="BootstrapRpmCommon $BOOTSTRAP_RPM_COMMON_VERSION" +elif [ -f /etc/os-release ] && `grep -q openSUSE /etc/os-release` ; then + Bootstrap() { BootstrapMessage "openSUSE-based OSes" BootstrapSuseCommon - elif [ -f /etc/arch-release ]; then + } + BOOTSTRAP_VERSION="BootstrapSuseCommon $BOOTSTRAP_SUSE_COMMON_VERSION" +elif [ -f /etc/arch-release ]; then + Bootstrap() { if [ "$DEBUG" = 1 ]; then BootstrapMessage "Archlinux" BootstrapArchCommon @@ -276,25 +313,76 @@ Bootstrap() { error "--debug flag." exit 1 fi - elif [ -f /etc/manjaro-release ]; then + } + BOOTSTRAP_VERSION="BootstrapArchCommon $BOOTSTRAP_ARCH_COMMON_VERSION" +elif [ -f /etc/manjaro-release ]; then + Bootstrap() { ExperimentalBootstrap "Manjaro Linux" BootstrapArchCommon - elif [ -f /etc/gentoo-release ]; then + } + BOOTSTRAP_VERSION="BootstrapArchCommon $BOOTSTRAP_ARCH_COMMON_VERSION" +elif [ -f /etc/gentoo-release ]; then + Bootstrap() { DeprecationBootstrap "Gentoo" BootstrapGentooCommon - elif uname | grep -iq FreeBSD ; then + } + BOOTSTRAP_VERSION="BootstrapGentooCommon $BOOTSTRAP_GENTOO_COMMON_VERSION" +elif uname | grep -iq FreeBSD ; then + Bootstrap() { DeprecationBootstrap "FreeBSD" BootstrapFreeBsd - elif uname | grep -iq Darwin ; then + } + BOOTSTRAP_VERSION="BootstrapFreeBsd $BOOTSTRAP_FREEBSD_VERSION" +elif uname | grep -iq Darwin ; then + Bootstrap() { DeprecationBootstrap "macOS" BootstrapMac - elif [ -f /etc/issue ] && grep -iq "Amazon Linux" /etc/issue ; then + } + BOOTSTRAP_VERSION="BootstrapMac $BOOTSTRAP_MAC_VERSION" +elif [ -f /etc/issue ] && grep -iq "Amazon Linux" /etc/issue ; then + Bootstrap() { ExperimentalBootstrap "Amazon Linux" BootstrapRpmCommon - elif [ -f /etc/product ] && grep -q "Joyent Instance" /etc/product ; then + } + BOOTSTRAP_VERSION="BootstrapRpmCommon $BOOTSTRAP_RPM_COMMON_VERSION" +elif [ -f /etc/product ] && grep -q "Joyent Instance" /etc/product ; then + Bootstrap() { ExperimentalBootstrap "Joyent SmartOS Zone" BootstrapSmartOS - else + } + BOOTSTRAP_VERSION="BootstrapSmartOS $BOOTSTRAP_SMARTOS_VERSION" +else + Bootstrap() { error "Sorry, I don't know how to bootstrap Certbot on your operating system!" error error "You will need to install OS dependencies, configure virtualenv, and run pip install manually." error "Please see https://letsencrypt.readthedocs.org/en/latest/contributing.html#prerequisites" error "for more info." exit 1 + } +fi + +# Sets PREV_BOOTSTRAP_VERSION to the identifier for the bootstrap script used +# to install OS dependencies on this system. PREV_BOOTSTRAP_VERSION isn't set +# if it is unknown how OS dependencies were installed on this system. +SetPrevBootstrapVersion() { + if [ -f $BOOTSTRAP_VERSION_PATH ]; then + PREV_BOOTSTRAP_VERSION=$(cat "$BOOTSTRAP_VERSION_PATH") + # The list below only contains bootstrap version strings that existed before + # we started writing them to disk. + # + # DO NOT MODIFY THIS LIST UNLESS YOU KNOW WHAT YOU'RE DOING! + elif grep -Fqx "$BOOTSTRAP_VERSION" << "UNLIKELY_EOF" +BootstrapDebCommon 1 +BootstrapMageiaCommon 1 +BootstrapRpmCommon 1 +BootstrapSuseCommon 1 +BootstrapArchCommon 1 +BootstrapGentooCommon 1 +BootstrapFreeBsd 1 +BootstrapMac 1 +BootstrapSmartOS 1 +UNLIKELY_EOF + then + # If there's no bootstrap version saved to disk, but the currently selected + # bootstrap script is from before we started saving the version number, + # return the currently selected version to prevent us from rebootstrapping + # unnecessarily. + PREV_BOOTSTRAP_VERSION="$BOOTSTRAP_VERSION" fi } @@ -308,18 +396,39 @@ if [ "$1" = "--le-auto-phase2" ]; then # Phase 2: Create venv, install LE, and run. shift 1 # the --le-auto-phase2 arg - if [ -f "$VENV_BIN/letsencrypt" ]; then - # --version output ran through grep due to python-cryptography DeprecationWarnings - # grep for both certbot and letsencrypt until certbot and shim packages have been released - INSTALLED_VERSION=$("$VENV_BIN/letsencrypt" --version 2>&1 | grep "^certbot\|^letsencrypt" | cut -d " " -f 2) - if [ -z "$INSTALLED_VERSION" ]; then - error "Error: couldn't get currently installed version for $VENV_BIN/letsencrypt: " 1>&2 - "$VENV_BIN/letsencrypt" --version - exit 1 + SetPrevBootstrapVersion + + INSTALLED_VERSION="none" + if [ -d "$VENV_PATH" ]; then + # If the selected Bootstrap function isn't a noop and it differs from the + # previously used version + if [ -n "$BOOTSTRAP_VERSION" -a "$BOOTSTRAP_VERSION" != "$PREV_BOOTSTRAP_VERSION" ]; then + # if non-interactive mode or stdin and stdout are connected to a terminal + if [ \( "$NONINTERACTIVE" = 1 \) -o \( \( -t 0 \) -a \( -t 1 \) \) ]; then + rm -rf "$VENV_PATH" + "$0" "$@" + exit 0 + else + error "Skipping upgrade because new OS dependencies may need to be installed." + error + error "To upgrade to a newer version, please run this script again manually so you can" + error "approve changes or with --non-interactive on the command line to automatically" + error "install any required packages." + # Set INSTALLED_VERSION to be the same so we don't update the venv + INSTALLED_VERSION="$LE_AUTO_VERSION" + fi + elif [ -f "$VENV_BIN/letsencrypt" ]; then + # --version output ran through grep due to python-cryptography DeprecationWarnings + # grep for both certbot and letsencrypt until certbot and shim packages have been released + INSTALLED_VERSION=$("$VENV_BIN/letsencrypt" --version 2>&1 | grep "^certbot\|^letsencrypt" | cut -d " " -f 2) + if [ -z "$INSTALLED_VERSION" ]; then + error "Error: couldn't get currently installed version for $VENV_BIN/letsencrypt: " 1>&2 + "$VENV_BIN/letsencrypt" --version + exit 1 + fi fi - else - INSTALLED_VERSION="none" fi + if [ "$LE_AUTO_VERSION" != "$INSTALLED_VERSION" ]; then say "Creating virtual environment..." DeterminePythonVersion @@ -330,6 +439,12 @@ if [ "$1" = "--le-auto-phase2" ]; then virtualenv --no-site-packages --python "$LE_PYTHON" "$VENV_PATH" > /dev/null fi + if [ -n "$BOOTSTRAP_VERSION" ]; then + echo "$BOOTSTRAP_VERSION" > "$BOOTSTRAP_VERSION_PATH" + elif [ -n "$PREV_BOOTSTRAP_VERSION" ]; then + echo "$PREV_BOOTSTRAP_VERSION" > "$BOOTSTRAP_VERSION_PATH" + fi + say "Installing Python packages..." TEMP_DIR=$(TempDir) trap 'rm -rf "$TEMP_DIR"' EXIT @@ -385,20 +500,15 @@ UNLIKELY_EOF rm -rf "$VENV_PATH" exit 1 fi + + if [ -d "$OLD_VENV_PATH" -a ! -L "$OLD_VENV_PATH" ]; then + rm -rf "$OLD_VENV_PATH" + ln -s "$VENV_PATH" "$OLD_VENV_PATH" + fi + say "Installation succeeded." fi - if [ -n "$SUDO" ]; then - # SUDO is su wrapper or sudo - say "Requesting root privileges to run certbot..." - say " $VENV_BIN/letsencrypt" "$@" - fi - if [ -z "$SUDO_ENV" ] ; then - # SUDO is su wrapper / noop - $SUDO "$VENV_BIN/letsencrypt" "$@" - else - # sudo - $SUDO "$SUDO_ENV" "$VENV_BIN/letsencrypt" "$@" - fi + "$VENV_BIN/letsencrypt" "$@" else # Phase 1: Upgrade certbot-auto if necessary, then self-invoke. @@ -409,12 +519,14 @@ else # package). Phase 2 checks the version of the locally installed certbot. if [ ! -f "$VENV_BIN/letsencrypt" ]; then - if [ "$HELP" = 1 ]; then - echo "$USAGE" - exit 0 + if [ -z "$OLD_VENV_PATH" -o ! -f "$OLD_VENV_PATH/bin/letsencrypt" ]; then + if [ "$HELP" = 1 ]; then + echo "$USAGE" + exit 0 + fi + # If it looks like we've never bootstrapped before, bootstrap: + Bootstrap fi - # If it looks like we've never bootstrapped before, bootstrap: - Bootstrap fi if [ "$OS_PACKAGES_ONLY" = 1 ]; then say "OS packages installed." @@ -445,13 +557,13 @@ UNLIKELY_EOF say "Replacing certbot-auto..." # Clone permissions with cp. chmod and chown don't have a --reference # option on macOS or BSD, and stat -c on Linux is stat -f on macOS and BSD: - $SUDO cp -p "$0" "$TEMP_DIR/letsencrypt-auto.permission-clone" - $SUDO cp "$TEMP_DIR/letsencrypt-auto" "$TEMP_DIR/letsencrypt-auto.permission-clone" + cp -p "$0" "$TEMP_DIR/letsencrypt-auto.permission-clone" + cp "$TEMP_DIR/letsencrypt-auto" "$TEMP_DIR/letsencrypt-auto.permission-clone" # Using mv rather than cp leaves the old file descriptor pointing to the # original copy so the shell can continue to read it unmolested. mv across # filesystems is non-atomic, doing `rm dest, cp src dest, rm src`, but the - # cp is unlikely to fail (esp. under sudo) if the rm doesn't. - $SUDO mv -f "$TEMP_DIR/letsencrypt-auto.permission-clone" "$0" + # cp is unlikely to fail if the rm doesn't. + mv -f "$TEMP_DIR/letsencrypt-auto.permission-clone" "$0" fi # A newer version is available. fi # Self-upgrading is allowed. diff --git a/letsencrypt-auto-source/pieces/bootstrappers/arch_common.sh b/letsencrypt-auto-source/pieces/bootstrappers/arch_common.sh index c3959484b..5759336c5 100755 --- a/letsencrypt-auto-source/pieces/bootstrappers/arch_common.sh +++ b/letsencrypt-auto-source/pieces/bootstrappers/arch_common.sh @@ -1,3 +1,7 @@ +# If new packages are installed by BootstrapArchCommon below, this version +# number must be increased. +BOOTSTRAP_ARCH_COMMON_VERSION=1 + BootstrapArchCommon() { # Tested with: # - ArchLinux (x86_64) @@ -18,17 +22,17 @@ BootstrapArchCommon() { " # pacman -T exits with 127 if there are missing dependencies - missing=$($SUDO pacman -T $deps) || true + missing=$(pacman -T $deps) || true if [ "$ASSUME_YES" = 1 ]; then noconfirm="--noconfirm" fi if [ "$missing" ]; then - if [ "$QUIET" = 1]; then - $SUDO pacman -S --needed $missing $noconfirm > /dev/null + if [ "$QUIET" = 1 ]; then + pacman -S --needed $missing $noconfirm > /dev/null else - $SUDO pacman -S --needed $missing $noconfirm + pacman -S --needed $missing $noconfirm fi fi } diff --git a/letsencrypt-auto-source/pieces/bootstrappers/deb_common.sh b/letsencrypt-auto-source/pieces/bootstrappers/deb_common.sh index afd279ac2..eb22225e4 100644 --- a/letsencrypt-auto-source/pieces/bootstrappers/deb_common.sh +++ b/letsencrypt-auto-source/pieces/bootstrappers/deb_common.sh @@ -1,3 +1,7 @@ +# If new packages are installed by BootstrapDebCommon below, this version +# number must be increased. +BOOTSTRAP_DEB_COMMON_VERSION=1 + BootstrapDebCommon() { # Current version tested with: # @@ -21,7 +25,7 @@ BootstrapDebCommon() { QUIET_FLAG='-qq' fi - $SUDO apt-get $QUIET_FLAG update || error apt-get update hit problems but continuing anyway... + apt-get $QUIET_FLAG update || error apt-get update hit problems but continuing anyway... # virtualenv binary can be found in different packages depending on # distro version (#346) @@ -71,13 +75,13 @@ BootstrapDebCommon() { esac fi if [ "$add_backports" = 1 ]; then - $SUDO sh -c "echo $BACKPORT_SOURCELINE >> /etc/apt/sources.list.d/$BACKPORT_NAME.list" - $SUDO apt-get $QUIET_FLAG update + sh -c "echo $BACKPORT_SOURCELINE >> /etc/apt/sources.list.d/$BACKPORT_NAME.list" + apt-get $QUIET_FLAG update fi fi fi if [ "$add_backports" != 0 ]; then - $SUDO apt-get install $QUIET_FLAG $YES_FLAG --no-install-recommends -t "$BACKPORT_NAME" $augeas_pkg + apt-get install $QUIET_FLAG $YES_FLAG --no-install-recommends -t "$BACKPORT_NAME" $augeas_pkg augeas_pkg= fi } @@ -96,7 +100,7 @@ BootstrapDebCommon() { # XXX add a case for ubuntu PPAs fi - $SUDO apt-get install $QUIET_FLAG $YES_FLAG --no-install-recommends \ + apt-get install $QUIET_FLAG $YES_FLAG --no-install-recommends \ python \ python-dev \ $virtualenv \ diff --git a/letsencrypt-auto-source/pieces/bootstrappers/free_bsd.sh b/letsencrypt-auto-source/pieces/bootstrappers/free_bsd.sh index f1bc00f3b..a67c85619 100755 --- a/letsencrypt-auto-source/pieces/bootstrappers/free_bsd.sh +++ b/letsencrypt-auto-source/pieces/bootstrappers/free_bsd.sh @@ -1,9 +1,13 @@ +# If new packages are installed by BootstrapFreeBsd below, this version number +# must be increased. +BOOTSTRAP_FREEBSD_VERSION=1 + BootstrapFreeBsd() { if [ "$QUIET" = 1 ]; then QUIET_FLAG="--quiet" fi - $SUDO pkg install -Ay $QUIET_FLAG \ + pkg install -Ay $QUIET_FLAG \ python \ py27-virtualenv \ augeas \ diff --git a/letsencrypt-auto-source/pieces/bootstrappers/gentoo_common.sh b/letsencrypt-auto-source/pieces/bootstrappers/gentoo_common.sh index 86a1ec7d6..e2d24b5fb 100755 --- a/letsencrypt-auto-source/pieces/bootstrappers/gentoo_common.sh +++ b/letsencrypt-auto-source/pieces/bootstrappers/gentoo_common.sh @@ -1,3 +1,7 @@ +# If new packages are installed by BootstrapGentooCommon below, this version +# number must be increased. +BOOTSTRAP_GENTOO_COMMON_VERSION=1 + BootstrapGentooCommon() { PACKAGES=" dev-lang/python:2.7 @@ -15,13 +19,13 @@ BootstrapGentooCommon() { case "$PACKAGE_MANAGER" in (paludis) - $SUDO cave resolve --preserve-world --keep-targets if-possible $PACKAGES -x + cave resolve --preserve-world --keep-targets if-possible $PACKAGES -x ;; (pkgcore) - $SUDO pmerge --noreplace --oneshot $ASK_OPTION $PACKAGES + pmerge --noreplace --oneshot $ASK_OPTION $PACKAGES ;; (portage|*) - $SUDO emerge --noreplace --oneshot $ASK_OPTION $PACKAGES + emerge --noreplace --oneshot $ASK_OPTION $PACKAGES ;; esac } diff --git a/letsencrypt-auto-source/pieces/bootstrappers/mac.sh b/letsencrypt-auto-source/pieces/bootstrappers/mac.sh index b88e96999..9e26d3389 100755 --- a/letsencrypt-auto-source/pieces/bootstrappers/mac.sh +++ b/letsencrypt-auto-source/pieces/bootstrappers/mac.sh @@ -1,3 +1,7 @@ +# If new packages are installed by BootstrapMac below, this version number must +# be increased. +BOOTSTRAP_MAC_VERSION=1 + BootstrapMac() { if hash brew 2>/dev/null; then say "Using Homebrew to install dependencies..." @@ -6,7 +10,7 @@ BootstrapMac() { elif hash port 2>/dev/null; then say "Using MacPorts to install dependencies..." pkgman=port - pkgcmd="$SUDO port install" + pkgcmd="port install" else say "No Homebrew/MacPorts; installing Homebrew..." ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)" @@ -26,8 +30,8 @@ BootstrapMac() { # Workaround for _dlopen not finding augeas on macOS if [ "$pkgman" = "port" ] && ! [ -e "/usr/local/lib/libaugeas.dylib" ] && [ -e "/opt/local/lib/libaugeas.dylib" ]; then say "Applying augeas workaround" - $SUDO mkdir -p /usr/local/lib/ - $SUDO ln -s /opt/local/lib/libaugeas.dylib /usr/local/lib/ + mkdir -p /usr/local/lib/ + ln -s /opt/local/lib/libaugeas.dylib /usr/local/lib/ fi if ! hash pip 2>/dev/null; then diff --git a/letsencrypt-auto-source/pieces/bootstrappers/mageia_common.sh b/letsencrypt-auto-source/pieces/bootstrappers/mageia_common.sh index 1c76bbcac..dfa5b47f3 100644 --- a/letsencrypt-auto-source/pieces/bootstrappers/mageia_common.sh +++ b/letsencrypt-auto-source/pieces/bootstrappers/mageia_common.sh @@ -1,9 +1,13 @@ +# If new packages are installed by BootstrapMageiaCommon below, this version +# number must be increased. +BOOTSTRAP_MAGEIA_COMMON_VERSION=1 + BootstrapMageiaCommon() { if [ "$QUIET" = 1 ]; then QUIET_FLAG='--quiet' fi - if ! $SUDO urpmi --force $QUIET_FLAG \ + if ! urpmi --force $QUIET_FLAG \ python \ libpython-devel \ python-virtualenv @@ -12,7 +16,7 @@ BootstrapMageiaCommon() { exit 1 fi - if ! $SUDO urpmi --force $QUIET_FLAG \ + if ! urpmi --force $QUIET_FLAG \ git \ gcc \ python-augeas \ diff --git a/letsencrypt-auto-source/pieces/bootstrappers/rpm_common.sh b/letsencrypt-auto-source/pieces/bootstrappers/rpm_common.sh index dcd535292..5b120a9e6 100755 --- a/letsencrypt-auto-source/pieces/bootstrappers/rpm_common.sh +++ b/letsencrypt-auto-source/pieces/bootstrappers/rpm_common.sh @@ -1,3 +1,7 @@ +# If new packages are installed by BootstrapRpmCommon below, this version +# number must be increased. +BOOTSTRAP_RPM_COMMON_VERSION=1 + BootstrapRpmCommon() { # Tested with: # - Fedora 20, 21, 22, 23 (x64) @@ -24,9 +28,9 @@ BootstrapRpmCommon() { QUIET_FLAG='--quiet' fi - if ! $SUDO $tool list *virtualenv >/dev/null 2>&1; then + if ! $tool list *virtualenv >/dev/null 2>&1; then echo "To use Certbot, packages from the EPEL repository need to be installed." - if ! $SUDO $tool list epel-release >/dev/null 2>&1; then + if ! $tool list epel-release >/dev/null 2>&1; then error "Enable the EPEL repository and try running Certbot again." exit 1 fi @@ -38,7 +42,7 @@ BootstrapRpmCommon() { /bin/echo -e "\e[0K\rEnabling the EPEL repository in 1 seconds..." sleep 1s fi - if ! $SUDO $tool install $yes_flag $QUIET_FLAG epel-release; then + if ! $tool install $yes_flag $QUIET_FLAG epel-release; then error "Could not enable EPEL. Aborting bootstrap!" exit 1 fi @@ -54,9 +58,8 @@ BootstrapRpmCommon() { ca-certificates " - # Some distros and older versions of current distros use a "python27" - # instead of "python" naming convention. Try both conventions. - if $SUDO $tool list python >/dev/null 2>&1; then + # Most RPM distros use the "python" or "python-" naming convention. Let's try that first. + if $tool list python >/dev/null 2>&1; then pkgs="$pkgs python python-devel @@ -64,6 +67,20 @@ BootstrapRpmCommon() { python-tools python-pip " + # Fedora 26 starts to use the prefix python2 for python2 based packages. + # this elseif is theoretically for any Fedora over version 26: + elif $tool list python2 >/dev/null 2>&1; then + pkgs="$pkgs + python2 + python2-libs + python2-setuptools + python2-devel + python2-virtualenv + python2-tools + python2-pip + " + # Some distros and older versions of current distros use a "python27" + # instead of the "python" or "python-" naming convention. else pkgs="$pkgs python27 @@ -74,13 +91,13 @@ BootstrapRpmCommon() { " fi - if $SUDO $tool list installed "httpd" >/dev/null 2>&1; then + if $tool list installed "httpd" >/dev/null 2>&1; then pkgs="$pkgs mod_ssl " fi - if ! $SUDO $tool install $yes_flag $QUIET_FLAG $pkgs; then + if ! $tool install $yes_flag $QUIET_FLAG $pkgs; then error "Could not install OS dependencies. Aborting bootstrap!" exit 1 fi diff --git a/letsencrypt-auto-source/pieces/bootstrappers/smartos.sh b/letsencrypt-auto-source/pieces/bootstrappers/smartos.sh index e721c1c0b..ac7c0ed4a 100644 --- a/letsencrypt-auto-source/pieces/bootstrappers/smartos.sh +++ b/letsencrypt-auto-source/pieces/bootstrappers/smartos.sh @@ -1,3 +1,7 @@ +# If new packages are installed by BootstrapSmartOS below, this version number +# must be increased. +BOOTSTRAP_SMARTOS_VERSION=1 + BootstrapSmartOS() { pkgin update pkgin -y install 'gcc49' 'py27-augeas' 'py27-virtualenv' diff --git a/letsencrypt-auto-source/pieces/bootstrappers/suse_common.sh b/letsencrypt-auto-source/pieces/bootstrappers/suse_common.sh index e60ca8628..c531cbe99 100755 --- a/letsencrypt-auto-source/pieces/bootstrappers/suse_common.sh +++ b/letsencrypt-auto-source/pieces/bootstrappers/suse_common.sh @@ -1,3 +1,7 @@ +# If new packages are installed by BootstrapSuseCommon below, this version +# number must be increased. +BOOTSTRAP_SUSE_COMMON_VERSION=1 + BootstrapSuseCommon() { # SLE12 don't have python-virtualenv @@ -10,7 +14,7 @@ BootstrapSuseCommon() { QUIET_FLAG='-qq' fi - $SUDO zypper $QUIET_FLAG $zypper_flags in $install_flags \ + zypper $QUIET_FLAG $zypper_flags in $install_flags \ python \ python-devel \ python-virtualenv \ diff --git a/letsencrypt-auto-source/pieces/certbot-requirements.txt b/letsencrypt-auto-source/pieces/certbot-requirements.txt index 3c91c6e76..808a6a8a5 100644 --- a/letsencrypt-auto-source/pieces/certbot-requirements.txt +++ b/letsencrypt-auto-source/pieces/certbot-requirements.txt @@ -1,12 +1,12 @@ -certbot==0.16.0 \ - --hash=sha256:e1cc2479f4f149a7128b67192c3f5f6c111b6b9ddcac421ebee8ac5030d5b09b \ - --hash=sha256:795801fd6b06b32060e364ac045312e6b26c6272d5ca32878277e5a2afdee186 -acme==0.16.0 \ - --hash=sha256:31592a744f7a6e7cbb4c5daf2deebc9af6b03997d6f80e5becc203ab8694edcf \ - --hash=sha256:538b69134cc50bdcdcc081b844c85158509022c61d7abc1f44216beeb95de9cd -certbot-apache==0.16.0 \ - --hash=sha256:337f84f391c7d7c31d84376e7a8c882ac119112a4aabceadd1a7de6a2f01ca06 \ - --hash=sha256:f1f37b56e1ceb0a28ccf51d54ca34444e6a708448845ef25372e223b9a82c4d4 -certbot-nginx==0.16.0 \ - --hash=sha256:1d57428202f8e7abdd99c3ddbcf374aad5f69c4262fe8dae53d2016e4ae04ded \ - --hash=sha256:77711655514d707ddf728195f00b2f6cdd1ad9db52440276b4a67664479c6954 +certbot==0.17.0 \ + --hash=sha256:64c25c7123357feffded6408660bc6f5c7d493dd635ae172081d21473075a86a \ + --hash=sha256:43f5b26c3f314d14babf79a3bdf3522e4fc9eef867a0681c426f113c650a669c +acme==0.17.0 \ + --hash=sha256:501710171633af13fc52aa61d0277a6fe335f7477db5810e72239aaf4f3a09e7 \ + --hash=sha256:3ccbe4aaeb98c77b98ee4093b4e4adb76a1a24cbdfec0130c489c206f1d9b66e +certbot-apache==0.17.0 \ + --hash=sha256:17a7e8d7526d838610e68b96cf052af17c4055655b76b06d1cbc74857d90a216 \ + --hash=sha256:29b9e7bc5eaaff6dc4bce8398e35eeacdf346126aad68cac3d41bb87df20a6b9 +certbot-nginx==0.17.0 \ + --hash=sha256:980c9a33a79ab839a089a0085ff0c5414f01f47b6db26ed342df25916658cec9 \ + --hash=sha256:e573f8b4283172755c07b9cca8a8da7ef2d31b4df763881394b5339b2d42994a diff --git a/letsencrypt-auto-source/pieces/dependency-requirements.txt b/letsencrypt-auto-source/pieces/dependency-requirements.txt index a8007ba3e..4b3da685c 100644 --- a/letsencrypt-auto-source/pieces/dependency-requirements.txt +++ b/letsencrypt-auto-source/pieces/dependency-requirements.txt @@ -58,29 +58,41 @@ cffi==1.10.0 \ --hash=sha256:285ab352552f52f1398c912556d4d36d4ea9b8450e5c65d03809bf9886755533 \ --hash=sha256:5576644b859197da7bbd8f8c7c2fb5dcc6cd505cadb42992d5f104c013f8a214 \ --hash=sha256:b3b02911eb1f6ada203b0763ba924234629b51586f72a21faacc638269f4ced5 -ConfigArgParse==0.10.0 \ - --hash=sha256:3b50a83dd58149dfcee98cb6565265d10b53e9c0a2bca7eeef7fb5f5524890a7 +ConfigArgParse==0.12.0 \ + --hash=sha256:28cd7d67669651f2a4518367838c49539457504584a139709b2b8f6c208ef339 configobj==5.0.6 \ --hash=sha256:a2f5650770e1c87fb335af19a9b7eb73fc05ccf22144eb68db7d00cd2bcb0902 -cryptography==1.9 \ - --hash=sha256:469a9d3d851038f1eb7d7f77bb08bb4775b41483372be450e25b293fe57bd59e \ - --hash=sha256:533143321d15c8743f91eec5c5f495c1b5cad9a25de8f6dab01eddd6b416903e \ - --hash=sha256:2230c186182d773064d06242e0fa604cd718bfff28aa9c5ae73d7e426e98a151 \ - --hash=sha256:3dc94ed5a26b8553a325767358f505c0a43e0c89df078647f77a4d022ddcdc57 \ - --hash=sha256:2eb8297b877cb6b56216750fa7017c9f5786bec8afd6a0f1aaace02cbfb6195f \ - --hash=sha256:025a96e48164106f2082b00f42bf430cf21f09e203e42585a712e420b75cbff0 \ - --hash=sha256:61eb3534f8ed2415dd708b28919205d523f220e4cd5b8165844edfdd4a649b8e \ - --hash=sha256:5474fe5ce6b517d3086e0231b6ad88f8978c551c4379f91c3d619c308490f0d7 \ - --hash=sha256:5ff169869624e23767d70db274f13a9ea4e97c029425a1224aa5e049e84ce2af \ - --hash=sha256:c26e057a2de13e97e708328d295c5ac4cd3eab4a5c42ce727dd1a53316034b8a \ - --hash=sha256:7db719432648f14ea33edffc5f75330c595804eac86ca916528b35ce50a8bfd6 \ - --hash=sha256:ca72537a7064bb80e34b6908e576ffc8e2c2cad29a7168f48d0999df089695c3 \ - --hash=sha256:2a5e577f5d2093e51486b4ec02b51bb5adb165b98fee99929d5af0813e90b469 \ - --hash=sha256:9d9da8bac2e31003d092f5ef6981a725c77c4e9a30638436884d61ad39f9a1ee \ - --hash=sha256:fab8ec6866e384d9827d5dc02a1383e991a6c05c54f818316c4b829e56ca2ba3 \ - --hash=sha256:365eb804362e581c9a02e7a610b30514f07dd77b2a38aed338eb5192446bbc58 \ - --hash=sha256:2908f709f02711dbb10561a9f154d2f7d1792385e2341e9708539cc4ecfb8667 \ - --hash=sha256:5518337022718029e367d982642f3e3523541e098ad671672a90b82474c84882 +cryptography==2.0.2 \ + --hash=sha256:187ae17358436d2c760f28c2aeb02fefa3f37647a9c5b6f7f7c3e83cd1c5a972 \ + --hash=sha256:19e43a13bbf52028dd1e810c803f2ad8880d0692d772f98d42e1eaf34bdee3d6 \ + --hash=sha256:da9291502cbc87dc0284a20c56876e4d2e68deac61cc43df4aec934e44ca97b1 \ + --hash=sha256:0954f8813095f581669330e0a2d5e726c33ac7f450c1458fac58bab54595e516 \ + --hash=sha256:d68b0cc40a8432ed3fc84876c519de704d6001800ec22b136e75ae841910c45b \ + --hash=sha256:2f8ad9580ab4da645cfea52a91d2da99a49a1e76616d8be68441a986fad652b0 \ + --hash=sha256:cc00b4511294f5f6b65c4e77a1a9c62f52490a63d2c120f3872176b40a82351e \ + --hash=sha256:cf896020f6a9f095a547b3d672c8db1ef2ed71fca11250731fa1d4a4cb8b1590 \ + --hash=sha256:e0fdb8322206fa02aa38f71519ff75dce2eb481b7e1110e2936795cb376bb6ee \ + --hash=sha256:277538466657ca5d6637f80be100242f9831d75138b788d718edd3aab34621f8 \ + --hash=sha256:2c77eb0560f54ce654ab82d6b2a64327a71ee969b29022bf9746ca311c9f5069 \ + --hash=sha256:755a7853b679e79d0a799351c092a9b0271f95ff54c8dd8823d8b527a2926a86 \ + --hash=sha256:77197a2d525e761cdd4c771180b4bd0d80703654c6385e4311cbbbe2beb56fa1 \ + --hash=sha256:eb8bb79d0ab00c931c8333b745f06fec481a51c52d70acd4ee95d6093ba5c386 \ + --hash=sha256:131f61de82ef28f3e20beb4bfc24f9692d28cecfd704e20e6c7f070f7793013a \ + --hash=sha256:ac35435974b2e27cd4520f29c191d7da36f4189aa3264e52c4c6c6d089ab6142 \ + --hash=sha256:04b6ea99daa2a8460728794213d76d45ad58ea247dc7e7ff148d7dd726e87863 \ + --hash=sha256:2b9442f8b4c3d575f6cc3db0e856034e0f5a9d55ecd636f52d8c496795b26952 \ + --hash=sha256:b3d3b3ecba1fe1bdb6f180770a137f877c8f07571f7b2934bb269475bcf0e5e8 \ + --hash=sha256:670a58c0d75cb0e78e73dd003bd96d4440bbb1f2bc041dcf7b81767ca4fb0ce9 \ + --hash=sha256:5af84d23bdb86b5e90aca263df1424b43f1748480bfcde3ac2a3cbe622612468 \ + --hash=sha256:ba22e8eefabdd7aca37d0c0c00d2274000d2cebb5cce9e5a710cb55bf8797b31 \ + --hash=sha256:b798b22fa7e92b439547323b8b719d217f1e1b7677585cfeeedf3b55c70bb7fb \ + --hash=sha256:59cff28af8cce96cb7e94a459726e1d88f6f5fa75097f9dcbebd99118d64ea4c \ + --hash=sha256:fe859e445abc9ba9e97950ddafb904e23234c4ecb76b0fae6c86e80592ce464a \ + --hash=sha256:655f3c474067f1e277430f23cc0549f0b1dc99b82aec6e53f80b9b2db7f76f11 \ + --hash=sha256:0ebc2be053c9a03a2f3e20a466e87bf12a51586b3c79bd2a22171b073a805346 \ + --hash=sha256:01e6e60654df64cca53733cda39446d67100c819c181d403afb120e0d2a71e1b \ + --hash=sha256:d46f4e5d455cb5563685c52ef212696f0a6cc1ea627603218eabbd8a095291d8 \ + --hash=sha256:3780b2663ee7ebb37cb83263326e3cd7f8b2ea439c448539d4b87de12c8d06ab enum34==1.1.2 \ --hash=sha256:2475d7fcddf5951e92ff546972758802de5260bf409319a9f1934e6bbc8b1dc7 \ --hash=sha256:35907defb0f992b75ab7788f65fedc1cf20ffa22688e0e6f6f12afc06b3ea501 diff --git a/letsencrypt-auto-source/pieces/pipstrap.py b/letsencrypt-auto-source/pieces/pipstrap.py index 505f8ca72..78491b5e3 100755 --- a/letsencrypt-auto-source/pieces/pipstrap.py +++ b/letsencrypt-auto-source/pieces/pipstrap.py @@ -21,6 +21,7 @@ anything goes wrong, it will exit with a non-zero status code. # The above copyright notice and this permission notice shall be included in # all copies or substantial portions of the Software. from __future__ import print_function +from distutils.version import StrictVersion from hashlib import sha256 from os.path import join from pipes import quote @@ -55,12 +56,14 @@ except ImportError: from urllib.parse import urlparse # 3.4 -__version__ = 1, 1, 1 +__version__ = 1, 3, 0 +PIP_VERSION = '9.0.1' # wheel has a conditional dependency on argparse: maybe_argparse = ( - [('https://pypi.python.org/packages/source/a/argparse/' + [('https://pypi.python.org/packages/18/dd/' + 'e617cfc3f6210ae183374cd9f6a26b20514bbb5a792af97949c5aacddf0f/' 'argparse-1.4.0.tar.gz', '62b089a55be1d8949cd2bc7e0df0bddb9e028faefc8c32038cc84862aefdd6e4')] if version_info < (2, 7, 0) else []) @@ -68,13 +71,19 @@ maybe_argparse = ( PACKAGES = maybe_argparse + [ # Pip has no dependencies, as it vendors everything: - ('https://pypi.python.org/packages/source/p/pip/pip-8.0.3.tar.gz', - '30f98b66f3fe1069c529a491597d34a1c224a68640c82caf2ade5f88aa1405e8'), + ('https://pypi.python.org/packages/11/b6/' + 'abcb525026a4be042b486df43905d6893fb04f05aac21c32c638e939e447/' + 'pip-{0}.tar.gz' + .format(PIP_VERSION), + '09f243e1a7b461f654c26a725fa373211bb7ff17a9300058b205c61658ca940d'), # This version of setuptools has only optional dependencies: - ('https://pypi.python.org/packages/source/s/setuptools/' + ('https://pypi.python.org/packages/69/65/' + '4c544cde88d4d876cdf5cbc5f3f15d02646477756d89547e9a7ecd6afa76/' 'setuptools-20.2.2.tar.gz', '24fcfc15364a9fe09a220f37d2dcedc849795e3de3e4b393ee988e66a9cbd85a'), - ('https://pypi.python.org/packages/source/w/wheel/wheel-0.29.0.tar.gz', + ('https://pypi.python.org/packages/c9/1d/' + 'bd19e691fd4cfe908c76c429fe6e4436c9e83583c4414b54f6c85471954a/' + 'wheel-0.29.0.tar.gz', '1ebb8ad7e26b448e9caa4773d2357849bf80ff9e313964bcaf79cbf0201a1648') ] @@ -124,11 +133,21 @@ def hashed_download(url, temp, digest): def main(): + pip_version = StrictVersion(check_output(['pip', '--version']) + .decode('utf-8').split()[1]) + min_pip_version = StrictVersion(PIP_VERSION) + if pip_version >= min_pip_version: + return 0 + has_pip_cache = pip_version >= StrictVersion('6.0') + temp = mkdtemp(prefix='pipstrap-') try: downloads = [hashed_download(url, temp, digest) for url, 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: + ('--no-cache-dir ' if has_pip_cache else '') + ' '.join(quote(d) for d in downloads), shell=True) except HashError as exc: diff --git a/letsencrypt-auto-source/tests/auto_test.py b/letsencrypt-auto-source/tests/auto_test.py index 6f21c28d5..5c63325ee 100644 --- a/letsencrypt-auto-source/tests/auto_test.py +++ b/letsencrypt-auto-source/tests/auto_test.py @@ -4,7 +4,7 @@ from BaseHTTPServer import HTTPServer, BaseHTTPRequestHandler from contextlib import contextmanager from functools import partial from json import dumps -from os import chmod, environ +from os import chmod, environ, makedirs from os.path import abspath, dirname, exists, join import re from shutil import copy, rmtree @@ -118,12 +118,13 @@ LE_AUTO_PATH = join(dirname(tests_dir()), 'letsencrypt-auto') @contextmanager -def ephemeral_dir(): +def temp_paths(): + """Creates and deletes paths for letsencrypt-auto and its venv.""" dir = mkdtemp(prefix='le-test-') try: - yield dir + yield join(dir, 'letsencrypt-auto'), join(dir, 'venv') finally: - rmtree(dir) + rmtree(dir, ignore_errors=True) def out_and_err(command, input=None, shell=False, env=None): @@ -160,21 +161,20 @@ def signed(content, private_key_name='signing.key'): return out -def install_le_auto(contents, venv_dir): +def install_le_auto(contents, install_path): """Install some given source code as the letsencrypt-auto script at the root level of a virtualenv. :arg contents: The contents of the built letsencrypt-auto script - :arg venv_dir: The path under which to install the script + :arg install_path: The path where to install the script """ - venv_le_auto_path = join(venv_dir, 'letsencrypt-auto') - with open(venv_le_auto_path, 'w') as le_auto: + with open(install_path, 'w') as le_auto: le_auto.write(contents) - chmod(venv_le_auto_path, S_IRUSR | S_IXUSR) + chmod(install_path, S_IRUSR | S_IXUSR) -def run_le_auto(venv_dir, base_url, **kwargs): +def run_le_auto(le_auto_path, venv_dir, base_url, **kwargs): """Run the prebuilt version of letsencrypt-auto, returning stdout and stderr strings. @@ -182,7 +182,7 @@ def run_le_auto(venv_dir, base_url, **kwargs): """ env = environ.copy() - d = dict(XDG_DATA_HOME=venv_dir, + d = dict(VENV_PATH=venv_dir, # URL to PyPI-style JSON that tell us the latest released version # of LE: LE_AUTO_JSON_URL=base_url + 'certbot/json', @@ -201,7 +201,7 @@ iQIDAQAB **kwargs) env.update(d) return out_and_err( - join(venv_dir, 'letsencrypt-auto') + ' --version', + le_auto_path + ' --version', shell=True, env=env) @@ -213,10 +213,12 @@ def set_le_script_version(venv_dir, version): print its version. """ - with open(join(venv_dir, 'letsencrypt', 'bin', 'letsencrypt'), 'w') as script: + letsencrypt_path = join(venv_dir, 'bin', 'letsencrypt') + with open(letsencrypt_path, 'w') as script: script.write("#!/usr/bin/env python\n" "from sys import stderr\n" "stderr.write('letsencrypt %s\\n')" % version) + chmod(letsencrypt_path, S_IRUSR | S_IXUSR) class AutoTests(TestCase): @@ -237,6 +239,11 @@ class AutoTests(TestCase): test suites. """ + NEW_LE_AUTO = build_le_auto( + version='99.9.9', + requirements='letsencrypt==99.9.9 --hash=sha256:1cc14d61ab424cdee446f51e50f1123f8482ec740587fe78626c933bba2873a0') + NEW_LE_AUTO_SIG = signed(NEW_LE_AUTO) + def test_successes(self): """Exercise most branches of letsencrypt-auto. @@ -252,20 +259,16 @@ class AutoTests(TestCase): the next, saving code. """ - NEW_LE_AUTO = build_le_auto( - version='99.9.9', - requirements='letsencrypt==99.9.9 --hash=sha256:1cc14d61ab424cdee446f51e50f1123f8482ec740587fe78626c933bba2873a0') - NEW_LE_AUTO_SIG = signed(NEW_LE_AUTO) - - with ephemeral_dir() as venv_dir: + with temp_paths() as (le_auto_path, venv_dir): # This serves a PyPI page with a higher version, a GitHub-alike # with a corresponding le-auto script, and a matching signature. resources = {'certbot/json': dumps({'releases': {'99.9.9': None}}), - 'v99.9.9/letsencrypt-auto': NEW_LE_AUTO, - 'v99.9.9/letsencrypt-auto.sig': NEW_LE_AUTO_SIG} + 'v99.9.9/letsencrypt-auto': self.NEW_LE_AUTO, + 'v99.9.9/letsencrypt-auto.sig': self.NEW_LE_AUTO_SIG} with serving(resources) as base_url: run_letsencrypt_auto = partial( run_le_auto, + le_auto_path, venv_dir, base_url, PIP_FIND_LINKS=join(tests_dir(), @@ -274,7 +277,7 @@ class AutoTests(TestCase): # Test when a phase-1 upgrade is needed, there's no LE binary # installed, and pip hashes verify: - install_le_auto(build_le_auto(version='50.0.0'), venv_dir) + install_le_auto(build_le_auto(version='50.0.0'), le_auto_path) out, err = run_letsencrypt_auto() ok_(re.match(r'letsencrypt \d+\.\d+\.\d+', err.strip().splitlines()[-1])) @@ -291,16 +294,28 @@ class AutoTests(TestCase): self.assertFalse('Upgrading certbot-auto ' in out) self.assertFalse('Creating virtual environment...' in out) - # Test when a phase-1 upgrade is not needed but a phase-2 - # upgrade is: + def test_phase2_upgrade(self): + """Test a phase-2 upgrade without a phase-1 upgrade.""" + with temp_paths() as (le_auto_path, venv_dir): + resources = {'certbot/json': dumps({'releases': {'99.9.9': None}}), + 'v99.9.9/letsencrypt-auto': self.NEW_LE_AUTO, + 'v99.9.9/letsencrypt-auto.sig': self.NEW_LE_AUTO_SIG} + with serving(resources) as base_url: + venv_bin = join(venv_dir, 'bin') + makedirs(venv_bin) set_le_script_version(venv_dir, '0.0.1') - out, err = run_letsencrypt_auto() + + install_le_auto(self.NEW_LE_AUTO, le_auto_path) + pip_find_links=join(tests_dir(), 'fake-letsencrypt', 'dist') + out, err = run_le_auto(le_auto_path, venv_dir, base_url, + PIP_FIND_LINKS=pip_find_links) + self.assertFalse('Upgrading certbot-auto ' in out) self.assertTrue('Creating virtual environment...' in out) def test_openssl_failure(self): """Make sure we stop if the openssl signature check fails.""" - with ephemeral_dir() as venv_dir: + with temp_paths() as (le_auto_path, venv_dir): # Serve an unrelated hash signed with the good key (easier than # making a bad key, and a mismatch is a mismatch): resources = {'': 'certbot/', @@ -308,9 +323,9 @@ class AutoTests(TestCase): 'v99.9.9/letsencrypt-auto': build_le_auto(version='99.9.9'), 'v99.9.9/letsencrypt-auto.sig': signed('something else')} with serving(resources) as base_url: - copy(LE_AUTO_PATH, venv_dir) + copy(LE_AUTO_PATH, le_auto_path) try: - out, err = run_le_auto(venv_dir, base_url) + out, err = run_le_auto(le_auto_path, venv_dir, base_url) except CalledProcessError as exc: eq_(exc.returncode, 1) self.assertTrue("Couldn't verify signature of downloaded " @@ -320,7 +335,7 @@ class AutoTests(TestCase): def test_pip_failure(self): """Make sure pip stops us if there is a hash mismatch.""" - with ephemeral_dir() as venv_dir: + with temp_paths() as (le_auto_path, venv_dir): resources = {'': 'certbot/', 'certbot/json': dumps({'releases': {'99.9.9': None}})} with serving(resources) as base_url: @@ -329,14 +344,14 @@ class AutoTests(TestCase): build_le_auto( version='99.9.9', requirements='configobj==5.0.6 --hash=sha256:badbadbadbadbadbadbadbadbadbadbadbadbadbadbadbadbadbadbadbadbadb'), - venv_dir) + le_auto_path) try: - out, err = run_le_auto(venv_dir, base_url) + out, err = run_le_auto(le_auto_path, venv_dir, base_url) except CalledProcessError as exc: eq_(exc.returncode, 1) self.assertTrue("THESE PACKAGES DO NOT MATCH THE HASHES " "FROM THE REQUIREMENTS FILE" in exc.output) - ok_(not exists(join(venv_dir, 'letsencrypt')), + ok_(not exists(venv_dir), msg="The virtualenv was left around, even though " "installation didn't succeed. We shouldn't do " "this, as it foils our detection of whether we " diff --git a/tox.ini b/tox.ini index 299b7f20f..dee14b8b3 100644 --- a/tox.ini +++ b/tox.ini @@ -85,11 +85,14 @@ deps = configobj==4.7.2 cryptography==1.2.3 enum34==0.9.23 + google-api-python-client==1.5 idna==2.0 ipaddress==1.0.16 mock==1.0.1 ndg-httpsclient==0.3.2 + oauth2client==2.0 parsedatetime==1.4 + pyasn1-modules==0.0.5 pyasn1==0.1.9 pyparsing==1.5.6 pyrfc3339==1.0