Skip to content

Commit

Permalink
FEAT: building mail-receiver with INCLUDE_DMARC enables SPF,DKIM & DM…
Browse files Browse the repository at this point in the history
…ARC checks
  • Loading branch information
tyb-talks committed Jun 11, 2024
1 parent 0fa2540 commit 2d5cf9f
Show file tree
Hide file tree
Showing 7 changed files with 235 additions and 2 deletions.
23 changes: 22 additions & 1 deletion Dockerfile
Original file line number Diff line number Diff line change
@@ -1,7 +1,13 @@
FROM debian:bullseye-slim

ARG INCLUDE_DMARC=false
ENV INCLUDE_DMARC=${INCLUDE_DMARC}

RUN DEBIAN_FRONTEND=noninteractive apt update \
&& DEBIAN_FRONTEND=noninteractive apt install -y --no-install-recommends curl perl postfix ruby socat \
&& if [ "$INCLUDE_DMARC" = "true" ]; then \
DEBIAN_FRONTEND=noninteractive apt install -y --no-install-recommends opendmarc opendkim opendkim-tools postfix-policyd-spf-python; \
fi \
&& DEBIAN_FRONTEND=noninteractive apt -y --purge autoremove \
&& DEBIAN_FRONTEND=noninteractive apt clean

Expand All @@ -19,12 +25,27 @@ RUN >/etc/postfix/main.cf \
&& postconf -e alias_maps= \
&& postconf -e mynetworks='127.0.0.0/8 [::1]/128 [fe80::]/64' \
&& postconf -e transport_maps=hash:/etc/postfix/transport \
&& postconf -e 'smtpd_recipient_restrictions = check_policy_service unix:private/policy' \
&& if [ "$INCLUDE_DMARC" = "true" ]; then \
postconf -e 'smtpd_recipient_restrictions=check_policy_service unix:private/policy,check_policy_service unix:private/policyd-spf' \
&& postconf -e smtpd_milters=unix:/run/opendkim/opendkim.sock,unix:/run/opendmarc/opendmarc.sock \
&& postconf -e non_smtpd_milters=$smtpd_milters \
&& postconf -e 'milter_default_action=accept'; \
else \
postconf -e 'smtpd_recipient_restrictions = check_policy_service unix:private/policy'; \
fi \
&& postconf -M -e 'smtp/inet=smtp inet n - n - - smtpd' \
&& postconf -M -e 'discourse/unix=discourse unix - n n - - pipe user=nobody:nogroup argv=/usr/local/bin/receive-mail ${recipient}' \
&& postconf -M -e 'policy/unix=policy unix - n n - - spawn user=nobody argv=/usr/local/bin/discourse-smtp-fast-rejection' \
&& if [ "$INCLUDE_DMARC" = "true" ]; then \
postconf -M -e 'policyd-spf/unix=policyd-spf unix - n n - - spawn user=nobody argv=/usr/bin/policyd-spf'; \
fi \
&& rm -rf /var/spool/postfix/*


COPY policyd-spf.conf /etc/postfix-policyd-spf-python/policyd-spf.conf
COPY opendkim.conf /etc/opendkim.conf
COPY opendmarc.conf /etc/opendmarc.conf

COPY receive-mail discourse-smtp-fast-rejection /usr/local/bin/
COPY lib/ /usr/local/lib/site_ruby/
COPY boot /sbin/
Expand Down
9 changes: 9 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,15 @@ For example, if you wanted to add a pre-delivery milter, you might use:

-e POSTCONF_smtpd_milters=192.0.2.42:12345

## SPF, DKIM and DMARC checks
These checks are enabled by default in the latest image `discourse/mail-receiver:release`.

You may also decide whether or not to set up a Postfix server that has SPF, DKIM and DMARC checks configured by setting the build argument `INCLUDE_DMARC` to `true` or `false` when building the docker container for mail-receiver:

```bash
docker build --build-arg INCLUDE_DMARC=true -t local_discourse/mail-receiver:latest .
```
Configurations for these checks are stored in their respective configuration files `policyd-spf.conf`, `opendkim.conf`, `opendmarc.conf` in this repository.

## Blacklisting sender domains

Expand Down
10 changes: 10 additions & 0 deletions boot
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,16 @@ for envvar in $(compgen -v); do
fi
done

if [ "$INCLUDE_DMARC" = "true" ]; then
echo "Starting OpenDKIM..." >&2
adduser postfix opendkim #ensure postfix is part of opendkim group so it can access the socket
/usr/sbin/opendkim -x /etc/opendkim.conf

echo "Starting OpenDMARC..." >&2
adduser postfix opendmarc #ensure postfix is part of opendmarc group so it can access the socket
/usr/sbin/opendmarc -c /etc/opendmarc.conf
fi

# Now, make sure that the Postfix filesystem environment is sane
mkdir -p -m 0755 /var/spool/postfix/pid
chown root:root /var/spool/postfix
Expand Down
2 changes: 1 addition & 1 deletion discourse_mail_receiver.gemspec
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)

Gem::Specification.new do |spec|
spec.name = 'discourse_mail_receiver'
spec.version = '4.0.7'
spec.version = '4.1.0'
spec.authors = ['Discourse Team']
spec.email = ['[email protected]']
spec.description = %q{A gem used to package the core .rb files of the mail-receiver.}
Expand Down
64 changes: 64 additions & 0 deletions opendkim.conf
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
# This is a basic configuration for signing and verifying. It can easily be
# adapted to suit a basic installation. See opendkim.conf(5) and
# /usr/share/doc/opendkim/examples/opendkim.conf.sample for complete
# documentation of available configuration parameters.

Syslog yes
SyslogSuccess yes
#LogWhy no

# Common signing and verification parameters. In Debian, the "From" header is
# oversigned, because it is often the identity key used by reputation systems
# and thus somewhat security sensitive.
Canonicalization relaxed/simple
#Mode sv
#SubDomains no
OversignHeaders From

# Signing domain, selector, and key (required). For example, perform signing
# for domain "example.com" with selector "2020" (2020._domainkey.example.com),
# using the private key stored in /etc/dkimkeys/example.private. More granular
# setup options can be found in /usr/share/doc/opendkim/README.opendkim.
#Domain example.com
#Selector 2020
#KeyFile /etc/dkimkeys/example.private

# In Debian, opendkim runs as user "opendkim". A umask of 007 is required when
# using a local socket with MTAs that access the socket as a non-privileged
# user (for example, Postfix). You may need to add user "postfix" to group
# "opendkim" in that case.
UserID opendkim
UMask 007

# Socket for the MTA connection (required). If the MTA is inside a chroot jail,
# it must be ensured that the socket is accessible. In Debian, Postfix runs in
# a chroot in /var/spool/postfix, therefore a Unix socket would have to be
# configured as shown on the last line below.
Socket local:/run/opendkim/opendkim.sock
#Socket inet:8892@localhost
#Socket inet:8891
#Socket local:/var/spool/postfix/opendkim/opendkim.sock

PidFile /run/opendkim/opendkim.pid

# Hosts for which to sign rather than verify, default is 127.0.0.1. See the
# OPERATION section of opendkim(8) for more information.
#InternalHosts 192.168.0.0/16, 10.0.0.0/8, 172.16.0.0/12

# The trust anchor enables DNSSEC. In Debian, the trust anchor file is provided
# by the package dns-root-data.
TrustAnchorFile /usr/share/dns/root.key
#Nameservers 127.0.0.1

# this server only receives email, so not necessary to have signing mode
Mode v

# ensures that even if no DKIM signature is present, we add the authentication result header
AlwaysAddARHeader true

# Selects the action to be taken when any verification or internal error of any kind is encountered. This is processed before
# the other "On-" values so it can be used as a blanket setting followed by specific overrides.
# On-Default accept
# On-KeyNotFound reject
# On-BadSignature reject
# On-DNSError tempfail
114 changes: 114 additions & 0 deletions opendmarc.conf
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
# This is a basic configuration that can easily be adapted to suit a standard
# installation. For more advanced options, see openmarc.conf(5) and/or
# /usr/share/doc/opendmarc/examples/opendmarc.conf.sample.

## AuthservID (string)
## defaults to MTA name
##
## Sets the "authserv-id" to use when generating the Authentication-Results:
## header field after verifying a message. If the string "HOSTNAME" is
## provided, the name of the host running the filter (as returned by the
## gethostname(3) function) will be used.
#
# AuthservID name

## FailureReports { true | false }
## default "false"
##
## Enables generation of failure reports when the DMARC test fails and the
## purported sender of the message has requested such reports. Reports are
## formatted per RFC6591.
#
# FailureReports false

## PidFile path
## default (none)
##
## Specifies the path to a file that should be created at process start
## containing the process ID.
#
PidFile /run/opendmarc/opendmarc.pid

## PublicSuffixList path
## default (none)
##
## Specifies the path to a file that contains top-level domains (TLDs) that
## will be used to compute the Organizational Domain for a given domain name,
## as described in the DMARC specification. If not provided, the filter will
## not be able to determine the Organizational Domain and only the presented
## domain will be evaluated.
#
PublicSuffixList /usr/share/publicsuffix/public_suffix_list.dat

## RejectFailures { true | false }
## default "false"
##
## If set, messages will be rejected if they fail the DMARC evaluation, or
## temp-failed if evaluation could not be completed. By default, no message
## will be rejected or temp-failed regardless of the outcome of the DMARC
## evaluation of the message. Instead, an Authentication-Results header
## field will be added.
#
# RejectFailures false

## Socket socketspec
## default (none)
##
## Specifies the socket that should be established by the filter to receive
## connections from sendmail(8) in order to provide service. socketspec is
## in one of two forms: local:path, which creates a UNIX domain socket at
## the specified path, or inet:port[@host] or inet6:port[@host] which creates
## a TCP socket on the specified port for the appropriate protocol family.
## If the host is not given as either a hostname or an IP address, the
## socket will be listening on all interfaces. This option is mandatory
## either in the configuration file or on the command line. If an IP
## address is used, it must be enclosed in square brackets.
#
Socket local:/run/opendmarc/opendmarc.sock

## Syslog { true | false }
## default "false"
##
## Log via calls to syslog(3) any interesting activity.
#
Syslog true

## SyslogFacility facility-name
## default "mail"
##
## Log via calls to syslog(3) using the named facility. The facility names
## are the same as the ones allowed in syslog.conf(5).
#
# SyslogFacility mail

## TrustedAuthservIDs string
## default HOSTNAME
##
## Specifies one or more "authserv-id" values to trust as relaying true
## upstream DKIM and SPF results. The default is to use the name of
## the MTA processing the message. To specify a list, separate each entry
## with a comma. The key word "HOSTNAME" will be replaced by the name of
## the host running the filter as reported by the gethostname(3) function.
#
# TrustedAuthservIDs HOSTNAME

## UMask mask
## default (none)
##
## Requests a specific permissions mask to be used for file creation. This
## only really applies to creation of the socket when Socket specifies a
## UNIX domain socket, and to the HistoryFile and PidFile (if any); temporary
## files are normally created by the mkstemp(3) function that enforces a
## specific file mode on creation regardless of the process umask. See
## umask(2) for more information.
#
UMask 0002

## UserID user[:group]
## default (none)
##
## Attempts to become the specified userid before starting operations.
## The process will be assigned all of the groups and primary group ID of
## the named userid unless an alternate group is specified.
#
UserID opendmarc
15 changes: 15 additions & 0 deletions policyd-spf.conf
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
# For a fully commented sample config file see policyd-spf.conf.commented

debugLevel = 1
TestOnly = 1

# Change these options to False if you want to pass SPF failures through to DMARC milter
HELO_reject = Fail
Mail_From_reject = Fail


PermError_reject = False
TempError_Defer = False

skip_addresses = 127.0.0.0/8,::ffff:127.0.0.0/104,::1

0 comments on commit 2d5cf9f

Please sign in to comment.