Like many others, I have navigated the SPF/DKIM/DMARC world with some confusion.
About 4 weeks ago or so I finished setting everything (SPF/DKIM/DMARC) up correctly for a GoDaddy-hosted domain that uses Google's mailservers.
I set the _dmarc TXT record to take zero action with p=none and I used Postmark to monitor the results to see what was passing and failing over a week.
After a week or so I looked at the Postmark results and inserted the include: statements for the domains that I wanted to pass, but weren't. Then I waited another week to see the results. However, the results showed that the domains still weren't passing SPF or DKIM. Below is the SPF record, I've redacted parts of it that are revealing, but two of the domains are legit and still aren't passing.
v=spf1 include:_spf.google.com include:freshemail.io include:cherryroad.com ~all
Do I need to use the actual IP addresses in the include statements instead of the domains? Postmark lists these as well so that would be easy if so.
No, you shouldn't copy their IPs in there because they are subject to change, especially Google's.
If it's failing, presumably you have some results (usually in message headers) that tell you exactly which IP is failing, and you can track it down manually though those includes, do a reverse lookup on it, etc.
However, you're also using GoDaddy, which is mostly guaranteed not to work as they either block outbound SMTP or route it through their own servers, so you're very unlikely to get an SPF pass.
The issue was with SPF DNS lookup limits. I had no idea this was a thing and I'm amazed that this isn't mentioned anywhere on the documentation (whether that's Google's official documentation or otherwise) on setting up SPF/DKIM/DMARC, and didn't come up in Googling of this issue. This limit is designed to prevent denial of service attacks and infinite DNS loops.
For anyone else who sees this post
v=spf1 include:_spf.google.com include:freshemail.io include:cherryroad.com ~all
This SPF record actually has almost 15 DNS lookups, and the limit is 10 per domain. You can find out how many SPF DNS lookups your domain has with a service like AutoSPF or Easy DMARC
The solution, once you see your total DNS lookups, comes in four options:
Create subdomains and use those to diversify the records. For example using "email#business.mydomain.com" as the email for freshemail.io. Then on the SPF record for that subdomain, you would only have v=spf1 include:freshemail.io resulting in less than 10 DNS lookups for that domain.
As #Synchro mentioned, you don't want to use IPs because those can very well change, but the concept of using IPs instead of the domain names does essentially work because an IP address doesn't cost a DNS lookup. Check with the support/engineering of whatever service you're using, it's possible that they have an IP (or an IP range) that doesn't change often. You might be able to bring your DNS lookups under ten using this.
Note that Google takes up about 3 DNS Lookups, and you'll probably want to leave that one as the _spf.google.com value
Note that every SPF record also has a 255 character limit, so if you're using only IPs you'll need to break that up into a lot of SPF records probably
Use an SPF flattening or compressing service like AutoSPF. Essentially, these services employ method #2, but do some backend work every few hours to check and update the IP addresses associated with the domains. Then they provide you with a "compressed" record like v=spf1 include:_6359384.autospf.com ~all that references all of your records and results in far fewer DNS lookups.
Create your own method that acts kind of like #2 and #3, using GoDaddy's API and brew up something that performs updated lookups on a schedule/job and updates separate SPF records including all of the IPs.
Related
Using G-suite email and DNS configuration for MX records in Route53, I'm blocked on how I can solve this error:
Multiple SPF records may cause delivery and spam classification
issues. v=spf1 include:_spf.google.com ~all v=spf1
include:transmail.net ~all
Route53 only allow a single TXT record for SPF information. Route53 does allow you to use new lines for additional SFP information.
However, when running the G-Suite check, I get the error quoted above and some clients are seeing our emails as SPAM.
Is there a solution to this?
You should have one SPF record for your domain, but you can have multiple include directives in the SPF record. You might want to try something like this:
v=spf1 include:_spf.google.com include:transmail.net ~all
The only way I was able to fix this was to proxy the DNS records in Cloudflare which allows an SPF record per line.
A domain MUST NOT have multiple SPF records, SPF fails with PermError otherwise.
An SPF record is a TXT record in the DNS starting exactly with "v=spf1", followed by an array of mechanisms and/or modifiers.
An SPF check starts by fetching all TXT records starting exactly with "v=spf1" on a domain:
if no such record is found, it returns None;
if multiple such records are found, it returns PermError.
If you have multiple services to add to SPF, you would need to combine them like mti2935.
Learn more here: https://dmarcly.com/blog/can-i-have-multiple-spf-records-on-my-domain
We have noticed that a lot of our emails are falsely flagged as spam. Upon reading online, it seems like a good way to solve this issue is to add an SPF record into the DNS, so we added a TXT record with this content:
v=spf1 a mx ip4:162.123.189.010 include:_spf.google.com include:bluehost.com ~all
Bluehost is our host provider,
162.123.189.010 is our VPS IP address from blue host,
and _spf.google.com is needed because we send/receive email using GMail.
After running a test on Google's MX tester, we got the following error:
The SPF string can not be parsed, do you have any typos in it?
Decision permanent error in processing
Explanation SPF Permanent Error: Too many DNS lookups
Record v=spf1 a mx ip4:162.123.189.010 include:_spf.google.com include:bluehost.com ~all
Does anyone have any idea what the issue is?
"SPF Permanent Error: Too many DNS lookups" is a very specific problem. Your record is too big and SPF checkers will refuse to perform enough DNS queries to determine if something passed SPF.
The SPF spec allows at most 10 DNS lookups. Your SPF record has 17.
RFC 4408 § 10.1 – Processing Limits states:
SPF implementations MUST limit the number of mechanisms and modifiers
that do DNS lookups to at most 10 per SPF check, including any
lookups caused by the use of the "include" mechanism or the
"redirect" modifier. If this number is exceeded during a check, a
PermError MUST be returned. The "include", "a", "mx", "ptr", and
"exists" mechanisms as well as the "redirect" modifier do count
against this limit. The "all", "ip4", and "ip6" mechanisms do not
require DNS lookups and therefore do not count against this limit.
The "exp" modifier does not count against this limit because the DNS
lookup to fetch the explanation string occurs after the SPF record
has been evaluated.
Your SPF record has four lookups before traversing the inclusions, including your a and mx:
v=spf1 a mx ip4:162.123.189.010 include:_spf.google.com include:bluehost.com ~all
Google's SPF
Google has three DNS lookups for three collections of CIDRs that it blesses:
_spf.google.com (+3 lookups)
v=spf1 include:_netblocks.google.com include:_netblocks2.google.com
include:_netblocks3.google.com ~all
_netblocks.google.com
v=spf1 ip4:35.190.247.0/24 ip4:64.233.160.0/19
ip4:66.102.0.0/20 ip4:66.249.80.0/20 ip4:72.14.192.0/18 ip4:74.125.0.0/16
ip4:108.177.8.0/21 ip4:173.194.0.0/16 ip4:209.85.128.0/17
ip4:216.58.192.0/19 ip4:216.239.32.0/19 ~all
_netblocks2.google.com
v=spf1 ip6:2001:4860:4000::/36
ip6:2404:6800:4000::/36 ip6:2607:f8b0:4000::/36 ip6:2800:3f0:4000::/36
ip6:2a00:1450:4000::/36 ip6:2c0f:fb50:4000::/36 ~all
_netblocks3.google.com
v=spf1 ip4:172.217.0.0/19 ip4:172.217.32.0/20
ip4:172.217.128.0/19 ip4:172.217.160.0/20 ip4:172.217.192.0/19
ip4:108.177.96.0/19 ip4:35.191.0.0/16 ip4:130.211.0.0/22 ~all"
Bluehost's SPF
The SPF record for bluehost.com is too large (its SPF record fails Google's MX tester on its own):
bluehost.com (5 lookups before further traversal)
v=spf1 include:spf2.bluehost.com include:_spf.qualtrics.com
include:_spf.google.com include:_spf.salesforce.com
include:sparkpostmail.com -all
spf2.bluehost.com (+0)
v=spf1 ip4:66.147.240.0/20 ip4:69.89.16.0/20 ip4:74.220.192.0/19
ip4:67.222.32.0/19 ip4:70.40.192.0/19 ip4:67.20.64.0/18 ip4:173.254.0.0/17
ip4:50.87.0.0/16 ip4:69.195.64.0/18 -all
_spf.qualtrics.com (+0)
v=spf1 ip4:139.60.152.0/22 ip4:162.247.216.0/22 ip4:54.186.193.102/32
ip4:52.222.73.120/32 ip4:52.222.73.83/32 ip4:52.222.62.51/32
ip4:52.222.75.85/32 ?all
(see above for _spf.google.com's +3 lookups, though redundant lookups don't add to your total)
_spf.salesforce.com (+1 using an SPF macro with the IP address)
v=spf1 exists:%{i}._spf.mta.salesforce.com -all
sparkpostmail.com is a redirection and then another exists macro and a pile of pointers (+6, wow)
v=spf1 redirect=_spf.sparkpostmail.com
v=spf1 exists:%{i}._spf.sparkpostmail.com include:_netblocks.sparkpostmail.com
ptr:sparkpostmail.com ptr:spmta.com ptr:flyingenvelope.com ~all
Danger! That sparkpost.com inclusion pulls in some ptr entries, which are arguably insecure, using a deprecated and "strongly discouraged" SPF mechanism (that's a direct quote from the spec).
_netblocks.sparkpostmail.com was pulled in by the previous record (+0)
v=spf1 ip4:147.253.208.0/20 ip4:192.174.80.0/20 ~all
Bluehost used to use SendGrid, who actually knows what they're doing (their SPF record has no additional lookups), but apparently they have traded SendGrid for SparkPost, who (based on their six extra lookups plus the insecure ptr entries) does not.
Since that totals 12 (13 with include:bluehost.com), you cannot include Bluehhost's SPF.
Bluehost's own suggested SPF record (and its default for all customers) is similarly broken (with 16 lookups, including an easily forged ptr).
Solution for Bluehost: a trimmed and safer SPF record
📢 Hello, Bluehost. I tweeted this at you. This section is just for you.
Bluehost could fix this with the following SPF records in place of their current one:
bluehost.com (7)
v=spf1 a include:spf2.bluehost.com include:_spf.google.com
include:_netblocks.sparkpostmail.com ~all
Though note that I had to downgrade include:sparkpostmail.com (7 + 6 = 13, too large, plus that includes the dangerous ptr records) to just its netblocks (7 + 0 ≤ 10). Bluehost needs to yell at SparkPost or go back to SendGrid. spf2.bluehost.com is unchanged from its current state and should be the only inclusion necessary for Bluehost customers.
(I'd use the IP for the A record to skip a lookup, but it changes so often that it looks like fast flux.)
Bluehost should suggest customers include just spf2.bluehost.com for all of the Bluehost services (assuming they're involved in sending mail). See the next section for how to advise Bluehost customers.
Solution for Bluehost customers
As noted in the previous section, start with this base (3 lookups):
v=spf1 a mx include:spf2.bluehost.com ~all
The final ~all ("soft failure") indicates mail recipients should be mildly dubious of —yet still deliver— mail that fails SPF. Set up DMARC to figure out what works and what is missed on the road to DMARC p=reject (which will block all forged mail).
You'll have to add any hosted email or Email Service Provider(s) you use, plus any other hosts that you want to authorize to send mail on behalf of your domain.
In the case of this question, I see an explicit IP address and hosted mail by Google, so you'll need:
v=spf1 ip4:162.123.189.10 a mx include:spf2.bluehost.com
include:_spf.google.com ~all
Your total DNS lookup count is now seven and therefore your SPF is valid.
"SPF Permanent Error: Too many DNS lookups" is a common type of SPF permanent error. This happens when you have more than 10 DNS lookups in your SPF record.
SPF imposes the 10-DNS-lookup limit to mitigate DDoS attacks.
You can use any online SPF checker to check your SPF record and make sure it doesn't exceed that limit.
However, if your SPF record does exceed the limit, SPF authentication returns the permanent error mentioned above, which is in turn interpreted (in DMARC or otherwise) as fail. This means that the email can fail authentication and be moved to the spam folder. If no further action is taken, this will have a negative impact on your email deliverability.
To fix the too-many-DNS-lookup issue, you can use a service like DMARCLY's Safe SPF
feature to automatically "flatten" your SPF record, so that it never exceeds the limit.
For more information on this, check out this post: Why SPF Authentication Fails
The most obvious problem is that leading 0 in your IP address, which makes it invalid. A minor issue is that it's considered best practice to put literal IPs first, as they are faster for receivers to evaluate. Give this a try:
v=spf1 ip4:162.123.189.10 a mx include:_spf.google.com include:bluehost.com ~all
Rather than using google's checker, I'd recommend Scott Kitterman's site, which is more likely to be accurate (Scott is one of the authors of the SPF spec), and spotted this exact problem.
My DNS provider works perfectly for A records.
I am having great difficulty understanding the syntax of SPF records. I have no prior experience.
The DNS provider supports SPF records and it has two control boxes for information: 'Name' and 'SPF data'.
The A record which functions fine looks like this:
Name: potsandpins.info
IPV4 Address: 45.61.228.207
The SPF record which is giving me no joy looks like this:
Name: potsandpins.info
SPF Data: "v=spf1 a -all" (including the quotation marks)
My emails are received with a red flag in Gmail which says 'Gmail couldn't verify that potsandpins.info actually sent this message'.
Can anyone suggest anything as I've tried all sensible permutations?
You don't seem to currently have an SPF record for potsandpins.info maybe you deleted it because you ran into trouble. Anyway, think of the SPF as a whitelist of any IP addresses or hosts you've given permission to send email on your behalf.
The name would be either the root domain, sometimes designated by the #, or a hostname, foo, which you'd use if you were sending email out as example#foo.example.com.
The SPF data would be the version number (v=spf1), then mechanisms (e.g., a), and then the ip addresses or hosts you'd like to authorize, then the qualifier such as -ALL, which intends a hard fail. You may want to back off from that using ~ALL for now, which intends a softfail. I think it's better to be specific in SPF records as then they're easier to follow exactly what they're authorizing.
Here's an example SPF record. Let's say you wanted to authorize 192.0.2.10 and Google.
v=spf1 ip4:192.0.2.10 include:_spf.google.com ~all
Let's say you wanted to authorize a range of IP addresses and MailChimp:
v=spf1 ip4:192.0.2.0/24 include:servers.mcsv.net ~all
Here's a good article on common mistakes in SPF records.
Then it's important to validate your SPF record using a tool such as the SPF Survey. I like this tool because it gives more detailed, actionable error messages when there's a problem.
if you post the full headers of an example email and indicate any other services you use to send email, then it would be possible to provide more specific advice. For future reference, it's best to provide more details when you post to Stack Overflow as that makes it easier to help. I tried in this post but the information you provided limited how specific the answer could be.
Also, for future reference, it's best to post using example.com rather than a real domain name and use IP addresses from an IPv4 block reserved for documentation.
The blocks 192.0.2.0/24 (TEST-NET-1), 198.51.100.0/24 (TEST-NET-2),
and 203.0.113.0/24 (TEST-NET-3) are provided for use in documentation.
Anyway, I hope this helps.
I have read through the documentation but the difference is still not very clear to me.
My understanding is:
1) include: will allow the IPs listed for the specified domain, and also any additional domains listed in that domain's own SPF records
2) a: simply allows IPs listed for the specified domain
Is that correct?
Another try at an answer for anyone else trying to put this together, and based on the syntax doc that OP is probably referring to, the open-spf.org "SPF Record Syntax" page.
Note: Previously this document lived at http://www.open-spf.org/SPF_Record_Syntax/, but that domain seems to have gone offline permanently in 2009. More details on the history and missing domain can be found here.
My answer is that yes, you seem to have it in mind.
a: Test the IP for a match in the A records for the domain.
include: Fetch the entire SPF record for the domain, evaluate IP against it, and if a PASS is found then that PASS becomes the result of the whole SPF test. If no PASS is found then it's not a fail, but your original/top-level SPF test continues (probably to the -all/~all/?all phase).
Reasons to use "a":
Because it's more predictable and straightforward.
Because you haven't set up SPF on the relevant domains.
Because you don't control those domains and the SPF isn't what you'd want (specifically if it's too lenient in accepting other servers that aren't in it's A records)
Reasons to use "include":
Because you already trust the SPF of the domain.
Because the SPF of the domain is complex, and you want to have a single source of truth for don't-repeat-yourself reasons.
Happy to be corrected!
1) include:other-domain.com just includes SPF records from other-domain.com.
If SPF entries of other-domain.com allows some ip's (for example have an ip4: or a: entry), then those ip will be also be allowed.
2) true
THE SITUATION:
I have ONE e-mail account per domain.
I use e-mails such as [some-alias]#[one-of-my-domains-name]. (server: mail.[mydomain]:[secure port]
My registrar (OVH) is different from my web host (Arvixe).
My hosting plan is a mutualised .NET hosting.
When I want to reply with one of my aliases, I use Mozilla Thunderbird 'Identities'. (Login = concrete domain mail account, FROM: 'the alias e-mail'.)
(And yes, this is very efficient to avoid getting spams and unwanted mailing lists.)
THE PROBLEM:
For some recipient using some spam protection services, I constantly get the error:
Remote server replied: 550 Blocked by SPF ()
HINTS/QUESTIONS AND IDEAS IN SEARCH FOR A SOLUTION
a friend said I have to configure the TXT spf record of my domain.
using different webmaster tools sites to get DIG info, I never get infos about the 'TXT' record. So I'm not sure: Should I edit this record on the side of my domain registrar or in the side of the hosting ?
Current on my registrar's side the record reads:
v=spf1 a:mail.[mydomain] include:mx.ovh.com ~all
and on my hosting's side it reads:
v=spf1 a:mail.[mydomain] ~all
THE BIG QUESTION:
How can I solve this ?
Thank you for your help
Your SPF record is a statement that the IP(s) sending the email are authorised to send email for your domain. In your case I am assuming you're actually sending through the Arvixe servers. The records should be set in the TXT record for your domain at the registrar (ovh).
So in this case you need at your registrar (ovh) to edit the TXT record for SPF to read:
v=spf1 mx a:mail.[yourdomain] include:spf.arvixe.com -all
Note: The modifiers on 'all' vary - +,-,~,? - and specify whether recipients should consider the tests conclusive and reject mail or not. There's a great (very thorough) howto on SPF here: http://www.zytrax.com/books/dns/ch9/spf.html
What is a bit unusual is that the recipients are rejecting what should be an inconclusive SPF record (e.g. default = accept). One of the core problems with email is that there will always be edge cases where recipient servers exhibit odd behaviour - I hope this helps in this case.
Solution found:
- I actually had to set the SPF or TXT (I set both at the same value) record on the hosting's side.
- I used check-auth#verifier.port25.com , which a mail service to checking if your e-mails are passing some anti-spam filter.
- The reply given by check-auth#verifier.port25.com made me understand the Source IP/HELO hostname were not mail.[mydomain] but [myhostingserversubdomain].arvixe.com here what my SPF and TXP spf records look like:
v=spf1 mx ptr:[thesubdomain].arvixe.com -all
(I think 'mx' is really not necessary in my case.)
EDIT : Slight improvement:
v=spf1 ip4:[[thesubdomain].arvixe.com IP] ptr:[thesubdomain].arvixe.com -all
mx removed since in my case the mx servers have nothing to do with it.
"ptr" requires DNS lookup, so I added the direct IP. I left "ptr" in case the IP changes (since it's managed by my host).