123 lines
5.6 KiB
Bash
123 lines
5.6 KiB
Bash
|
|
#!/bin/sh
|
||
|
|
# MAS Postfix integration profile (MAS-WP-0004). Configures an installed
|
||
|
|
# Postfix to receive mail for one MAS domain and hand every recipient to
|
||
|
|
# mas-gateway. See README.md for why each setting is needed.
|
||
|
|
#
|
||
|
|
# install.sh --domain D --statute FILE --app-url URL [--gateway CMD] [--auth]
|
||
|
|
# [--relay HOST:PORT --relay-credentials DIR]
|
||
|
|
#
|
||
|
|
# --relay sends outbound mail (the app's replies) through an authenticated
|
||
|
|
# submission relay; DIR holds the files username and password. Without it
|
||
|
|
# all outbound mail fails fast.
|
||
|
|
# --auth adds sender authentication (OpenDKIM verify + OpenDMARC milters,
|
||
|
|
# annotate-only) and strips forged Authentication-Results for D. The caller
|
||
|
|
# starts the milters (opendkim -f -x /etc/opendkim.conf, opendmarc -f -c
|
||
|
|
# /etc/opendmarc.conf) and Postfix.
|
||
|
|
set -e
|
||
|
|
|
||
|
|
GATEWAY=/usr/local/bin/mas-gateway
|
||
|
|
AUTH=no
|
||
|
|
while [ $# -gt 0 ]; do
|
||
|
|
case "$1" in
|
||
|
|
--domain) DOMAIN=$2; shift 2 ;;
|
||
|
|
--statute) STATUTE=$2; shift 2 ;;
|
||
|
|
--app-url) APP_URL=$2; shift 2 ;;
|
||
|
|
--gateway) GATEWAY=$2; shift 2 ;;
|
||
|
|
--auth) AUTH=yes; shift ;;
|
||
|
|
--relay) RELAY=$2; shift 2 ;;
|
||
|
|
--relay-credentials) RELAY_CREDS=$2; shift 2 ;;
|
||
|
|
*) echo "install.sh: unknown argument $1" >&2; exit 64 ;;
|
||
|
|
esac
|
||
|
|
done
|
||
|
|
: "${DOMAIN:?--domain is required}" "${STATUTE:?--statute is required}" "${APP_URL:?--app-url is required}"
|
||
|
|
DOMAIN_RE=$(printf '%s' "$DOMAIN" | sed 's/\./\\./g')
|
||
|
|
|
||
|
|
# 1. Relay control. Stock reject_unauth_destination refuses every local-part
|
||
|
|
# containing '!' or '%' (sender-specified routing), so it cannot be used.
|
||
|
|
# An explicit regexp permits only @DOMAIN; everything else hits the final
|
||
|
|
# 'reject' (smtpd will not start without a reject-class entry).
|
||
|
|
# allow_untrusted_routing=yes is needed, otherwise Postfix ignores the
|
||
|
|
# regexp result for '!'/'%' local-parts. It is safe here because
|
||
|
|
# relay_domains is empty and nothing rewrites such addresses (2.).
|
||
|
|
printf '/@%s$/ permit\n' "$DOMAIN_RE" > /etc/postfix/mas_rcpt.regexp
|
||
|
|
postconf -e \
|
||
|
|
"myhostname=$DOMAIN" "mydomain=$DOMAIN" "mydestination=$DOMAIN" "relay_domains=" \
|
||
|
|
"mynetworks=127.0.0.0/8" \
|
||
|
|
"smtpd_relay_restrictions=permit_mynetworks,check_recipient_access regexp:/etc/postfix/mas_rcpt.regexp,reject" \
|
||
|
|
"allow_untrusted_routing=yes"
|
||
|
|
|
||
|
|
# 2. Never rewrite MAS syntax: no bang-path swap, no percent hack.
|
||
|
|
postconf -e "swap_bangpath=no" "allow_percent_hack=no"
|
||
|
|
|
||
|
|
# 3. Every recipient of DOMAIN goes to the mas pipe, one recipient per call,
|
||
|
|
# as the envelope recipient Postfix received. recipient_delimiter=+ does
|
||
|
|
# not change ${recipient}; it only lets Postfix know facets exist.
|
||
|
|
postconf -e "local_transport=mas" "local_recipient_maps=" \
|
||
|
|
"mas_destination_recipient_limit=1" "recipient_delimiter=+"
|
||
|
|
if ! grep -q '^mas[[:space:]]' /etc/postfix/master.cf; then
|
||
|
|
cat >> /etc/postfix/master.cf <<CF
|
||
|
|
mas unix - n n - - pipe
|
||
|
|
flags=Rq user=mas argv=$GATEWAY
|
||
|
|
--statute $STATUTE --trusted-authserv-id $DOMAIN
|
||
|
|
pipe --app-url $APP_URL --recipient \${recipient}
|
||
|
|
CF
|
||
|
|
fi
|
||
|
|
|
||
|
|
# 4. Hygiene for a mail endpoint that only receives.
|
||
|
|
postconf -e "disable_vrfy_command=yes" "smtpd_helo_required=yes" "smtpd_recipient_limit=20"
|
||
|
|
|
||
|
|
# 5. Outbound. Only loopback may send (mynetworks), so this is the app's
|
||
|
|
# replies. Postfix's own bounces (null sender) are discarded rather than
|
||
|
|
# relayed, so the app's send cap bounds everything sent through the relay.
|
||
|
|
if [ -n "$RELAY" ]; then
|
||
|
|
: "${RELAY_CREDS:?--relay needs --relay-credentials}"
|
||
|
|
printf '%s %s:%s\n' "$RELAY" "$(cat "$RELAY_CREDS/username")" \
|
||
|
|
"$(cat "$RELAY_CREDS/password")" > /etc/postfix/sasl_passwd
|
||
|
|
chmod 600 /etc/postfix/sasl_passwd && postmap /etc/postfix/sasl_passwd
|
||
|
|
postconf -e "relayhost=$RELAY" "smtp_sasl_auth_enable=yes" \
|
||
|
|
"smtp_sasl_password_maps=hash:/etc/postfix/sasl_passwd" \
|
||
|
|
"smtp_sasl_security_options=noanonymous" "smtp_tls_security_level=secure" \
|
||
|
|
"smtp_tls_CApath=/etc/ssl/certs" \
|
||
|
|
"sender_dependent_default_transport_maps=inline:{<>=discard:}" \
|
||
|
|
"empty_address_default_transport_maps_lookup_key=<>"
|
||
|
|
else
|
||
|
|
postconf -e "default_transport=error:5.7.1 outbound mail disabled"
|
||
|
|
fi
|
||
|
|
|
||
|
|
[ "$AUTH" = yes ] || exit 0
|
||
|
|
|
||
|
|
# 6. Sender authentication (spec 14 item 10): annotate only, never reject,
|
||
|
|
# accept if a milter is down. Our own replies (loopback) are skipped.
|
||
|
|
# header_checks drop incoming A-R headers claiming DOMAIN; they do not
|
||
|
|
# apply to milter-added headers, so only the milters' results survive.
|
||
|
|
# (OpenDKIM's RemoveARFrom did not remove them in testing.)
|
||
|
|
mkdir -p /run/opendkim /run/opendmarc
|
||
|
|
chown opendkim:opendkim /run/opendkim && chown opendmarc:opendmarc /run/opendmarc
|
||
|
|
printf '127.0.0.1\n' > /etc/opendkim-peers
|
||
|
|
cat > /etc/opendkim.conf <<CONF
|
||
|
|
Mode v
|
||
|
|
Socket inet:8891@127.0.0.1
|
||
|
|
AuthservID $DOMAIN
|
||
|
|
PeerList /etc/opendkim-peers
|
||
|
|
UserID opendkim
|
||
|
|
PidFile /run/opendkim/opendkim.pid
|
||
|
|
Syslog no
|
||
|
|
CONF
|
||
|
|
cat > /etc/opendmarc.conf <<CONF
|
||
|
|
Socket inet:8893@127.0.0.1
|
||
|
|
AuthservID $DOMAIN
|
||
|
|
TrustedAuthservIDs $DOMAIN
|
||
|
|
SPFSelfValidate true
|
||
|
|
SPFIgnoreResults true
|
||
|
|
RejectFailures false
|
||
|
|
PublicSuffixList /usr/share/publicsuffix/public_suffix_list.dat
|
||
|
|
UserID opendmarc
|
||
|
|
PidFile /run/opendmarc/opendmarc.pid
|
||
|
|
Syslog false
|
||
|
|
CONF
|
||
|
|
printf '/^Authentication-Results:[[:space:]]*%s([[:space:];]|$)/ IGNORE\n' "$DOMAIN_RE" \
|
||
|
|
> /etc/postfix/ar_strip.regexp
|
||
|
|
postconf -e "header_checks=regexp:/etc/postfix/ar_strip.regexp" \
|
||
|
|
"smtpd_milters=inet:127.0.0.1:8891,inet:127.0.0.1:8893" "non_smtpd_milters=" \
|
||
|
|
"milter_default_action=accept" "milter_protocol=6"
|