Skip to content

Improve deliverability with activated postscreen - #59

Open
chrisblech wants to merge 2 commits into
springcomp:feature/postscreenfrom
chrisblech:feature/postscreen-dnswl
Open

Improve deliverability with activated postscreen#59
chrisblech wants to merge 2 commits into
springcomp:feature/postscreenfrom
chrisblech:feature/postscreen-dnswl

Conversation

@chrisblech

Copy link
Copy Markdown
Contributor

Motivation

After enabling postscreen (#55) on a production instance, we observed that mail from several large senders (e.g. Microsoft 365 / Outlook outbound) was either delayed by a significant margin or not delivered within a reasonable time at all.

The cause is postscreen's documented PASS NEW behavior: any client postscreen hasn't seen before is disconnected with a temporary 450 4.3.2 after passing the enabled tests, and only allowed straight through to smtpd on a subsequent connection. For senders with a small, stable set of outbound IPs this costs one retry and is then cached for postscreen_cache_retention_time. For senders that spread retries across a large, frequently-rotating IP pool, every attempt can look "new" to postscreen again, so the delay keeps recurring instead of resolving after the first retry.

Change

Add list.dnswl.org as a reputation source in postscreen_dnsbl_sites, weighted negative enough to clear postscreen_dnsbl_threshold. Reputable senders (most large providers are listed) then get scored before the greeting and are passed straight to smtpd, skipping the mandatory first-contact retry entirely. Unlisted/unknown clients are unaffected and keep going through the existing tests as before.

This also re-adds the dnsblog(8) service to master.cf, which PR #55 had dropped since it deliberately didn't use any DNSBL/DNSWL scoring — dnsblog is what postscreen delegates its DNS-based lookups to.

Testing

Verified locally with a Docker build of this branch: a compliant test client still receives the standard 450 + PASS NEW treatment when unlisted (no regression), and the resolver correctly reaches list.dnswl.org and evaluates the response against the configured weight patterns (confirmed via dnsblog log output). The container's own healthcheck (loopback, covered by permit_mynetworks) is unaffected and still gets an immediate banner.

Without any reputation signal, postscreen forces every source IP it
hasn't seen before through a mandatory temp-reject-then-retry cycle
before handing off to smtpd (documented "PASS NEW" behavior), even
when the client behaves perfectly. For senders with small, stable
outbound IP pools this only costs one retry, cached for
postscreen_cache_retention_time afterwards. For senders that spread
retries across a large, frequently-rotating pool (M365, Google, ...)
every attempt can look "new" again, causing repeated delivery delays.

Score reputable senders from list.dnswl.org negative enough to clear
postscreen_dnsbl_threshold, so postscreen fast-passes them straight to
smtpd instead of forcing the retry. Re-adds the dnsblog(8) auxiliary
service to master.cf (dropped in the postscreen PR since it
deliberately skipped DNSBL/DNSWL scoring) - postscreen delegates all
DNS-based lookups to it.
@chrisblech

Copy link
Copy Markdown
Contributor Author

After running this in production for two days, I observed a significant reduction in spam volume. However, some legitimate emails were delivered with a substantial delay of up to 20 hours, despite the persistence of the postscreen cache and an active DNSBL whitelist. For this reason, I recommend disabling some of the filters again - see bfcc02e

postscreen_bare_newline_enable, postscreen_non_smtp_command_enable and
postscreen_pipelining_enable each unconditionally force postscreen's
PASS_NEW temp-reject-then-reconnect cycle on every first-time client,
regardless of whether that client actually violates the test - this is
not tied to the *_action setting (confirmed empirically: even with
action=ignore, merely enabling one of these three still forces the
retry). Since the retry timing is entirely up to the sending MTA's own
backoff schedule, this held up real deliveries by hours (observed: up
to ~20h in production), and recurs for any correspondent postscreen
hasn't already cached, not just as one-off "cold cache" cost.

postscreen_greet_action (the PREGREET test) does not have this
problem - confirmed locally that a clean client passes straight
through with no forced reconnect when only greet_action + the DNSWL
scoring from the previous commit are configured. PREGREET alone
already caught 100% of the bot traffic observed in production logs
(including a live exploit-scan attempt), so this keeps the protection
that was actually earning its keep while dropping the tests that only
imposed cost.
@chrisblech
chrisblech force-pushed the feature/postscreen-dnswl branch from bfcc02e to 380ea6a Compare August 28, 2026 12:38
@chrisblech chrisblech changed the title Whitelist reputable senders via list.dnswl.org in postscreen Improve deliverability with activated postscreen Aug 28, 2026
@chrisblech chrisblech mentioned this pull request Aug 28, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant