From d93c9d40ed2f4670fd7d6c79ab23d789ac8a0177 Mon Sep 17 00:00:00 2001 From: Davide Polonio Date: Sun, 29 Nov 2020 12:14:23 +0000 Subject: [PATCH] Upgrade mail server & roundcube --- mail/docker-compose.yml | 4 +- mail/setup.sh | 559 +++++++++++++++++++++------------------- 2 files changed, 297 insertions(+), 266 deletions(-) diff --git a/mail/docker-compose.yml b/mail/docker-compose.yml index 80ae647..9927030 100644 --- a/mail/docker-compose.yml +++ b/mail/docker-compose.yml @@ -1,7 +1,7 @@ version: '3.7' services: mail: - image: tvial/docker-mailserver:release-v7.0.1 + image: tvial/docker-mailserver:release-v7.2.0 hostname: ${HOSTNAME} domainname: ${DOMAINNAME} container_name: ${CONTAINER_NAME} @@ -38,7 +38,7 @@ services: - internal roundcube: - image: roundcube/roundcubemail:1.4.8-apache + image: roundcube/roundcubemail:1.4.9-apache restart: always depends_on: - mail diff --git a/mail/setup.sh b/mail/setup.sh index ac2d7e2..74ce1a7 100755 --- a/mail/setup.sh +++ b/mail/setup.sh @@ -1,77 +1,109 @@ -#!/bin/sh +#! /bin/bash -## -# Wrapper for various setup scripts included in the docker-mailserver -# +# Wrapper for various setup scripts +# included in docker-mailserver -CRI= +SCRIPT='SETUP' -_check_root() { - if [[ $EUID -ne 0 ]]; then - echo "Curently docker-mailserver doesn't support podman's rootless mode, please run this script as root user." - exit 1 +set -euEo pipefail +trap '__log_err ${FUNCNAME[0]:-"?"} ${_:-"?"} ${LINENO:-"?"} ${?:-"?"}' ERR +trap '_unset_vars || :' EXIT + +function __log_err +{ + local FUNC_NAME LINE EXIT_CODE + FUNC_NAME="${1} / ${2}" + LINE="${3}" + EXIT_CODE="${4}" + + printf "\n––– \e[1m\e[31mUNCHECKED ERROR\e[0m\n%s\n%s\n%s\n%s\n\n" \ + " – script = ${SCRIPT,,}.sh" \ + " – function = ${FUNC_NAME}" \ + " – line = ${LINE}" \ + " – exit code = ${EXIT_CODE}" +} + +function _unset_vars +{ + unset CDIR CRI INFO IMAGE_NAME CONTAINER_NAME DEFAULT_CONFIG_PATH + unset USE_CONTAINER WISHED_CONFIG_PATH CONFIG_PATH VOLUME USE_TTY + unset SCRIPT USING_SELINUX +} + +function _get_current_directory +{ + if dirname "$(readlink -f "${0}")" &>/dev/null + then + CDIR="$(cd "$(dirname "$(readlink -f "${0}")")" && pwd)" + elif realpath -e -L "${0}" &>/dev/null + then + CDIR="$(realpath -e -L "${0}")" + CDIR="${CDIR%/setup.sh}" fi } -if [ -z "$CRI" ]; then - if [ ! -z "$(command -v docker)" ]; then - CRI=docker - elif [ ! -z "$(command -v podman)" ]; then - CRI=podman - _check_root - else - echo "No Support Container Runtime Interface Detected." - exit 1 - fi -fi +CDIR="$(pwd)" +_get_current_directory -INFO=$($CRI ps \ - --no-trunc \ - --format "{{.Image}};{{.Names}}" \ - --filter label=org.label-schema.name="docker-mailserver" | \ - tail -1) - -IMAGE_NAME=${INFO%;*} -CONTAINER_NAME=${INFO#*;} -DEFAULT_CONFIG_PATH="$(pwd)/config" +CRI= +INFO= +IMAGE_NAME= +CONTAINER_NAME='mail' +DEFAULT_CONFIG_PATH="${CDIR}/config" USE_CONTAINER=false +WISHED_CONFIG_PATH= +CONFIG_PATH= +VOLUME= +USE_TTY= +USING_SELINUX= -_update_config_path() { - if [ ! -z "$CONTAINER_NAME" ]; then - VOLUME=$(docker inspect $CONTAINER_NAME \ +function _check_root +{ + if [[ ${EUID} -ne 0 ]] + then + echo "Curently docker-mailserver doesn't support podman's rootless mode, please run this script as root user." + return 1 + fi +} + +function _update_config_path +{ + if [[ -n ${CONTAINER_NAME} ]] + then + VOLUME=$(${CRI} inspect "${CONTAINER_NAME}" \ --format="{{range .Mounts}}{{ println .Source .Destination}}{{end}}" | \ grep "/tmp/docker-mailserver$" 2>/dev/null) fi - if [ ! -z "$VOLUME" ]; then - CONFIG_PATH=$(echo $VOLUME | awk '{print $1}') + if [[ -n ${VOLUME} ]] + then + CONFIG_PATH=$(echo "${VOLUME}" | awk '{print $1}') fi } -if [ -z "$IMAGE_NAME" ]; then - if [ "$CRI" = "docker" ]; then - IMAGE_NAME=tvial/docker-mailserver:latest - elif [ "$CRI" = "podman" ]; then - IMAGE_NAME=docker.io/tvial/docker-mailserver:latest - fi -fi - -_inspect() { - if _docker_image_exists "$IMAGE_NAME"; then - echo "Image: $IMAGE_NAME" +function _inspect +{ + if _docker_image_exists "${IMAGE_NAME}" + then + echo "Image: ${IMAGE_NAME}" else - echo "Image: '$IMAGE_NAME' can’t be found." + echo "Image: '${IMAGE_NAME}' can’t be found." fi - if [ -n "$CONTAINER_NAME" ]; then - echo "Container: $CONTAINER_NAME" - echo "Config mount: $CONFIG_PATH" + + if [[ -n ${CONTAINER_NAME} ]] + then + echo "Container: ${CONTAINER_NAME}" + echo "Config mount: ${CONFIG_PATH}" else echo "Container: Not running, please start docker-mailserver." fi } -_usage() { - echo "Usage: $0 [-i IMAGE_NAME] [-c CONTAINER_NAME] [args] +function _usage +{ + echo "${SCRIPT,,}.sh Bootstrapping Script + +Usage: ${0} [-i IMAGE_NAME] [-c CONTAINER_NAME] [args] OPTIONS: @@ -81,266 +113,265 @@ OPTIONS: -c CONTAINER_NAME The name of the running container. - -p PATH config folder path (default: $(pwd)/config) + -p PATH Config folder path (default: ${CDIR}/config) + + -h Show this help dialogue + + -z Allow container access to the bind mount content + that is shared among multiple containers + on a SELinux-enabled host. + + -Z Allow container access to the bind mount content + that is private and unshared with other containers + on a SELinux-enabled host. SUBCOMMANDS: email: - $0 email add [] - $0 email update [] - $0 email del - $0 email restrict [] - $0 email list + ${0} email add [] + ${0} email update [] + ${0} email del + ${0} email restrict [] + ${0} email list alias: - $0 alias add - $0 alias del - $0 alias list + ${0} alias add + ${0} alias del + ${0} alias list quota: - $0 quota set [] - $0 quota del + ${0} quota set [] + ${0} quota del config: - $0 config dkim (default: 2048) - $0 config ssl + ${0} config dkim (default: 2048) + ${0} config ssl relay: - $0 relay add-domain [] - $0 relay add-auth [] - $0 relay exclude-domain + ${0} relay add-domain [] + ${0} relay add-auth [] + ${0} relay exclude-domain debug: - $0 debug fetchmail - $0 debug fail2ban [ ] - $0 debug show-mail-logs - $0 debug inspect - $0 debug login + ${0} debug fetchmail + ${0} debug fail2ban [ ] + ${0} debug show-mail-logs + ${0} debug inspect + ${0} debug login + + help: Show this help dialogue + " - exit 1 } -_docker_image_exists() { - if ${CRI} history -q "$1" >/dev/null 2>&1; then +function _docker_image_exists +{ + if ${CRI} history -q "${1}" >/dev/null 2>&1 + then return 0 else return 1 fi } -if tty -s ; then - USE_TTY="-ti" -fi - -_docker_image() { - if [ "$USE_CONTAINER" = true ]; then - # Reuse existing container specified on command line - ${CRI} exec ${USE_TTY} "$CONTAINER_NAME" "$@" +function _docker_image +{ + if ${USE_CONTAINER} + then + # reuse existing container specified on command line + ${CRI} exec "${USE_TTY}" "${CONTAINER_NAME}" "${@}" else - # Start temporary container with specified image - if ! _docker_image_exists "$IMAGE_NAME"; then - echo "Image '$IMAGE_NAME' not found. Pulling ..." - ${CRI} pull "$IMAGE_NAME" + # start temporary container with specified image + if ! _docker_image_exists "${IMAGE_NAME}" + then + echo "Image '${IMAGE_NAME}' not found. Pulling ..." + ${CRI} pull "${IMAGE_NAME}" fi - ${CRI} run \ - --rm \ - -v "$CONFIG_PATH":/tmp/docker-mailserver \ - ${USE_TTY} "$IMAGE_NAME" $@ + ${CRI} run --rm \ + -v "${CONFIG_PATH}":/tmp/docker-mailserver"${USING_SELINUX}" \ + "${USE_TTY}" "${IMAGE_NAME}" "${@}" fi } -_docker_container() { - if [ -n "$CONTAINER_NAME" ]; then - ${CRI} exec ${USE_TTY} "$CONTAINER_NAME" "$@" +function _docker_container +{ + if [[ -n ${CONTAINER_NAME} ]] + then + ${CRI} exec "${USE_TTY}" "${CONTAINER_NAME}" "${@}" else echo "The docker-mailserver is not running!" - exit 1 + exit 5 fi } -while getopts ":c:i:p:" OPT; do - case $OPT in - c) - CONTAINER_NAME="$OPTARG" - USE_CONTAINER=true # Container specified, connect to running instance - ;; - i) - IMAGE_NAME="$OPTARG" - ;; - p) - case "$OPTARG" in - /*) - WISHED_CONFIG_PATH="$OPTARG" - ;; - *) - WISHED_CONFIG_PATH="$(pwd)/$OPTARG" - ;; - esac - if [ ! -d "$WISHED_CONFIG_PATH" ]; then - echo "Directory doesn't exist" - _usage - exit 1 - fi - ;; - \?) - echo "Invalid option: -$OPTARG" >&2 - ;; - esac -done - -if [ ! -n "$WISHED_CONFIG_PATH" ]; then - # no wished config path - _update_config_path - - if [ ! -n "$CONFIG_PATH" ]; then - CONFIG_PATH=$DEFAULT_CONFIG_PATH +function _main +{ + if [[ -n $(command -v docker) ]] + then + CRI=docker + elif [[ -n $(command -v podman) ]] + then + CRI=podman + _check_root + else + echo "No supported Container Runtime Interface detected." + exit 10 fi -else - CONFIG_PATH=$WISHED_CONFIG_PATH -fi -shift $((OPTIND-1)) + INFO=$(${CRI} ps \ + --no-trunc \ + --format "{{.Image}};{{.Names}}" \ + --filter label=org.label-schema.name="docker-mailserver" | \ + tail -1) -case $1 in + IMAGE_NAME=${INFO%;*} + CONTAINER_NAME=${INFO#*;} - email) - shift - case $1 in - add) - shift - _docker_image addmailuser $@ + if [[ -z ${IMAGE_NAME} ]] + then + if [[ ${CRI} == "docker" ]] + then + IMAGE_NAME=tvial/docker-mailserver:latest + elif [[ ${CRI} == "podman" ]] + then + IMAGE_NAME=docker.io/tvial/docker-mailserver:latest + fi + fi + + if tty -s + then + USE_TTY="-ti" + fi + + local OPTIND + while getopts ":c:i:p:hzZ" OPT + do + case ${OPT} in + i ) IMAGE_NAME="${OPTARG}" ;; + z ) USING_SELINUX=":z" ;; + Z ) USING_SELINUX=":Z" ;; + c ) + # container specified, connect to running instance + CONTAINER_NAME="${OPTARG}" + USE_CONTAINER=true ;; - update) - shift - _docker_image updatemailuser $@ - ;; - del) - shift - _docker_image delmailuser $@ - ;; - restrict) - shift - _docker_container restrict-access $@ - ;; - list) - _docker_image listmailuser - ;; - *) + + h ) _usage + return ;; - esac - ;; - alias) - shift - case $1 in - add) - shift - _docker_image addalias $@ - ;; - del) - shift - _docker_image delalias $@ - ;; - list) - shift - _docker_image listalias $@ - ;; - *) + p ) + case "${OPTARG}" in + /* ) WISHED_CONFIG_PATH="${OPTARG}" ;; + * ) WISHED_CONFIG_PATH="${CDIR}/${OPTARG}" ;; + esac + + if [[ ! -d ${WISHED_CONFIG_PATH} ]] + then + echo "Directory doesn't exist" _usage - ;; - esac - ;; - - quota) - shift - case $1 in - set) - shift - _docker_image setquota $@ - ;; - del) - shift - _docker_image delquota $@ - ;; - *) - _usage - ;; - esac - ;; - - config) - shift - case $1 in - dkim) - _docker_image generate-dkim-config $2 - ;; - ssl) - _docker_image generate-ssl-certificate "$2" - ;; - *) - _usage - ;; - esac - ;; - - relay) - shift - case $1 in - add-domain) - shift - _docker_image addrelayhost $@ - ;; - add-auth) - shift - _docker_image addsaslpassword $@ - ;; - exclude-domain) - shift - _docker_image excluderelaydomain $@ - ;; - *) - _usage - ;; - esac - ;; - - debug) - shift - case $1 in - fetchmail) - _docker_image debug-fetchmail - ;; - fail2ban) - shift - _docker_container fail2ban $@ - ;; - show-mail-logs) - _docker_container cat /var/log/mail/mail.log - ;; - inspect) - _inspect - ;; - login) - shift - if [ -z "$1" ]; then - _docker_container /bin/bash - else - _docker_container /bin/bash -c "$@" + exit 40 fi ;; - *) - _usage - ;; - esac - ;; - *) - _usage - ;; -esac + * ) + echo "Invalid option: -${OPTARG}" >&2 + ;; + + esac + done + + shift $(( OPTIND - 1 )) + + if [[ -z ${WISHED_CONFIG_PATH} ]] + then + # no wished config path + _update_config_path + + if [[ -z ${CONFIG_PATH} ]] + then + CONFIG_PATH=${DEFAULT_CONFIG_PATH} + fi + else + CONFIG_PATH=${WISHED_CONFIG_PATH} + fi + + + case ${1:-} in + + email) + shift ; case ${1:-} in + add ) shift ; _docker_image addmailuser "${@}" ;; + update ) shift ; _docker_image updatemailuser "${@}" ;; + del ) shift ; _docker_image delmailuser "${@}" ;; + restrict ) shift ; _docker_container restrict-access "${@}" ;; + list ) _docker_image listmailuser ;; + * ) _usage ;; + esac + ;; + + alias) + shift ; case ${1:-} in + add ) shift ; _docker_image addalias "${1}" "${2}" ;; + del ) shift ; _docker_image delalias "${1}" "${2}" ;; + list ) shift ; _docker_image listalias ;; + * ) _usage ;; + esac + ;; + + quota) + shift ; case ${1:-} in + set ) shift ; _docker_image setquota "${@}" ;; + del ) shift ; _docker_image delquota "${@}" ;; + * ) _usage ;; + esac + ;; + + config) + shift ; case ${1:-} in + dkim ) _docker_image generate-dkim-config "${2:-2048}" ;; + ssl ) _docker_image generate-ssl-certificate "${2}" ;; + * ) _usage ;; + esac + ;; + + relay) + shift ; case ${1:-} in + add-domain ) shift ; _docker_image addrelayhost "${@}" ;; + add-auth ) shift ; _docker_image addsaslpassword "${@}" ;; + exclude-domain ) shift ; _docker_image excluderelaydomain "${@}" ;; + * ) _usage ;; + esac + ;; + + debug) + shift ; case ${1:-} in + fetchmail ) _docker_image debug-fetchmail ;; + fail2ban ) shift ; _docker_container fail2ban "${@}" ;; + show-mail-logs ) _docker_container cat /var/log/mail/mail.log ;; + inspect ) _inspect ;; + login ) + shift + if [[ -z ${1:-''} ]] + then + _docker_container /bin/bash + else + _docker_container /bin/bash -c "${@}" + fi + ;; + * ) _usage ; exit 1 ;; + esac + ;; + + help ) _usage ;; + * ) _usage ; exit 1 ;; + esac +} + +_main "${@}"