Committing before git pull to make merge error go away

This commit is contained in:
Peter Conrad 2017-11-16 11:57:56 -08:00
commit defdf39701
87 changed files with 1601 additions and 1020 deletions

View file

@ -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

View file

@ -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

View file

@ -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 = [

View file

@ -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 = [

View file

@ -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

View file

@ -8,8 +8,8 @@ MAINTAINER Brad Warren <bmw@eff.org>
# 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

View file

@ -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',

View file

@ -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 = [

View file

@ -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 = [

View file

@ -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 = [

View file

@ -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 = [

View file

@ -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 = [

View file

@ -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',

View file

@ -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 = [

View file

@ -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 = [

View file

@ -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 = [

View file

@ -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),

View file

@ -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,

View file

@ -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

View file

@ -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'

View file

@ -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 = [

View file

@ -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'

View file

@ -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."""

View file

@ -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

View file

@ -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:

View file

@ -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(

View file

@ -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):

View file

@ -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

View file

@ -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)

View file

@ -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(
"<Account(i_am_a_regr, bca5889f66457d5b62fbba7b25f9ab6f, Meta("))
"<Account(i_am_a_regr, 7adac10320f585ddf118429c0c4af2cd, Meta("))
class ReportNewAccountTest(unittest.TestCase):
class ReportNewAccountTest(test_util.ConfigTestCase):
"""Tests for certbot.account.report_new_account."""
def setUp(self):
self.config = mock.MagicMock(config_dir="/etc/letsencrypt")
def _call(self):
from certbot.account import report_new_account
report_new_account(self.config)
@ -98,14 +93,12 @@ class AccountMemoryStorageTest(unittest.TestCase):
self.assertEqual([account], self.storage.find_all())
class AccountFileStorageTest(TempDirTestCase):
class AccountFileStorageTest(test_util.ConfigTestCase):
"""Tests for certbot.account.AccountFileStorage."""
def setUp(self):
super(AccountFileStorageTest, self).setUp()
self.config = mock.MagicMock(
accounts_dir=os.path.join(self.tempdir, "accounts"))
from certbot.account import AccountFileStorage
self.storage = AccountFileStorage(self.config)

View file

@ -18,58 +18,50 @@ from certbot.storage import ALL_FOUR
from certbot.tests import storage_test
from certbot.tests import util as test_util
from certbot.tests.util import TempDirTestCase
class BaseCertManagerTest(TempDirTestCase):
class BaseCertManagerTest(test_util.ConfigTestCase):
"""Base class for setting up Cert Manager tests.
"""
def setUp(self):
super(BaseCertManagerTest, self).setUp()
os.makedirs(os.path.join(self.tempdir, "renewal"))
self.cli_config = configuration.NamespaceConfig(mock.MagicMock(
config_dir=self.tempdir,
work_dir=self.tempdir,
logs_dir=self.tempdir,
quiet=False,
))
self.config.quiet = False
os.makedirs(self.config.renewal_configs_dir)
self.domains = {
"example.org": None,
"other.com": os.path.join(self.tempdir, "specialarchive")
"other.com": os.path.join(self.config.config_dir, "specialarchive")
}
self.configs = dict((domain, self._set_up_config(domain, self.domains[domain]))
self.config_files = dict((domain, self._set_up_config(domain, self.domains[domain]))
for domain in self.domains)
# We also create a file that isn't a renewal config in the same
# location to test that logic that reads in all-and-only renewal
# configs will ignore it and NOT attempt to parse it.
junk = open(os.path.join(self.tempdir, "renewal", "IGNORE.THIS"), "w")
junk = open(os.path.join(self.config.renewal_configs_dir, "IGNORE.THIS"), "w")
junk.write("This file should be ignored!")
junk.close()
def _set_up_config(self, domain, custom_archive):
# TODO: maybe provide NamespaceConfig.make_dirs?
# TODO: main() should create those dirs, c.f. #902
os.makedirs(os.path.join(self.tempdir, "live", domain))
config = configobj.ConfigObj()
os.makedirs(os.path.join(self.config.live_dir, domain))
config_file = configobj.ConfigObj()
if custom_archive is not None:
os.makedirs(custom_archive)
config["archive_dir"] = custom_archive
config_file["archive_dir"] = custom_archive
else:
os.makedirs(os.path.join(self.tempdir, "archive", domain))
os.makedirs(os.path.join(self.config.default_archive_dir, domain))
for kind in ALL_FOUR:
config[kind] = os.path.join(self.tempdir, "live", domain,
config_file[kind] = os.path.join(self.config.live_dir, domain,
kind + ".pem")
config.filename = os.path.join(self.tempdir, "renewal",
config_file.filename = os.path.join(self.config.renewal_configs_dir,
domain + ".conf")
config.write()
return config
config_file.write()
return config_file
class UpdateLiveSymlinksTest(BaseCertManagerTest):
@ -86,27 +78,27 @@ class UpdateLiveSymlinksTest(BaseCertManagerTest):
if custom_archive is not None:
archive_dir_path = custom_archive
else:
archive_dir_path = os.path.join(self.tempdir, "archive", domain)
archive_dir_path = os.path.join(self.config.default_archive_dir, domain)
archive_paths[domain] = dict((kind,
os.path.join(archive_dir_path, kind + "1.pem")) for kind in ALL_FOUR)
for kind in ALL_FOUR:
live_path = self.configs[domain][kind]
live_path = self.config_files[domain][kind]
archive_path = archive_paths[domain][kind]
open(archive_path, 'a').close()
# path is incorrect but base must be correct
os.symlink(os.path.join(self.tempdir, kind + "1.pem"), live_path)
os.symlink(os.path.join(self.config.config_dir, kind + "1.pem"), live_path)
# run update symlinks
cert_manager.update_live_symlinks(self.cli_config)
cert_manager.update_live_symlinks(self.config)
# check that symlinks go where they should
prev_dir = os.getcwd()
try:
for domain in self.domains:
for kind in ALL_FOUR:
os.chdir(os.path.dirname(self.configs[domain][kind]))
os.chdir(os.path.dirname(self.config_files[domain][kind]))
self.assertEqual(
os.path.realpath(os.readlink(self.configs[domain][kind])),
os.path.realpath(os.readlink(self.config_files[domain][kind])),
os.path.realpath(archive_paths[domain][kind]))
finally:
os.chdir(prev_dir)
@ -121,9 +113,9 @@ class DeleteTest(storage_test.BaseRenewableCertTest):
def test_delete(self, mock_delete_files, mock_lineage_for_certname, unused_get_utility):
"""Test delete"""
mock_lineage_for_certname.return_value = self.test_rc
self.cli_config.certname = "example.org"
self.config.certname = "example.org"
from certbot import cert_manager
cert_manager.delete(self.cli_config)
cert_manager.delete(self.config)
self.assertTrue(mock_delete_files.called)
@ -137,15 +129,15 @@ class CertificatesTest(BaseCertManagerTest):
@mock.patch('certbot.cert_manager.logger')
@test_util.patch_get_utility()
def test_certificates_parse_fail(self, mock_utility, mock_logger):
self._certificates(self.cli_config)
self._certificates(self.config)
self.assertTrue(mock_logger.warning.called) #pylint: disable=no-member
self.assertTrue(mock_utility.called)
@mock.patch('certbot.cert_manager.logger')
@test_util.patch_get_utility()
def test_certificates_quiet(self, mock_utility, mock_logger):
self.cli_config.quiet = True
self._certificates(self.cli_config)
self.config.quiet = True
self._certificates(self.config)
self.assertFalse(mock_utility.notification.called)
self.assertTrue(mock_logger.warning.called) #pylint: disable=no-member
@ -158,7 +150,7 @@ class CertificatesTest(BaseCertManagerTest):
mock_utility, mock_logger, mock_verifier):
mock_verifier.return_value = None
mock_report.return_value = ""
self._certificates(self.cli_config)
self._certificates(self.config)
self.assertFalse(mock_logger.warning.called) #pylint: disable=no-member
self.assertTrue(mock_report.called)
self.assertTrue(mock_utility.called)
@ -167,20 +159,19 @@ class CertificatesTest(BaseCertManagerTest):
@mock.patch('certbot.cert_manager.logger')
@test_util.patch_get_utility()
def test_certificates_no_files(self, mock_utility, mock_logger):
tempdir = tempfile.mkdtemp()
cli_config = configuration.NamespaceConfig(mock.MagicMock(
config_dir=tempdir,
work_dir=tempdir,
logs_dir=tempdir,
quiet=False,
empty_tempdir = tempfile.mkdtemp()
empty_config = configuration.NamespaceConfig(mock.MagicMock(
config_dir=os.path.join(empty_tempdir, "config"),
work_dir=os.path.join(empty_tempdir, "work"),
logs_dir=os.path.join(empty_tempdir, "logs"),
quiet=False
))
os.makedirs(os.path.join(tempdir, "renewal"))
self._certificates(cli_config)
os.makedirs(empty_config.renewal_configs_dir)
self._certificates(empty_config)
self.assertFalse(mock_logger.warning.called) #pylint: disable=no-member
self.assertTrue(mock_utility.called)
shutil.rmtree(tempdir)
shutil.rmtree(empty_tempdir)
@mock.patch('certbot.cert_manager.ocsp.RevocationChecker.ocsp_revoked')
def test_report_human_readable(self, mock_revoked):
@ -261,7 +252,7 @@ class SearchLineagesTest(BaseCertManagerTest):
mock_renewable_cert.side_effect = errors.CertStorageError
from certbot import cert_manager
# pylint: disable=protected-access
self.assertEqual(cert_manager._search_lineages(self.cli_config, lambda x: x, "check"),
self.assertEqual(cert_manager._search_lineages(self.config, lambda x: x, "check"),
"check")
self.assertTrue(mock_make_or_verify_dir.called)
@ -278,7 +269,7 @@ class LineageForCertnameTest(BaseCertManagerTest):
mock_match = mock.Mock(lineagename="example.com")
mock_renewable_cert.return_value = mock_match
from certbot import cert_manager
self.assertEqual(cert_manager.lineage_for_certname(self.cli_config, "example.com"),
self.assertEqual(cert_manager.lineage_for_certname(self.config, "example.com"),
mock_match)
self.assertTrue(mock_make_or_verify_dir.called)
@ -288,7 +279,7 @@ class LineageForCertnameTest(BaseCertManagerTest):
mock_make_or_verify_dir):
mock_renewal_conf_file.return_value = "other.com.conf"
from certbot import cert_manager
self.assertEqual(cert_manager.lineage_for_certname(self.cli_config, "example.com"),
self.assertEqual(cert_manager.lineage_for_certname(self.config, "example.com"),
None)
self.assertTrue(mock_make_or_verify_dir.called)
@ -298,7 +289,7 @@ class LineageForCertnameTest(BaseCertManagerTest):
mock_make_or_verify_dir):
mock_renewal_conf_file.side_effect = errors.CertStorageError()
from certbot import cert_manager
self.assertEqual(cert_manager.lineage_for_certname(self.cli_config, "example.com"),
self.assertEqual(cert_manager.lineage_for_certname(self.config, "example.com"),
None)
self.assertTrue(mock_make_or_verify_dir.called)
@ -317,7 +308,7 @@ class DomainsForCertnameTest(BaseCertManagerTest):
mock_match.names.return_value = domains
mock_renewable_cert.return_value = mock_match
from certbot import cert_manager
self.assertEqual(cert_manager.domains_for_certname(self.cli_config, "example.com"),
self.assertEqual(cert_manager.domains_for_certname(self.config, "example.com"),
domains)
self.assertTrue(mock_make_or_verify_dir.called)
@ -327,7 +318,7 @@ class DomainsForCertnameTest(BaseCertManagerTest):
mock_make_or_verify_dir):
mock_renewal_conf_file.return_value = "somefile.conf"
from certbot import cert_manager
self.assertEqual(cert_manager.domains_for_certname(self.cli_config, "other.com"),
self.assertEqual(cert_manager.domains_for_certname(self.config, "other.com"),
None)
self.assertTrue(mock_make_or_verify_dir.called)
@ -337,15 +328,8 @@ class RenameLineageTest(BaseCertManagerTest):
def setUp(self):
super(RenameLineageTest, self).setUp()
self.mock_config = configuration.NamespaceConfig(
namespace=mock.MagicMock(
config_dir=self.tempdir,
work_dir=self.tempdir,
logs_dir=self.tempdir,
certname="example.org",
new_certname="after",
)
)
self.config.certname = "example.org"
self.config.new_certname = "after"
def _call(self, *args, **kwargs):
from certbot import cert_manager
@ -354,81 +338,81 @@ class RenameLineageTest(BaseCertManagerTest):
@mock.patch('certbot.storage.renewal_conf_files')
@test_util.patch_get_utility()
def test_no_certname(self, mock_get_utility, mock_renewal_conf_files):
mock_config = mock.Mock(certname=None, new_certname="two")
self.config.certname = None
self.config.new_certname = "two"
# if not choices
mock_renewal_conf_files.return_value = []
self.assertRaises(errors.Error, self._call, mock_config)
self.assertRaises(errors.Error, self._call, self.config)
mock_renewal_conf_files.return_value = ["one.conf"]
util_mock = mock.Mock()
util_mock.menu.return_value = (display_util.CANCEL, 0)
mock_get_utility.return_value = util_mock
self.assertRaises(errors.Error, self._call, mock_config)
self.assertRaises(errors.Error, self._call, self.config)
util_mock.menu.return_value = (display_util.OK, -1)
self.assertRaises(errors.Error, self._call, mock_config)
self.assertRaises(errors.Error, self._call, self.config)
@test_util.patch_get_utility()
def test_no_new_certname(self, mock_get_utility):
mock_config = mock.Mock(certname="one", new_certname=None)
self.config.certname = "one"
self.config.new_certname = None
util_mock = mock.Mock()
util_mock.input.return_value = (display_util.CANCEL, "name")
mock_get_utility.return_value = util_mock
self.assertRaises(errors.Error, self._call, mock_config)
self.assertRaises(errors.Error, self._call, self.config)
util_mock = mock.Mock()
util_mock.input.return_value = (display_util.OK, None)
mock_get_utility.return_value = util_mock
self.assertRaises(errors.Error, self._call, mock_config)
self.assertRaises(errors.Error, self._call, self.config)
@test_util.patch_get_utility()
@mock.patch('certbot.cert_manager.lineage_for_certname')
def test_no_existing_certname(self, mock_lineage_for_certname, unused_get_utility):
mock_config = mock.Mock(certname="one", new_certname="two")
self.config.certname = "one"
self.config.new_certname = "two"
mock_lineage_for_certname.return_value = None
self.assertRaises(errors.ConfigurationError,
self._call, mock_config)
self._call, self.config)
@test_util.patch_get_utility()
@mock.patch("certbot.storage.RenewableCert._check_symlinks")
def test_rename_cert(self, mock_check, unused_get_utility):
mock_check.return_value = True
mock_config = self.mock_config
self._call(mock_config)
self._call(self.config)
from certbot import cert_manager
updated_lineage = cert_manager.lineage_for_certname(mock_config, mock_config.new_certname)
updated_lineage = cert_manager.lineage_for_certname(self.config, self.config.new_certname)
self.assertTrue(updated_lineage is not None)
self.assertEqual(updated_lineage.lineagename, mock_config.new_certname)
self.assertEqual(updated_lineage.lineagename, self.config.new_certname)
@test_util.patch_get_utility()
@mock.patch("certbot.storage.RenewableCert._check_symlinks")
def test_rename_cert_interactive_certname(self, mock_check, mock_get_utility):
mock_check.return_value = True
mock_config = self.mock_config
mock_config.certname = None
self.config.certname = None
util_mock = mock.Mock()
util_mock.menu.return_value = (display_util.OK, 0)
mock_get_utility.return_value = util_mock
self._call(mock_config)
self._call(self.config)
from certbot import cert_manager
updated_lineage = cert_manager.lineage_for_certname(mock_config, mock_config.new_certname)
updated_lineage = cert_manager.lineage_for_certname(self.config, self.config.new_certname)
self.assertTrue(updated_lineage is not None)
self.assertEqual(updated_lineage.lineagename, mock_config.new_certname)
self.assertEqual(updated_lineage.lineagename, self.config.new_certname)
@test_util.patch_get_utility()
@mock.patch("certbot.storage.RenewableCert._check_symlinks")
def test_rename_cert_bad_new_certname(self, mock_check, unused_get_utility):
mock_check.return_value = True
mock_config = self.mock_config
# for example, don't rename to existing certname
mock_config.new_certname = "example.org"
self.assertRaises(errors.ConfigurationError, self._call, mock_config)
self.config.new_certname = "example.org"
self.assertRaises(errors.ConfigurationError, self._call, self.config)
mock_config.new_certname = "one{0}two".format(os.path.sep)
self.assertRaises(errors.ConfigurationError, self._call, mock_config)
self.config.new_certname = "one{0}two".format(os.path.sep)
self.assertRaises(errors.ConfigurationError, self._call, self.config)
class DuplicativeCertsTest(storage_test.BaseRenewableCertTest):
@ -436,36 +420,36 @@ class DuplicativeCertsTest(storage_test.BaseRenewableCertTest):
def setUp(self):
super(DuplicativeCertsTest, self).setUp()
self.config.write()
self.config_file.write()
self._write_out_ex_kinds()
@mock.patch('certbot.util.make_or_verify_dir')
def test_find_duplicative_names(self, unused_makedir):
from certbot.cert_manager import find_duplicative_certs
test_cert = test_util.load_vector('cert-san.pem')
test_cert = test_util.load_vector('cert-san_512.pem')
with open(self.test_rc.cert, 'wb') as f:
f.write(test_cert)
# No overlap at all
result = find_duplicative_certs(
self.cli_config, ['wow.net', 'hooray.org'])
self.config, ['wow.net', 'hooray.org'])
self.assertEqual(result, (None, None))
# Totally identical
result = find_duplicative_certs(
self.cli_config, ['example.com', 'www.example.com'])
self.config, ['example.com', 'www.example.com'])
self.assertTrue(result[0].configfile.filename.endswith('example.org.conf'))
self.assertEqual(result[1], None)
# Superset
result = find_duplicative_certs(
self.cli_config, ['example.com', 'www.example.com', 'something.new'])
self.config, ['example.com', 'www.example.com', 'something.new'])
self.assertEqual(result[0], None)
self.assertTrue(result[1].configfile.filename.endswith('example.org.conf'))
# Partial overlap doesn't count
result = find_duplicative_certs(
self.cli_config, ['example.com', 'something.new'])
self.config, ['example.com', 'something.new'])
self.assertEqual(result, (None, None))

View file

@ -18,23 +18,16 @@ import certbot.tests.util as test_util
KEY = test_util.load_vector("rsa512_key.pem")
CSR_SAN = test_util.load_vector("csr-san.pem")
CSR_SAN = test_util.load_vector("csr-san_512.pem")
class ConfigHelper(object):
"""Creates a dummy object to imitate a namespace object
Example: cfg = ConfigHelper(redirect=True, hsts=False, uir=False)
will result in: cfg.redirect=True, cfg.hsts=False, etc.
"""
def __init__(self, **kwds):
self.__dict__.update(kwds)
class RegisterTest(unittest.TestCase):
class RegisterTest(test_util.ConfigTestCase):
"""Tests for certbot.client.register."""
def setUp(self):
self.config = mock.MagicMock(rsa_key_size=1024, register_unsafely_without_email=False)
super(RegisterTest, self).setUp()
self.config.rsa_key_size = 1024
self.config.register_unsafely_without_email = False
self.account_storage = account.AccountMemoryStorage()
self.tos_cb = mock.MagicMock()
@ -116,14 +109,12 @@ class RegisterTest(unittest.TestCase):
self.assertFalse(mock_handle.called)
class ClientTestCommon(unittest.TestCase):
class ClientTestCommon(test_util.ConfigTestCase):
"""Common base class for certbot.client.Client tests."""
def setUp(self):
self.config = mock.MagicMock(
no_verify_ssl=False,
config_dir="/etc/letsencrypt",
work_dir="/var/lib/letsencrypt",
allow_subset_of_names=False)
super(ClientTestCommon, self).setUp()
self.config.no_verify_ssl = False
self.config.allow_subset_of_names = False
# pylint: disable=star-args
self.account = mock.MagicMock(**{"key.pem": KEY})
@ -143,7 +134,6 @@ class ClientTest(ClientTestCommon):
super(ClientTest, self).setUp()
self.config.allow_subset_of_names = False
self.config.config_dir = "/etc/letsencrypt"
self.config.dry_run = False
self.eg_domains = ["example.com", "www.example.com"]
@ -262,8 +252,8 @@ class ClientTest(ClientTestCommon):
mock_crypto.make_key.return_value = mock.sentinel.key_pem
key = util.Key(file=None, pem=mock.sentinel.key_pem)
with mock.patch.object(self.client.config, 'dry_run', new=True):
self._test_obtain_certificate_common(key, csr)
self.client.config.dry_run = True
self._test_obtain_certificate_common(key, csr)
mock_crypto.make_key.assert_called_once_with(self.config.rsa_key_size)
mock_acme_crypto.make_csr.assert_called_once_with(
@ -322,14 +312,14 @@ class ClientTest(ClientTestCommon):
@mock.patch("certbot.cli.helpful_parser")
def test_save_certificate(self, mock_parser):
# pylint: disable=too-many-locals
certs = ["matching_cert.pem", "cert.pem", "cert-san.pem"]
certs = ["cert_512.pem", "cert-san_512.pem"]
tmp_path = tempfile.mkdtemp()
os.chmod(tmp_path, 0o755) # TODO: really??
certr = mock.MagicMock(body=test_util.load_comparable_cert(certs[0]))
chain_cert = [test_util.load_comparable_cert(certs[1]),
test_util.load_comparable_cert(certs[2])]
candidate_cert_path = os.path.join(tmp_path, "certs", "cert.pem")
chain_cert = [test_util.load_comparable_cert(certs[0]),
test_util.load_comparable_cert(certs[1])]
candidate_cert_path = os.path.join(tmp_path, "certs", "cert_512.pem")
candidate_chain_path = os.path.join(tmp_path, "chains", "chain.pem")
candidate_fullchain_path = os.path.join(tmp_path, "chains", "fullchain.pem")
mock_parser.verb = "certonly"
@ -354,8 +344,8 @@ class ClientTest(ClientTestCommon):
with open(chain_path, "rb") as chain_file:
chain_contents = chain_file.read()
self.assertEqual(chain_contents, test_util.load_vector(certs[1]) +
test_util.load_vector(certs[2]))
self.assertEqual(chain_contents, test_util.load_vector(certs[0]) +
test_util.load_vector(certs[1]))
shutil.rmtree(tmp_path)

View file

@ -6,33 +6,32 @@ import mock
from certbot import errors
from certbot.tests import util as test_util
class NamespaceConfigTest(unittest.TestCase):
class NamespaceConfigTest(test_util.ConfigTestCase):
"""Tests for certbot.configuration.NamespaceConfig."""
def setUp(self):
self.namespace = mock.MagicMock(
config_dir='/tmp/config', work_dir='/tmp/foo',
logs_dir="/tmp/bar", foo='bar',
server='https://acme-server.org:443/new',
tls_sni_01_port=1234, http01_port=4321)
from certbot.configuration import NamespaceConfig
self.config = NamespaceConfig(self.namespace)
super(NamespaceConfigTest, self).setUp()
self.config.foo = 'bar'
self.config.server = 'https://acme-server.org:443/new'
self.config.tls_sni_01_port = 1234
self.config.http01_port = 4321
def test_init_same_ports(self):
self.namespace.tls_sni_01_port = 4321
self.config.namespace.tls_sni_01_port = 4321
from certbot.configuration import NamespaceConfig
self.assertRaises(errors.Error, NamespaceConfig, self.namespace)
self.assertRaises(errors.Error, NamespaceConfig, self.config.namespace)
def test_proxy_getattr(self):
self.assertEqual(self.config.foo, 'bar')
self.assertEqual(self.config.work_dir, '/tmp/foo')
self.assertEqual(self.config.work_dir, os.path.join(self.tempdir, 'work'))
def test_server_path(self):
self.assertEqual(['acme-server.org:443', 'new'],
self.config.server_path.split(os.path.sep))
self.namespace.server = ('http://user:pass@acme.server:443'
self.config.namespace.server = ('http://user:pass@acme.server:443'
'/p/a/t/h;parameters?query#fragment')
self.assertEqual(['user:pass@acme.server:443', 'p', 'a', 't', 'h'],
self.config.server_path.split(os.path.sep))
@ -48,12 +47,18 @@ class NamespaceConfigTest(unittest.TestCase):
constants.TEMP_CHECKPOINT_DIR = 't'
self.assertEqual(
self.config.accounts_dir, '/tmp/config/acc/acme-server.org:443/new')
self.assertEqual(self.config.backup_dir, '/tmp/foo/backups')
self.assertEqual(self.config.csr_dir, '/tmp/config/csr')
self.assertEqual(self.config.in_progress_dir, '/tmp/foo/../p')
self.assertEqual(self.config.key_dir, '/tmp/config/keys')
self.assertEqual(self.config.temp_checkpoint_dir, '/tmp/foo/t')
self.config.accounts_dir, os.path.join(
self.config.config_dir, 'acc/acme-server.org:443/new'))
self.assertEqual(
self.config.backup_dir, os.path.join(self.config.work_dir, 'backups'))
self.assertEqual(
self.config.csr_dir, os.path.join(self.config.config_dir, 'csr'))
self.assertEqual(
self.config.in_progress_dir, os.path.join(self.config.work_dir, '../p'))
self.assertEqual(
self.config.key_dir, os.path.join(self.config.config_dir, 'keys'))
self.assertEqual(
self.config.temp_checkpoint_dir, os.path.join(self.config.work_dir, 't'))
def test_absolute_paths(self):
from certbot.configuration import NamespaceConfig
@ -95,10 +100,13 @@ class NamespaceConfigTest(unittest.TestCase):
constants.LIVE_DIR = 'l'
constants.RENEWAL_CONFIGS_DIR = 'renewal_configs'
self.assertEqual(self.config.default_archive_dir, '/tmp/config/a')
self.assertEqual(self.config.live_dir, '/tmp/config/l')
self.assertEqual(
self.config.renewal_configs_dir, '/tmp/config/renewal_configs')
self.config.default_archive_dir, os.path.join(self.config.config_dir, 'a'))
self.assertEqual(
self.config.live_dir, os.path.join(self.config.config_dir, 'l'))
self.assertEqual(
self.config.renewal_configs_dir, os.path.join(
self.config.config_dir, 'renewal_configs'))
def test_renewal_absolute_paths(self):
from certbot.configuration import NamespaceConfig

View file

@ -17,11 +17,10 @@ RSA256_KEY = test_util.load_vector('rsa256_key.pem')
RSA256_KEY_PATH = test_util.vector_path('rsa256_key.pem')
RSA512_KEY = test_util.load_vector('rsa512_key.pem')
RSA2048_KEY_PATH = test_util.vector_path('rsa2048_key.pem')
CERT_PATH = test_util.vector_path('cert.pem')
CERT = test_util.load_vector('cert.pem')
SAN_CERT = test_util.load_vector('cert-san.pem')
SS_CERT_PATH = test_util.vector_path('self_signed_cert.pem')
SS_CERT = test_util.load_vector('self_signed_cert.pem')
CERT_PATH = test_util.vector_path('cert_512.pem')
CERT = test_util.load_vector('cert_512.pem')
SS_CERT_PATH = test_util.vector_path('cert_2048.pem')
SS_CERT = test_util.load_vector('cert_2048.pem')
class InitSaveKeyTest(test_util.TempDirTestCase):
"""Tests for certbot.crypto_util.init_save_key."""
@ -88,13 +87,13 @@ class ValidCSRTest(unittest.TestCase):
return valid_csr(csr)
def test_valid_pem_true(self):
self.assertTrue(self._call(test_util.load_vector('csr.pem')))
self.assertTrue(self._call(test_util.load_vector('csr_512.pem')))
def test_valid_pem_san_true(self):
self.assertTrue(self._call(test_util.load_vector('csr-san.pem')))
self.assertTrue(self._call(test_util.load_vector('csr-san_512.pem')))
def test_valid_der_false(self):
self.assertFalse(self._call(test_util.load_vector('csr.der')))
self.assertFalse(self._call(test_util.load_vector('csr_512.der')))
def test_empty_false(self):
self.assertFalse(self._call(''))
@ -113,11 +112,11 @@ class CSRMatchesPubkeyTest(unittest.TestCase):
def test_valid_true(self):
self.assertTrue(self._call(
test_util.load_vector('csr.pem'), RSA512_KEY))
test_util.load_vector('csr_512.pem'), RSA512_KEY))
def test_invalid_false(self):
self.assertFalse(self._call(
test_util.load_vector('csr.pem'), RSA256_KEY))
test_util.load_vector('csr_512.pem'), RSA256_KEY))
class ImportCSRFileTest(unittest.TestCase):
@ -129,9 +128,9 @@ class ImportCSRFileTest(unittest.TestCase):
return import_csr_file(*args, **kwargs)
def test_der_csr(self):
csrfile = test_util.vector_path('csr.der')
data = test_util.load_vector('csr.der')
data_pem = test_util.load_vector('csr.pem')
csrfile = test_util.vector_path('csr_512.der')
data = test_util.load_vector('csr_512.der')
data_pem = test_util.load_vector('csr_512.pem')
self.assertEqual(
(OpenSSL.crypto.FILETYPE_PEM,
@ -142,8 +141,8 @@ class ImportCSRFileTest(unittest.TestCase):
self._call(csrfile, data))
def test_pem_csr(self):
csrfile = test_util.vector_path('csr.pem')
data = test_util.load_vector('csr.pem')
csrfile = test_util.vector_path('csr_512.pem')
data = test_util.load_vector('csr_512.pem')
self.assertEqual(
(OpenSSL.crypto.FILETYPE_PEM,
@ -155,8 +154,8 @@ class ImportCSRFileTest(unittest.TestCase):
def test_bad_csr(self):
self.assertRaises(errors.Error, self._call,
test_util.vector_path('cert.pem'),
test_util.load_vector('cert.pem'))
test_util.vector_path('cert_512.pem'),
test_util.load_vector('cert_512.pem'))
class MakeKeyTest(unittest.TestCase): # pylint: disable=too-few-public-methods
@ -179,7 +178,7 @@ class VerifyCertSetup(unittest.TestCase):
self.renewable_cert.cert = SS_CERT_PATH
self.renewable_cert.chain = SS_CERT_PATH
self.renewable_cert.privkey = RSA2048_KEY_PATH
self.renewable_cert.fullchain = test_util.vector_path('self_signed_fullchain.pem')
self.renewable_cert.fullchain = test_util.vector_path('cert_fullchain_2048.pem')
self.bad_renewable_cert = mock.MagicMock()
self.bad_renewable_cert.chain = SS_CERT_PATH
@ -219,7 +218,7 @@ class VerifyRenewableCertSigTest(VerifyCertSetup):
self.assertEqual(None, self._call(self.renewable_cert))
def test_cert_sig_mismatch(self):
self.bad_renewable_cert.cert = test_util.vector_path('self_signed_cert_bad.pem')
self.bad_renewable_cert.cert = test_util.vector_path('cert_512_bad.pem')
self.assertRaises(errors.Error, self._call, self.bad_renewable_cert)
@ -275,7 +274,7 @@ class ValidPrivkeyTest(unittest.TestCase):
return valid_privkey(privkey)
def test_valid_true(self):
self.assertTrue(self._call(RSA256_KEY))
self.assertTrue(self._call(RSA512_KEY))
def test_empty_false(self):
self.assertFalse(self._call(''))
@ -293,12 +292,12 @@ class GetSANsFromCertTest(unittest.TestCase):
return get_sans_from_cert(*args, **kwargs)
def test_single(self):
self.assertEqual([], self._call(test_util.load_vector('cert.pem')))
self.assertEqual([], self._call(test_util.load_vector('cert_512.pem')))
def test_san(self):
self.assertEqual(
['example.com', 'www.example.com'],
self._call(test_util.load_vector('cert-san.pem')))
self._call(test_util.load_vector('cert-san_512.pem')))
class GetNamesFromCertTest(unittest.TestCase):
@ -312,19 +311,19 @@ class GetNamesFromCertTest(unittest.TestCase):
def test_single(self):
self.assertEqual(
['example.com'],
self._call(test_util.load_vector('cert.pem')))
self._call(test_util.load_vector('cert_512.pem')))
def test_san(self):
self.assertEqual(
['example.com', 'www.example.com'],
self._call(test_util.load_vector('cert-san.pem')))
self._call(test_util.load_vector('cert-san_512.pem')))
def test_common_name_sans_order(self):
# Tests that the common name comes first
# followed by the SANS in alphabetical order
self.assertEqual(
['example.com'] + ['{0}.example.com'.format(c) for c in 'abcd'],
self._call(test_util.load_vector('cert-5sans.pem')))
self._call(test_util.load_vector('cert-5sans_512.pem')))
def test_parse_non_cert(self):
self.assertRaises(OpenSSL.crypto.Error, self._call, "hello there")

View file

@ -4,20 +4,22 @@ import unittest
import mock
from certbot import constants
from certbot.tests import util
import certbot.tests.util as test_util
class HandleSubscriptionTest(unittest.TestCase):
class HandleSubscriptionTest(test_util.ConfigTestCase):
"""Tests for certbot.eff.handle_subscription."""
def setUp(self):
super(HandleSubscriptionTest, self).setUp()
self.email = 'certbot@example.org'
self.config = mock.Mock(email=self.email, eff_email=None)
self.config.email = self.email
self.config.eff_email = None
def _call(self):
from certbot.eff import handle_subscription
return handle_subscription(self.config)
@util.patch_get_utility()
@test_util.patch_get_utility()
@mock.patch('certbot.eff.subscribe')
def test_failure(self, mock_subscribe, mock_get_utility):
self.config.email = None
@ -32,12 +34,12 @@ class HandleSubscriptionTest(unittest.TestCase):
@mock.patch('certbot.eff.subscribe')
def test_no_subscribe_with_no_prompt(self, mock_subscribe):
self.config.eff_email = False
with util.patch_get_utility() as mock_get_utility:
with test_util.patch_get_utility() as mock_get_utility:
self._call()
self.assertFalse(mock_subscribe.called)
self._assert_no_get_utility_calls(mock_get_utility)
@util.patch_get_utility()
@test_util.patch_get_utility()
@mock.patch('certbot.eff.subscribe')
def test_subscribe_with_no_prompt(self, mock_subscribe, mock_get_utility):
self.config.eff_email = True
@ -49,7 +51,7 @@ class HandleSubscriptionTest(unittest.TestCase):
self.assertFalse(mock_get_utility().yesno.called)
self.assertFalse(mock_get_utility().add_message.called)
@util.patch_get_utility()
@test_util.patch_get_utility()
@mock.patch('certbot.eff.subscribe')
def test_subscribe_with_prompt(self, mock_subscribe, mock_get_utility):
mock_get_utility().yesno.return_value = True
@ -62,7 +64,7 @@ class HandleSubscriptionTest(unittest.TestCase):
self.assertTrue(mock_subscribe.called)
self.assertEqual(mock_subscribe.call_args[0][0], self.email)
@util.patch_get_utility()
@test_util.patch_get_utility()
@mock.patch('certbot.eff.subscribe')
def test_no_subscribe_with_prompt(self, mock_subscribe, mock_get_utility):
mock_get_utility().yesno.return_value = False
@ -105,7 +107,7 @@ class SubscribeTest(unittest.TestCase):
self.assertFalse(data is None)
self.assertEqual(data.get('email'), self.email)
@util.patch_get_utility()
@test_util.patch_get_utility()
def test_bad_status(self, mock_get_utility):
self.json['status'] = False
self._call() # pylint: disable=no-value-for-parameter
@ -113,7 +115,7 @@ class SubscribeTest(unittest.TestCase):
expected_part = 'because your e-mail address appears to be invalid.'
self.assertTrue(expected_part in actual)
@util.patch_get_utility()
@test_util.patch_get_utility()
def test_not_ok(self, mock_get_utility):
self.response.ok = False
self._call() # pylint: disable=no-value-for-parameter
@ -125,7 +127,7 @@ class SubscribeTest(unittest.TestCase):
self.assertTrue(mock_get_utility().add_message.called)
return mock_get_utility().add_message.call_args[0][0]
@util.patch_get_utility()
@test_util.patch_get_utility()
def test_subscribe(self, mock_get_utility):
self._call() # pylint: disable=no-value-for-parameter
self.assertFalse(mock_get_utility.called)

View file

@ -55,7 +55,7 @@ class PreArgParseSetupTest(unittest.TestCase):
memory_handler, 1, 2, 3, debug=True, log_path=mock.ANY)
class PostArgParseSetupTest(test_util.TempDirTestCase):
class PostArgParseSetupTest(test_util.ConfigTestCase):
"""Tests for certbot.log.post_arg_parse_setup."""
@classmethod
@ -65,9 +65,10 @@ class PostArgParseSetupTest(test_util.TempDirTestCase):
def setUp(self):
super(PostArgParseSetupTest, self).setUp()
self.config = mock.MagicMock(
debug=False, logs_dir=self.tempdir, max_log_backups=1000,
quiet=False, verbose_count=constants.CLI_DEFAULTS['verbose_count'])
self.config.debug = False
self.config.max_log_backups = 1000
self.config.quiet = False
self.config.verbose_count = constants.CLI_DEFAULTS['verbose_count']
self.devnull = open(os.devnull, 'w')
from certbot.log import ColoredStreamHandler
@ -102,7 +103,7 @@ class PostArgParseSetupTest(test_util.TempDirTestCase):
self.assertFalse(os.path.exists(self.temp_path))
mock_sys.excepthook(1, 2, 3)
mock_except_hook.assert_called_once_with(
1, 2, 3, debug=self.config.debug, log_path=self.tempdir)
1, 2, 3, debug=self.config.debug, log_path=self.config.logs_dir)
level = self.stream_handler.level
if self.config.quiet:
@ -119,7 +120,7 @@ class PostArgParseSetupTest(test_util.TempDirTestCase):
self.test_common()
class SetupLogFileHandlerTest(test_util.TempDirTestCase):
class SetupLogFileHandlerTest(test_util.ConfigTestCase):
"""Tests for certbot.log.setup_log_file_handler."""
@classmethod
@ -129,7 +130,7 @@ class SetupLogFileHandlerTest(test_util.TempDirTestCase):
def setUp(self):
super(SetupLogFileHandlerTest, self).setUp()
self.config = mock.MagicMock(logs_dir=self.tempdir, max_log_backups=42)
self.config.max_log_backups = 42
@mock.patch('certbot.main.logging.handlers.RotatingFileHandler')
def test_failure(self, mock_handler):

View file

@ -23,8 +23,6 @@ from certbot import configuration
from certbot import crypto_util
from certbot import errors
from certbot import main
from certbot import renewal
from certbot import storage
from certbot import util
from certbot.plugins import disco
@ -32,13 +30,13 @@ from certbot.plugins import manual
import certbot.tests.util as test_util
CERT_PATH = test_util.vector_path('cert.pem')
CERT = test_util.vector_path('cert.pem')
CSR = test_util.vector_path('csr.der')
CERT_PATH = test_util.vector_path('cert_512.pem')
CERT = test_util.vector_path('cert_512.pem')
CSR = test_util.vector_path('csr_512.der')
KEY = test_util.vector_path('rsa256_key.pem')
JWK = jose.JWKRSA.load(test_util.load_vector("rsa512_key_2.pem"))
JWK = jose.JWKRSA.load(test_util.load_vector('rsa512_key.pem'))
RSA2048_KEY_PATH = test_util.vector_path('rsa2048_key.pem')
SS_CERT_PATH = test_util.vector_path('self_signed_cert.pem')
SS_CERT_PATH = test_util.vector_path('cert_2048.pem')
class TestHandleIdenticalCerts(unittest.TestCase):
@ -227,7 +225,7 @@ class RevokeTest(test_util.TempDirTestCase):
shutil.copy(CERT_PATH, self.tempdir)
self.tmp_cert_path = os.path.abspath(os.path.join(self.tempdir,
'cert.pem'))
'cert_512.pem'))
self.patches = [
mock.patch('acme.client.Client', autospec=True),
@ -289,16 +287,14 @@ class RevokeTest(test_util.TempDirTestCase):
self.mock_success_revoke.assert_not_called()
class DetermineAccountTest(unittest.TestCase):
class DetermineAccountTest(test_util.ConfigTestCase):
"""Tests for certbot.main._determine_account."""
def setUp(self):
self.args = mock.MagicMock(account=None, email=None,
config_dir="unused_config",
logs_dir="unused_logs",
work_dir="unused_work",
register_unsafely_without_email=False)
self.config = configuration.NamespaceConfig(self.args)
super(DetermineAccountTest, self).setUp()
self.config.account = None
self.config.email = None
self.config.register_unsafely_without_email = False
self.accs = [mock.MagicMock(id='x'), mock.MagicMock(id='y')]
self.account_storage = account.AccountMemoryStorage()
# For use in saving accounts: fake out the new_authz URL.
@ -359,19 +355,16 @@ class DetermineAccountTest(unittest.TestCase):
self.assertEqual('other email', self.config.email)
class MainTest(test_util.TempDirTestCase): # pylint: disable=too-many-public-methods
class MainTest(test_util.ConfigTestCase): # pylint: disable=too-many-public-methods
"""Tests for different commands."""
def setUp(self):
super(MainTest, self).setUp()
self.config_dir = os.path.join(self.tempdir, 'config')
self.work_dir = os.path.join(self.tempdir, 'work')
self.logs_dir = os.path.join(self.tempdir, 'logs')
os.mkdir(self.logs_dir)
self.standard_args = ['--config-dir', self.config_dir,
'--work-dir', self.work_dir,
'--logs-dir', self.logs_dir, '--text']
os.mkdir(self.config.logs_dir)
self.standard_args = ['--config-dir', self.config.config_dir,
'--work-dir', self.config.work_dir,
'--logs-dir', self.config.logs_dir, '--text']
def tearDown(self):
# Reset globals in cli
@ -647,7 +640,7 @@ class MainTest(test_util.TempDirTestCase): # pylint: disable=too-many-public-me
self.assertRaises(
errors.Error, self._call,
'certonly --csr {0}'.format(
test_util.vector_path('csr-nonames.pem')).split())
test_util.vector_path('csr-nonames_512.pem')).split())
def test_csr_with_inconsistent_domains(self):
self.assertRaises(
@ -707,7 +700,7 @@ class MainTest(test_util.TempDirTestCase): # pylint: disable=too-many-public-me
def _test_renewal_common(self, due_for_renewal, extra_args, log_out=None,
args=None, should_renew=True, error_expected=False):
# pylint: disable=too-many-locals,too-many-arguments
cert_path = test_util.vector_path('cert.pem')
cert_path = test_util.vector_path('cert_512.pem')
chain_path = '/etc/letsencrypt/live/foo.bar/fullchain.pem'
mock_lineage = mock.MagicMock(cert=cert_path, fullchain=chain_path,
cert_path=cert_path, fullchain_path=chain_path)
@ -756,7 +749,7 @@ class MainTest(test_util.TempDirTestCase): # pylint: disable=too-many-public-me
raise
finally:
if log_out:
with open(os.path.join(self.logs_dir, "letsencrypt.log")) as lf:
with open(os.path.join(self.config.logs_dir, "letsencrypt.log")) as lf:
self.assertTrue(log_out in lf.read())
return mock_lineage, mock_get_utility, stdout
@ -788,18 +781,18 @@ class MainTest(test_util.TempDirTestCase): # pylint: disable=too-many-public-me
def _dump_log(self):
print("Logs:")
log_path = os.path.join(self.logs_dir, "letsencrypt.log")
log_path = os.path.join(self.config.logs_dir, "letsencrypt.log")
if os.path.exists(log_path):
with open(log_path) as lf:
print(lf.read())
def test_renew_verb(self):
test_util.make_lineage(self.config_dir, 'sample-renewal.conf')
test_util.make_lineage(self.config.config_dir, 'sample-renewal.conf')
args = ["renew", "--dry-run", "-tvv"]
self._test_renewal_common(True, [], args=args, should_renew=True)
def test_quiet_renew(self):
test_util.make_lineage(self.config_dir, 'sample-renewal.conf')
test_util.make_lineage(self.config.config_dir, 'sample-renewal.conf')
args = ["renew", "--dry-run"]
_, _, stdout = self._test_renewal_common(True, [], args=args, should_renew=True)
out = stdout.getvalue()
@ -811,36 +804,21 @@ class MainTest(test_util.TempDirTestCase): # pylint: disable=too-many-public-me
self.assertEqual("", out)
def test_renew_hook_validation(self):
test_util.make_lineage(self.config_dir, 'sample-renewal.conf')
test_util.make_lineage(self.config.config_dir, 'sample-renewal.conf')
args = ["renew", "--dry-run", "--post-hook=no-such-command"]
self._test_renewal_common(True, [], args=args, should_renew=False,
error_expected=True)
def test_renew_no_hook_validation(self):
test_util.make_lineage(self.config_dir, 'sample-renewal.conf')
test_util.make_lineage(self.config.config_dir, 'sample-renewal.conf')
args = ["renew", "--dry-run", "--post-hook=no-such-command",
"--disable-hook-validation"]
with mock.patch("certbot.hooks.post_hook"):
self._test_renewal_common(True, [], args=args, should_renew=True,
error_expected=False)
@mock.patch("certbot.cli.set_by_cli")
def test_ancient_webroot_renewal_conf(self, mock_set_by_cli):
mock_set_by_cli.return_value = False
rc_path = test_util.make_lineage(
self.config_dir, 'sample-renewal-ancient.conf')
args = mock.MagicMock(account=None, config_dir=self.config_dir,
logs_dir=self.logs_dir, work_dir=self.work_dir,
email=None, webroot_path=None)
config = configuration.NamespaceConfig(args)
lineage = storage.RenewableCert(rc_path, config)
renewalparams = lineage.configuration["renewalparams"]
# pylint: disable=protected-access
renewal._restore_webroot_config(config, renewalparams)
self.assertEqual(config.webroot_path, ["/var/www/"])
def test_renew_verb_empty_config(self):
rd = os.path.join(self.config_dir, 'renewal')
rd = os.path.join(self.config.config_dir, 'renewal')
if not os.path.exists(rd):
os.makedirs(rd)
with open(os.path.join(rd, 'empty.conf'), 'w'):
@ -849,7 +827,7 @@ class MainTest(test_util.TempDirTestCase): # pylint: disable=too-many-public-me
self._test_renewal_common(False, [], args=args, should_renew=False, error_expected=True)
def test_renew_with_certname(self):
test_util.make_lineage(self.config_dir, 'sample-renewal.conf')
test_util.make_lineage(self.config.config_dir, 'sample-renewal.conf')
self._test_renewal_common(True, [], should_renew=True,
args=['renew', '--dry-run', '--cert-name', 'sample-renewal'])
@ -859,7 +837,7 @@ class MainTest(test_util.TempDirTestCase): # pylint: disable=too-many-public-me
error_expected=True)
def _make_dummy_renewal_config(self):
renewer_configs_dir = os.path.join(self.config_dir, 'renewal')
renewer_configs_dir = os.path.join(self.config.config_dir, 'renewal')
os.makedirs(renewer_configs_dir)
with open(os.path.join(renewer_configs_dir, 'test.conf'), 'w') as f:
f.write("My contents don't matter")
@ -977,7 +955,7 @@ class MainTest(test_util.TempDirTestCase): # pylint: disable=too-many-public-me
chain = 'chain'
mock_client = mock.MagicMock()
mock_client.obtain_certificate_from_csr.return_value = (certr, chain)
cert_path = '/etc/letsencrypt/live/example.com/cert.pem'
cert_path = '/etc/letsencrypt/live/example.com/cert_512.pem'
full_path = '/etc/letsencrypt/live/example.com/fullchain.pem'
mock_client.save_certificate.return_value = cert_path, None, full_path
with mock.patch('certbot.main._init_le_client') as mock_init:

View file

@ -1,5 +1,4 @@
"""Tests for certbot.renewal"""
import os
import mock
import unittest
@ -9,24 +8,22 @@ from certbot import configuration
from certbot import errors
from certbot import storage
from certbot.tests import util
import certbot.tests.util as test_util
class RenewalTest(util.TempDirTestCase):
class RenewalTest(test_util.ConfigTestCase):
def setUp(self):
super(RenewalTest, self).setUp()
self.config_dir = os.path.join(self.tempdir, 'config')
@mock.patch('certbot.cli.set_by_cli')
def test_ancient_webroot_renewal_conf(self, mock_set_by_cli):
mock_set_by_cli.return_value = False
rc_path = util.make_lineage(
self.config_dir, 'sample-renewal-ancient.conf')
args = mock.MagicMock(account=None, config_dir=self.config_dir,
logs_dir="logs", work_dir="work",
email=None, webroot_path=None)
config = configuration.NamespaceConfig(args)
rc_path = test_util.make_lineage(
self.config.config_dir, 'sample-renewal-ancient.conf')
self.config.account = None
self.config.email = None
self.config.webroot_path = None
config = configuration.NamespaceConfig(self.config)
lineage = storage.RenewableCert(rc_path, config)
renewalparams = lineage.configuration['renewalparams']
# pylint: disable=protected-access
@ -35,10 +32,10 @@ class RenewalTest(util.TempDirTestCase):
self.assertEqual(config.webroot_path, ['/var/www/'])
class RestoreRequiredConfigElementsTest(unittest.TestCase):
class RestoreRequiredConfigElementsTest(test_util.ConfigTestCase):
"""Tests for certbot.renewal.restore_required_config_elements."""
def setUp(self):
self.config = mock.MagicMock()
super(RestoreRequiredConfigElementsTest, self).setUp()
@classmethod
def _call(cls, *args, **kwargs):

View file

@ -14,16 +14,16 @@ from certbot import errors
from certbot.tests import util as test_util
class ReverterCheckpointLocalTest(unittest.TestCase):
class ReverterCheckpointLocalTest(test_util.ConfigTestCase):
# pylint: disable=too-many-instance-attributes, too-many-public-methods
"""Test the Reverter Class."""
def setUp(self):
super(ReverterCheckpointLocalTest, self).setUp()
from certbot.reverter import Reverter
# Disable spurious errors... we are trying to test for them
logging.disable(logging.CRITICAL)
self.config = setup_work_direc()
self.reverter = Reverter(self.config)
tup = setup_test_files()
@ -277,15 +277,15 @@ class ReverterCheckpointLocalTest(unittest.TestCase):
self.assertEqual(read_in(self.config2), "directive-dir2")
class TestFullCheckpointsReverter(unittest.TestCase):
class TestFullCheckpointsReverter(test_util.ConfigTestCase):
# pylint: disable=too-many-instance-attributes
"""Tests functions having to deal with full checkpoints."""
def setUp(self):
super(TestFullCheckpointsReverter, self).setUp()
from certbot.reverter import Reverter
# Disable spurious errors...
logging.disable(logging.CRITICAL)
self.config = setup_work_direc()
self.reverter = Reverter(self.config)
tup = setup_test_files()
@ -439,21 +439,6 @@ class TestFullCheckpointsReverter(unittest.TestCase):
return config3
def setup_work_direc():
"""Setup directories.
:returns: Mocked :class:`certbot.interfaces.IConfig`
"""
work_dir = tempfile.mkdtemp("work")
backup_dir = os.path.join(work_dir, "backup")
return mock.MagicMock(
work_dir=work_dir, backup_dir=backup_dir,
temp_checkpoint_dir=os.path.join(work_dir, "temp"),
in_progress_dir=os.path.join(backup_dir, "in_progress_dir"))
def setup_test_files():
"""Setup sample configuration files."""
dir1 = tempfile.mkdtemp("dir1")

View file

@ -13,14 +13,14 @@ import six
import certbot
from certbot import cli
from certbot import configuration
from certbot import errors
from certbot.storage import ALL_FOUR
from certbot.tests import util
import certbot.tests.util as test_util
CERT = util.load_cert('cert.pem')
CERT = test_util.load_cert('cert_512.pem')
def unlink_all(rc_object):
@ -36,7 +36,7 @@ def fill_with_sample_data(rc_object):
f.write(kind)
class BaseRenewableCertTest(util.TempDirTestCase):
class BaseRenewableCertTest(test_util.ConfigTestCase):
"""Base class for setting up Renewable Cert tests.
.. note:: It may be required to write out self.config for
@ -50,39 +50,31 @@ class BaseRenewableCertTest(util.TempDirTestCase):
super(BaseRenewableCertTest, self).setUp()
self.cli_config = configuration.NamespaceConfig(
namespace=mock.MagicMock(
config_dir=self.tempdir,
work_dir=self.tempdir,
logs_dir=self.tempdir,
)
)
# TODO: maybe provide NamespaceConfig.make_dirs?
# TODO: main() should create those dirs, c.f. #902
os.makedirs(os.path.join(self.tempdir, "live", "example.org"))
archive_path = os.path.join(self.tempdir, "archive", "example.org")
os.makedirs(os.path.join(self.config.config_dir, "live", "example.org"))
archive_path = os.path.join(self.config.config_dir, "archive", "example.org")
os.makedirs(archive_path)
os.makedirs(os.path.join(self.tempdir, "renewal"))
os.makedirs(os.path.join(self.config.config_dir, "renewal"))
config = configobj.ConfigObj()
config_file = configobj.ConfigObj()
for kind in ALL_FOUR:
kind_path = os.path.join(self.tempdir, "live", "example.org",
kind_path = os.path.join(self.config.config_dir, "live", "example.org",
kind + ".pem")
config[kind] = kind_path
with open(os.path.join(self.tempdir, "live", "example.org",
config_file[kind] = kind_path
with open(os.path.join(self.config.config_dir, "live", "example.org",
"README"), 'a'):
pass
config["archive"] = archive_path
config.filename = os.path.join(self.tempdir, "renewal",
config_file["archive"] = archive_path
config_file.filename = os.path.join(self.config.config_dir, "renewal",
"example.org.conf")
config.write()
self.config = config
config_file.write()
self.config_file = config_file
# We also create a file that isn't a renewal config in the same
# location to test that logic that reads in all-and-only renewal
# configs will ignore it and NOT attempt to parse it.
junk = open(os.path.join(self.tempdir, "renewal", "IGNORE.THIS"), "w")
junk = open(os.path.join(self.config.config_dir, "renewal", "IGNORE.THIS"), "w")
junk.write("This file should be ignored!")
junk.close()
@ -90,7 +82,7 @@ class BaseRenewableCertTest(util.TempDirTestCase):
with mock.patch("certbot.storage.RenewableCert._check_symlinks") as check:
check.return_value = True
self.test_rc = storage.RenewableCert(config.filename, self.cli_config)
self.test_rc = storage.RenewableCert(config_file.filename, self.config)
def _write_out_kind(self, kind, ver, value=None):
link = getattr(self.test_rc, kind)
@ -117,7 +109,7 @@ class RenewableCertTests(BaseRenewableCertTest):
for kind in ALL_FOUR:
self.assertEqual(
getattr(self.test_rc, kind), os.path.join(
self.tempdir, "live", "example.org", kind + ".pem"))
self.config.config_dir, "live", "example.org", kind + ".pem"))
def test_renewal_bad_config(self):
"""Test that the RenewableCert constructor will complain if
@ -125,14 +117,14 @@ class RenewableCertTests(BaseRenewableCertTest):
"""
from certbot import storage
broken = os.path.join(self.tempdir, "broken.conf")
broken = os.path.join(self.config.config_dir, "broken.conf")
with open(broken, "w") as f:
f.write("[No closing bracket for you!")
self.assertRaises(errors.CertStorageError, storage.RenewableCert,
broken, self.cli_config)
broken, self.config)
os.unlink(broken)
self.assertRaises(errors.CertStorageError, storage.RenewableCert,
"fun", self.cli_config)
"fun", self.config)
def test_renewal_incomplete_config(self):
"""Test that the RenewableCert constructor will complain if
@ -143,30 +135,30 @@ class RenewableCertTests(BaseRenewableCertTest):
# Here the required privkey is missing.
config["chain"] = "imaginary_chain.pem"
config["fullchain"] = "imaginary_fullchain.pem"
config.filename = os.path.join(self.tempdir, "imaginary_config.conf")
config.filename = os.path.join(self.config.config_dir, "imaginary_config.conf")
config.write()
self.assertRaises(errors.CertStorageError, storage.RenewableCert,
config.filename, self.cli_config)
config.filename, self.config)
def test_no_renewal_version(self):
from certbot import storage
self._write_out_ex_kinds()
self.assertTrue("version" not in self.config)
self.assertTrue("version" not in self.config_file)
with mock.patch("certbot.storage.logger") as mock_logger:
storage.RenewableCert(self.config.filename, self.cli_config)
storage.RenewableCert(self.config_file.filename, self.config)
self.assertFalse(mock_logger.warning.called)
def test_renewal_newer_version(self):
from certbot import storage
self._write_out_ex_kinds()
self.config["version"] = "99.99.99"
self.config.write()
self.config_file["version"] = "99.99.99"
self.config_file.write()
with mock.patch("certbot.storage.logger") as mock_logger:
storage.RenewableCert(self.config.filename, self.cli_config)
storage.RenewableCert(self.config_file.filename, self.config)
self.assertTrue(mock_logger.warning.called)
self.assertTrue("version" in mock_logger.warning.call_args[0][0])
@ -191,7 +183,7 @@ class RenewableCertTests(BaseRenewableCertTest):
unlink_all(self.test_rc)
# Items must point to desired place if they are absolute
for kind in ALL_FOUR:
os.symlink(os.path.join(self.tempdir, kind + "17.pem"),
os.symlink(os.path.join(self.config.config_dir, kind + "17.pem"),
getattr(self.test_rc, kind))
self.assertFalse(self.test_rc._consistent())
unlink_all(self.test_rc)
@ -216,17 +208,17 @@ class RenewableCertTests(BaseRenewableCertTest):
# Relative path logic
self._write_out_kind("cert", 17)
self.assertTrue(os.path.samefile(self.test_rc.current_target("cert"),
os.path.join(self.tempdir, "archive",
os.path.join(self.config.config_dir, "archive",
"example.org",
"cert17.pem")))
# Absolute path logic
os.unlink(self.test_rc.cert)
os.symlink(os.path.join(self.tempdir, "archive", "example.org",
os.symlink(os.path.join(self.config.config_dir, "archive", "example.org",
"cert17.pem"), self.test_rc.cert)
with open(self.test_rc.cert, "w") as f:
f.write("cert")
self.assertTrue(os.path.samefile(self.test_rc.current_target("cert"),
os.path.join(self.tempdir, "archive",
os.path.join(self.config.config_dir, "archive",
"example.org",
"cert17.pem")))
@ -369,18 +361,21 @@ class RenewableCertTests(BaseRenewableCertTest):
def test_names(self):
# Trying the current version
self._write_out_kind("cert", 12, util.load_vector("cert-san.pem"))
self._write_out_kind("cert", 12, test_util.load_vector("cert-san_512.pem"))
self.assertEqual(self.test_rc.names(),
["example.com", "www.example.com"])
# Trying a non-current version
self._write_out_kind("cert", 15, util.load_vector("cert.pem"))
self._write_out_kind("cert", 15, test_util.load_vector("cert_512.pem"))
self.assertEqual(self.test_rc.names(12),
["example.com", "www.example.com"])
# Testing common name is listed first
self._write_out_kind(
"cert", 12, util.load_vector("cert-5sans.pem"))
"cert", 12, test_util.load_vector("cert-5sans_512.pem"))
self.assertEqual(
self.test_rc.names(12),
["example.com"] + ["{0}.example.com".format(c) for c in "abcd"])
@ -393,7 +388,8 @@ class RenewableCertTests(BaseRenewableCertTest):
def test_time_interval_judgments(self, mock_datetime):
"""Test should_autodeploy() and should_autorenew() on the basis
of expiry time windows."""
test_cert = util.load_vector("cert.pem")
test_cert = test_util.load_vector("cert_512.pem")
self._write_out_ex_kinds()
self.test_rc.update_all_links_to(12)
@ -491,7 +487,7 @@ class RenewableCertTests(BaseRenewableCertTest):
self.test_rc.update_all_links_to(3)
self.assertEqual(
6, self.test_rc.save_successor(3, b'new cert', None,
b'new chain', self.cli_config))
b'new chain', self.config))
with open(self.test_rc.version("cert", 6)) as f:
self.assertEqual(f.read(), "new cert")
with open(self.test_rc.version("chain", 6)) as f:
@ -504,10 +500,10 @@ class RenewableCertTests(BaseRenewableCertTest):
# Let's try two more updates
self.assertEqual(
7, self.test_rc.save_successor(6, b'again', None,
b'newer chain', self.cli_config))
b'newer chain', self.config))
self.assertEqual(
8, self.test_rc.save_successor(7, b'hello', None,
b'other chain', self.cli_config))
b'other chain', self.config))
# All of the subsequent versions should link directly to the original
# privkey.
for i in (6, 7, 8):
@ -522,14 +518,14 @@ class RenewableCertTests(BaseRenewableCertTest):
self.test_rc.update_all_links_to(8)
self.assertEqual(
9, self.test_rc.save_successor(8, b'last', None,
b'attempt', self.cli_config))
b'attempt', self.config))
for kind in ALL_FOUR:
self.assertEqual(self.test_rc.available_versions(kind),
list(six.moves.range(1, 10)))
self.assertEqual(self.test_rc.current_version(kind), 8)
with open(self.test_rc.version("fullchain", 9)) as f:
self.assertEqual(f.read(), "last" + "attempt")
temp_config_file = os.path.join(self.cli_config.renewal_configs_dir,
temp_config_file = os.path.join(self.config.renewal_configs_dir,
self.test_rc.lineagename) + ".conf.new"
with open(temp_config_file, "w") as f:
f.write("We previously crashed while writing me :(")
@ -537,7 +533,7 @@ class RenewableCertTests(BaseRenewableCertTest):
# be saved in a new file rather than creating a new symlink.
self.assertEqual(
10, self.test_rc.save_successor(9, b'with', b'a',
b'key', self.cli_config))
b'key', self.config))
self.assertTrue(os.path.exists(self.test_rc.version("privkey", 10)))
self.assertFalse(os.path.islink(self.test_rc.version("privkey", 10)))
self.assertFalse(os.path.exists(temp_config_file))
@ -597,38 +593,38 @@ class RenewableCertTests(BaseRenewableCertTest):
from certbot import storage
result = storage.RenewableCert.new_lineage(
"the-lineage.com", b"cert", b"privkey", b"chain", self.cli_config)
"the-lineage.com", b"cert", b"privkey", b"chain", self.config)
# This consistency check tests most relevant properties about the
# newly created cert lineage.
# pylint: disable=protected-access
self.assertTrue(result._consistent())
self.assertTrue(os.path.exists(os.path.join(
self.cli_config.renewal_configs_dir, "the-lineage.com.conf")))
self.config.renewal_configs_dir, "the-lineage.com.conf")))
self.assertTrue(os.path.exists(os.path.join(
self.cli_config.live_dir, "the-lineage.com", "README")))
self.config.live_dir, "the-lineage.com", "README")))
with open(result.fullchain, "rb") as f:
self.assertEqual(f.read(), b"cert" + b"chain")
# Let's do it again and make sure it makes a different lineage
result = storage.RenewableCert.new_lineage(
"the-lineage.com", b"cert2", b"privkey2", b"chain2", self.cli_config)
"the-lineage.com", b"cert2", b"privkey2", b"chain2", self.config)
self.assertTrue(os.path.exists(os.path.join(
self.cli_config.renewal_configs_dir, "the-lineage.com-0001.conf")))
self.config.renewal_configs_dir, "the-lineage.com-0001.conf")))
self.assertTrue(os.path.exists(os.path.join(
self.cli_config.live_dir, "the-lineage.com-0001", "README")))
self.config.live_dir, "the-lineage.com-0001", "README")))
# Now trigger the detection of already existing files
os.mkdir(os.path.join(
self.cli_config.live_dir, "the-lineage.com-0002"))
self.config.live_dir, "the-lineage.com-0002"))
self.assertRaises(errors.CertStorageError,
storage.RenewableCert.new_lineage, "the-lineage.com",
b"cert3", b"privkey3", b"chain3", self.cli_config)
os.mkdir(os.path.join(self.cli_config.default_archive_dir, "other-example.com"))
b"cert3", b"privkey3", b"chain3", self.config)
os.mkdir(os.path.join(self.config.default_archive_dir, "other-example.com"))
self.assertRaises(errors.CertStorageError,
storage.RenewableCert.new_lineage,
"other-example.com", b"cert4",
b"privkey4", b"chain4", self.cli_config)
b"privkey4", b"chain4", self.config)
# Make sure it can accept renewal parameters
result = storage.RenewableCert.new_lineage(
"the-lineage.com", b"cert2", b"privkey2", b"chain2", self.cli_config)
"the-lineage.com", b"cert2", b"privkey2", b"chain2", self.config)
# TODO: Conceivably we could test that the renewal parameters actually
# got saved
@ -640,19 +636,19 @@ class RenewableCertTests(BaseRenewableCertTest):
mock_rv.side_effect = lambda x: x
from certbot import storage
shutil.rmtree(self.cli_config.renewal_configs_dir)
shutil.rmtree(self.cli_config.default_archive_dir)
shutil.rmtree(self.cli_config.live_dir)
shutil.rmtree(self.config.renewal_configs_dir)
shutil.rmtree(self.config.default_archive_dir)
shutil.rmtree(self.config.live_dir)
storage.RenewableCert.new_lineage(
"the-lineage.com", b"cert2", b"privkey2", b"chain2", self.cli_config)
"the-lineage.com", b"cert2", b"privkey2", b"chain2", self.config)
self.assertTrue(os.path.exists(
os.path.join(
self.cli_config.renewal_configs_dir, "the-lineage.com.conf")))
self.config.renewal_configs_dir, "the-lineage.com.conf")))
self.assertTrue(os.path.exists(os.path.join(
self.cli_config.live_dir, "the-lineage.com", "privkey.pem")))
self.config.live_dir, "the-lineage.com", "privkey.pem")))
self.assertTrue(os.path.exists(os.path.join(
self.cli_config.default_archive_dir, "the-lineage.com", "privkey1.pem")))
self.config.default_archive_dir, "the-lineage.com", "privkey1.pem")))
@mock.patch("certbot.storage.util.unique_lineage_name")
def test_invalid_config_filename(self, mock_uln):
@ -660,7 +656,7 @@ class RenewableCertTests(BaseRenewableCertTest):
mock_uln.return_value = "this_does_not_end_with_dot_conf", "yikes"
self.assertRaises(errors.CertStorageError,
storage.RenewableCert.new_lineage, "example.com",
"cert", "privkey", "chain", self.cli_config)
"cert", "privkey", "chain", self.config)
def test_bad_kind(self):
self.assertRaises(
@ -744,18 +740,18 @@ class RenewableCertTests(BaseRenewableCertTest):
from certbot import storage
self.assertRaises(errors.CertStorageError,
storage.RenewableCert,
self.config.filename, self.cli_config)
os.symlink("missing", self.config[ALL_FOUR[0]])
self.config_file.filename, self.config)
os.symlink("missing", self.config_file[ALL_FOUR[0]])
self.assertRaises(errors.CertStorageError,
storage.RenewableCert,
self.config.filename, self.cli_config)
self.config_file.filename, self.config)
def test_write_renewal_config(self):
# Mostly tested by the process of creating and updating lineages,
# but we can test that this successfully creates files, removes
# unneeded items, and preserves comments.
temp = os.path.join(self.tempdir, "sample-file")
temp2 = os.path.join(self.tempdir, "sample-file.new")
temp = os.path.join(self.config.config_dir, "sample-file")
temp2 = os.path.join(self.config.config_dir, "sample-file.new")
with open(temp, "w") as f:
f.write("[renewalparams]\nuseful = value # A useful value\n"
"useless = value # Not needed\n")
@ -785,17 +781,17 @@ class RenewableCertTests(BaseRenewableCertTest):
def test_update_symlinks(self):
from certbot import storage
archive_dir_path = os.path.join(self.tempdir, "archive", "example.org")
archive_dir_path = os.path.join(self.config.config_dir, "archive", "example.org")
for kind in ALL_FOUR:
live_path = self.config[kind]
live_path = self.config_file[kind]
basename = kind + "1.pem"
archive_path = os.path.join(archive_dir_path, basename)
open(archive_path, 'a').close()
os.symlink(os.path.join(self.tempdir, basename), live_path)
os.symlink(os.path.join(self.config.config_dir, basename), live_path)
self.assertRaises(errors.CertStorageError,
storage.RenewableCert, self.config.filename,
self.cli_config)
storage.RenewableCert(self.config.filename, self.cli_config,
storage.RenewableCert, self.config_file.filename,
self.config)
storage.RenewableCert(self.config_file.filename, self.config,
update_symlinks=True)
class DeleteFilesTest(BaseRenewableCertTest):
@ -804,88 +800,88 @@ class DeleteFilesTest(BaseRenewableCertTest):
super(DeleteFilesTest, self).setUp()
for kind in ALL_FOUR:
kind_path = os.path.join(self.tempdir, "live", "example.org",
kind_path = os.path.join(self.config.config_dir, "live", "example.org",
kind + ".pem")
with open(kind_path, 'a'):
pass
self.config.write()
self.config_file.write()
self.assertTrue(os.path.exists(os.path.join(
self.cli_config.renewal_configs_dir, "example.org.conf")))
self.config.renewal_configs_dir, "example.org.conf")))
self.assertTrue(os.path.exists(os.path.join(
self.cli_config.live_dir, "example.org")))
self.config.live_dir, "example.org")))
self.assertTrue(os.path.exists(os.path.join(
self.tempdir, "archive", "example.org")))
self.config.config_dir, "archive", "example.org")))
def _call(self):
from certbot import storage
with mock.patch("certbot.storage.logger"):
storage.delete_files(self.cli_config, "example.org")
storage.delete_files(self.config, "example.org")
def test_delete_all_files(self):
self._call()
self.assertFalse(os.path.exists(os.path.join(
self.cli_config.renewal_configs_dir, "example.org.conf")))
self.config.renewal_configs_dir, "example.org.conf")))
self.assertFalse(os.path.exists(os.path.join(
self.cli_config.live_dir, "example.org")))
self.config.live_dir, "example.org")))
self.assertFalse(os.path.exists(os.path.join(
self.tempdir, "archive", "example.org")))
self.config.config_dir, "archive", "example.org")))
def test_bad_renewal_config(self):
with open(self.config.filename, 'a') as config_file:
with open(self.config_file.filename, 'a') as config_file:
config_file.write("asdfasfasdfasdf")
self.assertRaises(errors.CertStorageError, self._call)
self.assertTrue(os.path.exists(os.path.join(
self.cli_config.live_dir, "example.org")))
self.config.live_dir, "example.org")))
self.assertFalse(os.path.exists(os.path.join(
self.cli_config.renewal_configs_dir, "example.org.conf")))
self.config.renewal_configs_dir, "example.org.conf")))
def test_no_renewal_config(self):
os.remove(self.config.filename)
os.remove(self.config_file.filename)
self.assertRaises(errors.CertStorageError, self._call)
self.assertTrue(os.path.exists(os.path.join(
self.cli_config.live_dir, "example.org")))
self.assertFalse(os.path.exists(self.config.filename))
self.config.live_dir, "example.org")))
self.assertFalse(os.path.exists(self.config_file.filename))
def test_no_cert_file(self):
os.remove(os.path.join(
self.cli_config.live_dir, "example.org", "cert.pem"))
self.config.live_dir, "example.org", "cert.pem"))
self._call()
self.assertFalse(os.path.exists(self.config.filename))
self.assertFalse(os.path.exists(self.config_file.filename))
self.assertFalse(os.path.exists(os.path.join(
self.cli_config.live_dir, "example.org")))
self.config.live_dir, "example.org")))
self.assertFalse(os.path.exists(os.path.join(
self.tempdir, "archive", "example.org")))
self.config.config_dir, "archive", "example.org")))
def test_no_readme_file(self):
os.remove(os.path.join(
self.cli_config.live_dir, "example.org", "README"))
self.config.live_dir, "example.org", "README"))
self._call()
self.assertFalse(os.path.exists(self.config.filename))
self.assertFalse(os.path.exists(self.config_file.filename))
self.assertFalse(os.path.exists(os.path.join(
self.cli_config.live_dir, "example.org")))
self.config.live_dir, "example.org")))
self.assertFalse(os.path.exists(os.path.join(
self.tempdir, "archive", "example.org")))
self.config.config_dir, "archive", "example.org")))
def test_livedir_not_empty(self):
with open(os.path.join(
self.cli_config.live_dir, "example.org", "other_file"), 'a'):
self.config.live_dir, "example.org", "other_file"), 'a'):
pass
self._call()
self.assertFalse(os.path.exists(self.config.filename))
self.assertFalse(os.path.exists(self.config_file.filename))
self.assertTrue(os.path.exists(os.path.join(
self.cli_config.live_dir, "example.org")))
self.config.live_dir, "example.org")))
self.assertFalse(os.path.exists(os.path.join(
self.tempdir, "archive", "example.org")))
self.config.config_dir, "archive", "example.org")))
def test_no_archive(self):
archive_dir = os.path.join(self.tempdir, "archive", "example.org")
archive_dir = os.path.join(self.config.config_dir, "archive", "example.org")
os.rmdir(archive_dir)
self._call()
self.assertFalse(os.path.exists(self.config.filename))
self.assertFalse(os.path.exists(self.config_file.filename))
self.assertFalse(os.path.exists(os.path.join(
self.cli_config.live_dir, "example.org")))
self.config.live_dir, "example.org")))
self.assertFalse(os.path.exists(archive_dir))

11
certbot/tests/testdata/README vendored Normal file
View file

@ -0,0 +1,11 @@
The following command has been used to generate test keys:
for x in 256 512 2048; do openssl genrsa -out rsa${k}_key.pem $k; done
and for the CSR PEM (Certificate Signing Request):
openssl req -new -out csr-Xsans_X.pem -key rsa512_key.pem [-config csr-Xsans_X.conf | -subj '/CN=example.com'] [-outform DER > 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]

View file

@ -1 +0,0 @@
MIIB3jCCAYigAwIBAgICBTkwDQYJKoZIhvcNAQELBQAwdzELMAkGA1UEBhMCVVMxETAPBgNVBAgMCE1pY2hpZ2FuMRIwEAYDVQQHDAlBbm4gQXJib3IxKzApBgNVBAoMIlVuaXZlcnNpdHkgb2YgTWljaGlnYW4gYW5kIHRoZSBFRkYxFDASBgNVBAMMC2V4YW1wbGUuY29tMB4XDTE0MTIxMTIyMzQ0NVoXDTE0MTIxODIyMzQ0NVowdzELMAkGA1UEBhMCVVMxETAPBgNVBAgMCE1pY2hpZ2FuMRIwEAYDVQQHDAlBbm4gQXJib3IxKzApBgNVBAoMIlVuaXZlcnNpdHkgb2YgTWljaGlnYW4gYW5kIHRoZSBFRkYxFDASBgNVBAMMC2V4YW1wbGUuY29tMFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBAKx1c7RR7R_drnBSQ_zfx1vQLHUbFLh1AQQQ5R8DZUXd36efNK79vukFhN9HFoHZiUvOjm0c-pVE6K-EdE_twuUCAwEAATANBgkqhkiG9w0BAQsFAANBAC24z0IdwIVKSlntksllvr6zJepBH5fMndfk3XJp10jT6VE-14KNtjh02a56GoraAvJAT5_H67E8GvJ_ocNnB_o

Binary file not shown.

View file

@ -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-----

View file

@ -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

View file

@ -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-----

View file

@ -1,8 +0,0 @@
-----BEGIN CERTIFICATE REQUEST-----
MIIBFTCBwAIBADBbMQswCQYDVQQGEwJBVTETMBEGA1UECAwKU29tZS1TdGF0ZTEh
MB8GA1UECgwYSW50ZXJuZXQgV2lkZ2l0cyBQdHkgTHRkMRQwEgYDVQQDDAtleGFt
cGxlLm9yZzBcMA0GCSqGSIb3DQEBAQUAA0sAMEgCQQD0thFxUTc2v6qV55wRxfwn
BUOeN4bVfu5ywJqy65kzR7T1yZi5TPEiQyM7/3HgBVy9ddFc8RX4vNZaR+ROXNEz
AgMBAAGgADANBgkqhkiG9w0BAQsFAANBAMikGL8Ch7hQCStXH7chhDp6+pt2+VSo
wgsrPQ2Bw4veDMlSemUrH+4e0TwbbntHfvXTDHWs9P3BiIDJLxFrjuA=
-----END CERTIFICATE REQUEST-----

View file

@ -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

View file

@ -0,0 +1,9 @@
-----BEGIN CERTIFICATE REQUEST-----
MIIBMzCB3gIBADB5MQswCQYDVQQGEwJVUzERMA8GA1UECAwITWljaGlnYW4xEjAQ
BgNVBAcMCUFubiBBcmJvcjEMMAoGA1UECgwDRUZGMR8wHQYDVQQLDBZVbml2ZXJz
aXR5IHBmIE1pY2hpZ2FuMRQwEgYDVQQDDAtleGFtcGxlLmNvbTBcMA0GCSqGSIb3
DQEBAQUAA0sAMEgCQQCsdXO0Ue0f3a5wUkP838db0Cx1GxS4dQEEEOUfA2VF3d+n
nzSu/b7pBYTfRxaB2YlLzo5tHPqVROivhHRP7cLlAgMBAAGgADANBgkqhkiG9w0B
AQsFAANBAG06jIPvSC6wiGLy7sUTaEX4UCE6Cztp3vh/uXN7Q++CGn6KiXNs/BRW
eFlcFPbvxbVG/ZZFR5aPs+Oy6RhqOjg=
-----END CERTIFICATE REQUEST-----

View file

@ -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-----

View file

@ -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-----

View file

@ -1,14 +0,0 @@
-----BEGIN CERTIFICATE-----
MIICNzCCAeGgAwIBAgIJALizm9Y3q620MA0GCSqGSIb3DQEBCwUAMHcxCzAJBgNV
BAYTAlVTMREwDwYDVQQIDAhNaWNoaWdhbjESMBAGA1UEBwwJQW5uIEFyYm9yMSsw
KQYDVQQKDCJVbml2ZXJzaXR5IG9mIE1pY2hpZ2FuIGFuZCB0aGUgRUZGMRQwEgYD
VQQDDAtleGFtcGxlLmNvbTAeFw0xNTA1MDkwMDI0NTJaFw0xNjA1MDgwMDI0NTJa
MHcxCzAJBgNVBAYTAlVTMREwDwYDVQQIDAhNaWNoaWdhbjESMBAGA1UEBwwJQW5u
IEFyYm9yMSswKQYDVQQKDCJVbml2ZXJzaXR5IG9mIE1pY2hpZ2FuIGFuZCB0aGUg
RUZGMRQwEgYDVQQDDAtleGFtcGxlLmNvbTBcMA0GCSqGSIb3DQEBAQUAA0sAMEgC
QQD0thFxUTc2v6qV55wRxfwnBUOeN4bVfu5ywJqy65kzR7T1yZi5TPEiQyM7/3Hg
BVy9ddFc8RX4vNZaR+ROXNEzAgMBAAGjUDBOMB0GA1UdDgQWBBRJieHEVSHKmBk0
mTExx1erzlylCjAfBgNVHSMEGDAWgBRJieHEVSHKmBk0mTExx1erzlylCjAMBgNV
HRMEBTADAQH/MA0GCSqGSIb3DQEBCwUAA0EABT/nlpqOaanFSLZmWIrKv0zt63k4
bmWNMA8fYT45KYpLomsW8qXdpC82IlVKfNk7fW0UYT3HOeDSJRcycxNCTQ==
-----END CERTIFICATE-----

View file

@ -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-----

View file

@ -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.

View file

@ -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.

View file

@ -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

View file

@ -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 <https://github.com/diafygi/letsencrypt-nosudo>`_ or
`simp_le <https://github.com/kuba/simp_le>`_ are more appropriate choices.
`simp_le <https://github.com/zenhack/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!

View file

@ -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

View file

@ -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

View file

@ -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-----

View file

@ -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.

View file

@ -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.

View file

@ -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
}

View file

@ -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 \

View file

@ -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 \

View file

@ -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
}

View file

@ -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

View file

@ -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 \

View file

@ -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

View file

@ -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'

View file

@ -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 \

View file

@ -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

View file

@ -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

View file

@ -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:

View file

@ -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 = {'': '<a href="certbot/">certbot/</a>',
@ -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 = {'': '<a href="certbot/">certbot/</a>',
'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 "

View file

@ -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