Compare commits

...

20 Commits

Author SHA1 Message Date
Brad Warren
bd626da130 symlink test scriptls 2017-07-25 18:02:46 -07:00
Brad Warren
e5f2f14f3d Rename test_dirs to temp_paths for clarity. 2017-07-25 17:46:19 -07:00
Brad Warren
9962005861 Split test into test_phase2_upgrade. 2017-07-25 17:19:25 -07:00
Brad Warren
afc16afa1e Ignore cleanup errors.
This is caused by subdirectories being owned by root.
2017-07-25 17:03:18 -07:00
Brad Warren
f0962035d5 Deduplicate code with test_dirs() 2017-07-25 16:45:48 -07:00
Brad Warren
c9895741b5 Separate venv_dir and le_auto_path 2017-07-25 16:08:22 -07:00
Brad Warren
7dfc58bb14 Preserve PIP_FIND_LINKS 2017-07-25 16:04:18 -07:00
Brad Warren
fb91481100 Consolidate approach to preserving env vars. 2017-07-25 15:40:36 -07:00
Brad Warren
5f28f0c494 fix typo 2017-07-25 15:27:07 -07:00
Brad Warren
e83923554a Preserve select environment variables with sudo. 2017-07-25 14:21:08 -07:00
Brad Warren
a5cce1565f Handle env vars set to the empty string in fetch 2017-07-25 14:12:41 -07:00
Brad Warren
4a3ef4120e Preserve select environment variables with su. 2017-07-24 17:20:32 -07:00
Brad Warren
87846631ec Add list of environment variables to preserve. 2017-07-24 17:09:03 -07:00
Brad Warren
6335841e19 Delete old venv path when it exists.
Also, quote expansion of paths.
2017-07-21 13:00:21 -07:00
Brad Warren
5db9bf62b0 Create symlinks from old default venvs 2017-07-21 12:43:33 -07:00
Brad Warren
90de7b4612 default venv path = /opt/eff.org/certbot/venv 2017-07-21 11:47:05 -07:00
Brad Warren
5ce89e9281 remove $SUDO usage from bootstrappers 2017-07-21 10:41:10 -07:00
Brad Warren
25d1ed104f remove other $SUDO uses from template 2017-07-21 10:38:51 -07:00
Brad Warren
6716210ca5 run all of certbot-auto as root 2017-07-21 10:35:25 -07:00
Brad Warren
930d69c361 Update comment about root usage. 2017-07-21 10:01:08 -07:00
13 changed files with 284 additions and 192 deletions

View File

@@ -23,9 +23,9 @@ 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"
OLD_VENV_PATH="$XDG_DATA_HOME/letsencrypt"
VENV_PATH="/opt/eff.org/certbot/venv"
fi
VENV_BIN="$VENV_PATH/bin"
LE_AUTO_VERSION="0.17.0.dev0"
@@ -49,6 +49,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
@@ -119,15 +120,28 @@ 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 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 set explicitly by the
# user by overriding the environment variable LE_AUTO_SUDO to 'sudo',
# 'su_sudo', or '' as used below.
# Because the parameters in `su -c` has to be a string,
# we need to properly escape it.
# Run the specified command preserving select environment variables. If the
# command starts with `sudo`, the environment is still properly modified.
run_with_env() {
prefix=""
if [ "$1" = "sudo" ]; then
prefix="sudo"
shift 1
fi
$prefix LE_AUTO_DIR_TEMPLATE="$LE_AUTO_DIR_TEMPLATE" LE_AUTO_JSON_URL="$LE_AUTO_JSON_URL" LE_AUTO_PUBLIC_KEY="$LE_AUTO_PUBLIC_KEY" LE_PYTHON="$LE_PYTHON" OLD_VENV_PATH="$OLD_VENV_PATH" PIP_FIND_LINKS="$PIP_FIND_LINKS" VENV_PATH="$VENV_PATH" "$@"
}
# We need to preserve certain environment variables and because the parameters
# in `su -c` has to be a string, we need to properly escape it.
su_sudo() {
args=""
# This `while` loop iterates over all parameters given to this function.
@@ -144,38 +158,43 @@ su_sudo() {
args="$args'$(printf "%s" "$1" | sed -e "s/'/'\"'\"'/g")' "
shift
done
su root -c "$args"
run_with_env 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
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
SUDO_ENV=""
if [ -n "${LE_AUTO_SUDO+x}" ]; then
case "$LE_AUTO_SUDO" in
su_sudo|su)
SUDO=su_sudo
;;
sudo)
SUDO="run_with_env sudo"
;;
'') ;; # 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="run_with_env sudo"
else
say \"sudo\" is not available, will use \"su\" for installation steps...
SUDO=su_sudo
fi
else
SUDO=
fi
fi
$SUDO "$0" --cb-auto-has-root "$@"
exit 0
fi
BootstrapMessage() {
@@ -261,7 +280,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 +330,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 +355,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 \
@@ -380,9 +399,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 +413,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
@@ -412,7 +431,7 @@ BootstrapRpmCommon() {
# 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
if $tool list python >/dev/null 2>&1; then
pkgs="$pkgs
python
python-devel
@@ -430,13 +449,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
@@ -454,7 +473,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 \
@@ -485,7 +504,7 @@ 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"
@@ -493,9 +512,9 @@ BootstrapArchCommon() {
if [ "$missing" ]; then
if [ "$QUIET" = 1]; then
$SUDO pacman -S --needed $missing $noconfirm > /dev/null
pacman -S --needed $missing $noconfirm > /dev/null
else
$SUDO pacman -S --needed $missing $noconfirm
pacman -S --needed $missing $noconfirm
fi
fi
}
@@ -517,13 +536,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
}
@@ -533,7 +552,7 @@ BootstrapFreeBsd() {
QUIET_FLAG="--quiet"
fi
$SUDO pkg install -Ay $QUIET_FLAG \
pkg install -Ay $QUIET_FLAG \
python \
py27-virtualenv \
augeas \
@@ -548,7 +567,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 +587,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
@@ -595,7 +614,7 @@ BootstrapMageiaCommon() {
QUIET_FLAG='--quiet'
fi
if ! $SUDO urpmi --force $QUIET_FLAG \
if ! urpmi --force $QUIET_FLAG \
python \
libpython-devel \
python-virtualenv
@@ -604,7 +623,7 @@ BootstrapMageiaCommon() {
exit 1
fi
if ! $SUDO urpmi --force $QUIET_FLAG \
if ! urpmi --force $QUIET_FLAG \
git \
gcc \
python-augeas \
@@ -1100,20 +1119,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.
@@ -1165,7 +1179,14 @@ from subprocess import check_call, CalledProcessError
from sys import argv, exit
from urllib2 import build_opener, HTTPHandler, HTTPSHandler, HTTPError
PUBLIC_KEY = environ.get('LE_AUTO_PUBLIC_KEY', """-----BEGIN PUBLIC KEY-----
def get_environ(name, default=None):
"""os.environ.get except the default is also used for empty strings."""
value = environ.get(name)
return value if value else default
PUBLIC_KEY = get_environ('LE_AUTO_PUBLIC_KEY', """-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA6MR8W/galdxnpGqBsYbq
OzQb2eyW15YFjDDEMI0ZOzt8f504obNs920lDnpPD2/KqgsfjOgw2K7xWDJIj/18
xUvWPk3LDkrnokNiRkA3KOx3W6fHycKL+zID7zy+xZYBuh2fLyQtWV1VGQ45iNRp
@@ -1213,7 +1234,7 @@ def write(contents, dir, filename):
def latest_stable_version(get):
"""Return the latest stable release of letsencrypt."""
metadata = loads(get(
environ.get('LE_AUTO_JSON_URL',
get_environ('LE_AUTO_JSON_URL',
'https://pypi.python.org/pypi/certbot/json')))
# metadata['info']['version'] actually returns the latest of any kind of
# release release, contrary to https://wiki.python.org/moin/PyPIJSON.
@@ -1231,7 +1252,7 @@ def verified_new_le_auto(get, tag, temp_dir):
with the verification process, raise ExpectedError.
"""
le_auto_dir = environ.get(
le_auto_dir = get_environ(
'LE_AUTO_DIR_TEMPLATE',
'https://raw.githubusercontent.com/certbot/certbot/%s/'
'letsencrypt-auto-source/') % tag
@@ -1289,13 +1310,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"
mv -f "$TEMP_DIR/letsencrypt-auto.permission-clone" "$0"
fi # A newer version is available.
fi # Self-upgrading is allowed.

View File

@@ -23,9 +23,9 @@ 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"
OLD_VENV_PATH="$XDG_DATA_HOME/letsencrypt"
VENV_PATH="/opt/eff.org/certbot/venv"
fi
VENV_BIN="$VENV_PATH/bin"
LE_AUTO_VERSION="{{ LE_AUTO_VERSION }}"
@@ -49,6 +49,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
@@ -119,15 +120,28 @@ 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 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 set explicitly by the
# user by overriding the environment variable LE_AUTO_SUDO to 'sudo',
# 'su_sudo', or '' as used below.
# Because the parameters in `su -c` has to be a string,
# we need to properly escape it.
# Run the specified command preserving select environment variables. If the
# command starts with `sudo`, the environment is still properly modified.
run_with_env() {
prefix=""
if [ "$1" = "sudo" ]; then
prefix="sudo"
shift 1
fi
$prefix LE_AUTO_DIR_TEMPLATE="$LE_AUTO_DIR_TEMPLATE" LE_AUTO_JSON_URL="$LE_AUTO_JSON_URL" LE_AUTO_PUBLIC_KEY="$LE_AUTO_PUBLIC_KEY" LE_PYTHON="$LE_PYTHON" OLD_VENV_PATH="$OLD_VENV_PATH" PIP_FIND_LINKS="$PIP_FIND_LINKS" VENV_PATH="$VENV_PATH" "$@"
}
# We need to preserve certain environment variables and because the parameters
# in `su -c` has to be a string, we need to properly escape it.
su_sudo() {
args=""
# This `while` loop iterates over all parameters given to this function.
@@ -144,38 +158,43 @@ su_sudo() {
args="$args'$(printf "%s" "$1" | sed -e "s/'/'\"'\"'/g")' "
shift
done
su root -c "$args"
run_with_env 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
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
SUDO_ENV=""
if [ -n "${LE_AUTO_SUDO+x}" ]; then
case "$LE_AUTO_SUDO" in
su_sudo|su)
SUDO=su_sudo
;;
sudo)
SUDO="run_with_env sudo"
;;
'') ;; # 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="run_with_env sudo"
else
say \"sudo\" is not available, will use \"su\" for installation steps...
SUDO=su_sudo
fi
else
SUDO=
fi
fi
$SUDO "$0" --cb-auto-has-root "$@"
exit 0
fi
BootstrapMessage() {
@@ -385,20 +404,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.
@@ -445,13 +459,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"
mv -f "$TEMP_DIR/letsencrypt-auto.permission-clone" "$0"
fi # A newer version is available.
fi # Self-upgrading is allowed.

View File

@@ -18,7 +18,7 @@ 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"
@@ -26,9 +26,9 @@ BootstrapArchCommon() {
if [ "$missing" ]; then
if [ "$QUIET" = 1]; then
$SUDO pacman -S --needed $missing $noconfirm > /dev/null
pacman -S --needed $missing $noconfirm > /dev/null
else
$SUDO pacman -S --needed $missing $noconfirm
pacman -S --needed $missing $noconfirm
fi
fi
}

View File

@@ -21,7 +21,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 +71,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 +96,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

@@ -3,7 +3,7 @@ BootstrapFreeBsd() {
QUIET_FLAG="--quiet"
fi
$SUDO pkg install -Ay $QUIET_FLAG \
pkg install -Ay $QUIET_FLAG \
python \
py27-virtualenv \
augeas \

View File

@@ -15,13 +15,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

@@ -6,7 +6,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 +26,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

@@ -3,7 +3,7 @@ BootstrapMageiaCommon() {
QUIET_FLAG='--quiet'
fi
if ! $SUDO urpmi --force $QUIET_FLAG \
if ! urpmi --force $QUIET_FLAG \
python \
libpython-devel \
python-virtualenv
@@ -12,7 +12,7 @@ BootstrapMageiaCommon() {
exit 1
fi
if ! $SUDO urpmi --force $QUIET_FLAG \
if ! urpmi --force $QUIET_FLAG \
git \
gcc \
python-augeas \

View File

@@ -24,9 +24,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 +38,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
@@ -56,7 +56,7 @@ BootstrapRpmCommon() {
# 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
if $tool list python >/dev/null 2>&1; then
pkgs="$pkgs
python
python-devel
@@ -74,13 +74,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

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

@@ -22,7 +22,14 @@ from subprocess import check_call, CalledProcessError
from sys import argv, exit
from urllib2 import build_opener, HTTPHandler, HTTPSHandler, HTTPError
PUBLIC_KEY = environ.get('LE_AUTO_PUBLIC_KEY', """-----BEGIN PUBLIC KEY-----
def get_environ(name, default=None):
"""os.environ.get except the default is also used for empty strings."""
value = environ.get(name)
return value if value else default
PUBLIC_KEY = get_environ('LE_AUTO_PUBLIC_KEY', """-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA6MR8W/galdxnpGqBsYbq
OzQb2eyW15YFjDDEMI0ZOzt8f504obNs920lDnpPD2/KqgsfjOgw2K7xWDJIj/18
xUvWPk3LDkrnokNiRkA3KOx3W6fHycKL+zID7zy+xZYBuh2fLyQtWV1VGQ45iNRp
@@ -70,7 +77,7 @@ def write(contents, dir, filename):
def latest_stable_version(get):
"""Return the latest stable release of letsencrypt."""
metadata = loads(get(
environ.get('LE_AUTO_JSON_URL',
get_environ('LE_AUTO_JSON_URL',
'https://pypi.python.org/pypi/certbot/json')))
# metadata['info']['version'] actually returns the latest of any kind of
# release release, contrary to https://wiki.python.org/moin/PyPIJSON.
@@ -88,7 +95,7 @@ def verified_new_le_auto(get, tag, temp_dir):
with the verification process, raise ExpectedError.
"""
le_auto_dir = environ.get(
le_auto_dir = get_environ(
'LE_AUTO_DIR_TEMPLATE',
'https://raw.githubusercontent.com/certbot/certbot/%s/'
'letsencrypt-auto-source/') % tag

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

@@ -0,0 +1,35 @@
#!/bin/bash -xe
# $OS_TYPE $PUBLIC_IP $PRIVATE_IP $PUBLIC_HOSTNAME $BOULDER_URL
# are dynamically set at execution
cd letsencrypt
if ! command -v git ; then
if [ "$OS_TYPE" = "ubuntu" ] ; then
sudo apt-get update
fi
if ! ( sudo apt-get install -y git || sudo yum install -y git-all || sudo yum install -y git || sudo dnf install -y git ) ; then
echo git installation failed!
exit 1
fi
fi
BRANCH=`git rev-parse --abbrev-ref HEAD`
# 0.4.1 is the oldest version of letsencrypt-auto that can be used because
# it's the first version that both pins package versions and properly supports
# --no-self-upgrade.
git checkout -f v0.4.1
if ! ./letsencrypt-auto -v --debug --version --no-self-upgrade 2>&1 | grep 0.4.1 ; then
echo initial installation appeared to fail
exit 1
fi
git checkout -f "$BRANCH"
letsencrypt-auto-source/letsencrypt-auto -v --debug --version --no-self-upgrade
if [ "$(tools/readlink.py ~/.local/share/letsencrypt)" != "/opt/eff.org/certbot/venv" ]; then
echo symlink from old venv path not properly created!
exit 1
fi
echo upgrade appeared to be successful