EDIT: This blog entry is quite old, in the world of Postfix. It's easy to find RPMs for 2.0 experimental releases now, and the word "experimental" (as of August 2003) is starting to sound a little funny for what is becoming the mainstream release people are using day-to-day. If something doesn't work for you, I suggest you hit the Postfix home page and see if you can figure out what's up from the mailing list archives.
Yesterday evening (Saturday night. There are a whole lot more fun things to do on a Saturday night than this!) I upgraded the Postfix install on my firewall at work. The primary purpose of my upgrade was to enable "Sender Address Verification". This stuff is pretty cool. Although the sample-smtpd.cf has wonderful examples of how to use this, I found other information on the 'net totally lacking.
Now, some important information before you get excited about Sender Address Verification:
The basic process itself is pretty darn easy. After downloading postfix-2.0.9-20030424 (or whatever the current experimental release is), rather than the usual "./configure; make; make install" three-step you'll find on most packages, Postfix follows its own standard.
The Upgrading Postfix Four-step
Note that I use, and heartily recommend that everybody else does, too, "sudo" for all my root-privilege stuff. If you're doing all the above as root, leave off the word "sudo" from the last step.
It's unfortunate there's no RPM for the experimental releases, but Postfix is pretty good about figuring out how your previous version is installed (by looking at your old .cf files) and putting its stuff there. One thing the "make upgrade" doesn't do is copy over all the sample-foo.cf files. I heartily recommend figuring out where those sample files live on your system. On my FreeBSD test system, they live in /usr/local/etc/postfix/. On the RedHat 8 firewall, they live in /usr/share/doc/postfix-1.1.11/samples/.
On the FreeBSD test system, the install was flawless. Postfix worked exactly the same after "make upgrade" as it did before. Of course, that system runs barnson.org and is a slightly less complex install than the Red Hat 8 firewall at work. On my OpenBSD firewall at home, the results were similarly flawless.
However, when I reached my RedHat 8 install on the firewall, I immediately noticed some errors.
Problems with Postfix's "make upgrade" on a complex Red Hat 8 firewall
The reason we were running two Postfix instances on one box was because we were running Spam Assassin on mail, and didn't want mail leaving our network with spamassassin headers attached. We've since reduced that to just Anomy, and rely on Bayesian filters for those that want them within the firewall. It works better, and the firewall isn't pegged at 100% CPU usage 70% of the time anymore. So I turned off the secondary instance in my /etc/init.d/postfix script, and also added the localhost and internal interfaces to postfix's main.cf. I'll still have to see how that works.
OK, so you've read all this way, and you're probably wondering about all this Sender Address Verification stuff. It's really quite simple. In your "smtpd_recipient_restrictions" section, just add this one line:
somewhere before your "permit" at the end of the section. Reload postfix, and try this sample mail session. Any line that begins with a number is a response from the remote server. Any line that begins with an all-caps command is what you type. Start by telnetting to port 25 on the remote mail server.
[matthew@localhost matthew]$ telnet some.internet.host 25 Trying 22.214.171.124... Connected to some.internet.host. Escape character is '^]'. 220 220 some.internet.host ESMTP Postfix HELO barnson.org 250 bubba.aib.com MAIL FROM: 250 Ok RCPT TO: 550 : Sender address rejected: undeliverable address: host mail.barnson.org[126.96.36.199] said: 550 : User unknown in local recipient table (in reply to RCPT TO command) RSET 250 Ok QUIT 221 Bye Connection closed by foreign host.
In the following list, I use the terms "client", "server", and "verification host". The client is the machine attempting to send mail to your Postfix server. The server is your Postfix server. The Verification Host is the first available MX (Mail exchanger) host based on the MAIL FROM: sent by the client. The basic process that just happened was this:
How Sender Address Validation works:
So far, reject_unverified_sender is really mostly catching the "corner cases" for me, rather than the majority of spam. However, it has almost eliminated a class of messages that occasionally kill our mail server: dictionary attacks. Often a spammer will send thousands of messages to random addresses in our domain in hopes that someone will read their pitch. Well, our Postfix gateway is configured to blindly relay any address in our domain on back to our Groupwise server behind the firewall. Yeah, I know, it's not the best thing in the world, but at the moment it's easier to do that than to enter several thousand email addresses by hand into the "virtual" table for Postfix. Nasty getting those out of Groupwise. Anyway, if a spammer dictionary-attacks us using an invalid return address, when Groupwise bounces the message, those bounces bounce because the return address won't work. Basically, we're just checking to see that there's a real, live email address listed in the return. This prevents those thousands of "double bounces" from ever getting onto our network in the first place, and ending up in my inbox as postmaster of the domain. And that's really what this is all about: lowering my frustration level with double-bounces.
We're in the process of moving our organization from an ancient install of Groupwise 5.0 to Cyrus Mail. Yeah, we're going to lose a little functionality here and there, but my hope is that it's totally work-around-able for our users. Once that migration is complete, we can actually easily streamline this process by using reject_unverified_recipient, which makes a connection to the ultimate destination SMTP server, validates that a mail address exists and is deliverable, and does pretty much the same thing otherwise. This would still provide us with protection for our internal mail server by using an inbound mail gateway, yet dramatically reduce our double-bounces. Alternatively, we can just maintain a list of users in two places (one on the firewall), but I'm leery of storing any non-essential user names on a firewall, you know?
Eventually, I think the spammers will get wise to thise protection and begin always using valid email addresses as the return address of their promotions. Unfortunately, I think it's most likely that they will use known-good mailing addresses as the forged source of their unsolicited commercial email. However, this will catch those who are slow to catch on, and at least force fraud to be more quickly exposed. If I were your average mail administrator, I'd certainly want to keep an eye on RCPT TO: requests to my mail server, and if I see too many to a certain individual, temporarily disable their mail account until the attack is over.
OK, now the last thing. I'd planned on getting this implemented by the time I wrote this article, but I just haven't had the time yet. The final phase in my plan for anti-spam is to create a temporary rejection table called "five-fifties". This Postfix table would store the IP addresses of clients who do not honor 55x rejection error codes and make multiple mail delivery attempts after receiving a 550/554/whatever. I realize that it's not really going to be much of a help defending against spam (since I'm already rejecting them with a 55x anyway), but it would be a big help in tracking connection attempts from abusive IP addresses and setting up another daemon to automatically add repeat offenders to an iptables blacklist. Looks like you'll have to wait for another article to read more about that though, after I have implemented it!
Hope this helps!
***NOTE: Please be aware that I'm still getting used to my chosen blogging software, Drupal, at this point. So there may be strange typographical issues here and there. Please let me know via comment if you see something that needs fixing.***