changes from tomav#1599 without start-mailserver.sh

included all changes from the work on refactoring all scripts, but excluded one big script to make merging easier; replaced mapfile with read
This commit is contained in:
Georg Lauterbach 2020-09-05 16:19:12 +02:00
parent 14aa0cdcc3
commit bf679a5504
No known key found for this signature in database
GPG key ID: 386D76E7AD496709
11 changed files with 650 additions and 356 deletions

View file

@ -1,23 +1,41 @@
branches:
except:
- donttestme
- donttestme
language: bash
sudo: required
env:
global:
- HADOLINT_VERSION=1.17.1
- SHELLCHECK_VERSION=latest
addons:
apt:
packages:
- xz-utils
services:
- docker
- docker
before_install:
- sudo curl -L https://github.com/hadolint/hadolint/releases/download/v$HADOLINT_VERSION/hadolint-$(uname -s)-$(uname -m) -o /usr/local/bin/hadolint
- sudo chmod +rx /usr/local/bin/hadolint
- sudo curl -L https://github.com/hadolint/hadolint/releases/download/v$HADOLINT_VERSION/hadolint-$(uname -s)-$(uname -m) -o /usr/local/bin/hadolint
- sudo chmod +rx /usr/local/bin/hadolint
- sudo wget -qO- "https://github.com/koalaman/shellcheck/releases/download/${SHELLCHECK_VERSION}/shellcheck-${SHELLCHECK_VERSION}.linux.x86_64.tar.xz" | tar -xJv
- sudo cp "shellcheck-${SHELLCHECK_VERSION}/shellcheck" /usr/bin/
install:
- make lint
- travis_retry travis_wait make build
- make lint
- travis_retry travis_wait make build
script:
- make generate-accounts run generate-accounts-after-run fixtures tests
- make shellcheck
- make generate-accounts run generate-accounts-after-run fixtures tests
after_script:
- make clean
- make clean
notifications:
slack:
secure: TTo1z9nbZCWcIdfPwypubNa3y+pwvfgDGlzEVAGEuK7uuIpmEoAcAUNSSPTnbewDGHnDl8t/ml93MtvP+a+IVuAKytMqF39PHyoZO7aUl9J62V+G75OmnyGjXGJm40pQosCS6LzqoRRYXotl9+fwH568Kf4ifXCrMZX1d+ir7Ww=

View file

@ -2,18 +2,22 @@
`docker-mailserver` is OpenSource. That means that you can contribute on enhancements, bug fixing or improving the documentation in the Wiki.
## Open an issue
## Issues & PRs
### 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.
Please start the mail server with env `DMS_DEBUG=1` and paste the output into the issue.
## Pull Requests
### 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
``` TXT
├── config # User: personal configurations
├── target # Developer: default server configuration, used when building the image
└── test # Developer: integration tests to check that everything keeps working
```
#### Submit a Pull-Request
@ -35,3 +39,171 @@ The development workflow is the following:
- When changed are validated, your branch is merged into `master`
- `master` is automatically tested on Travis
- Docker builds a new `latest` image
## Coding Style
### Bash and Shell
When refactoring, writing or altering Script, that is Shell and Bash scripts, in any way, adhere to these rules:
1. **Adjust your style of coding to the style that is already present**! Even if you do not like it, this is due to consistency. Look up the GNU coding style guide. There was a lot of work involved in making these scripts consistent.
2. **Use `shellcheck` to check your scripts**! Your contributions are checked by TravisCI with shellcheck.
3. There is a **`.editorconfig`** file. Make your IDE use it or adhere to it manually!
4. It's okay to use `/bin/bash` instead of `/bin/sh`. You can alternatively use `/usr/bin/env bash`.
5. `setup.sh` provides a good starting point to look for.
6. When appropriate, use the `set` builtin. We recommend `set -euEo pipefail` (very strong) or `set -uE` (weaker).
#### Styling rules
##### initial description
When writing a script, provide the version and the script's task like so:
``` BASH
#!/usr/bin/env bash
# version 0.1.0
#
# <TASK DESCRIPTION> -> cut this off
# to make it not longer than approx.
# 60 cols.
```
We use [semantic versioning](https://semver.org/) - so do you.
##### if-else-statements
``` BASH
if <CONDITION1>
then
<CODE TO RUN>
elif <CONDITION2>
<CODE TO TUN>
else
<CODE TO RUN>
fi
# when using braces, use double braces!
if [[ <CONDITION1> ]] && [[ <CONDITION2> ]]
then
<CODE TO RUN>
fi
# remember you do not need "" when using [[ ]]
if [[ -f $FILE ]] # is fine
then
<CODE TO RUN>
fi
# equality checks with numbers - use -eq/-ne/-lt/-ge, not != or ==
if [[ $VAR -ne <NUMBER> ]] && [[ $SOME_VAR -eq 6 ]] || [[ $SOME_VAR -lt 42 ]]
then
<CODE TO RUN>
elif [[ $SOME_VAR -ge 242 ]]
then
<CODE TO RUN>
fi
```
##### variables
Variables are always uppercase.
``` BASH
# good
local VAR="good"
# bad
var="bad"
```
##### braces
We use braces in the following way:
``` BASH
# when it's clear and unambiguous,
# you do not have to use braces,
# but you might, see shellcheck SC2248
$VAR
# or
${VAR}
# when the variable is used
# in a bigger context
echo "/some/dir/${VAR}/to/destination/"
```
##### loops
Like `if-else`, loops look like this
``` BASH
for / while <LOOP CONDITION>
do
<CODE TO RUN>
done
```
##### functions
It's always nice to see the use of functions. Not only as it's more C-style, but it also provides a clear structure. If scripts are small, this is unnecessary, but if they become larger, please consider using functions. When doing so, provide `function _main()`. When using functions, they are **always** at the top of the script!
``` BASH
function _<name_underscored_and_lowercase>()
{
<CODE TO RUN>
# variables that can be local should be local
local _<LOCAL_VARIABLE_NAME>
}
```
##### error tracing
A construct to trace error in your scripts looks like this:
``` BASH
set -euxEo pipefail
trap '_report_err $_ $LINENO $?' ERR
function _report_err()
{
echo "ERROR occurred :: source (hint) $1 ; line $2 ; exit code $3 ;;" >&2
<CODE TO RUN AFTERWARDS>
}
```
Please use it like this (copy-paste) to make errors streamlined. Remember: Remove `set -x` in the end. This of debugging purposes only.
##### comments and descriptiveness
Comments should be kept minimal and only describe non-obvious matters, i.e. not what the code does. Comments should start lowercase as most of them are not sentences. Make the code **self-descriptive** by using meaningful names! Make comments not longer than approximately 60 columns, then wrap the line.
A negative example:
``` BASH
# adds one to the first argument
# and print it to stdout
function _add_one()
{
# save the first variable
local FIRST=$1
# add one here
local RESULT=$(( _FIRST + 1 ))
# print it to stdout
echo "$_RESULT"
}
```
A positive example:
``` BASH
# writes result to stdout
function _add_one()
{
echo $(( $1 + 1 ))
}

View file

@ -1,9 +1,14 @@
SHELL = /bin/bash
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 run generate-accounts-after-run fixtures tests clean
no-build: backup generate-accounts run generate-accounts-after-run fixtures tests clean
complete_test: lint build generate-accounts run generate-accounts-after-run fixtures tests
build:
docker build \
@ -12,22 +17,24 @@ build:
-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; \
# 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 ;\
@ if [ ! -d testconfig.bak ]; then\
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
docker run --rm -e MAIL_USER=user2@otherdomain.tld -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
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
@ 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
@ docker run --rm -e MAIL_USER=user2@otherdomain.tld -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
@ 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
run:
# Run containers
# run containers
-@ echo "Sleeping $(SLEEP) after each container"
docker run --rm -d --name mail \
-v "`pwd`/test/config":/tmp/docker-mailserver \
-v "`pwd`/test/test-files":/tmp/docker-mailserver-test:ro \
@ -50,14 +57,14 @@ run:
-e PERMIT_DOCKER=host \
-e DMS_DEBUG=0 \
-h mail.my-domain.com -t $(NAME)
sleep 15
-@ sleep $(SLEEP)
docker run --rm -d --name mail_smtponly_without_config \
-e SMTP_ONLY=1 \
-e ENABLE_LDAP=1 \
-e PERMIT_DOCKER=network \
-e OVERRIDE_HOSTNAME=mail.mydomain.com \
-t $(NAME)
sleep 15
-@ sleep $(SLEEP)
docker run --rm -d --name mail_override_hostname \
-v "`pwd`/test/config":/tmp/docker-mailserver \
-v "`pwd`/test/test-files":/tmp/docker-mailserver-test:ro \
@ -67,7 +74,7 @@ run:
-e OVERRIDE_HOSTNAME=mail.my-domain.com \
-h unknown.domain.tld \
-t $(NAME)
sleep 15
-@ sleep $(SLEEP)
docker run --rm -d --name mail_domainname \
-v "`pwd`/test/config":/tmp/docker-mailserver \
-v "`pwd`/test/test-files":/tmp/docker-mailserver-test:ro \
@ -77,7 +84,7 @@ run:
-e DOMAINNAME=my-domain.com \
-h unknown.domain.tld \
-t $(NAME)
sleep 15
-@ sleep $(SLEEP)
docker run --rm -d --name mail_srs_domainname \
-v "`pwd`/test/config":/tmp/docker-mailserver \
-v "`pwd`/test/test-files":/tmp/docker-mailserver-test:ro \
@ -88,7 +95,7 @@ run:
-e DOMAINNAME=my-domain.com \
-h unknown.domain.tld \
-t $(NAME)
sleep 15
-@ sleep $(SLEEP)
docker run --rm -d --name mail_disabled_clamav_spamassassin \
-v "`pwd`/test/config":/tmp/docker-mailserver \
-v "`pwd`/test/test-files":/tmp/docker-mailserver-test:ro \
@ -96,19 +103,19 @@ run:
-e ENABLE_SPAMASSASSIN=0 \
-e DMS_DEBUG=0 \
-h mail.my-domain.com -t $(NAME)
sleep 15
-@ sleep $(SLEEP)
generate-accounts-after-run:
docker run --rm -e MAIL_USER=added@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
docker exec mail addmailuser pass@localhost.localdomain 'may be \a `p^a.*ssword'
sleep 10
@ docker run --rm -e MAIL_USER=added@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
@ docker exec mail addmailuser pass@localhost.localdomain 'may be \a `p^a.*ssword'
@ sleep $(SLEEP)
fixtures:
# Setup sieve
# setup sieve
docker cp "`pwd`/test/config/sieve/dovecot.sieve" mail:/var/mail/localhost.localdomain/user1/.dovecot.sieve
sleep 30
# Sending test mails
sleep $(SLEEP)
sleep $(SLEEP)
# sending test mails
docker exec mail /bin/sh -c "nc 0.0.0.0 25 < /tmp/docker-mailserver-test/email-templates/amavis-spam.txt"
docker exec mail /bin/sh -c "nc 0.0.0.0 25 < /tmp/docker-mailserver-test/email-templates/amavis-virus.txt"
docker exec mail /bin/sh -c "nc 0.0.0.0 25 < /tmp/docker-mailserver-test/email-templates/existing-alias-external.txt"
@ -126,35 +133,44 @@ fixtures:
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-user1.txt"
docker exec mail /bin/sh -c "sendmail root < /tmp/docker-mailserver-test/email-templates/root-email.txt"
# postfix virtual transport lmtp
# postfix virtual transport lmtp
docker exec mail_override_hostname /bin/sh -c "nc 0.0.0.0 25 < /tmp/docker-mailserver-test/email-templates/existing-user1.txt"
# Wait for mails to be analyzed
# wait for mails to be analyzed
sleep 80
tests:
# Start tests
./test/bats/bin/bats test/*.bats
.PHONY: ALWAYS_RUN
test/%.bats: ALWAYS_RUN
./test/bats/bin/bats $@
lint:
# List files which name starts with 'Dockerfile'
# eg. Dockerfile, Dockerfile.build, etc.
git ls-files --exclude='Dockerfile*' --ignored | xargs --max-lines=1 hadolint
# List files which name starts with 'Dockerfile'
# eg. Dockerfile, Dockerfile.build, etc.
-@ git ls-files --exclude='Dockerfile*' --ignored | xargs --max-lines=1 hadolint
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\
# 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\
-@ if [ -d testconfig.bak ]; then\
sudo rm -rf test/config ;\
mv testconfig.bak test/config ;\
fi
-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
-@ 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
shellcheck:
@ echo -e "Testing shell / bash scripts with shellcheck\n"
@ shellcheck --version
@ echo ''
# currently without `start-mailserver` as this is to be merged separately
@ if find -iname "*.sh" -not -path "./test/*" -not -path "./target/docker-configomat/*" -not -wholename ./target/start-mailserver.sh -exec shellcheck -S style -Cauto -e SC2250,SC2154,SC2248 -W 50 {} \; | grep .; then\
echo -e "\nError" ;\
exit 1 ;\
else\
echo -e '\nSuccess' ;\
fi

View file

@ -1,11 +1,26 @@
# docker-mailserver
[![Build Status](https://travis-ci.org/tomav/docker-mailserver.svg?branch=master)](https://travis-ci.org/tomav/docker-mailserver) [![Docker Pulls](https://img.shields.io/docker/pulls/tvial/docker-mailserver.svg)](https://hub.docker.com/r/tvial/docker-mailserver/) [![Docker layers](https://images.microbadger.com/badges/image/tvial/docker-mailserver.svg)](https://microbadger.com/images/tvial/docker-mailserver) [![Github Stars](https://img.shields.io/github/stars/tomav/docker-mailserver.svg?label=github%20%E2%98%85)](https://github.com/tomav/docker-mailserver/) [![Github Stars](https://img.shields.io/github/contributors/tomav/docker-mailserver.svg)](https://github.com/tomav/docker-mailserver/) [![Github Forks](https://img.shields.io/github/forks/tomav/docker-mailserver.svg?label=github%20forks)](https://github.com/tomav/docker-mailserver/) [![Gitter](https://img.shields.io/gitter/room/tomav/docker-mailserver.svg)](https://gitter.im/tomav/docker-mailserver)
[![Build Status][build_status]][build_status::travis] [![Docker Pulls][docker_pulls]][docker_hub_pulls::hub] [![Docker layers][layers]][layers_outer::badger] [![Github Stars][gh_stars]][repo] [![Contributors][contributors]][repo] [![Github Forks][forks]][repo] [![Gitter][shields::gitter]][gitter]
[build_status]: https://travis-ci.org/tomav/docker-mailserver.svg?branch=master
[build_status::travis]: https://travis-ci.org/tomav/docker-mailserver
[docker_pulls]: https://img.shields.io/docker/pulls/tvial/docker-mailserver.svg
[docker_hub_pulls::hub]: https://hub.docker.com/r/tvial/docker-mailserver/
[layers]: https://images.microbadger.com/badges/image/tvial/docker-mailserver.svg
[layers_outer::badger]: https://microbadger.com/images/tvial/docker-mailserver
[gh_stars]: https://img.shields.io/github/stars/tomav/docker-mailserver.svg?label=github%20%E2%98%85
[repo]: https://github.com/tomav/docker-mailserver/
[contributors]: https://img.shields.io/github/contributors/tomav/docker-mailserver.svg
[forks]: https://img.shields.io/github/forks/tomav/docker-mailserver.svg?label=github%20forks
[shields::gitter]: https://img.shields.io/gitter/room/tomav/docker-mailserver.svg
[gitter]: https://gitter.im/tomav/docker-mailserver
A fullstack but simple mail server (smtp, imap, antispam, antivirus...).
Only configuration files, no SQL database. Keep it simple and versioned.
Easy to deploy and upgrade.
Why I created this image: [Simple mail server with Docker](http://tvi.al/simple-mail-server-with-docker/)
## ANNOUNCEMENT
At this point we have merged the next branch based on Debian Buster into master.
@ -19,7 +34,7 @@ The following possibly breaking changes are known:
If you want to stick to the old version a while longer, either switch to stable or to a specific version.
If you run into problems, please raise issues and ask for help. Don't forget to provide details.
Includes:
## Includes
- [Postfix](http://www.postfix.org) with smtp or ldap auth
- [Dovecot](https://www.dovecot.org) for sasl, imap (and optional pop3) with ssl support, with ldap auth, sieve and [quotas](https://github.com/tomav/docker-mailserver/wiki/Configure-Accounts#mailbox-quota)
@ -42,9 +57,9 @@ Includes:
- Plus addressing (a.k.a. [extension delimiters](http://www.postfix.org/postconf.5.html#recipient_delimiter))
works out of the box: email for `you+extension@example.com` go to `you@example.com`
Why I created this image: [Simple mail server with Docker](http://tvi.al/simple-mail-server-with-docker/)
## Issues & Contributing
Before you open an issue, please have a look this `README`, the [Wiki](https://github.com/tomav/docker-mailserver/wiki/) and Postfix/Dovecot documentation.
Before you open an issue, please have a look this `README`, the [Wiki](https://github.com/tomav/docker-mailserver/wiki/) and Postfix/Dovecot documentation. If you'd like to contribute, read [`CONTRIBUTING.md`](./CONTRIBUTING.md) thoroughly.
## Requirements
@ -67,7 +82,8 @@ Minimum:
Download the docker-compose.yml, the .env and the setup.sh files:
``` SH
``` BASH
curl -o setup.sh https://raw.githubusercontent.com/tomav/docker-mailserver/master/setup.sh; chmod a+x ./setup.sh
curl -o docker-compose.yml https://raw.githubusercontent.com/tomav/docker-mailserver/master/docker-compose.yml.dist
@ -89,7 +105,7 @@ curl -o env-mailserver https://raw.githubusercontent.com/tomav/docker-mailserver
**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).
### Starting the Container
### Start the Container
``` BASH
docker-compose up -d mail
@ -794,7 +810,7 @@ you to replace both instead of just the envelope sender.
#### Default Relay Host
#### DEFAULT_RELAY_HOST
##### DEFAULT_RELAY_HOST
- **empty** => don't set default relayhost setting in main.cf
- default host and port to relay all mail through.
@ -803,22 +819,22 @@ you to replace both instead of just the envelope sender.
#### Multi-domain Relay Hosts
#### RELAY_HOST
##### RELAY_HOST
- **empty** => don't configure relay host
- default host to relay mail through
#### RELAY_PORT
##### RELAY_PORT
- **empty** => 25
- default port to relay mail through
#### RELAY_USER
##### RELAY_USER
- **empty** => no default
- default relay username (if no specific entry exists in postfix-sasl-password.cf)
#### RELAY_PASSWORD
##### RELAY_PASSWORD
- **empty** => no default
- password for default relay user

View file

@ -169,7 +169,7 @@ function _docker_container()
fi
}
function main()
function _main()
{
if [[ -n $(command -v docker) ]]
then
@ -180,7 +180,7 @@ function main()
_check_root
else
echo "No supported Container Runtime Interface detected."
exit 1
exit 10
fi
INFO=$($CRI ps \
@ -316,5 +316,5 @@ function main()
esac
}
main "$@"
_main "$@"
_unset_vars

View file

@ -1,203 +1,237 @@
#!/bin/bash
# version 0.1.0
#
# <INSERT TASK HERE>
# shellcheck source=/dev/null
. /usr/local/bin/helper_functions.sh
# create date for log output
log_date=$(date +"%Y-%m-%d %H:%M:%S ")
echo "${log_date} Start check-for-changes script."
LOG_DATE=$(date +"%Y-%m-%d %H:%M:%S ")
echo "$LOG_DATE Start check-for-changes script."
# change directory
cd /tmp/docker-mailserver
# ? Checks ------------------------------------------------
cd /tmp/docker-mailserver || exit 1
# Check postfix-accounts.cf exist else break
if [ ! -f postfix-accounts.cf ]; then
echo "${log_date} postfix-accounts.cf is missing! This should not run! Exit!"
exit
if [[ ! -f postfix-accounts.cf ]]
then
echo "$LOG_DATE postfix-accounts.cf is missing! This should not run! Exit!"
exit
fi
# Verify checksum file exists; must be prepared by start-mailserver.sh
if [ ! -f $CHKSUM_FILE ]; then
echo "${log_date} ${CHKSUM_FILE} is missing! Start script failed? Exit!"
exit
if [[ ! -f $CHKSUM_FILE ]]
then
echo "$LOG_DATE $CHKSUM_FILE is missing! Start script failed? Exit!"
exit
fi
# ? Actual script begins ----------------------------------
# Determine postmaster address, duplicated from start-mailserver.sh
# This script previously didn't work when POSTMASTER_ADDRESS was empty
if [[ -n "${OVERRIDE_HOSTNAME}" ]]; then
DOMAINNAME=$(echo "${OVERRIDE_HOSTNAME}" | sed s/[^.]*.//)
if [[ -n $OVERRIDE_HOSTNAME ]]
then
DOMAINNAME="${OVERRIDE_HOSTNAME#*.}"
else
DOMAINNAME="$(hostname -d)"
fi
PM_ADDRESS="${POSTMASTER_ADDRESS:=postmaster@${DOMAINNAME}}"
echo "${log_date} Using postmaster address ${PM_ADDRESS}"
# Wait to make sure server is up before we start
echo "$LOG_DATE Using postmaster address $PM_ADDRESS"
sleep 10
# Run forever
while true; do
while true
do
LOG_DATE=$(date +"%Y-%m-%d %H:%M:%S ")
# recreate logdate
log_date=$(date +"%Y-%m-%d %H:%M:%S ")
# get chksum and check it, no need to lock config yet
_monitored_files_checksums >"${CHKSUM_FILE}.new"
# Get chksum and check it, no need to lock config yet
monitored_files_checksums >"$CHKSUM_FILE.new"
if ! cmp --silent -- "$CHKSUM_FILE" "$CHKSUM_FILE.new"
then
echo "${LOG_DATE} Change detected"
changed=$(grep -Fxvf "$CHKSUM_FILE" "$CHKSUM_FILE.new" | sed 's/^[^ ]\+ //')
mv "$CHKSUM_FILE.new" "$CHKSUM_FILE"
if ! cmp --silent -- "$CHKSUM_FILE" "$CHKSUM_FILE.new"; then
echo "${log_date} Change detected"
changed=$(grep -Fxvf "$CHKSUM_FILE" "$CHKSUM_FILE.new" | sed 's/^[^ ]\+ //')
mv "$CHKSUM_FILE.new" "$CHKSUM_FILE"
# Bug alert! This overwrites the alias set by start-mailserver.sh
# Take care that changes in one script are propagated to the other
# Bug alert! This overwrites the alias set by start-mailserver.sh
# Take care that changes in one script are propagated to the other
# Also note that changes are performed in place and are not atomic
# We should fix that and write to temporary files, stop, swap and start
# ! NEEDS FIX -----------------------------------------
# TODO FIX --------------------------------------------
# ! NEEDS EXTENSIONS ----------------------------
# TODO Perform updates below conditionally too --
# Also note that changes are performed in place and are not atomic
# We should fix that and write to temporary files, stop, swap and start
# Lock configuration while working
(
flock -e 200
# Lock configuration while working
# Not fixing indentation yet to reduce diff (fix later in separate commit)
(
flock -e 200
for file in $changed; do
case $file in
/etc/letsencrypt/acme.json)
for certdomain in $SSL_DOMAIN $HOSTNAME $DOMAINNAME; do
if extractCertsFromAcmeJson "$certdomain"; then
break
fi
for file in $changed
do
case $file in
/etc/letsencrypt/acme.json)
for certdomain in $SSL_DOMAIN $HOSTNAME $DOMAINNAME
do
if _extract_certs_from_acme "$certdomain"
then
break
fi
done
;;
* ) notify 'err' 'file not found for certificate in check_for_changes.sh' ;;
esac
done
;;
#TODO: Perform updates below conditionally as well.
esac
done
#regen postix aliases.
echo "root: ${PM_ADDRESS}" > /etc/aliases
if [ -f /tmp/docker-mailserver/postfix-aliases.cf ]; then
cat /tmp/docker-mailserver/postfix-aliases.cf>>/etc/aliases
fi
postalias /etc/aliases
# regenerate postix aliases
echo "root: ${PM_ADDRESS}" >/etc/aliases
if [[ -f /tmp/docker-mailserver/postfix-aliases.cf ]]
then
cat /tmp/docker-mailserver/postfix-aliases.cf >>/etc/aliases
fi
postalias /etc/aliases
#regen postfix accounts.
echo -n > /etc/postfix/vmailbox
echo -n > /etc/dovecot/userdb
# regenerate postfix accounts
echo -n >/etc/postfix/vmailbox
echo -n >/etc/dovecot/userdb
if [ -f /tmp/docker-mailserver/postfix-accounts.cf -a "$ENABLE_LDAP" != 1 ]; then
sed -i 's/\r//g' /tmp/docker-mailserver/postfix-accounts.cf
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
sed -i -e '$a\' /tmp/docker-mailserver/postfix-accounts.cf
chown dovecot:dovecot /etc/dovecot/userdb
chmod 640 /etc/dovecot/userdb
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
if [[ -f /tmp/docker-mailserver/postfix-accounts.cf ]] && [[ $ENABLE_LDAP -ne 1 ]]
then
sed -i 's/\r//g' /tmp/docker-mailserver/postfix-accounts.cf
echo "# WARNING: this file is auto-generated. Modify config/postfix-accounts.cf to edit user list." >/etc/postfix/vmailbox
# rebuild relay host
if [ ! -z "$RELAY_HOST" ]; then
# keep old config
echo -n > /etc/postfix/sasl_passwd
if [ ! -z "$SASL_PASSWD" ]; then
echo "$SASL_PASSWD" >> /etc/postfix/sasl_passwd
fi
# add domain-specific auth from config file
if [ -f /tmp/docker-mailserver/postfix-sasl-password.cf ]; then
(grep -v "^\s*$\|^\s*\#" /tmp/docker-mailserver/postfix-sasl-password.cf || true) | while read line; do
if ! echo "$line" | grep -q -e "\s*#"; then
echo "$line" >> /etc/postfix/sasl_passwd
fi
done
fi
# add default relay
if [ ! -z "$RELAY_USER" ] && [ ! -z "$RELAY_PASSWORD" ]; then
echo "[$RELAY_HOST]:$RELAY_PORT $RELAY_USER:$RELAY_PASSWORD" >> /etc/postfix/sasl_passwd
fi
fi
# Checking that /tmp/docker-mailserver/postfix-accounts.cf ends with a newline
# shellcheck disable=SC1003
sed -i -e '$a\' /tmp/docker-mailserver/postfix-accounts.cf
chown dovecot:dovecot /etc/dovecot/userdb
chmod 640 /etc/dovecot/userdb
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
# Creating users
# 'pass' is encrypted
# comments and empty lines are ignored
grep -v "^\s*$\|^\s*\#" /tmp/docker-mailserver/postfix-accounts.cf | while IFS=$'|' read login pass
do
# Setting variables for better readability
user=$(echo ${login} | cut -d @ -f1)
domain=$(echo ${login} | cut -d @ -f2)
# rebuild relay host
if [[ -n $RELAY_HOST ]]
then
# keep old config
echo -n >/etc/postfix/sasl_passwd
if [[ -n $SASL_PASSWD ]]
then
echo "$SASL_PASSWD" >>/etc/postfix/sasl_passwd
fi
user_attributes=""
# test if user has a defined quota
if [ -f /tmp/docker-mailserver/dovecot-quotas.cf ]; then
user_quota=($(grep "${user}@${domain}:" -i /tmp/docker-mailserver/dovecot-quotas.cf | tr ':' '\n'))
# add domain-specific auth from config file
if [[ -f /tmp/docker-mailserver/postfix-sasl-password.cf ]]
then
(grep -v "^\s*$\|^\s*\#" /tmp/docker-mailserver/postfix-sasl-password.cf || true) | while read -r line
do
if ! echo "$line" | grep -q -e "\s*#"
then
echo "$line" >>/etc/postfix/sasl_passwd
fi
done
fi
if [ ${#user_quota[@]} -eq 2 ]; then
user_attributes="${user_attributes}userdb_quota_rule=*:bytes=${user_quota[1]}"
fi
fi
# add default relay
if [[ -n "$RELAY_USER" ]] && [[ -n "$RELAY_PASSWORD" ]]
then
echo "[$RELAY_HOST]:$RELAY_PORT $RELAY_USER:$RELAY_PASSWORD" >>/etc/postfix/sasl_passwd
fi
fi
# Let's go!
echo "${login} ${domain}/${user}/" >> /etc/postfix/vmailbox
# User database for dovecot has the following format:
# user:password:uid:gid:(gecos):home:(shell):extra_fields
# Example :
# ${login}:${pass}:5000:5000::/var/mail/${domain}/${user}::userdb_mail=maildir:/var/mail/${domain}/${user}
echo "${login}:${pass}:5000:5000::/var/mail/${domain}/${user}::${user_attributes}" >> /etc/dovecot/userdb
mkdir -p /var/mail/${domain}/${user}
# creating users ; 'pass' is encrypted
# comments and empty lines are ignored
grep -v "^\s*$\|^\s*\#" /tmp/docker-mailserver/postfix-accounts.cf | while IFS=$'|' read -r login pass
do
user=$(echo "$login" | cut -d @ -f1)
domain=$(echo "$login" | cut -d @ -f2)
# Copy user provided sieve file, if present
test -e /tmp/docker-mailserver/${login}.dovecot.sieve && cp /tmp/docker-mailserver/${login}.dovecot.sieve /var/mail/${domain}/${user}/.dovecot.sieve
echo ${domain} >> /tmp/vhost.tmp
done
fi
if [ ! -z "$RELAY_HOST" ]; then
populate_relayhost_map
fi
if [ -f /etc/postfix/sasl_passwd ]; then
chown root:root /etc/postfix/sasl_passwd
chmod 0600 /etc/postfix/sasl_passwd
fi
if [ -f postfix-virtual.cf ]; then
# regen postfix aliases
echo -n > /etc/postfix/virtual
echo -n > /etc/postfix/regexp
if [ -f /tmp/docker-mailserver/postfix-virtual.cf ]; then
# Copying virtual file
cp -f /tmp/docker-mailserver/postfix-virtual.cf /etc/postfix/virtual
(grep -v "^\s*$\|^\s*\#" /tmp/docker-mailserver/postfix-virtual.cf || true) | while read from to
do
# Setting variables for better readability
uname=$(echo ${from} | cut -d @ -f1)
domain=$(echo ${from} | cut -d @ -f2)
# if they are equal it means the line looks like: "user1 other@domain.tld"
test "$uname" != "$domain" && echo ${domain} >> /tmp/vhost.tmp
done
fi
if [ -f /tmp/docker-mailserver/postfix-regexp.cf ]; then
# Copying regexp alias file
cp -f /tmp/docker-mailserver/postfix-regexp.cf /etc/postfix/regexp
sed -i -e '/^virtual_alias_maps/{
s/ regexp:.*//
s/$/ regexp:\/etc\/postfix\/regexp/
}' /etc/postfix/main.cf
fi
fi
# Set vhost
if [ -f /tmp/vhost.tmp ]; then
cat /tmp/vhost.tmp | sort | uniq > /etc/postfix/vhost && rm /tmp/vhost.tmp
fi
user_attributes=""
# test if user has a defined quota
if [[ -f /tmp/docker-mailserver/dovecot-quotas.cf ]]
then
IFS=':'
read -r -a user_quota < <(grep "${user}@${domain}:" -i /tmp/docker-mailserver/dovecot-quotas.cf)
unset IFS
# Set right new if needed
if [ `find /var/mail -maxdepth 3 -a \( \! -user 5000 -o \! -group 5000 \) | grep -c .` != 0 ]; then
chown -R 5000:5000 /var/mail
fi
[[ ${#user_quota[@]} -eq 2 ]] && user_attributes="${user_attributes}userdb_quota_rule=*:bytes=${user_quota[1]}"
fi
# Restart of the postfix
supervisorctl restart postfix
echo "$login ${domain}/${user}/" >>/etc/postfix/vmailbox
# Prevent restart of dovecot when smtp_only=1
if [ ! $SMTP_ONLY = 1 ]; then
supervisorctl restart dovecot
fi
# user database for dovecot has the following format:
# user:password:uid:gid:(gecos):home:(shell):extra_fields
# example :
# ${login}:${pass}:5000:5000::/var/mail/${domain}/${user}::userdb_mail=maildir:/var/mail/${domain}/${user}
echo "${login}:${pass}:5000:5000::/var/mail/${domain}/${user}::${user_attributes}" >>/etc/dovecot/userdb
mkdir -p "/var/mail/${domain}/${user}"
) 200<postfix-accounts.cf # end lock
fi
if [[ -e /tmp/docker-mailserver/${login}.dovecot.sieve ]]
then
cp "/tmp/docker-mailserver/${login}.dovecot.sieve" "/var/mail/${domain}/${user}/.dovecot.sieve"
fi
sleep 1
echo "$domain" >>/tmp/vhost.tmp
done
fi
[[ -n $RELAY_HOST ]] && _populate_relayhost_map
if [[ -f /etc/postfix/sasl_passwd ]]
then
chown root:root /etc/postfix/sasl_passwd
chmod 0600 /etc/postfix/sasl_passwd
fi
if [[ -f postfix-virtual.cf ]]
then
# regenerate postfix aliases
echo -n >/etc/postfix/virtual
echo -n >/etc/postfix/regexp
if [[ -f /tmp/docker-mailserver/postfix-virtual.cf ]]
then
cp -f /tmp/docker-mailserver/postfix-virtual.cf /etc/postfix/virtual
# the `to` seems to be important; don't delete it
# shellcheck disable=SC2034
(grep -v "^\s*$\|^\s*\#" /tmp/docker-mailserver/postfix-virtual.cf || true) | while read -r from to
do
uname=$(echo "$from" | cut -d @ -f1)
domain=$(echo "$from" | cut -d @ -f2)
# if they are equal it means the line looks like: "user1 other@domain.tld"
[ "$uname" != "$domain" ] && echo "$domain" >>/tmp/vhost.tmp
done
fi
if [[ -f /tmp/docker-mailserver/postfix-regexp.cf ]]
then
cp -f /tmp/docker-mailserver/postfix-regexp.cf /etc/postfix/regexp
sed -i -e '/^virtual_alias_maps/{
s/ regexp:.*//
s/$/ regexp:\/etc\/postfix\/regexp/
}' /etc/postfix/main.cf
fi
fi
if [[ -f /tmp/vhost.tmp ]]
then
# shellcheck disable=SC2002
cat /tmp/vhost.tmp | sort | uniq >/etc/postfix/vhost && rm /tmp/vhost.tmp
fi
if [[ $(find /var/mail -maxdepth 3 -a \( \! -user 5000 -o \! -group 5000 \) | grep -c .) -ne 0 ]]
then
chown -R 5000:5000 /var/mail
fi
supervisorctl restart postfix
# prevent restart of dovecot when smtp_only=1
[[ $SMTP_ONLY -ne 1 ]] && supervisorctl restart dovecot
) 200<postfix-accounts.cf # end lock
fi
sleep 1
done

View file

@ -1,5 +1,6 @@
#!/usr/bin/env bash
# fail2ban-wrapper.sh, version 0.0.1
# version 0.1.0
#
# You cannot start fail2ban in some foreground mode and
# it's more or less important that docker doesn't kill
@ -21,14 +22,12 @@ trap "/usr/bin/fail2ban-client stop" SIGINT
trap "/usr/bin/fail2ban-client stop" SIGTERM
trap "/usr/bin/fail2ban-client reload" SIGHUP
# start fail2ban
/usr/bin/fail2ban-client start
# lets give fail2ban some time to start
sleep 5
# wait until fail2ban is dead (triggered by trap)
while kill -0 "`cat /var/run/fail2ban/fail2ban.pid`"; do
while kill -0 "$(cat /var/run/fail2ban/fail2ban.pid)"
do
sleep 5
done

View file

@ -1,45 +1,64 @@
#!/bin/bash
# expects mask prefix length and the digit
function _mask_ip_digit() {
if [[ $1 -ge 8 ]]; then
# version 0.1.1
#
# Provides varous helpers.
# ? IP and CIDR -------------------------------------------
function _mask_ip_digit()
{
if [[ ${1} -ge 8 ]]
then
MASK=255
elif [[ ${1} -le 0 ]]
then
MASK=0
else
if [[ $1 -le 0 ]]; then
MASK=0
else
VALUES=('0' '128' '192' '224' '240' '248' '252' '254' '255')
MASK=${VALUES[$1]}
fi
VALUES=(0 128 192 224 240 248 252 254 255)
MASK=${VALUES[$1]}
fi
echo $(($2 & $MASK))
local DVAL=${2}
((DVAL&=MASK))
echo "$DVAL"
}
# transforms a specific ip with CIDR suffix like 1.2.3.4/16
# to subnet with cidr suffix like 1.2.0.0/16
function _sanitize_ipv4_to_subnet_cidr() {
IP=${1%%/*}
PREFIX_LENGTH=${1#*/}
# Transforms a specific IP with CIDR suffix
# like 1.2.3.4/16 to subnet with cidr suffix
# like 1.2.0.0/16.
# Assumes correct IP and subnet are provided.
function _sanitize_ipv4_to_subnet_cidr()
{
local DIGIT_PREFIX_LENGTH="${1#*/}"
# split IP by . into digits
DIGITS=(${IP//./ })
declare -a DIGITS
IFS='.' ; read -r -a DIGITS < <(echo "${1%%/*}")
unset IFS
# mask digits according to prefix length
MASKED_DIGITS=()
DIGIT_PREFIX_LENGTH="$PREFIX_LENGTH"
for DIGIT in "${DIGITS[@]}"; do
MASKED_DIGITS+=($(_mask_ip_digit $DIGIT_PREFIX_LENGTH $DIGIT))
DIGIT_PREFIX_LENGTH=$(($DIGIT_PREFIX_LENGTH - 8))
declare -a MASKED_DIGITS
for ((i = 0 ; i < 4 ; i++))
do
MASKED_DIGITS[i]=$(_mask_ip_digit "$DIGIT_PREFIX_LENGTH" "${DIGITS[i]}")
DIGIT_PREFIX_LENGTH=$((DIGIT_PREFIX_LENGTH - 8))
done
# output masked ip plus prefix length
echo ${MASKED_DIGITS[0]}.${MASKED_DIGITS[1]}.${MASKED_DIGITS[2]}.${MASKED_DIGITS[3]}/$PREFIX_LENGTH
echo "${MASKED_DIGITS[0]}.${MASKED_DIGITS[1]}.${MASKED_DIGITS[2]}.${MASKED_DIGITS[3]}/${1#*/}"
}
export -f _sanitize_ipv4_to_subnet_cidr
# extracts certificates from acme.json and returns 0 if found
function extractCertsFromAcmeJson() {
WHAT=$1
# ? ACME certs --------------------------------------------
function _extract_certs_from_acme()
{
local KEY
# shellcheck disable=SC2002
KEY=$(cat /etc/letsencrypt/acme.json | python -c "
import sys,json
acme = json.load(sys.stdin)
@ -47,10 +66,13 @@ for key, value in acme.items():
certs = value['Certificates']
for cert in certs:
if 'domain' in cert and 'key' in cert:
if 'main' in cert['domain'] and cert['domain']['main'] == '$WHAT' or 'sans' in cert['domain'] and '$WHAT' in cert['domain']['sans']:
if 'main' in cert['domain'] and cert['domain']['main'] == '$1' or 'sans' in cert['domain'] and '$1' in cert['domain']['sans']:
print cert['key']
break
")
local CERT
# shellcheck disable=SC2002
CERT=$(cat /etc/letsencrypt/acme.json | python -c "
import sys,json
acme = json.load(sys.stdin)
@ -58,26 +80,35 @@ for key, value in acme.items():
certs = value['Certificates']
for cert in certs:
if 'domain' in cert and 'certificate' in cert:
if 'main' in cert['domain'] and cert['domain']['main'] == '$WHAT' or 'sans' in cert['domain'] and '$WHAT' in cert['domain']['sans']:
if 'main' in cert['domain'] and cert['domain']['main'] == '$1' or 'sans' in cert['domain'] and '$1' in cert['domain']['sans']:
print cert['certificate']
break
")
if [[ -n "${KEY}${CERT}" ]]; then
mkdir -p /etc/letsencrypt/live/"$HOSTNAME"/
echo $KEY | base64 -d >/etc/letsencrypt/live/"$HOSTNAME"/key.pem || exit 1
echo $CERT | base64 -d >/etc/letsencrypt/live/"$HOSTNAME"/fullchain.pem || exit 1
echo "Cert found in /etc/letsencrypt/acme.json for $WHAT"
if [[ -n "${KEY}${CERT}" ]]
then
mkdir -p "/etc/letsencrypt/live/${HOSTNAME}/"
echo "$KEY" | base64 -d >/etc/letsencrypt/live/"$HOSTNAME"/key.pem || exit 1
echo "$CERT" | base64 -d >/etc/letsencrypt/live/"$HOSTNAME"/fullchain.pem || exit 1
echo "Cert found in /etc/letsencrypt/acme.json for $1"
return 0
else
return 1
fi
}
export -f _extract_certs_from_acme
# ? Notification ------------------------------------------
declare -A DEFAULT_VARS
DEFAULT_VARS["DMS_DEBUG"]="${DMS_DEBUG:="0"}"
function notify () {
function notify()
{
c_red="\e[0;31m"
c_green="\e[0;32m"
c_brown="\e[0;33m"
@ -91,95 +122,95 @@ function notify () {
msg=""
case "${notification_type}" in
'taskgrp')
msg="${c_bold}${notification_msg}${c_reset}"
;;
'task')
if [[ ${DEFAULT_VARS["DMS_DEBUG"]} == 1 ]]; then
'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
'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_red}Error: ${notification_msg}${c_reset}"
;;
*)
msg=""
;;
'started' ) msg="${c_green} ${notification_msg}${c_reset}" ;;
'warn' ) msg="${c_brown} Warning ${notification_msg}${c_reset}" ;;
'err' ) msg="${c_blue} Error ${notification_msg}${c_reset}" ;;
'fatal' ) msg="${c_red} Fatal Error: ${notification_msg}${c_reset}" ;;
* ) msg="" ;;
esac
case "${notification_format}" in
'n')
options="-ne"
;;
*)
options="-e"
;;
'n' ) options="-ne" ;;
* ) options="-e" ;;
esac
[[ ! -z "${msg}" ]] && echo $options "${msg}"
[[ -n "${msg}" ]] && echo $options "${msg}"
}
export -f notify
# ? Relay Host Map ----------------------------------------
# setup /etc/postfix/relayhost_map
# --
# @domain1.com [smtp.mailgun.org]:587
# @domain2.com [smtp.mailgun.org]:587
# @domain3.com [smtp.mailgun.org]:587
function populate_relayhost_map() {
function _populate_relayhost_map()
{
echo -n > /etc/postfix/relayhost_map
chown root:root /etc/postfix/relayhost_map
chmod 0600 /etc/postfix/relayhost_map
if [ -f /tmp/docker-mailserver/postfix-relaymap.cf ]; then
if [[ -f /tmp/docker-mailserver/postfix-relaymap.cf ]]
then
notify 'inf' "Adding relay mappings from postfix-relaymap.cf"
# Keep lines which are not a comment *and* have a destination.
sed -n '/^\s*[^#[:space:]]\S*\s\+\S/p' /tmp/docker-mailserver/postfix-relaymap.cf \
>> /etc/postfix/relayhost_map
# keep lines which are not a comment *and* have a destination.
sed -n '/^\s*[^#[:space:]]\S*\s\+\S/p' /tmp/docker-mailserver/postfix-relaymap.cf >> /etc/postfix/relayhost_map
fi
{
# Note: Won't detect domains when lhs has spaces (but who does that?!).
# note: won't detect domains when lhs has spaces (but who does that?!)
sed -n '/^\s*[^#[:space:]]/ s/^[^@|]*@\([^|]\+\)|.*$/\1/p' /tmp/docker-mailserver/postfix-accounts.cf
[ -f /tmp/docker-mailserver/postfix-virtual.cf ] &&
sed -n '/^\s*[^#[:space:]]/ s/^\s*[^@[:space:]]*@\(\S\+\)\s.*/\1/p' /tmp/docker-mailserver/postfix-virtual.cf
} | while read domain; do
if ! grep -q -e "^@${domain}\b" /etc/postfix/relayhost_map &&
! grep -qs -e "^\s*@${domain}\s*$" /tmp/docker-mailserver/postfix-relaymap.cf; then
# Domain not already present *and* not ignored.
[ -f /tmp/docker-mailserver/postfix-virtual.cf ] && sed -n '/^\s*[^#[:space:]]/ s/^\s*[^@[:space:]]*@\(\S\+\)\s.*/\1/p' /tmp/docker-mailserver/postfix-virtual.cf
} | while read -r domain
do
# domain not already present *and* not ignored
if ! grep -q -e "^@${domain}\b" /etc/postfix/relayhost_map && ! grep -qs -e "^\s*@${domain}\s*$" /tmp/docker-mailserver/postfix-relaymap.cf
then
notify 'inf' "Adding relay mapping for ${domain}"
echo "@${domain} [$RELAY_HOST]:$RELAY_PORT" >> /etc/postfix/relayhost_map
fi
done
}
export -f _populate_relayhost_map
# File storing the checksums of the monitored files.
# ? File checksums ----------------------------------------
# file storing the checksums of the monitored files.
# shellcheck disable=SC2034
CHKSUM_FILE=/tmp/docker-mailserver-config-chksum
# Compute checksums of monitored files.
function monitored_files_checksums() {
function _monitored_files_checksums()
{
(
cd /tmp/docker-mailserver
# (2>/dev/null to ignore warnings about files that don't exist)
cd /tmp/docker-mailserver || exit 1
exec sha512sum 2>/dev/null -- \
postfix-accounts.cf \
postfix-virtual.cf \
postfix-aliases.cf \
dovecot-quotas.cf \
/etc/letsencrypt/acme.json \
"/etc/letsencrypt/live/$HOSTNAME/key.pem" \
"/etc/letsencrypt/live/$HOSTNAME/fullchain.pem"
postfix-accounts.cf \
postfix-virtual.cf \
postfix-aliases.cf \
dovecot-quotas.cf \
/etc/letsencrypt/acme.json \
"/etc/letsencrypt/live/$HOSTNAME/key.pem" \
"/etc/letsencrypt/live/$HOSTNAME/fullchain.pem"
)
return 0
}
export -f _monitored_files_checksums

View file

@ -1,5 +1,6 @@
#!/usr/bin/env bash
# postfix-wrapper.sh, version 0.1.0
# version 0.1.0
#
# You cannot start postfix in some foreground mode and
# it's more or less important that docker doesn't kill
@ -21,14 +22,11 @@ trap "service postfix stop" SIGINT
trap "service postfix stop" SIGTERM
trap "service postfix reload" SIGHUP
# start postfix
service postfix start
# lets give postfix some time to start
sleep 5
# wait until postfix is dead (triggered by trap)
while kill -0 "`cat /var/spool/postfix/pid/master.pid`"; do
while kill -0 "$(cat /var/spool/postfix/pid/master.pid)"
do
sleep 5
done

View file

@ -1,43 +1,52 @@
#!/usr/bin/env bash
# postsrsd-wrapper.sh, version 0.2.2
if [ -n "$SRS_DOMAINNAME" ]; then
domain_name="$SRS_DOMAINNAME"
elif [ -n "$OVERRIDE_HOSTNAME" ]; then
domain_name="${OVERRIDE_HOSTNAME#*.}"
elif [ -n "$DOMAINNAME" ]; then
domain_name="$DOMAINNAME"
else
domain_name=$(hostname -d)
fi
# version 0.1.0
sed -i -e "s/localdomain/${domain_name}/g" /etc/default/postsrsd
postsrsd_secret_file='/etc/postsrsd.secret'
postsrsd_state_dir='/var/mail-state/etc-postsrsd'
postsrsd_state_secret_file="${postsrsd_state_dir}/postsrsd.secret"
generate_secret() {
( umask 0077
dd if=/dev/urandom bs=24 count=1 2>/dev/null | base64 -w0 > "$1" )
function generate_secret()
{
( umask 0077 ; dd if=/dev/urandom bs=24 count=1 2>/dev/null | base64 -w0 > "$1" )
}
if [ -n "$SRS_SECRET" ]; then
( umask 0077
echo "$SRS_SECRET" | tr ',' '\n' > "$postsrsd_secret_file" )
if [[ -n $SRS_DOMAINNAME ]]
then
NEW_DOMAIN_NAME="$SRS_DOMAINNAME"
elif [[ -n $OVERRIDE_HOSTNAME ]]
then
NEW_DOMAIN_NAME="${OVERRIDE_HOSTNAME#*.}"
elif [[ -n $DOMAINNAME ]]
then
NEW_DOMAIN_NAME="$DOMAINNAME"
else
if [ "$ONE_DIR" = 1 ]; then
if [ ! -f "$postsrsd_state_secret_file" ]; then
install -d -m 0775 "$postsrsd_state_dir"
generate_secret "$postsrsd_state_secret_file"
NEW_DOMAIN_NAME=$(hostname -d)
fi
sed -i -e "s/localdomain/${NEW_DOMAIN_NAME}/g" /etc/default/postsrsd
POSTSRSD_SECRET_FILE='/etc/postsrsd.secret'
POSTSRSD_STATE_DIR='/var/mail-state/etc-postsrsd'
POSTSRSD_STATE_SECRET_FILE="${POSTSRSD_STATE_DIR}/postsrsd.secret"
if [[ -n $SRS_SECRET ]]
then
( umask 0077 ; echo "$SRS_SECRET" | tr ',' '\n' > "$POSTSRSD_SECRET_FILE" )
else
if [[ $ONE_DIR -eq 1 ]]
then
if [[ ! -f $POSTSRSD_STATE_SECRET_FILE ]]
then
install -d -m 0775 "$POSTSRSD_STATE_DIR"
generate_secret "$POSTSRSD_STATE_SECRET_FILE"
fi
install -m 0400 "$postsrsd_state_secret_file" "$postsrsd_secret_file"
elif [ ! -f "$postsrsd_secret_file" ]; then
generate_secret "$postsrsd_secret_file"
install -m 0400 "$POSTSRSD_STATE_SECRET_FILE" "$POSTSRSD_SECRET_FILE"
elif [[ ! -f $POSTSRSD_SECRET_FILE ]]
then
generate_secret "$POSTSRSD_SECRET_FILE"
fi
fi
if [ -n "$SRS_EXCLUDE_DOMAINS" ]; then
if [[ -n $SRS_EXCLUDE_DOMAINS ]]
then
sed -i -e "s/^#\?SRS_EXCLUDE_DOMAINS=.*$/SRS_EXCLUDE_DOMAINS=$SRS_EXCLUDE_DOMAINS/g" /etc/default/postsrsd
fi

View file

@ -6,6 +6,7 @@
[supervisord]
nodaemon=true
strip_ansi=true
[program:mailserver]
startsecs=0