In my recent series at The Register, I’ve explained how you can set up a mail server with spam and virus filtering, using OpenBSD, Postfix and Amavisd. The configuration I’ve used is, at heart, one that I’ve run for a very long time myself, based on an original tutorial by Scott Vintinner.
The tutorial is in three parts, starting with a basic description, then configuring a basic mail server, before adding filtering.
However, in the configuration as described, though Greylisting gets rid of a lot of spam, it still leaves SpamAssassin to catch more, and that’s a fairly intensive process. Scott’s original tutorial was written quite a long time ago, before the addition of the Postscreen daemon in PostFix.
Postscreen is intended to do some checks right at the start of a mail transaction, and drop the connection if it looks like spam. That can save a lot of time later, and reduce the load on your system. So, rather than leaving it to SpamAssassin to deduce from the headers that a message has come from a known spam source, for example, Postscreen can check the same blacklists, but before it’s even sent a greeting to the remote end. If it’s spammy, it can bail out then.
This can obviously save a lot of time, so I’ve been looking at how to integrate it into the configuration I described. There’s a complete how-to on the Postfix site, but if you’ve followed the config options in my series of articles, you’ll need some tweaks, so I’ll describe that here.
Postscreen options
Postscreen has a number of checks that it can do, some of which can effectively result in the same sort of greylisting of incoming connections as the Postgrey system I’ve described in my setup. So you might think you can just replace one with the other.
However, I’m not going to do that, and I’m going to use only the checks that Postscreen applies on initial connection. That’s because that way that Postscreen is designed allows it to pass a connection on to the real mail server only at that stage.
If you ask it to check options within the mail transaction itself (called “deep protocol tests” in the readme), even if a system passes, it drops the connection, because it can no longer pass it on to the mail server. Then, the next time the system connects, it is passed directly to the real mail server. It’s similar to, but not quite the same as greylisting.
I’ve been testing a configuration for a few weeks now in which Postscreen checks the initial connection against some spam blacklists. If that’s passed, the connection is passed on to the usual smtp daemon, which may still reject it using Postgrey.
Effectively then, these tweaks simply put Postscreen in front of Postgrey, and use it to drop connections based on spam blacklists. I’ve found so far that it has drastically reduced the amount of junk I get in my quarantine folder – but I don’t feel I’ve used this config long enough to 100% recommend it to people. You may, for example, want to tweak the blacklists used, or the way they’re scored.
Setting Postscreen options
There are a lot of options for Postscreen, which the how-to I linked above explains. Fortunately all the ‘deep protocol tests’ are turned off by default, so all we need to add in the Postfix main.cf configuration file is a few lines that enforce black hole tests using DNS Block Lists. In this config I’ve used SpamHaus alongside SpamCop and SORBS, with more weight assigned to SpamHaus. Note that some DNSBL services require you to register, and some also charge, or have limits to the number of queries per day. Always check the conditions before using a service.
It’s also worth stressing that, to start with, you should probably change the ‘enforce’ options to ‘ignore’ and look in your mail logs to see what messages would be rejected by PostScreen, and perhaps try different combinations of DNSBLs and weightings. That warning out of the way, here’s what to add to your main.cf:
# post screen postscreen_access_list = permit_mynetworks postscreen_greet_banner = NW networks - please wait postscreen_dnsbl_threshold = 2 postscreen_dnsbl_sites = zen.spamhaus.org*2 dnbsbl.sorbs.net bl.spamcop.net postscreen_dnsbl_action = enforce postscreen_greet_action = enforce
The next change is to make some alterations to master.cf, which determines which services run. Find the line relating to the main smtp service (which will start ‘smtp’) and comment them out, including the lines that have the options for it.
If you have started with a recent version of Postfix, there should be a line for Postscreen that’s commented out. Uncomment it so that it reads
smtp inet n - y - 1 postscreen
Do similarly with the lines for tlsproxy and dnsblog:
dnsblog unix - - y - 0 dnsblog tlsproxy unix - - y - 0 tlsproxy
Now we diverge slightly from the official tutorial, which tells you to simply uncomment the smtpd pass service, which is what Postscreen passes connections to when it’s happy with them. We want that to continue using our Amavis filtering setup, which is on port 10024. (PostGrey is controlled by the check_policy_service setting in master.cf). So, this is what we need
smtpd pass - - y - - smtpd -o smtpd_proxy_filter=127.0.0.1:10024 -o smtpd_client_connection_count_limit=7
After making these changes, you can use the postfix reload command. Check /var/log/maillog for messages from postscreen, which will let you see what it’s deciding. I recommend doing that for a few days at least, with
postscreen_dnsbl_action = ignore
before changing it to ‘enforce’, or tweaking the weights of the different blacklists. You’ll see entries in /var/log/maillog with the text ‘DNSBL’ when mail matches, which can help you see how it’s behaving.
The problem with the setup you describe is that you do blacklist checks before greylisting. I’ve always taken the attitude that the server should do the cheapest checks first – greylisting being one of those. Blacklist checks come further down the list – partly because they need DNS lookups and that makes them more intensive in resource usage, and also because it reduces the load on the blacklist servers (if someone is giving me a free ride, the least I can do is minimise the load I put on the service).
There are a lot of checks you can do in Postfix to enforce protocol adherence. These work because spam software is designed to take shortcuts to get the spam out as fast as possible. Where I used to work, we cut the spam down massively just by these protocol enforcements (older version of Postfix).
Some things are a matter of balance. Enforcing that the HELO name be a valid DQDN does hit a lot of false positives – so sometimes I have to comment it out and ask someone to resend their message (as well as telling them to get their IT bods to fix it) when I spot from the logs that something I’m waiting for has bounced. Favourite one for that is Exchange servers leaking their invalid “.local” address.
From my own home server, though it needs tidying up a bit as most bits (client restrictions, helo restrictions, sender restrictions) can be rolled up into recipient restrictions :
## Requirements for the connecting server
smtpd_client_restrictions =
permit_mynetworks,
check_client_access cidr:/etc/postfix/blacklist,
permit
# Requirements for the HELO statement
smtpd_helo_restrictions =
check_client_access cidr:/etc/postfix/host_access,
permit_mynetworks,
check_helo_access hash:/etc/postfix/helo_access,
permit
# Requirements for the sender details
smtpd_sender_restrictions =
permit_mynetworks,
# hash:/etc/postfix/access,
check_client_access cidr:/etc/postfix/host_access,
permit_sasl_authenticated,
reject_non_fqdn_sender,
# reject_unknown_sender_domain,
reject_unauth_pipelining,
permit
# Requirement for the recipient address
smtpd_recipient_restrictions =
permit_mynetworks,
reject_unauth_pipelining,
check_client_access cidr:/etc/postfix/host_access,
check_policy_service inet:localhost:10031,
permit_sasl_authenticated,
reject_non_fqdn_recipient,
reject_unknown_recipient_domain,
reject_unlisted_recipient,
reject_unauth_destination,
# reject_non_fqdn_hostname,
# reject_unknown_helo_hostname,
# reject_invalid_helo_hostname,
reject_rbl_client zen.spamhaus.org,
permit
smtpd_end_of_data_restrictions =
check_policy_service inet:localhost:10031,
permit
## require proper helo at connections
smtpd_helo_required = yes
## waste spammers time before rejecting them, also moves client restriction evaluation to RCPT phase
smtpd_delay_reject = yes
disable_vrfy_command = yes