How to use Postfix's relay_recipient_maps feature to reject mail that is not addressed to valid SMTP addresses on the internal mail server

This document will explain how to use Postfix's relay_recipient_maps feature to reject mail that is not addressed to valid SMTP addresses on the internal mail server (in this case, a MS Exchange 2000/2003 server). So basically, mail for would be accepted, while mail for would not. This saves Exchange from wasting resources generating bounce messages most of which are from bogus senders anyway. Believe me, I get a LOT of email that is addressed to non-existant users, this setup will help stem this problem right at the front door.

This document assumes that you are following Scott L. Henderson's excellent spamfilter build doc, and you are placing the spamfilter in front of a MS Exchange server (Exchange 2000/2003 only, if you need help with Exhange 5.5, see this site). However, this procedure can probably be modified for use with any LDAP-enabled mail server, although I have only used it with Exchange 2000.

The method described here will employ Chris Covington's script to retrieve a list of SMTP addresses from a MS Active Directory Domain Controller. The real beauty of this method is that nothing needs to be run on the MS server itself and the script can be cronned to retrieve the list of users at any defined interval.


If you have any questions or comments, you can email me at vandensype AT hotmail DOT com.

With that, let us begin....

Remove mydestination and add relay_domains to

If you have been following the build document up to this point, you will have have to edit the file by removing the mydestination directive and adding the relay_domains directive. The actual data used for relay_domains will be the same as mydestination (i.e.,, ...), just the directive itself will be changed. The simplest way to do this is to open in vi and rename mydestination to relay_domains.

vi /etc/postfix/

relay_domains - What destination domains this system will relay mail to. This will be exactly the same domains you originally put in mydestination.

Change the file to reflect the domain(s) we will be relaying mail for:

Scroll down to the very end of the file (or hit "G" to go there immediatly). Here you will see all of the directives you have added so far (Note: this assumes that you have been following the original documents method of using "postconf -e" of editing Find the line that says:

mydestination =,

and replace "mydestination" with "relay_domains", so that it looks like:

relay_domains =,
save and exit vi

...of course, change the example domains to the real ones you accept mail for.

*Please note that Postfix will automatically set mydestination to equal $myhostname, localhost.$mydomain, localhost by default. This enables postfix to deliver mail to local user accounts (root & postmaster for instance). So mail addressed to, root@localhost, etc. will be accepted by postfix and delivered to root, which you aliased (or should have) to a real account earlier (e.g.


Next, we need to tell postfix where to look for the list of valid recipients by including the relay_recipient_maps directive:

postconf -e relay_recipient_maps = "hash:/etc/postfix/exchange_recipients"

Of course, you don't have to name the file "exchange_recipients" if you don't like, name it anything that makes sense to you and your situation.

Now tell Postfix to read its new configuration, and while your at it, save your new Postfix configuration to a text file for backup and review purposes.

postfix reload
postconf -n > /root/postconf.txt

Get Active Directory SMTP addresses utility:

Next, you will need to download the script that will query the Exchange server for Active Directory users "proxyAddresses" which is a list of all (i.e. both primary and secondary) email addresses. Save the file to your /usr/bin directory and make sure it is executable. The script itself is well commented, but to find out more about how this script works, I suggest you read Chris Covington's Posfix and Exchange HOWTO. The procedure I describe below is therefore somewhat of a reiteration of what his page/script already has, but I figured I would add it if it helps.

In order for the script to work correctly, you will need to install Net::LDAP from the MCPAN library, to refresh you memory, here is how:

perl -MCPAN -e shell

At the prompt, type:

install Net::LDAP 

Important: your spamfilter box will require port 389 access to your Active Directory DC in order for this script to work, so adjust your firewalls accordingly!

Open the script in your editor:

vi /usr/bin/

Enter the path to your recipient maps file by changing the line:

$VALID = "/etc/postfix/example_recipients";


$VALID = "/etc/postfix/exchange_recipients";

Of course, if you named your file something else, replace the path accordingly.

Next you will need to enter either the Fully Qualified Domain Name (FQDN) of your Active Directory Domain Controller or you can enter the DC's local IP address. You may have to do the latter if your DC uses the "" naming scheme since your spamfilter would not be able to resolve this address (unless you explicitly tell it). In any event, depending on your situation, this parameter may need some tweaking in order for the spamfilter to "talk" to the DC.

Change the lines that say:




Where w.x.y.z is the local IP address of your DC, and Backup Domain Controller ($dc2), if you have one.

Next, you will need to determine and enter the LDAP container of your user base. To do this you should download the Windows 2000 Support Tools and install them on your AD DC. Here I suggest you read the comments in the script carefully about how to determine your LDAP container information using the ADSI Edit program from the Windows 2000 Support Tools. Once you have determined what your base and your users container are, you should change the line:


to whatever it is for your situation.

Next, you will need to enter a username and password for a user in your Active Directory. This user does not need any special privileges but you should make sure that the user's password is set to not expire. The format of the user should be entered as "cn=username,cn=Users,dc=example,dc=com". Again, I suggest you read the comments in the script carefully. Note that because you are entering a password here in clear text, I would make sure that this script is only readable by root. Once you have the information you need, change the lines:


to the appropriate values.

* Please note that if the password you use contains the $ sign (and perhaps others? I am not familiar with perl really, but some characters such as $ and probably also quotes have special meaning) you will have to escape them appropriatly with the backslash or perl will complain. For exaple, if your password is: pa$$word, you would have to enter: pa\$\$word here.

Once you have made all the changes to the script you should save it (hit Esc, and then :wq):

save and exit vi

Now, test out the script and see if it works:

If the script runs successfully, you should now have a file in /etc/postfix called exchange_recipients listing all your email addresses. To verify this, issue the command:

less /etc/postfix/exchange_recipients

(of course, replace this with the file name you chose earlier, if needed) You should see a list scroll by with format similar to this: OK OK OK

You will now have to postmap this file to make it into a format that Postfix can understand, to do this:

postmap /etc/postfix/exchange_recipients

That's about it. Test it out by trying to send mail to bogus email addresses at your domain. You should see in /var/log/maillog an entry that says something to the effect of: "550 <>: Recipient address rejected: User unknown in relay recipient table". Of course, you should ensure that you still are receiving real addresses as well in the event that you royally borked something!



Set up a cron job to rebuild the list periodically

Depending on your situation, you should set up a cron job to run this script automatically with a frequency that is adequate for your needs. The benefit of this is that it makes your spamfilter more autonomous in that whenever you add/remove an smtp addresses for one of the Exchange users, you don't have to run the script manually on the spamfilter. If you (or other administrators in your orgainization) frequently adjust the smtp addresses, I would suggest setting the following cron script to run hourly. In my case, I only run it once a day as there are rarely any changes made to my Exchange server. To do this:

crontab -e

Scroll down to the bottom of the file and hit the "i" key to begin inserting text. Type in the following:

00 02 * * * /usr/bin/; /usr/sbin/postmap /etc/postfix/exchange_recipients
Hit Enter after you finish.
Save and exit


This will run the script every night at 2 a.m. If you would like to run the script hourly, simply replace the "02" with an asterisk (*). Don't forgot to leave at least one blank line after your last crontab entry or else cron won't work (don't ask me why, it just won't).

Some final comments:

If you look at your file, you will see that the unknown_local_recipient_reject_code is set to 550. However, this directive does not control the rejection code for a recipient that is not listed in the relay_recipients_maps. The default rejection code for unknown users is 550, which is most likely what you want, but if you ever wanted to change it, the directive to change is unknown_relay_recipient_reject_code.

I would also like to thank the many people at the postfix mailing list for there help in making sure I got this thing working properly. There help is always amazing, I suggest everyone to have a looksee.


Previous page: page: