Add SPF records

This section defines HOWTO configure a Sender Policy Framework (SPF) record for a domain and its mail servers.
Briefly the design intent of the SPF record is to allow a receiving MTA (Message Transfer Agent) to interrogate the Name Server of the domain which appears in the email (the sender) and determine if the originating IP of the mail (the source) is authorized to send mail for the sender’s domain.

The SPF information is contained in a standard TXT RR (though a new RR type may be allocated if and when SPF reaches standardization by the IETF).

If a SPF (TXT) RR exists and authorizes the source IP address the mail can be accepted by the MTA. If the SPF (TXT) RR does not authorize the IP address the mail can be bounced – it did not originate from an authorized source for the sender’s domain. If the domain does not have an SPF RR the situation is no worse than before.

Many Open Source MTAs have already been modified to use the SPF record and there is no down-side and plenty of potential up-side to implement the proposed record format now.

We use the following terminology to try and simplify the descriptions below:
sender – the full email address of the originator of the mail item (typically uses return-path in the actual SPF checks)
source-ip – the IP address of the SMTP server trying to send this message
sender-domain the domain name part of the sender’s email address e.g. assume the sender is info@example.com the sender-domain is example.com.

The SPF record defines one or more tests to carry out to verify the sender. Each test returns a condition code (pre below). The first test to pass will terminate SPF processing.

v=spf1 Mandatory. Defines the version being used. Currently the only version supported is spf1.
pre Optional (defaults to +). pre defines the code to return when a match occurs. If a test is conclusive either add + or omit (defaults to +). If a test might not be conclusive use “?” or “~” (tilde). “-“(minus) is typically only used with -all to indicate that if we have had no previous matches – fail.
Value Description
+ Default. Pass.
– Fail.
~ Softfail.
? Neutral.

type

Defines the mechanism type to use for verification of the sender. May take one of the following values:
Basic Mechanisms

These types do NOT define a verification mechanism but affect the verification sequence.
include – Recurse (restart) testing using supplied domain. The sender-domain is replaced with the included domain name. Example:

; spf record for example.com
example.com. IN TXT “v=spf1 include:example.net -all”
; use the SPF details for example.net
; in the above case to replace example.com’s SPF
; or
example.com. IN TXT “v=spf1 mx include:example.net -all”
; additive – use MX RR for example.com
; AND if that fails use example.nets’s SPF
all – The all type terminates processing (but may be optionally followed by a mod value). It is defined to be optional but it is a Good Thing™ to include it. It is normally present in the form -all to signify that if processing reaches this point without a prior match the result will be fail. But if you are not sure that the tests are conclusive you could use ?all which would allow mail to be accepted even if all previous checks failed.
Sender Mechanisms

These types define a verification mechanism.
ip4 – use IP Version 4 addresses e.g. 192.168.3.0 for verification
ip6 – use IP Version 6 addresses for verification
a – use DNS A RRs for verification
mx – use DNS MX RRs for verification
ptr – use DNS PTR RRs for verification
exists – test for existence of domain
Value Description

a
a:domain
a:domain/cidr
a/cidr

In its base form this uses the sender-domain to find an A RR(s) to verify the source. This form relies on an A RR for the domain e.g.

; fragment for example.com
$ORIGIN example.com.
example.com. IN TXT “v=spf1 a -all”
; needs domain A record
@ IN A 192.168.0.3
; functionally the same as
example.com. IN A 192.168.0.3

The form a/cidr applies the test to the cidr (or IP refix or slash) range of the sender-domain’s A RR.

The form a:domain replaces sender-domain with domain’s A RR for verification. This does NOT use domain’s SPF record(s) (use include for that). The domain form may use macro-expansion features. Example:

; fragment for example.net
$ORIGIN example.net.
@ IN TXT “v=spf1 a:example.com -all”
; will use a single A query to example.com
; which may not yield the result expected unless
; example.com has an A record as below
@ IN A 192.168.0.3
; functionally the same as
example.com. IN A 192.168.0.3

can take a host name format as shown below:

; fragment for example.net
$ORIGIN example.net.
@ IN TXT “v=spf1 a:mail.example.com -all”
; will use a single A query for mail.example.com

The form a:domain/cidr applies the cidr range to the IP address obtained from the A query e.g.

; fragment for example.net
$ORIGIN example.net.
@ IN TXT “v=spf1 a:mail.example.com/27 -all”
; will use a single A query for mail.example.com

Any of the 32 IP addresses that contain mail.example.com will pass. e.g. if the source-ip is 192.168.0.25 and the A RR for mail.example.net is 192.168.0.2 then the test will pass.

mx
mx:domain
mx:domain/cidr
mx/cidr

This basic form without any extensions uses the MX RR of the sender-domain to verify the mail source-ip. The MX record(s) return a host name from which the A record(s) can be obtained and compared with the source-ip. The form mx/cidr applies the IP Prefix or slash range to the A RR address. With any of the domain extensions the MX record of the designated (substituted) domain is used for verification. The domain form may use macro-expansion features.

Warning Remember the MX RR defines the receiving MTA. If this is not the same host(s) as the sending (SMTP) MTA tests based on an mx type will fail. Examples:

; fragment for example.com
$ORIGIN example.com.
IN TXT “v=spf1 mx:example.net -all”
; verify sender using exmple.net MX and A RRs

; fragment for example.com
$ORIGIN example.com.
IN TXT “v=spf1 mx:/26 -all”
; verify sender using exmple.com MX and A RRs
; and use 16 address range
ptr
ptr/domain

Use the source-ip’s PTR RR and a reverse map query. The AA RR for the host is then obtained. If this IP matches the sender-ip AND the sender-domain is the same as the domain name of the host obtained from the PTR RR then the test passes. The form ptr:domain replaces the sender-domain with domain in the final check for a valid domain name. The domain form may use macro-expansion features. The PTR record is the least preferred solution since it places a load on the IN-ADDR.ARPA (IPv4) or IPV6.ARPA reverse-map domains which generally have less capacity than the gTLD and ccTLD domains. Examples:

; fragment for example.com
$ORIGIN example.com.
@ IN TXT “v=spf1 ptr -all”
; the effect is to allow any host which is reverse mapped
; in the domain to send mail
ip4:ipv4
ip4:ipv4/cidr In its basic form defines an explicit ipv4 address to verify the mail source-ip. If the source-ip is the same as ipv4 the test passes. May optionally take the form ipv4/cidr to define a valid IP address range. Since this type incurs the least additional load on the DNS the current draft of the proposed RFC recommends this format. Examples:

; fragment for example.com
$ORIGIN example.com.
@ IN TXT “v=spf1 ip4:192.168.0.2 -all”
; if source-ip is 192.168.0.2 test passes
; cidr format
@ IN TXT “v=spf1 ip4:192.168.0.2/27 -all”
; if source-ip is in range 192.168.0.1
; to 192.168.0.31 test passes
ip6:ipv6
ip6:ipv6/cidr In its basic form defines an explicit ipv6 address to verify the mail source-ip. If the source-ip is the same as ipv6 the test passes. May optionally take the form ipv6/cidr to define a valid IP address range. Since this type incurs the least additional load on the DNS the current draft of the proposed RFC recommends this format. Examples:

; fragment for example.com
$ORIGIN example.com.
@ IN TXT “v=spf1 ip6:2001:db8::10 -all”
; if source-ip is 2001:db8:0:0:0:0:0:10 test passes
; cidr format
@ IN TXT “v=spf1 ip4:2001:db8::10/120 -all”
; if source-ip is in range 2001:db8:0:0:0:0:0:0
; to 2001:db8:0:0:0:0:0:FF test passes
exists:domain

The existence (any valid A RR) of the specified domain allows the test to pass. Domain may use macro-expansion features.

mod

Two optional record modifiers are defined. If present they should follow the last type directive i.e. after the all. The current values defined are as follows:
Modifier Description
redirect=domain Redirects verification to use the SPF records of the defined domain. Functionally equivalent to include but can appear on its own (without a terminating all) or can placed after the all which means “if all the previous test fail try this redirect”. Examples:

; fragment for example.com
$ORIGIN example.com.
@ IN TXT “v=spf1 ip4:192.168.0.2 -all redirect=example.net”
; if source-ip is 192.168.0.2 test passes
; if it fails redirect to example.net
; OR single redirect
@ IN TXT “v=spf1 redirect=example.net”
; only use example.net SPF record
exp=txt-rr

The exp record if present should come last in a SPF record (after the all if present). It defines a DNS name whose TXT record’s text may be returned with any failure message. Example:

; domain SPF record
IN TXT “v=spf1 mx -all exp=getlost.mydomain.com”
; the getlost TXT record
getlost IN TXT “Not authorized to send mail for the domain”

The syntax allowed by this record is significantly more complex (see macro-expansion below.
Macro-Expansion

SPF defines a number of macro-expansion features as defined below:
Modifier Description
%(c) Only allowed in TXT records referenced by the exp field. The IP of the receiving MTA.
%(d) The current domain normally the sender-domain %(o) but replaced by the value of any domain argument in the type above.
%(h) The domain name supplied on HELO or EHLO, normally the hostname of the sending SMTP server.
%(i) sender-ip The IP of SMTP server sending mail for user info@example.com.
%(l) replace with local part of sender e.g. if sender is infor@example.com local part is info.
%(o) The sender-domain e.g. if email address is info@example.com the sender-domain is example.com.
%(p) The validated domain name. The name obtained using the PTR RR of the sender-ip. Use of this macro will require an additional query unless a ptr type is used.
%(r) Only allowed in TXT records referenced by the exp field. The name of the host performing the SPF check. Normally the same as the receiving MTA.
%(t) Only allowed in TXT records referenced by the exp field. Current timestamp.
%(s) Replace with sender email address e.g. info@example.com
%(v) Replaced with “in-addr” if sender-ip is an IPv4 address and “ipv6” if an IPv6 address. Used to construct reverse map strings.

The above macros may take one or more additional arguments as follows:

r – Indicates reverse the order of the field e,g, %(or) would be displayed as com.example and %(ir) would display 192.168.0.2 as 2.0.168.192. The normal split used “.” (dor) as the separator but any other separator may be used e.g. %(sr@) would display example.com.info, when fields are rejojned they will always use a “.” (dot).

digit – the presence of a digit (range 1 to 128) limits the number of right most elements displayed e.g. %(d1) displays com only from example.com but %(d5) would display up to five right hand element up to the maximum available e.g. example.com.
Examples
Example 1

Example 1: Assumes a single mail server which both sends and receives mail for the domain.

; zone file fragment for example.com
$ORIGIN example.com.
IN MX 10 mail.example.com.
….
mail IN A 192.168.0.4
; SPF stuff
; domain SPF
example.com. IN TXT “v=spf1 mx -all”
; mail host SPF
mail IN TXT “v=spf1 a -all”

Notes:
the domain SPF is returned from a sender-domain query using the sender’s email address e.g. sender = info@example.com sender-domain = example.com. The SPF record only allows the MX host to send for the domain.
the mail host SPF is present in case the receiving MTA uses a reverse query to obtain the source-ip host name and then does a query for the SPF record of that host. The SPF record states that the A record of mail.example.com alone is permitted to send mail for the domain.

If the domain contains multiple MX servers the domain SPF would stay the same but each mail host should have a SPF record.

Share on Twitter
Share on Facebook