From 0581e0cf6be1c32afe29b7b0dc6bbff2300fa3d3 Mon Sep 17 00:00:00 2001 From: Pierre-Olivier Mercier Date: Tue, 21 Oct 2025 20:01:01 +0700 Subject: [PATCH] Use authentication_milter instead of opendkim and opendmarc --- Dockerfile | 98 ++++++++++++++++--- README.md | 5 +- .../authentication_milter.json | 69 +++++++++++++ docker/authentication_milter/mail-dmarc.ini | 58 +++++++++++ docker/entrypoint.sh | 19 ++-- docker/opendkim/opendkim.conf | 39 -------- docker/opendmarc/opendmarc.conf | 41 -------- docker/postfix/main.cf | 5 +- docker/supervisor/supervisord.conf | 23 ++--- 9 files changed, 226 insertions(+), 131 deletions(-) create mode 100644 docker/authentication_milter/authentication_milter.json create mode 100644 docker/authentication_milter/mail-dmarc.ini delete mode 100644 docker/opendkim/opendkim.conf delete mode 100644 docker/opendmarc/opendmarc.conf diff --git a/Dockerfile b/Dockerfile index 36d7d33..eee71bd 100644 --- a/Dockerfile +++ b/Dockerfile @@ -31,19 +31,88 @@ COPY --from=nodebuild /build/web/build/ ./web/build/ RUN go generate ./... && \ CGO_ENABLED=1 GOOS=linux go build -a -installsuffix cgo -ldflags="-w -s" -o happyDeliver ./cmd/happyDeliver -# Stage 3: Runtime image with Postfix and all filters +# Stage 3: Prepare perl +FROM alpine:3 AS pl + +RUN echo "@testing https://dl-cdn.alpinelinux.org/alpine/edge/testing" >> /etc/apk/repositories && \ + apk add --no-cache \ + build-base \ + musl-obstack-dev \ + openssl \ + openssl-dev \ + perl-app-cpanminus \ + perl-alien-libxml2 \ + perl-class-load-xs \ + perl-cpanel-json-xs \ + perl-crypt-openssl-rsa \ + perl-crypt-openssl-random \ + perl-crypt-openssl-verify \ + perl-crypt-openssl-x509 \ + perl-dbd-sqlite \ + perl-dbi \ + perl-email-address-xs \ + perl-json-xs \ + perl-list-moreutils \ + perl-moose \ + perl-net-idn-encode@testing \ + perl-net-ssleay \ + perl-netaddr-ip \ + perl-package-stash \ + perl-params-util \ + perl-params-validate \ + perl-proc-processtable \ + perl-sereal-decoder \ + perl-sereal-encoder \ + perl-socket6 \ + perl-sub-identify \ + perl-variable-magic \ + perl-xml-libxml \ + perl-dev \ + zlib-dev \ + && \ + ln -s /usr/bin/ld /bin/ld + +RUN cpanm --notest Mail::SPF && \ + cpanm --notest Mail::Milter::Authentication + +# Stage 4: Runtime image with Postfix and all filters FROM alpine:3 # Install all required packages -RUN apk add --no-cache \ +RUN echo "@testing https://dl-cdn.alpinelinux.org/alpine/edge/testing" >> /etc/apk/repositories && \ + apk add --no-cache \ bash \ ca-certificates \ - opendkim \ - opendkim-utils \ - opendmarc \ + openssl \ + perl \ + perl-alien-libxml2 \ + perl-class-load-xs \ + perl-cpanel-json-xs \ + perl-crypt-openssl-rsa \ + perl-crypt-openssl-random \ + perl-crypt-openssl-verify \ + perl-crypt-openssl-x509 \ + perl-dbd-sqlite \ + perl-dbi \ + perl-email-address-xs \ + perl-json-xs \ + perl-list-moreutils \ + perl-moose \ + perl-net-idn-encode@testing \ + perl-net-ssleay \ + perl-netaddr-ip \ + perl-package-stash \ + perl-params-util \ + perl-params-validate \ + perl-proc-processtable \ + perl-sereal-decoder \ + perl-sereal-encoder \ + perl-socket6 \ + perl-sub-identify \ + perl-variable-magic \ + perl-xml-libxml \ postfix \ postfix-pcre \ - postfix-policyd-spf-perl \ spamassassin \ spamassassin-client \ supervisor \ @@ -51,9 +120,8 @@ RUN apk add --no-cache \ tzdata \ && rm -rf /var/cache/apk/* -# Get test-only version of postfix-policyd-spf-perl -ADD https://git.nemunai.re/happyDomain/postfix-policyd-spf-perl/raw/branch/master/postfix-policyd-spf-perl /usr/bin/postfix-policyd-spf-perl -RUN chmod +x /usr/bin/postfix-policyd-spf-perl && chmod 755 /usr/bin/postfix-policyd-spf-perl +# Copy Mail::Milter::Authentication and its dependancies +COPY --from=pl /usr/local/ /usr/local/ # Create happydeliver user and group RUN addgroup -g 1000 happydeliver && \ @@ -63,12 +131,11 @@ RUN addgroup -g 1000 happydeliver && \ RUN mkdir -p /etc/happydeliver \ /var/lib/happydeliver \ /var/log/happydeliver \ - /var/spool/postfix/opendkim \ - /var/spool/postfix/opendmarc \ - /etc/opendkim/keys \ + /var/cache/authentication_milter \ + /var/lib/authentication_milter \ + /var/spool/postfix/authentication_milter \ && chown -R happydeliver:happydeliver /var/lib/happydeliver /var/log/happydeliver \ - && chown -R opendkim:postfix /var/spool/postfix/opendkim \ - && chown -R opendmarc:postfix /var/spool/postfix/opendmarc + && chown -R mail:mail /var/spool/postfix/authentication_milter # Copy the built application COPY --from=builder /build/happyDeliver /usr/local/bin/happyDeliver @@ -76,8 +143,7 @@ RUN chmod +x /usr/local/bin/happyDeliver # Copy configuration files COPY docker/postfix/ /etc/postfix/ -COPY docker/opendkim/ /etc/opendkim/ -COPY docker/opendmarc/ /etc/opendmarc/ +COPY docker/authentication_milter/authentication_milter.json /etc/authentication_milter.json COPY docker/spamassassin/ /etc/mail/spamassassin/ COPY docker/supervisor/ /etc/supervisor/ COPY docker/entrypoint.sh /entrypoint.sh diff --git a/README.md b/README.md index a4ded59..fe03381 100644 --- a/README.md +++ b/README.md @@ -17,13 +17,12 @@ An open-source email deliverability testing platform that analyzes test emails a ### With Docker (Recommended) -The easiest way to run happyDeliver is using the all-in-one Docker container that includes Postfix, OpenDKIM, OpenDMARC, SpamAssassin, and the happyDeliver application. +The easiest way to run happyDeliver is using the all-in-one Docker container that includes Postfix, authentication_milter, SpamAssassin, and the happyDeliver application. #### What's included in the Docker container: - **Postfix MTA**: Receives emails on port 25 -- **OpenDKIM**: DKIM signature verification -- **OpenDMARC**: DMARC policy validation +- **authentication_milter**: Entreprise grade email authentication - **SpamAssassin**: Spam scoring and analysis - **happyDeliver API**: REST API server on port 8080 - **SQLite Database**: Persistent storage for tests and reports diff --git a/docker/authentication_milter/authentication_milter.json b/docker/authentication_milter/authentication_milter.json new file mode 100644 index 0000000..2f65d3b --- /dev/null +++ b/docker/authentication_milter/authentication_milter.json @@ -0,0 +1,69 @@ +{ + "logtoerr" : "1", + "error_log" : "", + "connection" : "unix:/var/spool/postfix/authentication_milter/authentication_milter.sock", + "umask" : "0007", + "runas" : "mail", + "rungroup" : "mail", + "authserv_id" : "__HOSTNAME__", + + "connect_timeout" : 30, + "command_timeout" : 30, + "content_timeout" : 300, + "dns_timeout" : 10, + "dns_retry" : 2, + + "handlers" : { + + "Sanitize" : { + "hosts_to_remove" : [ + "__HOSTNAME__" + ] + }, + + "SPF" : { + "hide_none" : 0 + }, + + "DKIM" : { + "hide_none" : 0, + }, + + "XGoogleDKIM" : { + "hide_none" : 1, + }, + + "ARC" : { + "hide_none" : 0, + }, + + "DMARC" : { + "hide_none" : 0, + "detect_list_id" : "1" + }, + + "BIMI" : {}, + + "PTR" : {}, + + "SenderID" : { + "hide_none" : 1 + }, + + "IPRev" : {}, + + "Auth" : {}, + + "AlignedFrom" : {}, + + "LocalIP" : {}, + + "TrustedIP" : { + "trusted_ip_list" : [] + }, + + "!AddID" : {}, + + "ReturnOK" : {} + } +} diff --git a/docker/authentication_milter/mail-dmarc.ini b/docker/authentication_milter/mail-dmarc.ini new file mode 100644 index 0000000..8097ac6 --- /dev/null +++ b/docker/authentication_milter/mail-dmarc.ini @@ -0,0 +1,58 @@ +; This is YOU. DMARC reports include information about the reports. Enter it here. +[organization] +domain = example.com +org_name = My Company Limited +email = admin@example.com +extra_contact_info = http://example.com + +; aggregate DMARC reports need to be stored somewhere. Any database +; with a DBI module (MySQL, SQLite, DBD, etc.) should work. +; SQLite and MySQL are tested. +; Default is sqlite. +[report_store] +backend = SQL +;dsn = dbi:SQLite:dbname=dmarc_reports.sqlite +dsn = dbi:mysql:database=dmarc_reporting_database;host=localhost;port=3306 +user = authmilterusername +pass = authmiltpassword + +; backend can be perl or libopendmarc +[dmarc] +backend = perl + +[dns] +timeout = 5 +public_suffix_list = share/public_suffix_list + +[smtp] +; hostname is the external FQDN of this MTA +hostname = mx1.example.com +cc = dmarc.copy@example.com + +; list IP addresses to whitelist (bypass DMARC reject/quarantine) +; see sample whitelist in share/dmarc_whitelist +whitelist = /path/to/etc/dmarc_whitelist + +; By default, we attempt to email directly to the report recipient. +; Set these to relay via a SMTP smart host. +smarthost = mx2.example.com +smartuser = dmarccopyusername +smartpass = dmarccopypassword + +[imap] +server = mail.example.com +user = +pass = +; the imap folder where new dmarc messages will be found +folder = dmarc +; the folders to store processed reports (a=aggregate, f=forensic) +f_done = dmarc.forensic +a_done = dmarc.aggregate + +[http] +port = 8080 + +[https] +port = 8443 +ssl_crt = +ssl_key = \ No newline at end of file diff --git a/docker/entrypoint.sh b/docker/entrypoint.sh index 445602d..99744f6 100644 --- a/docker/entrypoint.sh +++ b/docker/entrypoint.sh @@ -10,28 +10,23 @@ HAPPYDELIVER_DOMAIN="${HAPPYDELIVER_DOMAIN:-happydeliver.local}" echo "Hostname: $HOSTNAME" echo "Domain: $HAPPYDELIVER_DOMAIN" -# Create runtime directories -mkdir -p /var/run/opendkim /var/run/opendmarc -chown opendkim:postfix /var/run/opendkim -chown opendmarc:postfix /var/run/opendmarc - # Create socket directories -mkdir -p /var/spool/postfix/opendkim /var/spool/postfix/opendmarc -chown opendkim:postfix /var/spool/postfix/opendkim -chown opendmarc:postfix /var/spool/postfix/opendmarc -chmod 750 /var/spool/postfix/opendkim /var/spool/postfix/opendmarc +mkdir -p /var/spool/postfix/authentication_milter +chown mail:mail /var/spool/postfix/authentication_milter +chmod 750 /var/spool/postfix/authentication_milter # Create log directory -mkdir -p /var/log/happydeliver +mkdir -p /var/log/happydeliver /var/cache/authentication_milter /var/spool/authentication_milter /var/lib/authentication_milter /run/authentication_milter chown happydeliver:happydeliver /var/log/happydeliver +chown mail:mail /var/cache/authentication_milter /run/authentication_milter /var/spool/authentication_milter /var/lib/authentication_milter # Replace placeholders in Postfix configuration echo "Configuring Postfix..." sed -i "s/__HOSTNAME__/${HOSTNAME}/g" /etc/postfix/main.cf sed -i "s/__DOMAIN__/${HAPPYDELIVER_DOMAIN}/g" /etc/postfix/main.cf -# Replace placeholders in OpenDMARC configuration -sed -i "s/__HOSTNAME__/${HOSTNAME}/g" /etc/opendmarc/opendmarc.conf +# Replace placeholders in configurations +sed -i "s/__HOSTNAME__/${HOSTNAME}/g" /etc/authentication_milter.json # Initialize Postfix aliases if [ -f /etc/postfix/aliases ]; then diff --git a/docker/opendkim/opendkim.conf b/docker/opendkim/opendkim.conf deleted file mode 100644 index 8fe2f8c..0000000 --- a/docker/opendkim/opendkim.conf +++ /dev/null @@ -1,39 +0,0 @@ -# OpenDKIM configuration for happyDeliver -# Verifies DKIM signatures on incoming emails - -# Log to syslog -Syslog yes -SyslogSuccess yes -LogWhy yes - -# Run as this user and group -UserID opendkim:mail - -UMask 002 - -# Socket for Postfix communication -Socket unix:/var/spool/postfix/opendkim/opendkim.sock - -# Process ID file -PidFile /var/run/opendkim/opendkim.pid - -# Operating mode - verify only (not signing) -Mode v - -# Canonicalization methods -Canonicalization relaxed/simple - -# DNS timeout -DNSTimeout 5 - -# Add header for verification results -AlwaysAddARHeader yes - -# Accept unsigned mail -On-NoSignature accept - -# Always add Authentication-Results header -AlwaysAddARHeader yes - -# Maximum verification attempts -MaximumSignaturesToVerify 3 diff --git a/docker/opendmarc/opendmarc.conf b/docker/opendmarc/opendmarc.conf deleted file mode 100644 index 882e11c..0000000 --- a/docker/opendmarc/opendmarc.conf +++ /dev/null @@ -1,41 +0,0 @@ -# OpenDMARC configuration for happyDeliver -# Verifies DMARC policies on incoming emails - -# Socket for Postfix communication -Socket unix:/var/spool/postfix/opendmarc/opendmarc.sock - -# Process ID file -PidFile /var/run/opendmarc/opendmarc.pid - -# Run as this user and group -UserID opendmarc:mail - -UMask 002 - -# Syslog configuration -Syslog true -SyslogFacility mail - -# Ignore authentication results from other hosts -IgnoreAuthenticatedClients true - -# Accept mail even if DMARC fails (we're analyzing, not filtering) -RejectFailures false - -# Trust Authentication-Results headers from localhost only -TrustedAuthservIDs __HOSTNAME__ - -# Add DMARC results to Authentication-Results header -#AddAuthenticationResults true - -# DNS timeout -DNSTimeout 5 - -# History file (for reporting) -# HistoryFile /var/spool/opendmarc/opendmarc.dat - -# Ignore hosts file -# IgnoreHosts /etc/opendmarc/ignore.hosts - -# Public suffix list -# PublicSuffixList /usr/share/publicsuffix/public_suffix_list.dat diff --git a/docker/postfix/main.cf b/docker/postfix/main.cf index 913eb57..e7d1fb0 100644 --- a/docker/postfix/main.cf +++ b/docker/postfix/main.cf @@ -28,14 +28,13 @@ transport_maps = pcre:/etc/postfix/transport_maps # OpenDKIM for DKIM verification milter_default_action = accept milter_protocol = 6 -smtpd_milters = unix:/var/spool/postfix/opendkim/opendkim.sock, unix:/var/spool/postfix/opendmarc/opendmarc.sock +smtpd_milters = unix:/var/spool/postfix/authentication_milter/authentication_milter.sock non_smtpd_milters = $smtpd_milters # SPF policy checking smtpd_recipient_restrictions = permit_mynetworks, - reject_unauth_destination, - check_policy_service unix:private/policy-spf + reject_unauth_destination # Logging debug_peer_level = 2 diff --git a/docker/supervisor/supervisord.conf b/docker/supervisor/supervisord.conf index 1a0666e..4d4ff32 100644 --- a/docker/supervisor/supervisord.conf +++ b/docker/supervisor/supervisord.conf @@ -22,26 +22,15 @@ autostart=true autorestart=true priority=9 -# OpenDKIM service -[program:opendkim] -command=/usr/sbin/opendkim -f -x /etc/opendkim/opendkim.conf +# Authentication Milter service +[program:authentication_milter] +command=/usr/local/bin/authentication_milter --pidfile /run/authentication_milter/authentication_milter.pid autostart=true autorestart=true priority=10 -stdout_logfile=/var/log/happydeliver/opendkim.log -stderr_logfile=/var/log/happydeliver/opendkim_error.log -user=opendkim -group=mail - -# OpenDMARC service -[program:opendmarc] -command=/usr/sbin/opendmarc -f -c /etc/opendmarc/opendmarc.conf -autostart=true -autorestart=true -priority=11 -stdout_logfile=/var/log/happydeliver/opendmarc.log -stderr_logfile=/var/log/happydeliver/opendmarc_error.log -user=opendmarc +stdout_logfile=/var/log/happydeliver/authentication_milter.log +stderr_logfile=/var/log/happydeliver/authentication_milter.log +user=mail group=mail # SpamAssassin daemon