mirror of
https://github.com/mail-in-a-box/mailinabox.git
synced 2025-04-03 00:07:05 +00:00
503 lines
15 KiB
Bash
Executable File
503 lines
15 KiB
Bash
Executable File
#####
|
|
##### This file is part of Mail-in-a-Box-LDAP which is released under the
|
|
##### terms of the GNU Affero General Public License as published by the
|
|
##### Free Software Foundation, either version 3 of the License, or (at
|
|
##### your option) any later version. See file LICENSE or go to
|
|
##### https://github.com/downtownallday/mailinabox-ldap for full license
|
|
##### details.
|
|
#####
|
|
|
|
|
|
#
|
|
# requires:
|
|
#
|
|
# test scripts: [ lib/misc.sh, lib/system.sh, lib/color-output.sh, lib/installed-state.sh ]
|
|
#
|
|
|
|
|
|
die() {
|
|
local msg="$1"
|
|
echo "$msg" 1>&2
|
|
exit 1
|
|
}
|
|
|
|
|
|
wait_for_docker_nextcloud() {
|
|
local container="$1"
|
|
local config_key="$2"
|
|
echo -n "Waiting ..."
|
|
local count=0
|
|
while true; do
|
|
if [ $count -ge 10 ]; then
|
|
echo "FAILED"
|
|
return 1
|
|
fi
|
|
sleep 6
|
|
let count+=1
|
|
if [ $(docker exec "$container" php -n -r "include 'config/config.php'; print \$CONFIG['$config_key']?'true':'false';") == "true" ]; then
|
|
echo "ok"
|
|
break
|
|
fi
|
|
echo -n "${count}..."
|
|
done
|
|
return 0
|
|
}
|
|
|
|
|
|
dump_conf_files() {
|
|
local skip
|
|
if [ $# -eq 0 ]; then
|
|
skip="false"
|
|
else
|
|
skip="true"
|
|
for item; do
|
|
if is_true "$item"; then
|
|
skip="false"
|
|
break
|
|
fi
|
|
done
|
|
fi
|
|
if [ "$skip" == "false" ]; then
|
|
dump_file "/etc/mailinabox.conf"
|
|
dump_file_if_exists "/etc/mailinabox_mods.conf"
|
|
dump_file "/etc/hosts"
|
|
dump_file "/etc/nsswitch.conf"
|
|
dump_file "/etc/resolv.conf"
|
|
dump_file "/etc/nsd/nsd.conf"
|
|
#dump_file "/etc/postfix/main.cf"
|
|
fi
|
|
}
|
|
|
|
|
|
|
|
#
|
|
# Initialize the test system
|
|
# hostname, time, apt update/upgrade, etc
|
|
#
|
|
# Errors are fatal
|
|
#
|
|
init_test_system() {
|
|
H2 "Update /etc/hosts"
|
|
if ! set_system_hostname; then
|
|
dump_file "/etc/hosts"
|
|
die "Could not set hostname"
|
|
fi
|
|
|
|
# update system time
|
|
H2 "Set system time"
|
|
update_system_time || echo "Ignoring error..."
|
|
|
|
# update package lists before installing anything
|
|
H2 "apt-get update"
|
|
wait_for_apt
|
|
exec_no_output apt-get update -qq || die "apt-get update failed!"
|
|
H2 "snap refresh"
|
|
exec_no_output snap refresh || echo "snap refresh failed! ignoring..."
|
|
|
|
# install .emacs file, if available
|
|
if [ -e tests/assets/.emacs -a -d /root ]; then
|
|
cp tests/assets/.emacs /root 1>/dev/null 2>&1
|
|
fi
|
|
|
|
# upgrade packages - if we don't do this and something like bind
|
|
# is upgraded through automatic upgrades (because maybe MiaB was
|
|
# previously installed), it may cause problems with the rest of
|
|
# the setup, such as with name resolution failures
|
|
if is_false "$TRAVIS" && [ "$SKIP_SYSTEM_UPDATE" != "1" ]; then
|
|
H2 "apt-get upgrade"
|
|
wait_for_apt
|
|
cp /var/log/apt/history.log /tmp/history.log \
|
|
|| die "Unable to copy /var/log/apt/history.log to /tmp"
|
|
exec_no_output apt-get upgrade -y --with-new-pkgs \
|
|
|| die "apt-get upgrade failed!"
|
|
diff /tmp/history.log /var/log/apt/history.log \
|
|
| sed 's/^> //' \
|
|
| awk '/^(Upgrade|Install): / { print $0 }'
|
|
rm -f /tmp/history.log
|
|
fi
|
|
|
|
# install avahi if the system dns domain is .local - note that
|
|
# /bin/dnsdomainname returns empty string at this point
|
|
case "$PRIMARY_HOSTNAME" in
|
|
*.local )
|
|
H2 "Install avahi"
|
|
wait_for_apt
|
|
exec_no_output apt-get install -y avahi-daemon \
|
|
|| die "could not install avahi"
|
|
;;
|
|
esac
|
|
}
|
|
|
|
|
|
#
|
|
# Initialize the test system with QA prerequisites
|
|
# Anything needed to use the test runner, speed up the installation,
|
|
# etc
|
|
#
|
|
init_miab_testing() {
|
|
[ -z "$STORAGE_ROOT" ] \
|
|
&& echo "Error: STORAGE_ROOT not set" 1>&2 \
|
|
&& return 1
|
|
|
|
# If EHDD_KEYFILE is set, use encryption-at-rest support. The
|
|
# drive must be created and mounted so that our QA files can be
|
|
# copied there.
|
|
H2 "Encryption-at-rest"
|
|
if [ ! -z "$EHDD_KEYFILE" ]; then
|
|
ehdd/create_hdd.sh ${EHDD_GB} || die "create luks drive failed"
|
|
ehdd/mount.sh || die "unable to mount luks drive"
|
|
else
|
|
echo "Not configured for encryption-at-rest"
|
|
fi
|
|
|
|
H2 "QA prerequisites"
|
|
local rc=0
|
|
|
|
# python3-pip: installed by setup, but we need it now
|
|
# python3-dnspython: is used by the python scripts in 'tests' and is
|
|
# not installed by setup
|
|
# also install 'jq' for json processing
|
|
echo "Install python3-pip, python3-dnspython, jq, git"
|
|
wait_for_apt
|
|
exec_no_output apt-get install -y python3-pip python3-dnspython jq git \
|
|
|| die "Unable to install setup prerequisites !!"
|
|
|
|
# browser-based tests
|
|
echo "Install chromium, selenium"
|
|
exec_no_output snap install chromium \
|
|
|| die "Unable to install chromium!"
|
|
# TODO: selenium 4 (has breaking changes). See: https://www.selenium.dev/documentation/webdriver/getting_started/upgrade_to_selenium_4/
|
|
exec_no_output python3 -m pip install "selenium>=3,<4" --quiet \
|
|
|| die "Selenium install failed!"
|
|
|
|
# tell git our directory is safe (new requirement for git 2.35.2)
|
|
if [ -d .git ]; then
|
|
git config --global --add safe.directory "$(pwd)"
|
|
fi
|
|
|
|
# copy in pre-built MiaB-LDAP ssl files
|
|
# 1. avoid the lengthy generation of DH params
|
|
if ! mkdir -p $STORAGE_ROOT/ssl; then
|
|
echo "Unable to create $STORAGE_ROOT/ssl ($?)"
|
|
rc=1
|
|
fi
|
|
echo "Copy dhparams"
|
|
if ! cp tests/assets/ssl/dh2048.pem $STORAGE_ROOT/ssl; then
|
|
echo "Copy failed ($?)"
|
|
rc=1
|
|
fi
|
|
|
|
# create miab_ldap.conf to specify what the Nextcloud LDAP service
|
|
# account password will be to avoid a random one created by start.sh
|
|
if [ ! -z "$LDAP_NEXTCLOUD_PASSWORD" ]; then
|
|
if ! mkdir -p $STORAGE_ROOT/ldap; then
|
|
echo "Could not create $STORAGE_ROOT/ldap"
|
|
rc=1
|
|
fi
|
|
[ -e $STORAGE_ROOT/ldap/miab_ldap.conf ] && \
|
|
echo "Warning: exists: $STORAGE_ROOT/ldap/miab_ldap.conf" 1>&2
|
|
touch $STORAGE_ROOT/ldap/miab_ldap.conf || rc=1
|
|
if ! grep "^LDAP_NEXTCLOUD_PASSWORD=" $STORAGE_ROOT/ldap/miab_ldap.conf >/dev/null; then
|
|
echo "LDAP_NEXTCLOUD_PASSWORD=\"$LDAP_NEXTCLOUD_PASSWORD\"" >> $STORAGE_ROOT/ldap/miab_ldap.conf
|
|
fi
|
|
fi
|
|
|
|
# process command line args
|
|
while [ $# -gt 0 ]; do
|
|
case "$1" in
|
|
--qa-ca )
|
|
echo "Copy certificate authority"
|
|
shift
|
|
if ! cp tests/assets/ssl/ca_*.pem $STORAGE_ROOT/ssl; then
|
|
echo "Copy failed ($?)"
|
|
rc=1
|
|
fi
|
|
;;
|
|
|
|
--enable-mod=* )
|
|
local mod="$(awk -F= '{print $2}' <<<"$1")"
|
|
shift
|
|
echo "Enabling local mod '$mod'"
|
|
if ! enable_miab_mod "$mod"; then
|
|
echo "Enabling mod '$mod' failed"
|
|
rc=1
|
|
fi
|
|
;;
|
|
|
|
* )
|
|
# ignore unknown option - may be interpreted elsewhere
|
|
shift
|
|
;;
|
|
esac
|
|
done
|
|
|
|
# now that we've copied our files, unmount STORAGE_ROOT if
|
|
# encryption-at-rest was enabled
|
|
ehdd/umount.sh
|
|
|
|
return $rc
|
|
}
|
|
|
|
|
|
enable_miab_mod() {
|
|
local name="${1}.sh"
|
|
if [ ! -e "$LOCAL_MODS_DIR/$name" ]; then
|
|
mkdir -p "$LOCAL_MODS_DIR"
|
|
if ! ln -s "$(pwd)/setup/mods.available/$name" "$LOCAL_MODS_DIR/$name"
|
|
then
|
|
echo "Warning: copying instead of symlinking $LOCAL_MODS_DIR/$name"
|
|
cp "setup/mods.available/$name" "$LOCAL_MODS_DIR/$name"
|
|
fi
|
|
fi
|
|
}
|
|
|
|
disable_miab_mod() {
|
|
local name="${1}.sh"
|
|
rm -f "$LOCAL_MODS_DIR/$name"
|
|
}
|
|
|
|
|
|
tag_from_readme() {
|
|
# extract the recommended TAG from README.md
|
|
# sets a global "TAG"
|
|
local readme="${1:-README.md}"
|
|
TAG="$(grep -F 'git checkout' "$readme" | sed 's/.*\(v[0123456789]*\.[0123456789]*\).*/\1/')"
|
|
[ $? -ne 0 -o -z "$TAG" ] && return 1
|
|
return 0
|
|
}
|
|
|
|
|
|
workaround_dovecot_sieve_bug() {
|
|
# Workaround a bug in dovecot/sieve that causes attempted sieve
|
|
# compilation when a compiled sieve has the same date as the
|
|
# source file. The fialure occurs with miab-installed "spam"
|
|
# sieve, which can't be recompiled due to the read-only /etc
|
|
# filesystem restriction in systemd (ProtectSystem=efull is set,
|
|
# see `systemctl cat dovecot.service`).
|
|
sleep 1
|
|
touch /etc/dovecot/sieve-spam.svbin
|
|
}
|
|
|
|
|
|
say_release_info() {
|
|
H2 "Release info"
|
|
echo "Code version: $(git describe)"
|
|
echo "Migration version (miab): $(cat "$STORAGE_ROOT/mailinabox.version")"
|
|
echo "Migration version (miabldap): $(cat "$STORAGE_ROOT/mailinabox-ldap.version")"
|
|
}
|
|
|
|
clone_repo_and_pushd() {
|
|
local repo=""
|
|
local treeish=""
|
|
local targetdir=""
|
|
for arg; do
|
|
case "$arg" in
|
|
--checkout-repo=* )
|
|
repo=$(awk -F= '{print $2}' <<<"$arg")
|
|
;;
|
|
--checkout-treeish=* | --checkout-tag=* )
|
|
treeish=$(awk -F= '{print $2}' <<<"$arg")
|
|
;;
|
|
--checkout-targetdir=* )
|
|
targetdir=$(awk -F= '{print $2}' <<<"$arg")
|
|
;;
|
|
esac
|
|
done
|
|
|
|
if [ -z "$repo" -o -z "$treeish" -o -z "$targetdir" ]; then
|
|
return 1
|
|
fi
|
|
|
|
H1 "Clone release $treeish from $repo"
|
|
git_clone \
|
|
"$repo" \
|
|
"$treeish" \
|
|
"$targetdir" \
|
|
"keep-existing" \
|
|
|| die "could not clone $repo ($treeish) to $targetdir"
|
|
|
|
pushd "$targetdir" >/dev/null
|
|
return 0
|
|
}
|
|
|
|
|
|
#
|
|
# install mail-in-a-box (upstream)
|
|
#
|
|
upstream_install() {
|
|
local need_pop="no"
|
|
if clone_repo_and_pushd "$@"; then
|
|
need_pop="yes"
|
|
fi
|
|
|
|
H1 "MIAB UPSTEAM INSTALL [$(git describe 2>/dev/null)]"
|
|
|
|
# ensure we're in a MiaB working directory
|
|
if [ -e setup/ldap.sh ]; then
|
|
die "Cannot install: the working directory is MiaB-LDAP!"
|
|
fi
|
|
if [ ! -e setup/start.sh ]; then
|
|
die "Cannot install: the working directory must contain the source"
|
|
fi
|
|
|
|
# Upstream expects a virgin system and we may have preinstalled
|
|
# nsd that was unable to start and is in the failed state
|
|
# (eg. could not bind to ::53 because named is also
|
|
# installed). Using `systemctl reset-failed` won't work becuase
|
|
# upstream's setup/dns.sh script doesn't start nsd - it only
|
|
# installs nsd using apt after creating nsd's configuration.
|
|
if systemctl is-failed --quiet nsd; then
|
|
echo "notice: removing nsd because systemd has it in the failed state"
|
|
exec_no_output apt-get remove -y nsd
|
|
#systemctl reset-failed nsd
|
|
fi
|
|
|
|
if ! setup/start.sh; then
|
|
echo "$F_WARN"
|
|
dump_file /var/log/syslog 100
|
|
echo "$F_RESET"
|
|
die "Upstream setup failed!"
|
|
fi
|
|
|
|
H2 "Post-setup actions"
|
|
workaround_dovecot_sieve_bug
|
|
|
|
# set actual STORAGE_ROOT, STORAGE_USER, PRIVATE_IP, etc
|
|
. /etc/mailinabox.conf || die "Could not source /etc/mailinabox.conf"
|
|
|
|
H2 "miab install success"
|
|
|
|
if [ "$need_pop" = "yes" ]; then
|
|
if [ ! -e tests/lxd ]; then
|
|
# if this is an upstream install, then populate using
|
|
# miabldap's populate scripts (upstream doesn't have any)
|
|
local d
|
|
d=$(pwd)
|
|
popd >/dev/null
|
|
populate_by_cli_argument "$@"
|
|
pushd "$d" >/dev/null
|
|
else
|
|
# otherwise populate using branch-supplied populate scripts
|
|
populate_by_cli_argument "$@"
|
|
fi
|
|
# state capture must be run from the source tree corresponding to the
|
|
# the installed state
|
|
capture_state_by_cli_argument "$@"
|
|
popd >/dev/null
|
|
else
|
|
# populate if specified on command line
|
|
populate_by_cli_argument "$@"
|
|
capture_state_by_cli_argument "$@"
|
|
fi
|
|
}
|
|
|
|
|
|
miab_ldap_install() {
|
|
local need_pop="no"
|
|
if clone_repo_and_pushd "$@"; then
|
|
need_pop="yes"
|
|
fi
|
|
|
|
H1 "MIAB-LDAP INSTALL [$(pwd)] [$(git describe 2>/dev/null)]"
|
|
# ensure we're in a MiaB-LDAP working directory
|
|
if [ ! -e setup/ldap.sh ]; then
|
|
die "Cannot install: the working directory is not MiaB-LDAP!"
|
|
fi
|
|
|
|
# setup/questions.sh installs the email_validator python3 module
|
|
# but only when in interactive mode. make sure it's also installed
|
|
# in non-interactive mode
|
|
if [ ! -z "${NONINTERACTIVE:-}" ]; then
|
|
echo "Install email_validator python3 module"
|
|
wait_for_apt
|
|
exec_no_output apt-get install -y -qq python3-pip \
|
|
|| die "Unable to install pip !"
|
|
exec_no_output pip3 install -q "email_validator>=1.0.0" \
|
|
|| die "Unable to install email_validator !"
|
|
fi
|
|
|
|
H2 "Run mailinabox-ldap setup"
|
|
# if EHDD_KEYFILE is set, use encryption-at-rest support
|
|
if [ ! -z "$EHDD_KEYFILE" ]; then
|
|
ehdd/start-encrypted.sh
|
|
else
|
|
setup/start.sh
|
|
fi
|
|
|
|
if [ $? -ne 0 ]; then
|
|
H1 "OUTPUT OF SELECT FILES"
|
|
dump_file "/var/log/syslog" 100
|
|
dump_conf_files "$TRAVIS"
|
|
H2; H2 "End"; H2
|
|
die "MiaB-LDAP setup failed!"
|
|
fi
|
|
|
|
H2 "Post-setup actions"
|
|
workaround_dovecot_sieve_bug
|
|
|
|
# set actual STORAGE_ROOT, STORAGE_USER, PRIVATE_IP, etc
|
|
. /etc/mailinabox.conf || die "Could not source /etc/mailinabox.conf"
|
|
|
|
# setup changes the hostname so avahi must be restarted
|
|
if systemctl is-active --quiet avahi-daemon; then
|
|
systemctl restart avahi-daemon
|
|
fi
|
|
|
|
H2 "miab-ldap install success"
|
|
|
|
# populate if specified on command line
|
|
populate_by_cli_argument "$@"
|
|
capture_state_by_cli_argument "$@"
|
|
|
|
if [ "$need_pop" = "yes" ]; then
|
|
popd >/dev/null
|
|
fi
|
|
}
|
|
|
|
capture_state_by_cli_argument() {
|
|
# this must be run with the working directory set to the source
|
|
# tree corresponding to the the installed state
|
|
|
|
# ...ignore unknown options they may be interpreted elsewhere
|
|
local state_dir=""
|
|
for arg; do
|
|
case "$arg" in
|
|
--capture-state=* )
|
|
state_dir=$(awk -F= '{print $2}' <<<"$arg")
|
|
;;
|
|
esac
|
|
done
|
|
|
|
if [ ! -z "$state_dir" ]; then
|
|
installed_state_capture "$state_dir"
|
|
fi
|
|
}
|
|
|
|
populate_by_cli_argument() {
|
|
# ...ignore unknown options they may be interpreted elsewhere
|
|
local populate_names=()
|
|
for arg; do
|
|
case "$arg" in
|
|
--populate=* )
|
|
populate_names+=( $(awk -F= '{print $2}' <<<"$arg") )
|
|
;;
|
|
esac
|
|
done
|
|
|
|
if [ ${#populate_names} -gt 0 ]; then
|
|
populate_by_name "${populate_names[@]}"
|
|
fi
|
|
}
|
|
|
|
populate_by_name() {
|
|
local populate_name
|
|
for populate_name; do
|
|
H1 "Populate Mail-in-a-Box ($populate_name)"
|
|
local populate_script="tests/system-setup/populate/${populate_name}-populate.sh"
|
|
if [ ! -e "$populate_script" ]; then
|
|
die "Does not exist: $populate_script"
|
|
fi
|
|
"$populate_script" || die "Failed: $populate_script"
|
|
done
|
|
}
|