# 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.
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.
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 (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

# The panel reads the mail queue with `postqueue -p` (spec 7.2.11, 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. postdrop membership is
# read-side only: it does not let the panel bypass any Postfix restriction that
# a local user does not already have through the world-executable sendmail.
RUN usermod -aG postdrop 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.
COPY --chmod=0644 LICENSE NOTICE /usr/share/doc/selfpost/

# 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/plans/logrotate-mode.md.
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-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)
# 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

# 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 (spec 4).
ENTRYPOINT ["/usr/local/bin/entrypoint.sh"]
