Skip to content

Commit

Permalink
Initial commit
Browse files Browse the repository at this point in the history
  • Loading branch information
jchonig committed Jan 17, 2021
1 parent 8914605 commit 2ea4073
Show file tree
Hide file tree
Showing 11 changed files with 362 additions and 1 deletion.
10 changes: 10 additions & 0 deletions .dockerignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
**/#*
**/*~
**/.#*
.dockerignore
.git
.gitignore
MANIFEST.in
Makefile
config

2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
*~
/config/
26 changes: 26 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
From lsiobase/alpine:3.13

ENV \
USE_SASL= \
# Works around an sasldb bug in Alpine 3.13 \
USE_SASLAUTHD=yes \
USE_DKIM= \
USE_TLS= \
MYHOSTNAME= \
MYORIGIN= \
MYDESTINATION= \
MASQUERADE_DOMAINS= \
SMTPD_TLS_SECURITY_LEVEL= \
BOUNCE_QUEUE_LIFETIME=

# Add configuration files
COPY root /

# Set up
RUN \
echo "*** Install required packages ****" && \
apk add --no-cache postfix cyrus-sasl rsync opendkim opendkim-utils ca-certificates

EXPOSE 25

VOLUME /config
39 changes: 39 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
TAG=devel
IMAGE=postfix
VOLUMES= \
-v ${PWD}/config:/config
ENV= \
-e USE_SASL=yes \
-e USE_SASLAUTHD=yes \
-e USE_TLS=yes \
-e MYHOSTNAME=home.honig.net \
-e MYORIGIN=honig.net \
-e MASQUERADE_DOMAINS=honig.net \
-e SMTPD_TLS_SECURITY_LEVEL=may \
-e BOUNCE_QUEUE_LIFETIME=1d
PORTS= \
-p 25:25

all: build

clean:
find . -name \*~ -delete

run: build
docker run ${VOLUMES} ${ENV} ${PORTS} -it ${IMAGE}:${TAG}

sasl_users: build
docker run ${VOLUMES} ${ENV} ${PORTS} -it ${IMAGE}:${TAG} sasl_users

# Run the container with just a bash shell
run-bash: build
docker run ${VOLUMES} ${ENV} ${PORTS} -it --entrypoint /bin/bash ${IMAGE}:${TAG}

# Start the container and run a bash shell
exec-bash: build
docker run ${VOLUMES} ${ENV} ${PORTS} -it ${IMAGE}:${TAG} /bin/bash

build: true
docker build -t ${IMAGE}:${TAG} .

true: ;
85 changes: 84 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,2 +1,85 @@
# docker-postfix
Postfix running in a container
A container running postfix intended to be used as an internal mail
relay to the outside world.

Optionally authenticate incoming connections with SASL

# Usage

## docker

```
docker create \
--name=posfix \
-e PUID=1000 \
-e PGID=1000 \
-v </path/to/appdata/config>:/config \
-p 25:25 \
--restart unless-stopped \
jchonig/postfix
```

### docker-compose

Compatible with docker-compose v2 schemas.

```
---
version: "2"
services:
postfix:
image: jchonig/postfix
environment:
- PUID=1000
- PGID=1000
volumes:
- </path/to/appdata/config>:/config
port:
- 25
restart: unless-stopped
```

# Parameters

## Ports (-p)

| Volume | Function |
| ------ | -------- |
| 25 | The incoming SMTP port |

## Environment Variables (-e)

| Env | Function |
| --- | -------- |
| PUID=1000 | for UserID - see below for explanation |
| PGID=1000 | for GroupID - see below for explanation |
| USE_SASL=yes | Use sasl for user authentication |
| USE_SASLAUTHD=yes | Use saslauthd |
| USE_DKIM=yes | Not yet implemented |
| USE_TLS=yes | Enable TLS for incoming connectinos |
| MYHOSTNAME=example.com | Configure postfix myhostname parameter |
| MYORIGIN=example.com | Configure postfix myorigin parameter |
| MYDESTINATION= | Configure postfix mydestination parameter |
| MASQUERADE_DOMAINS=example.com | A comma seperated list of domains to masquerade |
| SMTPD_TLS_SECURITY_LEVEL=may | Configure the level of TLS required on incomming connections |
| BOUNCE_QUEUE_LIFETIME=1d | Configure the postfix bounce queue lifetime |

## Volume Mappings (-v)

| Volume | Function |
| ------ | -------- |
| /config | All the config files reside here |

# Application Setup

+ When USE_TLS is enabled, /config/server.cert and /config/server.key should must exist
+ When using SASL, /config/sasl.users should should have one entry per line of user and password seperated by a space
+ When using SASL, run *update_sasldb_users when that file is updated
+ When using SASL, a bug in Alpine 3.13 requires USE_SASLAUTHD=yes
+ Additonal postfix configuration can be stored in /config/postconf as arguments to the postconf command

## TODO

+ [ ] Set up DKIM
+ [ ] Logging is to stdout, should it be to a log file with rotation?
...
134 changes: 134 additions & 0 deletions root/etc/cont-init.d/50-config
Original file line number Diff line number Diff line change
@@ -0,0 +1,134 @@
#!/usr/bin/with-contenv bash

print_vars () {
if [ -n "${1}" ]; then
title=" ${1}"
else
title=""
fi
echo "Environment${title}:"
echo " PUID=${PUID}"
echo " PGID=${PGID}"
echo " USE_SASL=${USE_SASL}"
echo " USE_SASLAUTHD=${USE_SASLAUTHD}"
echo " USE_DKIM=${USE_DKIM}"
echo " USE_TLS=${USE_TLS}"
echo " MYHOSTNAME=${MYHOSTNAME}"
echo " MYORIGIN=${MYORIGIN}"
echo " MYDESTINATION=${MYDESTINATION}"
echo " BOUNCE_QUEUE_LIFETIME=${BOUNCE_QUEUE_LIFETIME}"
echo " MASQUERADE_DOMAINS=${MASQUERADE_DOMAINS}"
echo " SMTPD_TLS_SECURITY_LEVEL=${SMTPD_TLS_SECURITY_LEVEL}"
echo " TZ=${TZ}"
for var in "${!MMONIT_@}"; do
printf ' %s=%s\n' "$var" "${!var}"
done
}

# load env file if it exists
if [ -f "/config/env" ]; then
source /config/env
print_vars "After sourcing /config/env"
fi

print_vars

#
MAIL_CONFIG=/etc/postfix

#
# Switch from abc to postfix
#
PUID=${PUID:-$(id -u abc)}
PGID=${PGID:-$(id -g abc)}

DIRS="/var/spool/postfix /config/spool ${MAIL_CONFIG}"
find ${DIRS} -user postfix -exec chown ${PUID} \{\} \;
find ${DIRS} -group postfix -exec chgrp ${PGID} \{\} \;

userdel abc

groupmod -o -g "$PGID" postfix
usermod -o -u "$PUID" -d /config/spool postfix

chown postfix:postfix /app
chown postfix:postfix /config
chown postfix:postfix /defaults

# Copy over the spool direcotry if it does not exist
if [ ! -d /config/spool ]; then
mkdir /config/spool
rsync -a /var/spool/postfix/ /config/spool
fi

# Set the queue directory
postconf -e queue_directory=/config/spool

postconf -e myhostname="${MYHOSTNAME}"
postconf -e myorigin="${MYORIGIN}"
postconf -e mydestination="${MYDESTINATION}"

if [ -n "${MASQUERADE_DOMAINS}" ]; then
postconf -e masquerade_domains="${MASQUERADE_DOMAINS}"
postconf -e masquerade_classes=envelope_sender,envelope_recipient,header_sender,header_recipient
fi

case ${USE_SASL} in
yes|true)
postconf -e smtpd_sasl_auth_enable=yes
postconf -e broken_sasl_auth_clients=yes
postconf -e smtpd_recipient_restrictions=permit_sasl_authenticated,reject_unauth_destination
postconf -e smtpd_relay_restrictions=permit_sasl_authenticated,reject
# mydomain needs to be set properly before this runs
update_sasldb_users
chown postfix.postfix /etc/sasl2/sasldb2
case ${USE_SASLAUTHD} in
yes|true)
cat > /etc/sasl2/smtpd.conf <<EOF
pwcheck_method: saslauthd
mech_list: PLAIN LOGIN CRAM-MD5 DIGEST-MD5 NTLM
EOF
;;
*)
rm -rf /etc/services.d/saslauthd
;;
esac
;;
esac

case ${USE_DKIM} in
yes|true)
true
;;
*)
rm -rf /etc/services.d/opendkim
;;
esac

case ${USE_TLS} in
yes|true)
set -x
postconf -e smtpd_tls_security_level=encrypt
postconf -e smtpd_tls_CAfile=/etc/ssl/certs/ca-certificates.crt
postconf -e smtpd_tls_cert_file=/config/server.cert
postconf -e smtpd_tls_key_file=/config/server.key
test -n "${SMTPD_TLS_SECURITY_LEVEL}" && postconf -e smtpd_tls_security_level=${SMTPD_TLS_SECURITY_LEVEL}
set +x
;;
esac

# Do some basic configuration
postconf -e maillog_file=/dev/stdout
#postconf -F '*/*/chroot = n'

test -n "${BOUNCE_QUEUE_LIFETIME}" && postconf -e bounce_queue_lifetime="${BOUNCE_QUEUE_LIFETIME}"

# Read additional config info from /config/postconf
if [ -f /config/postconf ]; then
while read -r user type; do
postconf ${line}
done < /config/postconf
fi

# Set permissions on everything
/usr/sbin/postfix set-permissions
3 changes: 3 additions & 0 deletions root/etc/sasl2/smtpd.conf
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
pwcheck_method: auxprop
auxprop_plugin: sasldb
mech_list: PLAIN LOGIN CRAM-MD5 DIGEST-MD5 NTLM
3 changes: 3 additions & 0 deletions root/etc/services.d/opendkim/run
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
#!/usr/bin/with-contenv bash

exec /usr/sbin/opendkim -f
3 changes: 3 additions & 0 deletions root/etc/services.d/postfix/run
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
#!/usr/bin/with-contenv bash

exec /usr/sbin/postfix start-fg
4 changes: 4 additions & 0 deletions root/etc/services.d/saslauthd/run
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
#!/usr/bin/with-contenv bash

exec /usr/sbin/saslauthd -a sasldb -d

54 changes: 54 additions & 0 deletions root/usr/local/bin/update_sasldb_users
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
#!/bin/bash

progname=$(basename ${0})

fail () {
echo "${progname}: ${*}" >&2
exit 1
}

verbose () {
echo "${progname}: ${*}"
}

for file in /config/sasl.users; do
test -f ${file} || fail "${file} is missing"
done

declare -A sasldb_users

if [ -f /etc/sasl2/sasldb2 ]; then
while read -r user type; do
test "${type}" != "userPassword" && continue
if [[ $user =~ ([^@]+)@([^:]+): ]]; then
sasldb_users[${BASH_REMATCH[1]}@${BASH_REMATCH[2]}]=1
else
fail "Unexpected line from sasdblistusers2: ${user}"
fi
done <<< $(sasldblistusers2)
fi

mydomain=$(postconf -h myhostname)

while read -r user password; do
test -z "${user}" -o -z "${password}" && continue
if [ -n "${sasldb_users[${user}@${mydomain}]}" ]; then
verbose "Updating ${user}@${mydomain}"
else
verbose "Adding ${user}@${mydomain}"
fi
echo "${password}" | saslpasswd2 -p -c -u "${mydomain}" "${user}"
unset sasldb_users[${user}@${mydomain}]
done </config/sasl.users

for key in "${!sasldb_users[@]}"; do
if [[ $key =~ ([^@]+)@(.+) ]]; then
user=${BASH_REMATCH[1]}
domain=${BASH_REMATCH[2]}
fi
verbose "Removing ${user}@${domain}"
saslpasswd2 -d -u "${domain}" "${user}"
done

exit 0

0 comments on commit 2ea4073

Please sign in to comment.