How To Implement SPF In Postfix
How To Implement SPF In Postfix
This tutorial shows how to implement SPF (Sender Policy Framework) in a Postfix 2.x installation. The Sender Policy Framework is an open standard specifying a technical method to prevent sender address forgery (see http://www.openspf.org/Introduction). There are lots of SPF extensions and patches available for Postfix, but most require that you recompile Postfix. Therefore we will install the postfix-policyd-spf-perl package from openspf.org which is a Perl package and can be implemented in existing Postfix installations (no Postfix compilation required).
I want to say first that this is not the only way of setting up such a system. There are many ways of achieving this goal but this is the way I take. I do not issue any guarantee that this will work for you!
1 Preliminary Note
I assume that you have already set up a working Postfix mail server.
The following procedure is distribution-independent, i.e., it should work on any Linux distribution (however, I tested this on Debian Etch).
2 Install Required Perl Modules
The postfix-policyd-spf-perl package depends on the Mail::SPF and the NetAddr::IP Perl modules. Therefore we are going to install them now using the Perl shell. Start the Perl shell like this:
perl -MCPAN -e shell
If you run the Perl shell for the first time, you will be asked a few questions. You can accept all default values. You will also be asked about the CPAN repositories to use. Select repositories that are close to you.
After the initial Perl shell configuration, we can start to install the needed modules. To install Mail::SPF, simply run
In my case, it tried to install Module::Build (which is a dependency), but then it failed. If this happens to you, simply quit the Perl shell by typing
Then start the Perl shell again:
perl -MCPAN -e shell
and try to install Mail::SPF again:
This time it should succeed, and you should see that it also installs the modules Net::DNS::Resolver::Programmable and NetAddr::IP on which Mail::SPF depends.
A successful installation of Mail:SPF should end like this:
Because NetAddr::IP has already been installed, we can now leave the Perl shell:
3 Install postfix-policyd-spf-perl
Next we download postfix-policyd-spf-perl from http://www.openspf.org/Software to the /usr/src/ directory and install it to the /usr/lib/postfix/ directory like this:
Then we edit /etc/postfix/master.cf and add the following stanza at the end:
(The leading spaces before user=nobody are important so that Postfix knows that this line belongs to the previous one!)
Then open /etc/postfix/main.cf and search for the smtpd_recipient_restrictions directive. You should have reject_unauth_destination in that directive, and right after reject_unauth_destination you add check_policy_service unix:private/policy like this:
or like this:
It is important that you specify check_policy_service AFTER reject_unauth_destination or else your system can become an open relay!
Then restart Postfix:
That's it already. You should check the README file that comes with the postfix-policyd-spf-perl package, it contains some important details about how postfix-policyd-spf-perl processes emails, e.g. like this part from the postfix-policyd-spf-perl-2.0001 README:
This version of the policy server always checks HELO before Mail From (older
If the message is not rejected or deferred, the policy server will PREPEND the
The policy server skips SPF checks for connections from the localhost (127.) and
4 Test policyd-spf-perl
We can test policyd-spf-perl by running
The cursor will then wait on the policyd-spf-perl shell. We can now act as if we tried to send an email from a certain domain and a certain server to another email address. policyd-spf-perl will then check if that certain server is allowed to send emails for the sender domain and show us the result.
So let's see what happens if we try to send a mail from info@h****forge.com from the server h****.server*********.net (IP address 81.169.1**.**). The h****forge.com has an SPF record that allows 81.169.1**.** to send emails from h****forge.com.
So on the policyd-spf-perl shell we type:
The output should look like this:
action=PREPEND Received-SPF: pass (h****forge.com: 81.169.1**.** is authorized to use 'info@h****forge.com' in 'mfrom' identity (mechanism 'ip4:81.169.1**.**' matched)) receiver=server1.example.com; identity=mfrom; envelope-from="info@h****forge.com"; helo=h****forge.com; client-ip=81.169.1**.**
which means we passed the test.
Let's run another test, this time we will send from the client 18.104.22.168 (www.example.com) which is not allowed to send emails from h****forge.com:
This is the output, the test failed as expected:
action=PREPEND Received-SPF: softfail (h****forge.com: Sender is not authorized by default to use 'info@h****forge.com' in 'mfrom' identity, however domain is not currently prepared for false failures (mechanism '~all' matched)) receiver=server1.example.com; identity=mfrom; envelope-from="info@h****forge.com"; helo=h****forge.com; client-ip=22.214.171.124
We can now even try to leave the sender field empty, as many spammers do. Still, policyd-spf-perl should be able to complete its tests:
This is the output, we are still allowed to send from h****forge.com:
action=PREPEND Received-SPF: pass (h****forge.com: 81.169.1**.** is authorized to use 'h****forge.com' in 'helo' identity (mechanism 'ip4:81.169.1**.**' matched)) receiver=server1.example.com; identity=helo; helo=h****forge.com; client-ip=81.169.1**.**
Let's try the same test with an invalid client:
As expected, this is the output:
action=PREPEND Received-SPF: softfail (h****forge.com: Sender is not authorized by default to use 'h****forge.com' in 'helo' identity, however domain is not currently prepared for false failures (mechanism '~all' matched)) receiver=server1.example.com; identity=helo; helo=h****forge.com; client-ip=126.96.36.199
To leave the policyd-spf-perl shell, type