There will be time that you have to succumb to things that you’ve been avoiding for the longest of time.. Such as sending bulkmail, either for let say, sending billing infos to your customers, or -god forbid- spa..I mean advertisement. On the other hand, you, google, yahoo and any other email providers would prefer that the users are not bothered with spams. So, some MTA start validating whether an email is sent through an smtp server that is allowed to send email on behalf of a domain. If it’s spam, at least it comes from a legit mail server. You can look at MTA event to see whether your traffic are being throttled. Here’s how it looks on a Trend Micro IMSS:
September 7, 2018 7:54:55 PM <xxyy@gmail.com> : Reply from aaa.bbb.ccc.ddd[aaa.bbb.ccc.ddd]: \n <<< 421-4.7.0 This message does not have authentication information or fails to pass\r\n421-4.7.0 authentication checks. To best protect our users from spam, the\r\n421-4.7.0 message has been blocked. Please visit\r\n421-4.7.0
At first, a recipient smtp server will validate an email sender with reverse DNS query. As long as the sender’s IP address point to a DNS record on the originating domain, the node can send email. Nowadays, your mail server might be hosted by some cloud providers, and you have no control of PTR records of these nodes, so a more robust identification method is required. I’ve listed a couple of stuff that might make google, yahoo and other e-mail providers tolerate your spammin-way a bit. Sender Policy Framework (SPF) record is one of them, and in this post we are going to dig a wee bit more about it.
What is SPF Record
v=spf1 mx ptr a:sendx.mach5.web.id ~all
So, first thing first. SPF record allows your domain server to announce who, or which nodes residing on internet are permitted to send e-mail from your domain. It can be your incoming mail servers or MX, it can be a specific member of your domain, or a random server on the internet identified only by their IP address. When an smtp server receive an email identifying your domain as the source, it can ask your DNS server for the SPF record to validate whether the sender smtp server is allowed to send e-mail on behalf of your domain. For example, let’s take a look at phoronix.com’s SPF record:
surfer@M5Rx:~$ dig phoronix.com txt ; <<>> DiG 9.11.3-1ubuntu1.2-Ubuntu <<>> phoronix.com txt ;; global options: +cmd ;; Got answer: ;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 9925 ;; flags: qr rd ra; QUERY: 1, ANSWER: 2, AUTHORITY: 0, ADDITIONAL: 1 ;; OPT PSEUDOSECTION: ; EDNS: version: 0, flags:; udp: 65494 ;; QUESTION SECTION: ;phoronix.com. IN TXT ;; ANSWER SECTION: phoronix.com. 300 IN TXT "google-site-verification=qxROJibLvBcqIvekS40fMEJQn7GEg8K9hgIS7w5MDmk" phoronix.com. 300 IN TXT "v=spf1 +a +mx +ip4:23.111.154.110 ~all"
Here’s the breakdown of each part:
- v=spf1 informs us that the record is using the version 1 of the spf protocol
- +a means phoronix.com allows e-mails to be sent from any node in the internet that can be identified with an “A” record
- +mx means phoronix.com allows e-mail to be sent from any nodes listed on the MX records of phoronix.com
- +ip4:23.111.154.110 means a node on internet, identified by a public IP of 23.111.154.110 can also send e-mail on behalf of phoronix.com
- ~all means that the recipient smtp server should still accept email from any nodes that cannot be identified by any of the first 3 rules, but it should mark them as SPF failure
SPF record is processed from left to right. On the above example, the recipient email address would start identifying whether the sender is listed as an A record on phoronix.com DNS server, and finally at the end, when a node fails the first 3 checks, will still allow the email to sent, but mark it as SPF failure.
Qualifiers and Attributes
So, you noticed that each of the rules are initiated with “+” or “~”. These are called qualifiers, and there are actually 4 of them, which are:
- “+” or pass means senders that match this rule are allowed to send email. It’s also the default qualifier, so that stating “+a” is equivalent to just stating “a”
- “-” or fail on the other hand inform the recipient email to server to reject any nodes that match this rule.
- “~” or soft fail asks the recipient email address to still receive the email when a node matches this rule, but mark it as SPF failure. “~all” is usually used to close an SPF record
- “?” or neutral means a node that matches the rule will neither pass or fail SPF check
We can identify a sender by several attributes:
- “a” matches sender to an “A” record of a domain. It’s equivalent of performing: <
surfer@M5Rx:~$ dig sx.mach5.web.id ; <<>> DiG 9.11.3-1ubuntu1.2-Ubuntu <<>> journal.mach5.web.id ;; global options: +cmd ;; Got answer: ;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 58323 ;; flags: qr rd ra; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 1 ;; OPT PSEUDOSECTION: ; EDNS: version: 0, flags:; udp: 65494 ;; QUESTION SECTION: ;journal.mach5.web.id. IN A ;; ANSWER SECTION: sx.mach5.web.id. 300 IN A 151.130.215.14 ;; Query time: 28 msec ;; SERVER: 127.0.0.53#53(127.0.0.53) ;; WHEN: Sun Sep 23 17:50:27 WIB 2018 ;; MSG SIZE rcvd: 65
a:sendx.mach5.web.id
- “mx” queries whether the sender node is listed on the sender’s domain MX records. It is equivalent to performing:
surfer@M5Rx:~$ dig phoronix.com mx ; <<>> DiG 9.11.3-1ubuntu1.2-Ubuntu <<>> phoronix.com mx ;; global options: +cmd ;; Got answer: ;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 30953 ;; flags: qr rd ra; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 1 ;; OPT PSEUDOSECTION: ; EDNS: version: 0, flags:; udp: 65494 ;; QUESTION SECTION: ;phoronix.com. IN MX ;; ANSWER SECTION: phoronix.com. 300 IN MX 0 dc-bb62d43202f0.phoronix.com. ;; Query time: 27 msec ;; SERVER: 127.0.0.53#53(127.0.0.53) ;; WHEN: Sun Sep 23 17:57:48 WIB 2018 ;; MSG SIZE rcvd: 73
- “ptr” informs the recipient smtp server to check whether the sender’s IP can pass a reverse DNS query and point to the exact FQDN of the sender node. It’s equivalent to performing:
surfer@M5Rx:~$ dig -x 111.27.182.216 ; <<>> DiG 9.11.3-1ubuntu1.2-Ubuntu <<>> -x 110.35.82.179 ;; global options: +cmd ;; Got answer: ;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 20634 ;; flags: qr rd ra; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 1 ;; OPT PSEUDOSECTION: ; EDNS: version: 0, flags:; udp: 65494 ;; QUESTION SECTION: ;179.82.35.110.in-addr.arpa. IN PTR ;; ANSWER SECTION: 216.182.27.111.in-addr.arpa. 86400 IN PTR s.aeon.co.id. ;; Query time: 552 msec ;; SERVER: 127.0.0.53#53(127.0.0.53) ;; WHEN: Sun Sep 23 18:21:56 WIB 2018 ;; MSG SIZE rcvd: 81
- “ip4/ip6” is used when you need to specify a certain node with specific IP address to be able to send email on behalf of your domain. As an example saying “ip4:111.222.123.231” o your spf record means a node on the internet with IPv4 address of “111.222.123.231” is allowed to send email from your domain. If the node has an IPv6 address, ip6 is used instead of ip4
- “include” is used if an SPF record from other domain can also be used to identify a sender. Here is the spf record for outlook.com:
"v=spf1 include:spf-a.outlook.com include:spf-b.outlook.com ip4:157.55.9.128/25 include:spf.protection.outlook.com include:spf-a.hotmail.com include:_spf-ssg-b.microsoft.com include:_spf-ssg-c.microsoft.com ~all"When a recipient smtp server receive an email identifying as originated from outlook.com, the recipient must also query the spf records of spf-a.outlook.com and spf-b.outlook.com, spf-a.hotmail.com and the rest the domains listed with the "include" attribute
- “redirect” works in similar fashion to “include” except it ask the recipient smtp server to use the SPF record of the other domain instead of the sender’s domain. Here is the SPF record for gmail:
"v=spf1 redirect=_spf.google.com"
- “all” is used as catch all attributes and mainly used to close an spf record. Asking a recipient smtp server to accept “~all” is like like saying “I can’t identify the sender of this particular email originating from may domain, but please receive it anyway, just in case, you know? You can tag it as “maybe spam, if you like”
Let’s build one
So, let’s build one. First you would want to identify which version of SPF protocol you’ll use. Let’s go with version 1
v=spf1
Let’s tell the recipient email address that all nodes that can receive email on behalf of our domain, or MX servers, can also send email:
v=spf1 mx
Then we tell them, if a sender is not listed on the mx record, it can still send email on behalf or our domain, as long as it pass a reverse DNS query
v=spf1 mx ptr
Next, we would like to identify a particular node on the internet is also able to send an email as well
v=spf1 mx ptr a:sendx.mach5.web.id
Then finally, we ask the recipient to still receive email from other sender, but tag it as SPF failure
v=spf1 mx ptr a:sendx.mach5.web.id ~all
We then can put this as a TXT record on our DNS server