Fighting spam with Sendmail, SpamAssassin, and Procmail on Red Hat Linux 9.0

by Charlton Rose

This tip assumes you have already installed Red Hat Linux 9.0 and that you chose "Install Everything" (like any good Linux lover would). It also assumes you have successfully configured Sendmail to deliver your mail locally. Individual variations may require adaptation. I also assume that you are root and not totally stupid.

Step 1: Enable the spamassassin service.

From the console, type:

/sbin/services spamassassin start

to enabled spamd, the SpamAssassin daemon. Use gnome-config-services to make sure it comes on automatically next time you reboot.

It is not strictly necessary to run SpamAssassin as a service; this is done only for efficiency. An alternative is suggested in Step 2.

Step 2: Configure global Procmail behavior.

Procmail is a program that Sendmail uses to deliver local mail. It's operation is governed globally by the configuration file /etc/procmailrc and per-user by ~/.procmailrc. We'll work on the global configuration first.

By default, there is no /etc/procmailrc on fresh Red Hat Linux 9.0 installs. You need to create one. Here is the one I'm using. (The line numbers are for reference and not part of the file.)

1  # send mail through SpamAssassin
2  :0 fw
3  * < 256000
4  | /usr/bin/spamc -f

Line 1 is, of course, a comment.

Line 2, beginning with colon-zero, marks the start of what is called a "Procmail recipe." A recipe is a condition followed by an action. For more information on Procmail recipes, see man procmailrc. The 'fw' indicates to Procmail that this recipe is a filter, meaning that it may modify the original email message.

Line 3, beginning with an asterisk, is a condition. It tells Procmail that this recipe applies only if the message is less than 256,000 bytes in size. Since 99% of all spam is smaller than this, this is a safe bet. We don't want to bog down our system checking messages that probably aren't spam.

Line 4 tells Procmail to filter the message text through SpamAssassin. In other words, the original message is replaced with SpamAssassin's output. If SpamAssassin determines that the message is spam, the subject line will be rewritten to include a spam tag. (Other less visible changes will also be made to the message.)

spamc is the counterpart to spamd. The former runs as a client, while the latter runs as a server. It is believed that this configuration is the most efficient way to run SpamAssassin in most instances (due to some sort of pre-forking nonsense). If you'd rather run SpamAssassin in stand-alone mode, you can always disable the spamd service by replacing procmailrc's filter line with:

| /usr/bin/spamassassin

I don't recommend it, however.

The flag '-f' in Line 4 instructs spamc to return the original message if it can't connect to spamd. This ensures that the message will be delivered by default if it can't be scanned for spam.

Step 3: Configure per-user Procmail behavior.

After Step 2, all incoming email is automatically checked for spam and flagged if it is suspected to be so. For many sendmail administrators, this behavior is sufficient. If, however, you'd like to reject (bounce) spam with an unfriendly error message, you will need Step 3.

I recommend this step for two reasons:

  1. The error message will cause some spammers to remove the recipient's email address from their spam list. As a result, they may never spam you again. Furthermore, they may remove the recipient's email address before they sell their list to someone else. This will result in less spam in the long run, freeing up your system's resources.

  2. Recipients of spam-tagged email may get in the habit of deleting tagged spam without actually examining it. (Of course! What's the point, otherwise?) This means that if there is a false positive, the recipient will never open it, and the legitimate sender will never suspect that the message was not received. On the other hand, if you reject the message, the sender will receive notification, so that he or she can take action.

Here is how I set my individual Procmail preferences. This is in file "~/.procmailrc":

1:  MAILDIR=$HOME/mail
3:  :0 H
4:  * ^X-Spam-Status:.*Yes
5:  {
7:  :0:
8:  spam
9:  }

Line 1 tells Procmail where I keep my mail folders. I use Pine, which keeps my folders in a directory called "mail" in my home directory.

Line 3 marks the start of a recipe. The 'H' indicates that the test on Line 4 should only be applied to the email's header, and not the body. (This is for efficiency.)

Line 4, the recipe's condition, determines whether SpamAssassin has tagged the incoming message as spam. This is a regular expression that looks for a mail header "X-Spam-Status" with the substring "Yes" somewhere in the header's value.

Lines 5 through 9 are the action. The braces are necessary and mark their contents as a "compound action" (since each recipe allows only one action). If you are a programmer, you probably understand what is going on here. If not, don't worry about it; just do it.

Line 6, which only executes when spam has been detected, tells Procmail to exit with error code 67. This indicates (albeit deceptively) to Sendmail that the user does not exist. In turn, Sendmail will bounce the spam, giving the illusion to the sender that it was never delivered.

In Procmail, compound actions can contain additional recipes. Line 7 starts one such "nested recipe." The colon following the zero tells Procmail to lock the delivery file before delivering the message. This will prevent problems if two spams arrive simultaneously.

Line 8 is the action for the inner recipe. Note, in this recipe, that there is no condition (i.e., no line beginning with an asterisk). The action tells Procmail to deliver the message into a folder called "spam" (which, according to Line 1, is in ~/mail).

Now we have the best of both worlds. From the senders point of view, the message has apparently been rejected. However, the truth is that it was delivered, but to the user's spam folder. This is a much better destination for spam than the user's inbox, don't you agree? If you prefer, you can instead write the message to /dev/null, a lovely black hole from which no data ever returns.

If you don't want to filter spam for all users, but instead wish to let each user make his own choice, you can omit Step 2 and put the recipe of Step 2 just above the recipe described in Step 3.

Step 4: Configure DNS blacklists.

If you wish to be even more aggressive with your spam filtering, you can configure Sendmail to completely ignore senders that have bad reputations. With this step, Sendmail won't even talk to them, let alone scan their email for spam.

Editing the file /etc/mail/, insert the following lines anywhere in the FEATURE section of the file:

dnl #
dnl # Here are Sharky's favorite DNSBL definitions.
dnl #
FEATURE(`dnsbl', `')dnl
FEATURE(`dnsbl', `')dnl
FEATURE(`dnsbl', `')dnl
FEATURE(`dnsbl', `')dnl
FEATURE(`dnsbl', `')dnl

Apply the changes by saving the file and running the following commands:

cd /etc/mail
make all
/sbin/service sendmail restart

From this point on, every time an SMTP client connects to Sendmail, Sendmail will refer to the blacklist authorities you added to verify the client's reputation. If the client is reported to have a shady reputation, Sendmail will hang up on him.


There used to be days when I would receive more than 50 spam messages in one day. Following the above 4 step procedure has virtually eliminated all of my spam problems. (Perhaps one spam leaks through every 3 days or so. My hat goes off to those ingenious spammers. They deserves an audience. They earned it.)

There are steps you can take to "white list" legitimate senders who are falsely identified as spammers. I don't know what those steps are. If you figure it out, please share it with me so I can include that information here.

Good luck!



Created 2003.05.03. Last updated 2003.05.03. Please report typos, misspellings, grammatical errors, omissions, and inaccuracies to the author.

Copyright © 2003 Sharkysoft. All rights reserved.

[ Sharkysoft home | more Linux tips ]