#! /bin/bash # version v0.2.4 stable # executed manually (via Make) # task wrapper for various setup scripts SCRIPT='setup.sh' set -euEo pipefail trap '__log_err ${FUNCNAME[0]:-"?"} ${_:-"?"} ${LINENO:-"?"} ${?:-"?"}' ERR trap '_unset_vars || :' EXIT function __log_err { printf "\n––– \e[1m\e[31mUNCHECKED ERROR\e[0m\n%s\n%s\n%s\n%s\n\n" \ " – script = ${SCRIPT:-${0}}" \ " – function = ${1} / ${2}" \ " – line = ${3}" \ " – exit code = ${4}" >&2 } 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="$(dirname "$(readlink -f "${0}")")" elif realpath -e -L "${0}" &>/dev/null then CDIR="$(realpath -e -L "${0}")" CDIR="${CDIR%/setup.sh}" fi } CDIR="$(pwd)" _get_current_directory 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= 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." exit 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 [[ -n ${VOLUME} ]] then CONFIG_PATH=$(echo "${VOLUME}" | awk '{print $1}') fi } function _inspect { if _docker_image_exists "${IMAGE_NAME}" then echo "Image: ${IMAGE_NAME}" else echo "Image: '${IMAGE_NAME}' can’t be found." fi if [[ -n ${CONTAINER_NAME} ]] then echo "Container: ${CONTAINER_NAME}" echo "Config mount: ${CONFIG_PATH}" else echo "Container: Not running, please start docker-mailserver." fi } function _usage { echo "${SCRIPT:-${0}} Bootstrapping Script Usage: ${0} [-i IMAGE_NAME] [-c CONTAINER_NAME] [args] OPTIONS: -i IMAGE_NAME The name of the docker-mailserver image The default value is 'docker.io/mailserver/docker-maiserver:latest' -c CONTAINER_NAME The name of the running container. -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 alias: ${0} alias add ${0} alias del ${0} alias list quota: ${0} quota set [] ${0} quota del config: ${0} config dkim (default: 2048) ${0} config ssl relay: ${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 help: Show this help dialogue " } function _docker_image_exists { ${CRI} history -q "${1}" &>/dev/null return ${?} } 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}" fi ${CRI} run --rm \ -v "${CONFIG_PATH}":/tmp/docker-mailserver"${USING_SELINUX}" \ "${USE_TTY}" "${IMAGE_NAME}" "${@}" fi } function _docker_container { if [[ -n ${CONTAINER_NAME} ]] then ${CRI} exec "${USE_TTY}" "${CONTAINER_NAME}" "${@}" else echo "The mailserver is not running!" exit 1 fi } function _main { if command -v docker &>/dev/null then CRI=docker elif command -v podman &>/dev/null then CRI=podman _check_root else echo "No supported Container Runtime Interface detected." exit 10 fi INFO=$(${CRI} ps --no-trunc --format "{{.Image}};{{.Names}}" --filter \ label=org.opencontainers.image.title="docker-mailserver" | tail -1) IMAGE_NAME=${INFO%;*} CONTAINER_NAME=${INFO#*;} if [[ -z ${IMAGE_NAME} ]] then IMAGE_NAME=${NAME:-docker.io/mailserver/docker-mailserver:latest} fi if test -t 0 then USE_TTY="-ti" else # GitHub Actions will fail (or really anything else # lacking an interactive tty) if we don't set a # value here; "-t" alone works for these cases. USE_TTY="-t" 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 ;; h ) _usage return ;; 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 exit 40 fi ;; * ) 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 "${@}"