Postfix Trick to Force Secondary MX to Deliver Locally

Wednesday 9 January 2008 by Bradley M. Kuhn

Suppose you have a domain name, example.org, that has a primary MX host (mail.example.org) that does most of the delivery. However, one of the users, who works at example.com, actually gets delivery of <user@example.org> at work (from the primary MX for example.com, mail.example.com). Of course, a simple .forward or /etc/aliases entry would work, but this would pointlessly push email back and forth between the two mail servers — in some cases, up to three pointless passes before the final destination! That's particularly an issue in today's SPAM-laden world. Here's how to solve this waste of bandwidth using Postfix.

This tutorial here assumes you have a some reasonable background knowledge of Postfix MTA administration. If you don't, this might go a bit fast for you.

To begin, first note that this setup assumes that you have something like this with regard to your MX setup:

            $ host -t mx example.org
            example.org mail is handled by 10 mail.example.org.
            example.org mail is handled by 20 mail.example.com.
            $ host -t mx example.com
            example.com mail is handled by 10 mail.example.com.
            

Our first task is to avoid example.org SPAM backscatter on mail.example.com. To do that, we make a file with all the valid accounts for example.org and put it in mail.example.com:/etc/postfix/relay_recipients. (For more information, read the Postfix docs or various tutorials about this.) After that, we have something like this in mail.example.com:/etc/postfix/main.cf:

            relay_domains = example.org
            relay_recipient_maps = hash:/etc/postfix/relay_recipients
            
And this in /etc/postfix/transport:
            example.org     smtp:[mail.example.org]
            

This will give proper delivery for our friend <user@example.org> (assuming mail.example.org is forwarding that address properly to <user@example.com>), but mail will push mail back and forth unnecessarily when mail.example.com gets a message for <user@example.org>. What we actually want is to wise up mail.example.com so it “knows” that mail for <user@example.org> is ultimately going to be delivered locally on that server.

To do this, we add <user@example.org> to the virtual_alias_maps, with an entry like:

            user@example.org      user
            
so that the key user@example.org resolves to the local username user. Fortunately, Postfix is smart enough to look at the virtual table first before performing a relay.

Now, what about aliases like <user.lastname@example.org>, that actually forwards to <user@example.org>? That will have the same pointless forwarding from server-to-server unless we address it specifically. To do so, we use the transport file. of course, we should already have that catch-all entry there to do the relaying:

            example.org     smtp:[mail.example.org]
            

But, we can also add email address specific entries for certain addresses in the example.org domain. Fortunately, email address matches in the transport table take precedence over whole domain match entries (see the transport man page for details.). Therefore, we simply add entries to that transport file like this for each of user's aliases:

            user.lastname@example.org    local:user
            
(Note: that assumes you have a delivery method in master.cf called local. Use whatever transport you typically use to force local delivery.)

And there you have it! If you have (those albeit rare) friendly and appreciative users, user will thank you for the slightly quicker mail delivery, and you'll be glad that you aren't pointlessly shipping SPAM back and forth between MX's unnecessarily.

Posted on Wednesday 9 January 2008 at 12:01 by Bradley M. Kuhn.

Submit comments on this post to <bkuhn@ebb.org>.



Creative Commons License This website and all documents on it are licensed under a Creative Commons Attribution-Share Alike 3.0 United States License .


#include <std/disclaimer.h>
use Standard::Disclaimer;
from standard import disclaimer
SELECT full_text FROM standard WHERE type = 'disclaimer';

Both previously and presently, I have been employed by and/or done work for various organizations that also have views on Free, Libre, and Open Source Software. As should be blatantly obvious, this is my website, not theirs, so please do not assume views and opinions here belong to any such organization.

— bkuhn


ebb is a (currently) unregistered service mark of Bradley M. Kuhn.

Bradley M. Kuhn <bkuhn@ebb.org>