diff --git a/.travis.yml b/.travis.yml index 4f945c21..cf62d9bf 100644 --- a/.travis.yml +++ b/.travis.yml @@ -5,8 +5,18 @@ language: bash sudo: required services: - docker +install: +- travis_wait make build-no-cache script: -- make all +- make generate-accounts run fixtures tests +after_script: +- make clean notifications: slack: secure: TTo1z9nbZCWcIdfPwypubNa3y+pwvfgDGlzEVAGEuK7uuIpmEoAcAUNSSPTnbewDGHnDl8t/ml93MtvP+a+IVuAKytMqF39PHyoZO7aUl9J62V+G75OmnyGjXGJm40pQosCS6LzqoRRYXotl9+fwH568Kf4ifXCrMZX1d+ir7Ww= + webhooks: + urls: + - https://webhooks.gitter.im/e/7c5e56a8257cdec003ab + on_success: always + on_failure: always + on_start: never diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 00000000..e1b107ad --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,32 @@ +# Contributing + +`docker-mailserver` is OpenSource. That means that you can contribute on enhancements, bug fixing or improving the documentation in the Wiki. + +## Open an issue + +When opening an issue, please provide details use case to let the community reproduce your problem. +Please start the mail server with env `DMS_DEBUG=1` and paste the ouput into the issue. + +## Pull Requests + +#### Project architecture + + ├── config # User: personal configurations + ├── target # Developer: default server configuration, used when building the image + └── test # Developer: integration tests to check that everything keeps working + +#### Development Workflow + +The development workflow is the following: + +- Fork project and clone your fork +- Create a branch using `git checkout -b branch_name` (you can use `issue-xxx` if fixing an existing issue) +- Code :-) +- Add integration tests in `test/tests.bats` +- Use `make` to build image locally and run tests +- Document your improvements +- [Commit](https://help.github.com/articles/closing-issues-via-commit-messages/), push and make a pull-request +- Pull-request is automatically tested on Travis +- When tests are green, your branch is merged into `master` +- `master` is automatically tested on Travis +- Docker builds a new `latest` image diff --git a/Dockerfile b/Dockerfile index f13df638..b9fa1c1b 100644 --- a/Dockerfile +++ b/Dockerfile @@ -102,6 +102,7 @@ RUN sed -i 's/START_DAEMON=no/START_DAEMON=yes/g' /etc/default/fetchmail # Configures Postfix COPY target/postfix/main.cf target/postfix/master.cf /etc/postfix/ +RUN echo "" > /etc/aliases # Configuring Logs RUN sed -i -r "/^#?compress/c\compress\ncopytruncate" /etc/logrotate.conf && \ @@ -117,8 +118,7 @@ RUN sed -i -r "/^#?compress/c\compress\ncopytruncate" /etc/logrotate.conf && \ sed -i -r 's|/var/log/mail|/var/log/mail/mail|g' /etc/logrotate.d/rsyslog # Get LetsEncrypt signed certificate -RUN curl -s https://letsencrypt.org/certs/lets-encrypt-x1-cross-signed.pem > /etc/ssl/certs/lets-encrypt-x1-cross-signed.pem && \ - curl -s https://letsencrypt.org/certs/lets-encrypt-x2-cross-signed.pem > /etc/ssl/certs/lets-encrypt-x2-cross-signed.pem +RUN curl -s https://letsencrypt.org/certs/lets-encrypt-x3-cross-signed.pem > /etc/ssl/certs/lets-encrypt-x3-cross-signed.pem COPY ./target/bin /usr/local/bin # Start-mailserver script diff --git a/Makefile b/Makefile index 8b425908..7837ca40 100644 --- a/Makefile +++ b/Makefile @@ -22,56 +22,55 @@ run: -v "`pwd`/test/config":/tmp/docker-mailserver \ -v "`pwd`/test":/tmp/docker-mailserver-test \ -v "`pwd`/test/onedir":/var/mail-state \ + -e ENABLE_CLAMAV=1 \ + -e ENABLE_SPAMASSASSIN=1 \ -e SA_TAG=1.0 \ -e SA_TAG2=2.0 \ -e SA_KILL=3.0 \ -e VIRUSMAILS_DELETE_DELAY=7 \ -e SASL_PASSWD="external-domain.com username:password" \ -e ENABLE_MANAGESIEVE=1 \ - -e PERMIT_DOCKER=host\ + -e PERMIT_DOCKER=host \ + -e DMS_DEBUG=0 \ -h mail.my-domain.com -t $(NAME) - sleep 20 + sleep 15 docker run -d --name mail_pop3 \ -v "`pwd`/test/config":/tmp/docker-mailserver \ -v "`pwd`/test":/tmp/docker-mailserver-test \ -v "`pwd`/test/config/letsencrypt":/etc/letsencrypt/live \ -e ENABLE_POP3=1 \ + -e DMS_DEBUG=1 \ -e SSL_TYPE=letsencrypt \ -h mail.my-domain.com -t $(NAME) - sleep 20 + sleep 15 docker run -d --name mail_smtponly \ -v "`pwd`/test/config":/tmp/docker-mailserver \ -v "`pwd`/test":/tmp/docker-mailserver-test \ -e SMTP_ONLY=1 \ -e PERMIT_DOCKER=network\ -h mail.my-domain.com -t $(NAME) - sleep 20 + sleep 15 docker run -d --name mail_fail2ban \ -v "`pwd`/test/config":/tmp/docker-mailserver \ -v "`pwd`/test":/tmp/docker-mailserver-test \ -e ENABLE_FAIL2BAN=1 \ --cap-add=NET_ADMIN \ -h mail.my-domain.com -t $(NAME) - sleep 20 + sleep 15 docker run -d --name mail_fetchmail \ -v "`pwd`/test/config":/tmp/docker-mailserver \ -v "`pwd`/test":/tmp/docker-mailserver-test \ -e ENABLE_FETCHMAIL=1 \ --cap-add=NET_ADMIN \ -h mail.my-domain.com -t $(NAME) - sleep 20 - docker run -d --name mail_disabled_amavis \ + sleep 15 + docker run -d --name mail_disabled_clamav_spamassassin \ -v "`pwd`/test/config":/tmp/docker-mailserver \ -v "`pwd`/test":/tmp/docker-mailserver-test \ - -e DISABLE_AMAVIS=1 \ + -e ENABLE_CLAMAV=0 \ + -e ENABLE_SPAMASSASSIN=0 \ -h mail.my-domain.com -t $(NAME) - sleep 20 - docker run -d --name mail_disabled_clamav \ - -v "`pwd`/test/config":/tmp/docker-mailserver \ - -v "`pwd`/test":/tmp/docker-mailserver-test \ - -e DISABLE_CLAMAV=1 \ - -h mail.my-domain.com -t $(NAME) - sleep 20 + sleep 15 docker run -d --name mail_manual_ssl \ -v "`pwd`/test/config":/tmp/docker-mailserver \ -v "`pwd`/test":/tmp/docker-mailserver-test \ @@ -79,11 +78,11 @@ run: -e SSL_CERT_PATH=/tmp/docker-mailserver/letsencrypt/mail.my-domain.com/fullchain.pem \ -e SSL_KEY_PATH=/tmp/docker-mailserver/letsencrypt/mail.my-domain.com/privkey.pem \ -h mail.my-domain.com -t $(NAME) - sleep 20 + sleep 15 docker run -d --name ldap_for_mail \ -e LDAP_DOMAIN="localhost.localdomain" \ -h mail.my-domain.com -t ldap - sleep 20 + sleep 15 docker run -d --name mail_with_ldap \ -v "`pwd`/test/config":/tmp/docker-mailserver \ -v "`pwd`/test":/tmp/docker-mailserver-test \ @@ -100,8 +99,17 @@ run: -e POSTMASTER_ADDRESS=postmaster@localhost.localdomain \ --link ldap_for_mail:ldap \ -h mail.my-domain.com -t $(NAME) + sleep 15 + docker run -d --name mail_with_imap \ + -v "`pwd`/test/config":/tmp/docker-mailserver \ + -v "`pwd`/test":/tmp/docker-mailserver-test \ + -e ENABLE_SASLAUTHD=1 \ + -e SASLAUTHD_MECHANISMS=rimap \ + -e SASLAUTHD_MECH_OPTIONS=127.0.0.1 \ + -e POSTMASTER_ADDRESS=postmaster@localhost.localdomain \ + -h mail.my-domain.com -t $(NAME) # Wait for containers to fully start - sleep 20 + sleep 15 docker run -d --name mail_lmtp_ip \ -v "`pwd`/test/config":/tmp/docker-mailserver \ -v "`pwd`/test/config/dovecot-lmtp":/etc/dovecot \ @@ -109,7 +117,7 @@ run: -e ENABLE_POSTFIX_VIRTUAL_TRANSPORT=1 \ -e POSTFIX_DAGENT=lmtp:127.0.0.1:24 \ -h mail.my-domain.com -t $(NAME) - sleep 20 + sleep 15 fixtures: cp config/postfix-accounts.cf config/postfix-accounts.cf.bak @@ -129,6 +137,7 @@ fixtures: docker exec mail /bin/sh -c "nc 0.0.0.0 25 < /tmp/docker-mailserver-test/email-templates/existing-catchall-local.txt" docker exec mail /bin/sh -c "nc 0.0.0.0 25 < /tmp/docker-mailserver-test/email-templates/sieve-spam-folder.txt" docker exec mail /bin/sh -c "nc 0.0.0.0 25 < /tmp/docker-mailserver-test/email-templates/non-existing-user.txt" + docker exec mail_disabled_clamav_spamassassin /bin/sh -c "nc 0.0.0.0 25 < /tmp/docker-mailserver-test/email-templates/existing-user.txt" # postfix virtual transport lmtp docker exec mail_lmtp_ip /bin/sh -c "nc 0.0.0.0 25 < /tmp/docker-mailserver-test/email-templates/existing-user.txt" # Wait for mails to be analyzed @@ -147,11 +156,11 @@ clean: mail_fail2ban \ mail_fetchmail \ fail-auth-mailer \ - mail_disabled_amavis \ - mail_disabled_clamav \ + mail_disabled_clamav_spamassassin \ mail_manual_ssl \ ldap_for_mail \ mail_with_ldap \ + mail_with_imap \ mail_lmtp_ip @if [ -f config/postfix-accounts.cf.bak ]; then\ diff --git a/README.md b/README.md index a8fc4467..ad9f3a37 100644 --- a/README.md +++ b/README.md @@ -20,6 +20,7 @@ Includes: - fetchmail - basic [sieve support](https://github.com/tomav/docker-mailserver/wiki/Configure-Sieve-filters) using dovecot - [LetsEncrypt](https://letsencrypt.org/) and self-signed certificates +- persistent data and state (but think about backups!) - [integration tests](https://travis-ci.org/tomav/docker-mailserver) - [automated builds on docker hub](https://hub.docker.com/r/tvial/docker-mailserver/) @@ -37,28 +38,40 @@ Before you open an issue, please have a look this `README`, the [Wiki](https://g Adapt this file with your FQDN. Install [docker-compose](https://docs.docker.com/compose/) in the version `1.6` or higher. +Your configs must be mounted in `/tmp/docker-mailserver/`. To understand how things work on boot, please have a look to [start-mailserver.sh](https://github.com/tomav/docker-mailserver/blob/master/target/start-mailserver.sh) + ```yaml version: '2' services: mail: - image: tvial/docker-mailserver:latest - # build: . + image: tvial/docker-mailserver:2.1 hostname: mail domainname: domain.com container_name: mail ports: - - "25:25" - - "143:143" - - "587:587" - - "993:993" + - "25:25" + - "143:143" + - "587:587" + - "993:993" volumes: - - maildata:/var/mail - - ./config/:/tmp/docker-mailserver/ + - maildata:/var/mail + - mailstate:/var/mail-state + - ./config/:/tmp/docker-mailserver/ + environment: + - ENABLE_SPAMASSASSIN=1 + - ENABLE_CLAMAV=1 + - ENABLE_FAIL2BAN=1 + - ONE_DIR=1 + - DMS_DEBUG=0 + cap_add: + - NET_ADMIN volumes: maildata: driver: local + mailstate: + driver: local ``` #### Create your mail accounts @@ -81,6 +94,8 @@ Don't forget to adapt MAIL_USER and MAIL_PASS to your needs Now 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. +Note: you can also manage email accounts, DKIM keys and more with the [setup.sh convenience script](https://github.com/tomav/docker-mailserver/wiki/Setup-docker-mailserver-using-the-script-setup.sh). + #### Start the container docker-compose up -d mail @@ -93,6 +108,39 @@ Please check [how the container starts](https://github.com/tomav/docker-mailserv Value in **bold** is the default value. +##### DMS_DEBUG + + - **0** => Debug disabled + - 1 => Enables debug on startup + +#### ENABLE_CLAMAV + + - **0** => Clamav is disabled + - 1 => Clamav is enabled + +#### ENABLE_SPAMASSASSIN + + - **0** => Spamassassin is disabled + - 1 => Spamassassin is enabled + +##### SA_TAG + + - **2.0** => add spam info headers if at, or above that level + +Note: this spamassassin setting needs `ENABLE_SPAMASSASSIN=1` + +##### SA_TAG2 + + - **6.31** => add 'spam detected' headers at that level + +Note: this spamassassin setting needs `ENABLE_SPAMASSASSIN=1` + +##### SA_KILL + + - **6.31** => triggers spam evasive actions + +Note: this spamassassin setting needs `ENABLE_SPAMASSASSIN=1` + ##### ENABLE_POP3 - **empty** => POP3 service disabled @@ -100,7 +148,7 @@ Value in **bold** is the default value. ##### ENABLE_FAIL2BAN - - **empty** => fail2ban service disabled + - **0** => fail2ban service disabled - 1 => Enables fail2ban service If you enable Fail2Ban, don't forget to add the following lines to your `docker-compose.yml`: @@ -116,7 +164,7 @@ Otherwise, `iptables` won't be able to ban IPs. - 1 => Enables Managesieve on port 4190 ##### ENABLE_FETCHMAIL - - **empty** => `fetchmail` disabled + - **0** => `fetchmail` disabled - 1 => `fetchmail` enabled ##### ENABLE_LDAP @@ -153,21 +201,9 @@ Otherwise, `iptables` won't be able to ban IPs. - **empty** => postmaster@domain.com - => Specify the postmaster address -##### SA_TAG - - - **2.0** => add spam info headers if at, or above that level - -##### SA_TAG2 - - - **6.31** => add 'spam detected' headers at that level - -##### SA_KILL - - - **6.31** => triggers spam evasive actions - ##### ENABLE_SASLAUTHD - - **empty** => `saslauthd` is disabled + - **0** => `saslauthd` is disabled - 1 => `saslauthd` is enabled ##### SASLAUTHD_MECHANISMS diff --git a/docker-compose.yml.dist b/docker-compose.yml.dist index 4eb13770..d965a9a6 100644 --- a/docker-compose.yml.dist +++ b/docker-compose.yml.dist @@ -2,7 +2,7 @@ version: '2' services: mail: - image: tvial/docker-mailserver:v2 + image: tvial/docker-mailserver:2.1 hostname: mail domainname: domain.com container_name: mail @@ -13,12 +13,19 @@ services: - "993:993" volumes: - maildata:/var/mail + - mailstate:/var/mail-state - ./config/:/tmp/docker-mailserver/ environment: + - ENABLE_SPAMASSASSIN=1 + - ENABLE_CLAMAV=1 - ENABLE_FAIL2BAN=1 + - ONE_DIR=1 + - DMS_DEBUG=0 cap_add: - NET_ADMIN volumes: maildata: driver: local + mailstate: + driver: local diff --git a/setup.sh b/setup.sh index bfdc9da4..40e29ead 100755 --- a/setup.sh +++ b/setup.sh @@ -43,6 +43,7 @@ SUBCOMMANDS: email: $0 email add + $0 email update $0 email del $0 email list @@ -115,6 +116,10 @@ case $1 in shift _docker_image addmailuser $@ ;; + update) + shift + _docker_image updatemailuser $@ + ;; del) shift _docker_image delmailuser $@ diff --git a/target/bin/addmailuser b/target/bin/addmailuser index f6a29ec6..c9e83d9c 100755 --- a/target/bin/addmailuser +++ b/target/bin/addmailuser @@ -1,29 +1,32 @@ -#!/bin/bash +#! /bin/bash -DATABASE=/tmp/docker-mailserver/postfix-accounts.cf +DATABASE=${DATABASE:-/tmp/docker-mailserver/postfix-accounts.cf} -function usage { - echo 'Usage: addmailuser [password]' - exit 1 +USER="$1" +PASSWD="$2" + +usage() { + echo "Usage: addmailuser []" } -if [ ! -z "$1" ]; then - USER=$1 - if [ -e "$DATABASE" ] && [ ! -z "$(grep $USER -i $DATABASE)" ]; then - echo "User already exists" - exit 1 - fi - if [ ! -z "$2" ]; then - PASS="$2" - else - read -s -p "Enter Password: " PASS - if [ -z "$PASS" ]; then - echo "Password can't be empty" - exit 1 - fi - fi - ENTRY=$(echo "$USER|$(doveadm pw -s SHA512-CRYPT -u "$USER" -p "$PASS")") - echo "$ENTRY" >> $DATABASE -else - usage +errex() { + echo "$@" 1>&2 + exit 1 +} + +escape() { + echo "${1//./\\.}" +} + +[ -z "$USER" ] && { usage; errex "no username specified"; } + +grep -qi "^$(escape "$USER")|" $DATABASE 2>/dev/null && + errex "User \"$USER\" already exists" + +if [ -z "$PASSWD" ]; then + read -s -p "Enter Password: " PASSWD + echo + [ -z "$PASSWD" ] && errex "Password must not be empty" fi + +echo "$USER|$(doveadm pw -s SHA512-CRYPT -u "$USER" -p "$PASSWD")" >>$DATABASE diff --git a/target/bin/delmailuser b/target/bin/delmailuser index 9638b026..e0d5762d 100755 --- a/target/bin/delmailuser +++ b/target/bin/delmailuser @@ -1,18 +1,24 @@ -#!/bin/bash +#! /bin/bash -DATABASE=/tmp/docker-mailserver/postfix-accounts.cf +DATABASE=${DATABASE:-/tmp/docker-mailserver/postfix-accounts.cf} -function usage { - echo "Usage: delmailuser " - exit 1 +USER="$1" + +usage() { + echo "Usage: delmailuser " } -if [ ! -z "$1" ]; then - USER=$1 - if [ -f "$DATABASE" ]; then - ENTRIES=$(grep "$USER" -vi $DATABASE) - echo "$ENTRIES" > $DATABASE - fi -else - usage -fi +errex() { + echo "$@" 1>&2 + exit 1 +} + +escape() { + echo "${1//./\\.}" +} + +[ -z "$USER" ] && { usage; errex "No user specifed"; } +[ -s "$DATABASE" ] || exit 0 + +# XXX $USER must not contain /s and other syntactic characters +sed -i "/^$(escape "$USER")|/d" $DATABASE diff --git a/target/bin/listmailuser b/target/bin/listmailuser index 0004b547..04352a5d 100755 --- a/target/bin/listmailuser +++ b/target/bin/listmailuser @@ -1,16 +1,13 @@ -#! /bin/sh +#! /bin/bash -DATABASE=/tmp/docker-mailserver/postfix-accounts.cf +DATABASE=${DATABASE:-/tmp/docker-mailserver/postfix-accounts.cf} -if [ ! -f "$DATABASE" ]; then - echo "The configuration file 'postfix-accounts.cf' doesn't exist. Until now no email addresses have been added." - exit 1 -fi +errex() { + echo "$@" 1>&2 + exit 1 +} -if [ ! -s "$DATABASE" ]; then - echo "No email addresses have been added." - exit 1 -fi - -cat "$DATABASE" | awk -F '|' '{print $1}' +[ -f $DATABASE ] || errex "No postfix-accounts.cf file" +[ -s $DATABASE ] || errex "Empty postfix-accounts.cf - no users have been added" +awk -F '|' '{ print $1; }' $DATABASE diff --git a/target/bin/updatemailuser b/target/bin/updatemailuser new file mode 100755 index 00000000..a302891c --- /dev/null +++ b/target/bin/updatemailuser @@ -0,0 +1,27 @@ +#! /bin/bash + +DATABASE=${DATABASE:-/tmp/docker-mailserver/postfix-accounts.cf} + +USER="$1" +PASSWD="$2" + +usage() { + echo "Usage: updatemailuser [password]" +} + +errex() { + echo "$@" 1>&2 + exit 1 +} + +escape() { + echo "${1//./\\.}" +} + +[ -z "$USER" ] && { usage; errex "no username specified"; } + +grep -qi "^$(escape "$USER")|" $DATABASE 2>/dev/null || + errex "User \"$USER\" does not exist" + +delmailuser "$USER" +addmailuser "$USER" "$PASSWD" diff --git a/target/dovecot/10-ssl.conf b/target/dovecot/10-ssl.conf index f6937d11..77f60c5c 100644 --- a/target/dovecot/10-ssl.conf +++ b/target/dovecot/10-ssl.conf @@ -46,10 +46,10 @@ ssl_key = > check funcs @@ -62,6 +73,7 @@ function register_functions() { if [ "$ENABLE_SASLAUTHD" = 1 ];then _register_setup_function "_setup_saslauthd" + _register_setup_function "_setup_postfix_sasl" fi _register_setup_function "_setup_dkim" @@ -75,7 +87,10 @@ function register_functions() { _register_setup_function "_setup_security_stack" _register_setup_function "_setup_postfix_aliases" _register_setup_function "_setup_postfix_vhost" - _register_setup_function "_setup_postfix_relay_amazon_ses" + + if [ ! -z "$AWS_SES_HOST" -a ! -z "$AWS_SES_USERPASS" ]; then + _register_setup_function "_setup_postfix_relay_amazon_ses" + fi if [ "$ENABLE_POSTFIX_VIRTUAL_TRANSPORT" = 1 ]; then _register_setup_function "_setup_postfix_virtual_transport" @@ -97,7 +112,8 @@ function register_functions() { ################### >> daemon funcs - _register_start_daemon "_start_daemons_sys" + _register_start_daemon "_start_daemons_cron" + _register_start_daemon "_start_daemons_rsyslog" if [ "$ENABLE_ELK_FORWARDER" = 1 ]; then _register_start_daemon "_start_daemons_filebeat" @@ -125,13 +141,11 @@ function register_functions() { _register_start_daemon "_start_daemons_fetchmail" fi - if ! [ "$DISABLE_CLAMAV" = 1 ]; then + if [ "$ENABLE_CLAMAV" = 1 ]; then _register_start_daemon "_start_daemons_clamav" fi - if ! [ "$DISABLE_AMAVIS" = 1 ]; then - _register_start_daemon "_start_daemons_amavis" - fi + _register_start_daemon "_start_daemons_amavis" ################### << daemon funcs } ########################################################################## @@ -195,45 +209,78 @@ function _register_misc_function() { function notify () { c_red="\e[0;31m" c_green="\e[0;32m" + c_brown="\e[0;33m" c_blue="\e[0;34m" c_bold="\033[1m" c_reset="\e[0m" notification_type=$1 notification_msg=$2 + notification_format=$3 + msg="" case "${notification_type}" in - 'inf') - msg="${c_green} * ${c_reset}${notification_msg}" - ;; - 'err') - msg="${c_red} * ${c_reset}${notification_msg}" - ;; - 'warn') - msg="${c_blue} * ${c_reset}${notification_msg}" - ;; - 'task') - msg=" >>>> ${notification_msg}" - ;; 'taskgrp') msg="${c_bold}${notification_msg}${c_reset}" ;; + 'task') + if [[ ${DEFAULT_VARS["DMS_DEBUG"]} == 1 ]]; then + msg=" ${notification_msg}${c_reset}" + fi + ;; + 'inf') + if [[ ${DEFAULT_VARS["DMS_DEBUG"]} == 1 ]]; then + msg="${c_green} * ${notification_msg}${c_reset}" + fi + ;; + 'started') + msg="${c_green} ${notification_msg}${c_reset}" + ;; + 'warn') + msg="${c_brown} * ${notification_msg}${c_reset}" + ;; + 'err') + msg="${c_red} * ${notification_msg}${c_reset}" + ;; 'fatal') - msg="${c_bold} >>>> ${notification_msg} <<<<${c_reset}" + msg="${c_red}Error: ${notification_msg}${c_reset}" ;; *) msg="" ;; esac - [[ ! -z "${msg}" ]] && echo -e "${msg}" + case "${notification_format}" in + 'n') + options="-ne" + ;; + *) + options="-e" + ;; + esac + + [[ ! -z "${msg}" ]] && echo $options "${msg}" } function defunc() { - notify 'fatal' "Please fix the failures. Exiting ..." + notify 'fatal' "Please fix your configuration. Exiting..." exit 1 } +function display_startup_daemon() { + $1 &>/dev/null + res=$? + if [[ ${DEFAULT_VARS["DMS_DEBUG"]} == 1 ]]; then + if [ $res = 0 ]; then + notify 'started' " [ OK ]" + else + echo "false" + notify 'err' " [ FAILED ]" + fi + fi + return $res +} + # !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! # ! CARE --> DON'T CHANGE, except you know exactly what you are doing # !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! @@ -247,7 +294,7 @@ function defunc() { # Description: Place functions for initial check of container sanity ########################################################################## function check() { - notify 'taskgrp' 'Checking configuration sanity:' + notify 'taskgrp' 'Checking configuration' for _func in "${FUNCS_CHECK[@]}";do $_func [ $? != 0 ] && defunc @@ -257,11 +304,11 @@ function check() { function _check_hostname() { notify "task" "Check that hostname/domainname is provided (no default docker hostname) [$FUNCNAME]" - if ( ! echo $(hostname) | grep -E '^(\S+[.]\S+)$' ); then + if ( ! echo $(hostname) | grep -E '^(\S+[.]\S+)$' > /dev/null ); then notify 'err' "Setting hostname/domainname is required" return 1 else - notify 'inf' "Hostname has been set" + notify 'inf' "Hostname has been set to $(hostname)" return 0 fi } @@ -281,11 +328,9 @@ function _check_environment_variables() { # Description: Place functions for functional configurations here ########################################################################## function setup() { - notify 'taskgrp' 'Setting up the Container:' - + notify 'taskgrp' 'Configuring mail server' for _func in "${FUNCS_SETUP[@]}";do $_func - [ $? != 0 ] && defunc done } @@ -295,14 +340,14 @@ function _setup_default_vars() { for var in ${!DEFAULT_VARS[@]}; do echo "export $var=${DEFAULT_VARS[$var]}" >> /root/.bashrc [ $? != 0 ] && notify 'err' "Unable to set $var=${DEFAULT_VARS[$var]}" && return 1 - notify 'inf' "$var=${DEFAULT_VARS[$var]} set" + notify 'inf' "Set $var=${DEFAULT_VARS[$var]}" done } function _setup_mailname() { notify 'task' 'Setting up Mailname' - echo "Creating /etc/mailname" + notify 'inf' "Creating /etc/mailname" echo $(hostname -d) > /etc/mailname } @@ -321,7 +366,7 @@ function _setup_dovecot() { # Enable Managesieve service by setting the symlink # to the configuration file Dovecot will actually find if [ "$ENABLE_MANAGESIEVE" = 1 ]; then - echo "Sieve management enabled" + notify 'inf' "Sieve management enabled" mv /etc/dovecot/protocols.d/managesieved.protocol.disab /etc/dovecot/protocols.d/managesieved.protocol fi } @@ -331,9 +376,9 @@ function _setup_dovecot_local_user() { echo -n > /etc/postfix/vmailbox echo -n > /etc/dovecot/userdb if [ -f /tmp/docker-mailserver/postfix-accounts.cf -a "$ENABLE_LDAP" != 1 ]; then - echo "Checking file line endings" + notify 'inf' "Checking file line endings" sed -i 's/\r//g' /tmp/docker-mailserver/postfix-accounts.cf - echo "Regenerating postfix 'vmailbox' and 'virtual' for given users" + notify 'inf' "Regenerating postfix user list" echo "# WARNING: this file is auto-generated. Modify config/postfix-accounts.cf to edit user list." > /etc/postfix/vmailbox # Checking that /tmp/docker-mailserver/postfix-accounts.cf ends with a newline @@ -353,7 +398,7 @@ function _setup_dovecot_local_user() { user=$(echo ${login} | cut -d @ -f1) domain=$(echo ${login} | cut -d @ -f2) # Let's go! - echo "user '${user}' for domain '${domain}' with password '********'" + notify 'inf' "user '${user}' for domain '${domain}' with password '********'" echo "${login} ${domain}/${user}/" >> /etc/postfix/vmailbox # User database for dovecot has the following format: # user:password:uid:gid:(gecos):home:(shell):extra_fields @@ -374,7 +419,7 @@ function _setup_dovecot_local_user() { echo ${domain} >> /tmp/vhost.tmp done < /tmp/docker-mailserver/postfix-accounts.cf else - echo "==> Warning: 'config/docker-mailserver/postfix-accounts.cf' is not provided. No mail account created." + notify 'warn' "'config/docker-mailserver/postfix-accounts.cf' is not provided. No mail account created." fi } @@ -388,7 +433,7 @@ function _setup_ldap() { /etc/postfix/ldap-${i}.cf done - echo "Configuring dovecot LDAP authentification" + notify 'inf' "Configuring dovecot LDAP authentification" sed -i -e 's|^hosts.*|hosts = '${LDAP_SERVER_HOST:="mail.domain.com"}'|g' \ -e 's|^base.*|base = '${LDAP_SEARCH_BASE:="ou=people,dc=domain,dc=com"}'|g' \ -e 's|^dn\s*=.*|dn = '${LDAP_BIND_DN:="cn=admin,dc=domain,dc=com"}'|g' \ @@ -398,39 +443,43 @@ function _setup_ldap() { # Add domainname to vhost. echo $(hostname -d) >> /tmp/vhost.tmp - echo "Enabling dovecot LDAP authentification" + notify 'inf' "Enabling dovecot LDAP authentification" sed -i -e '/\!include auth-ldap\.conf\.ext/s/^#//' /etc/dovecot/conf.d/10-auth.conf sed -i -e '/\!include auth-passwdfile\.inc/s/^/#/' /etc/dovecot/conf.d/10-auth.conf - echo "Configuring LDAP" + notify 'inf' "Configuring LDAP" [ -f /etc/postfix/ldap-users.cf ] && \ postconf -e "virtual_mailbox_maps = ldap:/etc/postfix/ldap-users.cf" || \ - echo '==> Warning: /etc/postfix/ldap-user.cf not found' + notify 'inf' "==> Warning: /etc/postfix/ldap-user.cf not found" [ -f /etc/postfix/ldap-aliases.cf -a -f /etc/postfix/ldap-groups.cf ] && \ postconf -e "virtual_alias_maps = ldap:/etc/postfix/ldap-aliases.cf, ldap:/etc/postfix/ldap-groups.cf" || \ - echo '==> Warning: /etc/postfix/ldap-aliases.cf or /etc/postfix/ldap-groups.cf not found' + notify 'inf' "==> Warning: /etc/postfix/ldap-aliases.cf or /etc/postfix/ldap-groups.cf not found" + return 0 +} + +function _setup_postfix_sasl() { [ ! -f /etc/postfix/sasl/smtpd.conf ] && cat > /etc/postfix/sasl/smtpd.conf << EOF pwcheck_method: saslauthd mech_list: plain login EOF -return 0 + return 0 } function _setup_saslauthd() { - notify 'task' 'Setting up Saslauthd' + notify 'task' "Setting up Saslauthd" - echo "Configuring Cyrus SASL" + notify 'inf' "Configuring Cyrus SASL" # checking env vars and setting defaults [ -z $SASLAUTHD_MECHANISMS ] && SASLAUTHD_MECHANISMS=pam - [ -z $SASLAUTHD_LDAP_SEARCH_BASE ] && SASLAUTHD_MECHANISMS=pam + [ "$SASLAUTHD_MECHANISMS" = ldap -a -z $SASLAUTHD_LDAP_SEARCH_BASE ] && SASLAUTHD_MECHANISMS=pam [ -z $SASLAUTHD_LDAP_SERVER ] && SASLAUTHD_LDAP_SERVER=localhost [ -z $SASLAUTHD_LDAP_FILTER ] && SASLAUTHD_LDAP_FILTER='(&(uniqueIdentifier=%u)(mailEnabled=TRUE))' ([ -z $SASLAUTHD_LDAP_SSL ] || [ $SASLAUTHD_LDAP_SSL == 0 ]) && SASLAUTHD_LDAP_PROTO='ldap://' || SASLAUTHD_LDAP_PROTO='ldaps://' if [ ! -f /etc/saslauthd.conf ]; then - echo "Creating /etc/saslauthd.conf" + notify 'inf' "Creating /etc/saslauthd.conf" cat > /etc/saslauthd.conf << EOF ldap_servers: ${SASLAUTHD_LDAP_PROTO}${SASLAUTHD_LDAP_SERVER} @@ -456,6 +505,13 @@ EOF -e "s|^MECHANISMS=.*|MECHANISMS="\"$SASLAUTHD_MECHANISMS\""|g" \ -e "s|^MECH_OPTIONS=.*|MECH_OPTIONS="\"$SASLAUTHD_MECH_OPTIONS\""|g" \ /etc/default/saslauthd + + if [ "$SASLAUTHD_MECHANISMS" = rimap ]; then + sed -i \ + -e 's|^OPTIONS="|OPTIONS="-r |g' \ + /etc/default/saslauthd + fi + sed -i \ -e "/smtpd_sasl_path =.*/d" \ -e "/smtpd_sasl_type =.*/d" \ @@ -481,11 +537,11 @@ function _setup_postfix_aliases() { test "$uname" != "$domain" && echo ${domain} >> /tmp/vhost.tmp done < /tmp/docker-mailserver/postfix-virtual.cf else - echo "==> Warning: 'config/postfix-virtual.cf' is not provided. No mail alias/forward created." + notify 'inf' "Warning 'config/postfix-virtual.cf' is not provided. No mail alias/forward created." fi if [ -f /tmp/docker-mailserver/postfix-regexp.cf ]; then # Copying regexp alias file - echo "Adding regexp alias file postfix-regexp.cf" + notify 'inf' "Adding regexp alias file postfix-regexp.cf" cp -f /tmp/docker-mailserver/postfix-regexp.cf /etc/postfix/regexp sed -i -e '/^virtual_alias_maps/{ s/ regexp:.*// @@ -497,18 +553,18 @@ function _setup_postfix_aliases() { function _setup_dkim() { notify 'task' 'Setting up DKIM' + mkdir -p /etc/opendkim && touch /etc/opendkim/SigningTable + # Check if keys are already available if [ -e "/tmp/docker-mailserver/opendkim/KeyTable" ]; then - mkdir -p /etc/opendkim cp -a /tmp/docker-mailserver/opendkim/* /etc/opendkim/ - echo "DKIM keys added for: `ls -C /etc/opendkim/keys/`" - echo "Changing permissions on /etc/opendkim" - # chown entire directory + notify 'inf' "DKIM keys added for: `ls -C /etc/opendkim/keys/`" + notify 'inf' "Changing permissions on /etc/opendkim" chown -R opendkim:opendkim /etc/opendkim/ # And make sure permissions are right chmod -R 0700 /etc/opendkim/keys/ else - echo "No DKIM key provided. Check the documentation to find how to get your keys." + notify 'warn' "No DKIM key provided. Check the documentation to find how to get your keys." fi } @@ -528,7 +584,7 @@ function _setup_ssl() { KEY="key" fi if [ -n "$KEY" ]; then - echo "Adding $(hostname) SSL certificate" + notify 'inf' "Adding $(hostname) SSL certificate" # Postfix configuration sed -i -r 's~smtpd_tls_cert_file=/etc/ssl/certs/ssl-cert-snakeoil.pem~smtpd_tls_cert_file=/etc/letsencrypt/live/'$(hostname)'/fullchain.pem~g' /etc/postfix/main.cf @@ -538,14 +594,14 @@ function _setup_ssl() { sed -i -e 's~ssl_cert = Warning: 'SASL_PASSWD' is not provided. /etc/postfix/sasl_passwd not created." + notify 'inf' "Warning: 'SASL_PASSWD' is not provided. /etc/postfix/sasl_passwd not created." fi } function _setup_postfix_relay_amazon_ses() { notify 'task' 'Setting up Postfix Relay Amazon SES' - - if [ ! -z "$AWS_SES_HOST" -a ! -z "$AWS_SES_USERPASS" ]; then - if [ -z "$AWS_SES_PORT" ];then - AWS_SES_PORT=25 - fi - echo "Setting up outgoing email via AWS SES host $AWS_SES_HOST:$AWS_SES_PORT" - echo "[$AWS_SES_HOST]:$AWS_SES_PORT $AWS_SES_USERPASS" >> /etc/postfix/sasl_passwd - postconf -e \ - "relayhost = [$AWS_SES_HOST]:$AWS_SES_PORT" \ - "smtp_sasl_auth_enable = yes" \ - "smtp_sasl_security_options = noanonymous" \ - "smtp_sasl_password_maps = texthash:/etc/postfix/sasl_passwd" \ - "smtp_use_tls = yes" \ - "smtp_tls_security_level = encrypt" \ - "smtp_tls_note_starttls_offer = yes" \ - "smtp_tls_CAfile = /etc/ssl/certs/ca-certificates.crt" + if [ -z "$AWS_SES_PORT" ];then + AWS_SES_PORT=25 fi + notify 'inf' "Setting up outgoing email via AWS SES host $AWS_SES_HOST:$AWS_SES_PORT" + echo "[$AWS_SES_HOST]:$AWS_SES_PORT $AWS_SES_USERPASS" >> /etc/postfix/sasl_passwd + postconf -e \ + "relayhost = [$AWS_SES_HOST]:$AWS_SES_PORT" \ + "smtp_sasl_auth_enable = yes" \ + "smtp_sasl_security_options = noanonymous" \ + "smtp_sasl_password_maps = texthash:/etc/postfix/sasl_passwd" \ + "smtp_use_tls = yes" \ + "smtp_tls_security_level = encrypt" \ + "smtp_tls_note_starttls_offer = yes" \ + "smtp_tls_CAfile = /etc/ssl/certs/ca-certificates.crt" } - function _setup_security_stack() { - notify 'task' 'Setting up Security Stack' + notify 'task' "Setting up Security Stack" - echo "Configuring Spamassassin" - SA_TAG=${SA_TAG:="2.0"} && sed -i -r 's/^\$sa_tag_level_deflt (.*);/\$sa_tag_level_deflt = '$SA_TAG';/g' /etc/amavis/conf.d/20-debian_defaults - SA_TAG2=${SA_TAG2:="6.31"} && sed -i -r 's/^\$sa_tag2_level_deflt (.*);/\$sa_tag2_level_deflt = '$SA_TAG2';/g' /etc/amavis/conf.d/20-debian_defaults - SA_KILL=${SA_KILL:="6.31"} && sed -i -r 's/^\$sa_kill_level_deflt (.*);/\$sa_kill_level_deflt = '$SA_KILL';/g' /etc/amavis/conf.d/20-debian_defaults - test -e /tmp/docker-mailserver/spamassassin-rules.cf && cp /tmp/docker-mailserver/spamassassin-rules.cf /etc/spamassassin/ + # recreate auto-generated file + dms_amavis_file="/etc/amavis/conf.d/51-dms_auto_generated" + echo "# WARNING: this file is auto-generated." > $dms_amavis_file + echo "use strict;" >> $dms_amavis_file + # Spamassassin + if [ "$ENABLE_SPAMASSASSIN" = 0 ]; then + notify 'warn' "Spamassassin is disabled. You can enable it with 'ENABLE_SPAMASSASSIN=1'" + echo "@bypass_spam_checks_maps = (1);" >> $dms_amavis_file + elif [ "$ENABLE_SPAMASSASSIN" = 1 ]; then + notify 'inf' "Enabling and configuring spamassassin" + SA_TAG=${SA_TAG:="2.0"} && sed -i -r 's/^\$sa_tag_level_deflt (.*);/\$sa_tag_level_deflt = '$SA_TAG';/g' /etc/amavis/conf.d/20-debian_defaults + SA_TAG2=${SA_TAG2:="6.31"} && sed -i -r 's/^\$sa_tag2_level_deflt (.*);/\$sa_tag2_level_deflt = '$SA_TAG2';/g' /etc/amavis/conf.d/20-debian_defaults + SA_KILL=${SA_KILL:="6.31"} && sed -i -r 's/^\$sa_kill_level_deflt (.*);/\$sa_kill_level_deflt = '$SA_KILL';/g' /etc/amavis/conf.d/20-debian_defaults + test -e /tmp/docker-mailserver/spamassassin-rules.cf && cp /tmp/docker-mailserver/spamassassin-rules.cf /etc/spamassassin/ + fi + + # Clamav + if [ "$ENABLE_CLAMAV" = 0 ]; then + notify 'warn' "Clamav is disabled. You can enable it with 'ENABLE_CLAMAV=1'" + echo "@bypass_virus_checks_maps = (1);" >> $dms_amavis_file + elif [ "$ENABLE_CLAMAV" = 1 ]; then + notify 'inf' "Enabling clamav" + fi + + echo "1; # ensure a defined return" >> $dms_amavis_file + + + # Fail2ban if [ "$ENABLE_FAIL2BAN" = 1 ]; then - echo "Fail2ban enabled" + notify 'inf' "Fail2ban enabled" test -e /tmp/docker-mailserver/fail2ban-jail.cf && cp /tmp/docker-mailserver/fail2ban-jail.cf /etc/fail2ban/jail.local else # Disable logrotate config for fail2ban if not enabled @@ -750,7 +821,7 @@ function _setup_elk_forwarder() { ELK_PORT=${ELK_PORT:="5044"} ELK_HOST=${ELK_HOST:="elk"} - echo "Enabling log forwarding to ELK ($ELK_HOST:$ELK_PORT)" + notify 'inf' "Enabling log forwarding to ELK ($ELK_HOST:$ELK_PORT)" cat /etc/filebeat/filebeat.yml.tmpl \ | sed "s@\$ELK_HOST@$ELK_HOST@g" \ | sed "s@\$ELK_PORT@$ELK_PORT@g" \ @@ -767,7 +838,7 @@ function _setup_elk_forwarder() { # Description: Place functions for temporary workarounds and fixes here ########################################################################## function fix() { - notify 'taskgrg' "Starting to fix:" + notify 'taskgrg' "Post-configuration checks..." for _func in "${FUNCS_FIX[@]}";do $_func [ $? != 0 ] && defunc @@ -779,10 +850,11 @@ function _fix_var_mail_permissions() { # Fix permissions, but skip this if 3 levels deep the user id is already set if [ `find /var/mail -maxdepth 3 -a \( \! -user 5000 -o \! -group 5000 \) | grep -c .` != 0 ]; then + notify 'inf' "Fixing /var/mail permissions" chown -R 5000:5000 /var/mail - echo "/var/mail permissions fixed" else - echo "Permissions in /var/mail look OK" + notify 'inf' "Permissions in /var/mail look OK" + return 0 fi } ########################################################################## @@ -796,11 +868,11 @@ function _fix_var_mail_permissions() { # Description: Place functions that do not fit in the sections above here ########################################################################## function misc() { - notify 'taskgrp' 'Starting Misc:' + notify 'taskgrp' 'Starting Misc' for _func in "${FUNCS_MISC[@]}";do $_func - [ $? != 0 ] && defunc + [ $? != 0 ] && defunc done } @@ -809,19 +881,19 @@ function _misc_save_states() { # directory statedir=/var/mail-state if [ "$ONE_DIR" = 1 -a -d $statedir ]; then - echo "Consolidating all state onto $statedir" + notify 'inf' "Consolidating all state onto $statedir" for d in /var/spool/postfix /var/lib/postfix /var/lib/amavis /var/lib/clamav /var/lib/spamassasin /var/lib/fail2ban; do dest=$statedir/`echo $d | sed -e 's/.var.//; s/\//-/g'` if [ -d $dest ]; then - echo " Destination $dest exists, linking $d to it" + notify 'inf' " Destination $dest exists, linking $d to it" rm -rf $d ln -s $dest $d elif [ -d $d ]; then - echo " Moving contents of $d to $dest:" `ls $d` + notify 'inf' " Moving contents of $d to $dest:" `ls $d` mv $d $dest ln -s $dest $d else - echo " Linking $d to $dest" + notify 'inf' " Linking $d to $dest" mkdir -p $dest ln -s $dest $d fi @@ -834,65 +906,66 @@ function _misc_save_states() { # >> Start Daemons ########################################################################## function start_daemons() { - notify 'taskgrp' 'Starting Daemons' + notify 'taskgrp' 'Starting mail server' for _func in "${DAEMONS_START[@]}";do $_func - [ $? != 0 ] && defunc + [ $? != 0 ] && defunc done } -function _start_daemons_sys() { - notify 'task' 'Starting Cron' - cron +function _start_daemons_cron() { + notify 'task' 'Starting cron' 'n' + display_startup_daemon "cron" +} - notify 'task' 'Starting rsyslog' - /etc/init.d/rsyslog start +function _start_daemons_rsyslog() { + notify 'task' 'Starting rsyslog' 'n' + display_startup_daemon "/etc/init.d/rsyslog start" } function _start_daemons_saslauthd() { - notify "task" "Starting saslauthd" - /etc/init.d/saslauthd start + notify 'task' 'Starting saslauthd' 'n' + display_startup_daemon "/etc/init.d/saslauthd start" } function _start_daemons_fail2ban() { - notify 'task' 'Starting fail2ban' + notify 'task' 'Starting fail2ban' 'n' touch /var/log/auth.log # Delete fail2ban.sock that probably was left here after container restart if [ -e /var/run/fail2ban/fail2ban.sock ]; then rm /var/run/fail2ban/fail2ban.sock fi - /etc/init.d/fail2ban start + display_startup_daemon "/etc/init.d/fail2ban start" } function _start_daemons_opendkim() { - notify 'task' 'Starting opendkim' - /etc/init.d/opendkim start + notify 'task' 'Starting opendkim' 'n' + display_startup_daemon "/etc/init.d/opendkim start" } function _start_daemons_opendmarc() { - notify 'task' 'Starting opendmarc' - /etc/init.d/opendmarc start + notify 'task' 'Starting opendmarc' 'n' + display_startup_daemon "/etc/init.d/opendmarc start" } function _start_daemons_postfix() { - notify 'task' 'Starting postfix' - /etc/init.d/postfix start + notify 'task' 'Starting postfix' 'n' + display_startup_daemon "/etc/init.d/postfix start" } function _start_daemons_dovecot() { # Here we are starting sasl and imap, not pop3 because it's disabled by default - notify 'task' "Starting dovecot services" - /usr/sbin/dovecot -c /etc/dovecot/dovecot.conf + notify 'task' 'Starting dovecot services' 'n' + display_startup_daemon "/usr/sbin/dovecot -c /etc/dovecot/dovecot.conf" if [ "$ENABLE_POP3" = 1 ]; then - echo "Starting POP3 services" + notify 'task' 'Starting pop3 services' 'n' mv /etc/dovecot/protocols.d/pop3d.protocol.disab /etc/dovecot/protocols.d/pop3d.protocol - /usr/sbin/dovecot reload + display_startup_daemon "/usr/sbin/dovecot reload" fi if [ -f /tmp/docker-mailserver/dovecot.cf ]; then - echo 'Adding file "dovecot.cf" to the Dovecot configuration' cp /tmp/docker-mailserver/dovecot.cf /etc/dovecot/local.conf /usr/sbin/dovecot reload fi @@ -908,25 +981,24 @@ function _start_daemons_dovecot() { } function _start_daemons_filebeat() { - notify 'task' 'Starting FileBeat' - /etc/init.d/filebeat start + notify 'task' 'Starting filebeat' 'n' + display_startup_daemon "/etc/init.d/filebeat start" } function _start_daemons_fetchmail() { - notify 'task' 'Starting fetchmail' + notify 'task' 'Starting fetchmail' 'n' /usr/local/bin/setup-fetchmail - echo "Fetchmail enabled" - /etc/init.d/fetchmail start + display_startup_daemon "/etc/init.d/fetchmail start" } function _start_daemons_clamav() { - notify 'task' "Starting clamav" - /etc/init.d/clamav-daemon start + notify 'task' 'Starting clamav' 'n' + display_startup_daemon "/etc/init.d/clamav-daemon start" } function _start_daemons_amavis() { - notify 'task' 'Starting Daemon Amavis' - /etc/init.d/amavis start + notify 'task' 'Starting amavis' 'n' + display_startup_daemon "/etc/init.d/amavis start" # @TODO fix: on integration test of mail_with_ldap amavis fails because of: # Starting amavisd: The value of variable $myhostname is "ldap", but should have been @@ -935,7 +1007,7 @@ function _start_daemons_amavis() { # in /etc/amavis/conf.d/05-node_id, or fix what uname(3) provides as a host's # network name! - # > temporary workaround to passe integration test + # > temporary workaround to pass integration test return 0 } ########################################################################## @@ -951,6 +1023,24 @@ function _start_daemons_amavis() { # !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! # >> +if [[ ${DEFAULT_VARS["DMS_DEBUG"]} == 1 ]]; then +notify 'taskgrp' "" +notify 'taskgrp' "#" +notify 'taskgrp' "#" +notify 'taskgrp' "# ENV" +notify 'taskgrp' "#" +notify 'taskgrp' "#" +notify 'taskgrp' "" +printenv +fi + +notify 'taskgrp' "" +notify 'taskgrp' "#" +notify 'taskgrp' "#" +notify 'taskgrp' "# docker-mailserver" +notify 'taskgrp' "#" +notify 'taskgrp' "#" +notify 'taskgrp' "" register_functions @@ -960,7 +1050,14 @@ fix misc start_daemons -tail -f /var/log/mail/mail.log +notify 'taskgrp' "" +notify 'taskgrp' "#" +notify 'taskgrp' "# $(hostname) is up and running" +notify 'taskgrp' "#" +notify 'taskgrp' "" + + +tail -fn 0 /var/log/mail/mail.log # !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! diff --git a/test/tests.bats b/test/tests.bats index db3b684d..6344d315 100644 --- a/test/tests.bats +++ b/test/tests.bats @@ -56,13 +56,8 @@ [ "$status" -eq 0 ] } -@test "checking process: amavis (amavis disabled by DISABLE_AMAVIS)" { - run docker exec mail_disabled_amavis /bin/bash -c "ps aux --forest | grep -v grep | grep '/usr/sbin/amavisd-new'" - [ "$status" -eq 1 ] -} - -@test "checking process: clamav (clamav disabled by DISABLE_CLAMAV)" { - run docker exec mail_disabled_clamav /bin/bash -c "ps aux --forest | grep -v grep | grep '/usr/sbin/clamd'" +@test "checking process: clamav (clamav disabled by ENABLED_CLAMAV=0)" { + run docker exec mail_disabled_clamav_spamassassin /bin/bash -c "ps aux --forest | grep -v grep | grep '/usr/sbin/clamd'" [ "$status" -eq 1 ] } @@ -71,6 +66,11 @@ [ "$status" -eq 0 ] } +@test "checking process: saslauthd (saslauthd server enabled)" { + run docker exec mail_with_imap /bin/bash -c "ps aux --forest | grep -v grep | grep '/usr/sbin/saslauthd'" + [ "$status" -eq 0 ] +} + # # imap # @@ -274,6 +274,16 @@ # spamassassin # +@test "checking spamassassin: should be listed in amavis when enabled" { + run docker exec mail /bin/sh -c "grep -i 'ANTI-SPAM-SA code' /var/log/mail/mail.log | grep 'NOT loaded'" + [ "$status" -eq 1 ] +} + +@test "checking spamassassin: should not be listed in amavis when disabled" { + run docker exec mail_disabled_clamav_spamassassin /bin/sh -c "grep -i 'ANTI-SPAM-SA code' /var/log/mail/mail.log | grep 'NOT loaded'" + [ "$status" -eq 0 ] +} + @test "checking spamassassin: docker env variables are set correctly (default)" { run docker exec mail_pop3 /bin/sh -c "grep '\$sa_tag_level_deflt' /etc/amavis/conf.d/20-debian_defaults | grep '= 2.0'" [ "$status" -eq 0 ] @@ -292,6 +302,25 @@ [ "$status" -eq 0 ] } +# +# clamav +# + +@test "checking clamav: should be listed in amavis when enabled" { + run docker exec mail grep -i 'Found secondary av scanner ClamAV-clamscan' /var/log/mail/mail.log + [ "$status" -eq 0 ] +} + +@test "checking clamav: should not be listed in amavis when disabled" { + run docker exec mail_disabled_clamav_spamassassin grep -i 'Found secondary av scanner ClamAV-clamscan' /var/log/mail/mail.log + [ "$status" -eq 1 ] +} + +@test "checking clamav: should not be called when disabled" { + run docker exec mail_disabled_clamav_spamassassin grep -i 'connect to /var/run/clamav/clamd.ctl failed' /var/log/mail/mail.log + [ "$status" -eq 1 ] +} + # # opendkim # @@ -402,13 +431,8 @@ [ "$status" -eq 0 ] } -@test "checking ssl: lets-encrypt-x1-cross-signed.pem is installed" { - run docker exec mail grep 'BEGIN CERTIFICATE' /etc/ssl/certs/lets-encrypt-x1-cross-signed.pem - [ "$status" -eq 0 ] -} - -@test "checking ssl: lets-encrypt-x2-cross-signed.pem is installed" { - run docker exec mail grep 'BEGIN CERTIFICATE' /etc/ssl/certs/lets-encrypt-x2-cross-signed.pem +@test "checking ssl: lets-encrypt-x3-cross-signed.pem is installed" { + run docker exec mail grep 'BEGIN CERTIFICATE' /etc/ssl/certs/lets-encrypt-x3-cross-signed.pem [ "$status" -eq 0 ] } @@ -483,7 +507,7 @@ # Getting mail_fail2ban container IP MAIL_FAIL2BAN_IP=$(docker inspect --format '{{ .NetworkSettings.IPAddress }}' mail_fail2ban) - # Create a container which will send wront authentications and should banned + # Create a container which will send wrong authentications and should banned docker run --name fail-auth-mailer -e MAIL_FAIL2BAN_IP=$MAIL_FAIL2BAN_IP -v "$(pwd)/test":/tmp/docker-mailserver-test -d $(docker inspect --format '{{ .Config.Image }}' mail) tail -f /var/log/faillog docker exec fail-auth-mailer /bin/sh -c 'nc $MAIL_FAIL2BAN_IP 25 < /tmp/docker-mailserver-test/auth/smtp-auth-login-wrong.txt' @@ -546,7 +570,7 @@ } @test "checking amavis: VIRUSMAILS_DELETE_DELAY override works as expected" { - run docker run -ti --rm -e VIRUSMAILS_DELETE_DELAY=2 `docker inspect --format '{{ .Config.Image }}' mail` /bin/bash -c 'echo $VIRUSMAILS_DELETE_DELAY | grep 2' + run docker run -ti --rm -e VIRUSMAILS_DELETE_DELAY=2 `docker inspect --format '{{ .Config.Image }}' mail` /bin/bash -c 'echo $VIRUSMAILS_DELETE_DELAY | grep 2' [ "$status" -eq 0 ] } @@ -577,6 +601,8 @@ [ "$status" -eq 1 ] run docker exec mail grep -i 'permission denied' /var/log/mail/mail.log [ "$status" -eq 1 ] + run docker exec mail grep -i '(!)connect' /var/log/mail/mail.log + [ "$status" -eq 1 ] run docker exec mail_pop3 grep 'non-null host address bits in' /var/log/mail/mail.log [ "$status" -eq 1 ] run docker exec mail_pop3 grep ': error:' /var/log/mail/mail.log @@ -632,19 +658,60 @@ @test "checking accounts: user3 should have been added to /tmp/docker-mailserver/postfix-accounts.cf" { docker exec mail /bin/sh -c "addmailuser user3@domain.tld mypassword" - run docker exec mail /bin/sh -c "grep user3@domain.tld -i /tmp/docker-mailserver/postfix-accounts.cf" + run docker exec mail /bin/sh -c "grep '^user3@domain\.tld|' -i /tmp/docker-mailserver/postfix-accounts.cf" [ "$status" -eq 0 ] [ ! -z "$output" ] } -@test "checking accounts: user3 should have been removed from /tmp/docker-mailserver/postfix-accounts.cf" { +@test "checking accounts: auser3 should have been added to /tmp/docker-mailserver/postfix-accounts.cf" { + docker exec mail /bin/sh -c "addmailuser auser3@domain.tld mypassword" + + run docker exec mail /bin/sh -c "grep '^auser3@domain\.tld|' -i /tmp/docker-mailserver/postfix-accounts.cf" + [ "$status" -eq 0 ] + [ ! -z "$output" ] +} + +@test "checking accounts: a.ser3 should have been added to /tmp/docker-mailserver/postfix-accounts.cf" { + docker exec mail /bin/sh -c "addmailuser a.ser3@domain.tld mypassword" + + run docker exec mail /bin/sh -c "grep '^a\.ser3@domain\.tld|' -i /tmp/docker-mailserver/postfix-accounts.cf" + [ "$status" -eq 0 ] + [ ! -z "$output" ] +} + +@test "checking accounts: user3 should have been removed from /tmp/docker-mailserver/postfix-accounts.cf but not auser3" { docker exec mail /bin/sh -c "delmailuser user3@domain.tld" - run docker exec mail /bin/sh -c "grep user3@domain.tld -i /tmp/docker-mailserver/postfix-accounts.cf" + run docker exec mail /bin/sh -c "grep '^user3@domain\.tld' -i /tmp/docker-mailserver/postfix-accounts.cf" [ "$status" -eq 1 ] [ -z "$output" ] + + run docker exec mail /bin/sh -c "grep '^auser3@domain\.tld' -i /tmp/docker-mailserver/postfix-accounts.cf" + [ "$status" -eq 0 ] + [ ! -z "$output" ] } +@test "checking user updating password for user in /tmp/docker-mailserver/postfix-accounts.cf" { + docker exec mail /bin/sh -c "addmailuser user4@domain.tld mypassword" + + initialpass=$(run docker exec mail /bin/sh -c "grep '^user4@domain\.tld' -i /tmp/docker-mailserver/postfix-accounts.cf") + sleep 2 + docker exec mail /bin/sh -c "updatemailuser user4@domain.tld mynewpassword" + sleep 2 + changepass=$(run docker exec mail /bin/sh -c "grep '^user4@domain\.tld' -i /tmp/docker-mailserver/postfix-accounts.cf") + + if [ initialpass != changepass ]; then + status="0" + else + status="1" + fi + + docker exec mail /bin/sh -c "delmailuser auser3@domain.tld" + + [ "$status" -eq 0 ] +} + + @test "checking accounts: listmailuser" { run docker exec mail /bin/sh -c "listmailuser | head -n 1" [ "$status" -eq 0 ] @@ -663,6 +730,7 @@ run docker run --rm \ -v "$(pwd)/test/config/without-accounts/":/tmp/docker-mailserver/ \ `docker inspect --format '{{ .Config.Image }}' mail` /bin/sh -c 'addmailuser user3@domain.tld mypassword' + [ "$status" -eq 0 ] run docker run --rm \ -v "$(pwd)/test/config/without-accounts/":/tmp/docker-mailserver/ \ `docker inspect --format '{{ .Config.Image }}' mail` /bin/sh -c 'grep user3@domain.tld -i /tmp/docker-mailserver/postfix-accounts.cf' @@ -730,6 +798,17 @@ run ./setup.sh -c mail email list [ "$status" -eq 0 ] } +@test "checking setup.sh: setup.sh email update" { + initialpass=$(cat ./config/postfix-accounts.cf | grep lorem@impsum.org | awk -F '|' '{print $2}') + run ./setup.sh -c mail email update lorem@impsum.org consectetur + updatepass=$(cat ./config/postfix-accounts.cf | grep lorem@impsum.org | awk -F '|' '{print $2}') + if [ initialpass != changepass ]; then + status="0" + else + status="1" + fi + [ "$status" -eq 0 ] +} @test "checking setup.sh: setup.sh email del" { run ./setup.sh -c mail email del lorem@impsum.org [ "$status" -eq 0 ] @@ -808,6 +887,27 @@ [ "$status" -eq 0 ] } +# +# RIMAP +# + +# dovecot +@test "checking dovecot: ldap rimap connection and authentication works" { + run docker exec mail_with_imap /bin/sh -c "nc -w 1 0.0.0.0 143 < /tmp/docker-mailserver-test/auth/imap-auth.txt" + [ "$status" -eq 0 ] +} + +# saslauthd +@test "checking saslauthd: sasl rimap authentication works" { + run docker exec mail_with_imap bash -c "testsaslauthd -u user1@localhost.localdomain -p mypassword" + [ "$status" -eq 0 ] +} + +@test "checking saslauthd: rimap smtp authentication" { + run docker exec mail_with_imap /bin/sh -c "nc -w 5 0.0.0.0 25 < /tmp/docker-mailserver-test/auth/smtp-auth-login.txt | grep 'Authentication successful'" + [ "$status" -eq 0 ] +} + # # Postfix VIRTUAL_TRANSPORT # @@ -820,4 +920,4 @@ run docker exec mail_lmtp_ip /bin/sh -c "grep 'postfix/lmtp' /var/log/mail/mail.log | grep 'status=sent' | grep ' Saved)' | wc -l" [ "$status" -eq 0 ] [ "$output" -eq 6 ] -} +