housekeeping

This commit is contained in:
Georg Lauterbach 2020-11-06 14:04:23 +01:00
parent 2f840d7da5
commit ed7106b04d
No known key found for this signature in database
GPG key ID: 386D76E7AD496709
20 changed files with 184 additions and 181 deletions

View file

@ -1,2 +1,4 @@
.github
.git
test/

View file

@ -58,10 +58,8 @@ When refactoring, writing or altering scripts, that is Shell and Bash scripts, i
When writing a script, provide the version and the script's task. Please use [semantic versioning][semver].
``` BASH
#!/usr/bin/env bash
#! /bin/bash
# version 0.1.0
#
# <TASK DESCRIPTION> -> cut this off
# to make it not longer than approx.
# 80 cols.

View file

@ -4,23 +4,20 @@ NAME = tvial/docker-mailserver:testing
VCS_REF := $(shell git rev-parse --short HEAD)
VCS_VERSION := $(shell git describe --tags --contains --always)
SLEEP = 15s
all: build backup generate-accounts tests clean
no-build: backup generate-accounts tests clean
complete_test: lint build generate-accounts tests
build:
docker build \
docker build -t $(NAME) . \
--build-arg VCS_REF=$(VCS_REF) \
--build-arg VCS_VERSION=$(VCS_VERSION) \
-t $(NAME) .
backup:
# if backup directories exist, clean hasn't been called, therefore
# we shouldn't overwrite it. It still contains the original content.
@ if [[ ! -d config.bak ]]; then cp -rp config config.bak; fi
@ if [[ ! -d testconfig.bak ]]; then cp -rp test/config testconfig.bak; fi
@ [[ ! -d config.bak ]] && cp -rp config config.bak; fi
@ [[ ! -d testconfig.bak ]] && cp -rp test/config testconfig.bak; fi
generate-accounts:
@ docker run --rm -e MAIL_USER=user1@localhost.localdomain -e MAIL_PASS=mypassword -t $(NAME) /bin/sh -c 'echo "$$MAIL_USER|$$(doveadm pw -s SHA512-CRYPT -u $$MAIL_USER -p $$MAIL_PASS)"' > test/config/postfix-accounts.cf
@ -28,25 +25,18 @@ generate-accounts:
@ echo "# this is a test comment, please don't delete me :'(" >> test/config/postfix-accounts.cf
@ echo " # this is also a test comment, :O" >> test/config/postfix-accounts.cf
tests:
./test/bats/bin/bats test/*.bats
.PHONY: ALWAYS_RUN
test/%.bats: ALWAYS_RUN
./test/bats/bin/bats $@
./test/bats/bin/bats $@
clean:
# remove running and stopped test containers
-@ docker ps -a | grep -E "docker-mailserver:testing|ldap_for_mail" | cut -f 1-1 -d ' ' | xargs --no-run-if-empty docker rm -f
-@ if [ -d config.bak ]; then\
rm -rf config ;\
mv config.bak config ;\
fi
-@ if [ -d testconfig.bak ]; then\
sudo rm -rf test/config ;\
mv testconfig.bak test/config ;\
fi
-@ [[ -d config.bak ]] && { rm -rf config ; mv config.bak config ; } || :
-@ [[ -d testconfig.bak ]] && { sudo rm -rf test/config ; mv testconfig.bak test/config ; } || :
-@ sudo rm -rf test/onedir test/alias test/quota test/relay test/config/dovecot-lmtp/userdb test/config/key* test/config/opendkim/keys/domain.tld/ test/config/opendkim/keys/example.com/ test/config/opendkim/keys/localdomain2.com/ test/config/postfix-aliases.cf test/config/postfix-receive-access.cf test/config/postfix-receive-access.cfe test/config/dovecot-quotas.cf test/config/postfix-send-access.cf test/config/postfix-send-access.cfe test/config/relay-hosts/chksum test/config/relay-hosts/postfix-aliases.cf test/config/dhparams.pem test/config/dovecot-lmtp/dh.pem test/config/relay-hosts/dovecot-quotas.cf test/config/user-patches.sh test/alias/config/postfix-virtual.cf test/quota/config/dovecot-quotas.cf test/quota/config/postfix-accounts.cf test/relay/config/postfix-relaymap.cf test/relay/config/postfix-sasl-password.cf test/duplicate_configs/
lint: eclint hadolint shellcheck

View file

@ -32,13 +32,9 @@ Easy to deploy and upgrade.
## Announcements
1. Since version `v7.1.0`, the use of default variables has changed slightly. Please consult the [environment Variables](#environment-variables) sections
2. Debian Buster is now Docker base image
- Filebeat was removed
- Dovecot was downgraded
3. ELK was removed
4. New contributing guidelines were added
5. Added coherent coding style and linting
6. Added option to use non-default network interface
2. New contributing guidelines were added
3. Added coherent coding style and linting
4. Added option to use non-default network interface
## Includes
@ -114,28 +110,31 @@ chmod a+x ./setup.sh
**Note:** If you want to use a bare domain (host name equals domain name) see [FAQ](https://github.com/tomav/docker-mailserver/wiki/FAQ-and-Tips#can-i-use-nakedbare-domains-no-host-name).
### Get up and running
**Note:** If using SELinux and is enabled, skip to next section below.
#### Default - Without SELinux
``` BASH
docker-compose up -d mail
./setup.sh email add <user@domain> [<password>]
./setup.sh config dkim
```
### Get up and running with SELinux
- Edit the files `.env` and `docker-compose.yml`:
- In `.env` uncomment the variable `SELINUX_LABEL`.
- If you want the volume bind mount to be shared among other containers switch `-Z` to `-z`.
- In `docker-compose.yml` uncomment the line that contains `${SELINUX_LABEL}` and comment out or remove the line above.
#### With SELinux
Edit the files `.env` and `docker-compose.yml`. In `.env` uncomment the variable `SELINUX_LABEL`. If you want the volume bind mount to be shared among other containers switch `-Z` to `-z`. In `docker-compose.yml`, uncomment the line that contains `${SELINUX_LABEL}` and comment out or remove the line above.
**Note:** When using `setup.sh` use the option `-z` or `-Z`. This should match the value of `SELINUX_LABEL` in the `.env` file.\
See the [wiki](https://github.com/tomav/docker-mailserver/wiki/Setup-docker-mailserver-using-the-script-setup.sh) for more information regarding `setup.sh`.
**Note:** When using `setup.sh` use the option `-z` or `-Z`. This should match the value of `SELINUX_LABEL` in the `.env` file. See the [wiki](https://github.com/tomav/docker-mailserver/wiki/Setup-docker-mailserver-using-the-script-setup.sh) for more information regarding `setup.sh`.
``` BASH
docker-compose up -d mail
./setup.sh -Z email add <user@domain> [<password>]
./setup.sh -Z config dkim
```
### DNS - DKIM
Now that the keys are generated, you can configure your DNS server by just pasting the content of `config/opendkim/keys/domain.tld/mail.txt` in your `domain.tld.hosts` zone.
### Miscellaneous
@ -144,7 +143,7 @@ Now that the keys are generated, you can configure your DNS server by just pasti
``` BASH
docker-compose down
docker pull tvial/docker-mailserver:latest
docker pull tvial/docker-mailserver:<VERSION TAG>
docker-compose up -d mail
```

View file

@ -5,4 +5,9 @@
HOSTNAME=mail
DOMAINNAME=domain.com
CONTAINER_NAME=mail
#SELINUX_LABEL=-Z
#
# SELinux Compose File Settings Variables
#
SELINUX_LABEL=-Z

View file

@ -16,8 +16,8 @@ services:
- mailstate:/var/mail-state
- maillogs:/var/log/mail
- ./config/:/tmp/docker-mailserver/
# If SELinux is enabled uncomment line below and comment line above
#- ./config/:/tmp/docker-mailserver/${SELINUX_LABEL}
# ? if SELinux is enabled, uncomment the line below and comment the line above
# - ./config/:/tmp/docker-mailserver/${SELINUX_LABEL}
env_file:
- mailserver.env
cap_add:

View file

@ -1,8 +1,9 @@
#!/bin/sh
VCS_REF=$(git rev-parse --short HEAD)
VCS_VERSION=$(git describe --tags --contains --always)
#! /bin/sh
VCS_REF="$(git rev-parse --short HEAD)"
VCS_VERSION="$(git describe --tags --contains --always)"
docker build \
--build-arg VCS_REF="$VCS_REF" \
--build-arg VCS_VERSION="$VCS_VERSION" \
-f "$DOCKERFILE_PATH" -t "$IMAGE_NAME" .
--build-arg VCS_REF="${VCS_REF}" \
--build-arg VCS_VERSION="${VCS_VERSION}" \
-f "${DOCKERFILE_PATH}" -t "${IMAGE_NAME}" .

View file

@ -175,9 +175,9 @@ REPORT_INTERVAL=daily
# Note: More details in http://www.postfix.org/postconf.5.html#inet_protocols
POSTFIX_INET_PROTOCOLS=all
# -----------------------------------------------------------------------------------------------------------------------------
# --------------------- Spamassassin section ----------------------------------------------------------------------------------
# -----------------------------------------------------------------------------------------------------------------------------
#
# Spamassassin Section
#
ENABLE_SPAMASSASSIN=0
@ -199,18 +199,18 @@ SA_KILL=6.31
# add tag to subject if spam detected
SA_SPAM_SUBJECT=***SPAM*****
# -----------------------------------------------------------------------------------------------------------------------------
# --------------------- Fetchmail section -------------------------------------------------------------------------------------
# -----------------------------------------------------------------------------------------------------------------------------
#
# Fetchmail Section
#
ENABLE_FETCHMAIL=0
# The interval to fetch mail in seconds
FETCHMAIL_POLL=300
# -----------------------------------------------------------------------------------------------------------------------------
# --------------------- LDAP section ------------------------------------------------------------------------------------------
# -----------------------------------------------------------------------------------------------------------------------------
#
# LDAP Section
#
# A second container for the ldap service is necessary (i.e. https://github.com/osixia/docker-openldap)
# For preparing the ldap server to use in combination with this container this article may be helpful: http://acidx.net/wordpress/2014/06/installing-a-mailserver-with-postfix-dovecot-sasl-ldap-roundcube/
@ -256,9 +256,9 @@ LDAP_QUERY_FILTER_ALIAS=
# => Specify how ldap should be asked for domains
LDAP_QUERY_FILTER_DOMAIN=
# -----------------------------------------------------------------------------------------------------------------------------
# ---------------- Dovecot section --------------------------------------------------------------------------------------------
# -----------------------------------------------------------------------------------------------------------------------------
#
# Dovecot Section
#
# empty => no
# yes => LDAP over TLS enabled for Dovecot
@ -279,9 +279,9 @@ DOVECOT_MAILBOX_FORMAT=maildir
# https://wiki.dovecot.org/AuthDatabase/LDAP/AuthBinds
DOVECOT_AUTH_BIND=
# -----------------------------------------------------------------------------------------------------------------------------
# ---------------- Postgrey section -------------------------------------------------------------------------------------------
# -----------------------------------------------------------------------------------------------------------------------------
#
# Postgrey Section
#
ENABLE_POSTGREY=0
# greylist for N seconds
@ -293,9 +293,9 @@ POSTGREY_TEXT=Delayed by postgrey
# whitelist host after N successful deliveries (N=0 to disable whitelisting)
POSTGREY_AUTO_WHITELIST_CLIENTS=5
# -----------------------------------------------------------------------------------------------------------------------------
# ---------------- SASL section -----------------------------------------------------------------------------------------------
# -----------------------------------------------------------------------------------------------------------------------------
#
# SASL Section
#
ENABLE_SASLAUTHD=0
@ -349,9 +349,9 @@ SASLAUTHD_LDAP_TLS_CHECK_PEER=
# string => `/etc/postfix/sasl_passwd` will be created with the string as password
SASL_PASSWD=
# -----------------------------------------------------------------------------------------------------------------------------
# ---------------- SRS section --------------------------------------------------------------------------------------------
# -----------------------------------------------------------------------------------------------------------------------------
#
# SRS Section
#
# envelope_sender => Rewrite only envelope sender address (default)
# header_sender => Rewrite only header sender (not recommended)
@ -372,9 +372,9 @@ SRS_EXCLUDE_DOMAINS=
# rotate and expire keys
SRS_SECRET=
# -----------------------------------------------------------------------------------------------------------------------------
# ---------------- Default relay host section ---------------------------------------------------------------------------------
# -----------------------------------------------------------------------------------------------------------------------------
#
# Default Relay Host Section
#
# Setup relaying all mail through a default relay host
#
@ -382,9 +382,9 @@ SRS_SECRET=
# default host and optional port to relay all mail through
DEFAULT_RELAY_HOST=
# -----------------------------------------------------------------------------------------------------------------------------
# ---------------- Multi-domain relay section ---------------------------------------------------------------------------------
# -----------------------------------------------------------------------------------------------------------------------------
#
# Multi-Domain Relay Section
#
# Setup relaying for multiple domains based on the domain name of the sender
# optionally uses usernames and passwords in postfix-sasl-password.cf and relay host mappings in postfix-relaymap.cf

View file

@ -1,7 +1,7 @@
#!/bin/bash
#! /bin/bash
# Wrapper for various setup scripts
# included in the docker-mailserver
# included in docker-mailserver
SCRIPT='SETUP'
@ -252,12 +252,24 @@ function _main
while getopts ":c:i:p:hzZ" OPT
do
case ${OPT} in
c) CONTAINER_NAME="${OPTARG}" ; USE_CONTAINER=true ;; # container specified, connect to running instance
i) IMAGE_NAME="${OPTARG}" ;;
p)
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}" ;;
/* ) WISHED_CONFIG_PATH="${OPTARG}" ;;
* ) WISHED_CONFIG_PATH="${CDIR}/${OPTARG}" ;;
esac
if [[ ! -d ${WISHED_CONFIG_PATH} ]]
@ -267,13 +279,15 @@ function _main
exit 40
fi
;;
h) _usage ; return ;;
z) USING_SELINUX=":z" ;;
Z) USING_SELINUX=":Z" ;;
*) echo "Invalid option: -${OPTARG}" >&2 ;;
* )
echo "Invalid option: -${OPTARG}" >&2
;;
esac
done
shift $((OPTIND-1))
shift $(( OPTIND - 1 ))
if [[ -z ${WISHED_CONFIG_PATH} ]]
then

View file

@ -1,6 +1,5 @@
#! /bin/bash
# version 0.1.0
# executed from scripts in target/bin/
# task provides frequently used functions

View file

@ -1,9 +1,5 @@
#! /bin/bash
# version 0.2.0
#
# <INSERT TASK HERE>
# shellcheck source=./helper-functions.sh
. /usr/local/bin/helper-functions.sh

View file

@ -1,4 +1,4 @@
#!/usr/bin/env sh
#! /usr/bin/env sh
# Report a quota usage warning to an user

View file

@ -1,7 +1,5 @@
#! /bin/bash
# version 0.1.0
#
# You cannot start fail2ban in some foreground mode and
# it's more or less important that docker doesn't kill
# fail2ban and its chilren if you stop the container.

View file

@ -1,10 +1,5 @@
#! /bin/bash
# version 0.1.1
#
# Provides varous helpers.
# ? IP and CIDR -------------------------------------------

View file

@ -1,7 +1,5 @@
#! /bin/bash
# version 0.1.0
#
# You cannot start postfix in some foreground mode and
# it's more or less important that docker doesn't kill
# postfix and its chilren if you stop the container.

View file

@ -1,5 +1,5 @@
#!/bin/sh
#
#! /bin/sh
# postgrey start/stop the postgrey greylisting deamon for postfix
# (priority should be smaller than that of postfix)
#
@ -8,7 +8,8 @@
# Distribute and/or modify at will.
#
# Version: $Id: postgrey.init 1436 2006-12-07 07:15:03Z avbidder $
#
# altered by Georg Lauterbach as aendeavor 2020-11.05 14:02:00Z
### BEGIN INIT INFO
# Provides: postgrey
# Required-Start: $syslog $local_fs $remote_fs
@ -20,46 +21,48 @@
set -e
PATH=/sbin:/bin:/usr/sbin:/usr/bin
DAEMON=/usr/sbin/postgrey
DAEMON_NAME=postgrey
DESC="postfix greylisting daemon"
DAEMON_USER=postgrey
PATH='/sbin:/bin:/usr/sbin:/usr/bin'
DAEMON='/usr/sbin/postgrey'
DAEMON_NAME='postgrey'
DESC='postfix greylisting daemon'
DAEMON_USER='postgrey'
PIDFILE=/var/run/$DAEMON_NAME/$DAEMON_NAME.pid
SCRIPTNAME=/etc/init.d/$DAEMON_NAME
PIDFILE="/var/run/${DAEMON_NAME}/${DAEMON_NAME}.pid"
SCRIPTNAME="/etc/init.d/${DAEMON_NAME}"
# Gracefully exit if the package has been removed.
test -x $DAEMON || exit 0
# gracefully exit if the package has been removed.
[ -x "${DAEMON}" ] || exit 0
# shellcheck source=/dev/null
. /lib/lsb/init-functions
# Read config file if it is present.
if [ -r /etc/default/$DAEMON_NAME ]
then
. /etc/default/$DAEMON_NAME
fi
# shellcheck source=/dev/null
[ -r "/etc/default/${DAEMON_NAME}" ] && . "/etc/default/${DAEMON_NAME}"
POSTGREY_OPTS="--pidfile=$PIDFILE --daemonize $POSTGREY_OPTS"
if [ -z "$POSTGREY_TEXT" ]; then
POSTGREY_OPTS="--pidfile=${PIDFILE} --daemonize ${POSTGREY_OPTS}"
if [ -z "${POSTGREY_TEXT}" ]
then
POSTGREY_TEXT_OPT=""
else
POSTGREY_TEXT_OPT="--greylist-text=$POSTGREY_TEXT"
POSTGREY_TEXT_OPT="--greylist-text=${POSTGREY_TEXT}"
fi
ret=0
do_start()
{
# Return
# 0 if daemon has been started
# 1 if daemon was already running
# 2 if daemon could not be started
start-stop-daemon --start --quiet --pidfile $PIDFILE --exec $DAEMON --test > /dev/null \
|| return 1
start-stop-daemon --start --quiet --pidfile $PIDFILE --exec $DAEMON -- \
$POSTGREY_OPTS "$POSTGREY_TEXT_OPT" \
|| return 2
# Return
# 0 if daemon has been started
# 1 if daemon was already running
# 2 if daemon could not be started
start-stop-daemon --start --quiet --pidfile \
"${PIDFILE}" --exec "${DAEMON}" --test >/dev/null || return 1
start-stop-daemon --start --quiet --pidfile \
"${PIDFILE}" --exec "${DAEMON}" -- "${POSTGREY_OPTS}" \
"${POSTGREY_TEXT_OPT}" || return 2
}
do_stop()
@ -69,9 +72,11 @@ do_stop()
# 1 if daemon was already stopped
# 2 if daemon could not be stopped
# other if a failure occurred
start-stop-daemon --user $DAEMON_USER --stop --quiet --retry=TERM/30/KILL/5 --pidfile $PIDFILE
RETVAL="$?"
[ "$RETVAL" = 2 ] && return 2
start-stop-daemon --user "${DAEMON_USER}" --stop --quiet \
--retry=TERM/30/KILL/5 --pidfile "${PIDFILE}"
RETVAL="$?"
[ "${RETVAL}" -eq 2 ] && return 2
# Wait for children to finish too if this is a daemon that forks
# and if the daemon is only ever run from this initscript.
@ -79,12 +84,13 @@ do_stop()
# that waits for the process to drop all resources that could be
# needed by services started subsequently. A last resort is to
# sleep for some time.
start-stop-daemon --user $DAEMON_USER --stop --quiet --oknodo --retry=0/30/KILL/5 --exec $DAEMON
[ "$?" = 2 ] && return 2
# Many daemons don't delete their pidfiles when they exit.
rm -f $PIDFILE
return "$RETVAL"
start-stop-daemon --user "${DAEMON_USER}" --stop --quiet \
--oknodo --retry=0/30/KILL/5 --exec "${DAEMON}"
[ "$?" -eq 2 ] && return 2
# Many daemons don't delete their pidfiles when they exit.
rm -f "${PIDFILE}"
return "${RETVAL}"
}
do_reload()
@ -94,49 +100,56 @@ do_reload()
# restarting (for example, when it is sent a SIGHUP),
# then implement that here.
#
start-stop-daemon --stop --signal 1 --quiet --pidfile $PIDFILE
start-stop-daemon --stop --signal 1 --quiet --pidfile "${PIDFILE}"
return 0
}
case "$1" in
start)
[ "$VERBOSE" != no ] && log_daemon_msg "Starting $DESC" "$DAEMON_NAME"
do_start
case "$?" in
0|1) [ "$VERBOSE" != no ] && log_end_msg 0 ;;
2) [ "$VERBOSE" != no ] && log_end_msg 1 ;;
esac
;;
stop)
[ "$VERBOSE" != no ] && log_daemon_msg "Stopping $DESC" "$DAEMON_NAME"
do_stop
case "$?" in
0|1) [ "$VERBOSE" != no ] && log_end_msg 0 ;;
2) [ "$VERBOSE" != no ] && log_end_msg 1 ;;
esac
;;
reload|force-reload)
[ "$VERBOSE" != no ] && log_daemon_msg "Reloading $DESC" "$DAEMON_NAME"
do_reload
case "$?" in
0|1) [ "$VERBOSE" != no ] && log_end_msg 0 ;;
2) [ "$VERBOSE" != no ] && log_end_msg 1 ;;
esac
;;
restart)
do_stop
do_start
;;
status)
status_of_proc -p $PIDFILE $DAEMON "$DAEMON_NAME" 2>/dev/null
ret=$?
;;
case "${1}" in
start )
[ "${VERBOSE}" != no ] && log_daemon_msg "Starting ${DESC}" "${DAEMON_NAME}"
do_start
*)
echo "Usage: $SCRIPTNAME {start|stop|restart|reload|force-reload|status}" >&2
exit 1
;;
case "${?}" in
0|1) [ "${VERBOSE}" != no ] && log_end_msg 0 ;;
2) [ "${VERBOSE}" != no ] && log_end_msg 1 ;;
esac
;;
stop )
[ "${VERBOSE}" != no ] && log_daemon_msg "Stopping ${DESC}" "${DAEMON_NAME}"
do_stop
case "${?}" in
0|1) [ "${VERBOSE}" != no ] && log_end_msg 0 ;;
2) [ "${VERBOSE}" != no ] && log_end_msg 1 ;;
esac
;;
reload|force-reload)
[ "${VERBOSE}" != no ] && log_daemon_msg "Reloading ${DESC}" "${DAEMON_NAME}"
do_reload
case "${?}" in
0|1) [ "${VERBOSE}" != no ] && log_end_msg 0 ;;
2) [ "${VERBOSE}" != no ] && log_end_msg 1 ;;
esac
;;
restart )
do_stop
do_start
;;
status )
status_of_proc -p "${PIDFILE}" "${DAEMON}" "${DAEMON_NAME}" 2>/dev/null
ret=${?}
;;
* )
echo "Usage: ${SCRIPTNAME} {start|stop|restart|reload|force-reload|status}" >&2
exit 1
;;
esac
exit $ret
exit ${ret}

View file

@ -1,7 +1,5 @@
#! /bin/bash
# version 0.1.0
function _generate_secret { ( umask 0077 ; dd if=/dev/urandom bs=24 count=1 2>/dev/null | base64 -w0 > "${1}" ) }
if [[ -n ${SRS_DOMAINNAME} ]]

View file

@ -1,9 +1,5 @@
#! /bin/bash
# version 0.2.1
#
# Starts the mailserver.
##########################################################################
# >> DEFAULT VARS
#

View file

@ -1,4 +1,4 @@
#!/usr/bin/env bash
#! /bin/bash
# version v0.1.0 stable
# executed by TravisCI / manually
@ -190,7 +190,7 @@ function _shellcheck
fi
done < <(find test/ -maxdepth 1 -type f -iname "*.bats")
if [[ ERR -eq 1 ]]
if [[ ${ERR} -eq 1 ]]
then
__log_abort 'errors encountered'
return 101

View file

@ -63,6 +63,7 @@ function teardown_file() {
assert_success
run docker exec mail_smtponly /bin/sh -c "/etc/init.d/postfix reload"
assert_success
sleep 5
run docker exec mail_smtponly /bin/sh -c "nc 0.0.0.0 25 < /tmp/docker-mailserver-test/email-templates/smtp-only.txt"
assert_success
run docker exec mail_smtponly /bin/sh -c 'grep -cE "to=<user2\@external.tld>.*status\=sent" /var/log/mail/mail.log'