How to forward to dynamically determined addresses with Postfix?

Each message to a local user must go to an address that is obtained by running an external command. That command will look at the local address tag, make API calls to several processes, and then calculate the destination address.

For example, we could use a .forward file in the user's home directory to pipe the message to the command (|command), which would then be responsible for forwarding the message, too. "An alias or ~/.forward file may list any combination of external commands, destination file names, :include: directives, or mail addresses." See: www.postfix.org/local.8.html Also: www.postfix.org/aliases.5.html

Maybe it need only adjust the To header and re-send the message via Postfix? Something tells me it's not so simple. The simplest would be if Postfix itself handled the entire forwarding job, like it does when the destination address is written directly into the .forward file. But the address is unknown; it can only be determined by the command. This is the problem.

Further, if the destination address cannot be determined, then the message must bounce with a failure notice supplied by the command.

Solution as implemented

This solution expands on Danila's answer. Specifically it redirects mail for certain wiki users known as "pipes" [2], relaying it instead to "minders" who are assigned in the wiki [1]. But generally it should be applicable to any relay where the destination address is dynamically determined by a script.

(a) In file /etc/postfix/main.cf:

transport_maps = regexp:/etc/postfix/transport.regexp
# enable transport mapping based on regular expressions

minder_destination_recipient_limit = 1
# See SINGLE-RECIPIENT DELIVERY [3].  relay-to-minder (master.cf) can handle only a
# single recipient per delivery, and flags DO have the same limitation.

(b) In file /etc/postfix/transport.regexp [4], do the transport mapping:

if /^pipe\+.+@zelea\.com$/
# relay all deliveries for pipe users [2] ...
!/^pipe\+bounceSpam@/ minder:
# ... except 'bounceSpam' deliveries, via master.cf 'minder' service
endif

(c) In file /etc/postfix/master.cf, define the 'minder' service:

# ==========================================================================
# service type  private unpriv  chroot  wakeup  maxproc command + args
#               (yes)   (yes)   (yes)   (never) (100)
# ==========================================================================
minder    unix  -       n       n       -       1       pipe
  flags=DO user=pipe argv=/usr/local/libexec/relay-to-minder ${sender} ${recipient}
# With maxproc 1 because it's sufficient, let Postfix do all queuing.
# Both flags D and O (adding headers Delivered-To and X-Original-To) require main.cf
# minder_destination_recipient_limit=1 [3]

(d) Make the relay-to-minder script executable by the user (pipe) specified in master.cf user=. The content of the script (including the latest version of these configuration notes) is located at http://zelea.com/system/host/havoc/usr/local/libexec/relay-to-minder

The script re-addresses the destination headers (still not sure that's proper), re-addresses the envelope, and feeds it back to the MTA for final delivery.

Notes

[1] zelea.com/w/Property:Minder

[2] zelea.com/w/Category:Pipe

[3] This meaning of "pipe" differs from [2]. www.postfix.org/pipe.8.html

[4] www.postfix.org/transport.5.html | www.postfix.org/regexp_table.5.html

Pardon the munged links. I haven't earned enough brownie points.


Solution 1:

The right way to do it is create a new transport in master.cf

Something like this:

myscript unix -  n  n   -  40   pipe
flags=R user=vmail argv=/path/to/myscript -o SENDER=${sender} -m USER=${user} EXTENSION=${extension}

You can use your own parameters.

Then you can use regexp for your catch rules

transport_maps = regexp:/etc/postfix/redirect_or_forward.regexp

Then in /etc/postfix/redirect_or_forward.regexp

Something like:

 /^user-.*@mydomain\.com/   myscript:

Read on postfix docs, there should be more extensive explanation on this. Hope this helps.