mirror of
https://github.com/certbot/certbot.git
synced 2026-08-02 00:22:28 +02:00
Split large independent scripts off from the main body of the proof-of-concept script. Integrate the bits of the old le-auto script that are still useful.
This makes the script more readable and easier to work on. We'll stitch it together with a build process. Also, stop passing the sudo command as an arg to the experimental bootstrappers. They will be inlined into the main script and can just reference $SUDO. As a result, stop recommending devs run the scripts manually, instead running le-auto --os-packages-only. This has the nice side effect of making dev documentation simpler. Name the folder "letsencrypt_auto" rather than "letsencrypt-auto" because git yield endless pain when replacing a file with a dir. Perhaps we can change it with impunity in a latter commit.
This commit is contained in:
@@ -20,8 +20,8 @@ deps="
|
|||||||
pkg-config
|
pkg-config
|
||||||
"
|
"
|
||||||
|
|
||||||
missing=$(pacman -T $deps)
|
missing=$("$SUDO" pacman -T $deps)
|
||||||
|
|
||||||
if [ "$missing" ]; then
|
if [ "$missing" ]; then
|
||||||
pacman -S --needed $missing
|
"$SUDO" pacman -S --needed $missing
|
||||||
fi
|
fi
|
||||||
|
|||||||
@@ -12,12 +12,12 @@ PACKAGES="dev-vcs/git
|
|||||||
|
|
||||||
case "$PACKAGE_MANAGER" in
|
case "$PACKAGE_MANAGER" in
|
||||||
(paludis)
|
(paludis)
|
||||||
cave resolve --keep-targets if-possible $PACKAGES -x
|
"$SUDO" cave resolve --keep-targets if-possible $PACKAGES -x
|
||||||
;;
|
;;
|
||||||
(pkgcore)
|
(pkgcore)
|
||||||
pmerge --noreplace $PACKAGES
|
"$SUDO" pmerge --noreplace $PACKAGES
|
||||||
;;
|
;;
|
||||||
(portage|*)
|
(portage|*)
|
||||||
emerge --noreplace $PACKAGES
|
"$SUDO" emerge --noreplace $PACKAGES
|
||||||
;;
|
;;
|
||||||
esac
|
esac
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
#!/bin/sh -xe
|
#!/bin/sh -xe
|
||||||
|
|
||||||
pkg install -Ay \
|
"$SUDO" pkg install -Ay \
|
||||||
git \
|
git \
|
||||||
python \
|
python \
|
||||||
py27-virtualenv \
|
py27-virtualenv \
|
||||||
|
|||||||
+9
-47
@@ -359,75 +359,37 @@ Now run tests inside the Docker image:
|
|||||||
Notes on OS dependencies
|
Notes on OS dependencies
|
||||||
========================
|
========================
|
||||||
|
|
||||||
OS level dependencies are managed by scripts in ``bootstrap``. Some notes
|
OS-level dependencies can be installed like so:
|
||||||
are provided here mainly for the :ref:`developers <hacking>` reference.
|
|
||||||
|
|
||||||
In general:
|
.. code-block:: shell
|
||||||
|
|
||||||
|
letsencrypt-auto/letsencrypt-auto --os-packages-only
|
||||||
|
|
||||||
|
In general...
|
||||||
|
|
||||||
* ``sudo`` is required as a suggested way of running privileged process
|
* ``sudo`` is required as a suggested way of running privileged process
|
||||||
* `Augeas`_ is required for the Python bindings
|
* `Augeas`_ is required for the Python bindings
|
||||||
* ``virtualenv`` and ``pip`` are used for managing other python library
|
* ``virtualenv`` and ``pip`` are used for managing other python library
|
||||||
dependencies
|
dependencies
|
||||||
|
|
||||||
|
What follow are OS-specific notes for the :ref:`developers <hacking>` reference.
|
||||||
|
|
||||||
.. _Augeas: http://augeas.net/
|
.. _Augeas: http://augeas.net/
|
||||||
.. _Virtualenv: https://virtualenv.pypa.io
|
.. _Virtualenv: https://virtualenv.pypa.io
|
||||||
|
|
||||||
Ubuntu
|
|
||||||
------
|
|
||||||
|
|
||||||
.. code-block:: shell
|
|
||||||
|
|
||||||
sudo ./bootstrap/ubuntu.sh
|
|
||||||
|
|
||||||
|
|
||||||
Debian
|
Debian
|
||||||
------
|
------
|
||||||
|
|
||||||
.. code-block:: shell
|
|
||||||
|
|
||||||
sudo ./bootstrap/debian.sh
|
|
||||||
|
|
||||||
For squeeze you will need to:
|
For squeeze you will need to:
|
||||||
|
|
||||||
- Use ``virtualenv --no-site-packages -p python`` instead of ``-p python2``.
|
- Use ``virtualenv --no-site-packages -p python`` instead of ``-p python2``.
|
||||||
|
|
||||||
|
|
||||||
.. _`#280`: https://github.com/letsencrypt/letsencrypt/issues/280
|
|
||||||
|
|
||||||
|
|
||||||
Mac OSX
|
|
||||||
-------
|
|
||||||
|
|
||||||
.. code-block:: shell
|
|
||||||
|
|
||||||
./bootstrap/mac.sh
|
|
||||||
|
|
||||||
|
|
||||||
Fedora
|
|
||||||
------
|
|
||||||
|
|
||||||
.. code-block:: shell
|
|
||||||
|
|
||||||
sudo ./bootstrap/fedora.sh
|
|
||||||
|
|
||||||
|
|
||||||
Centos 7
|
|
||||||
--------
|
|
||||||
|
|
||||||
.. code-block:: shell
|
|
||||||
|
|
||||||
sudo ./bootstrap/centos.sh
|
|
||||||
|
|
||||||
|
|
||||||
FreeBSD
|
FreeBSD
|
||||||
-------
|
-------
|
||||||
|
|
||||||
.. code-block:: shell
|
Package installation for FreeBSD uses ``pkg``, not ports.
|
||||||
|
|
||||||
sudo ./bootstrap/freebsd.sh
|
|
||||||
|
|
||||||
Bootstrap script for FreeBSD uses ``pkg`` for package installation,
|
|
||||||
i.e. it does not use ports.
|
|
||||||
|
|
||||||
FreeBSD by default uses ``tcsh``. In order to activate virtualenv (see
|
FreeBSD by default uses ``tcsh``. In order to activate virtualenv (see
|
||||||
below), you will need a compatible shell, e.g. ``pkg install bash &&
|
below), you will need a compatible shell, e.g. ``pkg install bash &&
|
||||||
|
|||||||
@@ -1,12 +1,8 @@
|
|||||||
#!/bin/sh -e
|
#!/bin/sh
|
||||||
#
|
#
|
||||||
# A script to run the latest release version of the Let's Encrypt in a
|
# Download and run the latest release version of the Let's Encrypt client.
|
||||||
# virtual environment
|
|
||||||
#
|
set -e # Work even if somebody does "sh thisscript.sh".
|
||||||
# Installs and updates the letencrypt virtualenv, and runs letsencrypt
|
|
||||||
# using that virtual environment. This allows the client to function decently
|
|
||||||
# without requiring specific versions of its dependencies from the operating
|
|
||||||
# system.
|
|
||||||
|
|
||||||
# Note: you can set XDG_DATA_HOME or VENV_PATH before running this script,
|
# 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
|
# if you want to change where the virtual environment will be installed
|
||||||
@@ -15,6 +11,77 @@ VENV_NAME="letsencrypt"
|
|||||||
VENV_PATH=${VENV_PATH:-"$XDG_DATA_HOME/$VENV_NAME"}
|
VENV_PATH=${VENV_PATH:-"$XDG_DATA_HOME/$VENV_NAME"}
|
||||||
VENV_BIN=${VENV_PATH}/bin
|
VENV_BIN=${VENV_PATH}/bin
|
||||||
|
|
||||||
|
ExperimentalBootstrap() {
|
||||||
|
# Arguments: Platform name, bootstrap function name
|
||||||
|
if [ "$DEBUG" = 1 ] ; then
|
||||||
|
if [ "$2" != "" ] ; then
|
||||||
|
echo "Bootstrapping dependencies via $1..."
|
||||||
|
$2
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
echo "WARNING: $1 support is very experimental at present..."
|
||||||
|
echo "if you would like to work on improving it, please ensure you have backups"
|
||||||
|
echo "and then run this script again with the --debug flag!"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
DeterminePythonVersion() {
|
||||||
|
if command -v python2.7 > /dev/null ; then
|
||||||
|
export LE_PYTHON=${LE_PYTHON:-python2.7}
|
||||||
|
elif command -v python27 > /dev/null ; then
|
||||||
|
export LE_PYTHON=${LE_PYTHON:-python27}
|
||||||
|
elif command -v python2 > /dev/null ; then
|
||||||
|
export LE_PYTHON=${LE_PYTHON:-python2}
|
||||||
|
elif command -v python > /dev/null ; then
|
||||||
|
export LE_PYTHON=${LE_PYTHON:-python}
|
||||||
|
else
|
||||||
|
echo "Cannot find any Pythons... please install one!"
|
||||||
|
fi
|
||||||
|
|
||||||
|
PYVER=`$LE_PYTHON --version 2>&1 | cut -d" " -f 2 | cut -d. -f1,2 | sed 's/\.//'`
|
||||||
|
if [ $PYVER -eq 26 ] ; then
|
||||||
|
ExperimentalBootstrap "Python 2.6"
|
||||||
|
elif [ $PYVER -lt 26 ] ; then
|
||||||
|
echo "You have an ancient version of Python entombed in your operating system..."
|
||||||
|
echo "This isn't going to work."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# Install required OS packages:
|
||||||
|
Bootstrap() {
|
||||||
|
if [ -f /etc/debian_version ] ; then
|
||||||
|
echo "Bootstrapping dependencies for Debian-based OSes..."
|
||||||
|
BootstrapDebCommon
|
||||||
|
elif [ -f /etc/redhat-release ] ; then
|
||||||
|
echo "Bootstrapping dependencies for RedHat-based OSes..."
|
||||||
|
BootstrapRpmCommon
|
||||||
|
elif `grep -q openSUSE /etc/os-release` ; then
|
||||||
|
echo "Bootstrapping dependencies for openSUSE-based OSes..."
|
||||||
|
BootstrapSuseCommon
|
||||||
|
elif [ -f /etc/arch-release ] ; then
|
||||||
|
echo "Bootstrapping dependencies for Archlinux..."
|
||||||
|
BootstrapArchLinux
|
||||||
|
elif [ -f /etc/manjaro-release ] ; then
|
||||||
|
ExperimentalBootstrap "Manjaro Linux" BootstrapManjaro
|
||||||
|
elif [ -f /etc/gentoo-release ] ; then
|
||||||
|
ExperimentalBootstrap "Gentoo" BootstrapGentooCommon
|
||||||
|
elif uname | grep -iq FreeBSD ; then
|
||||||
|
ExperimentalBootstrap "FreeBSD" BootstrapFreeBsd
|
||||||
|
elif uname | grep -iq Darwin ; then
|
||||||
|
ExperimentalBootstrap "Mac OS X" BootstrapMac
|
||||||
|
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
|
||||||
|
echo "You will need to bootstrap, configure virtualenv, and run a peep install manually."
|
||||||
|
echo "Please see https://letsencrypt.readthedocs.org/en/latest/contributing.html#prerequisites"
|
||||||
|
echo "for more info."
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
# This script takes the same arguments as the main letsencrypt program, but it
|
# This script takes the same arguments as the main letsencrypt program, but it
|
||||||
# additionally responds to --verbose (more output) and --debug (allow support
|
# additionally responds to --verbose (more output) and --debug (allow support
|
||||||
# for experimental platforms)
|
# for experimental platforms)
|
||||||
@@ -63,131 +130,81 @@ else
|
|||||||
SUDO=
|
SUDO=
|
||||||
fi
|
fi
|
||||||
|
|
||||||
ExperimentalBootstrap() {
|
if [ "$1" = "--os-packages-only" ]; then
|
||||||
# Arguments: Platform name, boostrap script name, SUDO command (iff needed)
|
Bootstrap
|
||||||
if [ "$DEBUG" = 1 ] ; then
|
elif [ "$1" != "--_skip-to-install" ]; then
|
||||||
if [ "$2" != "" ] ; then
|
echo "Upgrading letsencrypt-auto..."
|
||||||
echo "Bootstrapping dependencies for $1..."
|
|
||||||
if [ "$3" != "" ] ; then
|
if [ ! -f $VENV_BIN/letsencrypt ]; then
|
||||||
"$3" "$BOOTSTRAP/$2"
|
OLD_VERSION="0.0.0" # ($VENV_BIN/letsencrypt --version)
|
||||||
else
|
else
|
||||||
"$BOOTSTRAP/$2"
|
OLD_VERSION="0.0.0"
|
||||||
fi
|
|
||||||
fi
|
|
||||||
else
|
|
||||||
echo "WARNING: $1 support is very experimental at present..."
|
|
||||||
echo "if you would like to work on improving it, please ensure you have backups"
|
|
||||||
echo "and then run this script again with the --debug flag!"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
}
|
|
||||||
|
|
||||||
DeterminePythonVersion() {
|
|
||||||
if command -v python2.7 > /dev/null ; then
|
|
||||||
export LE_PYTHON=${LE_PYTHON:-python2.7}
|
|
||||||
elif command -v python27 > /dev/null ; then
|
|
||||||
export LE_PYTHON=${LE_PYTHON:-python27}
|
|
||||||
elif command -v python2 > /dev/null ; then
|
|
||||||
export LE_PYTHON=${LE_PYTHON:-python2}
|
|
||||||
elif command -v python > /dev/null ; then
|
|
||||||
export LE_PYTHON=${LE_PYTHON:-python}
|
|
||||||
else
|
|
||||||
echo "Cannot find any Pythons... please install one!"
|
|
||||||
fi
|
fi
|
||||||
|
|
||||||
PYVER=`$LE_PYTHON --version 2>&1 | cut -d" " -f 2 | cut -d. -f1,2 | sed 's/\.//'`
|
# TODO: Don't bother upgrading if we're already up to date.
|
||||||
if [ $PYVER -eq 26 ] ; then
|
if [ "$OLD_VERSION" != "1.2.3" ]; then
|
||||||
ExperimentalBootstrap "Python 2.6"
|
Bootstrap
|
||||||
elif [ $PYVER -lt 26 ] ; then
|
|
||||||
echo "You have an ancient version of Python entombed in your operating system..."
|
|
||||||
echo "This isn't going to work."
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
# virtualenv call is not idempotent: it overwrites pip upgraded in
|
|
||||||
# later steps, causing "ImportError: cannot import name unpack_url"
|
|
||||||
if [ ! -d $VENV_PATH ]
|
|
||||||
then
|
|
||||||
BOOTSTRAP=`dirname $0`/bootstrap
|
|
||||||
if [ ! -f $BOOTSTRAP/debian.sh ] ; then
|
|
||||||
echo "Cannot find the letsencrypt bootstrap scripts in $BOOTSTRAP"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [ -f /etc/debian_version ] ; then
|
|
||||||
echo "Bootstrapping dependencies for Debian-based OSes..."
|
|
||||||
$SUDO $BOOTSTRAP/_deb_common.sh
|
|
||||||
elif [ -f /etc/redhat-release ] ; then
|
|
||||||
echo "Bootstrapping dependencies for RedHat-based OSes..."
|
|
||||||
$SUDO $BOOTSTRAP/_rpm_common.sh
|
|
||||||
elif `grep -q openSUSE /etc/os-release` ; then
|
|
||||||
echo "Bootstrapping dependencies for openSUSE-based OSes..."
|
|
||||||
$SUDO $BOOTSTRAP/_suse_common.sh
|
|
||||||
elif [ -f /etc/arch-release ] ; then
|
|
||||||
echo "Bootstrapping dependencies for Archlinux..."
|
|
||||||
$SUDO $BOOTSTRAP/archlinux.sh
|
|
||||||
elif [ -f /etc/manjaro-release ] ; then
|
|
||||||
ExperimentalBootstrap "Manjaro Linux" manjaro.sh "$SUDO"
|
|
||||||
elif [ -f /etc/gentoo-release ] ; then
|
|
||||||
ExperimentalBootstrap "Gentoo" _gentoo_common.sh "$SUDO"
|
|
||||||
elif uname | grep -iq FreeBSD ; then
|
|
||||||
ExperimentalBootstrap "FreeBSD" freebsd.sh "$SUDO"
|
|
||||||
elif uname | grep -iq Darwin ; then
|
|
||||||
ExperimentalBootstrap "Mac OS X" mac.sh
|
|
||||||
elif grep -iq "Amazon Linux" /etc/issue ; then
|
|
||||||
ExperimentalBootstrap "Amazon Linux" _rpm_common.sh
|
|
||||||
else
|
|
||||||
echo "Sorry, I don't know how to bootstrap Let's Encrypt on your operating system!"
|
|
||||||
echo
|
|
||||||
echo "You will need to bootstrap, configure virtualenv, and run a pip install manually"
|
|
||||||
echo "Please see https://letsencrypt.readthedocs.org/en/latest/contributing.html#prerequisites"
|
|
||||||
echo "for more info"
|
|
||||||
fi
|
|
||||||
|
|
||||||
DeterminePythonVersion
|
|
||||||
echo "Creating virtual environment..."
|
echo "Creating virtual environment..."
|
||||||
|
rm -rf "$VENV_PATH"
|
||||||
|
DeterminePythonVersion
|
||||||
if [ "$VERBOSE" = 1 ] ; then
|
if [ "$VERBOSE" = 1 ] ; then
|
||||||
virtualenv --no-site-packages --python $LE_PYTHON $VENV_PATH
|
virtualenv --no-site-packages --python $LE_PYTHON $VENV_PATH
|
||||||
else
|
else
|
||||||
virtualenv --no-site-packages --python $LE_PYTHON $VENV_PATH > /dev/null
|
virtualenv --no-site-packages --python $LE_PYTHON $VENV_PATH > /dev/null
|
||||||
fi
|
fi
|
||||||
else
|
NEXT: Is all this stuff in the right if branch?
|
||||||
DeterminePythonVersion
|
|
||||||
fi
|
|
||||||
|
|
||||||
|
# Now we drop into Python so we don't have to install even more
|
||||||
printf "Updating letsencrypt and virtual environment dependencies..."
|
# dependencies (curl, etc.), for better flow control, and for the
|
||||||
if [ "$VERBOSE" = 1 ] ; then
|
# option of future Windows compatibility.
|
||||||
echo
|
#
|
||||||
$VENV_BIN/pip install -U setuptools
|
# This Python script prints a path to a temp dir
|
||||||
$VENV_BIN/pip install -U pip
|
# containing a new copy of letsencrypt-auto or returns non-zero.
|
||||||
$VENV_BIN/pip install -r py26reqs.txt -U letsencrypt letsencrypt-apache
|
# There is no $ interpolation due to quotes on heredoc delimiters.
|
||||||
# nginx is buggy / disabled for now, but upgrade it if the user has
|
set +e
|
||||||
# installed it manually
|
TEMP_DIR=`$PYTHON - << "UNLIKELY_EOF"
|
||||||
if $VENV_BIN/pip freeze | grep -q letsencrypt-nginx ; then
|
{download_upgrade}
|
||||||
$VENV_BIN/pip install -U letsencrypt letsencrypt-nginx
|
UNLIKELY_EOF`
|
||||||
|
DOWNLOAD_STATUS=$?
|
||||||
|
set -e
|
||||||
|
if [ "$DOWNLOAD_STATUS" = 0 ]; then
|
||||||
|
# Install new copy of letsencrypt-auto. This preserves permissions and
|
||||||
|
# ownership from the old copy.
|
||||||
|
# TODO: Deal with quotes in pathnames.
|
||||||
|
echo "Installing new version of letsencrypt-auto..."
|
||||||
|
echo " " $SUDO cp "$TEMP_DIR/letsencrypt-auto" "$0"
|
||||||
|
$SUDO cp "$TEMP_DIR/letsencrypt-auto" "$0"
|
||||||
|
# TODO: Clean up temp dir safely, even if it has quotes in its path.
|
||||||
|
"$0" --_skip-to-install "$TEMP_DIR" "$@"
|
||||||
|
else
|
||||||
|
# Report error:
|
||||||
|
echo $TEMP_DIR
|
||||||
|
exit 1
|
||||||
fi
|
fi
|
||||||
else
|
fi # should upgrade
|
||||||
$VENV_BIN/pip install -U setuptools > /dev/null
|
else # --_skip-to-install was passed.
|
||||||
printf .
|
# Install Python dependencies with peep, then run letsencrypt.
|
||||||
$VENV_BIN/pip install -U pip > /dev/null
|
echo "Installing Python package dependencies..."
|
||||||
printf .
|
TEMP_DIR="$2"
|
||||||
# nginx is buggy / disabled for now...
|
shift 2
|
||||||
$VENV_BIN/pip install -r py26reqs.txt > /dev/null
|
cat << "UNLIKELY_EOF" > $TEMP_DIR/letsencrypt-auto-requirements.txt
|
||||||
printf .
|
{requirements}
|
||||||
$VENV_BIN/pip install -U letsencrypt > /dev/null
|
UNLIKELY_EOF
|
||||||
printf .
|
|
||||||
$VENV_BIN/pip install -U letsencrypt-apache > /dev/null
|
|
||||||
if $VENV_BIN/pip freeze | grep -q letsencrypt-nginx ; then
|
|
||||||
printf .
|
|
||||||
$VENV_BIN/pip install -U letsencrypt-nginx > /dev/null
|
|
||||||
fi
|
|
||||||
echo
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Explain what's about to happen, for the benefit of those getting sudo
|
cat << "UNLIKELY_EOF" > $TEMP_DIR/peep.py
|
||||||
# password prompts...
|
{peep}
|
||||||
echo "Running with virtualenv:" $SUDO $VENV_BIN/letsencrypt "$@"
|
UNLIKELY_EOF
|
||||||
$SUDO $VENV_BIN/letsencrypt "$@"
|
|
||||||
|
set +e
|
||||||
|
PEEP_OUT=`$PYTHON $TEMP_DIR/peep.py install -r $TEMP_DIR/letsencrypt-auto-requirements.txt`
|
||||||
|
PEEP_STATUS=$?
|
||||||
|
set -e
|
||||||
|
if [ "$PEEP_STATUS" = 0 ]; then
|
||||||
|
echo "Running letsencrypt..."
|
||||||
|
$SUDO $VENV_BIN/letsencrypt "$@"
|
||||||
|
else
|
||||||
|
# Report error:
|
||||||
|
echo $PEEP_OUT
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
fi
|
||||||
@@ -0,0 +1,120 @@
|
|||||||
|
from distutils.version import LooseVersion
|
||||||
|
from json import loads
|
||||||
|
from os import devnull
|
||||||
|
from os.path import join
|
||||||
|
import re
|
||||||
|
from subprocess import check_call, CalledProcessError
|
||||||
|
from sys import exit
|
||||||
|
from tempfile import mkdtemp
|
||||||
|
from urllib2 import build_opener, HTTPHandler, HTTPSHandler, HTTPError
|
||||||
|
|
||||||
|
|
||||||
|
PUBLIC_KEY = """-----BEGIN PUBLIC KEY-----
|
||||||
|
MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAnwHkSuCSy3gIHawaCiIe
|
||||||
|
4ilJ5kfEmSoiu50uiimBhTESq1JG2gVqXVXFxxVgobGhahSF+/iRVp3imrTtGp1B
|
||||||
|
2heoHbELnPTTZ8E36WHKf4gkLEo0y0XgOP3oBJ9IM5q8J68x0U3Q3c+kTxd/sgww
|
||||||
|
s5NVwpjw4aAZhgDPe5u+rvthUYOD1whYUANgYvooCpV4httNv5wuDjo7SG2V797T
|
||||||
|
QTE8aG3AOhWzdsLm6E6Tl2o/dR6XKJi/RMiXIk53SzArimtAJXe/1GyADe1AgIGE
|
||||||
|
33Ja3hU3uu9lvnnkowy1VI0qvAav/mu/APahcWVYkBAvSVAhH3zGNAGZUnP2zfcP
|
||||||
|
rH7OPw/WrxLVGlX4trLnvQr1wzX7aiM2jdikcMiaExrP0JfQXPu00y3c+hjOC5S0
|
||||||
|
+E5P+e+8pqz5iC5mmvEqy2aQJ6pV7dSpYX3mcDs8pCYaVXXtCPXS1noWirCcqCMK
|
||||||
|
EHGGdJCTXXLHaWUaGQ9Gx1An1gU7Ljkkji2Al65ZwYhkFowsLfuniYKuAywRrCNu
|
||||||
|
q958HnzFpZiQZAqZYtOHaiQiaHPs/36ZN0HuOEy0zM9FEHbp4V/DEn4pNCfAmRY5
|
||||||
|
3v+3nIBhgiLdlM7cV9559aDNeutF25n1Uz2kvuSVSS94qTEmlteCPZGBQb9Rr2wn
|
||||||
|
I2OU8tPRzqKdQ6AwS9wvqscCAwEAAQ==
|
||||||
|
-----END PUBLIC KEY-----
|
||||||
|
""" # TODO: Replace with real one.
|
||||||
|
|
||||||
|
|
||||||
|
class ExpectedError(Exception):
|
||||||
|
"""A novice-readable exception that also carries the original exception for
|
||||||
|
debugging"""
|
||||||
|
|
||||||
|
|
||||||
|
class HttpsGetter(object):
|
||||||
|
def __init__(self):
|
||||||
|
"""Build an HTTPS opener."""
|
||||||
|
# Based on pip 1.4.1's URLOpener
|
||||||
|
# This verifies certs on only Python >=2.7.9.
|
||||||
|
self._opener = build_opener(HTTPSHandler())
|
||||||
|
# Strip out HTTPHandler to prevent MITM spoof:
|
||||||
|
for handler in self._opener.handlers:
|
||||||
|
if isinstance(handler, HTTPHandler):
|
||||||
|
self._opener.handlers.remove(handler)
|
||||||
|
|
||||||
|
def get(self, url):
|
||||||
|
"""Return the document contents pointed to by an HTTPS URL.
|
||||||
|
|
||||||
|
If something goes wrong (404, timeout, etc.), raise ExpectedError.
|
||||||
|
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
return self._opener.open(url).read()
|
||||||
|
except (HTTPError, IOError) as exc:
|
||||||
|
raise ExpectedError("Couldn't download %s." % url, exc)
|
||||||
|
|
||||||
|
|
||||||
|
class TempDir(object):
|
||||||
|
def __init__(self):
|
||||||
|
self.path = mkdtemp()
|
||||||
|
|
||||||
|
def write(self, contents, filename):
|
||||||
|
"""Write something to a named file in me."""
|
||||||
|
with open(join(self.path, filename), 'w') as file:
|
||||||
|
file.write(contents)
|
||||||
|
|
||||||
|
|
||||||
|
def latest_stable_version(get, package):
|
||||||
|
"""Apply a fairly safe heuristic to determine the latest stable release of
|
||||||
|
a PyPI package."""
|
||||||
|
metadata = loads(get('https://pypi.python.org/pypi/%s/json' % package))
|
||||||
|
# metadata['info']['version'] actually returns the latest of any kind of
|
||||||
|
# release release, contrary to https://wiki.python.org/moin/PyPIJSON.
|
||||||
|
return str(max(LooseVersion(r) for r
|
||||||
|
in metadata['releases'].iterkeys()
|
||||||
|
if re.match('^[0-9.]+$', r)))
|
||||||
|
|
||||||
|
|
||||||
|
def verified_new_le_auto(get, tag, temp):
|
||||||
|
"""Return the path to a verified, up-to-date letsencrypt-auto script.
|
||||||
|
|
||||||
|
If the download's signature does not verify or something else goes wrong,
|
||||||
|
raise ExpectedError.
|
||||||
|
|
||||||
|
"""
|
||||||
|
le_auto_dir = ('https://raw.githubusercontent.com/letsencrypt/letsencrypt/'
|
||||||
|
'%s/letsencrypt-auto/' % tag)
|
||||||
|
temp.write(get(le_auto_dir + 'letsencrypt-auto'), 'letsencrypt-auto')
|
||||||
|
temp.write(get(le_auto_dir + 'letsencrypt-auto.sig'), 'letsencrypt-auto.sig')
|
||||||
|
temp.write(PUBLIC_KEY, 'public_key.pem')
|
||||||
|
le_auto_path = join(temp.path, 'letsencrypt-auto')
|
||||||
|
try:
|
||||||
|
with open(devnull, 'w') as dev_null:
|
||||||
|
check_call(['openssl', 'dgst', '-sha256', '-verify',
|
||||||
|
join(temp.path, 'public_key.pem'),
|
||||||
|
'-signature',
|
||||||
|
join(temp.path, 'letsencrypt-auto.sig'),
|
||||||
|
le_auto_path],
|
||||||
|
stdout=dev_null,
|
||||||
|
stderr=dev_null)
|
||||||
|
except CalledProcessError as exc:
|
||||||
|
raise ExpectedError("Couldn't verify signature of downloaded "
|
||||||
|
"letsencrypt-auto.", exc)
|
||||||
|
else: # belt & suspenders
|
||||||
|
return le_auto_path
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
get = HttpsGetter().get
|
||||||
|
temp = TempDir()
|
||||||
|
try:
|
||||||
|
stable_tag = 'v' + latest_stable_version(get, 'letsencrypt')
|
||||||
|
print dirname(verified_new_le_auto(get, stable_tag, temp))
|
||||||
|
except ExpectedError as exc:
|
||||||
|
print exc.args[0], exc.args[1]
|
||||||
|
return 1
|
||||||
|
else:
|
||||||
|
return 0
|
||||||
|
|
||||||
|
|
||||||
|
exit(main())
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
# sha256: Jo-gDCfedW1xZj3WH3OkqNhydWm7G0dLLOYCBVOCaHI
|
||||||
|
# sha256: mXhebPcVzc3lne4FpnbpnwSDWnHnztIByjF0AcMiupY
|
||||||
|
certifi==2015.04.28
|
||||||
|
|
||||||
|
# sha256: mrHTE_mbIJ-PcaYp82gzAwyNfHIoLPd1aDS69WfcpmI
|
||||||
|
click==4.0
|
||||||
@@ -1,177 +1,3 @@
|
|||||||
#!/bin/sh
|
|
||||||
set -e # Work even if somebody does "sh thisscript.sh".
|
|
||||||
|
|
||||||
# If not --_skip-to-install:
|
|
||||||
# Bootstrap
|
|
||||||
# TODO: Inline the bootstrap scripts by putting each one into its own function (so they don't leak scope).
|
|
||||||
|
|
||||||
PYTHON=python
|
|
||||||
SUDO=sudo
|
|
||||||
|
|
||||||
if [ "$1" != "--_skip-to-install" ]; then
|
|
||||||
echo "Upgrading letsencrypt-auto..."
|
|
||||||
# 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.
|
|
||||||
#
|
|
||||||
# The following Python script prints a path to a temp dir containing a new
|
|
||||||
# copy of letsencrypt-auto or returns non-zero. There is no $ interpolation
|
|
||||||
# due to quotes on heredoc delimiters.
|
|
||||||
set +e
|
|
||||||
TEMP_DIR=`$PYTHON - << "UNLIKELY_EOF"
|
|
||||||
|
|
||||||
from distutils.version import LooseVersion
|
|
||||||
from json import loads
|
|
||||||
from os import devnull
|
|
||||||
from os.path import join
|
|
||||||
import re
|
|
||||||
from subprocess import check_call, CalledProcessError
|
|
||||||
from sys import exit
|
|
||||||
from tempfile import mkdtemp
|
|
||||||
from urllib2 import build_opener, HTTPHandler, HTTPSHandler, HTTPError
|
|
||||||
|
|
||||||
|
|
||||||
PUBLIC_KEY = """-----BEGIN PUBLIC KEY-----
|
|
||||||
MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAnwHkSuCSy3gIHawaCiIe
|
|
||||||
4ilJ5kfEmSoiu50uiimBhTESq1JG2gVqXVXFxxVgobGhahSF+/iRVp3imrTtGp1B
|
|
||||||
2heoHbELnPTTZ8E36WHKf4gkLEo0y0XgOP3oBJ9IM5q8J68x0U3Q3c+kTxd/sgww
|
|
||||||
s5NVwpjw4aAZhgDPe5u+rvthUYOD1whYUANgYvooCpV4httNv5wuDjo7SG2V797T
|
|
||||||
QTE8aG3AOhWzdsLm6E6Tl2o/dR6XKJi/RMiXIk53SzArimtAJXe/1GyADe1AgIGE
|
|
||||||
33Ja3hU3uu9lvnnkowy1VI0qvAav/mu/APahcWVYkBAvSVAhH3zGNAGZUnP2zfcP
|
|
||||||
rH7OPw/WrxLVGlX4trLnvQr1wzX7aiM2jdikcMiaExrP0JfQXPu00y3c+hjOC5S0
|
|
||||||
+E5P+e+8pqz5iC5mmvEqy2aQJ6pV7dSpYX3mcDs8pCYaVXXtCPXS1noWirCcqCMK
|
|
||||||
EHGGdJCTXXLHaWUaGQ9Gx1An1gU7Ljkkji2Al65ZwYhkFowsLfuniYKuAywRrCNu
|
|
||||||
q958HnzFpZiQZAqZYtOHaiQiaHPs/36ZN0HuOEy0zM9FEHbp4V/DEn4pNCfAmRY5
|
|
||||||
3v+3nIBhgiLdlM7cV9559aDNeutF25n1Uz2kvuSVSS94qTEmlteCPZGBQb9Rr2wn
|
|
||||||
I2OU8tPRzqKdQ6AwS9wvqscCAwEAAQ==
|
|
||||||
-----END PUBLIC KEY-----
|
|
||||||
""" # TODO: Replace with real one.
|
|
||||||
|
|
||||||
|
|
||||||
class ExpectedError(Exception):
|
|
||||||
"""A novice-readable exception that also carries the original exception for
|
|
||||||
debugging"""
|
|
||||||
|
|
||||||
|
|
||||||
class HttpsGetter(object):
|
|
||||||
def __init__(self):
|
|
||||||
"""Build an HTTPS opener."""
|
|
||||||
# Based on pip 1.4.1's URLOpener
|
|
||||||
# This verifies certs on only Python >=2.7.9.
|
|
||||||
self._opener = build_opener(HTTPSHandler())
|
|
||||||
# Strip out HTTPHandler to prevent MITM spoof:
|
|
||||||
for handler in self._opener.handlers:
|
|
||||||
if isinstance(handler, HTTPHandler):
|
|
||||||
self._opener.handlers.remove(handler)
|
|
||||||
|
|
||||||
def get(self, url):
|
|
||||||
"""Return the document contents pointed to by an HTTPS URL.
|
|
||||||
|
|
||||||
If something goes wrong (404, timeout, etc.), raise ExpectedError.
|
|
||||||
|
|
||||||
"""
|
|
||||||
try:
|
|
||||||
return self._opener.open(url).read()
|
|
||||||
except (HTTPError, IOError) as exc:
|
|
||||||
raise ExpectedError("Couldn't download %s." % url, exc)
|
|
||||||
|
|
||||||
|
|
||||||
class TempDir(object):
|
|
||||||
def __init__(self):
|
|
||||||
self.path = mkdtemp()
|
|
||||||
|
|
||||||
def write(self, contents, filename):
|
|
||||||
"""Write something to a named file in me."""
|
|
||||||
with open(join(self.path, filename), 'w') as file:
|
|
||||||
file.write(contents)
|
|
||||||
|
|
||||||
|
|
||||||
def latest_stable_version(get, package):
|
|
||||||
"""Apply a fairly safe heuristic to determine the latest stable release of
|
|
||||||
a PyPI package."""
|
|
||||||
metadata = loads(get('https://pypi.python.org/pypi/%s/json' % package))
|
|
||||||
# metadata['info']['version'] actually returns the latest of any kind of
|
|
||||||
# release release, contrary to https://wiki.python.org/moin/PyPIJSON.
|
|
||||||
return str(max(LooseVersion(r) for r
|
|
||||||
in metadata['releases'].iterkeys()
|
|
||||||
if re.match('^[0-9.]+$', r)))
|
|
||||||
|
|
||||||
|
|
||||||
def verified_new_le_auto(get, tag, temp):
|
|
||||||
"""Return the path to a verified, up-to-date letsencrypt-auto script.
|
|
||||||
|
|
||||||
If the download's signature does not verify or something else goes wrong,
|
|
||||||
raise ExpectedError.
|
|
||||||
|
|
||||||
"""
|
|
||||||
root = ('https://raw.githubusercontent.com/letsencrypt/letsencrypt/%s/' %
|
|
||||||
tag)
|
|
||||||
temp.write(get(root + 'letsencrypt-auto'), 'letsencrypt-auto')
|
|
||||||
temp.write(get(root + 'letsencrypt-auto.sig'), 'letsencrypt-auto.sig')
|
|
||||||
temp.write(PUBLIC_KEY, 'public_key.pem')
|
|
||||||
le_auto_path = join(temp.path, 'letsencrypt-auto')
|
|
||||||
try:
|
|
||||||
with open(devnull, 'w') as dev_null:
|
|
||||||
check_call(['openssl', 'dgst', '-sha256', '-verify',
|
|
||||||
join(temp.path, 'public_key.pem'),
|
|
||||||
'-signature',
|
|
||||||
join(temp.path, 'letsencrypt-auto.sig'),
|
|
||||||
le_auto_path],
|
|
||||||
stdout=dev_null,
|
|
||||||
stderr=dev_null)
|
|
||||||
except CalledProcessError as exc:
|
|
||||||
raise ExpectedError("Couldn't verify signature of downloaded "
|
|
||||||
"letsencrypt-auto.", exc)
|
|
||||||
else: # belt & suspenders
|
|
||||||
return le_auto_path
|
|
||||||
|
|
||||||
|
|
||||||
def main():
|
|
||||||
get = HttpsGetter().get
|
|
||||||
temp = TempDir()
|
|
||||||
try:
|
|
||||||
stable_tag = 'v' + latest_stable_version(get, 'letsencrypt')
|
|
||||||
print dirname(verified_new_le_auto(get, stable_tag, temp))
|
|
||||||
except ExpectedError as exc:
|
|
||||||
print exc.args[0], exc.args[1]
|
|
||||||
return 1
|
|
||||||
else:
|
|
||||||
return 0
|
|
||||||
|
|
||||||
|
|
||||||
exit(main())
|
|
||||||
UNLIKELY_EOF`
|
|
||||||
DOWNLOAD_STATUS=$?
|
|
||||||
set -e
|
|
||||||
if [ "$DOWNLOAD_STATUS" = 0 ]; then
|
|
||||||
# Install new copy of letsencrypt-auto. This preserves permissions and
|
|
||||||
# ownership from the old copy.
|
|
||||||
# TODO: Deal with quotes in pathnames.
|
|
||||||
# TODO: Don't bother upgrading if we're already up to date.
|
|
||||||
echo " " $SUDO cp "$TEMP_DIR/letsencrypt-auto" "$0"
|
|
||||||
$SUDO cp "$TEMP_DIR/letsencrypt-auto" "$0"
|
|
||||||
# TODO: Clean up temp dir safely, even if it has quotes in its path.
|
|
||||||
"$0" --_skip-to-install "$TEMP_DIR" "$@"
|
|
||||||
else
|
|
||||||
# Report error:
|
|
||||||
echo $TEMP_DIR
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
else # --_skip-to-install was passed.
|
|
||||||
# Install Python dependencies with peep.
|
|
||||||
TEMP_DIR="$2"
|
|
||||||
shift 2
|
|
||||||
echo "Installing Python package dependencies..."
|
|
||||||
cat << "UNLIKELY_EOF" > $TEMP_DIR/letsencrypt-auto-requirements.txt
|
|
||||||
# sha256: Jo-gDCfedW1xZj3WH3OkqNhydWm7G0dLLOYCBVOCaHI
|
|
||||||
# sha256: mXhebPcVzc3lne4FpnbpnwSDWnHnztIByjF0AcMiupY
|
|
||||||
certifi==2015.04.28
|
|
||||||
|
|
||||||
# sha256: mrHTE_mbIJ-PcaYp82gzAwyNfHIoLPd1aDS69WfcpmI
|
|
||||||
click==4.0
|
|
||||||
UNLIKELY_EOF
|
|
||||||
|
|
||||||
cat << "UNLIKELY_EOF" > $TEMP_DIR/peep.py
|
|
||||||
#!/usr/bin/env python
|
#!/usr/bin/env python
|
||||||
"""peep ("prudently examine every package") verifies that packages conform to a
|
"""peep ("prudently examine every package") verifies that packages conform to a
|
||||||
trusted, locally stored hash and only then installs them::
|
trusted, locally stored hash and only then installs them::
|
||||||
@@ -1076,17 +902,3 @@ if __name__ == '__main__':
|
|||||||
except Exception:
|
except Exception:
|
||||||
exception_handler(*sys.exc_info())
|
exception_handler(*sys.exc_info())
|
||||||
exit(SOMETHING_WENT_WRONG)
|
exit(SOMETHING_WENT_WRONG)
|
||||||
UNLIKELY_EOF
|
|
||||||
|
|
||||||
set +e
|
|
||||||
PEEP_OUT=`$PYTHON $TEMP_DIR/peep.py install -r $TEMP_DIR/letsencrypt-auto-requirements.txt`
|
|
||||||
PEEP_STATUS=$?
|
|
||||||
set -e
|
|
||||||
if [ "$PEEP_STATUS" = 0 ]; then
|
|
||||||
echo "Running letsencrypt..."
|
|
||||||
else
|
|
||||||
# Report error:
|
|
||||||
echo $PEEP_OUT
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
Reference in New Issue
Block a user