For most purposes, including basic message authentication checks (SPF, DMARC, BIMI, ..), either interface is suitable. For content checking (blocking unwanted attachment), signatures (DKIM, ARC) and such, you need an interface that gives you that information, such as the milter one.
The interfaces do offer most features and basic actions, namely both can:
- investigate the sending address, name and envelope information
- permanently reject (code 5XX) or temporarily defer (code 4XX a.k.a "tempfail"), with optionally custom smtp status code, extended status code, and (possibly multi-line) smtp status text.
- receive additional configuration:
milter_macro_v
/policy_context
- prepend message headers (a popular choice would be
Authentication-Results:
)
- schedule a message for silent discarding or quarantine despite signalling acceptance
The interface also share some limitations, though specifics differ:
- your software must not malfunction on multi-recipient messages
- your software needs to get IDN handling right. (those lovely
xn--
A-labels in domains)
- partly by their nature, partly by design, neither interface permits graceful handling local maildrop (non-SMTP-submissions) in all the same ways you could handle SMTP submissions.
- your software needs to get stuff done while the sender is waiting - expensive processing, network lookups or unreliable checks must at some point be interrupted, the message deferred, and measures taken so it finishes in time next time.
Unique for milter interface
The milter interface allows you to parse, and selectively remove or rewrite arbitrary headers, or message body (parts). However, editing is possibly better done in post-queue processing, past address-rewriting and multi-recipient pitfalls.
In any case, message syntax and encoding features must not not make your software misbehave. Think of the notoriously difficult-to-parse address headers and attachment filenames. I have yet to encounter software that gets this 100% right.
You want to use the milter interface if you need to act on non-default SMTP commands, or care about SMTP session state transitions. In the milter interface, you get to handle things like RSET
and (must) handle your local state accordingly - where in the policy interface postfix sends you what you need with the effective protocol_state
.
Beware: the milter interface documentation is far from clear on where Postfix aims for sendmail bug-compatibility, and where it deviates. Also, the header hook was broken in the past (in by now unsupported releases).
Milters signals their final decisions to postfix by (if using the C library) returning fixed SMFIS_*
codes (e.g. SMFIS_TEMPFAIL
for deferral), but may precede that to with a smfi_setreply(…, "4XX", "4.X.X", text)
call to customize the return.
Unique to policy daemon interface
You may choose the order between restriction classes and your daemon - that is much more versatile than just ordering milters against other milters.
You want to use the the policy interface if you need to return non-SMTP results, such as defer_if_permit
or your custom smtpd_restriction_classes
. Cases where you will need this include the performance motivated need skip some checks for very busy servers. E.g. when your policy must be, rather than being the final arbiter of what can be accepted or not, merely select a message class to be processed in additional checks or routing decisions.
Policy daemons signal their final decisions to postfix via textual actions and their arguments, if applicable, e.g. action=DEFER optional text...
(which is a fancier way of saying action=450 4.7.1 optional text...
)