Wednesday, August 29, 2007

Multiple Recipient Delimiters in Postfix

err.pngSome time ago I enabled recipient delimiters (e.g. user+foo@host.tld) as a convenient way to know if shady web forms are contributing to my spam folder. The idea is that when House Depot requires me to have an account before I can see if they have loose screws in stock locally, I can sign up with garrison+housedepot@codefix.net instead of my usual e-mail. With recipient delimiters enabled, postfix will try to deliver any incoming mail to garrison+housedepot but when it finds no such user, it will try garrison and I get my mail. The problem arises when I discover that House Depot’s broken web form rejects any e-mail addresses with “+” in the user name as invalid. I’m already using garrison+foo style addresses elsewhere so I don’t want to change the recipient delimiter, but neither do I trust my real address to a company that can’t even create a proper web form.


Postfix allows only one recipient delimiter, but address rewriting can mimic multiple recipient delimiters; I chose to use regular expression tables in my aliases database. I’m most comfortable with Perl, so I chose to install the Perl compatible regular expression package, on Debian/Ubuntu this is done with:

sudo aptitude install postfix-pcre

Next I created a file named aliases-pcre with the following content:

/^garrison\./ garrison

Finally I updated main.conf with these lines:

alias_database = hash:/etc/postfix/aliases
alias_maps = hash:/etc/postfix/aliases, pcre:/etc/postfix/aliases-pcre


Now I can enjoy e-mail addresses like garrison+housedepot@codefix.net or garrison.housedepot@codefix.net without needing to keep track of each alias. If I get spam addressed to that address, I can easily block it and complain loudly to House Depot’s customer service in India.

6 comments:

  1. you don't need to (may not) list the pcre under alias_database because pcre doesn't need to build db files from the 'source' file.

    so delete the
    alias_database = pcre:/etc/postfix/aliases-pcre
    part

    ReplyDelete
  2. Thank you for pointing that out, Alex. I'll be doubly grateful if you can explain why 'source' files need to be listed in main.conf at all. (or do they not?)

    ReplyDelete
  3. Thanks for the great hint! Regarding some details:

    1. I note that it is just as easy to use POSIX regular expression maps (which are built-in) for such simple examples, see regexp_table(5).

    2. It seems your map will just "lose" the extension. I haven't tested this, but I'm afraid it won't work with my set-up where I forward the address extension to procmail:

    mailbox_command = /some/where/procmail -a "$EXTENSION"

    Inside procmail, I can use this argument to filter mail, like this:

    EXTENSION=$1

    :0:
    * EXTENSION ?? ^^housedepot^^
    .Mailings/

    3. So to avoid losing the extension, I took advantage of the fact that you can use backreferences in the result. In your table, it would look like this:

    /^garrison\.([a-z0-9_]+)$/ garrison+$1

    ReplyDelete
  4. Thanks for your comment, Bruno; your suggestion is a fine alternative, but I can clarify a few points:

    1. I choose PCRE simply because I am a perl programmer and I have given up trying to remember the differences between POSIX, PCRE, grep, sed, and awk regex engines. POSIX is more efficient (in this case), but PCRE eases my development.

    2a. The map will lose the extension, but only for the delivery address, which happens anyway once 'foo+bar' fails (so it actually saves postfix a step). In all cases the mail headers are left intact so the mail is still addressed 'foo.bar'.

    2b. Both setups should work with procmail and maildrop for the reason stated above (2a); however, I do quite a bit of automatic sorting, filtering, and forwarding with a custom delivery agent I wrote in perl with Mail::Audit. I should probably do a post about that.

    3. Although keeping the extension is not strictly necessary, I strongly advocate that all readers modify my examples in the interest of maintainability for each local configuration.

    ReplyDelete
  5. actually, newer postfix versions don't allow regular expressions for aliases anymore (security reasons).

    Instead, the virtual table can be used:

    1. create /etc/postfix/virtual-regexp:

    /^garrison\.[A-Za-z0-9_]+@codefix\.net$/ garrison

    2. in main.cf write:

    virtual_alias_maps = hash:/etc/postfix/virtual, regexp:/etc/postfix/virtual-regexp

    ReplyDelete
  6. In fact, what Postfix disallows in address mapping is $number substitution, such as:

    /^(\S)\.\S@domain\.com$/ $1

    This applies to both aliases and virtual addresses, but non capturing regular expressions can indeed be used.

    http://www.postfix.org/PCRE_README.html

    ReplyDelete