March 18, 2003

Virtual mail in Postfix

[Edited 2003-03-24: added vmail_destination_recipient_limit to]

[Edited 2003-03-19: change postfix-virtual-tools to a directory link. Just grab the latest version out of there. I'm going to be changing this package a lot in the coming days.]

Wouldn’t it be nice if I could make an entry at least once a week? I need to get back on my old schedule.

So here’s my quick run down on what I did to get a “POP toaster” kind of setup with Postfix+userdb+Courier-IMAP. Note that while this seems to work, and I have tested it a bit, and just put it in to production for one customer, it hasn’t been tested super extensively. In addition, I might be circumventing some Postfix security checks here, if not opening up some new ones on my own. Proceed with caution, use at your own risk, blah blah blah.

I’m on RH 8.0, so I install their postfix RPM. Grab maildrop and run rpmbuild -ta on the tarball. (rpmbuild -ta magically peeks inside a tarball for a .spec file and then attempts to build a package with that spec file.) This produces a maildrop package. Two patches you might want to apply to maildrop, both written by me so use at your own risk:

  • My lower case account name lookup patch to maildrop. The problem: if you have an account name bob, and someone sends e-mail to an account named Bob, (note the uppercase) maildrop rejects the e-mail. You can think of it as being case sensitive about e-mail addresses. Solution: if initial lookup in userdb fails for a given account name, we try converting that account name to all lowercase and try to look that up. I’ve been using this patch in production for a year or so. It’s not perfect by a long shot, but it works for me, and if you’ve got a similar setup to what I’m describing here it’ll probably work for you too.

  • My EX_NOUSER patch for maildrop. The problem: the author of maildrop apparently thinks the only error maildrop should ever return is EX_TEMPFAIL (see /usr/include/sysexits.h), even under errors which should probably be fatal, such as an attempt to deliver to a user that doesn’t exist. I find this kind of bizarre given that we’ve got all these nice status codes we can return, like EX_NOUSER when a user isn’t found. I think the author of maildrop thinks the job of deciding why an error occurred should be left up to the MTA, for some reason. In any case EX_TEMPFAIL will cause Postfix to defer instead of bouncing immediately, and Postfix will have no way to check if a message is attempting to deliver to a non-existent user. This patch causes maildrop to return EX_NOUSER when you attempt to deliver to a user that doesn’t exist.

In the end, here’s my spec file for maildrop. Grab this, stick the maildrop tarball in SOURCES, and rpmbuild -ba your way to happiness.

So far we’ve got Postfix and maildrop installed. Go ahead and set up Postfix for local delivery, if you’d like. I think Postfix will actually work without reconfiguration (except perhaps the root alias in /etc/postfix/aliases) but feel free to configure for local delivery if you’d like; note that we modify mydestination below. Here’s what I use for my maildrop local delivery in /etc/postfix/

mailbox_command = /usr/bin/maildrop -d "$USER" -f "$SENDER" "$EXTENSION"

The changes to /etc/postfix/ that our virtual setup is going to require are the following lines at the end:

transport_maps = hash:/etc/postfix/transport
mydestination = $myhostname, localhost.$mydomain, hash:/etc/postfix/transport
vmail_destination_recipient_limit = 1

Note that you need to comment out any mydestination value in other than the one given above. The first line says that a per-destination transport can be selected from the map /etc/postfix/transport, a file that already exists (though it has no mappings yet) in the RH8 RPM. The second line tells Postfix to receive mail for our host name, localhost, and any domains that are going to be listed in our /etc/postfix/transport. The third line instructs Postfix (really pipe(8)) that maildrop can only deliver to one recipient at a time. Without the third line, a message addressed to multiple recipients would be called like maildrop -d, which maildrop won’t accept.

Next, we have to modify /etc/postfix/ I get the feeling that normal people don’t have any good reason to modify this, but pretend you’re super-human for a bit and add the following two lines, indentation and all, at the end of

vmail     unix  -       n       n       -       -       pipe
  flags=Fu user=vmail argv=/usr/bin/maildrop -d $recipient -f $sender $extension

You might have noticed that this line is suspiciously similar to the mailbox_command we set above. It’s basically identical, in fact. These two lines set up a new transport named vmail. It will always be run as the user vmail (hence user=vmail) and with the user vmail‘s primary group ID. We’re going to create the vmail user right after we’re done reconfiguring Postfix.

The final change to make is to add all your virtual domains to /etc/postfix/transport. Here’s an example:	vmail:

Note the vmail: at the end of the line, which means “use the vmail transport for any mail addressed to the domain Once you’re done don’t forget to run postmap /etc/postfix/transport to hash it for Postfix.

Now to create our vmail user. In RH8, I recommend the invocation of:

useradd -r -d /var/spool/vmail -s /bin/false -M vmail

This creates a system user (UID /var/spool/vmail, and a shell of /bin/false which should keep it from logging in successfully. Well, that and the fact that the account never gets a password set on it, and so remains forever locked. You’ll need to create /var/spool/vmail and then /var/spool/vmail/domains after this. chown both directories to vmail:vmail. Under /var/spool/vmail/domains is where all your mail is going to be spooled. Then under /var/spool/vmail/domains create a directory for each domain you want to host. Make sure these directories are also owned by vmail:vmail. You probably also want ~vmail (/var/spool/vmail) to have permission 0770, so any local users won’t go poking around where they’re not wanted. This is a good permission value for most every directory you’re going to create under ~vmail.

At this point you’re pretty much done, or close at least. Go grab some Courier-IMAP RPMs, or maybe you have to rpmbuild -ta the tarball again — I don’t remember. I’m sure if you’ve gotten this far you’ll be able to handle it. The nice thing about Courier-IMAP in this setup is that we basically have to do absolutely nothing for it to work correctly. Courier-IMAP will automatically look to userdb (/etc/userdb*) to authenticate users and such. In my setup I wanted to keep Courier-IMAP from authenticating any local users, so I took authpam out of the authmodulelist variable in /usr/lib/courier-imap/etc/authdaemonrc. (authdaemonrc was probably copied by me from authdaemonrc.dist which is in the same directory, I believe.)

Now time to start it all up and test. First service postfix restart, then service courier-imap restart. Now hope for the best.

The one thing that pushed me towards this setup as opposed to any Postfix solutions was that I’d have a single source for user information: userdb. That all said and done, at some point I might generate a map from /etc/userdb so that Postfix can reject mail to unknown users without accepting it in the first place: no bouncing!

If you’d like to add users and such, you can check out my quite immature postfix-virtual-tools package. This has had very minimal testing, and no documentation basically. (Config::PerlConfiguration was sucked brutally out of my floundering DarkWiki project, BTW.)

I’ve been getting steadily more drowsy, so I’m going to drop off now. Please let me know if you have any questions. (I’m really going to respond to you people that have written me. I promise.)

Comments are closed.