mirror of
https://github.com/certbot/certbot.git
synced 2026-01-26 07:41:33 +03:00
* Fix check permissions logic (#7034)
Fixes #7031
I use the same approach than in `CreateVenv()` and `CompareVersions()`: a new bash function `CheckPathPermissions()` is declared an execute a python script passed to the interpreter through stdin.
This allows:
* to not require the temp_dir that holds a temporary script to be executed
* to reduce at the bare minimum the change to make on the order of bash command to execute (including when the temp_dir is created)
* Fix check permissions logic in certbot-auto by making a temp dir useless
* Update CHANGELOG.md
(cherry picked from commit 71b1b8c2d9)
* Fixup changelog.
730 lines
26 KiB
Bash
Executable File
730 lines
26 KiB
Bash
Executable File
#!/bin/sh
|
|
#
|
|
# Download and run the latest release version of the Certbot client.
|
|
#
|
|
# NOTE: THIS SCRIPT IS AUTO-GENERATED AND SELF-UPDATING
|
|
#
|
|
# IF YOU WANT TO EDIT IT LOCALLY, *ALWAYS* RUN YOUR COPY WITH THE
|
|
# "--no-self-upgrade" FLAG
|
|
#
|
|
# IF YOU WANT TO SEND PULL REQUESTS, THE REAL SOURCE FOR THIS FILE IS
|
|
# letsencrypt-auto-source/letsencrypt-auto.template AND
|
|
# letsencrypt-auto-source/pieces/bootstrappers/*
|
|
|
|
set -e # Work even if somebody does "sh thisscript.sh".
|
|
|
|
# Note: you can set XDG_DATA_HOME or VENV_PATH before running this script,
|
|
# if you want to change where the virtual environment will be installed
|
|
|
|
# HOME might not be defined when being run through something like systemd
|
|
if [ -z "$HOME" ]; then
|
|
HOME=~root
|
|
fi
|
|
if [ -z "$XDG_DATA_HOME" ]; then
|
|
XDG_DATA_HOME=~/.local/share
|
|
fi
|
|
if [ -z "$VENV_PATH" ]; then
|
|
# 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]
|
|
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-bootstrap do not install OS dependencies
|
|
--no-permissions-check do not warn about file system permissions
|
|
--no-self-upgrade do not download updates
|
|
--os-packages-only install OS dependencies and exit
|
|
--install-only install certbot, upgrade if needed, and exit
|
|
-v, --verbose provide more output
|
|
-q, --quiet provide only update/error output;
|
|
implies --non-interactive
|
|
|
|
All arguments are accepted and forwarded to the Certbot client when run."
|
|
export CERTBOT_AUTO="$0"
|
|
|
|
for arg in "$@" ; do
|
|
case "$arg" in
|
|
--debug)
|
|
DEBUG=1;;
|
|
--os-packages-only)
|
|
OS_PACKAGES_ONLY=1;;
|
|
--install-only)
|
|
INSTALL_ONLY=1;;
|
|
--no-self-upgrade)
|
|
# Do not upgrade this script (also prevents client upgrades, because each
|
|
# copy of the script pins a hash of the python client)
|
|
NO_SELF_UPGRADE=1;;
|
|
--no-permissions-check)
|
|
NO_PERMISSIONS_CHECK=1;;
|
|
--no-bootstrap)
|
|
NO_BOOTSTRAP=1;;
|
|
--help)
|
|
HELP=1;;
|
|
--noninteractive|--non-interactive)
|
|
NONINTERACTIVE=1;;
|
|
--quiet)
|
|
QUIET=1;;
|
|
renew)
|
|
ASSUME_YES=1;;
|
|
--verbose)
|
|
VERBOSE=1;;
|
|
-[!-]*)
|
|
OPTIND=1
|
|
while getopts ":hnvq" short_arg $arg; do
|
|
case "$short_arg" in
|
|
h)
|
|
HELP=1;;
|
|
n)
|
|
NONINTERACTIVE=1;;
|
|
q)
|
|
QUIET=1;;
|
|
v)
|
|
VERBOSE=1;;
|
|
esac
|
|
done;;
|
|
esac
|
|
done
|
|
|
|
if [ $BASENAME = "letsencrypt-auto" ]; then
|
|
# letsencrypt-auto does not respect --help or --yes for backwards compatibility
|
|
NONINTERACTIVE=1
|
|
HELP=0
|
|
fi
|
|
|
|
# Set ASSUME_YES to 1 if QUIET or NONINTERACTIVE
|
|
if [ "$QUIET" = 1 -o "$NONINTERACTIVE" = 1 ]; then
|
|
ASSUME_YES=1
|
|
fi
|
|
|
|
say() {
|
|
if [ "$QUIET" != 1 ]; then
|
|
echo "$@"
|
|
fi
|
|
}
|
|
|
|
error() {
|
|
echo "$@"
|
|
}
|
|
|
|
# Support for busybox and others where there is no "command",
|
|
# but "which" instead
|
|
if command -v command > /dev/null 2>&1 ; then
|
|
export EXISTS="command -v"
|
|
elif which which > /dev/null 2>&1 ; then
|
|
export EXISTS="which"
|
|
else
|
|
error "Cannot find command nor which... please install one!"
|
|
exit 1
|
|
fi
|
|
|
|
# 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.
|
|
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
|
|
# will be wrapped in a pair of `'`, then appended to `$args` string
|
|
# For example, `echo "It's only 1\$\!"` will be escaped to:
|
|
# 'echo' 'It'"'"'s only 1$!'
|
|
# │ │└┼┘│
|
|
# │ │ │ └── `'s only 1$!'` the literal string
|
|
# │ │ └── `\"'\"` is a single quote (as a string)
|
|
# │ └── `'It'`, to be concatenated with the strings following it
|
|
# └── `echo` wrapped in a pair of `'`, it's totally fine for the shell command itself
|
|
while [ $# -ne 0 ]; do
|
|
args="$args'$(printf "%s" "$1" | sed -e "s/'/'\"'\"'/g")' "
|
|
shift
|
|
done
|
|
su root -c "$args"
|
|
}
|
|
|
|
# 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"
|
|
;;
|
|
'')
|
|
# If we're not running with root, don't check that this script can only
|
|
# be modified by system users and groups.
|
|
NO_PERMISSIONS_CHECK=1
|
|
;;
|
|
*)
|
|
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 -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
|
|
else
|
|
SetRootAuthMechanism
|
|
if [ -n "$SUDO" ]; then
|
|
say "Requesting to rerun $0 with root privileges..."
|
|
$SUDO "$0" --cb-auto-has-root "$@"
|
|
exit 0
|
|
fi
|
|
fi
|
|
|
|
# Runs this script again with the given arguments. --cb-auto-has-root is added
|
|
# to the command line arguments to ensure we don't try to acquire root a
|
|
# second time. After the script is rerun, we exit the current script.
|
|
RerunWithArgs() {
|
|
"$0" --cb-auto-has-root "$@"
|
|
exit 0
|
|
}
|
|
|
|
BootstrapMessage() {
|
|
# Arguments: Platform name
|
|
say "Bootstrapping dependencies for $1... (you can skip this with --no-bootstrap)"
|
|
}
|
|
|
|
ExperimentalBootstrap() {
|
|
# Arguments: Platform name, bootstrap function name
|
|
if [ "$DEBUG" = 1 ]; then
|
|
if [ "$2" != "" ]; then
|
|
BootstrapMessage $1
|
|
$2
|
|
fi
|
|
else
|
|
error "FATAL: $1 support is very experimental at present..."
|
|
error "if you would like to work on improving it, please ensure you have backups"
|
|
error "and then run this script again with the --debug flag!"
|
|
error "Alternatively, you can install OS dependencies yourself and run this script"
|
|
error "again with --no-bootstrap."
|
|
exit 1
|
|
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
|
|
}
|
|
|
|
MIN_PYTHON_VERSION="2.7"
|
|
MIN_PYVER=$(echo "$MIN_PYTHON_VERSION" | sed 's/\.//')
|
|
# Sets LE_PYTHON to Python version string and PYVER to the first two
|
|
# digits of the python version
|
|
DeterminePythonVersion() {
|
|
# Arguments: "NOCRASH" if we shouldn't crash if we don't find a good python
|
|
#
|
|
# If no Python is found, PYVER is set to 0.
|
|
if [ "$USE_PYTHON_3" = 1 ]; then
|
|
for LE_PYTHON in "$LE_PYTHON" python3; do
|
|
# Break (while keeping the LE_PYTHON value) if found.
|
|
$EXISTS "$LE_PYTHON" > /dev/null && break
|
|
done
|
|
else
|
|
for LE_PYTHON in "$LE_PYTHON" python2.7 python27 python2 python; do
|
|
# Break (while keeping the LE_PYTHON value) if found.
|
|
$EXISTS "$LE_PYTHON" > /dev/null && break
|
|
done
|
|
fi
|
|
if [ "$?" != "0" ]; then
|
|
if [ "$1" != "NOCRASH" ]; then
|
|
error "Cannot find any Pythons; please install one!"
|
|
exit 1
|
|
else
|
|
PYVER=0
|
|
return 0
|
|
fi
|
|
fi
|
|
|
|
PYVER=`"$LE_PYTHON" -V 2>&1 | cut -d" " -f 2 | cut -d. -f1,2 | sed 's/\.//'`
|
|
if [ "$PYVER" -lt "$MIN_PYVER" ]; then
|
|
if [ "$1" != "NOCRASH" ]; then
|
|
error "You have an ancient version of Python entombed in your operating system..."
|
|
error "This isn't going to work; you'll need at least version $MIN_PYTHON_VERSION."
|
|
exit 1
|
|
fi
|
|
fi
|
|
}
|
|
|
|
{{ bootstrappers/deb_common.sh }}
|
|
{{ bootstrappers/rpm_common_base.sh }}
|
|
{{ bootstrappers/rpm_common.sh }}
|
|
{{ bootstrappers/rpm_python3.sh }}
|
|
{{ bootstrappers/suse_common.sh }}
|
|
{{ bootstrappers/arch_common.sh }}
|
|
{{ bootstrappers/gentoo_common.sh }}
|
|
{{ bootstrappers/free_bsd.sh }}
|
|
{{ bootstrappers/mac.sh }}
|
|
{{ bootstrappers/smartos.sh }}
|
|
{{ bootstrappers/mageia_common.sh }}
|
|
|
|
# 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 BOOTSTRAP_VERSION is not set.
|
|
if [ -f /etc/debian_version ]; then
|
|
Bootstrap() {
|
|
BootstrapMessage "Debian-based OSes"
|
|
BootstrapDebCommon
|
|
}
|
|
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
|
|
}
|
|
BOOTSTRAP_VERSION="BootstrapMageiaCommon $BOOTSTRAP_MAGEIA_COMMON_VERSION"
|
|
elif [ -f /etc/redhat-release ]; then
|
|
# Run DeterminePythonVersion to decide on the basis of available Python versions
|
|
# whether to use 2.x or 3.x on RedHat-like systems.
|
|
# Then, revert LE_PYTHON to its previous state.
|
|
prev_le_python="$LE_PYTHON"
|
|
unset LE_PYTHON
|
|
DeterminePythonVersion "NOCRASH"
|
|
# Starting to Fedora 29, python2 is on a deprecation path. Let's move to python3 then.
|
|
RPM_DIST_NAME=`(. /etc/os-release 2> /dev/null && echo $ID) || echo "unknown"`
|
|
RPM_DIST_VERSION=0
|
|
if [ "$RPM_DIST_NAME" = "fedora" ]; then
|
|
RPM_DIST_VERSION=`(. /etc/os-release 2> /dev/null && echo $VERSION_ID) || echo "0"`
|
|
fi
|
|
if [ "$RPM_DIST_NAME" = "fedora" -a "$RPM_DIST_VERSION" -ge 29 -o "$PYVER" -eq 26 ]; then
|
|
Bootstrap() {
|
|
BootstrapMessage "RedHat-based OSes that will use Python3"
|
|
BootstrapRpmPython3
|
|
}
|
|
USE_PYTHON_3=1
|
|
BOOTSTRAP_VERSION="BootstrapRpmPython3 $BOOTSTRAP_RPM_PYTHON3_VERSION"
|
|
else
|
|
Bootstrap() {
|
|
BootstrapMessage "RedHat-based OSes"
|
|
BootstrapRpmCommon
|
|
}
|
|
BOOTSTRAP_VERSION="BootstrapRpmCommon $BOOTSTRAP_RPM_COMMON_VERSION"
|
|
fi
|
|
LE_PYTHON="$prev_le_python"
|
|
elif [ -f /etc/os-release ] && `grep -q openSUSE /etc/os-release` ; then
|
|
Bootstrap() {
|
|
BootstrapMessage "openSUSE-based OSes"
|
|
BootstrapSuseCommon
|
|
}
|
|
BOOTSTRAP_VERSION="BootstrapSuseCommon $BOOTSTRAP_SUSE_COMMON_VERSION"
|
|
elif [ -f /etc/arch-release ]; then
|
|
Bootstrap() {
|
|
if [ "$DEBUG" = 1 ]; then
|
|
BootstrapMessage "Archlinux"
|
|
BootstrapArchCommon
|
|
else
|
|
error "Please use pacman to install letsencrypt packages:"
|
|
error "# pacman -S certbot certbot-apache"
|
|
error
|
|
error "If you would like to use the virtualenv way, please run the script again with the"
|
|
error "--debug flag."
|
|
exit 1
|
|
fi
|
|
}
|
|
BOOTSTRAP_VERSION="BootstrapArchCommon $BOOTSTRAP_ARCH_COMMON_VERSION"
|
|
elif [ -f /etc/manjaro-release ]; then
|
|
Bootstrap() {
|
|
ExperimentalBootstrap "Manjaro Linux" BootstrapArchCommon
|
|
}
|
|
BOOTSTRAP_VERSION="BootstrapArchCommon $BOOTSTRAP_ARCH_COMMON_VERSION"
|
|
elif [ -f /etc/gentoo-release ]; then
|
|
Bootstrap() {
|
|
DeprecationBootstrap "Gentoo" BootstrapGentooCommon
|
|
}
|
|
BOOTSTRAP_VERSION="BootstrapGentooCommon $BOOTSTRAP_GENTOO_COMMON_VERSION"
|
|
elif uname | grep -iq FreeBSD ; then
|
|
Bootstrap() {
|
|
DeprecationBootstrap "FreeBSD" BootstrapFreeBsd
|
|
}
|
|
BOOTSTRAP_VERSION="BootstrapFreeBsd $BOOTSTRAP_FREEBSD_VERSION"
|
|
elif uname | grep -iq Darwin ; then
|
|
Bootstrap() {
|
|
DeprecationBootstrap "macOS" BootstrapMac
|
|
}
|
|
BOOTSTRAP_VERSION="BootstrapMac $BOOTSTRAP_MAC_VERSION"
|
|
elif [ -f /etc/issue ] && grep -iq "Amazon Linux" /etc/issue ; then
|
|
Bootstrap() {
|
|
ExperimentalBootstrap "Amazon Linux" BootstrapRpmCommon
|
|
}
|
|
BOOTSTRAP_VERSION="BootstrapRpmCommon $BOOTSTRAP_RPM_COMMON_VERSION"
|
|
elif [ -f /etc/product ] && grep -q "Joyent Instance" /etc/product ; then
|
|
Bootstrap() {
|
|
ExperimentalBootstrap "Joyent SmartOS Zone" BootstrapSmartOS
|
|
}
|
|
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
|
|
|
|
# We handle this case after determining the normal bootstrap version to allow
|
|
# variables like USE_PYTHON_3 to be properly set. As described above, if the
|
|
# Bootstrap function doesn't install any packages, BOOTSTRAP_VERSION should not
|
|
# be set so we unset it here.
|
|
if [ "$NO_BOOTSTRAP" = 1 ]; then
|
|
Bootstrap() {
|
|
:
|
|
}
|
|
unset BOOTSTRAP_VERSION
|
|
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
|
|
}
|
|
|
|
TempDir() {
|
|
mktemp -d 2>/dev/null || mktemp -d -t 'le' # Linux || macOS
|
|
}
|
|
|
|
# Returns 0 if a letsencrypt installation exists at $OLD_VENV_PATH, otherwise,
|
|
# returns a non-zero number.
|
|
OldVenvExists() {
|
|
[ -n "$OLD_VENV_PATH" -a -f "$OLD_VENV_PATH/bin/letsencrypt" ]
|
|
}
|
|
|
|
# Given python path, version 1 and version 2, check if version 1 is outdated compared to version 2.
|
|
# An unofficial version provided as version 1 (eg. 0.28.0.dev0) will be treated
|
|
# specifically by printing "UNOFFICIAL". Otherwise, print "OUTDATED" if version 1
|
|
# is outdated, and "UP_TO_DATE" if not.
|
|
# This function relies only on installed python environment (2.x or 3.x) by certbot-auto.
|
|
CompareVersions() {
|
|
"$1" - "$2" "$3" << "UNLIKELY_EOF"
|
|
import sys
|
|
from distutils.version import StrictVersion
|
|
|
|
try:
|
|
current = StrictVersion(sys.argv[1])
|
|
except ValueError:
|
|
sys.stdout.write('UNOFFICIAL')
|
|
sys.exit()
|
|
|
|
try:
|
|
remote = StrictVersion(sys.argv[2])
|
|
except ValueError:
|
|
sys.stdout.write('UP_TO_DATE')
|
|
sys.exit()
|
|
|
|
if current < remote:
|
|
sys.stdout.write('OUTDATED')
|
|
else:
|
|
sys.stdout.write('UP_TO_DATE')
|
|
UNLIKELY_EOF
|
|
}
|
|
|
|
# Create a new virtual environment for Certbot. It will overwrite any existing one.
|
|
# Parameters: LE_PYTHON, VENV_PATH, PYVER, VERBOSE
|
|
CreateVenv() {
|
|
"$1" - "$2" "$3" "$4" << "UNLIKELY_EOF"
|
|
{{ create_venv.py }}
|
|
UNLIKELY_EOF
|
|
}
|
|
|
|
# Check that the given PATH_TO_CHECK has secured permissions.
|
|
# Parameters: LE_PYTHON, PATH_TO_CHECK
|
|
CheckPathPermissions() {
|
|
"$1" - "$2" << "UNLIKELY_EOF"
|
|
{{ check_permissions.py }}
|
|
UNLIKELY_EOF
|
|
}
|
|
|
|
if [ "$1" = "--le-auto-phase2" ]; then
|
|
# Phase 2: Create venv, install LE, and run.
|
|
|
|
shift 1 # the --le-auto-phase2 arg
|
|
SetPrevBootstrapVersion
|
|
|
|
if [ -z "$PHASE_1_VERSION" -a "$USE_PYTHON_3" = 1 ]; then
|
|
unset LE_PYTHON
|
|
fi
|
|
|
|
INSTALLED_VERSION="none"
|
|
if [ -d "$VENV_PATH" ] || OldVenvExists; 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
|
|
if [ -d "$VENV_PATH" ]; then
|
|
rm -rf "$VENV_PATH"
|
|
fi
|
|
# In the case the old venv was just a symlink to the new one,
|
|
# OldVenvExists is now false because we deleted the venv at VENV_PATH.
|
|
if OldVenvExists; then
|
|
rm -rf "$OLD_VENV_PATH"
|
|
ln -s "$VENV_PATH" "$OLD_VENV_PATH"
|
|
fi
|
|
RerunWithArgs "$@"
|
|
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"
|
|
# Continue to use OLD_VENV_PATH if the new venv doesn't exist
|
|
if [ ! -d "$VENV_PATH" ]; then
|
|
VENV_BIN="$OLD_VENV_PATH/bin"
|
|
fi
|
|
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
|
|
fi
|
|
|
|
if [ "$LE_AUTO_VERSION" != "$INSTALLED_VERSION" ]; then
|
|
say "Creating virtual environment..."
|
|
DeterminePythonVersion
|
|
CreateVenv "$LE_PYTHON" "$VENV_PATH" "$PYVER" "$VERBOSE"
|
|
|
|
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
|
|
# There is no $ interpolation due to quotes on starting heredoc delimiter.
|
|
# -------------------------------------------------------------------------
|
|
cat << "UNLIKELY_EOF" > "$TEMP_DIR/letsencrypt-auto-requirements.txt"
|
|
{{ dependency-requirements.txt }}
|
|
{{ letsencrypt-requirements.txt }}
|
|
{{ certbot-requirements.txt }}
|
|
UNLIKELY_EOF
|
|
# -------------------------------------------------------------------------
|
|
cat << "UNLIKELY_EOF" > "$TEMP_DIR/pipstrap.py"
|
|
{{ pipstrap.py }}
|
|
UNLIKELY_EOF
|
|
# -------------------------------------------------------------------------
|
|
# Set PATH so pipstrap upgrades the right (v)env:
|
|
PATH="$VENV_BIN:$PATH" "$VENV_BIN/python" "$TEMP_DIR/pipstrap.py"
|
|
set +e
|
|
if [ "$VERBOSE" = 1 ]; then
|
|
"$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 --disable-pip-version-check --no-cache-dir --require-hashes -r "$TEMP_DIR/letsencrypt-auto-requirements.txt" 2>&1`
|
|
fi
|
|
PIP_STATUS=$?
|
|
set -e
|
|
if [ "$PIP_STATUS" != 0 ]; then
|
|
# Report error. (Otherwise, be quiet.)
|
|
error "Had a problem while installing Python packages."
|
|
if [ "$VERBOSE" != 1 ]; then
|
|
error
|
|
error "pip prints the following errors: "
|
|
error "====================================================="
|
|
error "$PIP_OUT"
|
|
error "====================================================="
|
|
error
|
|
error "Certbot has problem setting up the virtual environment."
|
|
|
|
if `echo $PIP_OUT | grep -q Killed` || `echo $PIP_OUT | grep -q "allocate memory"` ; then
|
|
error
|
|
error "Based on your pip output, the problem can likely be fixed by "
|
|
error "increasing the available memory."
|
|
else
|
|
error
|
|
error "We were not be able to guess the right solution from your pip "
|
|
error "output."
|
|
fi
|
|
|
|
error
|
|
error "Consult https://certbot.eff.org/docs/install.html#problems-with-python-virtual-environment"
|
|
error "for possible solutions."
|
|
error "You may also find some support resources at https://certbot.eff.org/support/ ."
|
|
fi
|
|
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 [ "$INSTALL_ONLY" = 1 ]; then
|
|
say "Certbot is installed."
|
|
exit 0
|
|
fi
|
|
|
|
"$VENV_BIN/letsencrypt" "$@"
|
|
|
|
else
|
|
# Phase 1: Upgrade certbot-auto if necessary, 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
|
|
# certbot-auto (which is always the same as that of the certbot
|
|
# package). Phase 2 checks the version of the locally installed certbot.
|
|
export PHASE_1_VERSION="$LE_AUTO_VERSION"
|
|
|
|
if [ ! -f "$VENV_BIN/letsencrypt" ]; then
|
|
if ! OldVenvExists; then
|
|
if [ "$HELP" = 1 ]; then
|
|
echo "$USAGE"
|
|
exit 0
|
|
fi
|
|
# If it looks like we've never bootstrapped before, bootstrap:
|
|
Bootstrap
|
|
fi
|
|
fi
|
|
if [ "$OS_PACKAGES_ONLY" = 1 ]; then
|
|
say "OS packages installed."
|
|
exit 0
|
|
fi
|
|
|
|
DeterminePythonVersion "NOCRASH"
|
|
# Don't warn about file permissions if the user disabled the check or we
|
|
# can't find an up-to-date Python.
|
|
if [ "$PYVER" -ge "$MIN_PYVER" -a "$NO_PERMISSIONS_CHECK" != 1 ]; then
|
|
# If the script fails for some reason, don't break certbot-auto.
|
|
set +e
|
|
# Suppress unexpected error output.
|
|
CHECK_PERM_OUT=$(CheckPathPermissions "$LE_PYTHON" "$0" 2>/dev/null)
|
|
CHECK_PERM_STATUS="$?"
|
|
set -e
|
|
# Only print output if the script ran successfully and it actually produced
|
|
# output. The latter check resolves
|
|
# https://github.com/certbot/certbot/issues/7012.
|
|
if [ "$CHECK_PERM_STATUS" = 0 -a -n "$CHECK_PERM_OUT" ]; then
|
|
error "$CHECK_PERM_OUT"
|
|
fi
|
|
fi
|
|
|
|
if [ "$NO_SELF_UPGRADE" != 1 ]; then
|
|
TEMP_DIR=$(TempDir)
|
|
trap 'rm -rf "$TEMP_DIR"' EXIT
|
|
# ---------------------------------------------------------------------------
|
|
cat << "UNLIKELY_EOF" > "$TEMP_DIR/fetch.py"
|
|
{{ fetch.py }}
|
|
UNLIKELY_EOF
|
|
# ---------------------------------------------------------------------------
|
|
if [ "$PYVER" -lt "$MIN_PYVER" ]; then
|
|
error "WARNING: couldn't find Python $MIN_PYTHON_VERSION+ to check for updates."
|
|
elif ! REMOTE_VERSION=`"$LE_PYTHON" "$TEMP_DIR/fetch.py" --latest-version` ; then
|
|
error "WARNING: unable to check for updates."
|
|
fi
|
|
|
|
LE_VERSION_STATE=`CompareVersions "$LE_PYTHON" "$LE_AUTO_VERSION" "$REMOTE_VERSION"`
|
|
if [ "$LE_VERSION_STATE" = "UNOFFICIAL" ]; then
|
|
say "Unofficial certbot-auto version detected, self-upgrade is disabled: $LE_AUTO_VERSION"
|
|
elif [ "$LE_VERSION_STATE" = "OUTDATED" ]; then
|
|
say "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 certbot-auto.
|
|
# TODO: Deal with quotes in pathnames.
|
|
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:
|
|
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 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.
|
|
|
|
RerunWithArgs --le-auto-phase2 "$@"
|
|
fi
|