server-dotfiles/mail/setup.sh

420 lines
12 KiB
Bash
Raw Normal View History

2020-11-29 13:14:23 +01:00
#! /bin/bash
2020-07-14 11:07:33 +02:00
2021-04-05 15:43:52 +02:00
# version v0.3.0 stable
# executed manually / via Make
2021-01-30 15:49:10 +01:00
# task wrapper for various setup scripts
2020-07-14 11:07:33 +02:00
2021-01-30 15:49:10 +01:00
SCRIPT='setup.sh'
2020-07-14 11:07:33 +02:00
2021-04-05 15:43:52 +02:00
WHITE="\e[37m"
RED="\e[31m"
PURPLE="\e[35m"
YELLOW="\e[93m"
ORANGE="\e[38;5;214m"
CYAN="\e[96m"
BLUE="\e[34m"
LBLUE="\e[94m"
BOLD="\e[1m"
RESET="\e[0m"
2020-11-29 13:14:23 +01:00
set -euEo pipefail
2021-04-05 15:43:52 +02:00
trap '__log_err "${FUNCNAME[0]:-?}" "${BASH_COMMAND:-?}" "${LINENO:-?}" "${?:-?}"' ERR
2020-11-29 13:14:23 +01:00
trap '_unset_vars || :' EXIT
function __log_err
{
2021-04-05 15:43:52 +02:00
printf "\n ${BOLD}${RED}UNCHECKED ERROR${RESET}\n%s\n%s\n%s\n%s\n\n" \
2021-01-30 15:49:10 +01:00
" script = ${SCRIPT:-${0}}" \
" function = ${1} / ${2}" \
" line = ${3}" \
" exit code = ${4}" >&2
2021-04-05 15:43:52 +02:00
printf "Make sure you use a version of this script that matches
the version / tag of docker-mailserver. Please read the
'Get the tools' section in the README on GitHub careful-
ly and use ./setup.sh help and read the VERSION section.\n" >&2
2020-07-14 11:07:33 +02:00
}
2020-11-29 13:14:23 +01:00
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
2021-01-30 15:49:10 +01:00
CDIR="$(dirname "$(readlink -f "${0}")")"
2020-11-29 13:14:23 +01:00
elif realpath -e -L "${0}" &>/dev/null
then
CDIR="$(realpath -e -L "${0}")"
CDIR="${CDIR%/setup.sh}"
2020-07-14 11:07:33 +02:00
fi
2020-11-29 13:14:23 +01:00
}
2020-07-14 11:07:33 +02:00
2020-11-29 13:14:23 +01:00
CDIR="$(pwd)"
_get_current_directory
2020-07-14 11:07:33 +02:00
2020-11-29 13:14:23 +01:00
CRI=
INFO=
IMAGE_NAME=
2021-01-30 15:49:10 +01:00
CONTAINER_NAME=
2020-11-29 13:14:23 +01:00
DEFAULT_CONFIG_PATH="${CDIR}/config"
2020-07-14 11:07:33 +02:00
USE_CONTAINER=false
2020-11-29 13:14:23 +01:00
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."
2021-01-30 15:49:10 +01:00
exit 1
2020-11-29 13:14:23 +01:00
fi
}
2020-07-14 11:07:33 +02:00
2020-11-29 13:14:23 +01:00
function _update_config_path
{
if [[ -n ${CONTAINER_NAME} ]]
then
VOLUME=$(${CRI} inspect "${CONTAINER_NAME}" \
2020-07-14 11:07:33 +02:00
--format="{{range .Mounts}}{{ println .Source .Destination}}{{end}}" | \
grep "/tmp/docker-mailserver$" 2>/dev/null)
fi
2020-11-29 13:14:23 +01:00
if [[ -n ${VOLUME} ]]
then
CONFIG_PATH=$(echo "${VOLUME}" | awk '{print $1}')
2020-07-14 11:07:33 +02:00
fi
}
2020-11-29 13:14:23 +01:00
function _inspect
{
if _docker_image_exists "${IMAGE_NAME}"
then
echo "Image: ${IMAGE_NAME}"
2020-07-14 11:07:33 +02:00
else
2020-11-29 13:14:23 +01:00
echo "Image: '${IMAGE_NAME}' cant be found."
2020-07-14 11:07:33 +02:00
fi
2020-11-29 13:14:23 +01:00
if [[ -n ${CONTAINER_NAME} ]]
then
echo "Container: ${CONTAINER_NAME}"
echo "Config mount: ${CONFIG_PATH}"
2020-07-14 11:07:33 +02:00
else
echo "Container: Not running, please start docker-mailserver."
fi
}
2020-11-29 13:14:23 +01:00
function _usage
{
2021-04-05 15:43:52 +02:00
# shellcheck disable=SC2059
printf "${PURPLE}SETUP${RED}(${YELLOW}1${RED})
${ORANGE}NAME${RESET}
${SCRIPT:-${0}} - docker-mailserver administration script
${ORANGE}SYNOPSIS${RESET}
./${SCRIPT:-${0}} [ OPTIONS${RED}...${RESET} ] COMMAND [ help ${RED}|${RESET} ARGUMENTS${RED}...${RESET} ]
COMMAND ${RED}:=${RESET} { email ${RED}|${RESET} alias ${RED}|${RESET} quota ${RED}|${RESET} config ${RED}|${RESET} relay ${RED}|${RESET} debug } SUBCOMMAND
${ORANGE}DESCRIPTION${RESET}
This is the main administration script that you use for all interactions with your
mail server. Setup, configuration and much more is done with this script.
Please note that the script executes most of the commands inside the container itself.
If the image was not found, this script will pull the ${WHITE}:latest${RESET} tag of
${WHITE}mailserver/docker-mailserver${RESET}. This tag refers to the latest release,
see the tagging convention in the README under
${BLUE}https://github.com/docker-mailserver/docker-mailserver/blob/master/README.md${RESET}
You will be able to see detailed information about the script you're invoking and
its arguments by appending ${WHITE}help${RESET} after your command. Currently, this
does not work with all scripts.
${ORANGE}VERSION${RESET}
The current version of this script is backwards compatible with versions of
${WHITE}docker-mailserver${RESET} ${BOLD}after${RESET} ${BLUE}8.0.1${RESET}. In case that there is not a more recent release,
this script is currently only working with the ${WHITE}:edge${RESET} tag.
You can download the script for your release by substituting TAG from the
following URL, where TAG looks like 'vX.X.X':
https://raw.githubusercontent.com/docker-mailserver/docker-mailserver/TAG/setup.sh
${ORANGE}OPTIONS${RESET}
${LBLUE}Config path, container or image adjustments${RESET}
-i IMAGE_NAME
Provides the name of the docker-mailserver image. The default value is
${WHITE}docker.io/mailserver/docker-mailserver:latest${RESET}
-c CONTAINER_NAME
Provides the name of the running container.
-p PATH
Provides the config folder path. The default is
${WHITE}${CDIR}/config/${RESET}
${LBLUE}SELinux${RESET}
-z
Allows container access to the bind mount content that is shared among
multiple containers on a SELinux-enabled host.
-Z
Allows container access to the bind mount content that is private and
unshared with other containers on a SELinux-enabled host.
${RED}[${ORANGE}SUB${RED}]${ORANGE}COMMANDS${RESET}
${LBLUE}COMMAND${RESET} email ${RED}:=${RESET}
${0} email add <EMAIL ADDRESS> [<PASSWORD>]
${0} email update <EMAIL ADDRESS> [<PASSWORD>]
${0} email del [ OPTIONS${RED}...${RESET} ] <EMAIL ADDRESS> [ <EMAIL ADDRESS>${RED}...${RESET} ]
${0} email restrict <add${RED}|${RESET}del${RED}|${RESET}list> <send${RED}|${RESET}receive> [<EMAIL ADDRESS>]
${0} email list
${LBLUE}COMMAND${RESET} alias ${RED}:=${RESET}
${0} alias add <EMAIL ADDRESS> <RECIPIENT>
${0} alias del <EMAIL ADDRESS> <RECIPIENT>
${0} alias list
${LBLUE}COMMAND${RESET} quota ${RED}:=${RESET}
${0} quota set <EMAIL ADDRESS> [<QUOTA>]
${0} quota del <EMAIL ADDRESS>
${LBLUE}COMMAND${RESET} config ${RED}:=${RESET}
${0} config dkim [ ARGUMENTS${RED}...${RESET} ]
${0} config ssl <FQDN> (${CYAN}ATTENTION${RESET}: This is deprecated and will be removed soon.)
${LBLUE}COMMAND${RESET} relay ${RED}:=${RESET}
${0} relay add-domain <DOMAIN> <HOST> [<PORT>]
${0} relay add-auth <DOMAIN> <USERNAME> [<PASSWORD>]
${0} relay exclude-domain <DOMAIN>
${LBLUE}COMMAND${RESET} debug ${RED}:=${RESET}
${0} debug fetchmail
${0} debug fail2ban [unban <IP>]
${0} debug show-mail-logs
${0} debug inspect
${0} debug login <COMMANDS>
${ORANGE}EXAMPLES${RESET}
${WHITE}./setup.sh email add test@domain.tld${RESET}
Add the email account ${WHITE}test@domain.tld${RESET}. You will be prompted
to input a password afterwards since no password was supplied.
${WHITE}./setup.sh config dkim keysize 2048 domain 'whoami.com,whoareyou.org'${RESET}
Creates keys of length 2048 but in an LDAP setup where domains are not known to
Postfix by default, so you need to provide them yourself in a comma-separated list.
${WHITE}./setup.sh config dkim help${RESET}
This will provide you with a detailed explanation on how to use the ${WHITE}
config dkim${RESET} command, showing what arguments can be passed and what they do.
${ORANGE}EXIT STATUS${RESET}
Exit status is 0 if the command was successful. If there was an unexpected error, an error
message is shown describing the error. In case of an error, the script will exit with exit
status 1.
2020-11-29 13:14:23 +01:00
2020-07-14 11:07:33 +02:00
"
}
2020-11-29 13:14:23 +01:00
function _docker_image_exists
{
2021-01-30 15:49:10 +01:00
${CRI} history -q "${1}" &>/dev/null
return ${?}
2020-07-14 11:07:33 +02:00
}
2020-11-29 13:14:23 +01:00
function _docker_image
{
if ${USE_CONTAINER}
then
# reuse existing container specified on command line
${CRI} exec "${USE_TTY}" "${CONTAINER_NAME}" "${@}"
2020-07-14 11:07:33 +02:00
else
2020-11-29 13:14:23 +01:00
# start temporary container with specified image
if ! _docker_image_exists "${IMAGE_NAME}"
then
echo "Image '${IMAGE_NAME}' not found. Pulling ..."
${CRI} pull "${IMAGE_NAME}"
2020-07-14 11:07:33 +02:00
fi
2020-11-29 13:14:23 +01:00
${CRI} run --rm \
2021-04-05 15:43:52 +02:00
-v "${CONFIG_PATH}:/tmp/docker-mailserver${USING_SELINUX}" \
2020-11-29 13:14:23 +01:00
"${USE_TTY}" "${IMAGE_NAME}" "${@}"
2020-07-14 11:07:33 +02:00
fi
}
2020-11-29 13:14:23 +01:00
function _docker_container
{
if [[ -n ${CONTAINER_NAME} ]]
then
${CRI} exec "${USE_TTY}" "${CONTAINER_NAME}" "${@}"
2020-07-14 11:07:33 +02:00
else
2021-01-30 15:49:10 +01:00
echo "The mailserver is not running!"
exit 1
2020-07-14 11:07:33 +02:00
fi
}
2020-11-29 13:14:23 +01:00
function _main
{
2021-01-30 15:49:10 +01:00
if command -v docker &>/dev/null
2020-11-29 13:14:23 +01:00
then
CRI=docker
2021-01-30 15:49:10 +01:00
elif command -v podman &>/dev/null
2020-11-29 13:14:23 +01:00
then
CRI=podman
_check_root
else
echo "No supported Container Runtime Interface detected."
exit 10
2020-07-14 11:07:33 +02:00
fi
2021-01-30 15:49:10 +01:00
INFO=$(${CRI} ps --no-trunc --format "{{.Image}};{{.Names}}" --filter \
label=org.opencontainers.image.title="docker-mailserver" | tail -1)
2020-11-29 13:14:23 +01:00
IMAGE_NAME=${INFO%;*}
CONTAINER_NAME=${INFO#*;}
if [[ -z ${IMAGE_NAME} ]]
then
2021-01-30 15:49:10 +01:00
IMAGE_NAME=${NAME:-docker.io/mailserver/docker-mailserver:latest}
2020-11-29 13:14:23 +01:00
fi
2020-07-14 11:07:33 +02:00
2021-01-30 15:49:10 +01:00
if test -t 0
2020-11-29 13:14:23 +01:00
then
USE_TTY="-ti"
2021-01-30 15:49:10 +01:00
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"
2020-11-29 13:14:23 +01:00
fi
2020-07-14 11:07:33 +02:00
2020-11-29 13:14:23 +01:00
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
2020-07-14 11:07:33 +02:00
;;
2020-11-29 13:14:23 +01:00
p )
case "${OPTARG}" in
/* ) WISHED_CONFIG_PATH="${OPTARG}" ;;
* ) WISHED_CONFIG_PATH="${CDIR}/${OPTARG}" ;;
esac
2020-07-14 11:07:33 +02:00
2020-11-29 13:14:23 +01:00
if [[ ! -d ${WISHED_CONFIG_PATH} ]]
then
echo "Directory doesn't exist"
2020-07-14 11:07:33 +02:00
_usage
2020-11-29 13:14:23 +01:00
exit 40
2020-07-14 11:07:33 +02:00
fi
;;
2020-11-29 13:14:23 +01:00
* )
2021-04-05 15:43:52 +02:00
echo "Invalid option: -${OPT}" >&2
2020-07-14 11:07:33 +02:00
;;
2020-11-29 13:14:23 +01:00
2020-07-14 11:07:33 +02:00
esac
2020-11-29 13:14:23 +01:00
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
2021-04-05 15:43:52 +02:00
email )
case ${2:-} in
add ) shift 2 ; _docker_image addmailuser "${@}" ;;
update ) shift 2 ; _docker_image updatemailuser "${@}" ;;
del ) shift 2 ; _docker_container delmailuser "${@}" ;;
restrict ) shift 2 ; _docker_container restrict-access "${@}" ;;
2020-11-29 13:14:23 +01:00
list ) _docker_image listmailuser ;;
* ) _usage ;;
esac
;;
2021-04-05 15:43:52 +02:00
alias )
case ${2:-} in
add ) shift 2 ; _docker_image addalias "${1}" "${2}" ;;
del ) shift 2 ; _docker_image delalias "${1}" "${2}" ;;
list ) shift 2 ; _docker_image listalias ;;
2020-11-29 13:14:23 +01:00
* ) _usage ;;
esac
;;
2021-04-05 15:43:52 +02:00
quota )
case ${2:-} in
set ) shift 2 ; _docker_image setquota "${@}" ;;
del ) shift 2 ; _docker_image delquota "${@}" ;;
2021-01-30 15:49:10 +01:00
* ) _usage ;;
2020-11-29 13:14:23 +01:00
esac
;;
2021-04-05 15:43:52 +02:00
config )
case ${2:-} in
dkim ) shift 2 ; _docker_image open-dkim "${@}" ;;
ssl ) shift 2 ; _docker_image generate-ssl-certificate "${1}" ;;
2020-11-29 13:14:23 +01:00
* ) _usage ;;
esac
;;
2021-04-05 15:43:52 +02:00
relay )
case ${2:-} in
add-domain ) shift 2 ; _docker_image addrelayhost "${@}" ;;
add-auth ) shift 2 ; _docker_image addsaslpassword "${@}" ;;
exclude-domain ) shift 2 ; _docker_image excluderelaydomain "${@}" ;;
2020-11-29 13:14:23 +01:00
* ) _usage ;;
esac
;;
2021-04-05 15:43:52 +02:00
debug )
case ${2:-} in
2020-11-29 13:14:23 +01:00
fetchmail ) _docker_image debug-fetchmail ;;
2021-04-05 15:43:52 +02:00
fail2ban ) shift 2 ; _docker_container fail2ban "${@}" ;;
2020-11-29 13:14:23 +01:00
show-mail-logs ) _docker_container cat /var/log/mail/mail.log ;;
inspect ) _inspect ;;
login )
2021-04-05 15:43:52 +02:00
shift 2
if [[ -z ${1:-} ]]
2020-11-29 13:14:23 +01:00
then
_docker_container /bin/bash
else
_docker_container /bin/bash -c "${@}"
fi
;;
2021-04-05 15:43:52 +02:00
* ) _usage ; exit 1 ;;
2020-11-29 13:14:23 +01:00
esac
;;
help ) _usage ;;
* ) _usage ; exit 1 ;;
esac
}
2020-07-14 11:07:33 +02:00
2020-11-29 13:14:23 +01:00
_main "${@}"