mirror of
https://github.com/certbot/certbot.git
synced 2026-06-09 08:42:57 -04:00
Merge remote-tracking branch 'origin/master' into new-dl
This commit is contained in:
commit
54220a1336
16 changed files with 324 additions and 189 deletions
|
|
@ -484,9 +484,11 @@ class ClientNetworkTest(unittest.TestCase):
|
|||
def test_check_response_not_ok_jobj_no_error(self):
|
||||
self.response.ok = False
|
||||
self.response.json.return_value = {}
|
||||
# pylint: disable=protected-access
|
||||
self.assertRaises(
|
||||
errors.ClientError, self.net._check_response, self.response)
|
||||
with mock.patch('acme.client.messages.Error.from_json') as from_json:
|
||||
from_json.side_effect = jose.DeserializationError
|
||||
# pylint: disable=protected-access
|
||||
self.assertRaises(
|
||||
errors.ClientError, self.net._check_response, self.response)
|
||||
|
||||
def test_check_response_not_ok_jobj_error(self):
|
||||
self.response.ok = False
|
||||
|
|
|
|||
|
|
@ -37,9 +37,9 @@ class Error(jose.JSONObjectWithFields, errors.Error):
|
|||
)
|
||||
)
|
||||
|
||||
typ = jose.Field('type')
|
||||
typ = jose.Field('type', omitempty=True, default='about:blank')
|
||||
title = jose.Field('title', omitempty=True)
|
||||
detail = jose.Field('detail')
|
||||
detail = jose.Field('detail', omitempty=True)
|
||||
|
||||
@property
|
||||
def description(self):
|
||||
|
|
@ -143,12 +143,6 @@ class Directory(jose.JSONDeSerializable):
|
|||
|
||||
def __init__(self, jobj):
|
||||
canon_jobj = util.map_keys(jobj, self._canon_key)
|
||||
if not set(canon_jobj).issubset(
|
||||
set(self._REGISTERED_TYPES).union(['meta'])):
|
||||
# TODO: acme-spec is not clear about this: 'It is a JSON
|
||||
# dictionary, whose keys are the "resource" values listed
|
||||
# in {{https-requests}}'
|
||||
raise ValueError('Wrong directory fields')
|
||||
# TODO: check that everything is an absolute URL; acme-spec is
|
||||
# not clear on that
|
||||
self._jobj = canon_jobj
|
||||
|
|
@ -171,10 +165,7 @@ class Directory(jose.JSONDeSerializable):
|
|||
@classmethod
|
||||
def from_json(cls, jobj):
|
||||
jobj['meta'] = cls.Meta.from_json(jobj.pop('meta', {}))
|
||||
try:
|
||||
return cls(jobj)
|
||||
except ValueError as error:
|
||||
raise jose.DeserializationError(str(error))
|
||||
return cls(jobj)
|
||||
|
||||
|
||||
class Resource(jose.JSONObjectWithFields):
|
||||
|
|
|
|||
|
|
@ -28,6 +28,14 @@ class ErrorTest(unittest.TestCase):
|
|||
self.error_custom = Error(typ='custom', detail='bar')
|
||||
self.jobj_cusom = {'type': 'custom', 'detail': 'bar'}
|
||||
|
||||
def test_default_typ(self):
|
||||
from acme.messages import Error
|
||||
self.assertEqual(Error().typ, 'about:blank')
|
||||
|
||||
def test_from_json_empty(self):
|
||||
from acme.messages import Error
|
||||
self.assertEqual(Error(), Error.from_json('{}'))
|
||||
|
||||
def test_from_json_hashable(self):
|
||||
from acme.messages import Error
|
||||
hash(Error.from_json(self.error.to_json()))
|
||||
|
|
@ -97,9 +105,9 @@ class DirectoryTest(unittest.TestCase):
|
|||
),
|
||||
})
|
||||
|
||||
def test_init_wrong_key_value_error(self):
|
||||
def test_init_wrong_key_value_success(self): # pylint: disable=no-self-use
|
||||
from acme.messages import Directory
|
||||
self.assertRaises(ValueError, Directory, {'foo': 'bar'})
|
||||
Directory({'foo': 'bar'})
|
||||
|
||||
def test_getitem(self):
|
||||
self.assertEqual('reg', self.dir['new-reg'])
|
||||
|
|
@ -127,10 +135,9 @@ class DirectoryTest(unittest.TestCase):
|
|||
},
|
||||
})
|
||||
|
||||
def test_from_json_deserialization_error_on_wrong_key(self):
|
||||
def test_from_json_deserialization_unknown_key_success(self): # pylint: disable=no-self-use
|
||||
from acme.messages import Directory
|
||||
self.assertRaises(
|
||||
jose.DeserializationError, Directory.from_json, {'foo': 'bar'})
|
||||
Directory.from_json({'foo': 'bar'})
|
||||
|
||||
|
||||
class RegistrationTest(unittest.TestCase):
|
||||
|
|
|
|||
|
|
@ -17,7 +17,7 @@ class RawNginxParser(object):
|
|||
right_bracket = Literal("}").suppress()
|
||||
semicolon = Literal(";").suppress()
|
||||
space = White().suppress()
|
||||
key = Word(alphanums + "_/")
|
||||
key = Word(alphanums + "_/+-.")
|
||||
# Matches anything that is not a special character AND any chars in single
|
||||
# or double quotes
|
||||
value = Regex(r"((\".*\")?(\'.*\')?[^\{\};,]?)+")
|
||||
|
|
|
|||
|
|
@ -241,6 +241,9 @@ to serve all files under specified web root ({0})."""
|
|||
elif exc.errno == errno.EACCES:
|
||||
logger.debug("Challenges cleaned up but no permissions for %s",
|
||||
root_path)
|
||||
elif exc.errno == errno.ENOENT:
|
||||
logger.debug("Challenges cleaned up, %s does not exists",
|
||||
root_path)
|
||||
else:
|
||||
raise
|
||||
|
||||
|
|
|
|||
|
|
@ -218,13 +218,26 @@ class AuthenticatorTest(unittest.TestCase):
|
|||
self.auth.perform([self.achall])
|
||||
|
||||
os_error = OSError()
|
||||
os_error.errno = errno.ENOENT
|
||||
os_error.errno = errno.EPERM
|
||||
mock_rmdir.side_effect = os_error
|
||||
|
||||
self.assertRaises(OSError, self.auth.cleanup, [self.achall])
|
||||
self.assertFalse(os.path.exists(self.validation_path))
|
||||
self.assertTrue(os.path.exists(self.root_challenge_path))
|
||||
|
||||
@mock.patch('os.rmdir')
|
||||
def test_cleanup_file_not_exists(self, mock_rmdir):
|
||||
self.auth.prepare()
|
||||
self.auth.perform([self.achall])
|
||||
|
||||
os_error = OSError()
|
||||
os_error.errno = errno.ENOENT
|
||||
mock_rmdir.side_effect = os_error
|
||||
|
||||
self.auth.cleanup([self.achall])
|
||||
self.assertFalse(os.path.exists(self.validation_path))
|
||||
self.assertTrue(os.path.exists(self.root_challenge_path))
|
||||
|
||||
|
||||
class WebrootActionTest(unittest.TestCase):
|
||||
"""Tests for webroot argparse actions."""
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
#!/bin/sh
|
||||
#
|
||||
# Download and run the latest release version of the Let's Encrypt client.
|
||||
# Download and run the latest release version of the Certbot client.
|
||||
#
|
||||
# NOTE: THIS SCRIPT IS AUTO-GENERATED AND SELF-UPDATING
|
||||
#
|
||||
|
|
@ -20,10 +20,35 @@ VENV_NAME="letsencrypt"
|
|||
VENV_PATH=${VENV_PATH:-"$XDG_DATA_HOME/$VENV_NAME"}
|
||||
VENV_BIN="$VENV_PATH/bin"
|
||||
LE_AUTO_VERSION="0.6.0.dev0"
|
||||
BASENAME=$(basename $0)
|
||||
USAGE="Usage: $BASENAME [OPTIONS]
|
||||
A self-updating wrapper script for the Certbot ACME client. When run, updates
|
||||
to both this script and certbot will be downloaded and installed. After
|
||||
ensuring you have the latest versions installed, certbot will be invoked with
|
||||
all arguments you have provided.
|
||||
|
||||
Help for certbot itself cannot be provided until it is installed.
|
||||
|
||||
--debug attempt experimental installation
|
||||
-h, --help print this help
|
||||
-n, --non-interactive, --noninteractive run without asking for user input
|
||||
--no-self-upgrade do not download updates
|
||||
--os-packages-only install OS dependencies and exit
|
||||
-v, --verbose provide more output
|
||||
|
||||
All arguments are accepted and forwarded to the Certbot client when run."
|
||||
|
||||
while getopts ":hnv" arg; do
|
||||
case $arg in
|
||||
h)
|
||||
HELP=1;;
|
||||
n)
|
||||
ASSUME_YES=1;;
|
||||
v)
|
||||
VERBOSE=1;;
|
||||
esac
|
||||
done
|
||||
|
||||
# This script takes the same arguments as the main letsencrypt program, but it
|
||||
# additionally responds to --verbose (more output) and --debug (allow support
|
||||
# for experimental platforms)
|
||||
for arg in "$@" ; do
|
||||
case "$arg" in
|
||||
--debug)
|
||||
|
|
@ -34,19 +59,17 @@ for arg in "$@" ; do
|
|||
# Do not upgrade this script (also prevents client upgrades, because each
|
||||
# copy of the script pins a hash of the python client)
|
||||
NO_SELF_UPGRADE=1;;
|
||||
--help)
|
||||
HELP=1;;
|
||||
--noninteractive|--non-interactive)
|
||||
ASSUME_YES=1;;
|
||||
--verbose)
|
||||
VERBOSE=1;;
|
||||
[!-]*|-*[!v]*|-)
|
||||
# Anything that isn't -v, -vv, etc.: that is, anything that does not
|
||||
# start with a -, contains anything that's not a v, or is just "-"
|
||||
;;
|
||||
*) # -v+ remains.
|
||||
VERBOSE=1;;
|
||||
esac
|
||||
done
|
||||
|
||||
# letsencrypt-auto needs root access to bootstrap OS dependencies, and
|
||||
# letsencrypt itself needs root access for almost all modes of operation
|
||||
# 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`
|
||||
|
|
@ -81,6 +104,12 @@ else
|
|||
SUDO=
|
||||
fi
|
||||
|
||||
if [ $BASENAME = "letsencrypt-auto" ]; then
|
||||
# letsencrypt-auto does not respect --help or --yes for backwards compatibility
|
||||
ASSUME_YES=1
|
||||
HELP=0
|
||||
fi
|
||||
|
||||
ExperimentalBootstrap() {
|
||||
# Arguments: Platform name, bootstrap function name
|
||||
if [ "$DEBUG" = 1 ]; then
|
||||
|
|
@ -151,30 +180,45 @@ BootstrapDebCommon() {
|
|||
augeas_pkg="libaugeas0 augeas-lenses"
|
||||
AUGVERSION=`apt-cache show --no-all-versions libaugeas0 | grep ^Version: | cut -d" " -f2`
|
||||
|
||||
if [ "$ASSUME_YES" = 1 ]; then
|
||||
YES_FLAG="-y"
|
||||
fi
|
||||
|
||||
AddBackportRepo() {
|
||||
# ARGS:
|
||||
BACKPORT_NAME="$1"
|
||||
BACKPORT_SOURCELINE="$2"
|
||||
echo "To use the Apache Certbot plugin, augeas needs to be installed from $BACKPORT_NAME."
|
||||
if ! grep -v -e ' *#' /etc/apt/sources.list | grep -q "$BACKPORT_NAME" ; then
|
||||
# This can theoretically error if sources.list.d is empty, but in that case we don't care.
|
||||
if ! grep -v -e ' *#' /etc/apt/sources.list.d/* 2>/dev/null | grep -q "$BACKPORT_NAME"; then
|
||||
/bin/echo -n "Installing augeas from $BACKPORT_NAME in 3 seconds..."
|
||||
sleep 1s
|
||||
/bin/echo -ne "\e[0K\rInstalling augeas from $BACKPORT_NAME in 2 seconds..."
|
||||
sleep 1s
|
||||
/bin/echo -e "\e[0K\rInstalling augeas from $BACKPORT_NAME in 1 second ..."
|
||||
sleep 1s
|
||||
if echo $BACKPORT_NAME | grep -q wheezy ; then
|
||||
/bin/echo '(Backports are only installed if explicitly requested via "apt-get install -t wheezy-backports")'
|
||||
if [ "$ASSUME_YES" = 1 ]; then
|
||||
/bin/echo -n "Installing augeas from $BACKPORT_NAME in 3 seconds..."
|
||||
sleep 1s
|
||||
/bin/echo -ne "\e[0K\rInstalling augeas from $BACKPORT_NAME in 2 seconds..."
|
||||
sleep 1s
|
||||
/bin/echo -e "\e[0K\rInstalling augeas from $BACKPORT_NAME in 1 second ..."
|
||||
sleep 1s
|
||||
add_backports=1
|
||||
else
|
||||
read -p "Would you like to enable the $BACKPORT_NAME repository [Y/n]? " response
|
||||
case $response in
|
||||
[yY][eE][sS]|[yY]|"")
|
||||
add_backports=1;;
|
||||
*)
|
||||
add_backports=0;;
|
||||
esac
|
||||
fi
|
||||
if [ "$add_backports" = 1 ]; then
|
||||
$SUDO sh -c "echo $BACKPORT_SOURCELINE >> /etc/apt/sources.list.d/$BACKPORT_NAME.list"
|
||||
$SUDO apt-get update
|
||||
fi
|
||||
|
||||
$SUDO sh -c "echo $BACKPORT_SOURCELINE >> /etc/apt/sources.list.d/$BACKPORT_NAME.list"
|
||||
$SUDO apt-get update
|
||||
fi
|
||||
fi
|
||||
$SUDO apt-get install -y --no-install-recommends -t "$BACKPORT_NAME" $augeas_pkg
|
||||
augeas_pkg=
|
||||
|
||||
if [ "$add_backports" != 0 ]; then
|
||||
$SUDO apt-get install $YES_FLAG --no-install-recommends -t "$BACKPORT_NAME" $augeas_pkg
|
||||
augeas_pkg=
|
||||
fi
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -186,12 +230,12 @@ BootstrapDebCommon() {
|
|||
AddBackportRepo precise-backports "deb http://archive.ubuntu.com/ubuntu precise-backports main restricted universe multiverse"
|
||||
else
|
||||
echo "No libaugeas0 version is available that's new enough to run the"
|
||||
echo "Let's Encrypt apache plugin..."
|
||||
echo "Certbot apache plugin..."
|
||||
fi
|
||||
# XXX add a case for ubuntu PPAs
|
||||
fi
|
||||
|
||||
$SUDO apt-get install -y --no-install-recommends \
|
||||
$SUDO apt-get install $YES_FLAG --no-install-recommends \
|
||||
python \
|
||||
python-dev \
|
||||
$virtualenv \
|
||||
|
|
@ -212,9 +256,10 @@ BootstrapDebCommon() {
|
|||
|
||||
BootstrapRpmCommon() {
|
||||
# Tested with:
|
||||
# - Fedora 22, 23 (x64)
|
||||
# - Fedora 20, 21, 22, 23 (x64)
|
||||
# - Centos 7 (x64: on DigitalOcean droplet)
|
||||
# - CentOS 7 Minimal install in a Hyper-V VM
|
||||
# - CentOS 6 (EPEL must be installed manually)
|
||||
|
||||
if type dnf 2>/dev/null
|
||||
then
|
||||
|
|
@ -228,54 +273,62 @@ BootstrapRpmCommon() {
|
|||
exit 1
|
||||
fi
|
||||
|
||||
pkgs="
|
||||
gcc
|
||||
dialog
|
||||
augeas-libs
|
||||
openssl
|
||||
openssl-devel
|
||||
libffi-devel
|
||||
redhat-rpm-config
|
||||
ca-certificates
|
||||
"
|
||||
|
||||
# Some distros and older versions of current distros use a "python27"
|
||||
# instead of "python" naming convention. Try both conventions.
|
||||
if ! $SUDO $tool install -y \
|
||||
python \
|
||||
python-devel \
|
||||
python-virtualenv \
|
||||
python-tools \
|
||||
python-pip
|
||||
then
|
||||
if ! $SUDO $tool install -y \
|
||||
python27 \
|
||||
python27-devel \
|
||||
python27-virtualenv \
|
||||
python27-tools \
|
||||
python27-pip
|
||||
then
|
||||
echo "Could not install Python dependencies. Aborting bootstrap!"
|
||||
exit 1
|
||||
fi
|
||||
if $SUDO $tool list python >/dev/null 2>&1; then
|
||||
pkgs="$pkgs
|
||||
python
|
||||
python-devel
|
||||
python-virtualenv
|
||||
python-tools
|
||||
python-pip
|
||||
"
|
||||
else
|
||||
pkgs="$pkgs
|
||||
python27
|
||||
python27-devel
|
||||
python27-virtualenv
|
||||
python27-tools
|
||||
python27-pip
|
||||
"
|
||||
fi
|
||||
|
||||
if ! $SUDO $tool install -y \
|
||||
gcc \
|
||||
dialog \
|
||||
augeas-libs \
|
||||
openssl \
|
||||
openssl-devel \
|
||||
libffi-devel \
|
||||
redhat-rpm-config \
|
||||
ca-certificates
|
||||
then
|
||||
echo "Could not install additional dependencies. Aborting bootstrap!"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
|
||||
if $SUDO $tool list installed "httpd" >/dev/null 2>&1; then
|
||||
if ! $SUDO $tool install -y mod_ssl
|
||||
then
|
||||
echo "Apache found, but mod_ssl could not be installed."
|
||||
fi
|
||||
pkgs="$pkgs
|
||||
mod_ssl
|
||||
"
|
||||
fi
|
||||
|
||||
if [ "$ASSUME_YES" = 1 ]; then
|
||||
yes_flag="-y"
|
||||
fi
|
||||
|
||||
if ! $SUDO $tool install $yes_flag $pkgs; then
|
||||
echo "Could not install OS dependencies. Aborting bootstrap!"
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
BootstrapSuseCommon() {
|
||||
# SLE12 don't have python-virtualenv
|
||||
|
||||
$SUDO zypper -nq in -l \
|
||||
if [ "$ASSUME_YES" = 1 ]; then
|
||||
zypper_flags="-nq"
|
||||
install_flags="-l"
|
||||
fi
|
||||
|
||||
$SUDO zypper $zypper_flags in $install_flags \
|
||||
python \
|
||||
python-devel \
|
||||
python-virtualenv \
|
||||
|
|
@ -310,8 +363,12 @@ BootstrapArchCommon() {
|
|||
# pacman -T exits with 127 if there are missing dependencies
|
||||
missing=$($SUDO pacman -T $deps) || true
|
||||
|
||||
if [ "$ASSUME_YES" = 1 ]; then
|
||||
noconfirm="--noconfirm"
|
||||
fi
|
||||
|
||||
if [ "$missing" ]; then
|
||||
$SUDO pacman -S --needed $missing
|
||||
$SUDO pacman -S --needed $missing $noconfirm
|
||||
fi
|
||||
}
|
||||
|
||||
|
|
@ -426,7 +483,7 @@ Bootstrap() {
|
|||
elif grep -iq "Amazon Linux" /etc/issue ; then
|
||||
ExperimentalBootstrap "Amazon Linux" BootstrapRpmCommon
|
||||
else
|
||||
echo "Sorry, I don't know how to bootstrap Let's Encrypt on your operating system!"
|
||||
echo "Sorry, I don't know how to bootstrap Certbot on your operating system!"
|
||||
echo
|
||||
echo "You will need to bootstrap, configure virtualenv, and run pip install manually."
|
||||
echo "Please see https://letsencrypt.readthedocs.org/en/latest/contributing.html#prerequisites"
|
||||
|
|
@ -446,7 +503,8 @@ if [ "$1" = "--le-auto-phase2" ]; then
|
|||
shift 1 # the --le-auto-phase2 arg
|
||||
if [ -f "$VENV_BIN/letsencrypt" ]; then
|
||||
# --version output ran through grep due to python-cryptography DeprecationWarnings
|
||||
INSTALLED_VERSION=$("$VENV_BIN/letsencrypt" --version 2>&1 | grep ^letsencrypt | cut -d " " -f 2)
|
||||
# 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)
|
||||
else
|
||||
INSTALLED_VERSION="none"
|
||||
fi
|
||||
|
|
@ -465,8 +523,8 @@ if [ "$1" = "--le-auto-phase2" ]; then
|
|||
# There is no $ interpolation due to quotes on starting heredoc delimiter.
|
||||
# -------------------------------------------------------------------------
|
||||
cat << "UNLIKELY_EOF" > "$TEMP_DIR/letsencrypt-auto-requirements.txt"
|
||||
# This is the flattened list of packages letsencrypt-auto installs. To generate
|
||||
# this, do `pip install --no-cache-dir -e acme -e . -e letsencrypt-apache`, and
|
||||
# This is the flattened list of packages certbot-auto installs. To generate
|
||||
# this, do `pip install --no-cache-dir -e acme -e . -e certbot-apache`, and
|
||||
# then use `hashin` or a more secure method to gather the hashes.
|
||||
|
||||
argparse==1.4.0 \
|
||||
|
|
@ -823,18 +881,22 @@ UNLIKELY_EOF
|
|||
fi
|
||||
echo "Installation succeeded."
|
||||
fi
|
||||
echo "Requesting root privileges to run letsencrypt..."
|
||||
echo "Requesting root privileges to run certbot..."
|
||||
echo " " $SUDO "$VENV_BIN/letsencrypt" "$@"
|
||||
$SUDO "$VENV_BIN/letsencrypt" "$@"
|
||||
else
|
||||
# Phase 1: Upgrade letsencrypt-auto if neceesary, then self-invoke.
|
||||
# Phase 1: Upgrade certbot-auto if neceesary, then self-invoke.
|
||||
#
|
||||
# Each phase checks the version of only the thing it is responsible for
|
||||
# upgrading. Phase 1 checks the version of the latest release of
|
||||
# letsencrypt-auto (which is always the same as that of the letsencrypt
|
||||
# package). Phase 2 checks the version of the locally installed letsencrypt.
|
||||
# certbot-auto (which is always the same as that of the certbot
|
||||
# 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
|
||||
fi
|
||||
# If it looks like we've never bootstrapped before, bootstrap:
|
||||
Bootstrap
|
||||
fi
|
||||
|
|
@ -953,7 +1015,7 @@ def verified_new_le_auto(get, tag, temp_dir):
|
|||
stderr=dev_null)
|
||||
except CalledProcessError as exc:
|
||||
raise ExpectedError("Couldn't verify signature of downloaded "
|
||||
"letsencrypt-auto.", exc)
|
||||
"certbot-auto.", exc)
|
||||
|
||||
|
||||
def main():
|
||||
|
|
@ -980,27 +1042,24 @@ UNLIKELY_EOF
|
|||
DeterminePythonVersion
|
||||
REMOTE_VERSION=`"$LE_PYTHON" "$TEMP_DIR/fetch.py" --latest-version`
|
||||
if [ "$LE_AUTO_VERSION" != "$REMOTE_VERSION" ]; then
|
||||
echo "Upgrading letsencrypt-auto $LE_AUTO_VERSION to $REMOTE_VERSION..."
|
||||
echo "Upgrading certbot-auto $LE_AUTO_VERSION to $REMOTE_VERSION..."
|
||||
|
||||
# Now we drop into Python so we don't have to install even more
|
||||
# dependencies (curl, etc.), for better flow control, and for the option of
|
||||
# future Windows compatibility.
|
||||
"$LE_PYTHON" "$TEMP_DIR/fetch.py" --le-auto-script "v$REMOTE_VERSION"
|
||||
|
||||
# Install new copy of letsencrypt-auto.
|
||||
# Install new copy of certbot-auto.
|
||||
# TODO: Deal with quotes in pathnames.
|
||||
echo "Replacing letsencrypt-auto..."
|
||||
echo "Replacing certbot-auto..."
|
||||
# Clone permissions with cp. chmod and chown don't have a --reference
|
||||
# option on OS X or BSD, and stat -c on Linux is stat -f on OS X and BSD:
|
||||
echo " " $SUDO cp -p "$0" "$TEMP_DIR/letsencrypt-auto.permission-clone"
|
||||
$SUDO cp -p "$0" "$TEMP_DIR/letsencrypt-auto.permission-clone"
|
||||
echo " " $SUDO cp "$TEMP_DIR/letsencrypt-auto" "$TEMP_DIR/letsencrypt-auto.permission-clone"
|
||||
$SUDO 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.
|
||||
echo " " $SUDO mv -f "$TEMP_DIR/letsencrypt-auto.permission-clone" "$0"
|
||||
$SUDO mv -f "$TEMP_DIR/letsencrypt-auto.permission-clone" "$0"
|
||||
# TODO: Clean up temp dir safely, even if it has quotes in its path.
|
||||
rm -rf "$TEMP_DIR"
|
||||
|
|
|
|||
|
|
@ -20,10 +20,35 @@ VENV_NAME="letsencrypt"
|
|||
VENV_PATH=${VENV_PATH:-"$XDG_DATA_HOME/$VENV_NAME"}
|
||||
VENV_BIN="$VENV_PATH/bin"
|
||||
LE_AUTO_VERSION="{{ LE_AUTO_VERSION }}"
|
||||
BASENAME=$(basename $0)
|
||||
USAGE="Usage: $BASENAME [OPTIONS]
|
||||
A self-updating wrapper script for the Certbot ACME client. When run, updates
|
||||
to both this script and certbot will be downloaded and installed. After
|
||||
ensuring you have the latest versions installed, certbot will be invoked with
|
||||
all arguments you have provided.
|
||||
|
||||
Help for certbot itself cannot be provided until it is installed.
|
||||
|
||||
--debug attempt experimental installation
|
||||
-h, --help print this help
|
||||
-n, --non-interactive, --noninteractive run without asking for user input
|
||||
--no-self-upgrade do not download updates
|
||||
--os-packages-only install OS dependencies and exit
|
||||
-v, --verbose provide more output
|
||||
|
||||
All arguments are accepted and forwarded to the Certbot client when run."
|
||||
|
||||
while getopts ":hnv" arg; do
|
||||
case $arg in
|
||||
h)
|
||||
HELP=1;;
|
||||
n)
|
||||
ASSUME_YES=1;;
|
||||
v)
|
||||
VERBOSE=1;;
|
||||
esac
|
||||
done
|
||||
|
||||
# This script takes the same arguments as the main letsencrypt program, but it
|
||||
# additionally responds to --verbose (more output) and --debug (allow support
|
||||
# for experimental platforms)
|
||||
for arg in "$@" ; do
|
||||
case "$arg" in
|
||||
--debug)
|
||||
|
|
@ -34,18 +59,16 @@ for arg in "$@" ; do
|
|||
# Do not upgrade this script (also prevents client upgrades, because each
|
||||
# copy of the script pins a hash of the python client)
|
||||
NO_SELF_UPGRADE=1;;
|
||||
--help)
|
||||
HELP=1;;
|
||||
--noninteractive|--non-interactive)
|
||||
ASSUME_YES=1;;
|
||||
--verbose)
|
||||
VERBOSE=1;;
|
||||
[!-]*|-*[!v]*|-)
|
||||
# Anything that isn't -v, -vv, etc.: that is, anything that does not
|
||||
# start with a -, contains anything that's not a v, or is just "-"
|
||||
;;
|
||||
*) # -v+ remains.
|
||||
VERBOSE=1;;
|
||||
esac
|
||||
done
|
||||
|
||||
# letsencrypt-auto needs root access to bootstrap OS dependencies, and
|
||||
# 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
|
||||
|
|
@ -81,6 +104,12 @@ else
|
|||
SUDO=
|
||||
fi
|
||||
|
||||
if [ $BASENAME = "letsencrypt-auto" ]; then
|
||||
# letsencrypt-auto does not respect --help or --yes for backwards compatibility
|
||||
ASSUME_YES=1
|
||||
HELP=0
|
||||
fi
|
||||
|
||||
ExperimentalBootstrap() {
|
||||
# Arguments: Platform name, bootstrap function name
|
||||
if [ "$DEBUG" = 1 ]; then
|
||||
|
|
@ -177,7 +206,8 @@ if [ "$1" = "--le-auto-phase2" ]; then
|
|||
shift 1 # the --le-auto-phase2 arg
|
||||
if [ -f "$VENV_BIN/letsencrypt" ]; then
|
||||
# --version output ran through grep due to python-cryptography DeprecationWarnings
|
||||
INSTALLED_VERSION=$("$VENV_BIN/letsencrypt" --version 2>&1 | grep ^letsencrypt | cut -d " " -f 2)
|
||||
# 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)
|
||||
else
|
||||
INSTALLED_VERSION="none"
|
||||
fi
|
||||
|
|
@ -223,14 +253,18 @@ UNLIKELY_EOF
|
|||
echo " " $SUDO "$VENV_BIN/letsencrypt" "$@"
|
||||
$SUDO "$VENV_BIN/letsencrypt" "$@"
|
||||
else
|
||||
# Phase 1: Upgrade letsencrypt-auto if neceesary, then self-invoke.
|
||||
# Phase 1: Upgrade certbot-auto if neceesary, then self-invoke.
|
||||
#
|
||||
# Each phase checks the version of only the thing it is responsible for
|
||||
# upgrading. Phase 1 checks the version of the latest release of
|
||||
# letsencrypt-auto (which is always the same as that of the certbot
|
||||
# certbot-auto (which is always the same as that of the certbot
|
||||
# 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
|
||||
fi
|
||||
# If it looks like we've never bootstrapped before, bootstrap:
|
||||
Bootstrap
|
||||
fi
|
||||
|
|
@ -250,27 +284,24 @@ UNLIKELY_EOF
|
|||
DeterminePythonVersion
|
||||
REMOTE_VERSION=`"$LE_PYTHON" "$TEMP_DIR/fetch.py" --latest-version`
|
||||
if [ "$LE_AUTO_VERSION" != "$REMOTE_VERSION" ]; then
|
||||
echo "Upgrading letsencrypt-auto $LE_AUTO_VERSION to $REMOTE_VERSION..."
|
||||
echo "Upgrading certbot-auto $LE_AUTO_VERSION to $REMOTE_VERSION..."
|
||||
|
||||
# Now we drop into Python so we don't have to install even more
|
||||
# dependencies (curl, etc.), for better flow control, and for the option of
|
||||
# future Windows compatibility.
|
||||
"$LE_PYTHON" "$TEMP_DIR/fetch.py" --le-auto-script "v$REMOTE_VERSION"
|
||||
|
||||
# Install new copy of letsencrypt-auto.
|
||||
# Install new copy of certbot-auto.
|
||||
# TODO: Deal with quotes in pathnames.
|
||||
echo "Replacing letsencrypt-auto..."
|
||||
echo "Replacing certbot-auto..."
|
||||
# Clone permissions with cp. chmod and chown don't have a --reference
|
||||
# option on OS X or BSD, and stat -c on Linux is stat -f on OS X and BSD:
|
||||
echo " " $SUDO cp -p "$0" "$TEMP_DIR/letsencrypt-auto.permission-clone"
|
||||
$SUDO cp -p "$0" "$TEMP_DIR/letsencrypt-auto.permission-clone"
|
||||
echo " " $SUDO cp "$TEMP_DIR/letsencrypt-auto" "$TEMP_DIR/letsencrypt-auto.permission-clone"
|
||||
$SUDO 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.
|
||||
echo " " $SUDO mv -f "$TEMP_DIR/letsencrypt-auto.permission-clone" "$0"
|
||||
$SUDO mv -f "$TEMP_DIR/letsencrypt-auto.permission-clone" "$0"
|
||||
# TODO: Clean up temp dir safely, even if it has quotes in its path.
|
||||
rm -rf "$TEMP_DIR"
|
||||
|
|
|
|||
|
|
@ -21,7 +21,11 @@ BootstrapArchCommon() {
|
|||
# pacman -T exits with 127 if there are missing dependencies
|
||||
missing=$($SUDO pacman -T $deps) || true
|
||||
|
||||
if [ "$ASSUME_YES" = 1 ]; then
|
||||
noconfirm="--noconfirm"
|
||||
fi
|
||||
|
||||
if [ "$missing" ]; then
|
||||
$SUDO pacman -S --needed $missing
|
||||
$SUDO pacman -S --needed $missing $noconfirm
|
||||
fi
|
||||
}
|
||||
|
|
|
|||
|
|
@ -34,30 +34,45 @@ BootstrapDebCommon() {
|
|||
augeas_pkg="libaugeas0 augeas-lenses"
|
||||
AUGVERSION=`apt-cache show --no-all-versions libaugeas0 | grep ^Version: | cut -d" " -f2`
|
||||
|
||||
if [ "$ASSUME_YES" = 1 ]; then
|
||||
YES_FLAG="-y"
|
||||
fi
|
||||
|
||||
AddBackportRepo() {
|
||||
# ARGS:
|
||||
BACKPORT_NAME="$1"
|
||||
BACKPORT_SOURCELINE="$2"
|
||||
echo "To use the Apache Certbot plugin, augeas needs to be installed from $BACKPORT_NAME."
|
||||
if ! grep -v -e ' *#' /etc/apt/sources.list | grep -q "$BACKPORT_NAME" ; then
|
||||
# This can theoretically error if sources.list.d is empty, but in that case we don't care.
|
||||
if ! grep -v -e ' *#' /etc/apt/sources.list.d/* 2>/dev/null | grep -q "$BACKPORT_NAME"; then
|
||||
/bin/echo -n "Installing augeas from $BACKPORT_NAME in 3 seconds..."
|
||||
sleep 1s
|
||||
/bin/echo -ne "\e[0K\rInstalling augeas from $BACKPORT_NAME in 2 seconds..."
|
||||
sleep 1s
|
||||
/bin/echo -e "\e[0K\rInstalling augeas from $BACKPORT_NAME in 1 second ..."
|
||||
sleep 1s
|
||||
if echo $BACKPORT_NAME | grep -q wheezy ; then
|
||||
/bin/echo '(Backports are only installed if explicitly requested via "apt-get install -t wheezy-backports")'
|
||||
if [ "$ASSUME_YES" = 1 ]; then
|
||||
/bin/echo -n "Installing augeas from $BACKPORT_NAME in 3 seconds..."
|
||||
sleep 1s
|
||||
/bin/echo -ne "\e[0K\rInstalling augeas from $BACKPORT_NAME in 2 seconds..."
|
||||
sleep 1s
|
||||
/bin/echo -e "\e[0K\rInstalling augeas from $BACKPORT_NAME in 1 second ..."
|
||||
sleep 1s
|
||||
add_backports=1
|
||||
else
|
||||
read -p "Would you like to enable the $BACKPORT_NAME repository [Y/n]? " response
|
||||
case $response in
|
||||
[yY][eE][sS]|[yY]|"")
|
||||
add_backports=1;;
|
||||
*)
|
||||
add_backports=0;;
|
||||
esac
|
||||
fi
|
||||
if [ "$add_backports" = 1 ]; then
|
||||
$SUDO sh -c "echo $BACKPORT_SOURCELINE >> /etc/apt/sources.list.d/$BACKPORT_NAME.list"
|
||||
$SUDO apt-get update
|
||||
fi
|
||||
|
||||
$SUDO sh -c "echo $BACKPORT_SOURCELINE >> /etc/apt/sources.list.d/$BACKPORT_NAME.list"
|
||||
$SUDO apt-get update
|
||||
fi
|
||||
fi
|
||||
$SUDO apt-get install -y --no-install-recommends -t "$BACKPORT_NAME" $augeas_pkg
|
||||
augeas_pkg=
|
||||
|
||||
if [ "$add_backports" != 0 ]; then
|
||||
$SUDO apt-get install $YES_FLAG --no-install-recommends -t "$BACKPORT_NAME" $augeas_pkg
|
||||
augeas_pkg=
|
||||
fi
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -74,7 +89,7 @@ BootstrapDebCommon() {
|
|||
# XXX add a case for ubuntu PPAs
|
||||
fi
|
||||
|
||||
$SUDO apt-get install -y --no-install-recommends \
|
||||
$SUDO apt-get install $YES_FLAG --no-install-recommends \
|
||||
python \
|
||||
python-dev \
|
||||
$virtualenv \
|
||||
|
|
|
|||
|
|
@ -1,8 +1,9 @@
|
|||
BootstrapRpmCommon() {
|
||||
# Tested with:
|
||||
# - Fedora 22, 23 (x64)
|
||||
# - Fedora 20, 21, 22, 23 (x64)
|
||||
# - Centos 7 (x64: on DigitalOcean droplet)
|
||||
# - CentOS 7 Minimal install in a Hyper-V VM
|
||||
# - CentOS 6 (EPEL must be installed manually)
|
||||
|
||||
if type dnf 2>/dev/null
|
||||
then
|
||||
|
|
@ -16,46 +17,49 @@ BootstrapRpmCommon() {
|
|||
exit 1
|
||||
fi
|
||||
|
||||
pkgs="
|
||||
gcc
|
||||
dialog
|
||||
augeas-libs
|
||||
openssl
|
||||
openssl-devel
|
||||
libffi-devel
|
||||
redhat-rpm-config
|
||||
ca-certificates
|
||||
"
|
||||
|
||||
# Some distros and older versions of current distros use a "python27"
|
||||
# instead of "python" naming convention. Try both conventions.
|
||||
if ! $SUDO $tool install -y \
|
||||
python \
|
||||
python-devel \
|
||||
python-virtualenv \
|
||||
python-tools \
|
||||
python-pip
|
||||
then
|
||||
if ! $SUDO $tool install -y \
|
||||
python27 \
|
||||
python27-devel \
|
||||
python27-virtualenv \
|
||||
python27-tools \
|
||||
python27-pip
|
||||
then
|
||||
echo "Could not install Python dependencies. Aborting bootstrap!"
|
||||
exit 1
|
||||
fi
|
||||
if $SUDO $tool list python >/dev/null 2>&1; then
|
||||
pkgs="$pkgs
|
||||
python
|
||||
python-devel
|
||||
python-virtualenv
|
||||
python-tools
|
||||
python-pip
|
||||
"
|
||||
else
|
||||
pkgs="$pkgs
|
||||
python27
|
||||
python27-devel
|
||||
python27-virtualenv
|
||||
python27-tools
|
||||
python27-pip
|
||||
"
|
||||
fi
|
||||
|
||||
if ! $SUDO $tool install -y \
|
||||
gcc \
|
||||
dialog \
|
||||
augeas-libs \
|
||||
openssl \
|
||||
openssl-devel \
|
||||
libffi-devel \
|
||||
redhat-rpm-config \
|
||||
ca-certificates
|
||||
then
|
||||
echo "Could not install additional dependencies. Aborting bootstrap!"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
|
||||
if $SUDO $tool list installed "httpd" >/dev/null 2>&1; then
|
||||
if ! $SUDO $tool install -y mod_ssl
|
||||
then
|
||||
echo "Apache found, but mod_ssl could not be installed."
|
||||
fi
|
||||
pkgs="$pkgs
|
||||
mod_ssl
|
||||
"
|
||||
fi
|
||||
|
||||
if [ "$ASSUME_YES" = 1 ]; then
|
||||
yes_flag="-y"
|
||||
fi
|
||||
|
||||
if ! $SUDO $tool install $yes_flag $pkgs; then
|
||||
echo "Could not install OS dependencies. Aborting bootstrap!"
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,7 +1,12 @@
|
|||
BootstrapSuseCommon() {
|
||||
# SLE12 don't have python-virtualenv
|
||||
|
||||
$SUDO zypper -nq in -l \
|
||||
if [ "$ASSUME_YES" = 1 ]; then
|
||||
zypper_flags="-nq"
|
||||
install_flags="-l"
|
||||
fi
|
||||
|
||||
$SUDO zypper $zypper_flags in $install_flags \
|
||||
python \
|
||||
python-devel \
|
||||
python-virtualenv \
|
||||
|
|
|
|||
|
|
@ -103,7 +103,7 @@ def verified_new_le_auto(get, tag, temp_dir):
|
|||
stderr=dev_null)
|
||||
except CalledProcessError as exc:
|
||||
raise ExpectedError("Couldn't verify signature of downloaded "
|
||||
"letsencrypt-auto.", exc)
|
||||
"certbot-auto.", exc)
|
||||
|
||||
|
||||
def main():
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
# This is the flattened list of packages letsencrypt-auto installs. To generate
|
||||
# This is the flattened list of packages certbot-auto installs. To generate
|
||||
# this, do `pip install --no-cache-dir -e acme -e . -e certbot-apache`, and
|
||||
# then use `hashin` or a more secure method to gather the hashes.
|
||||
|
||||
|
|
|
|||
|
|
@ -231,7 +231,7 @@ class AutoTests(TestCase):
|
|||
* The OpenSSL sig mismatches.
|
||||
|
||||
For tests which get to the end, we run merely ``letsencrypt --version``.
|
||||
The functioning of the rest of the letsencrypt script is covered by other
|
||||
The functioning of the rest of the certbot script is covered by other
|
||||
test suites.
|
||||
|
||||
"""
|
||||
|
|
@ -277,7 +277,7 @@ class AutoTests(TestCase):
|
|||
ok_(re.match(r'letsencrypt \d+\.\d+\.\d+',
|
||||
err.strip().splitlines()[-1]))
|
||||
# Make a few assertions to test the validity of the next tests:
|
||||
self.assertIn('Upgrading letsencrypt-auto ', out)
|
||||
self.assertIn('Upgrading certbot-auto ', out)
|
||||
self.assertIn('Creating virtual environment...', out)
|
||||
|
||||
# Now we have le-auto 99.9.9 and LE 99.9.9 installed. This
|
||||
|
|
@ -286,14 +286,14 @@ class AutoTests(TestCase):
|
|||
# Test when neither phase-1 upgrade nor phase-2 upgrade is
|
||||
# needed (probably a common case):
|
||||
out, err = run_letsencrypt_auto()
|
||||
self.assertNotIn('Upgrading letsencrypt-auto ', out)
|
||||
self.assertNotIn('Upgrading certbot-auto ', out)
|
||||
self.assertNotIn('Creating virtual environment...', out)
|
||||
|
||||
# Test when a phase-1 upgrade is not needed but a phase-2
|
||||
# upgrade is:
|
||||
set_le_script_version(venv_dir, '0.0.1')
|
||||
out, err = run_letsencrypt_auto()
|
||||
self.assertNotIn('Upgrading letsencrypt-auto ', out)
|
||||
self.assertNotIn('Upgrading certbot-auto ', out)
|
||||
self.assertIn('Creating virtual environment...', out)
|
||||
|
||||
def test_openssl_failure(self):
|
||||
|
|
@ -312,10 +312,10 @@ class AutoTests(TestCase):
|
|||
except CalledProcessError as exc:
|
||||
eq_(exc.returncode, 1)
|
||||
self.assertIn("Couldn't verify signature of downloaded "
|
||||
"letsencrypt-auto.",
|
||||
"certbot-auto.",
|
||||
exc.output)
|
||||
else:
|
||||
self.fail('Signature check on letsencrypt-auto erroneously passed.')
|
||||
self.fail('Signature check on certbot-auto erroneously passed.')
|
||||
|
||||
def test_pip_failure(self):
|
||||
"""Make sure pip stops us if there is a hash mismatch."""
|
||||
|
|
|
|||
|
|
@ -45,7 +45,7 @@ export GPG_TTY=$(tty)
|
|||
PORT=${PORT:-1234}
|
||||
|
||||
# subpackages to be released
|
||||
SUBPKGS=${SUBPKGS:-"acme certbot-apache certbot-nginx letshelp-certbot"}
|
||||
SUBPKGS=${SUBPKGS:-"acme certbot-apache certbot-nginx letshelp-certbot letsencrypt letsencrypt-apache letsencrypt-nginx letshelp-letsencrypt"}
|
||||
subpkgs_modules="$(echo $SUBPKGS | sed s/-/_/g)"
|
||||
# certbot_compatibility_test is not packaged because:
|
||||
# - it is not meant to be used by anyone else than Certbot devs
|
||||
|
|
@ -162,12 +162,12 @@ done
|
|||
deactivate
|
||||
|
||||
# pin pip hashes of the things we just built
|
||||
for pkg in acme certbot certbot-apache ; do
|
||||
for pkg in acme certbot certbot-apache letsencrypt letsencrypt-apache ; do
|
||||
echo $pkg==$version \\
|
||||
pip hash dist."$version/$pkg"/*.{whl,gz} | grep "^--hash" | python2 -c 'from sys import stdin; input = stdin.read(); print " ", input.replace("\n--hash", " \\\n --hash"),'
|
||||
done > /tmp/hashes.$$
|
||||
|
||||
if ! wc -l /tmp/hashes.$$ | grep -qE "^\s*9 " ; then
|
||||
if ! wc -l /tmp/hashes.$$ | grep -qE "^\s*15 " ; then
|
||||
echo Unexpected pip hash output
|
||||
exit 1
|
||||
fi
|
||||
|
|
@ -191,9 +191,10 @@ done
|
|||
gpg -u "$RELEASE_GPG_KEY" --detach-sign --armor --sign letsencrypt-auto-source/letsencrypt-auto
|
||||
|
||||
# copy leauto to the root, overwriting the previous release version
|
||||
cp -p letsencrypt-auto-source/letsencrypt-auto certbot-auto
|
||||
cp -p letsencrypt-auto-source/letsencrypt-auto letsencrypt-auto
|
||||
|
||||
git add letsencrypt-auto letsencrypt-auto-source
|
||||
git add certbot-auto letsencrypt-auto letsencrypt-auto-source
|
||||
git diff --cached
|
||||
git commit --gpg-sign="$RELEASE_GPG_KEY" -m "Release $version"
|
||||
git tag --local-user "$RELEASE_GPG_KEY" --sign --message "Release $version" "$tag"
|
||||
|
|
|
|||
Loading…
Reference in a new issue