diff options
Diffstat (limited to 'test')
-rwxr-xr-x | test | 260 |
1 files changed, 260 insertions, 0 deletions
@@ -0,0 +1,260 @@ +#!/bin/bash + +#---------------------------------------------------------------------- +# ACME client written with process isolation and minimal privileges in mind +# (test suite) +# Copyright © 2015-2021 Guilhem Moulin <guilhem@fripost.org> +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <https://www.gnu.org/licenses/>. +#---------------------------------------------------------------------- + +set -ue +PATH="/usr/bin:/bin" +export PATH + +usage() { + local rv="${1-0}" + echo "Usage: $0 [--deb|--dev] [TEST..]" >&2 + exit $rv +} + +# Setup: for any subdomain under $DOMAINNAME, +# http://$subdomain.$DOMAINNAME/.well-known/acme-challenge/$challenge +# must be routed to this machine. +# This can be done with a wildcard DNS record and opening tcp/80 in firewall. +DOMAINNAME="lacme-test.guilhem.org" + +MODE="dev" +DISTRIBUTION="sid" +BUILDDIR="build/test" +while [ $# -gt 0 ]; do + case "$1" in + --deb) MODE="deb"; shift;; + --dev) MODE="dev"; shift;; + --help|-h) usage 0;; + -*) echo "Error: Unknown option $1" >&2; usage 1;; + --) shift; break;; + *) break; + esac +done + +cd "$(dirname -- "$0")" +declare -a TESTS=() +if [ $# -eq 0 ]; then + # always start with registration, the account key might be new + TESTS+=( "register" ) + for t in tests/*; do + if [ "$t" != "tests/register" ] && [ "${t#tests/account-encrypted-}" = "$t" ] && [ -f "$t" ]; then + # skip registration and non-interactive tests + TESTS+=( "${t#tests/}" ) + fi + done +else + for t in "$@"; do + if [ -f "tests/$t" ]; then + TESTS+=( "$t" ) + else + echo "Error: '$1': no such test" >&2 + exit 1 + fi + done +fi + +if [ "$MODE" = "deb" ]; then + DISTRIBUTION="$(dpkg-parsechangelog -S Distribution)" + [ "$DISTRIBUTION" != "UNRELEASED" ] || DISTRIBUTION="sid" + PKG_DESTDIR="${XDG_CACHE_HOME:-"$HOME/.cache"}/build-area" +fi + +ACCOUNT_KEY="$BUILDDIR/account.key" +mkdir -pv -- "$BUILDDIR" +if [ ! -f "$ACCOUNT_KEY" ]; then + # keep the account key (up to `make clean`) to avoid hitting + # rate-liming -- currently 50 registrations per 3h per IP, see + # https://letsencrypt.org/docs/staging-environment/ + echo "Generating account key $ACCOUNT_KEY..." >&2 + openssl genpkey -algorithm RSA -out "$ACCOUNT_KEY" +fi + +ARCH="$(dpkg-architecture -qDEB_BUILD_ARCH)" +CHROOT="" + +cleanup() { + if [ -n "$CHROOT" ]; then + schroot -c "$CHROOT" -e + fi +} +trap cleanup EXIT INT TERM + +run() { + local t="tests/$1" rootdir version sub + if [ ! -f "$t" ]; then + echo "Error: '$1': no such test" >&2 + exit 1 + fi + + # Don't need to rebuild for each test, but editing the code at the + # same time might cause `make install` to rebuild a wrong version + make all -- \ + BUILDDIR="$BUILDDIR" \ + DESTDIR="" \ + exec_prefix="/usr" \ + datadir="/usr/share" \ + runstatedir="/run" \ + lacme_www_user=_lacme-www \ + lacme_www_group=nogroup \ + lacme_client_user=_lacme-client \ + lacme_client_group=nogroup \ + acmeapi_server="https://acme-staging-v02.api.letsencrypt.org/directory" + + CHROOT="$(schroot -c "$DISTRIBUTION-$ARCH-sbuild" -b)" + rootdir="/run/schroot/mount/$CHROOT" + + if [ "$MODE" = "deb" ]; then + version="$(dpkg-parsechangelog -S Version)" + echo "Installing lacme $version into $CHROOT..." >&2 + install -vt "$rootdir/dev/shm" -m0644 -- \ + "$PKG_DESTDIR/lacme_${version}_all.deb" \ + "$PKG_DESTDIR/lacme-accountd_${version}_all.deb" + sudo schroot -d"/" -c "$CHROOT" -r -- \ + env DEBIAN_FRONTEND="noninteractive" apt install -y \ + "/dev/shm/lacme_${version}_all.deb" \ + "/dev/shm/lacme-accountd_${version}_all.deb" + + elif [ "$MODE" = "dev" ]; then + echo "Installing lacme dev into $CHROOT..." >&2 + sudo make install -- \ + BUILDDIR="$BUILDDIR" \ + DESTDIR="$rootdir" \ + exec_prefix="$rootdir/usr" \ + datadir="$rootdir/usr/share" \ + runstatedir="$rootdir/run" + sudo schroot -d"/" -c "$CHROOT" -r -- \ + env DEBIAN_FRONTEND="noninteractive" apt install -y \ + adduser \ + libconfig-tiny-perl \ + libcrypt-openssl-rsa-perl \ + libjson-perl \ + libnet-ssleay-perl \ + libtimedate-perl \ + libwww-perl \ + openssl + sudo schroot -d"/" -c "$CHROOT" -r -- \ + adduser --force-badname --system \ + --home /nonexistent --no-create-home \ + --gecos "lacme www user" \ + --quiet _lacme-www + sudo schroot -d"/" -c "$CHROOT" -r -- \ + adduser --force-badname --system \ + --home /nonexistent --no-create-home \ + --gecos "lacme client user" \ + --quiet _lacme-client + fi + + # set up staging environment, see https://letsencrypt.org/docs/staging-environment/ + sudo install -oroot -groot -m0644 -vt "$rootdir/usr/share/lacme" certs-staging/*.pem + sudo install -oroot -groot -m0644 -vT "$BUILDDIR/certs-staging/ca-certificates.crt" \ + "$rootdir/usr/share/lacme/ca-certificates.crt" + sudo schroot -d"/" -c "$CHROOT" -r -- \ + sed -ri '0,/^#?server\s*=.*/ {s||server = https://acme-staging-v02.api.letsencrypt.org/directory|}' \ + /etc/lacme/lacme.conf + + # install account key and configure lacme accordingly + sudo install -oroot -groot -m0600 -vT -- "$BUILDDIR/account.key" \ + "$rootdir/etc/lacme/account.key" + sudo schroot -d"/" -c "$CHROOT" -r -- \ + sed -ri '0,\|^#?privkey\s*=.*| {s||privkey = file:/etc/lacme/account.key|}' \ + /etc/lacme/lacme-accountd.conf + + # use lacme's internal webserver bound to INADDR_ANY port 80 + sudo schroot -d"/" -c "$CHROOT" -r -- \ + sed -ri 's|^#?listen\s*=.*|listen = 0.0.0.0|' /etc/lacme/lacme.conf + + # use a sample lacme-certs.conf, with a random subdomain so we can + # verify that challenges are answered correctly + sub="$(head -c10 /dev/urandom | base32 -w0)" + sudo tee "$rootdir/etc/lacme/lacme-certs.conf.d/simpletest-rsa.conf" >/dev/null <<- EOF + [simpletest-rsa] + certificate-key = /etc/lacme/simpletest.rsa.key + certificate-chain = /etc/lacme/simpletest.rsa.crt + subject = /CN=${sub,,[A-Z]}.$DOMAINNAME + EOF + sudo schroot -d"/" -c "$CHROOT" -r -- \ + openssl genpkey -algorithm RSA -out /etc/lacme/simpletest.rsa.key + + # copy test wrapper and unit file + local testdir="/dev/shm/lacme.test" + sudo install -oroot -groot -m0700 -d -- "$rootdir$testdir" + sudo install -oroot -groot -m0755 -T -- /dev/stdin "$rootdir$testdir/run" <<-EOF + STDERR="$testdir/stderr" + touch "\$STDERR" + fail() { + set +x + local rv=\$? i + if [ \$rv -eq 0 ]; then rv=1; fi + if [ -s "\$STDERR" ]; then + echo "vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv" + cat <"\$STDERR" >&2 + echo "^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^" + fi + [ \$# -eq 0 ] || echo "Error: \$*" >&2 + exit \$rv + } + grepstderr() { + grep "\$@" <"\$STDERR" || fail + } + ngrepstderr() { + ! grep "\$@" <"\$STDERR" || fail + } + set -x + EOF + sudo tee -a "$rootdir$testdir/run" >/dev/null <"$t" + + sudo schroot -d"/" -c "$CHROOT" -r -- env -i \ + USER="root" \ + HOME="/root" \ + SHELL="/bin/sh" \ + LOGNAME="root" \ + TERM="$TERM" \ + PATH="/usr/sbin:/usr/bin:/sbin:/bin" \ + DOMAINNAME="$DOMAINNAME" \ + sh -ue "$testdir/run" || return $? +} + +RV=0 +declare -a PASSED=() FAILED=() +for t in "${TESTS[@]}"; do + run "$t" && rv=0 || rv=$? + if [ -n "$CHROOT" ]; then + # clean up + schroot -c "$CHROOT" -e + CHROOT="" + fi + if [ $rv -eq 0 ]; then + PASSED+=( "$t" ) + else + FAILED+=( "$t" ) + RV=$rv + break # stop at the first failure + fi +done + +echo >&2 +echo "================================================================================" >&2 + +echo "PASSED: ${PASSED[*]:-"(none)"}" >&2 +if [ ${#FAILED[@]} -gt 0 ]; then + echo "FAILED: ${FAILED[*]}" >&2 +fi +exit $RV |