Spam is a growing problem for most people who have had an e-mail address for any length of time. Spammers discover your e-mail addresses through public directories, mailing lists, newsgroup postings, and random dictionary attacks on a mail server. So what can you do to stop it?
In this article, I will explore some of the principles of configuring Postfix to accept or reject messages before they ever get delivered to a mailbox.
Check out the whole series
In previous installments of this series, I set up Postfix as a Mail Transfer Agent (MTA), Courier-IMAP to handle connections to user e-mail accounts, and Squirrelmail to provide Web access. Now that the e-mail system is fundamentally complete, let me turn your attention to a few techniques for limiting spam. In case you missed them, the following articles are designed to help you build an e-mail system from the ground up:
- "An inexpensive e-mail server setup"
- "Set up a basic e-mail server with Postfix as the MTA"
- "Extend the reach of users' e-mail with IMAP"
- "Complete the e-mail setup: User accounts, DNS, and more"
- "Add Web mail to enhance your mail system"
Postfix unsolicited commercial e-mail configuration
Trying to figure out what you actually need to implement can be a challenge—very few Web sites provide a good explanation of how e-mail goes through the system. With a default installation, Postfix is reasonably secure. It checks to see if an e-mail is either going to an address it knows about or is coming from an address on the local subnet of the mail server. If it's not, Postfix doesn't accept the message, and the sender gets a bounce message.
With Postfix (and most other MTAs), you can take a few more steps to block mail that has been forged or comes from badly configured servers. Here is what I'm going to check incoming mail for:
- Blacklisted IP ranges
- IP addresses that generate more than a few errors in a short time
- Connections to or from e-mail addresses that aren't listed in DNS
Blacklist an IP address or range
A few months ago, my server was getting scanned for valid users, presumably by a spammer trying to beef up his database. My mail log showed a sequence of connections from an IP address that resolved to somewhere in Brazil. The spammer connected and then tried a new e-mail address at my domain every two or three seconds. The account names were all kinds of common names and words, and Postfix rejected them with "User Unknown" errors. My logs were filled with hundreds of these attempts.
My first attempt to stop this activity was to add the sender's IP address to an access map. Postfix has many ways of looking up items, but perhaps the easiest is to set up a hash (a type of database file based on the Berkeley DB from Sleepycat Software). You create a simple text file named /etc/postfix/access with two columns: the first column contains the text to match the IP address, and the second column tells the server what to do if the address matches (reject it). You can separate the columns with one or more spaces or tabs.
After creating or modifying the access file, run the postmap utility to turn it into a hash:
# cd /etc/postfix
# postmap access
Then, add the hash to the smtpd_recipient_restrictions directive using the following format in /etc/postfix/main.cf:
smtpd_recipient_restrictions = check_client_access hash:/etc/postfix/access,
Postfix will now reject mail with a "Relay Access Denied" message, no longer revealing whether a user account exists. You can use an asterisk in the access map as a wild card to block ranges of IP addresses.
Set error limits for IP addresses
In my situation, the problem with blocking the IP address was that a day or two later, the spammer had moved to a different address. Now I had the same problem as before. I could block a whole subnet of IP addresses, or I could find a more generic way of dealing with the problem.
What made my spammer finally go away was setting the error limits to a lower level. Postfix provides a few settings for dealing with many errors, but the most effective one for my situation was smtpd_soft_error_limit. By lowering this setting to three, I managed to make the spammer go away.
What this setting does is allow an SMTP server connecting to my server to make three errors within a short period of time. Then Postfix starts leaving the connection open, without responding, for an increasing amount of time for each instance that the originating server sends a mail that is rejected. Three strikes and you have to wait before you get a response. Four, and you have to wait longer. Soon my spammer went to find an easier target.
You also want to lower the smtpd_hard_error_limit to prevent a potential denial of service attack, because the soft errors tie up a Postfix process while it's waiting, and you don't want to run out of available processes for legitimate mail. When Postfix gets more errors from an IP address than the hard error limit, it just disconnects. If this happens too soon, the spammer just reconnects. I set mine to the number six setting.
Block other malformed mail
When Postfix receives mail, it checks for restrictions at four different points:
- The initial connection (smtpd_client_restrictions)
- The HELO string the connecting server passes (smtpd_helo_restrictions)
- After evaluating the sender envelope address (smtpd_sender_restrictions)
- When evaluating the destination e-mail address (smtpd_recipient_restrictions)
An e-mail must pass through all of these lists without being rejected to get delivered.
Most of these lists can be empty—the only requirement is that you must reject unauthorized destinations in the smtpd_recipient_restrictions setting. You can stack any restriction you want in the smtpd_recipient_restrictions. Some restrictions you should add to this list are:
These restrictions check that the sender address is a Fully-Qualified Domain Name (FQDN), and that both the sender and recipient are listed in DNS. It also blocks spam software that attempts to "pipeline" messages through your server. Another setting to turn on (outside of the restriction lists) is smtpd_helo_required, which rejects mail servers that do not introduce themselves properly.
One restriction I've had problems with is reject_unknown_client. This checks to see if the sending server is listed in the DNS. I've found that many of my business clients have their mail servers incorrectly listed, so this restriction blocks a lot of mail I want to see.
If you want to see the effect of a restriction without actually rejecting mail, put warn_if_reject right before the restriction, and you will get a warning in the mail log without rejecting the mail.
Mail server administration
As you might realize after reading this, managing an e-mail server is a job that takes diligence to keep the system up to date. However, as overseer of the e-mail system, you have several weapons you can use to deflect the amount of spam that tries to find its way to your user's Inbox. Keep an eye on the mail log, check for nefarious activity, and use these techniques to do what you can to block spam.