Chapter 18 Mail Transport & Defense Admin: #pMtaFilters

Milters (Sendmail milter protocol) and MTA hooks (HTTP per SMTP stage)

Existing filters plug straight into the SMTP dialogue: milters speak the Sendmail milter protocol version 6 (Rspamd through its milter proxy, ClamAV-milter, OpenDKIM, DLP products); MTA hooks are plain HTTP services that receive a JSON document per stage (CONNECT, EHLO, MAIL FROM, RCPT TO, DATA) and return a decision. Both run for inbound mail (port 25) and optionally for submission (465/587). Configure them under Milters & MTA hooks (#pMtaFilters), each entry with a test button.

Operational Tip & Architecture Note

Choose the default action deliberately: "accept" (fail open) keeps mail flowing when a filter is down, "tempfail" (like Postfix milter_default_action = tempfail) makes senders retry later until the filter is back. For virus scanners tempfail is safer, for scoring services accept. Quarantine puts inbound mail into the Junk folder; on submission the message is rejected with 550.

MTA hook response (Stalwart compatible)

Request: context (stage, client, sasl, tls, server, queue, protocol), envelope (from, to) and at DATA message (headers, contents, size). Signature in the X-Nextxch-Signature header: sha256=HMAC over the body.

Reference / Configuration
POST https://dlp.internal/hook   (X-Nextxch-Hook-Stage: data)

{
  "action": "accept",
  "modifications": [
    { "type": "addHeader", "name": "X-DLP-Status", "value": "clean" },
    { "type": "changeHeader", "index": 1, "name": "Subject", "value": "[external] Offer" },
    { "type": "deleteHeader", "index": 1, "name": "X-Internal-Route" }
  ]
}

{ "action": "reject", "response": { "status": 550, "enhancedStatus": "5.7.1", "message": "Confidential data detected" } }
{ "action": "tempfail", "response": { "status": 451, "message": "Please try again later" } }
{ "action": "quarantine" }      { "action": "discard" }

Settings, Default Values & Best Practices

Setting / Location Description Default Value Production Best Practice
Milter-Adresse TCP or Unix socket of the milter: inet:host:port, inet:port@host (Sendmail notation), host:port or unix:/path. - Rspamd: inet:127.0.0.1:11332 (proxy worker with milter = yes). ClamAV-milter: unix:/run/clamav/clamav-milter.ctl or inet:127.0.0.1:7357.
Phasen und Aktionen (Milter) Option negotiation v6, macros (j, {client_addr}, i, {auth_authen}, {mail_addr}, {rcpt_addr}), connect, helo, mail, rcpt, data, header, eoh, body, eom. Replies: accept, continue, reject, tempfail, discard, reply code, quarantine. Modifications: add, insert and change headers, replace body, add/delete recipients, change sender. alle angeboten Stages the milter switches off (SMFIP_NO*) are not sent; stages without reply (SMFIP_NR_*) are not waited for.
Zeitlimits Connect, per command and for content/end of message, set separately; progress replies extend the content timeout. 5 s / 10 s / 60 s Raise the content timeout to 120 to 300 s for virus scanners with large attachments.
MTA-Hook Phasen und Sicherheit Per hook: connect, ehlo, mail, rcpt, data. Optional auth header (e.g. Authorization: Bearer ...) and HMAC-SHA256 signature; both are stored encrypted and never shown. data Enable only the stages you need: each call adds the hook's response time to the SMTP dialogue. The hook should answer in under a second.
Reihenfolge Milters and hooks run by their order field; the first rejection ends the chain. At DATA each filter sees the message with the changes of the ones before. Milter 100, Hooks 200 Put fast, cheap checks (e.g. reputation) before expensive ones (virus scan).