Skip to content

Commit

Permalink
Merge pull request #49 from panubo/upgrade_debian12
Browse files Browse the repository at this point in the history
Upgrade to debian 12 and add postfix_exporter
  • Loading branch information
trnubo committed Jan 30, 2024
2 parents d039fe8 + 8baf6f0 commit 38360e9
Show file tree
Hide file tree
Showing 10 changed files with 157 additions and 53 deletions.
55 changes: 24 additions & 31 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -1,6 +1,23 @@
FROM golang:1.16 as postfix_exporter

ENV \
POSTFIX_EXPORTER_VERSION=0.3.0 \
POSTFIX_EXPORTER_CHECKSUM=a0d45f3615d6f24b5532d4048fbb08a248588cac7587279aef1473b6e50b6157

RUN set -x \
&& wget "https://github.com/kumina/postfix_exporter/archive/refs/tags/${POSTFIX_EXPORTER_VERSION}.tar.gz" \
&& echo "${POSTFIX_EXPORTER_CHECKSUM} ${POSTFIX_EXPORTER_VERSION}.tar.gz" > SHA256SUM \
&& ( sha256sum -c SHA256SUM || ( echo "Expected ${POSTFIX_EXPORTER_VERSION}.tar.gz: $(sha256sum ${POSTFIX_EXPORTER_VERSION}.tar.gz)"; exit 1; )) \
&& tar -zxf ${POSTFIX_EXPORTER_VERSION}.tar.gz \
&& cd postfix_exporter-${POSTFIX_EXPORTER_VERSION} \
&& go mod download \
&& go install -tags nosystemd,nodocker \
;

# Postfix SMTP Relay

FROM debian:bullseye
# Debian Bookworm
FROM debian:12

EXPOSE 25 587 2525

Expand All @@ -16,39 +33,11 @@ RUN set -x \
RUN set -x \
&& export DEBIAN_FRONTEND=noninteractive \
&& apt-get update \
&& apt-get install -y --no-install-recommends postfix mailutils busybox-syslogd opendkim opendkim-tools libsasl2-modules sasl2-bin curl ca-certificates procps \
&& apt-get install -y --no-install-recommends postfix mailutils busybox-syslogd opendkim opendkim-tools libsasl2-modules sasl2-bin curl ca-certificates procps s6 inotify-tools \
&& apt-get clean \
&& rm -rf /var/lib/apt/lists/* \
;

# Install s6
RUN set -x \
&& S6_VERSION=2.11.0.0 \
&& EXECLINE_VERSION=2.8.1.0 \
&& SKAWARE_RELEASE=2.0.7 \
&& S6_CHECKSUM_X86_64=fcf79204c1957016fc88b0ad7d98f150071483583552103d5822cbf56824cc87 \
&& S6_CHECKSUM_AARCH64=64151e136f887c6c2c7df69e3100573c318ec7400296680cc698bc7b0ca36943 \
&& EXECLINE_CHECKSUM_X86_64=b216cfc4db928729d950df5a354aa34bc529e8250b55ab0de700193693dea682 \
&& EXECLINE_CHECKSUM_AARCH64=8cb1d5c2d44cb94990d63023db48f7d3cd71ead10cbb19c05b99dbd528af5748 \
&& if [ "$(uname -m)" = "x86_64" ] ; then \
S6_CHECKSUM="${S6_CHECKSUM_X86_64}"; \
EXECLINE_CHECKSUM="${EXECLINE_CHECKSUM_X86_64}"; \
SKAWARE_ARCH="amd64"; \
elif [ "$(uname -m)" = "aarch64" ]; then \
S6_CHECKSUM="${S6_CHECKSUM_AARCH64}"; \
EXECLINE_CHECKSUM="${EXECLINE_CHECKSUM_AARCH64}"; \
SKAWARE_ARCH="aarch64"; \
fi \
&& curl -sSf -L -o /tmp/s6-${S6_VERSION}-linux-${SKAWARE_ARCH}-bin.tar.gz https://github.com/just-containers/skaware/releases/download/v${SKAWARE_RELEASE}/s6-${S6_VERSION}-linux-${SKAWARE_ARCH}-bin.tar.gz \
&& curl -sSf -L -o /tmp/execline-${EXECLINE_VERSION}-linux-${SKAWARE_ARCH}-bin.tar.gz https://github.com/just-containers/skaware/releases/download/v${SKAWARE_RELEASE}/execline-${EXECLINE_VERSION}-linux-${SKAWARE_ARCH}-bin.tar.gz \
&& echo "${S6_CHECKSUM} s6-${S6_VERSION}-linux-${SKAWARE_ARCH}-bin.tar.gz" > /tmp/SHA256SUM \
&& echo "${EXECLINE_CHECKSUM} execline-${EXECLINE_VERSION}-linux-${SKAWARE_ARCH}-bin.tar.gz" >> /tmp/SHA256SUM \
&& ( cd /tmp; sha256sum -c SHA256SUM || ( echo "Expected S6: $(sha256sum s6-${S6_VERSION}-linux-${SKAWARE_ARCH}-bin.tar.gz) Execline: $(sha256sum execline-${EXECLINE_VERSION}-linux-${SKAWARE_ARCH}-bin.tar.gz)"; exit 1; )) \
&& tar -C /usr -zxf /tmp/s6-${S6_VERSION}-linux-${SKAWARE_ARCH}-bin.tar.gz \
&& tar -C /usr -zxf /tmp/execline-${EXECLINE_VERSION}-linux-${SKAWARE_ARCH}-bin.tar.gz \
&& rm -rf /tmp/* \
;

# Configure Postfix / dkim
RUN set -x \
&& postconf -e smtpd_banner="\$myhostname ESMTP" \
Expand All @@ -63,9 +52,13 @@ RUN set -x \

COPY header_checks /etc/postfix/header_checks
COPY opendkim.conf.sh /etc/

COPY --from=postfix_exporter /go/bin/postfix_exporter /usr/local/bin/postfix_exporter
COPY s6 /etc/s6/
COPY entry.sh /

RUN set -x \
&& chmod 0644 /etc/postfix/header_checks \
;

ENTRYPOINT ["/entry.sh"]
CMD ["/usr/bin/s6-svscan", "/etc/s6"]
39 changes: 25 additions & 14 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -2,37 +2,45 @@ NAME := postfix
TAG := latest
IMAGE_NAME := panubo/$(NAME)

.PHONY: *
.PHONY: help bash run run-* build push clean _ci_test

help:
@printf "$$(grep -hE '^\S+:.*##' $(MAKEFILE_LIST) | sed -e 's/:.*##\s*/:/' -e 's/^\(.\+\):\(.*\)/\\x1b[36m\1\\x1b[m:\2/' | column -c2 -t -s :)\n"

bash: ## Runs a bash shell in the docker image
docker run --rm -it -e MAILNAME=mail.example.com $(IMAGE_NAME):latest bash
docker run --rm -it -e MAILNAME=mail.example.com $(IMAGE_NAME):$(TAG) bash

run: ## Runs the docker image in a test mode
$(eval ID := $(shell docker run -d --name postfix -e RELAYHOST=172.17.0.2 -e MAILNAME=mail.example.com -e SIZELIMIT=20480000 -e LOGOUTPUT=/var/log/maillog $(IMAGE_NAME):latest))
$(eval ID := $(shell docker rm -f $(NAME) >/dev/null 2>&1; docker run -d --name $(NAME) --hostname mail.example.com \
-e RELAYHOST=172.17.0.2 \
-e MAILNAME=mail.example.com \
-e SIZELIMIT=20480000 \
-e LOGOUTPUT=/var/log/maillog \
-e CONFIG_RELOADER_ENABLED=true \
-e POSTFIX_EXPORTER_ENABLED=false $(IMAGE_NAME):$(TAG)))
$(eval IP := $(shell docker inspect --format '{{ .NetworkSettings.IPAddress }}' ${ID}))
@echo "Running ${ID} @ smtp:https://${IP}"
@docker attach ${ID}
@docker kill ${ID}

run-tls: ## Runs the docker image in a test mode with TLS
$(eval ID := $(shell docker run -d --name postfix --hostname mail.example.com -e RELAYHOST=172.17.0.2 -e MAILNAME=mail.example.com -e USE_TLS=yes $(IMAGE_NAME):latest))
$(eval IP := $(shell docker inspect --format '{{ .NetworkSettings.IPAddress }}' ${ID}))
@echo "Running ${ID} @ smtp:https://${IP}"
@docker attach ${ID}
@docker kill ${ID}

run-dkim: ## Runs the docker image in a test mode with DKIM
$(eval ID := $(shell docker run -d --name postfix --hostname mail.example.com -e RELAYHOST=172.17.0.2 -e MAILNAME=mail.example.com -e DKIM_DOMAINS=foo.example.com,bar.example.com,example.net -e USE_DKIM=yes -v `pwd`/dkim.key:/etc/opendkim/dkim.key $(IMAGE_NAME):latest))
run-dkim: dkim.key ## Runs the docker image in a test mode with DKIM
$(eval ID := $(shell docker rm -f $(NAME) >/dev/null 2>&1; docker run -d --name $(NAME) --hostname mail.example.com \
-e RELAYHOST=172.17.0.2 \
-e MAILNAME=mail.example.com \
-e CONFIG_RELOADER_ENABLED=true \
-e USE_DKIM=yes -v `pwd`/dkim.key:/etc/opendkim/dkim.key $(IMAGE_NAME):$(TAG)))
$(eval IP := $(shell docker inspect --format '{{ .NetworkSettings.IPAddress }}' ${ID}))
@echo "Running ${ID} @ smtp:https://${IP}"
@docker attach ${ID}
@docker kill ${ID}

run-all-dkim: ## Runs the docker image in a test mode. All settings
$(eval ID := $(shell docker run -d --name postfix --hostname mail.example.com -e RELAYHOST=172.17.0.2 -e MAILNAME=mail.example.com -e DKIM_DOMAINS=foo.example.com,bar.example.com,example.net -e DKIM_SELECTOR=6091aa68-f43d-47cf-a52e-bafda525d0bc -e USE_DKIM=yes -v `pwd`/dkim.key:/etc/opendkim/dkim.key $(IMAGE_NAME):latest))
run-all-dkim: dkim.key ## Runs the docker image in a test mode. All settings
$(eval ID := $(shell docker rm -f $(NAME) >/dev/null 2>&1; docker run -d --name $(NAME) --hostname mail.example.com \
-e RELAYHOST=172.17.0.2 \
-e MAILNAME=mail.example.com \
-e DKIM_DOMAINS=foo.example.com,bar.example.com,example.net \
-e DKIM_SELECTOR=6091aa68-f43d-47cf-a52e-bafda525d0bc \
-e USE_DKIM=yes -v `pwd`/dkim.key:/etc/opendkim/dkim.key $(IMAGE_NAME):$(TAG)))
$(eval IP := $(shell docker inspect --format '{{ .NetworkSettings.IPAddress }}' ${ID}))
@echo "Running ${ID} @ smtp:https://${IP}"
@docker attach ${ID}
Expand All @@ -49,3 +57,6 @@ clean: ## Remove built image

_ci_test:
true

dkim.key:
openssl genrsa -out dkim.key 2048
29 changes: 28 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,9 @@ This image is available on quay.io `quay.io/panubo/postfix` and AWS ECR Public `

- `MAILNAME` - set this to a legitimate FQDN hostname for this service (required). (example, `mail.example.com`)
- `MYNETWORKS` - comma separated list of IP subnets that are allowed to relay. Default `127.0.0.0/8, 10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16`
- `LOGOUTPUT` - Syslog log file location. eg `/var/log/maillog`. Default `/dev/stdout`.
- `LOGOUTPUT` - Log file location. eg `/var/log/maillog`. Default `/dev/stdout`. See [Logging](#logging)
- `TZ` - set timezone. This is used by Postfix to create `Received` headers. Default `UTC`.
- `POSTFIX_EXPORTER_ENABLED` - enable the Prometheus postfix_exporter. Default `false`. See [Postfix Exporter](#postfix-prometheus-exporter)

**General Postfix:**

Expand Down Expand Up @@ -89,6 +90,32 @@ POSTCONF=masquerade_domains=foo.example.com example.com;masquerade_exceptions=ro

Would result in `masquerade_domains` and `masquerade_exceptions` being configured for Postfix.

**Config Reloader**

The config reloader watches the known TLS cert and keys (`TLS_CRT`, `TLS_KEY` etc) for changes (`mv` or updated Kubernetes secret) then reloads Postfix.

- `CONFIG_RELOADER_ENABLED` - Enable the config reloader. Default `false`, must be set to `true` to enable.

## Postfix Prometheus Exporter

This image comes with [kumina/postfix_exporter](https://github.com/kumina/postfix_exporter) pre-installed. To enable set the environment variable `POSTFIX_EXPORTER_ENABLED=true` (this must be exactly "true"). The exporter requires that the logoutput is `/dev/stdout` it can't be anything else.

The exporter listens on port `9154/tcp`.

See [Logging](#logging)

## Logging

This container outputs the Postfix mail log to stdout by default, additionally logs are saved to `/var/log/s6-maillog/current` which is rotated every 10MB with only 3 log files retained.

If you want to output somewhere else you can set environment variable `LOGOUTPUT`. For example `LOGOUTPUT=/var/log/maillog`.

When enabled OpenDKIM only supports syslog output, the syslogd daemon is only used for OpenDKIM. Only /dev/stdout is supported for OpenDKIM syslog logs.

_Note: the Postfix Prometheus exporter only works when the logs are left at /dev/stdout. This requirement of logs going to /dev/stdout is due to the containers logging structure. This may be improved but was needed to keep with backwards compatibility without adding additional variables to configured_

_Note: The log `/var/log/s6-maillog/current` is always created but won't actually contain any logs if `LOGOUTPUT` is not `/dev/stdout`._

## Custom Scripts

Executable shell scripts and binaries can be mounted or copied in to `/etc/entrypoint.d`. These will be run when the container is launched but before postfix is started. These can be used to customise the behaviour of the container.
Expand Down
8 changes: 8 additions & 0 deletions s6/config-reloader/finish
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
#!/usr/bin/env bash

: "${CONFIG_RELOADER_ENABLED:=false}"

if [[ "${CONFIG_RELOADER_ENABLED}" == "true" ]]; then
# Shutdown everything and exit the process crashes or is stopped.
s6-svscanctl -t /etc/s6
fi
28 changes: 28 additions & 0 deletions s6/config-reloader/run
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
#!/usr/bin/env bash

# This script watches the known TLS cert and keys for changes (`mv` or updated Kubernetes secret) then reloads postfix.

: "${CONFIG_RELOADER_ENABLED:=false}"

if [[ "${CONFIG_RELOADER_ENABLED}" != "true" ]]; then
echo "config-reloader >> Config reloader is not being started"
s6-svc -d "$(pwd)"
exit
fi

watch_files=(
"${TLS_CRT:-/etc/ssl/certs/ssl-cert-snakeoil.pem}"
"${TLS_KEY:-/etc/ssl/private/ssl-cert-snakeoil.key}"
"${CLIENT_TLS_KEY:-/etc/ssl/certs/ssl-cert-snakeoil.pem}"
"${CLIENT_TLS_CRT:-/etc/ssl/private/ssl-cert-snakeoil.key}"
)

# Start infinite loop
while true; do
postfix reload
echo "config-reloader >> Waiting on config changes..."
# delete_self is the event that triggers when the link is removed and replaced.
inotifywait --event delete_self "${watch_files[@]}"
# sleep to prevent race condition
sleep 3
done
6 changes: 6 additions & 0 deletions s6/postfix/log/run
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
#!/usr/bin/env bash

mkdir /var/log/s6-maillog
chown postfix:postfix /var/log/s6-maillog

exec s6-setuidgid postfix s6-log -bp 1 n3 s10000000 /var/log/s6-maillog
9 changes: 7 additions & 2 deletions s6/postfix/run
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ set -e
# Defaults
: "${SIZELIMIT:=15728640}" # 10Meg with headroom
: "${RELAYHOST:=}" # empty
: "${LOGOUTPUT:=/dev/stdout}"

# TLS
: "${USE_TLS:=yes}"
Expand Down Expand Up @@ -219,14 +220,18 @@ if [ ! -z "${SMTPD_USERS}" ]; then
sed -i -E 's/etc\/host\.conf etc\/nsswitch\.conf etc\/nss_mdns\.config"/etc\/host.conf etc\/nsswitch.conf etc\/nss_mdns.config etc\/sasldb2"/' /usr/lib/postfix/configure-instance.sh
fi

# Configure logging
echo "postfix >> Setting maillog_file to ${LOGOUTPUT}"
postconf -e maillog_file="${LOGOUTPUT}"

# Configure advanced settings
if [ -n "${POSTCONF}" ]; then
echo "postfix >> Configuring additional postfix parameters"
# Note: Used ; as IFS since comma is common in postfix options
IFS=';' read -ra CONFIG <<< "${POSTCONF}"
for C in "${CONFIG[@]}"; do
IFS='=' read -ra MAP <<< "$C"
echo "postfix >> Setting parameter ${MAP[0]}"
echo "postfix >> Setting parameter ${MAP[0]} to ${MAP[1]}"
postconf -e "$C"
done
fi
Expand All @@ -241,5 +246,5 @@ rm -f /var/spool/postfix/pid/*
echo "postfix >> Checking Postfix Configuration"
postfix check

# start postfix in foreground
echo "postfix >> Starting postfix"
exec /usr/sbin/postfix start-fg
6 changes: 6 additions & 0 deletions s6/postfix_exporter/finish
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
#!/usr/bin/env bash

if [[ "${POSTFIX_EXPORTER_ENABLED}" == "true" ]]; then
# Shutdown everything and exit the process crashes or is stopped.
s6-svscanctl -t /etc/s6
fi
23 changes: 23 additions & 0 deletions s6/postfix_exporter/run
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
#!/usr/bin/env bash

set -e

[ "$DEBUG" == 'true' ] && set -x

# Defaults
: "${LOGOUTPUT:=/dev/stdout}"

if [[ "${POSTFIX_EXPORTER_ENABLED}" == "true" ]] && [[ "${LOGOUTPUT}" != "/dev/stdout" ]]; then
echo "postfix_exporter >> FATAL exporter is enabled but requires LOGOUTPUT is set to /dev/stdout"
s6-svscanctl -t /etc/s6
exit 1
elif [[ "${POSTFIX_EXPORTER_ENABLED}" == "true" ]]; then
s6-svwait -u /etc/s6/postfix/log
echo "postfix_exporter >> Starting postfix_exporter"
exec s6-setuidgid postfix postfix_exporter --postfix.logfile_path=/var/log/s6-maillog/current
fi

echo "postfix_exporter >> POSTFIX_EXPORTER_ENABLED not \"true\", not starting postfix_exporter"

s6-svc -d "$(pwd)"
exit
7 changes: 2 additions & 5 deletions s6/syslogd/run
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,6 @@ set -e

[ "$DEBUG" == 'true' ] && set -x

# Defaults
: "${LOGOUTPUT:=/dev/stdout}"
echo "syslogd >> Setting syslogd output to /dev/stdout (Only used for OpenDKIM)"

echo "syslogd >> Setting syslogd output to ${LOGOUTPUT}"

exec syslogd -n -O "${LOGOUTPUT}" -S
exec syslogd -n -O "/dev/stdout" -S

0 comments on commit 38360e9

Please sign in to comment.