test / test (push) Canceled after 0s
M17: the supervisord control socket is root:supervisor 0770, a group whose one member is the panel; OpenDKIM and Postfix keep `selfpost` for the files under /data and no longer reach the socket. L30, read in OpenDKIM 2.11.0~beta2's source first: it logs through syslog(3) only and has no Logfile option, and nothing in the container served /dev/log. internal/syslogrelay (the panel binary with -syslog-relay=/run/syslog/log, program syslog-relay before OpenDKIM, /dev/log a link to its socket) passes the datagrams to stdout, error and worse marked. opendkim.conf: Syslog yes, Canonicalization relaxed/simple, OversignHeaders From (RFC 6376 § 3.4, § 8.15). e2e: OpenDKIM's start line reaches the container log; every program RUNNING; DKIM verifies. Tested on the test server: go vet + go test of 26 packages, make e2e (all pass, 626 s) on this tree. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
149 lines
7.1 KiB
Docker
149 lines
7.1 KiB
Docker
# syntax=docker/dockerfile:1
|
|
#
|
|
# SelfPost — single Debian-slim image running postfix + opendkim + panel under
|
|
# supervisord. Build from the repository root:
|
|
#
|
|
# docker build -f build/Dockerfile -t selfpost:dev --build-arg VERSION=dev .
|
|
|
|
# ---- build stage -------------------------------------------------------------
|
|
FROM golang:1.26-bookworm AS build
|
|
|
|
WORKDIR /src
|
|
|
|
# Version stamped into both binaries; MUST match the image tag.
|
|
ARG VERSION=dev
|
|
|
|
# Module metadata first for layer caching.
|
|
COPY go.mod go.sum ./
|
|
RUN go mod download
|
|
|
|
COPY cmd ./cmd
|
|
COPY internal ./internal
|
|
|
|
ENV CGO_ENABLED=0
|
|
RUN go vet ./... \
|
|
&& go build -trimpath \
|
|
-ldflags "-X github.com/mixeme/selfpost/internal/buildinfo.Version=${VERSION}" \
|
|
-o /out/panel ./cmd/panel \
|
|
&& go build -trimpath \
|
|
-ldflags "-X github.com/mixeme/selfpost/internal/buildinfo.Version=${VERSION}" \
|
|
-o /out/selfpost-backup ./cmd/selfpost-backup
|
|
|
|
# ---- runtime stage -----------------------------------------------------------
|
|
FROM debian:bookworm-slim AS runtime
|
|
|
|
ENV DEBIAN_FRONTEND=noninteractive
|
|
|
|
# Preseed Postfix so its install is non-interactive and yields a working
|
|
# main.cf. The real relay configuration is generated by the panel.
|
|
# (postfix 3.7.11-0+deb12u1, debian/templates: postfix/main_mailer_type and
|
|
# postfix/mailname are the questions the install asks.)
|
|
# What else arrives through Depends, and is used here: openssl, by
|
|
# ca-certificates ("Depends: openssl (>= 1.1.1)", packages.debian.org/bookworm/
|
|
# ca-certificates) - postfix-cert-reload.sh; python3, by supervisor (supervisor
|
|
# 4.2.5-1, debian/control, ${python3:Depends}) - crashexit.py. sasl2-bin brings
|
|
# saslpasswd2 and libsasl2-modules the PLAIN and LOGIN plugins; the sasldb
|
|
# auxprop plugin is in libsasl2-modules-db, a Depends of libsasl2-2
|
|
# (cyrus-sasl2 2.1.28+dfsg-10, debian/control and debian/*.install). db-util
|
|
# ships /usr/bin/db_dump (packages.debian.org/bookworm/amd64/db-util/filelist).
|
|
RUN echo "postfix postfix/mailname string localhost" | debconf-set-selections \
|
|
&& echo "postfix postfix/main_mailer_type string Internet Site" | debconf-set-selections \
|
|
&& apt-get update \
|
|
&& apt-get install -y --no-install-recommends \
|
|
postfix \
|
|
opendkim \
|
|
opendkim-tools \
|
|
sasl2-bin \
|
|
libsasl2-modules \
|
|
db-util \
|
|
supervisor \
|
|
logrotate \
|
|
ca-certificates \
|
|
curl \
|
|
&& rm -rf /var/lib/apt/lists/*
|
|
|
|
# Unprivileged user for the panel process.
|
|
RUN useradd --system --no-create-home --shell /usr/sbin/nologin panel
|
|
|
|
# Shared group bridging the unprivileged services: the panel generates
|
|
# per-domain DKIM keys, application SASL accounts (sasldb2) and the Postfix
|
|
# sender map, while OpenDKIM and Postfix (different users) must read them.
|
|
# Membership in this group — plus setgid dirs under /data (set up in
|
|
# entrypoint.sh) — lets OpenDKIM read the panel-owned keys and lets Postfix read
|
|
# the sasldb2/sender map. (The supervisor control socket has a group of its
|
|
# own, `supervisor`, below.)
|
|
# The id is fixed, and it is the one the group has always had: an operator
|
|
# gives the TLS private key on the host to this group by number, so that the
|
|
# panel can put it into a full backup (guide § Taking a full backup).
|
|
# groupadd(8) refuses a --gid already in use, so a package that took 998 would
|
|
# fail this build rather than share the group.
|
|
RUN groupadd --system --gid 998 selfpost \
|
|
&& usermod -aG selfpost panel \
|
|
&& usermod -aG selfpost opendkim \
|
|
&& usermod -aG selfpost postfix
|
|
|
|
# The panel reads the mail queue with `postqueue -p` (and the status page's
|
|
# queue card). postqueue is setgid postdrop, which normally gives it the group
|
|
# needed to reach Postfix's showq socket — but the documented deployment runs
|
|
# with `no-new-privileges`, which disables setgid transitions, so the panel
|
|
# would always see "Permission denied". Making `panel` a real member of postdrop
|
|
# grants the same access without relying on a setgid escalation the hardening
|
|
# deliberately forbids. The same membership is what lets the panel submit its
|
|
# own notifications with sendmail(1) - postdrop, too, is setgid and would fail
|
|
# to write the maildrop directory (internal/notify/mailer.go). It does not let
|
|
# the panel bypass any Postfix restriction that a local user does not already
|
|
# have through the world-executable sendmail.
|
|
# (Postfix 3.7.11, conf/postfix-files: postdrop and postqueue are
|
|
# root:$setgid_group 2755. Linux, Documentation/userspace-api/no_new_privs.rst:
|
|
# with no_new_privs, execve() no longer changes the gid for a setgid file.)
|
|
RUN usermod -aG postdrop panel
|
|
# The supervisord control socket's group (supervisord.conf, [unix_http_server]):
|
|
# the panel alone - not OpenDKIM's or Postfix's users, which share `selfpost`
|
|
# for the files under /data.
|
|
RUN groupadd --system supervisor \
|
|
&& usermod -aG supervisor panel
|
|
|
|
# Runtime directories: milter sockets and the consolidated persistent root.
|
|
RUN mkdir -p /run/opendkim /run/selfpost /data \
|
|
&& chown opendkim:opendkim /run/opendkim \
|
|
&& chown panel:panel /run/selfpost /data
|
|
|
|
COPY --from=build /out/panel /usr/local/bin/panel
|
|
COPY --from=build /out/selfpost-backup /usr/local/bin/selfpost-backup
|
|
|
|
# Licence text shipped with the image (AGPL-3.0 conveyance). The panel also
|
|
# serves the same text at /license from an embedded copy. OFL.txt is the SIL
|
|
# Open Font License 1.1 that must travel with the IBM Plex WOFF2 files
|
|
# embedded in the panel binary (NOTICE).
|
|
COPY --chmod=0644 LICENSE NOTICE /usr/share/doc/selfpost/
|
|
COPY --chmod=0644 internal/web/view/static/OFL.txt /usr/share/doc/selfpost/OFL.txt
|
|
|
|
# File modes are pinned here so a build context copied from a checkout without
|
|
# POSIX permissions (e.g. Windows tar sync) cannot land group-writable config
|
|
# that logrotate would silently ignore — see docs/development.md § Building
|
|
# binaries and the image.
|
|
COPY --chmod=0644 build/opendkim.conf /etc/opendkim.conf
|
|
COPY --chmod=0644 build/logrotate-mail.conf /etc/logrotate.d/mail
|
|
COPY --chmod=0755 build/postfix-wrapper.sh /usr/local/bin/postfix-wrapper.sh
|
|
COPY --chmod=0755 build/postfix-reload.sh /usr/local/bin/postfix-reload.sh
|
|
COPY --chmod=0755 build/postfix-config.sh /usr/local/bin/postfix-config.sh
|
|
COPY --chmod=0755 build/postfix-cert-reload.sh /usr/local/bin/postfix-cert-reload.sh
|
|
COPY --chmod=0755 build/logrotate-loop.sh /usr/local/bin/logrotate-loop.sh
|
|
COPY --chmod=0755 build/crashexit.py /usr/local/bin/crashexit.py
|
|
COPY --chmod=0755 build/entrypoint.sh /usr/local/bin/entrypoint.sh
|
|
COPY --chmod=0644 build/supervisord.conf /etc/supervisor/supervisord.conf
|
|
|
|
# Published submission ports: 465 (smtps, primary) and 587 (submission, optional),
|
|
# inbound SMTP on 25 when INBOUND_RELAY_ENABLE=true, plus the panel on 8080.
|
|
# Outbound delivery dials remote MXs on 25 as a client even when inbound is off.
|
|
EXPOSE 8080 465 587 25
|
|
|
|
# Liveness probe: panel HTTP plus mail-path processes (opendkim, panel, postfix).
|
|
# Does not verify TLS, DNS, or end-to-end delivery — see docs/guide.md Operations.
|
|
HEALTHCHECK --interval=30s --timeout=5s --start-period=90s --retries=3 \
|
|
CMD curl -fsS http://127.0.0.1:8080/healthz || exit 1
|
|
|
|
# The entrypoint fixes /data ownership (bind mount) as root, then execs
|
|
# supervisord, which becomes PID 1 and owns process supervision.
|
|
ENTRYPOINT ["/usr/local/bin/entrypoint.sh"]
|