Setting up Postfix + MySQL + Courier-IMAP + Mailman + vmail + Procmail + SpamAssassin

Original document by Probsd.net, rewritten and extended by Mitsu Hadeishi

I just finished setting up Postfix + MySQL + Courier-IMAP + Mailman + vmail + Procmail + SpamAssassin. This combination will give you email sending, receiving, and picking up via IMAP, POP3 or POP3+SSL, true virtual domains and email accounts, mailing lists with a web interface, and a simple web interface for creating virtual domains and email accounts. These instructions should work with Fedora Core 1 and Red Hat 9, and I hope these instructions will also be helpful for people with other distributions. This document is based on the vmail setup document, but I have added much more detail as well as specific instructions for Red Hat-based distributions, as well as explanations of the structure of the mail system and some information on Mailman, Procmail, and SpamAssassin (more to come later).


Basic defintions. Postfix is a mail transfer agent which handles SMTP (for sending and receiving email). Courier-IMAP is the IMAP and POP3 component (for retrieving email) of the Courier mail system, which can be configured to work with other mail transfer agents (like Postfix). Procmail is used to actually deliver the email, and passes the email through SpamAssassin. SpamAssassin is a spam email filter. Mailman is a mailing list system with a web interface for configuring the mailing lists (but basic configuration must be done by hand), and vmail is a simple PHP-based web interface for setting up virtual domains and virtual domain-based email accounts. MySQL is used to store authentication information and virtual domain account information.

To get all this software to work together, we have to configure them to use a compatible format for storing/delivering mail (when Postfix receives mail it needs to store it in a form that Courier-IMAP understands, so users can retrieve their email via POP3 or IMAP, and it should send it to Procmail for the actual delivery), authentication (Courier-IMAP needs to know how to check user passwords when users retrieve their email), administration (vmail needs to be configured to store user and virtual domain information in a way that Postfix and Courier-IMAP can understand), spam filtering (Procmail needs to filter email through SpamAssassin) and since Mailman also creates email aliases, we also need to configure Postfix and Mailman so they are using a compatible format for these aliases (Mailman creates its own aliases, and Postfix needs to know how to read these aliases).

For in depth information about using the postfix virtual agent check out this howto.

We assume a base directory of /usr/local/vmail, user "vmail", database user "vmail" here, use whatever values you want, these are only examples, but be sure to put the right settings in the php and perl scripts.

Make a user

Our user "vmail" will own all the mail directories, run vipw (or use whatever administrative tool you like) and add it:
  Note: change uid:gid to whatever free uid and
    gid are available for the vmail account.
    Note the uid and gid for later reference.
Add a group for vmail and a mail alias for vmail to root or some admin account, so you get an email if the maintainence cron script spits out an error.


A few notes about Postfix. You cannot use the prebuilt rpm for Postfix that comes with Red Hat 9 or Fedora Core 1 because it does not support mysql. Therefore you must compile Postfix from source, making sure you use the --with-mysql option. Generic instructions on building rpms from source rpms (SRPMS), and some instructions on building Postfix from an srpm (however note those instructions are designed for Cyrus-SASL, not Courier-IMAP, so make sure you use the --with-mysql option). Alternately, if you want the latest version, download the Postfix source and build it (again with the --with-mysql option, plus any others you might like).

IMPORTANT: For security reasons it is a good idea to configure Postfix to run chrooted, which means that certain Postfix processes (like the SMTP server) will only be able to see files underneath /var/spool/postfix. For instructions on configuring Postfix in a chrooted environment, read the INSTALL document in the Postfix source directory. The problem is when you run Postfix chrooted, it will not be able to access the /var/lib/mysql/mysql.sock file to talk to MySQL on localhost, since that file is outside of the chroot jail. Symlinks to files outside the chroot jail will also fail. You can create a hard link to the socket, but that link will be invalidated whenever you restart mysqld. You can do two things to get around this: either re-create the hard link by editing the mysqld startup script and adding the link command there (/etc/rc.d/init.d/mysqld), which seems a bit kludgy to me, or, a better solution is to specify as the host name to pfsqlfiles (see below), instead of localhost, which will force Postfix to use TCP to talk with MySQL. Note that your mysqld server must be configured to accept TCP connections on the standard port (at least from the local machine) --- this is the default so you probably don't need to do anything special here.

The pfsqlfiles script will make the mysql info files for postfix, to save some annoying typing. It will prompt for database info (remember to use and write the files to the current directory.

  # cd /etc/postfix; mkdir sql; cd sql;
  # /usr/local/vmail/scripts/pfsqlfiles

Set permissions on the files so the postfix user can read them and others can't access them.

Put the following in your main.cf:

  virtual_mailbox_base = /usr/local/vmail
  virtual_minimum_uid = 500 <== the minimum uid that can be returned
    from a virtual_uid_maps lookup, 500 is probably fine
  virtual_mailbox_maps = mysql:/etc/postfix/sql/vmailbox

  # the virtual-mailman.db file must exist or Postfix will fail
  # when receiving mail; be sure Mailman has generated this
  # file: manually generate a dummy virtual-mailman.db file
  # with the postmap command, or remove virtual-mailman.db
  # from the virtual_maps list until Mailman generates the file

  virtual_maps = mysql:/etc/postfix/sql/virtual,
  transport_maps = mysql:/etc/postfix/sql/transport

  # change to the uid of the vmail user
  virtual_uid_maps = static:uid

  # change to the gid of the vmail group
  virtual_gid_maps = static:gid
  local_recipient_maps = unix:passwd.byname $alias_maps $virtual_mailbox_maps
  fallback_transport = virtual
  relay_domains = $transport_maps
Some notes:
virtual_mailbox_base: Used to determine under which directory the virtual domain mailboxes will be stored.
virtual_maps: The first map refers to the database which will contain virtual aliases information, and the second refers to aliases created by the Mailman mailing list manager (see below)

Also, edit alias_maps to look like this:

  alias_maps = hash:/etc/postfix/aliases, hash:/usr/local/mailman/data/aliases
Again, this allows Mailman to modify the aliases for Postfix.

You also need to edit various parameters in /etc/postfix/main.cf. Some of the parameters below are needed, others are just some possible recommendations --- please look at main.cf and edit these parameters carefully. Note that some of these parameters are not found in main.cf, but are rather defined in main.cf.default --- do NOT edit them in main.cf.default, rather copy them from main.cf.default to main.cf and edit them there (or simply insert these lines into main.cf):

  myhostname = full_hostname <== i.e., host.mydomain.com
  mydomain = domainname <== i.e., mydomain.com
  myorigin = $myhostname <== This sets the default origin
    domain for email coming from this server directly. You can also
    set this to $mydomain.  Since I use $mydomain as a virtual
    email domain, I set this to $myhostname to avoid collisions
  inet_interfaces = all <== listen to all interfaces on this machine
  mydestination = $myhostname, $mydomain, virtual_domain1,
    virtual_domain2, ... <== list out all virtual domains on
    this email server
  local_recipient_maps = unix:passwd.byname $alias_maps
    $virtual_mailbox_maps <= list out aliases and virtual mailboxes
  unknown_local_recipient_reject_code = 550 <== set to 550 after
    everything is working, set to 450 (try again later) while debugging
  mynetworks_style = host <== set to host if you only trust
    the local machine for accepting mail for relaying, etc.,
    without doing additional checks
  home_mailbox = Maildir/ <== this is necessary! so Courier-IMAP can
    read the mailboxes (Courier-IMAP does not support mbox format)
  mailbox_command = /usr/bin/procmail <== Procmail does the
    delivery and filtering via SpamAssassin
  disable_vrfy_command = yes <== disables verification that a user
    is valid, for security reasons
  smtpd_recipient_restrictions = permit_mynetworks,
    reject_unauth_destination, reject_unknown_client <== helps to
    secure your SMTP server
  owner_request_special = no <== this is needed by Mailman
Note that you can find log file messages from Postfix in /var/log/maillog (Courier-IMAP places log messages here as well).


First, we create and populate the database, then create a vmail user to access the database (choose your own password):
  mysql -u root -p < vmail.sql

  mysql> GRANT select, insert, update, delete
  ON vmail.* TO vmail@localhost IDENTIFIED BY 'password';
  mysql> GRANT select, insert, update, delete
  ON vmail.* TO vmail@hostname IDENTIFIED BY 'password';
NOTE: The first GRANT is for processes that are accessing the database via the localhost /var/lib/mysql/mysql.sock socket directly (this includes the vmail PHP scripts), and the second is for processes (like Postfix inside a chroot jail) to access the database via a TCP socket. In place of hostname, use the domain name for the server computer (that seems to be what Postfix uses when logging in).

Debugging MySQL problems: You may wish to start mysqld with the log option turned on so you can debug common problems with configuration. To do this, edit the /etc/rc.d/init.d/mysqld script file and add --log=/var/log/mysql.log as the last argument to /usr/bin/safe_mysqld and restart mysqld with "service mysqld restart". You will then be able to track problems with logins or permissions by looking at /var/log/mysql.log. Be sure to take the log option out once you have everything working, because logging will slow down the performance of the MySQL database.


Note that vmail requires Apache and PHP be installed already (you can use the standard Red Hat/Fedora rpms for this' purpose). Download vmail and copy the php scripts into their directory (we're assuming /usr/local/vmail in this document), copy php/config.php.dist to php/config.php and edit:
$server = 'localhost';
$user = 'vmail';
$pass = 'password'; // <== database password you chose above
$db = 'vmail';

// vmail home dir (set this)
$vmailbase = '/usr/local/vmail';

// vmail uid, gid
$uid = uid; // <== set this to the uid of the vmail user
$gid = gid; // <== set this to the gid of the vmail group

// main admins email address
$admin = 'admin_email_address'; // <== send to a real person

// dir name for admin login (http://url.to/vmail/admindir/)
$admindir = 'admin';

Install the perl scripts maintain and vmadmin in the base vmail directory and make sure to set the options in the top of the scripts. Make sure to set secure permissions on config.php and perl scripts containing the db password.

The maintain script handles deleting maildirs on deleted accounts, renaming maildirs, etc...
I add an /etc/crontab entry to run it as the vmail user:

*/5	*	*	*	*	vmail	/usr/local/vmail/maintain

admin setup

Add an admin account for the web interface:
	# vmadmin +admin

Now you can browse to http://your.url.to/vmail/admin/ to login as the superuser and add a domain. Your base url will be the normal login for postmasters.

You can also use the vmadmin script to edit things on the command line, but its not quite polished yet. make sure to su to the vmail user to run it.

The web interface is pretty self explainitory, just take a look around...


Unfortunately there is no pre-configured package for Red Hat for Courier-IMAP, so you have to download and build it from source. When you do this, you'll notice that the source package doesn't like to compile and install directly, but rather tries to get you to build an RPM file, which you then install via rpm -ivh. Note that the instructions on the Courier-IMAP web page do not mention this. Basically, download Courier-IMAP, copy to a normal (non-root) user folder, become that user, unpack the file and build Courier-IMAP as an rpm file following the directions in the INSTALL file. Remember to configure Courier-IMAP with the following flags:
  --with-authmysql --with-mysql-libs=/usr/local/lib/mysql/ \
Notes for FreeBSD users: The courier-imap port in freebsd is kinda messed up with regards to getting the mysql auth stuff to compile... find ".if !defined(WITH_MYSQL)" in the Makefile and change this:

to this:

    --with-authmysql --with-mysql-libs=/usr/local/lib/mysql/ \

Add --with-authpwd to CONFIGURE_ARGS if you're going to be serving regular system accounts also.
Then you can make -DWITH_MYSQL and courier will compile with authmysql correctly.

Consult the docs for courier-imap to get it set up and use these settings in your authmysqlrc (the default location is /usr/lib/courier-imap/etc/authmysqlrc). These settings tell Courier-IMAP which database, table, and field names to use for accessing virtual domain mailbox information:

  MYSQL_SERVER            localhost
  MYSQL_USERNAME          vmail
  MYSQL_PASSWORD          password <== database password
  MYSQL_SOCKET            /var/lib/mysql/mysql.sock <== or whereever
    mysql.sock is on your system, sometimes /tmp/mysql.sock
    or /var/tmp/mysql.sock
  MYSQL_PORT              3306 <== you don't necessarily need to
    set this since it will use mysql.sock
  MYSQL_DATABASE          vmail
  MYSQL_USER_TABLE        passwd
  MYSQL_NAME_FIELD        gecos

Authdaemond also, by default, just chooses the first authentication module it finds in the /usr/lib/courier-imap/libexec/authlib directory. You can either move all the authdaemond.* files out of that directory except for authdaemond.mysql, or set AUTHDAEMOND="authdaemond.mysql" in the /usr/lib/courier-imap/etc/authdaemonrc file.


If you installed Postfix from an rpm compiled from an SRPM, you should be able to install the Mailman rpm from a Red Hat or Fedora repository. Otherwise you have to download Mailman and build it from source. Once you've installed Mailman, follow these instructions for configuring Mailman for Postfix. Note that some of the things you need to do for Postfix I've already included in the above instructions, but you will need to modify the Mailman part of the instructions.


Make sure Procmail is installed; the standard Red Hat procmail rpm should work fine.


You should be able to use the SpamAssassin rpm from a Red Hat repository. I have not yet tested this extensively. This page discusses SpamAssassin/Procmail setup. I have found this interesting SpamAssassin configuration generator which I haven't yet tried out. I will post more here as I learn to use the configuration.