diff --git a/CHANGELOG.md b/CHANGELOG.md index 78aa307..0675280 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -216,6 +216,17 @@ Format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/); version showq service: Permission denied"), the Queue card said "Could not be read" and the queue page had no listing. Present since the persistent queue of 1.3.0; mail flow was not affected. The e2e suite opens the queue page now. +- **Port 25 answers again with DMARC report ingest on.** The generated + `master.cf` gave the port-25 service `-o smtpd_recipient_restrictions= + check_recipient_access texthash:…,…` with a space in the value; Postfix + cuts a service line at whitespace, so `smtpd` was started with a stray + argument and died with "fatal: unexpected command-line argument" on every + connection - no inbound relay and no DMARC reports were accepted while + `DMARC_REPORTS_ENABLE=true`, although `postfix check` and the suite were + silent. The list is written with commas only, the generator refuses a + value with whitespace, and the e2e check asks Postfix what it reads. + Present since DMARC ingest was fitted into the 1.9.1 fix of the same + stanza. - Overview's Processes card counts the programs that are meant to stay up ("6 of 6 running"); the idle one-shot `postfix-reload` was counted as running. The Milter sockets card no longer clips its second line with three diff --git a/build/postfix-config.sh b/build/postfix-config.sh index f0aaa01..48783ae 100644 --- a/build/postfix-config.sh +++ b/build/postfix-config.sh @@ -13,12 +13,25 @@ # a fixed literal or a container environment variable. set -eu -# postconf -P refuses master.cf -o values that contain whitespace; inbound port -# 25 needs restriction lists and milter specs that include spaces. Patch the -# smtp/inet service stanza directly instead. +# Sets one -o option of the smtp/inet (port 25) service by patching its stanza. +# +# A master.cf -o value must not contain whitespace: master splits the line on +# it, so "-o name=check_recipient_access texthash:/x,reject" reaches smtpd as +# the option "name=check_recipient_access" plus a stray argument, and smtpd dies +# at start with "fatal: unexpected command-line argument" - port 25 then +# answers nobody. (postconf -P refuses such values for the same reason.) Postfix +# splits a restriction list on commas as well as on whitespace, so every value +# passed here is written with commas only; a value with whitespace is a bug in +# this script and stops the container. master_cf_set_smtp_inet_o() { param="$1" value="$2" + case "$value" in + *[[:space:]]*) + echo "FATAL: master.cf -o ${param} would contain whitespace: ${value}" >&2 + exit 1 + ;; + esac awk -v p="$param" -v v="$value" ' /^smtp[ \t]+inet[ \t]/ && !done { print @@ -284,7 +297,9 @@ if [ "${INBOUND_RELAY_ENABLE}" = "true" ] || [ "${DMARC_REPORTS_ENABLE}" = "true RELAY_DOMAINS_SETTING="texthash:${DMARC_RELAY_DOMAINS_MAP}" TRANSPORT_SETTING="texthash:${DMARC_TRANSPORT_MAP}" fi - RECIPIENT_RESTRICTIONS="check_recipient_access texthash:${DMARC_RECIPIENTS_MAP},${RECIPIENT_RESTRICTIONS}" + # Comma, not a space, between check_recipient_access and its table: see + # master_cf_set_smtp_inet_o. + RECIPIENT_RESTRICTIONS="check_recipient_access,texthash:${DMARC_RECIPIENTS_MAP},${RECIPIENT_RESTRICTIONS}" fi postconf -e \ "relay_domains=${RELAY_DOMAINS_SETTING}" \ diff --git a/test/e2e/port25_config_check.go b/test/e2e/port25_config_check.go index 44b82d8..faf0d03 100644 --- a/test/e2e/port25_config_check.go +++ b/test/e2e/port25_config_check.go @@ -47,7 +47,7 @@ var port25Cases = []port25Case{ "DMARC_REPORTS_ENABLE=true", }, check: []optionCheck{ - {param: "smtpd_recipient_restrictions", contains: "check_recipient_access texthash:/data/postfix/dmarc_recipients,reject", wantExact: true}, + {param: "smtpd_recipient_restrictions", contains: "check_recipient_access,texthash:/data/postfix/dmarc_recipients,reject", wantExact: true}, {param: "smtpd_milters", contains: "", wantExact: true}, }, }, @@ -58,7 +58,7 @@ var port25Cases = []port25Case{ "DMARC_REPORTS_ENABLE=true", }, check: []optionCheck{ - {param: "smtpd_recipient_restrictions", contains: "check_recipient_access texthash:/data/postfix/dmarc_recipients,reject_unauth_destination,reject_unlisted_recipient", wantExact: true}, + {param: "smtpd_recipient_restrictions", contains: "check_recipient_access,texthash:/data/postfix/dmarc_recipients,reject_unauth_destination,reject_unlisted_recipient", wantExact: true}, {param: "smtpd_milters", contains: "", wantExact: true}, }, }, @@ -116,6 +116,18 @@ func checkPort25ConfigCombinations(s *stack) error { case !c.wantExact && !strings.Contains(got, c.contains): return fmt.Errorf("%s: -o %s = %q, want it to contain %q", tc.name, c.param, got, c.contains) } + // What the file says is not yet what Postfix reads: master splits + // a service line on whitespace, so a value with a space in it is + // cut at the space and the rest becomes a stray smtpd argument + // ("fatal: unexpected command-line argument", port 25 dead while + // `postfix check` stays silent). Ask Postfix itself. + seen, err := s.execIn("selfpost", "postconf", "-Ph", "smtp/inet/"+c.param) + if err != nil { + return fmt.Errorf("%s: postconf -P smtp/inet/%s: %v\n%s", tc.name, c.param, err, seen) + } + if strings.TrimSpace(seen) != got { + return fmt.Errorf("%s: master.cf has -o %s = %q but Postfix reads %q - the value contains whitespace", tc.name, c.param, got, strings.TrimSpace(seen)) + } } }