From Robin's Wiki

RobinsStuff: PostfixAndMySQL

This is currently in progress. (Not anymore, everything here has been running fine for me for a long time now) It is based on this howto. Here I detail the problems I had and how I fixed them. The implementation I'm aiming for is it bit more complex than the howto allows for. As well as having MySQL driven virtual users, I want mail to be delivered for shell users like normal, and for the virtual mail to be filtered so that something like spamassassin can be used.

Note that this was written in a sequence-of-discovery type way, so the solutions I find at one point may be superseded by something later on.

Contents:

Postfix

Problem: Postfix can't talk to the MySQL socket.
Solution: By default, parts of Postfix run chrooted. This needs to be changed. Solution found here. Other options I'm going to explore at some stage: having postfix talk to MySQL via the network, and having MySQL put a socket inside the postfix chroot.
Solution2: add this in the init.d/postfix script

    start)
      echo -n "Starting mail transport agent: Postfix"
      if [ -e /var/spool/postfix/var/run/mysqld/mysqld.sock ]; then
            rm /var/spool/postfix/var/run/mysqld/mysqld.sock
      fi
      mkdir -p /var/spool/postfix/var/run/mysqld
      chown mysql /var/spool/postfix/var/run/mysqld
      ln /var/run/mysqld/mysqld.sock /var/spool/postfix/var/run/mysqld/mysqld.sock

Problem: The users in the mailbox table aren't detected by Postfix.
Solution: Turns out you can't have mydomain and myhostname the same as anything in the virtual maps. So the hostname should be set to the actual machine name to prevent this. This has the side effect that you can't deliver to local users in the 'default' postfix way. Also note that setting mydestination to a domain that is the same as in the virtual maps, then it takes precidence over the virtual map, and you only get delivery to local users using the virtual delivery code.

Problem: Can't deliver to local users if they're not listed in the virtual map. Also, if they are added to the virtual maps it means that mail won't be delivered with their permissions.
Solution: In main.cf, put:

 local_recipient_maps = proxy:unix:passwd.byname, $alias_maps,mysql:/etc/postfix/mysql_virtual_mailbox_maps.cf

to ensure that postfix knows the users exist. Then put:

 fallback_transport = virtual

so that non-local users end up with mail being sent to the maildir given in the database.

Problem: Mail for the non-local users needs to be run through procmail, however we somehow need to pass the destination maildir information that is in the database to procmail so that it knows where to put it. (Note: running local mail through procmail is easy, by using

 mailbox_command= /usr/bin/procmail -p /etc/procmailrc -a  "$EXTENSION"

where /etc/procmailrc is a default procmail setup for local users. Also, it seems that setting virtual_transport = procmail doesn't work when fallback_transport = virtual. The mail doesn't seem to hit procmail. Besides, we'd still need to get the destination maildir information to it somehow.)
Solution: Procmail won't cut it for this. It will need building a custom ruleset to shunt the mail where it needs to go. Instead, I'm using maildrop. Maildrop is like procmail, but better. Most usefully, it reads (in Debian) /etc/courier/userdb.dat (generated from /etc/courier/userdb) to allow mappings of non-existant users to the place where their mail goes. In Debian, the maildrop package won't work, it doesn't know about the userdb system. Instead, you need to use courier-maildrop. Also, it is necessary to change the home directory of the user that will be running maildrop, in my case mail, to be somewhere that isn't world writable. To tell postfix to use maildrop, simply set fallback_transport = maildrop.

Problem: Mail passing through maildrop gets delivered to /home/mail/Maildir rather than the directory specified in the mail= line in userdb.
Solution: Unfortunately this appears to be impossible, or at least, I couldn't figure it out, so I made a compromise: postfix tells maildrop the recipient, and maildrop just uses that value to work out where to put the message, based on it's own rules. This means that the information in the database or userdb as to the destination maildir is ignored, and mail will always go to a location defined by the appropriate maildroprc rule.

In Postfix, maildrop is called by the following in master.cf:

 maildrop  unix  -       n       n       -       -       pipe
   flags=DRhu user=mail argv=/usr/bin/maildrop /etc/maildroprc-virtual ${recipient}

where /etc/maildroprc-virtual is the configuration file that controls where the message ends up, based on the value of ${recipient}. In maildroprc-virtual this is:

 DEFAULT="/var/spool/virtualmail/$1/"

additional filtering rules can be placed after this as needed. It may be possible to allow lookups to be done in the filter rules in order to control the destination of the message. This is something to look into later. Another thing is that we need to ensure that the destination maildir exists, otherwise maildrop gets quite unhappy. A nice way of doing this is to have maildrop create non-existing maildirs itself, that way we don't need any external intervention. That can be done with the following addition near the top of the filter rule:

 # If the destination maildir doesn't exist, create it.
 `[ -d $DEFAULT ] || (maildirmake $DEFAULT && maildirmake -f Spam $DEFAULT)`

this also sets up a Spam folder, which will be used below.

Problem: The above may not work sometimes. If you get this error from postfix:

 
relay=maildrop, delay=1,status=deferred (temporary failure. Command 
output: /usr/bin/maildrop: Unable to create a dot-lock. )

it can indicate that the destination maildir doesn't exist.
Solution: A possible cause is that the SHELL environment isn't being set, so the backtick evaluation above doesn't happen. To fix, add this line to the top of maildroprc:

 SHELL=/bin/bash

(Thanks to Daniele Palumbo for this pointer)

Problem: We want SpamAssassin to be run over all the messages that pass through this.
Solution: This is easily done with maildrop's filtering. Add the following to the maildroprc (or maildroprc-virtual):

 
if ( $SIZE < 26144 )
 {
     exception {
        xfilter "/usr/bin/spamc"
     }
 }

 if (/^X-Spam-Flag: *YES/)
 {
     exception {
         to "$DEFAULT/.Spam/"
     }
 }
 else
 {
     exception {
         to "/$DEFAULT"
     }
 }

also ensure that the destination Spam folder does exist, using the maildirmake command. If this isn't the case, the message is delivered to the default maildir.

With this, the incoming mail side of things appears to be working fine. For the interested, my postconf -n output is:

 $  postconf -n
command_directory = /usr/sbin
command_time_limit = 10000
config_directory = /etc/postfix
debug_peer_list = 127.0.0.1
fallback_transport = maildrop
inet_interfaces = all
ipc_timeout = 13600
local_recipient_maps = proxy:unix:passwd.byname, $alias_maps, mysql:/etc/postfix/mysql_virtual_mailbox_maps.cf
mail_owner = postfix
mail_spool_directory = /var/spool/mail/
mailbox_command = /usr/bin/procmail -p /etc/procmailrc -a "$EXTENSION"
mailq_path = /usr/bin/mailq
manpage_directory = /usr/man
mydestination = kallisti.2y.net, localhost.$mydomain, kallisti.net.nz, kallisti.hopto.org, www.kallisti.net.nz
myhostname = agrajag.kallisti.net.nz
mynetworks = 192.168.0.0/16,127.0.0.0/8
myorigin = $mydomain
newaliases_path = /usr/bin/newaliases
queue_directory = /var/spool/postfix
relay_domains = $mydestination
sample_directory = /etc/postfix
sendmail_path = /usr/sbin/sendmail
setgid_group = postdrop
smtpd_banner = $myhostname ESMTP $mail_name (Commodore Vic-20)
soft_bounce = no
unknown_local_recipient_reject_code = 550
virtual_alias_maps = mysql:/etc/postfix/mysql_virtual_alias_maps.cf
virtual_gid_maps = static:105
virtual_mailbox_base = /var/spool/virtualmail
virtual_mailbox_domains = mysql:/etc/postfix/mysql_virtual_domains_maps.cf
virtual_mailbox_limit = 51200000
virtual_mailbox_maps = mysql:/etc/postfix/mysql_virtual_mailbox_maps.cf
virtual_minimum_uid = 100
virtual_transport = procmail
virtual_uid_maps = static:104

Courier

Making Courier work with this setup was very smooth. The information in the howto is all that was needed. There are a few minor things that I had to change:


Administration

Having all this set up and working is nice, but you also need some way of controlling it. To this end, I wrote up a few PHP scripts that can be used to administer the system. They are fairly primitive, but should be totally functional. They are also likely to undergo a lot of tweaking in the next while, so I'd recommend accessing them via Subversion. The Subversion URL is https://www.kallisti.net.nz/svn/mailadmin (you'll likely want the trunk subdirectory of this). It can also be browsed in a pretty online format. Details on its usage are located in the readme file.

Retrieved from http://www.kallisti.net.nz/RobinsStuff/PostfixAndMySQL
Page last modified on April 16, 2008, at 11:06 AM