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
18
.travis.yml
18
.travis.yml
|
@ -1,23 +1,41 @@
|
|||
branches:
|
||||
except:
|
||||
- donttestme
|
||||
|
||||
language: bash
|
||||
|
||||
sudo: required
|
||||
|
||||
env:
|
||||
global:
|
||||
- HADOLINT_VERSION=1.17.1
|
||||
- SHELLCHECK_VERSION=latest
|
||||
|
||||
addons:
|
||||
apt:
|
||||
packages:
|
||||
- xz-utils
|
||||
|
||||
services:
|
||||
- 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 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
|
||||
|
||||
script:
|
||||
- make shellcheck
|
||||
- make generate-accounts run generate-accounts-after-run fixtures tests
|
||||
|
||||
after_script:
|
||||
- make clean
|
||||
|
||||
notifications:
|
||||
slack:
|
||||
secure: TTo1z9nbZCWcIdfPwypubNa3y+pwvfgDGlzEVAGEuK7uuIpmEoAcAUNSSPTnbewDGHnDl8t/ml93MtvP+a+IVuAKytMqF39PHyoZO7aUl9J62V+G75OmnyGjXGJm40pQosCS6LzqoRRYXotl9+fwH568Kf4ifXCrMZX1d+ir7Ww=
|
||||
|
|
178
CONTRIBUTING.md
178
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.
|
||||
|
||||
## 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
|
||||
|
||||
``` 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 ))
|
||||
}
|
||||
|
|
74
Makefile
74
Makefile
|
@ -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,7 +17,8 @@ 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 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
|
||||
|
@ -21,13 +27,14 @@ backup:
|
|||
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"
|
||||
|
@ -128,33 +135,42 @@ fixtures:
|
|||
docker exec mail /bin/sh -c "sendmail root < /tmp/docker-mailserver-test/email-templates/root-email.txt"
|
||||
# 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
|
||||
-@ 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
|
||||
|
|
38
README.md
38
README.md
|
@ -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
|
||||
|
|
6
setup.sh
6
setup.sh
|
@ -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
|
||||
|
|
|
@ -1,91 +1,108 @@
|
|||
#!/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!"
|
||||
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!"
|
||||
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"
|
||||
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
|
||||
|
||||
# ! 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
|
||||
# Not fixing indentation yet to reduce diff (fix later in separate commit)
|
||||
(
|
||||
flock -e 200
|
||||
|
||||
for file in $changed; do
|
||||
for file in $changed
|
||||
do
|
||||
case $file in
|
||||
/etc/letsencrypt/acme.json)
|
||||
for certdomain in $SSL_DOMAIN $HOSTNAME $DOMAINNAME; do
|
||||
if extractCertsFromAcmeJson "$certdomain"; then
|
||||
for certdomain in $SSL_DOMAIN $HOSTNAME $DOMAINNAME
|
||||
do
|
||||
if _extract_certs_from_acme "$certdomain"
|
||||
then
|
||||
break
|
||||
fi
|
||||
done
|
||||
;;
|
||||
#TODO: Perform updates below conditionally as well.
|
||||
* ) notify 'err' 'file not found for certificate in check_for_changes.sh' ;;
|
||||
esac
|
||||
done
|
||||
|
||||
#regen postix aliases.
|
||||
# regenerate postix aliases
|
||||
echo "root: ${PM_ADDRESS}" >/etc/aliases
|
||||
if [ -f /tmp/docker-mailserver/postfix-aliases.cf ]; then
|
||||
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.
|
||||
# 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
|
||||
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
|
||||
|
||||
# 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
|
||||
|
@ -93,84 +110,103 @@ if ! cmp --silent -- "$CHKSUM_FILE" "$CHKSUM_FILE.new"; then
|
|||
sed -i -e '/\!include auth-passwdfile\.inc/s/^#//' /etc/dovecot/conf.d/10-auth.conf
|
||||
|
||||
# rebuild relay host
|
||||
if [ ! -z "$RELAY_HOST" ]; then
|
||||
if [[ -n $RELAY_HOST ]]
|
||||
then
|
||||
# keep old config
|
||||
echo -n >/etc/postfix/sasl_passwd
|
||||
if [ ! -z "$SASL_PASSWD" ]; then
|
||||
if [[ -n $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
|
||||
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
|
||||
|
||||
# add default relay
|
||||
if [ ! -z "$RELAY_USER" ] && [ ! -z "$RELAY_PASSWORD" ]; then
|
||||
if [[ -n "$RELAY_USER" ]] && [[ -n "$RELAY_PASSWORD" ]]
|
||||
then
|
||||
echo "[$RELAY_HOST]:$RELAY_PORT $RELAY_USER:$RELAY_PASSWORD" >>/etc/postfix/sasl_passwd
|
||||
fi
|
||||
fi
|
||||
|
||||
# Creating users
|
||||
# 'pass' is encrypted
|
||||
# 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
|
||||
grep -v "^\s*$\|^\s*\#" /tmp/docker-mailserver/postfix-accounts.cf | while IFS=$'|' read -r login pass
|
||||
do
|
||||
# Setting variables for better readability
|
||||
user=$(echo ${login} | cut -d @ -f1)
|
||||
domain=$(echo ${login} | cut -d @ -f2)
|
||||
user=$(echo "$login" | cut -d @ -f1)
|
||||
domain=$(echo "$login" | cut -d @ -f2)
|
||||
|
||||
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'))
|
||||
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
|
||||
|
||||
if [ ${#user_quota[@]} -eq 2 ]; then
|
||||
user_attributes="${user_attributes}userdb_quota_rule=*:bytes=${user_quota[1]}"
|
||||
fi
|
||||
[[ ${#user_quota[@]} -eq 2 ]] && user_attributes="${user_attributes}userdb_quota_rule=*:bytes=${user_quota[1]}"
|
||||
fi
|
||||
|
||||
# Let's go!
|
||||
echo "${login} ${domain}/${user}/" >> /etc/postfix/vmailbox
|
||||
# User database for dovecot has the following format:
|
||||
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 :
|
||||
# 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}
|
||||
mkdir -p "/var/mail/${domain}/${user}"
|
||||
|
||||
# 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
|
||||
if [[ -e /tmp/docker-mailserver/${login}.dovecot.sieve ]]
|
||||
then
|
||||
cp "/tmp/docker-mailserver/${login}.dovecot.sieve" "/var/mail/${domain}/${user}/.dovecot.sieve"
|
||||
fi
|
||||
|
||||
echo "$domain" >>/tmp/vhost.tmp
|
||||
done
|
||||
fi
|
||||
if [ ! -z "$RELAY_HOST" ]; then
|
||||
populate_relayhost_map
|
||||
fi
|
||||
if [ -f /etc/postfix/sasl_passwd ]; then
|
||||
|
||||
[[ -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
|
||||
# regen postfix aliases
|
||||
|
||||
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
|
||||
# Copying virtual file
|
||||
|
||||
if [[ -f /tmp/docker-mailserver/postfix-virtual.cf ]]
|
||||
then
|
||||
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
|
||||
# Setting variables for better readability
|
||||
uname=$(echo ${from} | cut -d @ -f1)
|
||||
domain=$(echo ${from} | cut -d @ -f2)
|
||||
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
|
||||
[ "$uname" != "$domain" ] && echo "$domain" >>/tmp/vhost.tmp
|
||||
done
|
||||
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
|
||||
sed -i -e '/^virtual_alias_maps/{
|
||||
s/ regexp:.*//
|
||||
|
@ -178,24 +214,22 @@ if ! cmp --silent -- "$CHKSUM_FILE" "$CHKSUM_FILE.new"; then
|
|||
}' /etc/postfix/main.cf
|
||||
fi
|
||||
fi
|
||||
# Set vhost
|
||||
if [ -f /tmp/vhost.tmp ]; then
|
||||
|
||||
if [[ -f /tmp/vhost.tmp ]]
|
||||
then
|
||||
# shellcheck disable=SC2002
|
||||
cat /tmp/vhost.tmp | sort | uniq >/etc/postfix/vhost && rm /tmp/vhost.tmp
|
||||
fi
|
||||
|
||||
# Set right new if needed
|
||||
if [ `find /var/mail -maxdepth 3 -a \( \! -user 5000 -o \! -group 5000 \) | grep -c .` != 0 ]; then
|
||||
if [[ $(find /var/mail -maxdepth 3 -a \( \! -user 5000 -o \! -group 5000 \) | grep -c .) -ne 0 ]]
|
||||
then
|
||||
chown -R 5000:5000 /var/mail
|
||||
fi
|
||||
|
||||
# Restart of the postfix
|
||||
supervisorctl restart postfix
|
||||
|
||||
# Prevent restart of dovecot when smtp_only=1
|
||||
if [ ! $SMTP_ONLY = 1 ]; then
|
||||
supervisorctl restart dovecot
|
||||
fi
|
||||
|
||||
# prevent restart of dovecot when smtp_only=1
|
||||
[[ $SMTP_ONLY -ne 1 ]] && supervisorctl restart dovecot
|
||||
) 200<postfix-accounts.cf # end lock
|
||||
fi
|
||||
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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
|
||||
else
|
||||
if [[ $1 -le 0 ]]; then
|
||||
elif [[ ${1} -le 0 ]]
|
||||
then
|
||||
MASK=0
|
||||
else
|
||||
VALUES=('0' '128' '192' '224' '240' '248' '252' '254' '255')
|
||||
VALUES=(0 128 192 224 240 248 252 254 255)
|
||||
MASK=${VALUES[$1]}
|
||||
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
|
||||
# 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,87 +122,87 @@ function notify () {
|
|||
msg=""
|
||||
|
||||
case "${notification_type}" in
|
||||
'taskgrp')
|
||||
msg="${c_bold}${notification_msg}${c_reset}"
|
||||
;;
|
||||
'taskgrp' ) msg="${c_bold}${notification_msg}${c_reset}" ;;
|
||||
'task' )
|
||||
if [[ ${DEFAULT_VARS["DMS_DEBUG"]} == 1 ]]; then
|
||||
if [[ ${DEFAULT_VARS["DMS_DEBUG"]} == 1 ]]
|
||||
then
|
||||
msg=" ${notification_msg}${c_reset}"
|
||||
fi
|
||||
;;
|
||||
'inf' )
|
||||
if [[ ${DEFAULT_VARS["DMS_DEBUG"]} == 1 ]]; then
|
||||
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 \
|
||||
|
@ -181,5 +212,5 @@ function monitored_files_checksums() {
|
|||
"/etc/letsencrypt/live/$HOSTNAME/key.pem" \
|
||||
"/etc/letsencrypt/live/$HOSTNAME/fullchain.pem"
|
||||
)
|
||||
return 0
|
||||
}
|
||||
export -f _monitored_files_checksums
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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
|
||||
install -m 0400 "$postsrsd_state_secret_file" "$postsrsd_secret_file"
|
||||
elif [ ! -f "$postsrsd_secret_file" ]; then
|
||||
generate_secret "$postsrsd_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
|
||||
|
||||
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
|
||||
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
|
||||
[supervisord]
|
||||
nodaemon=true
|
||||
strip_ansi=true
|
||||
|
||||
[program:mailserver]
|
||||
startsecs=0
|
||||
|
|
Loading…
Reference in a new issue