internetstandards / Internet.nl

Internet standards compliance test suite
https://internet.nl
178 stars 38 forks source link

SPF lookup count might still be wrong after last update #1544

Open WKobes opened 2 weeks ago

WKobes commented 2 weeks ago

Main has PR #1533 which fixed #1529

On main, domain eviny.no still fails on lookup count

SPF of eviny.no is: v=spf1 mx ip4:149.72.90.22/32 include:spf.logicalware.com include:spf.protection.outlook.com include:sendgrid.net include:_spf.anpdm.com include:_spf.atlassian.net include:spf.zoho.eu include:eu.transmail.net -all

Count: 1 (mx) + 7 (includes) = 8

Two SPF's have additional includes:

SPF of sendgrid.net is: v=spf1 ip4:167.89.0.0/17 ip4:208.117.48.0/20 ip4:50.31.32.0/19 ip4:198.37.144.0/20 ip4:198.21.0.0/21 ip4:192.254.112.0/20 ip4:168.245.0.0/17 ip4:149.72.0.0/16 ip4:159.183.0.0/16 include:ab.sendgrid.net ~all

SPF of _spf.atlassian.net is: v=spf1 ip4:167.89.0.0/17 ip4:168.245.0.0/17 include:amazonses.com -all

This brings the count to 10. No further includes are found.

Our test possibily fails because we might count the mx term as 1 + N where N is the number of MX Resource Records. The RFC is quite unclear on this. External sources seems to disagree whether the count of mx term should be 1, N or N+1. Other online tests seem to count as 1 or N.

Discussed with @baknu we need to know the correct count here before release.

bwbroersma commented 2 weeks ago

Ok, I think the RFC is not so clear, I use to think mx should count as '1 MX lookup' + 1 lookup for each MX record, since this is indeed a lookup, at least that is how I read RFC 7208 - § 4.6.4. DNS Lookup Limits.

In the meantime I found a nice :crab:rust tool spftrace (output below) that uses viaspf, which has Issue #3 - Revisit DNS lookup limits with quite some elaborate research on this topic, including links to older RFCs and the IETF mailing list with the author. Most importantly this answer which indeed would say the sub lookups should not be counted in the total (BTW repo moved to codeberg, probably best to fully quote the research instead of linking).

Some nice output for this case, it should indeed be valid according to this count:

$ spftrace eviny.no 0.0.0.0
eviny.no
│   "v=spf1 mx ip4:149.72.90.22/32 include:spf.logicalware.com include:spf.protection.outlook.com
│    include:sendgrid.net include:_spf.anpdm.com include:_spf.atlassian.net include:spf.zoho.eu
│    include:eu.transmail.net -all"
├── mx → eviny.no (lookups: 1/10, nested: 1/10)
│   └── eviny-no.mail.protection.outlook.com
│       ├── 52.101.73.28
│       ├── 52.101.73.1
│       ├── 52.101.73.26
│       └── 52.101.68.21
│   not-match
├── ip4:149.72.90.22/32 not-match
├── include:spf.logicalware.com → spf.logicalware.com (lookups: 2/10)
│   spf.logicalware.com
│   │   "v=spf1 ip4:54.246.247.240/28 ip4:23.251.238.30/31 ip4:23.251.238.32/31
│   │    ip4:54.240.104.177/32 ip4:54.240.104.178/31 ip4:212.89.52.12/32 ip4:212.89.52.11/32
│   │    ip4:212.89.52.46/31 -all"
│   ├── ip4:54.246.247.240/28 not-match
│   ├── ip4:23.251.238.30/31 not-match
│   ├── ip4:23.251.238.32/31 not-match
│   ├── ip4:54.240.104.177/32 not-match
│   ├── ip4:54.240.104.178/31 not-match
│   ├── ip4:212.89.52.12/32 not-match
│   ├── ip4:212.89.52.11/32 not-match
│   ├── ip4:212.89.52.46/31 not-match
│   └── all match result=fail
│   not-match
├── include:spf.protection.outlook.com → spf.protection.outlook.com (lookups: 3/10)
│   spf.protection.outlook.com
│   │   "v=spf1 ip4:40.92.0.0/15 ip4:40.107.0.0/16 ip4:52.100.0.0/15 ip4:52.102.0.0/16
│   │    ip4:52.103.0.0/17 ip4:104.47.0.0/17 ip6:2a01:111:f400::/48 ip6:2a01:111:f403::/49
│   │    ip6:2a01:111:f403:8000::/51 ip6:2a01:111:f403:c000::/51 ip6:2a01:111:f403:f000::/52 -all"
│   ├── ip4:40.92.0.0/15 not-match
│   ├── ip4:40.107.0.0/16 not-match
│   ├── ip4:52.100.0.0/15 not-match
│   ├── ip4:52.102.0.0/16 not-match
│   ├── ip4:52.103.0.0/17 not-match
│   ├── ip4:104.47.0.0/17 not-match
│   ├── ip6:2a01:111:f400::/48 not-match
│   ├── ip6:2a01:111:f403::/49 not-match
│   ├── ip6:2a01:111:f403:8000::/51 not-match
│   ├── ip6:2a01:111:f403:c000::/51 not-match
│   ├── ip6:2a01:111:f403:f000::/52 not-match
│   └── all match result=fail
│   not-match
├── include:sendgrid.net → sendgrid.net (lookups: 4/10)
│   sendgrid.net
│   │   "v=spf1 ip4:167.89.0.0/17 ip4:208.117.48.0/20 ip4:50.31.32.0/19 ip4:198.37.144.0/20
│   │    ip4:198.21.0.0/21 ip4:192.254.112.0/20 ip4:168.245.0.0/17 ip4:149.72.0.0/16
│   │    ip4:159.183.0.0/16 include:ab.sendgrid.net ~all"
│   ├── ip4:167.89.0.0/17 not-match
│   ├── ip4:208.117.48.0/20 not-match
│   ├── ip4:50.31.32.0/19 not-match
│   ├── ip4:198.37.144.0/20 not-match
│   ├── ip4:198.21.0.0/21 not-match
│   ├── ip4:192.254.112.0/20 not-match
│   ├── ip4:168.245.0.0/17 not-match
│   ├── ip4:149.72.0.0/16 not-match
│   ├── ip4:159.183.0.0/16 not-match
│   ├── include:ab.sendgrid.net → ab.sendgrid.net (lookups: 5/10)
│   │   ab.sendgrid.net
│   │   │   "v=spf1 ip4:223.165.113.0/24 ip4:223.165.115.0/24 ip4:223.165.118.0/23
│   │   │    ip4:223.165.120.0/23 ~all"
│   │   ├── ip4:223.165.113.0/24 not-match
│   │   ├── ip4:223.165.115.0/24 not-match
│   │   ├── ip4:223.165.118.0/23 not-match
│   │   ├── ip4:223.165.120.0/23 not-match
│   │   └── all match result=softfail
│   │   not-match
│   └── all match result=softfail
│   not-match
├── include:_spf.anpdm.com → _spf.anpdm.com (lookups: 6/10)
│   _spf.anpdm.com
│   │   "v=spf1 ip4:91.227.208.0/24 ip4:185.64.72.0/24 ip4:185.64.73.0/24 ip4:185.64.74.0/24
│   │    ip4:52.17.51.198 -all"
│   ├── ip4:91.227.208.0/24 not-match
│   ├── ip4:185.64.72.0/24 not-match
│   ├── ip4:185.64.73.0/24 not-match
│   ├── ip4:185.64.74.0/24 not-match
│   ├── ip4:52.17.51.198 not-match
│   └── all match result=fail
│   not-match
├── include:_spf.atlassian.net → _spf.atlassian.net (lookups: 7/10)
│   _spf.atlassian.net
│   │   "v=spf1 ip4:167.89.0.0/17 ip4:168.245.0.0/17 include:amazonses.com -all"
│   ├── ip4:167.89.0.0/17 not-match
│   ├── ip4:168.245.0.0/17 not-match
│   ├── include:amazonses.com → amazonses.com (lookups: 8/10)
│   │   amazonses.com
│   │   │   "v=spf1 ip4:199.255.192.0/22 ip4:199.127.232.0/22 ip4:54.240.0.0/18 ip4:69.169.224.0/20
│   │   │    ip4:23.249.208.0/20 ip4:23.251.224.0/19 ip4:76.223.176.0/20 ip4:54.240.64.0/19
│   │   │    ip4:54.240.96.0/19 ip4:76.223.128.0/19 ip4:216.221.160.0/19 ip4:206.55.144.0/20 -all"
│   │   ├── ip4:199.255.192.0/22 not-match
│   │   ├── ip4:199.127.232.0/22 not-match
│   │   ├── ip4:54.240.0.0/18 not-match
│   │   ├── ip4:69.169.224.0/20 not-match
│   │   ├── ip4:23.249.208.0/20 not-match
│   │   ├── ip4:23.251.224.0/19 not-match
│   │   ├── ip4:76.223.176.0/20 not-match
│   │   ├── ip4:54.240.64.0/19 not-match
│   │   ├── ip4:54.240.96.0/19 not-match
│   │   ├── ip4:76.223.128.0/19 not-match
│   │   ├── ip4:216.221.160.0/19 not-match
│   │   ├── ip4:206.55.144.0/20 not-match
│   │   └── all match result=fail
│   │   not-match
│   └── all match result=fail
│   not-match
├── include:spf.zoho.eu → spf.zoho.eu (lookups: 9/10)
│   spf.zoho.eu
│   │   "v=spf1 ip4:185.20.209.0/24 ip4:31.186.226.0/24 ip4:31.186.243.0/24 ip4:89.36.170.0/24
│   │    ip4:185.20.211.0/24 ip4:185.172.199.0/24 ip4:91.135.68.104/29 ip4:185.230.214.0/23
│   │    ip4:136.143.168.0/22 ~all"
│   ├── ip4:185.20.209.0/24 not-match
│   ├── ip4:31.186.226.0/24 not-match
│   ├── ip4:31.186.243.0/24 not-match
│   ├── ip4:89.36.170.0/24 not-match
│   ├── ip4:185.20.211.0/24 not-match
│   ├── ip4:185.172.199.0/24 not-match
│   ├── ip4:91.135.68.104/29 not-match
│   ├── ip4:185.230.214.0/23 not-match
│   ├── ip4:136.143.168.0/22 not-match
│   └── all match result=softfail
│   not-match
├── include:eu.transmail.net → eu.transmail.net (lookups: 10/10)
│   eu.transmail.net
│   │   "v=spf1 ip4:136.143.168.0/24 ip4:136.143.170.0/24 ip4:185.172.198.0/24 ~all"
│   ├── ip4:136.143.168.0/24 not-match
│   ├── ip4:136.143.170.0/24 not-match
│   ├── ip4:185.172.198.0/24 not-match
│   └── all match result=softfail
│   not-match
└── all match result=fail
fail

Note: a lot of tooling is wrong in this space.

mxsasha commented 1 week ago

I'll dig into this specific one, but long term, perhaps we should replace our code entirely with viaspf. Why kludge it ourselves when they've already done it? A bit of work to integrate with rust code, but not that hard.

mxsasha commented 1 week ago

From careful comparison, I think this is actually an off by one error. We are counting the same way as spftrace (great tool!), we just cut it off once we hit include:amazonses.com. #1548 has a fix.

bwbroersma commented 1 week ago

The tricky thing might be integration with our DNS resolver, etc. But indeed looks like a nice tool. In our case the 'no-match' isn't really a display thing, even all IPs might be truncated. Then again, we might add a warning for large CIDRs, e.g. 0.0.0.0/0 or mx/0: