Using Postscreen to filter out connections on your mail server

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.