Using Postfix With SpamAssassin

Although there are many good articles on using postfix, everyone has different needs. I set it up on home machines or workstations with no problem. I set it up on one corporate mail server, again with no problems.

However, when I set it up on another corporate server, as a backup, it didn't work properly. This was a situation where there was no great need, and it was considered to be a project done in my spare time. So, I began setting it up from scratch and decided, this time, to document what I did.

The server is a backup mail server, which does some internal things. As spammers have a nasty habit of sending their wares to backup servers (those with a higher MX number) it was necessary to put spamassassin in as well.

The server is running FreeBSD 6.x. So, I installed postfix from ports. During installation I chose to let postfix replace sendmail in /etc/mail/mailer.conf. I followed the instructions, (seen by running pkg_info -Dx postfix | more) to have it run at startup.

To start, I sent mail to a test user at the localhost. We'll call the domain the overused example.com. The domain's first MX record, that with a value of 10, was email.example.com, this backup box mail.example1.com. The example.com domain has several sub domains, example.com, example1.com, example2.com, example3.com, etc.

As mail sent from the outside to user@example.com or user@example1.com would go to the primary mail server, I tested by sending it to user@mail.example1.com.

Test one was leaving the default configuration alone. In each case, I would also test internally first, but that had never been a problem.

As you probably know, in general, one makes their changes to main.cf. The advice in the file's comments say to change one or two parameters at a time and test it. This is what I hadn't done last time, so this time, I went very slowly.

I sent mail from the outside to user@mail.example1.com. This worked. I then changed the main.cf $mydestination parameter from the default of locahost to localhost and $mydomain. This also worked. Now I was getting somewhere.

Although the comments in main.cf make the above fairly self-explanatory, for the beginner let me point out that I simply did this by uncommenting the line in main.cf that reads

mydestination = $myhostname, localhost.$mydomain, localhost, $mydomain

I left the continued line below it commented.

When you have long lines that have to be continued, this is done by inserting a space or a tab at the beginning of the second line. Therefore, if I had wanted to include the second line of the mydestination parameter, I would have done

mydestination = $myhostname, localhost.$mydomain, localhost, $mydomain,
        mail.$mydomain, www$mydomain, ftp.$mydomain

I then added example.com's various subnets to the $mynetworks parameter. As there are only a few subnets, I followed the suggestion in the comments and simply added them by hand.

mynetworks = 192.168.1.0/24, 192.168.2.0/24,
        192.168.3.0/24, 127.0.0.0/8

(Again, note the white space on the second line.)

In this case, authentication is done through NIS--as long as the user has an account on the NIS server, they can access their mail. I have the mail put in /var/mail, the FreeBSD default, and run qpopper as the pop3 server. I was able to install qpopper without changing any of the default settings. Rather than run it from inetd, I simply created a /usr/local/etc/rc.d/qpopper.sh script that reads

    
#!/bin/sh


case "$1" in

start) /usr/local/libexec/qpopper
;;

stop) killall qpopper 
;;

*) echo "Usage: $(basename $0) (start|stop)" >&2 
;;

esac

The test user was able to retrieve mail with this method, so that wasn't a problem.

Next, I enabled soft bounces in main.cf. I simply changed the line that reads no for soft bounces to yes. Suddenly, I began having a problem. Mail sent from the outside was no longer reaching the machine. I changed it back. In this case, I simply commented it out, and did

postfix reload

To make sure the change had taken place. Then I typed

postconf | grep soft

The postconf command shows your configuration. With no arguments it will print all parameters. With the -n flag it will show you the parameters that you have changed from default settings. With soft bounce disabled, I could receive messages from the outside--with it enabled, I couldn't. The soft_bounce parameter should simply, if a message can't be delivered, mean that the message will be tried again later, so I'm not sure why enabling it kept messages from being delivered. With it at the default of disabled, messages didn't bounce, they were delivered as they should have been.

Next I began adding to the mydestination line. I uncommented the second line which (as you'll remember, it begins with white space) added in www.example.com, ftp.example.com and a few others. I also added in one of our domains. Remember our domain is example.com but until now, the only domain in mydestination was example1.com, as it was the $mydomain part of the machine's host name. I added example2.com.

Once again, I sent a test email, everything was still working.

There is a note in main.cf to only change a few parameters at a time. This is excellent advice, because otherwise, you won't be sure where the problem began. My next step was to move mydestination and mynetworks to separate text files. Although we don't have that many destinations or networks, it still seems to make things a bit simpler. So, I changed the mydestinations line in main.cf to

mydestination = $myhostname, locahost.$mydomain, localhost, $mydomain,
        mail.$mydomain, www.$mydomain, ftp.$mydomain,   
        /usr/local/etc/postfix/destination.txt

Somewhere in the docs, it points out that such a file should consist of what would normally go on right of the = sign. Note the white space, just as I would have had it had I left it in main.cf. I've found that if I put in the variable names, however, such as $mydomain, localhost.$mydomain and the like, it doesn't work and suddenly everything gets relay access denied. The mydestination.txt file simply read


example.com, example1.com, example2.com, example3.com, 
        example4.com

I tested this and then I did the same with my networks, changing the line in main.cf to read

mynetworks = /usr/local/etc/postfix/mynetworks.txt

Again, the mynetworks.txt consisted of what I would put on the right side of the = sign. In that case, there were no variables such as $mydomain so all of the networks go in mynetworks.txt
192.168.1.0/24, 192.168.2.0/24,
        192.168.3.0/24, 127.0.0.0/8

Now I had postfix working, being able to send and receive mail to and from the outside. The next step was to use it with spamassassin.
I prefer maildrop to procmail. However, as there were no home directories, getting maildrop to work turned out be, if not non-trivial, requiring more googling time than I wished to spend. Maildrop is picky about ownership, and when I tried to use it, as all mboxes are in /var/mail, it complained that it couldn't open the mailbox.
Postfix can work directly with spamassassin, however, I preferred having something like procmail or maildrop to work with, as I'm more familiar with doing it that way.
First, I changed the mailbox_command in postfix's main.cf to have it hand everything to procmail.
mailbox_command = /usr/local/bin/procmail

I then created a very simple /usr/local/etc/procmailrc

SPAM_MBOX=/var/mail/spam

:0 fw
|/usr/local/bin/spamassassin
:0:
* ^X-Spam-Flag: YES
$SPAM_MBOX

For more on procmail, see my procmail page.. In a nutshell the :0fw tells procmail it's a filter and that procmail should wait for the program to finish before proceding.
The |/usr/bin/spamassassin (note the | before the path to spamassassin) calls, obviously enough, spamassassin. The :0: creates a lock file for the recipe and the next two lines tell it what to do--if spamassassin gives it an X-Spam-Flag of YES then put it in /var/mail/spam.
This worked like a charm. Mail tagged as spam would be put into the spam folder. Mail not tagged would be delivered to the user's mailboxes.

I use sa-learn to teach spamassassin about new spam. I gather spam emails, put them in a directory called spamteach and as root ran
sa-learn --spam --mbox /var/mail/spamteach

Those of you familiar with spamasssassin probably realize that this did nothing but set root's bayes tokens. A bit of googling indicated that adding
bayes_path /usr/local/etc/mail/spamassassin/bayes

to /usr/local/etc/mail/spamassassin/local.cf fixed it.

For a slightly more complex setup (but still rather easy to follow) see the stearns.org page on setting up spamassassin.

That was all there was to it. I've gone on at great length about my relatively simple postfix configuration to try to impress upon the reader the importance of changing one or two parameters at a time.
Although most mail setups these days seem to be more complex than this one, it was quite adequate for a small company's backup mail server.

Backscatter filtering

Note for sendmail users: I don't use sendmail, but this link may be useful.

If a spammer grabs your email address and starts using it, you will find your mailbox full of bounced messages that allegedly came from you. This is known as backscatter.

Although that link gives a solution to the problem from postfix's creator, there is a quicker solution. In my case, where I have a very small server, only serving me, I use a solution gotten from Justin Mason's weblog. (Both links given above are worth reading, and the reader can make their own decision.) Mr. Mason's solution is to add the following to /etc/postfix/main.cf. (Your path to your postfix files may be different.)
header_checks = regexp:/etc/postfix/header_checks

Add the following to /etc/postfix/header_checks
/^Content-Type: multipart\/report; report-type=delivery-status\;/ REJECT no third-party DSNs
/^Content-Type: message\/delivery-status; /     REJECT no third-party DSNs

Restart postfix. This will catch most of the bounces.

Using postfix as your localhost MTA

Although many systems come with sendmail enabled by default, even if it only sends local system messages, this can easily be done with postfix instead. Perhaps you don't want a mailserver, but would still prefer to use postfix for the basic system email messages. This is easily done.

Install postfix and have it replace sendmail as you would usually do on your system. This varies widely between systems--for example, with RedHat based systems, one uses the command
alternatives --config mta

You'll then be presented with a dialog enabling you to switch to postfix.

To have postfix do nothing but send system messages on the localhost to root or whatever user gets root's mail on the localhost, do the following. In /etc/postfix (on most systems--FreeBSD uses /usr/local/etc/postfix) look for the line in master.cf (not main.cf) that says something like
smtp inet n - n - - smtpd

Comment it out by putting a # sign in front of it. In main.cf, look for the relayhost entries. There's usually a series of them, commented out, along the lines of relayhost = $mydomain, relayhost = [gateway.my.domain], etc. (All on separate lines.) Put in the line
relayhost = [localhost]

Putting localhost in the square brackets [ ] disables MX lookup. You don't want it doing an MX lookup on the localhost, it already knows where the localhost is. (If it doesn't, there is a major problem).

Restart postfix with /etc/init.d, /etc/rc.d/ or whatever method is used on your system and you should be good to go.