mirror of
https://github.com/docker-mailserver/docker-mailserver.git
synced 2024-01-19 02:48:50 +00:00
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:
parent
14aa0cdcc3
commit
bf679a5504
32
.travis.yml
32
.travis.yml
|
@ -1,23 +1,41 @@
|
||||||
branches:
|
branches:
|
||||||
except:
|
except:
|
||||||
- donttestme
|
- donttestme
|
||||||
|
|
||||||
language: bash
|
language: bash
|
||||||
|
|
||||||
sudo: required
|
sudo: required
|
||||||
|
|
||||||
env:
|
env:
|
||||||
global:
|
global:
|
||||||
- HADOLINT_VERSION=1.17.1
|
- HADOLINT_VERSION=1.17.1
|
||||||
|
- SHELLCHECK_VERSION=latest
|
||||||
|
|
||||||
|
addons:
|
||||||
|
apt:
|
||||||
|
packages:
|
||||||
|
- xz-utils
|
||||||
|
|
||||||
services:
|
services:
|
||||||
- docker
|
- docker
|
||||||
|
|
||||||
before_install:
|
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 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 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:
|
install:
|
||||||
- make lint
|
- make lint
|
||||||
- travis_retry travis_wait make build
|
- travis_retry travis_wait make build
|
||||||
|
|
||||||
script:
|
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:
|
after_script:
|
||||||
- make clean
|
- make clean
|
||||||
|
|
||||||
notifications:
|
notifications:
|
||||||
slack:
|
slack:
|
||||||
secure: TTo1z9nbZCWcIdfPwypubNa3y+pwvfgDGlzEVAGEuK7uuIpmEoAcAUNSSPTnbewDGHnDl8t/ml93MtvP+a+IVuAKytMqF39PHyoZO7aUl9J62V+G75OmnyGjXGJm40pQosCS6LzqoRRYXotl9+fwH568Kf4ifXCrMZX1d+ir7Ww=
|
secure: TTo1z9nbZCWcIdfPwypubNa3y+pwvfgDGlzEVAGEuK7uuIpmEoAcAUNSSPTnbewDGHnDl8t/ml93MtvP+a+IVuAKytMqF39PHyoZO7aUl9J62V+G75OmnyGjXGJm40pQosCS6LzqoRRYXotl9+fwH568Kf4ifXCrMZX1d+ir7Ww=
|
||||||
|
|
184
CONTRIBUTING.md
184
CONTRIBUTING.md
|
@ -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.
|
`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.
|
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
|
#### Project architecture
|
||||||
|
|
||||||
├── config # User: personal configurations
|
``` TXT
|
||||||
├── target # Developer: default server configuration, used when building the image
|
├── config # User: personal configurations
|
||||||
└── test # Developer: integration tests to check that everything keeps working
|
├── target # Developer: default server configuration, used when building the image
|
||||||
|
└── test # Developer: integration tests to check that everything keeps working
|
||||||
|
```
|
||||||
|
|
||||||
#### Submit a Pull-Request
|
#### Submit a Pull-Request
|
||||||
|
|
||||||
|
@ -35,3 +39,171 @@ The development workflow is the following:
|
||||||
- When changed are validated, your branch is merged into `master`
|
- When changed are validated, your branch is merged into `master`
|
||||||
- `master` is automatically tested on Travis
|
- `master` is automatically tested on Travis
|
||||||
- Docker builds a new `latest` image
|
- 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 ))
|
||||||
|
}
|
||||||
|
|
88
Makefile
88
Makefile
|
@ -1,9 +1,14 @@
|
||||||
|
SHELL = /bin/bash
|
||||||
|
|
||||||
NAME = tvial/docker-mailserver:testing
|
NAME = tvial/docker-mailserver:testing
|
||||||
VCS_REF := $(shell git rev-parse --short HEAD)
|
VCS_REF := $(shell git rev-parse --short HEAD)
|
||||||
VCS_VERSION := $(shell git describe --tags --contains --always)
|
VCS_VERSION := $(shell git describe --tags --contains --always)
|
||||||
|
|
||||||
|
SLEEP = 15s
|
||||||
|
|
||||||
all: build backup generate-accounts run generate-accounts-after-run fixtures tests clean
|
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
|
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:
|
build:
|
||||||
docker build \
|
docker build \
|
||||||
|
@ -12,22 +17,24 @@ build:
|
||||||
-t $(NAME) .
|
-t $(NAME) .
|
||||||
|
|
||||||
backup:
|
backup:
|
||||||
# if backup directories exist, clean hasn't been called, therefore we shouldn't overwrite it. It still contains the original content.
|
# if backup directories exist, clean hasn't been called, therefore
|
||||||
@if [ ! -d config.bak ]; then\
|
# we shouldn't overwrite it. It still contains the original content.
|
||||||
cp -rp config config.bak; \
|
@ if [ ! -d config.bak ]; then\
|
||||||
|
cp -rp config config.bak;\
|
||||||
fi
|
fi
|
||||||
@if [ ! -d testconfig.bak ]; then\
|
@ if [ ! -d testconfig.bak ]; then\
|
||||||
cp -rp test/config testconfig.bak ;\
|
cp -rp test/config testconfig.bak;\
|
||||||
fi
|
fi
|
||||||
|
|
||||||
generate-accounts:
|
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=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
|
@ 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 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
|
@ echo " # this is also a test comment, :O" >> test/config/postfix-accounts.cf
|
||||||
|
|
||||||
run:
|
run:
|
||||||
# Run containers
|
# run containers
|
||||||
|
-@ echo "Sleeping $(SLEEP) after each container"
|
||||||
docker run --rm -d --name mail \
|
docker run --rm -d --name mail \
|
||||||
-v "`pwd`/test/config":/tmp/docker-mailserver \
|
-v "`pwd`/test/config":/tmp/docker-mailserver \
|
||||||
-v "`pwd`/test/test-files":/tmp/docker-mailserver-test:ro \
|
-v "`pwd`/test/test-files":/tmp/docker-mailserver-test:ro \
|
||||||
|
@ -50,14 +57,14 @@ run:
|
||||||
-e PERMIT_DOCKER=host \
|
-e PERMIT_DOCKER=host \
|
||||||
-e DMS_DEBUG=0 \
|
-e DMS_DEBUG=0 \
|
||||||
-h mail.my-domain.com -t $(NAME)
|
-h mail.my-domain.com -t $(NAME)
|
||||||
sleep 15
|
-@ sleep $(SLEEP)
|
||||||
docker run --rm -d --name mail_smtponly_without_config \
|
docker run --rm -d --name mail_smtponly_without_config \
|
||||||
-e SMTP_ONLY=1 \
|
-e SMTP_ONLY=1 \
|
||||||
-e ENABLE_LDAP=1 \
|
-e ENABLE_LDAP=1 \
|
||||||
-e PERMIT_DOCKER=network \
|
-e PERMIT_DOCKER=network \
|
||||||
-e OVERRIDE_HOSTNAME=mail.mydomain.com \
|
-e OVERRIDE_HOSTNAME=mail.mydomain.com \
|
||||||
-t $(NAME)
|
-t $(NAME)
|
||||||
sleep 15
|
-@ sleep $(SLEEP)
|
||||||
docker run --rm -d --name mail_override_hostname \
|
docker run --rm -d --name mail_override_hostname \
|
||||||
-v "`pwd`/test/config":/tmp/docker-mailserver \
|
-v "`pwd`/test/config":/tmp/docker-mailserver \
|
||||||
-v "`pwd`/test/test-files":/tmp/docker-mailserver-test:ro \
|
-v "`pwd`/test/test-files":/tmp/docker-mailserver-test:ro \
|
||||||
|
@ -67,7 +74,7 @@ run:
|
||||||
-e OVERRIDE_HOSTNAME=mail.my-domain.com \
|
-e OVERRIDE_HOSTNAME=mail.my-domain.com \
|
||||||
-h unknown.domain.tld \
|
-h unknown.domain.tld \
|
||||||
-t $(NAME)
|
-t $(NAME)
|
||||||
sleep 15
|
-@ sleep $(SLEEP)
|
||||||
docker run --rm -d --name mail_domainname \
|
docker run --rm -d --name mail_domainname \
|
||||||
-v "`pwd`/test/config":/tmp/docker-mailserver \
|
-v "`pwd`/test/config":/tmp/docker-mailserver \
|
||||||
-v "`pwd`/test/test-files":/tmp/docker-mailserver-test:ro \
|
-v "`pwd`/test/test-files":/tmp/docker-mailserver-test:ro \
|
||||||
|
@ -77,7 +84,7 @@ run:
|
||||||
-e DOMAINNAME=my-domain.com \
|
-e DOMAINNAME=my-domain.com \
|
||||||
-h unknown.domain.tld \
|
-h unknown.domain.tld \
|
||||||
-t $(NAME)
|
-t $(NAME)
|
||||||
sleep 15
|
-@ sleep $(SLEEP)
|
||||||
docker run --rm -d --name mail_srs_domainname \
|
docker run --rm -d --name mail_srs_domainname \
|
||||||
-v "`pwd`/test/config":/tmp/docker-mailserver \
|
-v "`pwd`/test/config":/tmp/docker-mailserver \
|
||||||
-v "`pwd`/test/test-files":/tmp/docker-mailserver-test:ro \
|
-v "`pwd`/test/test-files":/tmp/docker-mailserver-test:ro \
|
||||||
|
@ -88,7 +95,7 @@ run:
|
||||||
-e DOMAINNAME=my-domain.com \
|
-e DOMAINNAME=my-domain.com \
|
||||||
-h unknown.domain.tld \
|
-h unknown.domain.tld \
|
||||||
-t $(NAME)
|
-t $(NAME)
|
||||||
sleep 15
|
-@ sleep $(SLEEP)
|
||||||
docker run --rm -d --name mail_disabled_clamav_spamassassin \
|
docker run --rm -d --name mail_disabled_clamav_spamassassin \
|
||||||
-v "`pwd`/test/config":/tmp/docker-mailserver \
|
-v "`pwd`/test/config":/tmp/docker-mailserver \
|
||||||
-v "`pwd`/test/test-files":/tmp/docker-mailserver-test:ro \
|
-v "`pwd`/test/test-files":/tmp/docker-mailserver-test:ro \
|
||||||
|
@ -96,19 +103,19 @@ run:
|
||||||
-e ENABLE_SPAMASSASSIN=0 \
|
-e ENABLE_SPAMASSASSIN=0 \
|
||||||
-e DMS_DEBUG=0 \
|
-e DMS_DEBUG=0 \
|
||||||
-h mail.my-domain.com -t $(NAME)
|
-h mail.my-domain.com -t $(NAME)
|
||||||
sleep 15
|
-@ sleep $(SLEEP)
|
||||||
|
|
||||||
generate-accounts-after-run:
|
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 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'
|
@ docker exec mail addmailuser pass@localhost.localdomain 'may be \a `p^a.*ssword'
|
||||||
|
@ sleep $(SLEEP)
|
||||||
sleep 10
|
|
||||||
|
|
||||||
fixtures:
|
fixtures:
|
||||||
# Setup sieve
|
# setup sieve
|
||||||
docker cp "`pwd`/test/config/sieve/dovecot.sieve" mail:/var/mail/localhost.localdomain/user1/.dovecot.sieve
|
docker cp "`pwd`/test/config/sieve/dovecot.sieve" mail:/var/mail/localhost.localdomain/user1/.dovecot.sieve
|
||||||
sleep 30
|
sleep $(SLEEP)
|
||||||
# Sending test mails
|
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-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/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"
|
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 /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_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"
|
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"
|
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
|
sleep 80
|
||||||
|
|
||||||
tests:
|
tests:
|
||||||
# Start tests
|
|
||||||
./test/bats/bin/bats test/*.bats
|
./test/bats/bin/bats test/*.bats
|
||||||
|
|
||||||
.PHONY: ALWAYS_RUN
|
.PHONY: ALWAYS_RUN
|
||||||
|
|
||||||
test/%.bats: ALWAYS_RUN
|
test/%.bats: ALWAYS_RUN
|
||||||
./test/bats/bin/bats $@
|
./test/bats/bin/bats $@
|
||||||
|
|
||||||
lint:
|
lint:
|
||||||
# List files which name starts with 'Dockerfile'
|
# List files which name starts with 'Dockerfile'
|
||||||
# eg. Dockerfile, Dockerfile.build, etc.
|
# eg. Dockerfile, Dockerfile.build, etc.
|
||||||
git ls-files --exclude='Dockerfile*' --ignored | xargs --max-lines=1 hadolint
|
-@ git ls-files --exclude='Dockerfile*' --ignored | xargs --max-lines=1 hadolint
|
||||||
|
|
||||||
clean:
|
clean:
|
||||||
# Remove running and stopped test containers
|
# 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
|
-@ 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\
|
||||||
@if [ -d config.bak ]; then\
|
|
||||||
rm -rf config ;\
|
rm -rf config ;\
|
||||||
mv config.bak config ;\
|
mv config.bak config ;\
|
||||||
fi
|
fi
|
||||||
@if [ -d testconfig.bak ]; then\
|
-@ if [ -d testconfig.bak ]; then\
|
||||||
sudo rm -rf test/config ;\
|
sudo rm -rf test/config ;\
|
||||||
mv testconfig.bak test/config ;\
|
mv testconfig.bak test/config ;\
|
||||||
fi
|
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
|
||||||
|
|
38
README.md
38
README.md
|
@ -1,11 +1,26 @@
|
||||||
# docker-mailserver
|
# 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...).
|
A fullstack but simple mail server (smtp, imap, antispam, antivirus...).
|
||||||
Only configuration files, no SQL database. Keep it simple and versioned.
|
Only configuration files, no SQL database. Keep it simple and versioned.
|
||||||
Easy to deploy and upgrade.
|
Easy to deploy and upgrade.
|
||||||
|
|
||||||
|
Why I created this image: [Simple mail server with Docker](http://tvi.al/simple-mail-server-with-docker/)
|
||||||
|
|
||||||
## ANNOUNCEMENT
|
## ANNOUNCEMENT
|
||||||
|
|
||||||
At this point we have merged the next branch based on Debian Buster into master.
|
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 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.
|
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
|
- [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)
|
- [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))
|
- 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`
|
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
|
## Requirements
|
||||||
|
|
||||||
|
@ -67,7 +82,8 @@ Minimum:
|
||||||
|
|
||||||
Download the docker-compose.yml, the .env and the setup.sh files:
|
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 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
|
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).
|
**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
|
``` BASH
|
||||||
docker-compose up -d mail
|
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
|
##### DEFAULT_RELAY_HOST
|
||||||
|
|
||||||
- **empty** => don't set default relayhost setting in main.cf
|
- **empty** => don't set default relayhost setting in main.cf
|
||||||
- default host and port to relay all mail through.
|
- 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
|
#### Multi-domain Relay Hosts
|
||||||
|
|
||||||
#### RELAY_HOST
|
##### RELAY_HOST
|
||||||
|
|
||||||
- **empty** => don't configure relay host
|
- **empty** => don't configure relay host
|
||||||
- default host to relay mail through
|
- default host to relay mail through
|
||||||
|
|
||||||
#### RELAY_PORT
|
##### RELAY_PORT
|
||||||
|
|
||||||
- **empty** => 25
|
- **empty** => 25
|
||||||
- default port to relay mail through
|
- default port to relay mail through
|
||||||
|
|
||||||
#### RELAY_USER
|
##### RELAY_USER
|
||||||
|
|
||||||
- **empty** => no default
|
- **empty** => no default
|
||||||
- default relay username (if no specific entry exists in postfix-sasl-password.cf)
|
- default relay username (if no specific entry exists in postfix-sasl-password.cf)
|
||||||
|
|
||||||
#### RELAY_PASSWORD
|
##### RELAY_PASSWORD
|
||||||
|
|
||||||
- **empty** => no default
|
- **empty** => no default
|
||||||
- password for default relay user
|
- password for default relay user
|
||||||
|
|
6
setup.sh
6
setup.sh
|
@ -169,7 +169,7 @@ function _docker_container()
|
||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
function main()
|
function _main()
|
||||||
{
|
{
|
||||||
if [[ -n $(command -v docker) ]]
|
if [[ -n $(command -v docker) ]]
|
||||||
then
|
then
|
||||||
|
@ -180,7 +180,7 @@ function main()
|
||||||
_check_root
|
_check_root
|
||||||
else
|
else
|
||||||
echo "No supported Container Runtime Interface detected."
|
echo "No supported Container Runtime Interface detected."
|
||||||
exit 1
|
exit 10
|
||||||
fi
|
fi
|
||||||
|
|
||||||
INFO=$($CRI ps \
|
INFO=$($CRI ps \
|
||||||
|
@ -316,5 +316,5 @@ function main()
|
||||||
esac
|
esac
|
||||||
}
|
}
|
||||||
|
|
||||||
main "$@"
|
_main "$@"
|
||||||
_unset_vars
|
_unset_vars
|
||||||
|
|
|
@ -1,91 +1,108 @@
|
||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
|
|
||||||
|
# version 0.1.0
|
||||||
|
#
|
||||||
|
# <INSERT TASK HERE>
|
||||||
|
|
||||||
|
# shellcheck source=/dev/null
|
||||||
. /usr/local/bin/helper_functions.sh
|
. /usr/local/bin/helper_functions.sh
|
||||||
|
|
||||||
# create date for log output
|
LOG_DATE=$(date +"%Y-%m-%d %H:%M:%S ")
|
||||||
log_date=$(date +"%Y-%m-%d %H:%M:%S ")
|
echo "$LOG_DATE Start check-for-changes script."
|
||||||
echo "${log_date} Start check-for-changes script."
|
|
||||||
|
|
||||||
# change directory
|
# ? Checks ------------------------------------------------
|
||||||
cd /tmp/docker-mailserver
|
|
||||||
|
cd /tmp/docker-mailserver || exit 1
|
||||||
|
|
||||||
# Check postfix-accounts.cf exist else break
|
# Check postfix-accounts.cf exist else break
|
||||||
if [ ! -f postfix-accounts.cf ]; then
|
if [[ ! -f postfix-accounts.cf ]]
|
||||||
echo "${log_date} postfix-accounts.cf is missing! This should not run! Exit!"
|
then
|
||||||
|
echo "$LOG_DATE postfix-accounts.cf is missing! This should not run! Exit!"
|
||||||
exit
|
exit
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Verify checksum file exists; must be prepared by start-mailserver.sh
|
# Verify checksum file exists; must be prepared by start-mailserver.sh
|
||||||
if [ ! -f $CHKSUM_FILE ]; then
|
if [[ ! -f $CHKSUM_FILE ]]
|
||||||
echo "${log_date} ${CHKSUM_FILE} is missing! Start script failed? Exit!"
|
then
|
||||||
|
echo "$LOG_DATE $CHKSUM_FILE is missing! Start script failed? Exit!"
|
||||||
exit
|
exit
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
# ? Actual script begins ----------------------------------
|
||||||
|
|
||||||
# Determine postmaster address, duplicated from start-mailserver.sh
|
# Determine postmaster address, duplicated from start-mailserver.sh
|
||||||
# This script previously didn't work when POSTMASTER_ADDRESS was empty
|
# This script previously didn't work when POSTMASTER_ADDRESS was empty
|
||||||
if [[ -n "${OVERRIDE_HOSTNAME}" ]]; then
|
if [[ -n $OVERRIDE_HOSTNAME ]]
|
||||||
DOMAINNAME=$(echo "${OVERRIDE_HOSTNAME}" | sed s/[^.]*.//)
|
then
|
||||||
|
DOMAINNAME="${OVERRIDE_HOSTNAME#*.}"
|
||||||
else
|
else
|
||||||
DOMAINNAME="$(hostname -d)"
|
DOMAINNAME="$(hostname -d)"
|
||||||
fi
|
fi
|
||||||
PM_ADDRESS="${POSTMASTER_ADDRESS:=postmaster@${DOMAINNAME}}"
|
PM_ADDRESS="${POSTMASTER_ADDRESS:=postmaster@${DOMAINNAME}}"
|
||||||
echo "${log_date} Using postmaster address ${PM_ADDRESS}"
|
echo "$LOG_DATE Using postmaster address $PM_ADDRESS"
|
||||||
|
|
||||||
# Wait to make sure server is up before we start
|
|
||||||
sleep 10
|
sleep 10
|
||||||
|
|
||||||
# Run forever
|
while true
|
||||||
while true; do
|
do
|
||||||
|
LOG_DATE=$(date +"%Y-%m-%d %H:%M:%S ")
|
||||||
|
|
||||||
# recreate logdate
|
# get chksum and check it, no need to lock config yet
|
||||||
log_date=$(date +"%Y-%m-%d %H:%M:%S ")
|
_monitored_files_checksums >"${CHKSUM_FILE}.new"
|
||||||
|
|
||||||
# Get chksum and check it, no need to lock config yet
|
if ! cmp --silent -- "$CHKSUM_FILE" "$CHKSUM_FILE.new"
|
||||||
monitored_files_checksums >"$CHKSUM_FILE.new"
|
then
|
||||||
|
echo "${LOG_DATE} Change detected"
|
||||||
if ! cmp --silent -- "$CHKSUM_FILE" "$CHKSUM_FILE.new"; then
|
|
||||||
echo "${log_date} Change detected"
|
|
||||||
changed=$(grep -Fxvf "$CHKSUM_FILE" "$CHKSUM_FILE.new" | sed 's/^[^ ]\+ //')
|
changed=$(grep -Fxvf "$CHKSUM_FILE" "$CHKSUM_FILE.new" | sed 's/^[^ ]\+ //')
|
||||||
mv "$CHKSUM_FILE.new" "$CHKSUM_FILE"
|
mv "$CHKSUM_FILE.new" "$CHKSUM_FILE"
|
||||||
|
|
||||||
# Bug alert! This overwrites the alias set by start-mailserver.sh
|
# Bug alert! This overwrites the alias set by start-mailserver.sh
|
||||||
# Take care that changes in one script are propagated to the other
|
# Take care that changes in one script are propagated to the other
|
||||||
|
|
||||||
|
# ! NEEDS FIX -----------------------------------------
|
||||||
|
# TODO FIX --------------------------------------------
|
||||||
|
# ! NEEDS EXTENSIONS ----------------------------
|
||||||
|
# TODO Perform updates below conditionally too --
|
||||||
# Also note that changes are performed in place and are not atomic
|
# 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
|
# We should fix that and write to temporary files, stop, swap and start
|
||||||
|
|
||||||
# Lock configuration while working
|
# Lock configuration while working
|
||||||
# Not fixing indentation yet to reduce diff (fix later in separate commit)
|
|
||||||
(
|
(
|
||||||
flock -e 200
|
flock -e 200
|
||||||
|
|
||||||
for file in $changed; do
|
for file in $changed
|
||||||
|
do
|
||||||
case $file in
|
case $file in
|
||||||
/etc/letsencrypt/acme.json)
|
/etc/letsencrypt/acme.json)
|
||||||
for certdomain in $SSL_DOMAIN $HOSTNAME $DOMAINNAME; do
|
for certdomain in $SSL_DOMAIN $HOSTNAME $DOMAINNAME
|
||||||
if extractCertsFromAcmeJson "$certdomain"; then
|
do
|
||||||
|
if _extract_certs_from_acme "$certdomain"
|
||||||
|
then
|
||||||
break
|
break
|
||||||
fi
|
fi
|
||||||
done
|
done
|
||||||
;;
|
;;
|
||||||
#TODO: Perform updates below conditionally as well.
|
* ) notify 'err' 'file not found for certificate in check_for_changes.sh' ;;
|
||||||
esac
|
esac
|
||||||
done
|
done
|
||||||
|
|
||||||
#regen postix aliases.
|
# regenerate postix aliases
|
||||||
echo "root: ${PM_ADDRESS}" > /etc/aliases
|
echo "root: ${PM_ADDRESS}" >/etc/aliases
|
||||||
if [ -f /tmp/docker-mailserver/postfix-aliases.cf ]; then
|
if [[ -f /tmp/docker-mailserver/postfix-aliases.cf ]]
|
||||||
cat /tmp/docker-mailserver/postfix-aliases.cf>>/etc/aliases
|
then
|
||||||
|
cat /tmp/docker-mailserver/postfix-aliases.cf >>/etc/aliases
|
||||||
fi
|
fi
|
||||||
postalias /etc/aliases
|
postalias /etc/aliases
|
||||||
|
|
||||||
#regen postfix accounts.
|
# regenerate postfix accounts
|
||||||
echo -n > /etc/postfix/vmailbox
|
echo -n >/etc/postfix/vmailbox
|
||||||
echo -n > /etc/dovecot/userdb
|
echo -n >/etc/dovecot/userdb
|
||||||
|
|
||||||
if [ -f /tmp/docker-mailserver/postfix-accounts.cf -a "$ENABLE_LDAP" != 1 ]; then
|
if [[ -f /tmp/docker-mailserver/postfix-accounts.cf ]] && [[ $ENABLE_LDAP -ne 1 ]]
|
||||||
|
then
|
||||||
sed -i 's/\r//g' /tmp/docker-mailserver/postfix-accounts.cf
|
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
|
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
|
# 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
|
sed -i -e '$a\' /tmp/docker-mailserver/postfix-accounts.cf
|
||||||
chown dovecot:dovecot /etc/dovecot/userdb
|
chown dovecot:dovecot /etc/dovecot/userdb
|
||||||
chmod 640 /etc/dovecot/userdb
|
chmod 640 /etc/dovecot/userdb
|
||||||
|
@ -93,111 +110,128 @@ if ! cmp --silent -- "$CHKSUM_FILE" "$CHKSUM_FILE.new"; then
|
||||||
sed -i -e '/\!include auth-passwdfile\.inc/s/^#//' /etc/dovecot/conf.d/10-auth.conf
|
sed -i -e '/\!include auth-passwdfile\.inc/s/^#//' /etc/dovecot/conf.d/10-auth.conf
|
||||||
|
|
||||||
# rebuild relay host
|
# rebuild relay host
|
||||||
if [ ! -z "$RELAY_HOST" ]; then
|
if [[ -n $RELAY_HOST ]]
|
||||||
|
then
|
||||||
# keep old config
|
# keep old config
|
||||||
echo -n > /etc/postfix/sasl_passwd
|
echo -n >/etc/postfix/sasl_passwd
|
||||||
if [ ! -z "$SASL_PASSWD" ]; then
|
if [[ -n $SASL_PASSWD ]]
|
||||||
echo "$SASL_PASSWD" >> /etc/postfix/sasl_passwd
|
then
|
||||||
|
echo "$SASL_PASSWD" >>/etc/postfix/sasl_passwd
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# add domain-specific auth from config file
|
# add domain-specific auth from config file
|
||||||
if [ -f /tmp/docker-mailserver/postfix-sasl-password.cf ]; then
|
if [[ -f /tmp/docker-mailserver/postfix-sasl-password.cf ]]
|
||||||
(grep -v "^\s*$\|^\s*\#" /tmp/docker-mailserver/postfix-sasl-password.cf || true) | while read line; do
|
then
|
||||||
if ! echo "$line" | grep -q -e "\s*#"; then
|
(grep -v "^\s*$\|^\s*\#" /tmp/docker-mailserver/postfix-sasl-password.cf || true) | while read -r line
|
||||||
echo "$line" >> /etc/postfix/sasl_passwd
|
do
|
||||||
|
if ! echo "$line" | grep -q -e "\s*#"
|
||||||
|
then
|
||||||
|
echo "$line" >>/etc/postfix/sasl_passwd
|
||||||
fi
|
fi
|
||||||
done
|
done
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# add default relay
|
# add default relay
|
||||||
if [ ! -z "$RELAY_USER" ] && [ ! -z "$RELAY_PASSWORD" ]; then
|
if [[ -n "$RELAY_USER" ]] && [[ -n "$RELAY_PASSWORD" ]]
|
||||||
echo "[$RELAY_HOST]:$RELAY_PORT $RELAY_USER:$RELAY_PASSWORD" >> /etc/postfix/sasl_passwd
|
then
|
||||||
|
echo "[$RELAY_HOST]:$RELAY_PORT $RELAY_USER:$RELAY_PASSWORD" >>/etc/postfix/sasl_passwd
|
||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Creating users
|
# creating users ; 'pass' is encrypted
|
||||||
# 'pass' is encrypted
|
|
||||||
# comments and empty lines are ignored
|
# comments and empty lines are ignored
|
||||||
grep -v "^\s*$\|^\s*\#" /tmp/docker-mailserver/postfix-accounts.cf | while IFS=$'|' read login pass
|
grep -v "^\s*$\|^\s*\#" /tmp/docker-mailserver/postfix-accounts.cf | while IFS=$'|' read -r login pass
|
||||||
do
|
do
|
||||||
# Setting variables for better readability
|
user=$(echo "$login" | cut -d @ -f1)
|
||||||
user=$(echo ${login} | cut -d @ -f1)
|
domain=$(echo "$login" | cut -d @ -f2)
|
||||||
domain=$(echo ${login} | cut -d @ -f2)
|
|
||||||
|
|
||||||
user_attributes=""
|
user_attributes=""
|
||||||
# test if user has a defined quota
|
# test if user has a defined quota
|
||||||
if [ -f /tmp/docker-mailserver/dovecot-quotas.cf ]; then
|
if [[ -f /tmp/docker-mailserver/dovecot-quotas.cf ]]
|
||||||
user_quota=($(grep "${user}@${domain}:" -i /tmp/docker-mailserver/dovecot-quotas.cf | tr ':' '\n'))
|
then
|
||||||
|
IFS=':'
|
||||||
|
read -r -a user_quota < <(grep "${user}@${domain}:" -i /tmp/docker-mailserver/dovecot-quotas.cf)
|
||||||
|
unset IFS
|
||||||
|
|
||||||
if [ ${#user_quota[@]} -eq 2 ]; then
|
[[ ${#user_quota[@]} -eq 2 ]] && user_attributes="${user_attributes}userdb_quota_rule=*:bytes=${user_quota[1]}"
|
||||||
user_attributes="${user_attributes}userdb_quota_rule=*:bytes=${user_quota[1]}"
|
|
||||||
fi
|
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Let's go!
|
echo "$login ${domain}/${user}/" >>/etc/postfix/vmailbox
|
||||||
echo "${login} ${domain}/${user}/" >> /etc/postfix/vmailbox
|
|
||||||
# User database for dovecot has the following format:
|
# user database for dovecot has the following format:
|
||||||
# user:password:uid:gid:(gecos):home:(shell):extra_fields
|
# user:password:uid:gid:(gecos):home:(shell):extra_fields
|
||||||
# Example :
|
# example :
|
||||||
# ${login}:${pass}:5000:5000::/var/mail/${domain}/${user}::userdb_mail=maildir:/var/mail/${domain}/${user}
|
# ${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
|
echo "${login}:${pass}:5000:5000::/var/mail/${domain}/${user}::${user_attributes}" >>/etc/dovecot/userdb
|
||||||
mkdir -p /var/mail/${domain}/${user}
|
mkdir -p "/var/mail/${domain}/${user}"
|
||||||
|
|
||||||
# Copy user provided sieve file, if present
|
if [[ -e /tmp/docker-mailserver/${login}.dovecot.sieve ]]
|
||||||
test -e /tmp/docker-mailserver/${login}.dovecot.sieve && cp /tmp/docker-mailserver/${login}.dovecot.sieve /var/mail/${domain}/${user}/.dovecot.sieve
|
then
|
||||||
echo ${domain} >> /tmp/vhost.tmp
|
cp "/tmp/docker-mailserver/${login}.dovecot.sieve" "/var/mail/${domain}/${user}/.dovecot.sieve"
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "$domain" >>/tmp/vhost.tmp
|
||||||
done
|
done
|
||||||
fi
|
fi
|
||||||
if [ ! -z "$RELAY_HOST" ]; then
|
|
||||||
populate_relayhost_map
|
[[ -n $RELAY_HOST ]] && _populate_relayhost_map
|
||||||
fi
|
|
||||||
if [ -f /etc/postfix/sasl_passwd ]; then
|
|
||||||
|
if [[ -f /etc/postfix/sasl_passwd ]]
|
||||||
|
then
|
||||||
chown root:root /etc/postfix/sasl_passwd
|
chown root:root /etc/postfix/sasl_passwd
|
||||||
chmod 0600 /etc/postfix/sasl_passwd
|
chmod 0600 /etc/postfix/sasl_passwd
|
||||||
fi
|
fi
|
||||||
if [ -f postfix-virtual.cf ]; then
|
|
||||||
# regen postfix aliases
|
if [[ -f postfix-virtual.cf ]]
|
||||||
echo -n > /etc/postfix/virtual
|
then
|
||||||
echo -n > /etc/postfix/regexp
|
# regenerate postfix aliases
|
||||||
if [ -f /tmp/docker-mailserver/postfix-virtual.cf ]; then
|
echo -n >/etc/postfix/virtual
|
||||||
# Copying virtual file
|
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
|
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
|
|
||||||
|
# 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
|
do
|
||||||
# Setting variables for better readability
|
uname=$(echo "$from" | cut -d @ -f1)
|
||||||
uname=$(echo ${from} | cut -d @ -f1)
|
domain=$(echo "$from" | cut -d @ -f2)
|
||||||
domain=$(echo ${from} | cut -d @ -f2)
|
|
||||||
# if they are equal it means the line looks like: "user1 other@domain.tld"
|
# if they are equal it means the line looks like: "user1 other@domain.tld"
|
||||||
test "$uname" != "$domain" && echo ${domain} >> /tmp/vhost.tmp
|
[ "$uname" != "$domain" ] && echo "$domain" >>/tmp/vhost.tmp
|
||||||
done
|
done
|
||||||
fi
|
fi
|
||||||
if [ -f /tmp/docker-mailserver/postfix-regexp.cf ]; then
|
|
||||||
# Copying regexp alias file
|
if [[ -f /tmp/docker-mailserver/postfix-regexp.cf ]]
|
||||||
|
then
|
||||||
cp -f /tmp/docker-mailserver/postfix-regexp.cf /etc/postfix/regexp
|
cp -f /tmp/docker-mailserver/postfix-regexp.cf /etc/postfix/regexp
|
||||||
sed -i -e '/^virtual_alias_maps/{
|
sed -i -e '/^virtual_alias_maps/{
|
||||||
s/ regexp:.*//
|
s/ regexp:.*//
|
||||||
s/$/ regexp:\/etc\/postfix\/regexp/
|
s/$/ regexp:\/etc\/postfix\/regexp/
|
||||||
}' /etc/postfix/main.cf
|
}' /etc/postfix/main.cf
|
||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
# Set vhost
|
|
||||||
if [ -f /tmp/vhost.tmp ]; then
|
|
||||||
cat /tmp/vhost.tmp | sort | uniq > /etc/postfix/vhost && rm /tmp/vhost.tmp
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Set right new if needed
|
if [[ -f /tmp/vhost.tmp ]]
|
||||||
if [ `find /var/mail -maxdepth 3 -a \( \! -user 5000 -o \! -group 5000 \) | grep -c .` != 0 ]; then
|
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
|
chown -R 5000:5000 /var/mail
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Restart of the postfix
|
|
||||||
supervisorctl restart postfix
|
supervisorctl restart postfix
|
||||||
|
|
||||||
# Prevent restart of dovecot when smtp_only=1
|
# prevent restart of dovecot when smtp_only=1
|
||||||
if [ ! $SMTP_ONLY = 1 ]; then
|
[[ $SMTP_ONLY -ne 1 ]] && supervisorctl restart dovecot
|
||||||
supervisorctl restart dovecot
|
) 200<postfix-accounts.cf # end lock
|
||||||
fi
|
fi
|
||||||
|
|
||||||
) 200<postfix-accounts.cf # end lock
|
sleep 1
|
||||||
fi
|
|
||||||
|
|
||||||
sleep 1
|
|
||||||
done
|
done
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
#!/usr/bin/env bash
|
#!/usr/bin/env bash
|
||||||
# fail2ban-wrapper.sh, version 0.0.1
|
|
||||||
|
# version 0.1.0
|
||||||
#
|
#
|
||||||
# You cannot start fail2ban in some foreground mode and
|
# You cannot start fail2ban in some foreground mode and
|
||||||
# it's more or less important that docker doesn't kill
|
# 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 stop" SIGTERM
|
||||||
trap "/usr/bin/fail2ban-client reload" SIGHUP
|
trap "/usr/bin/fail2ban-client reload" SIGHUP
|
||||||
|
|
||||||
# start fail2ban
|
|
||||||
/usr/bin/fail2ban-client start
|
/usr/bin/fail2ban-client start
|
||||||
|
|
||||||
# lets give fail2ban some time to start
|
|
||||||
sleep 5
|
sleep 5
|
||||||
|
|
||||||
# wait until fail2ban is dead (triggered by trap)
|
# 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
|
sleep 5
|
||||||
done
|
done
|
||||||
|
|
||||||
|
|
|
@ -1,45 +1,64 @@
|
||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
|
|
||||||
# expects mask prefix length and the digit
|
# version 0.1.1
|
||||||
function _mask_ip_digit() {
|
#
|
||||||
if [[ $1 -ge 8 ]]; then
|
# Provides varous helpers.
|
||||||
|
|
||||||
|
|
||||||
|
# ? IP and CIDR -------------------------------------------
|
||||||
|
|
||||||
|
|
||||||
|
function _mask_ip_digit()
|
||||||
|
{
|
||||||
|
if [[ ${1} -ge 8 ]]
|
||||||
|
then
|
||||||
MASK=255
|
MASK=255
|
||||||
else
|
elif [[ ${1} -le 0 ]]
|
||||||
if [[ $1 -le 0 ]]; then
|
then
|
||||||
MASK=0
|
MASK=0
|
||||||
else
|
else
|
||||||
VALUES=('0' '128' '192' '224' '240' '248' '252' '254' '255')
|
VALUES=(0 128 192 224 240 248 252 254 255)
|
||||||
MASK=${VALUES[$1]}
|
MASK=${VALUES[$1]}
|
||||||
fi
|
fi
|
||||||
fi
|
|
||||||
echo $(($2 & $MASK))
|
local DVAL=${2}
|
||||||
|
((DVAL&=MASK))
|
||||||
|
|
||||||
|
echo "$DVAL"
|
||||||
}
|
}
|
||||||
|
|
||||||
# transforms a specific ip with CIDR suffix like 1.2.3.4/16
|
# Transforms a specific IP with CIDR suffix
|
||||||
# to subnet with cidr suffix like 1.2.0.0/16
|
# like 1.2.3.4/16 to subnet with cidr suffix
|
||||||
function _sanitize_ipv4_to_subnet_cidr() {
|
# like 1.2.0.0/16.
|
||||||
IP=${1%%/*}
|
# Assumes correct IP and subnet are provided.
|
||||||
PREFIX_LENGTH=${1#*/}
|
function _sanitize_ipv4_to_subnet_cidr()
|
||||||
|
{
|
||||||
|
local DIGIT_PREFIX_LENGTH="${1#*/}"
|
||||||
|
|
||||||
# split IP by . into digits
|
declare -a DIGITS
|
||||||
DIGITS=(${IP//./ })
|
IFS='.' ; read -r -a DIGITS < <(echo "${1%%/*}")
|
||||||
|
unset IFS
|
||||||
|
|
||||||
# mask digits according to prefix length
|
declare -a MASKED_DIGITS
|
||||||
MASKED_DIGITS=()
|
|
||||||
DIGIT_PREFIX_LENGTH="$PREFIX_LENGTH"
|
for ((i = 0 ; i < 4 ; i++))
|
||||||
for DIGIT in "${DIGITS[@]}"; do
|
do
|
||||||
MASKED_DIGITS+=($(_mask_ip_digit $DIGIT_PREFIX_LENGTH $DIGIT))
|
MASKED_DIGITS[i]=$(_mask_ip_digit "$DIGIT_PREFIX_LENGTH" "${DIGITS[i]}")
|
||||||
DIGIT_PREFIX_LENGTH=$(($DIGIT_PREFIX_LENGTH - 8))
|
DIGIT_PREFIX_LENGTH=$((DIGIT_PREFIX_LENGTH - 8))
|
||||||
done
|
done
|
||||||
|
|
||||||
# output masked ip plus prefix length
|
echo "${MASKED_DIGITS[0]}.${MASKED_DIGITS[1]}.${MASKED_DIGITS[2]}.${MASKED_DIGITS[3]}/${1#*/}"
|
||||||
echo ${MASKED_DIGITS[0]}.${MASKED_DIGITS[1]}.${MASKED_DIGITS[2]}.${MASKED_DIGITS[3]}/$PREFIX_LENGTH
|
|
||||||
}
|
}
|
||||||
|
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 "
|
KEY=$(cat /etc/letsencrypt/acme.json | python -c "
|
||||||
import sys,json
|
import sys,json
|
||||||
acme = json.load(sys.stdin)
|
acme = json.load(sys.stdin)
|
||||||
|
@ -47,10 +66,13 @@ for key, value in acme.items():
|
||||||
certs = value['Certificates']
|
certs = value['Certificates']
|
||||||
for cert in certs:
|
for cert in certs:
|
||||||
if 'domain' in cert and 'key' in cert:
|
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']
|
print cert['key']
|
||||||
break
|
break
|
||||||
")
|
")
|
||||||
|
|
||||||
|
local CERT
|
||||||
|
# shellcheck disable=SC2002
|
||||||
CERT=$(cat /etc/letsencrypt/acme.json | python -c "
|
CERT=$(cat /etc/letsencrypt/acme.json | python -c "
|
||||||
import sys,json
|
import sys,json
|
||||||
acme = json.load(sys.stdin)
|
acme = json.load(sys.stdin)
|
||||||
|
@ -58,26 +80,35 @@ for key, value in acme.items():
|
||||||
certs = value['Certificates']
|
certs = value['Certificates']
|
||||||
for cert in certs:
|
for cert in certs:
|
||||||
if 'domain' in cert and 'certificate' in cert:
|
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']
|
print cert['certificate']
|
||||||
break
|
break
|
||||||
")
|
")
|
||||||
|
|
||||||
if [[ -n "${KEY}${CERT}" ]]; then
|
if [[ -n "${KEY}${CERT}" ]]
|
||||||
mkdir -p /etc/letsencrypt/live/"$HOSTNAME"/
|
then
|
||||||
echo $KEY | base64 -d >/etc/letsencrypt/live/"$HOSTNAME"/key.pem || exit 1
|
mkdir -p "/etc/letsencrypt/live/${HOSTNAME}/"
|
||||||
echo $CERT | base64 -d >/etc/letsencrypt/live/"$HOSTNAME"/fullchain.pem || exit 1
|
|
||||||
echo "Cert found in /etc/letsencrypt/acme.json for $WHAT"
|
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
|
return 0
|
||||||
else
|
else
|
||||||
return 1
|
return 1
|
||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
export -f _extract_certs_from_acme
|
||||||
|
|
||||||
|
|
||||||
|
# ? Notification ------------------------------------------
|
||||||
|
|
||||||
|
|
||||||
declare -A DEFAULT_VARS
|
declare -A DEFAULT_VARS
|
||||||
DEFAULT_VARS["DMS_DEBUG"]="${DMS_DEBUG:="0"}"
|
DEFAULT_VARS["DMS_DEBUG"]="${DMS_DEBUG:="0"}"
|
||||||
|
|
||||||
function notify () {
|
function notify()
|
||||||
|
{
|
||||||
c_red="\e[0;31m"
|
c_red="\e[0;31m"
|
||||||
c_green="\e[0;32m"
|
c_green="\e[0;32m"
|
||||||
c_brown="\e[0;33m"
|
c_brown="\e[0;33m"
|
||||||
|
@ -91,87 +122,87 @@ function notify () {
|
||||||
msg=""
|
msg=""
|
||||||
|
|
||||||
case "${notification_type}" in
|
case "${notification_type}" in
|
||||||
'taskgrp')
|
'taskgrp' ) msg="${c_bold}${notification_msg}${c_reset}" ;;
|
||||||
msg="${c_bold}${notification_msg}${c_reset}"
|
'task' )
|
||||||
;;
|
if [[ ${DEFAULT_VARS["DMS_DEBUG"]} == 1 ]]
|
||||||
'task')
|
then
|
||||||
if [[ ${DEFAULT_VARS["DMS_DEBUG"]} == 1 ]]; then
|
|
||||||
msg=" ${notification_msg}${c_reset}"
|
msg=" ${notification_msg}${c_reset}"
|
||||||
fi
|
fi
|
||||||
;;
|
;;
|
||||||
'inf')
|
'inf' )
|
||||||
if [[ ${DEFAULT_VARS["DMS_DEBUG"]} == 1 ]]; then
|
if [[ ${DEFAULT_VARS["DMS_DEBUG"]} == 1 ]]
|
||||||
|
then
|
||||||
msg="${c_green} * ${notification_msg}${c_reset}"
|
msg="${c_green} * ${notification_msg}${c_reset}"
|
||||||
fi
|
fi
|
||||||
;;
|
;;
|
||||||
'started')
|
'started' ) msg="${c_green} ${notification_msg}${c_reset}" ;;
|
||||||
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}" ;;
|
||||||
'warn')
|
'fatal' ) msg="${c_red} Fatal Error: ${notification_msg}${c_reset}" ;;
|
||||||
msg="${c_brown} * ${notification_msg}${c_reset}"
|
* ) msg="" ;;
|
||||||
;;
|
|
||||||
'err')
|
|
||||||
msg="${c_red} * ${notification_msg}${c_reset}"
|
|
||||||
;;
|
|
||||||
'fatal')
|
|
||||||
msg="${c_red}Error: ${notification_msg}${c_reset}"
|
|
||||||
;;
|
|
||||||
*)
|
|
||||||
msg=""
|
|
||||||
;;
|
|
||||||
esac
|
esac
|
||||||
|
|
||||||
case "${notification_format}" in
|
case "${notification_format}" in
|
||||||
'n')
|
'n' ) options="-ne" ;;
|
||||||
options="-ne"
|
* ) options="-e" ;;
|
||||||
;;
|
|
||||||
*)
|
|
||||||
options="-e"
|
|
||||||
;;
|
|
||||||
esac
|
esac
|
||||||
|
|
||||||
[[ ! -z "${msg}" ]] && echo $options "${msg}"
|
[[ -n "${msg}" ]] && echo $options "${msg}"
|
||||||
}
|
}
|
||||||
|
export -f notify
|
||||||
|
|
||||||
|
|
||||||
|
# ? Relay Host Map ----------------------------------------
|
||||||
|
|
||||||
|
|
||||||
# setup /etc/postfix/relayhost_map
|
# setup /etc/postfix/relayhost_map
|
||||||
# --
|
# --
|
||||||
# @domain1.com [smtp.mailgun.org]:587
|
# @domain1.com [smtp.mailgun.org]:587
|
||||||
# @domain2.com [smtp.mailgun.org]:587
|
# @domain2.com [smtp.mailgun.org]:587
|
||||||
# @domain3.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
|
echo -n > /etc/postfix/relayhost_map
|
||||||
chown root:root /etc/postfix/relayhost_map
|
chown root:root /etc/postfix/relayhost_map
|
||||||
chmod 0600 /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"
|
notify 'inf' "Adding relay mappings from postfix-relaymap.cf"
|
||||||
# Keep lines which are not a comment *and* have a destination.
|
# 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 \
|
sed -n '/^\s*[^#[:space:]]\S*\s\+\S/p' /tmp/docker-mailserver/postfix-relaymap.cf >> /etc/postfix/relayhost_map
|
||||||
>> /etc/postfix/relayhost_map
|
|
||||||
fi
|
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
|
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
|
[ -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
|
} | while read -r domain
|
||||||
if ! grep -q -e "^@${domain}\b" /etc/postfix/relayhost_map &&
|
do
|
||||||
! grep -qs -e "^\s*@${domain}\s*$" /tmp/docker-mailserver/postfix-relaymap.cf; then
|
# domain not already present *and* not ignored
|
||||||
# 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}"
|
notify 'inf' "Adding relay mapping for ${domain}"
|
||||||
echo "@${domain} [$RELAY_HOST]:$RELAY_PORT" >> /etc/postfix/relayhost_map
|
echo "@${domain} [$RELAY_HOST]:$RELAY_PORT" >> /etc/postfix/relayhost_map
|
||||||
fi
|
fi
|
||||||
done
|
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
|
CHKSUM_FILE=/tmp/docker-mailserver-config-chksum
|
||||||
|
|
||||||
# Compute checksums of monitored files.
|
# Compute checksums of monitored files.
|
||||||
function monitored_files_checksums() {
|
function _monitored_files_checksums()
|
||||||
|
{
|
||||||
(
|
(
|
||||||
cd /tmp/docker-mailserver
|
cd /tmp/docker-mailserver || exit 1
|
||||||
# (2>/dev/null to ignore warnings about files that don't exist)
|
|
||||||
exec sha512sum 2>/dev/null -- \
|
exec sha512sum 2>/dev/null -- \
|
||||||
postfix-accounts.cf \
|
postfix-accounts.cf \
|
||||||
postfix-virtual.cf \
|
postfix-virtual.cf \
|
||||||
|
@ -181,5 +212,5 @@ function monitored_files_checksums() {
|
||||||
"/etc/letsencrypt/live/$HOSTNAME/key.pem" \
|
"/etc/letsencrypt/live/$HOSTNAME/key.pem" \
|
||||||
"/etc/letsencrypt/live/$HOSTNAME/fullchain.pem"
|
"/etc/letsencrypt/live/$HOSTNAME/fullchain.pem"
|
||||||
)
|
)
|
||||||
return 0
|
|
||||||
}
|
}
|
||||||
|
export -f _monitored_files_checksums
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
#!/usr/bin/env bash
|
#!/usr/bin/env bash
|
||||||
# postfix-wrapper.sh, version 0.1.0
|
|
||||||
|
# version 0.1.0
|
||||||
#
|
#
|
||||||
# You cannot start postfix in some foreground mode and
|
# You cannot start postfix in some foreground mode and
|
||||||
# it's more or less important that docker doesn't kill
|
# 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 stop" SIGTERM
|
||||||
trap "service postfix reload" SIGHUP
|
trap "service postfix reload" SIGHUP
|
||||||
|
|
||||||
# start postfix
|
|
||||||
service postfix start
|
service postfix start
|
||||||
|
|
||||||
# lets give postfix some time to start
|
|
||||||
sleep 5
|
sleep 5
|
||||||
|
|
||||||
# wait until postfix is dead (triggered by trap)
|
# 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
|
sleep 5
|
||||||
done
|
done
|
||||||
|
|
||||||
|
|
|
@ -1,43 +1,52 @@
|
||||||
#!/usr/bin/env bash
|
#!/usr/bin/env bash
|
||||||
# postsrsd-wrapper.sh, version 0.2.2
|
|
||||||
|
|
||||||
if [ -n "$SRS_DOMAINNAME" ]; then
|
# version 0.1.0
|
||||||
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
|
|
||||||
|
|
||||||
sed -i -e "s/localdomain/${domain_name}/g" /etc/default/postsrsd
|
function generate_secret()
|
||||||
|
{
|
||||||
postsrsd_secret_file='/etc/postsrsd.secret'
|
( umask 0077 ; dd if=/dev/urandom bs=24 count=1 2>/dev/null | base64 -w0 > "$1" )
|
||||||
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" )
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if [ -n "$SRS_SECRET" ]; then
|
if [[ -n $SRS_DOMAINNAME ]]
|
||||||
( umask 0077
|
then
|
||||||
echo "$SRS_SECRET" | tr ',' '\n' > "$postsrsd_secret_file" )
|
NEW_DOMAIN_NAME="$SRS_DOMAINNAME"
|
||||||
|
elif [[ -n $OVERRIDE_HOSTNAME ]]
|
||||||
|
then
|
||||||
|
NEW_DOMAIN_NAME="${OVERRIDE_HOSTNAME#*.}"
|
||||||
|
elif [[ -n $DOMAINNAME ]]
|
||||||
|
then
|
||||||
|
NEW_DOMAIN_NAME="$DOMAINNAME"
|
||||||
else
|
else
|
||||||
if [ "$ONE_DIR" = 1 ]; then
|
NEW_DOMAIN_NAME=$(hostname -d)
|
||||||
if [ ! -f "$postsrsd_state_secret_file" ]; then
|
fi
|
||||||
install -d -m 0775 "$postsrsd_state_dir"
|
|
||||||
generate_secret "$postsrsd_state_secret_file"
|
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
|
fi
|
||||||
install -m 0400 "$postsrsd_state_secret_file" "$postsrsd_secret_file"
|
|
||||||
elif [ ! -f "$postsrsd_secret_file" ]; then
|
install -m 0400 "$POSTSRSD_STATE_SECRET_FILE" "$POSTSRSD_SECRET_FILE"
|
||||||
generate_secret "$postsrsd_secret_file"
|
elif [[ ! -f $POSTSRSD_SECRET_FILE ]]
|
||||||
|
then
|
||||||
|
generate_secret "$POSTSRSD_SECRET_FILE"
|
||||||
fi
|
fi
|
||||||
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
|
sed -i -e "s/^#\?SRS_EXCLUDE_DOMAINS=.*$/SRS_EXCLUDE_DOMAINS=$SRS_EXCLUDE_DOMAINS/g" /etc/default/postsrsd
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
|
|
@ -6,6 +6,7 @@
|
||||||
|
|
||||||
[supervisord]
|
[supervisord]
|
||||||
nodaemon=true
|
nodaemon=true
|
||||||
|
strip_ansi=true
|
||||||
|
|
||||||
[program:mailserver]
|
[program:mailserver]
|
||||||
startsecs=0
|
startsecs=0
|
||||||
|
|
Loading…
Reference in a new issue