58b458c264
- deploy/docker-compose.yml: pinned-tag ghcr image, hardened (cap_drop ALL + minimal cap_add, no-new-privileges, panel bound to 127.0.0.1 only). Apache itself runs on the host (spec 10.5), fragment at deploy/apache/. - Alternative reverse-proxy fragments: nginx (+certbot sidecar), Caddy (automatic ACME), Traefik (+acme.json PEM extraction script). - .github/workflows/release.yml: tag-triggered ghcr.io publish, version piped from the git tag into both the binary ldflags and the image tag (spec 10.1). - Closed a gap from Phase 1: logrotate was installed but never invoked; wired up build/logrotate-mail.conf + logrotate-loop.sh + a supervisor program (copytruncate, since postlogd holds mail.log open with nothing to signal on rotation). - README rewritten: site requirements checklist, reverse-proxy comparison, DNS setup (server- vs domain-level), IP warmup, backup/restore vs domain export/import, fixed-tag rationale, machine requirements. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
99 lines
4.0 KiB
Docker
99 lines
4.0 KiB
Docker
# syntax=docker/dockerfile:1
|
|
#
|
|
# SelfPost — single Debian-slim image running postfix + opendkim + panel under
|
|
# supervisord (spec 4). 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 (spec 7.5.A).
|
|
ARG VERSION=dev
|
|
|
|
# Module metadata first for layer caching. go.sum arrived in Phase 2 with the
|
|
# SQLite driver and bcrypt.
|
|
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 codeberg.org/mix/selfpost/internal/buildinfo.Version=${VERSION}" \
|
|
-o /out/panel ./cmd/panel \
|
|
&& go build -trimpath \
|
|
-ldflags "-X codeberg.org/mix/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 in Phase 5.
|
|
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 \
|
|
&& rm -rf /var/lib/apt/lists/*
|
|
|
|
# Unprivileged user for the panel process (spec 7.6.8).
|
|
RUN useradd --system --no-create-home --shell /usr/sbin/nologin panel
|
|
|
|
# Shared group bridging the unprivileged services (spec 5.1, 6): 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, and lets the panel reach the supervisor control socket
|
|
# to signal OpenDKIM/Postfix reloads without any process running as root.
|
|
RUN groupadd --system selfpost \
|
|
&& usermod -aG selfpost panel \
|
|
&& usermod -aG selfpost opendkim \
|
|
&& usermod -aG selfpost postfix
|
|
|
|
# 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
|
|
|
|
COPY build/opendkim.conf /etc/opendkim.conf
|
|
COPY build/logrotate-mail.conf /etc/logrotate.d/mail
|
|
COPY build/postfix-wrapper.sh /usr/local/bin/postfix-wrapper.sh
|
|
COPY build/postfix-config.sh /usr/local/bin/postfix-config.sh
|
|
COPY build/postfix-cert-reload.sh /usr/local/bin/postfix-cert-reload.sh
|
|
COPY build/logrotate-loop.sh /usr/local/bin/logrotate-loop.sh
|
|
COPY build/crashexit.py /usr/local/bin/crashexit.py
|
|
COPY build/entrypoint.sh /usr/local/bin/entrypoint.sh
|
|
COPY build/supervisord.conf /etc/supervisor/supervisord.conf
|
|
RUN chmod +x /usr/local/bin/postfix-wrapper.sh /usr/local/bin/postfix-config.sh \
|
|
/usr/local/bin/postfix-cert-reload.sh /usr/local/bin/logrotate-loop.sh \
|
|
/usr/local/bin/crashexit.py /usr/local/bin/entrypoint.sh
|
|
|
|
# Published submission ports: 465 (smtps, primary) and 587 (submission, optional)
|
|
# plus the panel on 8080. Outbound delivery dials remote MXs on 25 as a client,
|
|
# which needs no inbound listener or EXPOSE.
|
|
EXPOSE 8080 465 587
|
|
|
|
# The entrypoint fixes /data ownership (bind mount) as root, then execs
|
|
# supervisord, which becomes PID 1 and owns process supervision (spec 4).
|
|
ENTRYPOINT ["/usr/local/bin/entrypoint.sh"]
|