#!/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 < /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 < /etc/opendmarc.conf < /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"