mirror of
https://github.com/bokysan/docker-postfix.git
synced 2024-09-20 06:46:10 +08:00
Larger refactoring of the codebase + DKIM_SELECTOR
Summary ^^^^^^^ This commit refactors the code base to be more manageble and prepares the groundwork for tests. Refactoring ^^^^^^^^^^^ Files are now moved to subdirectories, all for the sole purpose of easier management. Tests live in their own folders, as well as configs and other files. Test framework ^^^^^^^^^^^^^^ Two new important scripts/directories are available: - `unit-tests.sh` / `/unit-test` which executes unit tests across shell scripts, and - `integration-test.sh` / `integration-tests`, which spins up the container and tries to send the email. Both tests use the [BATS](https://github.com/sstephenson/bats) framework for testing. To create a new test, simply drop a `.bats` file into a corresponding directory. Functions have been extracted into `common-run.sh`, to be able to test them independently. DKIM_SELECTOR ^^^^^^^^^^^^^ It is now possible to specify a DKIM selector to use (instead of the default "mail"). See `README.md` for more details. JSON logging ^^^^^^^^^^^^ WIP: rsyslog will now output JSON logs. This is especially important if you plan on deploying the image into Kubernetes, as [Prometheus](https://prometheus.io/) can handle logs in JSON much easier. TODO: Make this an optional feature, to not confuse existing users.
This commit is contained in:
parent
ed09d86c8d
commit
9b1902c047
25
.editorconfig
Normal file
25
.editorconfig
Normal file
|
@ -0,0 +1,25 @@
|
|||
# http://editorconfig.org
|
||||
root = true
|
||||
indent_size = 2
|
||||
charset = utf-8
|
||||
trim_trailing_whitespace = true
|
||||
insert_final_newline = true
|
||||
end_of_line = lf
|
||||
max_line_length = 120
|
||||
|
||||
# Indent shell scripts with tabs
|
||||
[**.sh,**.bats]
|
||||
indent_style = tab
|
||||
|
||||
# Indent YAML files with spaces
|
||||
[**.yaml,**.yml]
|
||||
indent_style = space
|
||||
|
||||
# The JSON files contain newlines inconsistently
|
||||
[*.json]
|
||||
insert_final_newline = ignore
|
||||
|
||||
# Minified JavaScript files shouldn't be changed
|
||||
[**.min.js]
|
||||
indent_style = ignore
|
||||
insert_final_newline = ignore
|
13
Dockerfile
13
Dockerfile
|
@ -27,6 +27,8 @@ ENV ALLOW_EMPTY_SENDER_DOMAINS=
|
|||
ENV MESSAGE_SIZE_LIMIT=
|
||||
# Enable additional debugging for connections to postfix
|
||||
ENV INBOUND_DEBUGGING=
|
||||
# DKIM domain selector. If not set, the default (mail) will be used
|
||||
ENV DKIM_SELECTOR=
|
||||
|
||||
# Install supervisor, postfix
|
||||
# Install postfix first to get the first account (101)
|
||||
|
@ -40,11 +42,12 @@ RUN true && \
|
|||
(rm "/tmp/"* 2>/dev/null || true) && (rm -rf /var/cache/apk/* 2>/dev/null || true)
|
||||
|
||||
# Set up configuration
|
||||
COPY supervisord.conf /etc/supervisord.conf
|
||||
COPY rsyslog.conf /etc/rsyslog.conf
|
||||
COPY opendkim.conf /etc/opendkim/opendkim.conf
|
||||
COPY smtp_header_checks /etc/postfix/smtp_header_checks
|
||||
COPY commons.sh run.sh opendkim.sh /
|
||||
COPY /configs/supervisord.conf /etc/supervisord.conf
|
||||
COPY /configs/rsyslog.conf /etc/rsyslog.conf
|
||||
COPY /configs/opendkim.conf /etc/opendkim/opendkim.conf
|
||||
COPY /configs/smtp_header_checks /etc/postfix/smtp_header_checks
|
||||
COPY /scripts/*.sh /
|
||||
|
||||
RUN chmod +x /run.sh /opendkim.sh
|
||||
|
||||
# Set up volumes
|
||||
|
|
98
README.md
98
README.md
|
@ -1,10 +1,6 @@
|
|||
# docker-postfix ![Docker image](https://github.com/bokysan/docker-postfix/workflows/Docker%20image/badge.svg)
|
||||
Simple postfix relay host for your Docker containers. Based on Alpine Linux.
|
||||
|
||||
|
||||
## Project update
|
||||
|
||||
**Notice, that while this commits are old, there project is not dead.** It's simply considered feature complete. You will find the latest version of the code on Dockerhub (https://hub.docker.com/r/boky/postfix). If you do have any suggestions, feel free to clone and post a merge.
|
||||
Simple postfix relay host ("postfix null client") for your Docker containers. Based on Alpine Linux.
|
||||
|
||||
## Description
|
||||
|
||||
|
@ -17,7 +13,8 @@ This is a _server side_ POSTFIX image, geared towards emails that need to be sen
|
|||
## TL;DR
|
||||
|
||||
To run the container, do the following:
|
||||
```
|
||||
|
||||
```sh
|
||||
docker run --rm --name postfix -e "ALLOWED_SENDER_DOMAINS=example.com" -p 1587:587 boky/postfix
|
||||
```
|
||||
|
||||
|
@ -26,8 +23,9 @@ you haven't configured your `example.com` domain to allow sending from this IP (
|
|||
[openspf](http://www.openspf.org/)), your emails will most likely be regarded as spam.
|
||||
|
||||
All standard caveats of configuring the SMTP server apply:
|
||||
- **MAKE SURE YOUR OUTGOING PORT 25 IS NOT BLOCKED.**
|
||||
- Most ISPs block outgoing connections to port 25 and several companies (e.g. [NoIP](https://www.noip.com/blog/2013/03/26/my-isp-blocks-smtp-port-25-can-i-still-host-a-mail-server/), [Dynu](https://www.dynu.com/en-US/Blog/Article?Article=How-to-host-email-server-if-ISP-blocks-port-25) offer workarounds).
|
||||
|
||||
- **MAKE SURE YOUR OUTGOING PORT 25 IS NOT BLOCKED.**
|
||||
- Most ISPs block outgoing connections to port 25 and several companies (e.g. [NoIP](https://www.noip.com/blog/2013/03/26/my-isp-blocks-smtp-port-25-can-i-still-host-a-mail-server/), [Dynu](https://www.dynu.com/en-US/Blog/Article?Article=How-to-host-email-server-if-ISP-blocks-port-25) offer workarounds).
|
||||
- Hosting centers also tend to block port 25, which can be unblocked per requirst (e.g. for AWS either [fill out a form](https://aws.amazon.com/premiumsupport/knowledge-center/ec2-port-25-throttle/) or forward mail to their [SES](https://aws.amazon.com/ses/) service, which is free for low volumes)
|
||||
- You'll most likely need to at least [set up SPF records](https://en.wikipedia.org/wiki/Sender_Policy_Framework) or [DKIM](https://en.wikipedia.org/wiki/DomainKeys_Identified_Mail)
|
||||
- If using DKIM (below), make sure to add DKIM keys to your domain's DNS entries
|
||||
|
@ -35,34 +33,38 @@ All standard caveats of configuring the SMTP server apply:
|
|||
|
||||
If you don't know what any of the above means, get some help. Google is your friend. It's also worth noting that as a consequence it's pretty difficult to host a SMTP server on a dynamic IP address.
|
||||
|
||||
|
||||
**Please note that the image uses the submission (587) port by default**. Port 25 is not
|
||||
**Please note that the image uses the submission (587) port by default**. Port 25 is not
|
||||
exposed on purpose, as it's regularly blocked by ISP or already occupied by other services.
|
||||
|
||||
|
||||
|
||||
## Configuration options
|
||||
|
||||
The following configuration options are available:
|
||||
```
|
||||
|
||||
```Dockerfile
|
||||
ENV vars
|
||||
$TZ = The timezone for the image
|
||||
$HOSTNAME = Postfix myhostname
|
||||
$RELAYHOST = Host that relays your msgs
|
||||
$RELAYHOST_USERNAME = An (optional) username for the relay server
|
||||
$RELAYHOST_PASSWORD = An (optional) login password for the relay server
|
||||
$RELAYHOST_TLS_LEVEL = Relay host TLS connection leve
|
||||
$MYNETWORKS = allow domains from per Network ( default 127.0.0.0/8,10.0.0.0/8,172.16.0.0/12,192.168.0.0/16 )
|
||||
$ALLOWED_SENDER_DOMAINS = domains sender domains
|
||||
$ALLOW_EMPTY_SENDER_DOMAINS = if value is set (i.e: "true"), $ALLOWED_SENDER_DOMAINS can be unset
|
||||
$MESSAGE_SIZE_LIMIT = The size of the messsage, in bytes
|
||||
$INBOUND_DEBUGGING = Set to 1 to enable detailed debugging in the logs
|
||||
$MASQUERADED_DOMAINS = domains where you want to masquerade internal hosts
|
||||
|
||||
$DKIM_SELECTOR = override DKIM selector (by default "mail")
|
||||
```
|
||||
|
||||
### `HOSTNAME`
|
||||
|
||||
You may configure a specific hostname that the SMTP server will use to identify itself. If you don't do it,
|
||||
the default Docker host name will be used. A lot of times, this will be just the container id (e.g. `f73792d540a5`)
|
||||
which may make it difficult to track your emails in the log files. If you care about tracking at all,
|
||||
I suggest you set this variable, e.g.:
|
||||
```
|
||||
|
||||
```sh
|
||||
docker run --rm --name postfix -e HOSTNAME=postfix-docker -p 1587:587 boky/postfix
|
||||
```
|
||||
|
||||
|
@ -73,41 +75,44 @@ you will most likely have a dedicated outgoing mail server. By setting this opti
|
|||
(hence the name) all incoming emails to the target server for actual delivery.
|
||||
|
||||
Example:
|
||||
```
|
||||
|
||||
```sh
|
||||
docker run --rm --name postfix -e RELAYHOST=192.168.115.215 -p 1587:587 boky/postfix
|
||||
```
|
||||
|
||||
You may optionally specifiy a relay port, e.g.:
|
||||
```
|
||||
|
||||
```sh
|
||||
docker run --rm --name postfix -e RELAYHOST=192.168.115.215:587 -p 1587:587 boky/postfix
|
||||
```
|
||||
|
||||
Or an IPv6 address, e.g.:
|
||||
```
|
||||
|
||||
```sh
|
||||
docker run --rm --name postfix -e 'RELAYHOST=[2001:db8::1]:587' -p 1587:587 boky/postfix
|
||||
```
|
||||
|
||||
If your end server requires you to authenticate with username/password, add them also:
|
||||
```
|
||||
|
||||
```sh
|
||||
docker run --rm --name postfix -e RELAYHOST=mail.google.com -e RELAYHOST_USERNAME=hello@gmail.com -e RELAYHOST_PASSWORD=world -p 1587:587 boky/postfix
|
||||
```
|
||||
|
||||
### `RELAYHOST_TLS_LEVEL`
|
||||
|
||||
Define relay host TLS connection level. See http://www.postfix.org/postconf.5.html#smtp_tls_security_level for details. By default, the permissive level ("may") is used, which basically means "use TLS if available" and should be a sane default in most cases.
|
||||
Define relay host TLS connection level. See [smtp_tls_security_level](http://www.postfix.org/postconf.5.html#smtp_tls_security_level) for details. By default, the permissive level ("may") is used, which basically means "use TLS if available" and should be a sane default in most cases.
|
||||
|
||||
This level defines how the postfix will connect to your upstream server.
|
||||
|
||||
### `MESSAGE_SIZE_LIMIT`
|
||||
|
||||
Define the maximum size of the message, in bytes.
|
||||
See more in [Postfix documentation](http://www.postfix.org/postconf.5.html#message_size_limit).
|
||||
Define the maximum size of the message, in bytes.
|
||||
See more in [Postfix documentation](http://www.postfix.org/postconf.5.html#message_size_limit).
|
||||
|
||||
By default, this limit is set to 0 (zero), which means unlimited. Why would you want to set this? Well, this is especially useful in relation
|
||||
with `RELAYHOST` setting. If your relay host has a message limit (and usually it does), set it also here. This will help you "fail fast" --
|
||||
your message will be rejected at the time of sending instead having it stuck in the outbound queue indefenetly.
|
||||
|
||||
|
||||
### `MYNETWORKS`
|
||||
|
||||
This implementation is meant for private installations -- so that when you configure your services using _docker compose_
|
||||
|
@ -118,7 +123,8 @@ Most likely you won't need to change this. However, if you need to support IPv6
|
|||
override this setting.
|
||||
|
||||
Example:
|
||||
```
|
||||
|
||||
```sh
|
||||
docker run --rm --name postfix -e "MYNETWORKS=10.1.2.0/24" -p 1587:587 boky/postfix
|
||||
```
|
||||
|
||||
|
@ -128,7 +134,8 @@ Due to in-built spam protection in [Postfix](http://www.postfix.org/postconf.5.h
|
|||
sender domains -- the domains you are using to send your emails from, otherwise Postfix will refuse to start.
|
||||
|
||||
Example:
|
||||
```
|
||||
|
||||
```sh
|
||||
docker run --rm --name postfix -e "ALLOWED_SENDER_DOMAINS=example.com example.org" -p 1587:587 boky/postfix
|
||||
```
|
||||
|
||||
|
@ -139,13 +146,13 @@ If you want to set the restrictions on the recipient and not on the sender (anyo
|
|||
Enable additional debugging for any connection comming from `MYNETWORKS`. Set to a non-empty string (usually "1" or "yes") to
|
||||
enable debugging.
|
||||
|
||||
|
||||
### `MASQUERADED_DOMAINS`
|
||||
|
||||
If you don't want outbound mails to expose hostnames, you can use this variable to enable Postfix's [address masquerading](http://www.postfix.org/ADDRESS_REWRITING_README.html#masquerade). This can be used to do things like rewrite `lorem@ipsum.example.com` to `lorem@example.com`.
|
||||
|
||||
Example:
|
||||
```
|
||||
|
||||
```sh
|
||||
docker run --rm --name postfix -e "ALLOWED_SENDER_DOMAINS=example.com example.org" -e "MASQUERADED_DOMAINS=example.com" -p 1587:587 boky/postfix
|
||||
```
|
||||
|
||||
|
@ -154,24 +161,25 @@ docker run --rm --name postfix -e "ALLOWED_SENDER_DOMAINS=example.com example.or
|
|||
This image allows you to execute Postfix [header checks](http://www.postfix.org/header_checks.5.html). Header checks allow you to execute a certain
|
||||
action when a certain MIME header is found. For example, header checks can be used prevent attaching executable files to emails.
|
||||
|
||||
Header checks work by comparing each message header line to a pre-configured list of patterns. When a match is found the corresponding action is
|
||||
Header checks work by comparing each message header line to a pre-configured list of patterns. When a match is found the corresponding action is
|
||||
executed. The default patterns that come with this image can be found in the `smtp_header_checks` file. Feel free to override this file in any derived
|
||||
images or, alternately, provide your own in another directory.
|
||||
|
||||
Set `SMTP_HEADER_CHECKS` to type and location of the file to enable this feature. The sample file is uploaded into `/etc/postfix/smtp_header_checks`
|
||||
Set `SMTP_HEADER_CHECKS` to type and location of the file to enable this feature. The sample file is uploaded into `/etc/postfix/smtp_header_checks`
|
||||
in the image. As a convenience, setting `SMTP_HEADER_CHECKS=1` will set this to `regexp:/etc/postfix/smtp_header_checks`.
|
||||
|
||||
Example:
|
||||
```
|
||||
|
||||
```sh
|
||||
docker run --rm --name postfix -e "SMTP_HEADER_CHECKS="regexp:/etc/postfix/smtp_header_checks" -e "ALLOWED_SENDER_DOMAINS=example.com example.org" -p 1587:587 boky/postfix
|
||||
```
|
||||
|
||||
## `DKIM`
|
||||
|
||||
**This image is equiped with support for DKIM.** If you want to use DKIM you will need to generate DKIM keys yourself.
|
||||
**This image is equiped with support for DKIM.** If you want to use DKIM you will need to generate DKIM keys yourself.
|
||||
You'll need to create a folder for every domain you want to send through Postfix and generate they key(s) with the following command, e.g.
|
||||
|
||||
```
|
||||
```sh
|
||||
mkdir -p /host/keys; cd /host/keys
|
||||
|
||||
for DOMAIN in example.com example.org; do
|
||||
|
@ -188,12 +196,27 @@ done
|
|||
|
||||
`opendkim-genkey` is usually in your favourite distribution provided by installing `opendkim-tools` or `opendkim-utils`.
|
||||
|
||||
Add the created `<domain>.txt` files to your DNS records. Afterwards, just mount `/etc/opendkim/keys` into your image and DKIM
|
||||
Add the created `<domain>.txt` files to your DNS records. Afterwards, just mount `/etc/opendkim/keys` into your image and DKIM
|
||||
will be used automatically, e.g.:
|
||||
```
|
||||
|
||||
```sh
|
||||
docker run --rm --name postfix -e "ALLOWED_SENDER_DOMAINS=example.com example.org" -v /host/keys:/etc/opendkim/keys -p 1587:587 boky/postfix
|
||||
```
|
||||
|
||||
**NOTE:** `mail` is the *default DKIM selector* and should be sufficient for most usages. If you wish to override the selector,
|
||||
set the environment variable `DKIM_SELECTOR`, e.g. `... -e DKIM_SELECTOR=postfix`. Note that the same DKIM selector will be
|
||||
applied to all found domains. To override a selector for a specific domain use the syntax `[<domain>=<selector>,...]`, e.g.:
|
||||
|
||||
```sh
|
||||
DKIM_SELECTOR=foo,example.org=postfix,example.com=blah
|
||||
```
|
||||
|
||||
This means:
|
||||
|
||||
- use `postfix` for `example.org` domain
|
||||
- use `blah` for `example.com` domain
|
||||
- use `foo` if no domain matches
|
||||
|
||||
## Extending the image
|
||||
|
||||
If you need to add custom configuration to postfix or have it do something outside of the scope of this configuration, simply
|
||||
|
@ -201,7 +224,8 @@ add your scripts to `/docker-init.db/`: All files with the `.sh` extension will
|
|||
startup script.
|
||||
|
||||
E.g.: create a custom `Dockerfile` like this:
|
||||
```
|
||||
|
||||
```sh
|
||||
FROM boky/postfix
|
||||
LABEL maintainer="Jack Sparrow <jack.sparrow@theblackpearl.example.com>"
|
||||
ADD Dockerfiles/additional-config.sh /docker-init.db/
|
||||
|
@ -213,12 +237,12 @@ Or -- alternately -- bind this folder in your docker config and put your scripts
|
|||
to your postfix server or override configs created by the script.
|
||||
|
||||
For example, your script could contain something like this:
|
||||
```
|
||||
|
||||
```sh
|
||||
#!/bin/sh
|
||||
postconf -e "address_verify_negative_cache=yes"
|
||||
```
|
||||
|
||||
|
||||
## Security
|
||||
|
||||
Postfix will run the master proces as `root`, because that's how it's designed. Subprocesses will run under the `postfix` account
|
||||
|
|
42
configs/rsyslog.conf
Normal file
42
configs/rsyslog.conf
Normal file
|
@ -0,0 +1,42 @@
|
|||
$ModLoad immark.so # provides --MARK-- message capability
|
||||
$ModLoad imuxsock.so # provides support for local system logging (e.g. via logger command)
|
||||
|
||||
# default permissions for all log files.
|
||||
$FileOwner root
|
||||
$FileGroup adm
|
||||
$FileCreateMode 0640
|
||||
$DirCreateMode 0755
|
||||
$Umask 0022
|
||||
|
||||
template (name="devicelog" type="string" string="/dev/stdout")
|
||||
|
||||
template(name="json_syslog"
|
||||
type="list") {
|
||||
constant(value="{")
|
||||
constant(value="\"@timestamp\":\"") property(name="timereported" dateFormat="rfc3339")
|
||||
constant(value="\",\"type\":\"syslog_json")
|
||||
constant(value="\",\"tag\":\"") property(name="syslogtag" format="json")
|
||||
constant(value="\",\"relayhost\":\"") property(name="fromhost")
|
||||
constant(value="\",\"relayip\":\"") property(name="fromhost-ip")
|
||||
constant(value="\",\"logsource\":\"") property(name="source")
|
||||
constant(value="\",\"hostname\":\"") property(name="hostname" caseconversion="lower")
|
||||
constant(value="\",\"program\":\"") property(name="programname")
|
||||
constant(value="\",\"priority\":\"") property(name="pri")
|
||||
constant(value="\",\"severity\":\"") property(name="syslogseverity")
|
||||
constant(value="\",\"facility\":\"") property(name="syslogfacility")
|
||||
constant(value="\",\"severity_label\":\"") property(name="syslogseverity-text")
|
||||
constant(value="\",\"facility_label\":\"") property(name="syslogfacility-text")
|
||||
constant(value="\",\"message\":\"") property(name="rawmsg" format="json")
|
||||
constant(value="\",\"end_msg\":\"")
|
||||
constant(value="\"}\n")
|
||||
}
|
||||
|
||||
|
||||
if $syslogseverity <= '6' then {
|
||||
# matching logs will be saved
|
||||
action(type="omfile" DynaFile="devicelog" template="json_syslog" DirCreateMode="0755" FileCreateMode="0644")
|
||||
# enable below to stop processing further this log
|
||||
stop
|
||||
}
|
||||
|
||||
stop
|
|
@ -23,7 +23,6 @@ directory = /etc/postfix
|
|||
command = /usr/sbin/postfix -c /etc/postfix start
|
||||
startsecs = 0
|
||||
|
||||
|
||||
[program:opendkim]
|
||||
command = /opendkim.sh
|
||||
user = opendkim
|
3
integration-test.sh
Executable file
3
integration-test.sh
Executable file
|
@ -0,0 +1,3 @@
|
|||
#!/bin/sh
|
||||
cd integration-tests
|
||||
docker-compose up --build --abort-on-container-exit --exit-code-from tests
|
15
integration-tests/Dockerfile
Normal file
15
integration-tests/Dockerfile
Normal file
|
@ -0,0 +1,15 @@
|
|||
# ---- MAILSEND ----
|
||||
FROM boky/alpine-bootstrap AS mailsend
|
||||
COPY install_mailsend.sh /tmp/
|
||||
RUN chmod +x /tmp/install_mailsend.sh && /tmp/install_mailsend.sh
|
||||
|
||||
# ---- TEST ----
|
||||
FROM boky/alpine-bootstrap
|
||||
|
||||
RUN apk add --no-cache bash bats
|
||||
COPY --from=mailsend /tmp/mailsend-go-dir/mailsend-go /usr/local/bin/mailsend
|
||||
|
||||
WORKDIR /code
|
||||
ENTRYPOINT ["/usr/bin/bats"]
|
||||
|
||||
CMD ["-v"]
|
|
@ -1,8 +1,10 @@
|
|||
version: '3.6'
|
||||
version: '3.7'
|
||||
services:
|
||||
postfix_test_587:
|
||||
hostname: "postfix"
|
||||
image: "boky/postfix"
|
||||
build:
|
||||
context: ..
|
||||
restart: always
|
||||
healthcheck:
|
||||
test: [ "CMD", "sh", "-c", "netstat -an | fgrep 587 | fgrep -q LISTEN" ]
|
||||
|
@ -10,10 +12,16 @@ services:
|
|||
timeout: 5s
|
||||
start_period: 10s
|
||||
retries: 2
|
||||
ports:
|
||||
- "1587:587"
|
||||
volumes:
|
||||
- "./test-keys:/etc/opendkim/keys"
|
||||
- "./docker-init.db:/docker-init.db/"
|
||||
environment:
|
||||
ALLOWED_SENDER_DOMAINS: "example.org"
|
||||
INBOUND_DEBUGGING: 1
|
||||
tests:
|
||||
image: "boky/postfix-integration-test"
|
||||
restart: "no"
|
||||
volumes:
|
||||
- "..:/code"
|
||||
build:
|
||||
context: .
|
||||
command: "/code/integration-tests/"
|
6
integration-tests/docker-init.db/relay_to_blackhole.sh
Executable file
6
integration-tests/docker-init.db/relay_to_blackhole.sh
Executable file
|
@ -0,0 +1,6 @@
|
|||
#!/bin/sh
|
||||
|
||||
. /common.sh
|
||||
|
||||
echo -e "‣ $notice Relaying all mails to blackhole.${reset}"
|
||||
postconf -e "smtpd_end_of_data_restrictions=check_client_access static:discard"
|
19
integration-tests/install_mailsend.sh
Normal file
19
integration-tests/install_mailsend.sh
Normal file
|
@ -0,0 +1,19 @@
|
|||
#!/usr/bin/env bash
|
||||
set -e
|
||||
|
||||
install_mailsend() {
|
||||
local ARCH="$(uname -m)"
|
||||
local MAILSEND_VERSION="1.0.9"
|
||||
|
||||
if [[ "$ARCH" == "x86_64" ]]; then
|
||||
ARCH="64bit"
|
||||
else
|
||||
ARCH="ARM"
|
||||
fi
|
||||
|
||||
cd /tmp
|
||||
curl -LsS https://github.com/muquit/mailsend-go/releases/download/v${MAILSEND_VERSION}/mailsend-go_${MAILSEND_VERSION}_linux-${ARCH}.tar.gz | tar xzfv -
|
||||
chmod +x /tmp/mailsend-go-dir/mailsend-go
|
||||
}
|
||||
|
||||
install_mailsend
|
40
integration-tests/test.bats
Executable file
40
integration-tests/test.bats
Executable file
|
@ -0,0 +1,40 @@
|
|||
#!/usr/bin/env bats
|
||||
|
||||
FROM=$1
|
||||
TO=$2
|
||||
|
||||
if [ -z "$FROM" ]; then
|
||||
FROM="demo@example.org"
|
||||
fi
|
||||
|
||||
if [ -z "$TO" ]; then
|
||||
TO="test@gmail.com"
|
||||
fi
|
||||
|
||||
# Wait for postfix to startup
|
||||
echo "Waiting for startup..."
|
||||
wait-for-service -q tcp://postfix_test_587:587
|
||||
|
||||
SMTP_DATA="-smtp postfix_test_587 -port 587"
|
||||
|
||||
@test "Get server info" {
|
||||
mailsend -info $SMTP_DATA
|
||||
}
|
||||
|
||||
@test "Send a simple mail" {
|
||||
mailsend \
|
||||
-sub "Test email 1" $SMTP_DATA \
|
||||
-from "$FROM" -to "$TO" \
|
||||
body \
|
||||
-msg "Hello world!\nThis is a simple test message!"
|
||||
}
|
||||
|
||||
@test "Send mail with attachment" {
|
||||
mailsend \
|
||||
-sub "Test with attachment" $SMTP_DATA \
|
||||
-from "$FROM" -to "$TO" \
|
||||
body \
|
||||
-msg "Hi!\nThis is an example of an attachment." \
|
||||
attach \
|
||||
-file "/usr/local/bin/mailsend"
|
||||
}
|
13
rsyslog.conf
13
rsyslog.conf
|
@ -1,13 +0,0 @@
|
|||
$ModLoad immark.so # provides --MARK-- message capability
|
||||
$ModLoad imuxsock.so # provides support for local system logging (e.g. via logger command)
|
||||
|
||||
# default permissions for all log files.
|
||||
$FileOwner root
|
||||
$FileGroup adm
|
||||
$FileCreateMode 0640
|
||||
$DirCreateMode 0755
|
||||
$Umask 0022
|
||||
|
||||
#*.info /dev/stdout
|
||||
#mail.* /dev/stdout
|
||||
mail.info /dev/stdout
|
239
run.sh
239
run.sh
|
@ -1,239 +0,0 @@
|
|||
#!/bin/sh
|
||||
|
||||
. /commons.sh
|
||||
|
||||
echo -e "******************************"
|
||||
echo -e "**** POSTFIX STARTING UP *****"
|
||||
echo -e "******************************"
|
||||
|
||||
# Check if we need to configure the container timezone
|
||||
if [ ! -z "$TZ" ]; then
|
||||
TZ_FILE="/usr/share/zoneinfo/$TZ"
|
||||
if [ -f "$TZ_FILE" ]; then
|
||||
echo -e "‣ $notice Setting container timezone to: ${emphasis}$TZ${reset}"
|
||||
ln -snf "$TZ_FILE" /etc/localtime
|
||||
echo "$TZ" > /etc/timezone
|
||||
else
|
||||
echo -e "‣ $warn Cannot set timezone to: ${emphasis}$TZ${reset} -- this timezone does not exist."
|
||||
fi
|
||||
else
|
||||
echo -e "‣ $info Not setting any timezone for the container"
|
||||
fi
|
||||
|
||||
# Make and reown postfix folders
|
||||
mkdir -p /var/spool/postfix/ && mkdir -p /var/spool/postfix/pid
|
||||
chown root: /var/spool/postfix/
|
||||
chown root: /var/spool/postfix/pid
|
||||
|
||||
# Disable SMTPUTF8, because libraries (ICU) are missing in alpine
|
||||
postconf -e smtputf8_enable=no
|
||||
|
||||
# Update aliases database. It's not used, but postfix complains if the .db file is missing
|
||||
postalias /etc/postfix/aliases
|
||||
|
||||
# Disable local mail delivery
|
||||
postconf -e mydestination=
|
||||
|
||||
# Don't relay for any domains
|
||||
postconf -e relay_domains=
|
||||
|
||||
# Increase the allowed header size, the default (102400) is quite smallish
|
||||
postconf -e "header_size_limit=4096000"
|
||||
|
||||
if [ ! -z "$MESSAGE_SIZE_LIMIT" ]; then
|
||||
echo -e "‣ $notice Restricting message_size_limit to: ${emphasis}$MESSAGE_SIZE_LIMIT bytes${reset}"
|
||||
postconf -e "message_size_limit=$MESSAGE_SIZE_LIMIT"
|
||||
else
|
||||
# As this is a server-based service, allow any message size -- we hope the
|
||||
# sender knows what he is doing.
|
||||
echo -e "‣ $info Using ${emphasis}unlimited${reset} message size."
|
||||
postconf -e "message_size_limit=0"
|
||||
fi
|
||||
|
||||
# Reject invalid HELOs
|
||||
postconf -e smtpd_delay_reject=yes
|
||||
postconf -e smtpd_helo_required=yes
|
||||
postconf -e "smtpd_helo_restrictions=permit_mynetworks,reject_invalid_helo_hostname,permit"
|
||||
postconf -e "smtpd_sender_restrictions=permit_mynetworks"
|
||||
|
||||
# Set up host name
|
||||
if [ ! -z "$HOSTNAME" ]; then
|
||||
echo -e "‣ $notice Setting myhostname: ${emphasis}$HOSTNAME${reset}"
|
||||
postconf -e myhostname="$HOSTNAME"
|
||||
else
|
||||
postconf -# myhostname
|
||||
fi
|
||||
|
||||
if [ -z "$RELAYHOST_TLS_LEVEL" ]; then
|
||||
echo -e "‣ $info Setting smtp_tls_security_level: ${emphasis}may${reset}"
|
||||
postconf -e "smtp_tls_security_level=may"
|
||||
else
|
||||
echo -e "‣ $notice Setting smtp_tls_security_level: ${emphasis}$RELAYHOST_TLS_LEVEL${reset}"
|
||||
postconf -e "smtp_tls_security_level=$RELAYHOST_TLS_LEVEL"
|
||||
fi
|
||||
|
||||
# Set up a relay host, if needed
|
||||
if [ ! -z "$RELAYHOST" ]; then
|
||||
echo -en "‣ $notice Forwarding all emails to ${emphasis}$RELAYHOST${reset}"
|
||||
postconf -e "relayhost=$RELAYHOST"
|
||||
# Alternately, this could be a folder, like this:
|
||||
# smtp_tls_CApath
|
||||
postconf -e "smtp_tls_CAfile=/etc/ssl/certs/ca-certificates.crt"
|
||||
|
||||
if [ -n "$RELAYHOST_USERNAME" ] && [ -n "$RELAYHOST_PASSWORD" ]; then
|
||||
echo -e " using username ${emphasis}$RELAYHOST_USERNAME${reset} and password ${emphasis}(redacted)${reset}."
|
||||
echo "$RELAYHOST $RELAYHOST_USERNAME:$RELAYHOST_PASSWORD" >> /etc/postfix/sasl_passwd
|
||||
postmap hash:/etc/postfix/sasl_passwd
|
||||
postconf -e "smtp_sasl_auth_enable=yes"
|
||||
postconf -e "smtp_sasl_password_maps=hash:/etc/postfix/sasl_passwd"
|
||||
postconf -e "smtp_sasl_security_options=noanonymous"
|
||||
postconf -e "smtp_sasl_tls_security_options=noanonymous"
|
||||
else
|
||||
echo -e " without any authentication. ${emphasis}Make sure your server is configured to accept emails coming from this IP.${reset}"
|
||||
fi
|
||||
else
|
||||
echo -e "‣ $notice Will try to deliver emails directly to the final server. ${emphasis}Make sure your DNS is setup properly!${reset}"
|
||||
postconf -# relayhost
|
||||
postconf -# smtp_sasl_auth_enable
|
||||
postconf -# smtp_sasl_password_maps
|
||||
postconf -# smtp_sasl_security_options
|
||||
fi
|
||||
|
||||
if [ ! -z "$MYNETWORKS" ]; then
|
||||
echo -e "‣ $notice Using custom allowed networks: ${emphasis}$MYNETWORKS${reset}"
|
||||
else
|
||||
echo -e "‣ $info Using default private network list for trusted networks."
|
||||
MYNETWORKS="127.0.0.0/8,10.0.0.0/8,172.16.0.0/12,192.168.0.0/16"
|
||||
fi
|
||||
|
||||
postconf -e "mynetworks=$MYNETWORKS"
|
||||
|
||||
if [ ! -z "$INBOUND_DEBUGGING" ]; then
|
||||
echo -e "‣ $notice Enabling additional debbuging for: ${emphasis}$MYNETWORKS${reset}"
|
||||
postconf -e "debug_peer_list=$MYNETWORKS"
|
||||
|
||||
sed -i -E 's/^[ \t]*#?[ \t]*LogWhy[ \t]*.+$/LogWhy yes/' /etc/opendkim/opendkim.conf
|
||||
if ! egrep -q '^LogWhy' /etc/opendkim/opendkim.conf; then
|
||||
echo >> /etc/opendkim/opendkim.conf
|
||||
echo "LogWhy yes" >> /etc/opendkim/opendkim.conf
|
||||
fi
|
||||
else
|
||||
sed -i -E 's/^[ \t]*#?[ \t]*LogWhy[ \t]*.+$/LogWhy no/' /etc/opendkim/opendkim.conf
|
||||
if ! egrep -q '^LogWhy' /etc/opendkim/opendkim.conf; then
|
||||
echo >> /etc/opendkim/opendkim.conf
|
||||
echo "LogWhy no" >> /etc/opendkim/opendkim.conf
|
||||
fi
|
||||
fi
|
||||
|
||||
if [ ! -z "$ALLOWED_SENDER_DOMAINS" ]; then
|
||||
echo -en "‣ $info Setting up allowed SENDER domains:"
|
||||
allowed_senders=/etc/postfix/allowed_senders
|
||||
rm -f $allowed_senders $allowed_senders.db > /dev/null
|
||||
touch $allowed_senders
|
||||
for i in $ALLOWED_SENDER_DOMAINS; do
|
||||
echo -ne " ${emphasis}$i${reset}"
|
||||
echo -e "$i\tOK" >> $allowed_senders
|
||||
done
|
||||
echo
|
||||
postmap $allowed_senders
|
||||
|
||||
postconf -e "smtpd_restriction_classes=allowed_domains_only"
|
||||
postconf -e "allowed_domains_only=permit_mynetworks, reject_non_fqdn_sender reject"
|
||||
postconf -e "smtpd_recipient_restrictions=reject_non_fqdn_recipient, reject_unknown_recipient_domain, check_sender_access hash:$allowed_senders, reject"
|
||||
|
||||
# Since we are behind closed doors, let's just permit all relays.
|
||||
postconf -e "smtpd_relay_restrictions=permit"
|
||||
elif [ -z "$ALLOW_EMPTY_SENDER_DOMAINS" ]; then
|
||||
echo -e "ERROR: You need to specify ALLOWED_SENDER_DOMAINS otherwise Postfix will not run!"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ ! -z "$MASQUERADED_DOMAINS" ]; then
|
||||
echo -e "‣ $notice Setting up address masquerading: ${emphasis}$MASQUERADED_DOMAINS${reset}"
|
||||
postconf -e "masquerade_domains = $MASQUERADED_DOMAINS"
|
||||
postconf -e "local_header_rewrite_clients = static:all"
|
||||
fi
|
||||
|
||||
if [ ! -z "$SMTP_HEADER_CHECKS" ]; then
|
||||
if [ "$SMTP_HEADER_CHECKS" == "1" ]; then
|
||||
echo -e "‣ $info Using default file for SMTP header checks"
|
||||
SMTP_HEADER_CHECKS="regexp:/etc/postfix/smtp_header_checks"
|
||||
fi
|
||||
|
||||
FORMAT=$(echo "$SMTP_HEADER_CHECKS" | cut -d: -f1)
|
||||
FILE=$(echo "$SMTP_HEADER_CHECKS" | cut -d: -f2-)
|
||||
|
||||
if [ "$FORMAT" == "$FILE" ]; then
|
||||
echo -e "‣ $warn No Postfix format defined for file ${emphasis}SMTP_HEADER_CHECKS${reset}. Using default ${emphasis}regexp${reset}. To avoid this message, set format explicitly, e.g. ${emphasis}SMTP_HEADER_CHECKS=regexp:$SMTP_HEADER_CHECKS${reset}."
|
||||
FORMAT="regexp"
|
||||
fi
|
||||
|
||||
if [ -f "$FILE" ]; then
|
||||
echo -e "‣ $notice Setting up ${emphasis}smtp_header_checks${reset} to ${emphasis}$FORMAT:$FILE${reset}"
|
||||
postconf -e "smtp_header_checks=$FORMAT:$FILE"
|
||||
else
|
||||
echo -e "‣ $error File ${emphasis}$FILE${reset} cannot be found. Please make sure your SMTP_HEADER_CHECKS variable points to the right file. Startup aborted."
|
||||
exit 2
|
||||
fi
|
||||
fi
|
||||
|
||||
DKIM_ENABLED=
|
||||
if [ -d /etc/opendkim/keys ] && [ ! -z "$(find /etc/opendkim/keys -type f ! -name .)" ]; then
|
||||
DKIM_ENABLED=", ${emphasis}opendkim${reset}"
|
||||
echo -e "‣ $notice Configuring OpenDKIM."
|
||||
mkdir -p /var/run/opendkim
|
||||
chown -R opendkim:opendkim /var/run/opendkim
|
||||
dkim_socket=$(cat /etc/opendkim/opendkim.conf | egrep ^Socket | awk '{ print $2 }')
|
||||
if [ $(echo "$dkim_socket" | cut -d: -f1) == "inet" ]; then
|
||||
dkim_socket=$(echo "$dkim_socket" | cut -d: -f2)
|
||||
dkim_socket="inet:$(echo "$dkim_socket" | cut -d@ -f2):$(echo "$dkim_socket" | cut -d@ -f1)"
|
||||
fi
|
||||
echo -e " ...using socket $dkim_socket"
|
||||
|
||||
postconf -e "milter_protocol=6"
|
||||
postconf -e "milter_default_action=accept"
|
||||
postconf -e "smtpd_milters=$dkim_socket"
|
||||
postconf -e "non_smtpd_milters=$dkim_socket"
|
||||
|
||||
echo > /etc/opendkim/TrustedHosts
|
||||
echo > /etc/opendkim/KeyTable
|
||||
echo > /etc/opendkim/SigningTable
|
||||
|
||||
# Since it's an internal service anyways, it's safe
|
||||
# to assume that *all* hosts are trusted.
|
||||
echo "0.0.0.0/0" > /etc/opendkim/TrustedHosts
|
||||
|
||||
if [ ! -z "$ALLOWED_SENDER_DOMAINS" ]; then
|
||||
for i in $ALLOWED_SENDER_DOMAINS; do
|
||||
private_key=/etc/opendkim/keys/$i.private
|
||||
if [ -f $private_key ]; then
|
||||
echo -e " ...for domain ${emphasis}$i${reset}"
|
||||
echo "mail._domainkey.$i $i:mail:$private_key" >> /etc/opendkim/KeyTable
|
||||
echo "*@$i mail._domainkey.$i" >> /etc/opendkim/SigningTable
|
||||
else
|
||||
echo " ...$warn skipping for domain ${emphasis}$i${reset}. File $private_key not found!"
|
||||
fi
|
||||
done
|
||||
fi
|
||||
else
|
||||
echo -e "‣ $info No DKIM keys found, will not use DKIM."
|
||||
postconf -# smtpd_milters
|
||||
postconf -# non_smtpd_milters
|
||||
fi
|
||||
|
||||
# Use 587 (submission)
|
||||
sed -i -r -e 's/^#submission/submission/' /etc/postfix/master.cf
|
||||
|
||||
if [ -d /docker-init.db/ ]; then
|
||||
echo -e "‣ $notice Executing any found custom scripts..."
|
||||
for f in /docker-init.db/*; do
|
||||
case "$f" in
|
||||
*.sh) chmod +x "$f"; echo -e "\trunning ${emphasis}$f${reset}"; . "$f" ;;
|
||||
*) echo "$0: ignoring $f" ;;
|
||||
esac
|
||||
done
|
||||
fi
|
||||
|
||||
echo -e "‣ $notice Starting: ${emphasis}rsyslog${reset}, ${emphasis}postfix${reset}$DKIM_ENABLED"
|
||||
exec supervisord -c /etc/supervisord.conf
|
||||
|
268
scripts/common-run.sh
Normal file
268
scripts/common-run.sh
Normal file
|
@ -0,0 +1,268 @@
|
|||
#!/bin/sh
|
||||
|
||||
announce_startup() {
|
||||
echo -e "******************************"
|
||||
echo -e "**** POSTFIX STARTING UP *****"
|
||||
echo -e "******************************"
|
||||
}
|
||||
|
||||
setup_timezone() {
|
||||
if [ ! -z "$TZ" ]; then
|
||||
TZ_FILE="/usr/share/zoneinfo/$TZ"
|
||||
if [ -f "$TZ_FILE" ]; then
|
||||
echo -e "‣ $notice Setting container timezone to: ${emphasis}$TZ${reset}"
|
||||
ln -snf "$TZ_FILE" /etc/localtime
|
||||
echo "$TZ" > /etc/timezone
|
||||
else
|
||||
echo -e "‣ $warn Cannot set timezone to: ${emphasis}$TZ${reset} -- this timezone does not exist."
|
||||
fi
|
||||
else
|
||||
echo -e "‣ $info Not setting any timezone for the container"
|
||||
fi
|
||||
}
|
||||
|
||||
reown_folders() {
|
||||
mkdir -p /var/spool/postfix/ && mkdir -p /var/spool/postfix/pid
|
||||
chown root: /var/spool/postfix/
|
||||
chown root: /var/spool/postfix/pid
|
||||
}
|
||||
|
||||
postfix_disable_utf8() {
|
||||
postconf -e smtputf8_enable=no
|
||||
}
|
||||
|
||||
postfix_create_aliases() {
|
||||
postalias /etc/postfix/aliases
|
||||
}
|
||||
|
||||
postfix_disable_local_mail_delivery() {
|
||||
postconf -e mydestination=
|
||||
}
|
||||
|
||||
postfix_disable_domain_relays() {
|
||||
postconf -e relay_domains=
|
||||
}
|
||||
|
||||
postfix_increase_header_size_limit() {
|
||||
postconf -e "header_size_limit=4096000"
|
||||
}
|
||||
|
||||
postfix_restrict_message_size() {
|
||||
if [ ! -z "$MESSAGE_SIZE_LIMIT" ]; then
|
||||
echo -e "‣ $notice Restricting message_size_limit to: ${emphasis}$MESSAGE_SIZE_LIMIT bytes${reset}"
|
||||
postconf -e "message_size_limit=$MESSAGE_SIZE_LIMIT"
|
||||
else
|
||||
# As this is a server-based service, allow any message size -- we hope the
|
||||
# sender knows what he is doing.
|
||||
echo -e "‣ $info Using ${emphasis}unlimited${reset} message size."
|
||||
postconf -e "message_size_limit=0"
|
||||
fi
|
||||
}
|
||||
|
||||
postfix_reject_invalid_helos() {
|
||||
postconf -e smtpd_delay_reject=yes
|
||||
postconf -e smtpd_helo_required=yes
|
||||
postconf -e "smtpd_helo_restrictions=permit_mynetworks,reject_invalid_helo_hostname,permit"
|
||||
postconf -e "smtpd_sender_restrictions=permit_mynetworks"
|
||||
}
|
||||
|
||||
postfix_set_hostname() {
|
||||
if [ ! -z "$HOSTNAME" ]; then
|
||||
echo -e "‣ $notice Setting myhostname: ${emphasis}$HOSTNAME${reset}"
|
||||
postconf -e myhostname="$HOSTNAME"
|
||||
else
|
||||
postconf -# myhostname
|
||||
fi
|
||||
}
|
||||
|
||||
postfix_set_relay_tls_level() {
|
||||
if [ -z "$RELAYHOST_TLS_LEVEL" ]; then
|
||||
echo -e "‣ $info Setting smtp_tls_security_level: ${emphasis}may${reset}"
|
||||
postconf -e "smtp_tls_security_level=may"
|
||||
else
|
||||
echo -e "‣ $notice Setting smtp_tls_security_level: ${emphasis}$RELAYHOST_TLS_LEVEL${reset}"
|
||||
postconf -e "smtp_tls_security_level=$RELAYHOST_TLS_LEVEL"
|
||||
fi
|
||||
}
|
||||
|
||||
postfix_setup_relayhost() {
|
||||
if [ ! -z "$RELAYHOST" ]; then
|
||||
echo -en "‣ $notice Forwarding all emails to ${emphasis}$RELAYHOST${reset}"
|
||||
postconf -e "relayhost=$RELAYHOST"
|
||||
# Alternately, this could be a folder, like this:
|
||||
# smtp_tls_CApath
|
||||
postconf -e "smtp_tls_CAfile=/etc/ssl/certs/ca-certificates.crt"
|
||||
|
||||
if [ -n "$RELAYHOST_USERNAME" ] && [ -n "$RELAYHOST_PASSWORD" ]; then
|
||||
echo -e " using username ${emphasis}$RELAYHOST_USERNAME${reset} and password ${emphasis}(redacted)${reset}."
|
||||
echo "$RELAYHOST $RELAYHOST_USERNAME:$RELAYHOST_PASSWORD" >> /etc/postfix/sasl_passwd
|
||||
postmap hash:/etc/postfix/sasl_passwd
|
||||
postconf -e "smtp_sasl_auth_enable=yes"
|
||||
postconf -e "smtp_sasl_password_maps=hash:/etc/postfix/sasl_passwd"
|
||||
postconf -e "smtp_sasl_security_options=noanonymous"
|
||||
postconf -e "smtp_sasl_tls_security_options=noanonymous"
|
||||
else
|
||||
echo -e " without any authentication. ${emphasis}Make sure your server is configured to accept emails coming from this IP.${reset}"
|
||||
fi
|
||||
else
|
||||
echo -e "‣ $notice Will try to deliver emails directly to the final server. ${emphasis}Make sure your DNS is setup properly!${reset}"
|
||||
postconf -# relayhost
|
||||
postconf -# smtp_sasl_auth_enable
|
||||
postconf -# smtp_sasl_password_maps
|
||||
postconf -# smtp_sasl_security_options
|
||||
fi
|
||||
}
|
||||
|
||||
postfix_setup_networks() {
|
||||
if [ ! -z "$MYNETWORKS" ]; then
|
||||
echo -e "‣ $notice Using custom allowed networks: ${emphasis}$MYNETWORKS${reset}"
|
||||
else
|
||||
echo -e "‣ $info Using default private network list for trusted networks."
|
||||
MYNETWORKS="127.0.0.0/8,10.0.0.0/8,172.16.0.0/12,192.168.0.0/16"
|
||||
fi
|
||||
|
||||
postconf -e "mynetworks=$MYNETWORKS"
|
||||
}
|
||||
|
||||
postfix_setup_debugging() {
|
||||
if [ ! -z "$INBOUND_DEBUGGING" ]; then
|
||||
echo -e "‣ $notice Enabling additional debbuging for: ${emphasis}$MYNETWORKS${reset}, as INBOUND_DEBUGGING=''${INBOUND_DEBUGGING}''"
|
||||
postconf -e "debug_peer_list=$MYNETWORKS"
|
||||
|
||||
sed -i -E 's/^[ \t]*#?[ \t]*LogWhy[ \t]*.+$/LogWhy yes/' /etc/opendkim/opendkim.conf
|
||||
if ! egrep -q '^LogWhy' /etc/opendkim/opendkim.conf; then
|
||||
echo >> /etc/opendkim/opendkim.conf
|
||||
echo "LogWhy yes" >> /etc/opendkim/opendkim.conf
|
||||
fi
|
||||
else
|
||||
echo -e "‣ $info Debugging is disabled.${reset}"
|
||||
sed -i -E 's/^[ \t]*#?[ \t]*LogWhy[ \t]*.+$/LogWhy no/' /etc/opendkim/opendkim.conf
|
||||
if ! egrep -q '^LogWhy' /etc/opendkim/opendkim.conf; then
|
||||
echo >> /etc/opendkim/opendkim.conf
|
||||
echo "LogWhy no" >> /etc/opendkim/opendkim.conf
|
||||
fi
|
||||
fi
|
||||
}
|
||||
|
||||
postfix_setup_sender_domains() {
|
||||
if [ ! -z "$ALLOWED_SENDER_DOMAINS" ]; then
|
||||
echo -en "‣ $info Setting up allowed SENDER domains:"
|
||||
allowed_senders=/etc/postfix/allowed_senders
|
||||
rm -f $allowed_senders $allowed_senders.db > /dev/null
|
||||
touch $allowed_senders
|
||||
for i in $ALLOWED_SENDER_DOMAINS; do
|
||||
echo -ne " ${emphasis}$i${reset}"
|
||||
echo -e "$i\tOK" >> $allowed_senders
|
||||
done
|
||||
echo
|
||||
postmap $allowed_senders
|
||||
|
||||
postconf -e "smtpd_restriction_classes=allowed_domains_only"
|
||||
postconf -e "allowed_domains_only=permit_mynetworks, reject_non_fqdn_sender reject"
|
||||
postconf -e "smtpd_recipient_restrictions=reject_non_fqdn_recipient, reject_unknown_recipient_domain, check_sender_access hash:$allowed_senders, reject"
|
||||
|
||||
# Since we are behind closed doors, let's just permit all relays.
|
||||
postconf -e "smtpd_relay_restrictions=permit"
|
||||
elif [ -z "$ALLOW_EMPTY_SENDER_DOMAINS" ]; then
|
||||
echo -e "ERROR: You need to specify ALLOWED_SENDER_DOMAINS otherwise Postfix will not run!"
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
postfix_setup_masquarading() {
|
||||
if [ ! -z "$MASQUERADED_DOMAINS" ]; then
|
||||
echo -e "‣ $notice Setting up address masquerading: ${emphasis}$MASQUERADED_DOMAINS${reset}"
|
||||
postconf -e "masquerade_domains = $MASQUERADED_DOMAINS"
|
||||
postconf -e "local_header_rewrite_clients = static:all"
|
||||
fi
|
||||
}
|
||||
|
||||
postfix_setup_header_checks() {
|
||||
if [ ! -z "$SMTP_HEADER_CHECKS" ]; then
|
||||
if [ "$SMTP_HEADER_CHECKS" == "1" ]; then
|
||||
echo -e "‣ $info Using default file for SMTP header checks"
|
||||
SMTP_HEADER_CHECKS="regexp:/etc/postfix/smtp_header_checks"
|
||||
fi
|
||||
|
||||
FORMAT=$(echo "$SMTP_HEADER_CHECKS" | cut -d: -f1)
|
||||
FILE=$(echo "$SMTP_HEADER_CHECKS" | cut -d: -f2-)
|
||||
|
||||
if [ "$FORMAT" == "$FILE" ]; then
|
||||
echo -e "‣ $warn No Postfix format defined for file ${emphasis}SMTP_HEADER_CHECKS${reset}. Using default ${emphasis}regexp${reset}. To avoid this message, set format explicitly, e.g. ${emphasis}SMTP_HEADER_CHECKS=regexp:$SMTP_HEADER_CHECKS${reset}."
|
||||
FORMAT="regexp"
|
||||
fi
|
||||
|
||||
if [ -f "$FILE" ]; then
|
||||
echo -e "‣ $notice Setting up ${emphasis}smtp_header_checks${reset} to ${emphasis}$FORMAT:$FILE${reset}"
|
||||
postconf -e "smtp_header_checks=$FORMAT:$FILE"
|
||||
else
|
||||
echo -e "‣ $error File ${emphasis}$FILE${reset} cannot be found. Please make sure your SMTP_HEADER_CHECKS variable points to the right file. Startup aborted."
|
||||
exit 2
|
||||
fi
|
||||
fi
|
||||
}
|
||||
|
||||
postfix_setup_dkim() {
|
||||
local DKIM_ENABLED=
|
||||
local domain_dkim_selector="mail"
|
||||
if [ -d /etc/opendkim/keys ] && [ ! -z "$(find /etc/opendkim/keys -type f ! -name .)" ]; then
|
||||
DKIM_ENABLED=", ${emphasis}opendkim${reset}"
|
||||
echo -e "‣ $notice Configuring OpenDKIM."
|
||||
mkdir -p /var/run/opendkim
|
||||
chown -R opendkim:opendkim /var/run/opendkim
|
||||
dkim_socket=$(cat /etc/opendkim/opendkim.conf | egrep ^Socket | awk '{ print $2 }')
|
||||
if [ $(echo "$dkim_socket" | cut -d: -f1) == "inet" ]; then
|
||||
dkim_socket=$(echo "$dkim_socket" | cut -d: -f2)
|
||||
dkim_socket="inet:$(echo "$dkim_socket" | cut -d@ -f2):$(echo "$dkim_socket" | cut -d@ -f1)"
|
||||
fi
|
||||
echo -e " ...using socket $dkim_socket"
|
||||
|
||||
postconf -e "milter_protocol=6"
|
||||
postconf -e "milter_default_action=accept"
|
||||
postconf -e "smtpd_milters=$dkim_socket"
|
||||
postconf -e "non_smtpd_milters=$dkim_socket"
|
||||
|
||||
echo > /etc/opendkim/TrustedHosts
|
||||
echo > /etc/opendkim/KeyTable
|
||||
echo > /etc/opendkim/SigningTable
|
||||
|
||||
# Since it's an internal service anyways, it's safe
|
||||
# to assume that *all* hosts are trusted.
|
||||
echo "0.0.0.0/0" > /etc/opendkim/TrustedHosts
|
||||
|
||||
if [ ! -z "$ALLOWED_SENDER_DOMAINS" ]; then
|
||||
for i in $ALLOWED_SENDER_DOMAINS; do
|
||||
private_key=/etc/opendkim/keys/$i.private
|
||||
if [ -f $private_key ]; then
|
||||
domain_dkim_selector="$(get_dkim_selector "$i")"
|
||||
echo -e " ...for domain ${emphasis}$i${reset} (selector: ${emphasis}${domain_dkim_selector}${reset})"
|
||||
echo "${domain_dkim_selector}._domainkey.$i $i:mail:$private_key" >> /etc/opendkim/KeyTable
|
||||
echo "*@$i ${domain_dkim_selector}._domainkey.$i" >> /etc/opendkim/SigningTable
|
||||
else
|
||||
echo " ...$warn skipping for domain ${emphasis}$i${reset}. File $private_key not found!"
|
||||
fi
|
||||
done
|
||||
fi
|
||||
else
|
||||
echo -e "‣ $info No DKIM keys found, will not use DKIM."
|
||||
postconf -# smtpd_milters
|
||||
postconf -# non_smtpd_milters
|
||||
fi
|
||||
}
|
||||
|
||||
postfix_open_submission_port() {
|
||||
# Use 587 (submission)
|
||||
sed -i -r -e 's/^#submission/submission/' /etc/postfix/master.cf
|
||||
}
|
||||
|
||||
execute_post_init_scripts() {
|
||||
if [ -d /docker-init.db/ ]; then
|
||||
echo -e "‣ $notice Executing any found custom scripts..."
|
||||
for f in /docker-init.db/*; do
|
||||
case "$f" in
|
||||
*.sh) chmod +x "$f"; echo -e "\trunning ${emphasis}$f${reset}"; . "$f" ;;
|
||||
*) echo "$0: ignoring $f" ;;
|
||||
esac
|
||||
done
|
||||
fi
|
||||
}
|
|
@ -44,4 +44,34 @@ fi
|
|||
info="${green}INFO:${reset}"
|
||||
notice="${yellow}NOTE:${reset}"
|
||||
warn="${orange}WARN:${reset}"
|
||||
error="${red}ERROR:${reset}"
|
||||
error="${red}ERROR:${reset}"
|
||||
|
||||
# Return a DKIM selector from DKIM_SELECTOR environment variable.
|
||||
# See README.md for details.
|
||||
get_dkim_selector() {
|
||||
if [ -z "${DKIM_SELECTOR}" ]; then
|
||||
echo "mail"
|
||||
return
|
||||
fi
|
||||
|
||||
local domain="$1"
|
||||
local old="$IFS"
|
||||
local no_domain_selector="mail"
|
||||
local IFS=","
|
||||
for part in ${DKIM_SELECTOR}; do
|
||||
if contains "$part" "="; then
|
||||
k="$(echo "$part" | cut -f1 -d=)"
|
||||
v="$(echo "$part" | cut -f2 -d=)"
|
||||
if [ "$k" == "$domain" ]; then
|
||||
echo "$v"
|
||||
IFS="${old}"
|
||||
return
|
||||
fi
|
||||
else
|
||||
no_domain_selector="$part"
|
||||
fi
|
||||
done
|
||||
IFS="${old}"
|
||||
|
||||
echo "${no_domain_selector}"
|
||||
}
|
30
scripts/run.sh
Normal file
30
scripts/run.sh
Normal file
|
@ -0,0 +1,30 @@
|
|||
#!/bin/sh
|
||||
set -e
|
||||
|
||||
. /common.sh
|
||||
. /common-run.sh
|
||||
|
||||
announce_startup # Print startup banner
|
||||
setup_timezone # Check if we need to configure the container timezone
|
||||
reown_folders # Make and reown postfix folders
|
||||
postfix_disable_utf8 # Disable SMTPUTF8, because libraries (ICU) are missing in alpine
|
||||
postfix_create_aliases # Update aliases database. It's not used, but postfix complains if the .db file is missing
|
||||
postfix_disable_local_mail_delivery # Disable local mail delivery
|
||||
postfix_disable_domain_relays # Don't relay for any domains
|
||||
postfix_increase_header_size_limit # Increase the allowed header size, the default (102400) is quite smallish
|
||||
postfix_restrict_message_size # Restrict the size of messages (or set them to unlimited)
|
||||
postfix_reject_invalid_helos # Reject invalid HELOs
|
||||
postfix_set_hostname # Set up host name
|
||||
postfix_set_relay_tls_level # Set TLS level security for relays
|
||||
postfix_setup_relayhost # Setup a relay host, if defined
|
||||
postfix_setup_networks # Set MYNETWORKS
|
||||
postfix_setup_debugging # Enable debugging, if defined
|
||||
postfix_setup_sender_domains # Configure allowed sender domains
|
||||
postfix_setup_masquarading # Setup masquaraded domains
|
||||
postfix_setup_header_checks # Enable SMTP header checks, if defined
|
||||
postfix_setup_dkim # Configure DKIM, if enabled
|
||||
postfix_open_submission_port # Enable the submission port
|
||||
execute_post_init_scripts # Execute any scripts found in /docker-init.db/
|
||||
|
||||
echo -e "‣ $notice Starting: ${emphasis}rsyslog${reset}, ${emphasis}postfix${reset}$DKIM_ENABLED"
|
||||
exec supervisord -c /etc/supervisord.conf
|
36
test.sh
36
test.sh
|
@ -1,36 +0,0 @@
|
|||
#!/bin/sh
|
||||
docker build . -t boky/postfix
|
||||
docker-compose up -d
|
||||
|
||||
FROM=$1
|
||||
TO=$2
|
||||
|
||||
# Wait for postfix to startup
|
||||
echo "Waiting for startup..."
|
||||
while ! docker ps | fgrep postfix_test_587 | grep -q healthy; do
|
||||
sleep 1
|
||||
done
|
||||
|
||||
cat <<EOF | nc -C localhost 1587
|
||||
HELO test
|
||||
MAIL FROM:$FROM
|
||||
RCPT TO:$TO
|
||||
DATA
|
||||
Subject: Postfix message test
|
||||
From: $FROM
|
||||
To: $TO
|
||||
Date: $(date)
|
||||
Content-Type: text/plain
|
||||
|
||||
This is a simple text of message sending using boky/postfix.
|
||||
.
|
||||
QUIT
|
||||
EOF
|
||||
|
||||
# Wait for email to be delivered
|
||||
echo "Waiting to shutdown..."
|
||||
sleep 5
|
||||
|
||||
# Shut down tests
|
||||
docker-compose down
|
||||
|
3
unit-tests.sh
Executable file
3
unit-tests.sh
Executable file
|
@ -0,0 +1,3 @@
|
|||
#!/bin/sh
|
||||
cd unit-tests
|
||||
docker-compose up --build --abort-on-container-exit --exit-code-from tests
|
9
unit-tests/Dockerfile
Normal file
9
unit-tests/Dockerfile
Normal file
|
@ -0,0 +1,9 @@
|
|||
|
||||
FROM alpine:latest
|
||||
|
||||
RUN apk add --no-cache bash bats
|
||||
|
||||
WORKDIR /code
|
||||
ENTRYPOINT ["/usr/bin/bats"]
|
||||
|
||||
CMD ["-v"]
|
10
unit-tests/docker-compose.yml
Normal file
10
unit-tests/docker-compose.yml
Normal file
|
@ -0,0 +1,10 @@
|
|||
version: '3.7'
|
||||
services:
|
||||
tests:
|
||||
image: "boky/postfix-unit-test"
|
||||
restart: "no"
|
||||
volumes:
|
||||
- "..:/code"
|
||||
build:
|
||||
context: .
|
||||
command: "/code/unit-tests/"
|
56
unit-tests/get_dkim_selector.bats
Normal file
56
unit-tests/get_dkim_selector.bats
Normal file
|
@ -0,0 +1,56 @@
|
|||
#!/usr/bin/env bats
|
||||
|
||||
load /code/scripts/common.sh
|
||||
|
||||
@test "check if 'mail' when DKIM_SELECTOR is not defined" {
|
||||
result="$(get_dkim_selector)"
|
||||
[ "$result" == "mail" ]
|
||||
}
|
||||
|
||||
@test "check if 'xxx' when DKIM_SELECTOR is defined" {
|
||||
local DKIM_SELECTOR="xxx"
|
||||
result="$(get_dkim_selector)"
|
||||
[ "$result" == "xxx" ]
|
||||
}
|
||||
|
||||
@test "check if 'xxx' without domain when DKIM_SELECTOR=xxx,example.org=yyy,example.com=zzz" {
|
||||
local DKIM_SELECTOR="xxx,example.org=yyy,example.com=zzz"
|
||||
result="$(get_dkim_selector example.org)"
|
||||
echo "result=$result"
|
||||
[ "$result" == "yyy" ]
|
||||
}
|
||||
|
||||
@test "check if 'yyy' when domain is example.org DKIM_SELECTOR=xxx,example.org=yyy,example.com=zzz" {
|
||||
local DKIM_SELECTOR="xxx,example.org=yyy,example.com=zzz"
|
||||
result="$(get_dkim_selector example.org)"
|
||||
echo "result=$result"
|
||||
[ "$result" == "yyy" ]
|
||||
}
|
||||
|
||||
@test "check if 'zzz' when domain is example.org DKIM_SELECTOR=xxx,example.org=yyy,example.com=zzz" {
|
||||
local DKIM_SELECTOR="xxx,example.org=yyy,example.com=zzz"
|
||||
result="$(get_dkim_selector example.com)"
|
||||
echo "result=$result"
|
||||
[ "$result" == "zzz" ]
|
||||
}
|
||||
|
||||
@test "check if 'aaa' when domain is example.net DKIM_SELECTOR=xxx,example.org=yyy,example.com=zzz,bbb,aaa" {
|
||||
local DKIM_SELECTOR="xxx,example.org=yyy,example.com=zzz,bbb,aaa"
|
||||
result="$(get_dkim_selector example.net)"
|
||||
echo "result=$result"
|
||||
[ "$result" == "aaa" ]
|
||||
}
|
||||
|
||||
@test "check if 'bbb' when domain is example.net DKIM_SELECTOR=example.com=bbb" {
|
||||
local DKIM_SELECTOR="example.com=bbb"
|
||||
result="$(get_dkim_selector example.com)"
|
||||
echo "result=$result"
|
||||
[ "$result" == "bbb" ]
|
||||
}
|
||||
|
||||
@test "check if 'mail' when domain is example.net DKIM_SELECTOR=example.com=bbb" {
|
||||
local DKIM_SELECTOR="example.com=bbb"
|
||||
result="$(get_dkim_selector example.net)"
|
||||
echo "result=$result"
|
||||
[ "$result" == "mail" ]
|
||||
}
|
Loading…
Reference in a new issue