Conditional address rewriting with Postfix

5 people like this post.

Today I had the need to (automatically) rewrite sender addresses of an email depending on the recipient domain. A way to trick Postfix into applying a sort of “conditional masquerading”. Postfix rewriting tables are just static key → value dictionaries: they’re used to lookup B given A, but there’s no available logic to cope with more complicated patterns.
A little more context to help me explain: I’m talking about a monitoring system. Alert emails are generated by Nagios and handed to a local Postfix on the same server. And here are the rules to implement:

  • A locally generated email whose destination is inside the company, should leave Postfix with a @FQDN suffix (@hostname.localdomain.lan) in its sender addresses. Sender addresses shouldn’t be rewritten/masqueraded at all.
  • A locally generated email whose destination is outside of the company, needs to be masquerated, its sender addresses rewritten as @extdomain.com .

Moreover, but that’s a routing matter rather than a rewriting one:

  • Emails directed to @smsgw.localdomain.lan have to be relayed through a different mail server.

As you can see, the logic is: lookup B (rewritten sender) given A (sender) depending on C (recipient).

I found the right hint deeply buried in Postfix’s mailing list: check out Noel Jones post, kudos to him.

  • First, define a new smtp transport in “master.cf”; just copy/paste the existing one and change the first column to whatever name you like. We are explicitly telling the new transport that it will use its own generic regexp map (the -o command-line option).
    [root@hostname postfix]# cd /etc/postfix
    [root@hostname postfix]# grep '^\(smtp\|toext\).*unix' master.cf
    smtp      unix  -       -       n       -       -       smtp
    toext     unix  -       -       n       -       -       smtp -o smtp_generic_maps=regexp:/etc/postfix/generic_toext
  • We also need to take control over the mail routing mechanism. This is done by enabling transport maps.
    [root@hostname postfix]# grep ^transport main.cf
    transport_maps = regexp:/etc/postfix/transport
  • Transport maps (remember that they’re matched against From addresses) are configured in order to:
    • Route mail that should be delivered locally through the local transport. This will preserve /etc/aliases and .forward behaviour and make everything act like you expect on Unix.
    • Route mail to @smsgw.localdomain.lan, via its dedicated gateway, using the “standard” smtp transport.
    • Route mail to @localdomain.lan, through the main SMTP gateway, using the smtp transport.
    • Route any other message through the main SMTP gateway, but use our custom transport.
    [root@hostname postfix]# tail -4 transport
    /@hostname\.localdomain\.lan$/  local:hostname.localdomain.lan
    /@smsgw\.localdomain\.lan$/     smtp:[smsgw.localdomain.lan]
    /@localdomain\.lan/             smtp:[gateway.localdomain.lan]
    /@.*$/                          toext:[gateway.localdomain.lan]
  • The custom transport’s generic map rewrites recipient adresses, shortening the FQDN by preserving just the domain name, and changing the address part before the @ sign. Hostname is being stripped but I still want to be able to tell, at a glance, from where the message originates. When they leave the mail system, rewritten addresses look like usernamehostname@extdomain.com .
    [root@hostname postfix]# cat generic_toext
    /^(.*)@hostname\.localdomain\.lan$/ $1-hostname@extdomain.com