The Postfix Virtual Delivery Agent ( VDA, aka virtual(8) ) is a local delivery agent written to abstract the email user from the system user. virtual(8) is a ment to handle the task of getting messages from the queue manager (qmgr) into a local mailbox. What makes virtual(8) different than local(8) is that the location of the mailbox is determined witha map lookup. The UID/GID owning the mailbox is also configured with a map. This allows the postmaster to place the mailboxes anywhere on disk they like. When combined with a modern POP3/IMAP server this can be a fantastic way to manage mailboxes.
The design of virtual(8) limits its usefulness in some cases. While local(8) can execute commands from an aliases or .forward file, virtual(8) will not do either. Since virtual(8) is run as a configurable (on a per mailbox basis) UID, it does not even attmept to enter security sensative realms such as program execution. But almost every "vacation" program out there is run from a .forward file, which implies real system users. The solution is to use other parts of postfix to get around the (good) limitations of virtual(8).
This is where the names of some of the postfix processes gets in the way. Postfix has two components named "virtual". There is virtual(5) and there is virtual(8). The VDA is virtual(8), where the (8) simply refers to the section of the manpages where this process's documentation is. The other "virtual", virtual(5) is really a map format used by the cleanup(8) daemon to rewrite the recipient address of emails as they come in. The naming is confusing but the purpose of each is clearly seperate. In the quest of a working autoresponder virtual(5) is going to help us split each incoming email into two. One will be allowed to pass to the VDA and another will be sent to a different, specially formatted address.
"What specially formatted address?" you say. Well, this is the tricky bit. We need to get the email message to an autoresponder program. That autoresponder will be explained in a moment. The most secure way to do this is through Postfix's pipe(8) transport. We can have pipe(8) run our autoresponder program and we can get message to pipe using a transport map. I told you this was tricky. Here is a little diagram of how this will work.
Message 1 comes in via STMP and gets split during cleanup into messages 2 and 3. Both are given to the queue manager which sends the message with the original recipient address off to virtual(8) for placement in the user's maildir. Message 3 is routed via a transport map to the pipe transport set to run our autoreply command. The autoreply command looks at the message and forms a new message if it decides it should reply to the message. This final message, number 4, leave postfix via SMTP (some steps are ommitted for the path of message 4, but you get the idea).
Most versions of postfix can route mail in a transport map based on the recipient's domain name (very new snapshots can do this on a per-address basis). Assuming the server is handling the email for example.com (please don't try to use this, I'm sure the firstname.lastname@example.org has better things to do), we will create a subdomain called autoreply.example.com. Now this subdomain does not have to exist in DNS, in fact it may be better that it doesn't. It only exists in our transport map and our virtual(5) rewrite table. Your transport map (normally located at /etc/postfix/transport) should look like this:
example.com virtual: autoreply.example.com autoreply:
Make sure to run postmap /etc/postfix/transport after editing this file. At this point if you email something @autoreply.example.com it will fail since we haven't defained what the autoreply transport is. Carefully add the following lines to /etc/postfix/master.cf :
autoreply unix - n n - - pipe flags=F user=nobody argv=/usr/bin/perl /usr/local/bin/autoreply.pl $sender $recipient
autoreply.pl is a perl script that uses Net::LDAP to pull the autoresponder message out of a directory server. This script is not currently available. I hope to release the source code as soon as it clears my employer's legal department. Watch this space. I will provide the disign of such a beast in another document. Here are the basics of the program. It takes the original envelope sender and the specially formatted envelop recipient on the command line. It then parses out the original envelope recipient and checks to see that an autoresponder message exists for this address. If one does then it checks a local database to see if it has already replied to this sender/recipient pair in the last 24 hours. It checks the sender against a list of known "don't reply" addresses (MAILER-DAEMON@, postmaster@, *-owner@). If all these checks pass it formats and sends a new message, copying the original subject.
As for the "specially formatted" recipient addresses, we need to take the original address and somehow change it to be @autoreply.example.com, but still contain an encoded version of the original address. I chose a very simple way of doing this. I took the original recipient address and replaced the '@' with a '#', and appended the resulting string to @autoreply.example.com. So if the address in question was email@example.com, the new recipient address would be firstname.lastname@example.org. I do this because my autoresponder can handle any number of domains. If you only have one domain, your address encoding might be as simple as email@example.com. This way to enable or disable the autoresponder for a given user, you just add or remove the specially formatted entry from the virtual table. Here is an example of what that virtual map might look like:
Since we're using both a transport map and a virtual(5) map, be sure you have the following entries in your main.cf file:
virtual_maps = hash:/etc/postfix/virtual transport_maps = hash:/etc/postfix/transport
That concludes this document. If you have questions feel free to post them to the postfix-users mailling list.
Joshua E. Warchol