From 2ea40736652c4b4ec62e788050eaa1f75fcf4556 Mon Sep 17 00:00:00 2001 From: Jeffrey C Honig Date: Sat, 16 Jan 2021 21:29:28 -0500 Subject: [PATCH] Initial commit --- .dockerignore | 10 ++ .gitignore | 2 + Dockerfile | 26 +++++ Makefile | 39 +++++++ README.md | 85 +++++++++++++++- root/etc/cont-init.d/50-config | 134 +++++++++++++++++++++++++ root/etc/sasl2/smtpd.conf | 3 + root/etc/services.d/opendkim/run | 3 + root/etc/services.d/postfix/run | 3 + root/etc/services.d/saslauthd/run | 4 + root/usr/local/bin/update_sasldb_users | 54 ++++++++++ 11 files changed, 362 insertions(+), 1 deletion(-) create mode 100644 .dockerignore create mode 100644 .gitignore create mode 100644 Dockerfile create mode 100644 Makefile create mode 100644 root/etc/cont-init.d/50-config create mode 100644 root/etc/sasl2/smtpd.conf create mode 100644 root/etc/services.d/opendkim/run create mode 100755 root/etc/services.d/postfix/run create mode 100644 root/etc/services.d/saslauthd/run create mode 100755 root/usr/local/bin/update_sasldb_users diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..2e9d4fb --- /dev/null +++ b/.dockerignore @@ -0,0 +1,10 @@ +**/#* +**/*~ +**/.#* +.dockerignore +.git +.gitignore +MANIFEST.in +Makefile +config + diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..70f9d6f --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +*~ +/config/ diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..dc5b7ad --- /dev/null +++ b/Dockerfile @@ -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 diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..201ca7e --- /dev/null +++ b/Makefile @@ -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: ; diff --git a/README.md b/README.md index c93b00c..35eb1f3 100644 --- a/README.md +++ b/README.md @@ -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 :/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: + - :/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? +... diff --git a/root/etc/cont-init.d/50-config b/root/etc/cont-init.d/50-config new file mode 100644 index 0000000..40f0501 --- /dev/null +++ b/root/etc/cont-init.d/50-config @@ -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 <&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