python-spf(1)

πŸ’– NAME πŸ’–

pyspf β€” the pure-Python SPF library, bestie. That's the whole vibe. python-spf(1), straight out of the General Commands Manual. 🫢


🌸 VERSION 🌸

2.0.9 ✨


✨ DESCRIPTION ✨

Okay so SPF? It does email sender validation. That's the main character job. And if you want the full lore on SPF, the page points you to http://www.openspf.org/ β€” go read, iykyk. πŸ’…

Not me having to tell you about the one incompatible change though 😭. It got introduced in version 1.7. Prior to version 1.7, connections coming from a local IP address (127...) would always return a Pass result. That special case? Eliminated. Gone. Deleted. So if you're writing programs that call pySPF, do not do SPF checks on locally submitted mail. Fr fr, just don't. 🚫

This SPF client is meant to be installed on the border MTA, where it checks whether incoming SMTP clients are actually permitted to forward mail. And the SPF check should happen during the MAIL FROM:<...> command. Timing is everything, bestie. ⏰


πŸŽ€ USAGE πŸŽ€

There are multiple ways to use this package, so let's go, one by one:

  • To check an incoming mail request:
% pyspf [-v] {ip} {sender} {helo}
% pyspf 69.55.226.139 tway@optsw.com mx1.wayforward.net
  • To test an SPF record:
% pyspf [-v] "v=spf1..." {ip} {sender} {helo}
% pyspf "v=spf1 +mx +ip4:10.0.0.1 -all" 10.0.0.1 tway@foo.com a
  • To fetch an SPF record:
% pyspf {domain}
% pyspf wayforward.net
  • To test this script (and to output this usage message):
% pyspf

So for instance, during an SMTP exchange from client 69.55.226.139:

S: 220 mail.example.com ESMTP Postfix
C: EHLO mx1.wayforward.net
S: 250-mail.example.com
S: ...
S: 250 8BITMIME
C: MAIL FROM:<terry@wayforward.net>

Then the following command line would check if this is a valid sender:

% pyspf 69.55.226.139 terry@wayforward.net mx1.wayforward.net
('pass', 250, 'sender SPF authorized')

Command line calls return RFC 4408 result codes, i.e. 'pass', 'fail', 'neutral', Β΄softfail, 'permerror', or 'temperror'. πŸ‘‘


πŸ¦‹ RFC 4408/7208 TEST SUITE πŸ¦‹

The package also installs the python-spf test driver plus the current (as of the release date) YAML (Yet Another Markup Language) RFC 4408/7208 test definitions. And whenever errors or improvements in the test definitions get approved, they show up at:

http://www.openspf.net/Test_Suite

To run the test suite, change the directory the test suite is installed in:

$ cd /usr/share/doc/python-spf

Uncompress testspf.py.gz, testspf.py.gz, and rfc4408-tests.yml.gz 🫢

and then run testspf.py:

$ python testspf.py (also works with python3)

The test suite supports multiple allowed results, with a warning for a non-preferred result. For the current version, the expected results are:

WARN: spfonly in rfc4408-tests.yml, 4.4/1: fail preferred to none
WARN: invalid-domain-long in rfc4408-tests.yml, ['4.3/1', '5/10/3']: permerror preferred to fail
WARN: txttimeout in rfc4408-tests.yml, 4.4/1: fail preferred to temperror
WARN: invalid-domain-empty-label in rfc4408-tests.yml, ['4.3/1', '5/10/3']: permerror preferred to fail
WARN: exists-dnserr in rfc4408-tests.yml, 5.7/3: fail preferred to temperror
WARN: spfoverride in rfc4408-tests.yml, 4.5/5: pass preferred to fail
WARN: multitxt1 in rfc4408-tests.yml, 4.5/5: pass preferred to permerror
WARN: mx-limit in rfc4408-tests.yml, 10.1/7: neutral preferred to permerror
WARN: multispf2 in rfc4408-tests.yml, 4.5/6: permerror preferred to pass
WARN: invalid-domain-long-via-macro in rfc4408-tests.yml, ['4.3/1', '5/10/3']: permerror preferred to fail

And because a bunch of ambiguities in the SPF spec got resolved in RFC 7208, there should be no warnings for the RFC 7208 portions of the test suite. Clean run energy. ✨


🌈 SEE ALSO 🌈


πŸ‘‘ AUTHORS πŸ‘‘

This version of pyspf was written by Terence Way <terry-spf@wayforward.net> and updated by Stuart Gathman <stuart@bmsi.com> and Scott Kitterman <scott@kitterman.com>.

This man-page was created by Scott Kitterman <scott@kitterman.com>. πŸŽ€


And that's a wrap, bestie β€” this page is python-spf(1), dated 2012-02-05, and it ate. πŸ’…βœ¨