SPF
Any SMTP client can claim any domain in the envelope of a message, so a receiving server needs a way to tell whether the client is allowed to send for that domain. The Sender Policy Framework (SPF, RFC 7208) provides one. A domain publishes, in a TXT record, the IP addresses that may send mail with that domain in the SMTP envelope. A receiving server compares the client’s IP address with the record and obtains one of seven results:
pass: the address is authorised.fail: the address is not authorised.softfail: the address is probably not authorised.neutral: the domain makes no statement about the address.none: the domain has no record.temperror: a temporary DNS error prevented the check.permerror: the record is invalid.
SPF authenticates only the envelope. Its result therefore says nothing about the From header, which is the address that recipients see. DMARC connects the two through alignment.
Identities
Section titled “Identities”Stalwart checks two identities, and each has its own setting on the SenderAuth singleton (found in the WebUI under Settings › MTA › Inbound › Sender Authentication):
spfEhloVerifycovers the domain that the client gave inEHLO. It is checked when the client sendsEHLO.spfFromVerifycovers the domain of the envelope sender. It is checked atMAIL FROM. Bounces use the null sender<>, which has no domain, so for them the EHLO domain is checked aspostmaster@<EHLO domain>, as RFC 7208 specifies.
The default expressions of both fields apply relaxed on port 25 and disable on the other ports. Mail that users submit is therefore not checked:
{ "spfEhloVerify": { "match": {"0": {"if": "local_port == 25", "then": "relaxed"}}, "else": "disable" }, "spfFromVerify": { "match": {"0": {"if": "local_port == 25", "then": "relaxed"}}, "else": "disable" }}Each field is an expression that returns one of three modes:
| Mode | Effect |
|---|---|
relaxed | Perform the check and record the result in the Received-SPF and Authentication-Results headers, for DMARC, the spam filter and system Sieve scripts (env.spf.result, env.spf_ehlo.result). Never reject. |
strict | Also refuse the command unless the result is pass. |
disable | Do not perform the check. |
In strict mode, every result other than pass is refused. A temperror receives 451 4.7.24 Temporary SPF validation error., so the client retries later. The results fail, softfail, neutral, none and permerror receive 550 5.7.23 SPF validation failed, status: <result>. The command that is refused depends on the identity: a strict EHLO check refuses the EHLO command, and a strict MAIL FROM check refuses MAIL FROM.
In relaxed mode, the results do not refuse anything but still have consequences. They count towards the spam score, as tags such as SPF_ALLOW, SPF_FAIL and SPF_SOFTFAIL. They also count towards DMARC, which applies the domain owner’s policy. Failed checks are reported to domains that request SPF failure reports, and are logged as smtp.spf-ehlo-fail and smtp.spf-from-fail.