Initial support for DKIM and testing

This commit brings two important features:
- **DKIM support** It's now possible to configure this postfix
  image to sign messages using DKIM by simply generating the keys
  and providing them in the approprate folder. This should bring
  us one step closer to directly sending out emails without relying
  on a 3rd-party proxy.
- **test support** A nice and handy script, conviniently called
  `test.sh` has been provided, builds the image, spins it up and
  tries to send out an email. You'll need `docker-compose` to run
  it, though.
This commit is contained in:
Bojan Čekrlić 2019-02-19 10:49:55 +01:00
parent d7b6bdc32c
commit 928e5d64c7
10 changed files with 210 additions and 15 deletions

View file

@ -1,5 +1,5 @@
FROM alpine:latest
MAINTAINER Bojan Cekrlic - https://github.com/bokysan/docker-postfix/
LABEL maintaner="Bojan Cekrlic - https://github.com/bokysan/docker-postfix/"
# See README.md for details
@ -27,23 +27,25 @@ ENV INBOUND_DEBUGGING=
# Install supervisor, postfix
RUN true && \
apk add --no-cache --update postfix ca-certificates tzdata supervisor rsyslog && \
apk add --no-cache --update postfix ca-certificates tzdata supervisor rsyslog opendkim && \
apk add --no-cache --upgrade musl musl-utils && \
(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 run.sh /run.sh
RUN chmod +x /run.sh
COPY opendkim.sh /opendkim.sh
RUN chmod +x /run.sh /opendkim.sh
# Set up spool volume
VOLUME [ "/var/spool/postfix", "/etc/postfix" ]
# Set up volumes
VOLUME [ "/var/spool/postfix", "/etc/postfix", "/etc/opendkim/keys" ]
# Run supervisord
USER root
WORKDIR /tmp
EXPOSE 587
ENTRYPOINT ["/run.sh"]
CMD ["/bin/sh", "-c", "/run.sh"]

View file

@ -21,13 +21,18 @@ To run the container, do the following:
docker run --rm --name postfix -e "ALLOWED_SENDER_DOMAINS=example.com" -p 1587:587 boky/postfix
```
You can now send emails by using `localhost:1587` as your SMTP server address. **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.
You can now send emails by using `localhost:1587` as your SMTP server address. Of course, if
you haven't configured your `example.com` domain to allow sending from this IP (see
[openspf](http://www.openspf.org/)), your emails will most likely be regarded as spam.
All standard caveats of configuring the SMTP server apply -- e.g. you'll need to make sure your DNS
entries are updated properly if you don't want your emails marked as spam.
**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:
@ -40,6 +45,7 @@ $RELAYHOST_PASSWORD = An (optional) login password for the relay server
$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
$MASQUERADED_DOMAINS = domains where you want to masquerade internal hosts
```
### `HOSTNAME`
@ -132,6 +138,27 @@ Example:
docker run --rm --name postfix -e "ALLOWED_SENDER_DOMAINS=example.com example.org" -e "MASQUERADED_DOMAINS=example.com" -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.
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.
```
mkdir -p /host/keys; cd /host/keys
mkdir example.com; cd example.com
opendkim-genkey -s mail -d example.com
cd ..
mkdir example.org; cd example.org
opendkim-genkey -s mail -d example.corg
```
`opendkim-genkey` is usually in your favourite distribution provided by installing `opendkim-tools` or `opendkim-utils`.
Add the created `mail.txt` files to your DNS record. Afterwards, just mount `/etc/opendkim/keys` into your image and DKIM
will be used automatically, e.g.:
```
docker run --rm --name postfix -e "ALLOWED_SENDER_DOMAINS=example.com example.org" -v /host/keys:/etc/opendkim/keys -p 1587:587 boky/postfix
```
## Extending the image
@ -142,7 +169,7 @@ startup script.
E.g.: create a custom `Dockerfile` like this:
```
FROM boky/postfix
MAINTAINER Some Randombloke "randombloke@example.com"
LABEL maintainer="Jack Sparrow <jack.sparrow@theblackpearl.example.com>"
ADD Dockerfiles/additional-config.sh /docker-init.db/
```
@ -161,4 +188,4 @@ 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
which will use `UID:GID` of `100:101`.
which will use `UID:GID` of `100:101`. `opendkim` will run under account `102:103`.

19
docker-compose.yml Normal file
View file

@ -0,0 +1,19 @@
version: '3.6'
services:
postfix_test_587:
hostname: "postfix"
image: "boky/postfix"
restart: always
healthcheck:
test: [ "CMD", "sh", "-c", "netstat -an | fgrep 587 | fgrep -q LISTEN" ]
interval: 10s
timeout: 5s
start_period: 10s
retries: 2
ports:
- "1587:587"
volumes:
- "./test-keys:/etc/opendkim/keys"
environment:
ALLOWED_SENDER_DOMAINS: "example.org"
INBOUND_DEBUGGING: 1

20
opendkim.conf Normal file
View file

@ -0,0 +1,20 @@
AutoRestart Yes
AutoRestartRate 10/1h
UMask 002
Syslog yes
SyslogSuccess Yes
LogWhy Yes
Canonicalization relaxed/simple
ExternalIgnoreList refile:/etc/opendkim/TrustedHosts
InternalHosts refile:/etc/opendkim/TrustedHosts
KeyTable refile:/etc/opendkim/KeyTable
SigningTable refile:/etc/opendkim/SigningTable
Mode sv
PidFile /var/run/opendkim/opendkim.pid
SignatureAlgorithm rsa-sha256
UserID opendkim:opendkim
Socket inet:8891@localhost

10
opendkim.sh Normal file
View file

@ -0,0 +1,10 @@
#!/bin/sh
if [ ! -d /etc/opendkim/keys ]; then
sleep 9999999999999999999
elif [ -z "$(find /etc/opendkim/keys -type d ! -name .)" ]; then
sleep 9999999999999999999
else
/usr/sbin/opendkim -D -f -x /etc/opendkim/opendkim.conf
fi

63
run.sh
View file

@ -154,11 +154,22 @@ 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
# Split with space
if [ ! -z "$ALLOWED_SENDER_DOMAINS" ]; then
echo -en "$notice Setting up allowed SENDER domains:"
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
@ -179,7 +190,7 @@ if [ ! -z "$ALLOWED_SENDER_DOMAINS" ]; then
# Since we are behind closed doors, let's just permit all relays.
postconf -e "smtpd_relay_restrictions=permit"
else
echo -e "ERROR: You need to specify sender domains otherwise Postfix will not run!"
echo -e "ERROR: You need to specify ALLOWED_SENDER_DOMAINS otherwise Postfix will not run!"
exit 1
fi
@ -189,6 +200,50 @@ if [ ! -z "$MASQUERADED_DOMAINS" ]; then
postconf -e "local_header_rewrite_clients = static:all"
fi
DKIM_ENABLED=
if [ -d /etc/opendkim/keys ] && [ ! -z "$(find /etc/opendkim/keys -type d ! -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=2"
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
echo "127.0.0.1" >> /etc/opendkim/TrustedHosts
echo "localhost" >> /etc/opendkim/TrustedHosts
echo "" >> /etc/opendkim/TrustedHosts
if [ ! -z "$ALLOWED_SENDER_DOMAINS" ]; then
for i in $ALLOWED_SENDER_DOMAINS; do
private_key=/etc/opendkim/keys/$i/mail.private
if [ -f $private_key ]; then
echo -e " ...for domain ${emphasis}$i${reset}"
echo "*.$i" >> /etc/opendkim/TrustedHosts
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
@ -202,6 +257,6 @@ if [ -d /docker-init.db/ ]; then
done
fi
echo -e "$notice Staring ${emphasis}rsyslog${reset} and ${emphasis}postfix${reset}"
echo -e "$notice Starting: ${emphasis}rsyslog${reset}, ${emphasis}postfix${reset}$DKIM_ENABLED"
exec supervisord -c /etc/supervisord.conf

View file

@ -22,3 +22,16 @@ autorestart = false
directory = /etc/postfix
command = /usr/sbin/postfix -c /etc/postfix start
startsecs = 0
[program:opendkim]
command = /opendkim.sh
user = opendkim
autostart = true
autorestart = true
startsecs = 2
stopwaitsecs = 2
stdout_logfile = /dev/stdout
stderr_logfile = /dev/stderr
stdout_logfile_maxbytes = 0
stderr_logfile_maxbytes = 0

View file

@ -0,0 +1,15 @@
-----BEGIN RSA PRIVATE KEY-----
MIICXgIBAAKBgQC/f+ltPdT/pAp0EjKSiR0m8KT3dNSoC2XSyK0viIHNwwx0IS/G
7Xkl1wR2vzIN+3EvXXeQtajJQ+55+xEUQlXa1RIStZmamCGEKNbXsgNhUBW5tlMJ
HMxiUbHQHDrv0rDl/9nqpEGeCGRMlWTM4HbQgXm9LWc4bKaDe7ByzmWWhwIDAQAB
AoGAfz9jwK9Bhc1fFNmyuSRbNrDZLYV4eWF5e6LVUayHdE6TS+dxc+IAimBiGvXZ
MOlPwIV/Ga1cRsDZCj0GkEuz99kCC4S59jb4N1d44y7NIzzdBn3dAWe1gYRB4gZF
NiBlt63zOUBw26j2PCN98GKzHl2u/rmpkI6CyUqws0zZ2MECQQD6LEqQcEYhQjm9
4Ygu/CUm5QZ1lkMT9F9kiS4u5f/h67tQsAHjCPZSGH3wm2fmII0MfmcY4XxeV8DU
2fDg4hhZAkEAw/XEuCP5k3+S8Aw4rmkE1/JkIkQOOB0o5EAWHDlIOii/0FNGiB3B
EsRDsAmu5yz9Wy25lrgi3CHNXZRHlGlJ3wJBANzKmkQ/0qUnva5Hjdlpz6A0IS9G
Ho1N76LbL1dUxjCty/O6Yu/syPhLhVsEzP3dXJ7aMENVPl5FNHqmnmDbKoECQQCI
eodD6GpOPC/OV/SyJBpnfD4ZT2TH2Cc+sRO1HIAdYXwWT6lz66UouOCbnmLeRws9
kZ8MRbqhrjklz7v6tUnpAkEAjCkM1+NWpXtdafH20kDXyZ/EEIYPoNKFJlCDuAhh
drB2AbWplr7U5q1Gx/r+TNHLj461L+Ys0WtvjQ+mi9p+OQ==
-----END RSA PRIVATE KEY-----

View file

@ -0,0 +1,2 @@
mail._domainkey IN TXT ( "v=DKIM1; k=rsa; "
"p=MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQC/f+ltPdT/pAp0EjKSiR0m8KT3dNSoC2XSyK0viIHNwwx0IS/G7Xkl1wR2vzIN+3EvXXeQtajJQ+55+xEUQlXa1RIStZmamCGEKNbXsgNhUBW5tlMJHMxiUbHQHDrv0rDl/9nqpEGeCGRMlWTM4HbQgXm9LWc4bKaDe7ByzmWWhwIDAQAB" ) ; ----- DKIM key mail for example.org

32
test.sh Executable file
View file

@ -0,0 +1,32 @@
#!/bin/sh
docker build . -t boky/postfix
docker-compose up -d
# 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:test@example.org
RCPT TO:check-auth@verifier.port25.com
DATA
Subject: Postfix message test
From: test@example.org
To: check-auth@verifier.port25.com
Content-Type: text/plain
This is a simple text
.
QUIT
EOF
# Wait for email to be delivered
echo "Waiting to shutdown..."
sleep 5
# Shut down tests
docker-compose down