Virtual Users and Domains with Courier-IMAP and MySQL

From postfix

Jump to: navigation, search

Contents

Introduction

This document is written for Postfix 2.0 and higher.

This document describes how to setup Virtual Domains (Aliases and Mailboxes) with Postfix, Courier-IMAP (or dovecot imap) and MySQL. I have found that this is the easiest combination that allows you to serve Virtual Domains, and Users. With this it's also very easy to implement webmail systems like SquirrelMail.

NOTE: One thing that you have to keep in mind is that Courier-IMAP only supports the Maildir format.

About the used software:

Postfix attempts to be fast, easy to administer, and secure, while at the same time being sendmail compatible enough to not upset existing users. Thus, the outside has a sendmail-ish flavor, but the inside is completely different.

Courier-IMAP is a server that provides IMAP access to Maildirs. This IMAP server does NOT handle traditional mailbox files (/var/spool/mail, and derivatives), it was written for the specific purpose of providing IMAP access to Maildirs.

The MySQL database server is the world's most popular open source database. Its architecture makes it extremely fast and easy to customize. Extensive reuse of code within the software and a minimalistic approach to producing functionally-rich features has resulted in a database management system unmatched in speed, compactness, stability and ease of deployment. The unique separation of the core server from the table handler makes it possible to run with strict transaction control or with ultra-fast transactionless disk access, whichever is most appropriate for the situation.

SASL is the Simple Authentication and Security Layer, a method for adding authentication support to connection-based protocols. To use SASL, a protocol includes a command for identifying and authenticating a user to a server and for optionally negotiating protection of subsequent protocol interactions. If its use is negotiated, a security layer is inserted between the protocol and the connection.

Postfix VDA enables quota support for Postfix.

Dovecot [1] Is an alternative IMAP server, known to perform a lot better than courier, especially with clients like thunderbird and mail.app. It is also slightly easier to configure, and works well with the virtual users setup.

If you are planning to use this howto as a basis for Postfix Admin, please be aware that there is some differences in the tables.

Please read the TABLE_CHANGES.TXT

Disclaimer

This document assumes that you have some knowledge on Postfix, Courier-IMAP, MySQL and SASL. At least enough to get everything installed. Installing the software is outside the scope of this document.

MySQL Install

Installation of MySQL is outside the scope of this document. I'm using an out of the box MySQL install on FreeBSD.

Since a long time, in Slackware you should do this (as root):

% su mysql && mysql_install_db && exit 
% sh /etc/rc.d/rc.mysqld start \
&& mysql_secure_installation && mysqld_safe --user=mysql& \
&& mysqladmin -u root password 'your password'

MySQL Setup

Create the database

% mysqladmin -u root --password='<your password>' create postfix

These columns are used to make your life easier together with Postfix Admin:

  • created
  • modified
  • active

The "active" column is not used at the moment.

Create the Alias table

#
# Table structure for table alias
#
USE postfix;
CREATE TABLE `alias` (
  `address` varchar(255) NOT NULL default '',
  `goto` text NOT NULL,
  `domain` varchar(255) NOT NULL default '',
  `created` datetime NOT NULL default '0000-00-00 00:00:00',
  `modified` datetime NOT NULL default '0000-00-00 00:00:00',
  `active` tinyint(1) NOT NULL default '1',
  PRIMARY KEY  (address)
) TYPE=MyISAM COMMENT='Postfix Admin - Virtual Aliases';

Postfix: is using the "address" and "goto" column. Courier: is not using this table.

NOTE: This table can be used for virtual .forward files. This table is nothing more than /etc/aliases that you will find on any *nix OS. Multiple destination email addresses need to be separated by a "," (comma).

Create the Domain table

#
# Table structure for table domain
#
USE postfix;
CREATE TABLE `domain` (
  `domain` varchar(255) NOT NULL default '',
  `description` varchar(255) NOT NULL default '',
  `aliases` int(10) NOT NULL default '0',
  `mailboxes` int(10) NOT NULL default '0',
  `maxquota` int(10) NOT NULL default '0',
  `transport` varchar(255) default NULL,
  `backupmx` tinyint(1) NOT NULL default '0',
  `created` datetime NOT NULL default '0000-00-00 00:00:00',
  `modified` datetime NOT NULL default '0000-00-00 00:00:00',
  `active` tinyint(1) NOT NULL default '1',
  PRIMARY KEY  (domain)
) TYPE=MyISAM COMMENT='Postfix Admin - Virtual Domains';


Postfix: is using the "domain" and "description" column. Courier: is not using this table.

Create the Mailbox table

#
# Table structure for table mailbox
#
USE postfix;
CREATE TABLE `mailbox` (
  `username` varchar(255) NOT NULL default '',
  `password` varchar(255) NOT NULL default '',
  `name` varchar(255) NOT NULL default '',
  `maildir` varchar(255) NOT NULL default '',
  `quota` int(10) NOT NULL default '0',
  `domain` varchar(255) NOT NULL default '',
  `created` datetime NOT NULL default '0000-00-00 00:00:00',
  `modified` datetime NOT NULL default '0000-00-00 00:00:00',
  `active` tinyint(1) NOT NULL default '1',
  PRIMARY KEY  (`username`)
) TYPE=MyISAM COMMENT='Postfix Admin - Virtual Mailboxes';

Postfix: is using the "username" and "maildir" column. Courier: is using the "username, "password", "name" and "maildir" column.

Populate the tables

USE postfix;
INSERT INTO domain (domain,description) VALUES ('domain.tld','Test Domain');
INSERT INTO alias (address,goto) VALUES ('alias@domain.tld', 'user@domain.tld');
INSERT INTO mailbox (username,password,name,maildir)  VALUES ('user@domain.tld','$1$caea3837$gPafod/Do/8Jj5M9HehhM.','Mailbox User','user@domain.tld/');

The password (MD5 encrypted) is "secret" ($1$caea3837$gPafod/Do/8Jj5M9HehhM.)

This is a "standard" MD5 encrypted password out of /etc/passwd.

The first INSERT is to let Postfix know that this domain is a virtual domain and should be handled by Postfix. It's also possible to have everything in one table but I think this is nicer.

The second INSERT is a virtual alias pointing to the third INSERT.

The third INSERT is an actual Virtual Mailbox, as you can see I'm using MD5 password for backwards compatibility with local defined mail accounts. If you are using MD5 passwords, make sure you don't use the built in MySQL routine to generate MD5 passwords. This is not compatible with Courier-IMAP. If you want you can also use clear text or encrypted passwords.

To make sure that the new MySQL users are working, do the following from the command line.

% mysqladmin -u root --password='sua_senha, your password' reload

Postfix Install

Build and install Postfix 2.x, or the latest snapshot. Make sure that you at least build it with MySQL & Postfix VDA (quota). Apply patch like this:

% zcat postfix-x.x.x-vda.patch.gz | patch -p0

I built everything in FreeBSD and the default location is /usr/local/etc/postfix. Your configuration might be different.

RedHat 9 (including SASL):

% make makefiles 'CCARGS=-DHAS_MYSQL -I/usr/include/mysql -DUSE_SASL_AUTH -I/usr/include/sasl' \
'AUXLIBS=-L/usr/lib/mysql -lmysqlclient -lz -lm -L/usr/lib -lsasl'

% make && make install;

Slackware 10 (need cyrus-sasl-mysql from LinuxPackages): (read Note below first)

% make makefiles 'CCARGS=-DHAS_MYSQL -I/usr/include/mysql -DUSE_SASL_AUTH -I/usr/include/sasl' \
'AUXLIBS=-L/usr/lib/mysql -lmysqlclient -lz -lm -L/usr/lib -lsasl2'
% make && make install;

Note: Starting with Postfix's snapshot 20051220 (and so if you are compiling the latest version), you should change the above command to:

% make makefiles 'CCARGS=-DHAS_MYSQL -I/usr/include/mysql -DUSE_CYRUS_SASL -DUSE_SASL_AUTH -I/usr/include/sasl' \
'AUXLIBS=-L/usr/lib/mysql -lmysqlclient -lz -lm -L/usr/lib -lsasl2'

The error message in your postfix logs for not doing so is: unsupported SASL server implementation: cyrus

After that you have to create a directory to have all your virtual users mail dropped in, this directory needs to be owned by Postfix.

% mkdir /usr/local/virtual
% chown -R postfix:postfix /usr/local/virtual
% chmod -R 771 /usr/local/virtual

Postfix Setup

main.cf

The example below is the part that goes into your main.cf file of Postfix. The path to the mysql files might be different on your setup. The same might be for uid_maps, gid_maps and minimum_uid values. These values should *NOT* be the ones from the postfix user and group. Create a restricted user/group for this purpose.

virtual_alias_maps = mysql:/usr/local/etc/postfix/mysql_virtual_alias_maps.cf
virtual_gid_maps = static:1001
virtual_mailbox_base = /usr/local/virtual
virtual_mailbox_domains = mysql:/usr/local/etc/postfix/mysql_virtual_domains_maps.cf
virtual_mailbox_limit = 51200000
virtual_mailbox_maps = mysql:/usr/local/etc/postfix/mysql_virtual_mailbox_maps.cf
virtual_minimum_uid = 1001
virtual_transport = virtual
virtual_uid_maps = static:1001
# Additional for quota support
virtual_create_maildirsize = yes
virtual_mailbox_extended = yes
virtual_mailbox_limit_maps = mysql:/usr/local/etc/postfix/mysql_virtual_mailbox_limit_maps.cf
virtual_mailbox_limit_override = yes
virtual_maildir_limit_message = Sorry, the user's maildir has overdrawn his diskspace quota, please try again later.
virtual_overquota_bounce = yes

If you want to use MySQL also to store your Backup MX domains add this as well

relay_domains = mysql:/usr/local/etc/postfix/mysql_relay_domains_maps.cf

Performance and reliability under high load will be much improved if you use the Postfix proxymap service with your MySQL interface. This allows MySQL query connections to be shared among Postfix smtpd processes; without it, you will need much higher-end database hardware as Postfix will need to spawn a number of SQL connections for every smtpd or cleanup process. This problem typically only shows up under high load, just when you least want to see it.

To access MySQL via proxymap, change the MySQL maps lines above to read:

virtual_alias_maps = proxy:mysql:/usr/local/etc/postfix/mysql_virtual_alias_maps.cf
virtual_mailbox_domains = proxy:mysql:/usr/local/etc/postfix/mysql_virtual_domains_maps.cf
virtual_mailbox_maps = proxy:mysql:/usr/local/etc/postfix/mysql_virtual_mailbox_maps.cf

and

relay_domains = proxy:mysql:/usr/local/etc/postfix/mysql_relay_domains_maps.cf

To proxy your virtual_mailbox_limit_maps queries, you must add the map to the proxy_read_maps variable, as that map gets added via the VDA quota patch and is not included in the list of maps which Postfix will automatically allow to be proxied:

virtual_mailbox_limit_maps = proxy:mysql:/usr/local/etc/postfix/mysql_virtual_mailbox_limit_maps.cf
proxy_read_maps = $local_recipient_maps $mydestination $virtual_alias_maps
  $virtual_alias_domains $virtual_mailbox_maps $virtual_mailbox_domains
  $relay_recipient_maps $relay_domains $canonical_maps $sender_canonical_maps
  $recipient_canonical_maps $relocated_maps $transport_maps $mynetworks
  $virtual_mailbox_limit_maps

master.cf

If you plan to run your postfix chrooted, be careful not to run virtual chrooted.

If virtual is running chrooted your mysql_*.cf will not be found by postfix.
Example for such an error:

postfix/virtual[8431]: fatal: open /etc/postfix/mysql_virtual_mailbox_maps.cf: No such file or directory

NOTE: for SuSE Systems have a look at #SuSE_Linux_note

# ==========================================================================
# service type  private unpriv  chroot  wakeup  maxproc command + args
#               (yes)   (yes)   (yes)   (never) (100)
# ==========================================================================
smtp      inet  n       -       y       -       -       smtpd
[...]
virtual   unix  -       n       n       -       -       virtual
[...]

mysql_virtual_alias_maps.cf

You will need to put this into a text file for postfix to pickup.

user = postfix
password = postfix
hosts = localhost
dbname = postfix
table = alias
select_field = goto
where_field = address

#Syntax with postfix 2.2.x:
user = postfix
password = postfix
hosts = localhost
dbname = postfix
query = SELECT goto FROM alias WHERE address='%s' AND active = 1

mysql_virtual_domains_maps.cf

You will need to put this into a text file for postfix to pickup.

user = postfix
password = postfix
hosts = localhost
dbname = postfix
table = domain
select_field = domain
where_field = domain
#additional_conditions = and backupmx = '0' and active = '1'

#Syntax with postfix 2.2.x:
user = postfix
password = postfix
hosts = localhost
dbname = postfix
query = SELECT domain FROM domain WHERE domain='%s'
#optional query to use when relaying for backup MX
#query = SELECT domain FROM domain WHERE domain='%s' and backupmx = '0' and active = '1'

mysql_virtual_mailbox_maps.cf

You will need to put this into a text file for postfix to pickup.

user = postfix
password = postfix
hosts = localhost
dbname = postfix
table = mailbox
select_field = maildir
where_field = username
#additional_conditions = and active = '1'

#Syntax with postfix 2.2.x:
user = postfix
password = postfix
hosts = localhost
dbname = postfix
query = SELECT maildir FROM mailbox WHERE username='%s' AND active = 1

NOTE: In the 2.2.x syntax above, postfix will use whatever it receives as the "maildir" argument to create the users maildir in the base directory you specified in your main.cf (i.e. /usr/local/virtual). For example, if you create user@domain.tld, his maildir will be /usr/local/virtual/user@domain.tld. Then when you create user@otherdomain.tld, his maildir will be /usr/local/virtual/user@otherdomain.tld... in other words, all user mailboxes will be in the same main directory, and not segregated into individual domain folders. If you'd like to create folders for each of your domains for the user boxes to be created under, you can modify the the 2.2.x syntax to be:

query = SELECT CONCAT(domain,'/',maildir) FROM mailbox WHERE username='%s' AND active = 1

This will create domain folders for each domain you host, and your users mailboxes will be created under those folders.

mysql_virtual_mailbox_limit_maps.cf

You will need to put this into a text file for postfix to pickup.

user = postfix
password = postfix
hosts = localhost
dbname = postfix
table = mailbox
select_field = quota
where_field = username
#additional_conditions = and active = '1'

#Syntax with postfix 2.2.x:
user = postfix
password = postfix
hosts = localhost
dbname = postfix
query = SELECT quota FROM mailbox WHERE username='%s'

mysql_relay_domains_maps.cf

You will need to put this into a text file for postfix to pickup.

user = postfix
password = postfix
hosts = localhost
dbname = postfix
table = domain
select_field = domain
where_field = domain
additional_conditions = and backupmx = '1'

#Syntax with postfix 2.2.x:
user = postfix
password = postfix
hosts = localhost
dbname = postfix
query = SELECT domain FROM domain WHERE domain='%s' and backupmx = '1'


For security you should do

chmod 640 mysql_*
chgrp postfix mysql_*

User question/comment: Read last line here: http://www.postfix.org/faq.html#virtual_command. On RedHat based systems chmoding to 640 on mysql_* files might cause virtual errors like:

postfix/virtual[22114]: fatal: open /etc/postfix/mysql_virtual_mailbox_maps.cf: Permission denied

In this case try setting correct group for postfix.

MySQL note

Tip for chroot/jailed enviroment (default in Debian, Ubuntu and others).

Using "localhost" means that a local socket will be used to connect to mysql, which cause problems in a chroot'd environment. You must either create a link to the socket in the chroot environment or use hostname 127.0.0.1 to connect using TCP/IP instead. Postfix runs in a jailed enviroment in the /var/spool/postfix, and that's why it cannot connect to mysql.

  • If you have skip-networking in your my.cnf that means mysql accept connections thru socket only. You need to create a link to the mysql.sock in the postfix jail. Just run these commands:
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

For Debian users: insert the last line into /etc/mysql/debian-start, so that the link is automatically updated, when the mysql server is restarted.

  • If your mysql listen on the loopback interface (localhost), the way is to change all the hosts in /etc/postfix/mysql_*_maps.cf files from localhost to 127.0.0.1. Quote from mysql_table(5):
NOTE: if you specify localhost as a hostname (even if you prefix it with
inet:), MySQL will connect to the default UNIX domain socket.  In order
to instruct MySQL to connect to localhost over TCP you have to specify
                  hosts = 127.0.0.1

SuSE Linux note

Tip for SuSE Users and SuSEconfig (done on SuSE 9.1 Pro)

For having SuSEconfig work with "master.cf" when running postfix chroot/jailed and not want to have "virtual" chroot/jailed apply the following patch to /sbin/conf.d/SuSEconfig.postfix
NOTE: make a backup of /sbin/conf.d/SuSEconfig.postfix but don't leave your backup inside /sbin/conf.d, because SuSEconfig will then run SuSEconfig.postfix and you backup. :)

do the following as root:
cp -a /sbin/conf.d/SuSEconfig.postfix ~/SuSEconfig.postfix-orig

NOTE: this patch makes #MySQL note obsolete. Link for mysql.sock is created by SuSEconfig.postfix

--- SuSEconfig.postfix-orig 2006-02-05 14:34:03.000000000 +0100
+++ SuSEconfig.postfix  2006-02-06 13:52:31.501589142 +0100
@@ -78,6 +78,8 @@
  rm -rvf etc lib usr var proc
     elif [ "$(echo "$POSTFIX_UPDATE_CHROOT_JAIL" | tr 'A-Z' 'a-z' )" != "no" ]; then
  echo "checking postfix chroot environment..."
+  echo "removing mysql.sock"
+  rm -rvf var/lib/mysql

  if rpmqpack pam_ldap &> /dev/null; then
      cpifnewer /etc/openldap/ldap.conf etc/openldap
@@ -133,6 +135,27 @@
  fi

  chown -R root /var/spool/postfix/{etc,lib,usr,var}
+
+  # something for having postfix comunicate with mysql via socket
+  HERE="/var/spool/postfix"
+  MS_DIR="var/lib/mysql"
+  MYSQL_SOCKET="/var/lib/mysql/mysql.sock"
+  CHR_MYSQL_SOCKET="var/lib/mysql/mysql.sock"
+  [ ! -d $MS_DIR ] && {
+    mkdir -p $MS_DIR
+    chown mysql: $MS_DIR
+  }
+  if [ ! -S $MYSQL_SOCKET ]; then
+    warn_user "\tMySQL has to be started to have mysql.sock linked\n\
++\tinto postfix chroot. Please start mysql and run\n\
++\tSuSEconfig again!"
+  else
+    [ ! -e $CHR_MYSQL_SOCKET -a ! -S $CHR_MYSQL_SOCKET ] && {
+      #echo "does not exist, linking socket"
+      ln $MYSQL_SOCKET $HERE/$CHR_MYSQL_SOCKET
+    }
+  fi
+
     fi
 }

@@ -513,7 +536,7 @@

     my $match = 0;
     foreach my $serv ( ( "smtp", "pickup", "cleanup", "qmgr", "rewrite",
-      "bounce", "defer", "showq", "error", "virtual",
+      "bounce", "defer", "showq", "error",
       "lmtp", "smtps", "tlsmgr", "localhost:10025" ) ) {
  if( $line =~ /^$serv\s+/ ) {
      $line =~ /(^$serv\s+\w+\s+[yn-]?\s+[yn-]?\s+)[yn-]?(.*)/;

SASL2 Install

The easiest is to get SASL2 compiled with Courier-IMAP authdaemon support. When doing this SASL2 will hand-off the authentication to authdaemond.
On FreeBSD: make install WITH_AUTHDAEMON=yes

Sidenote: Dovecot also does Sasl with postfix and is in my (Gaqzi) opinion easier than these instructions. More information at: http://wiki.dovecot.org/PostfixAndDovecotSASL

The following has been built on Fedora Core 1-4 and has worked perfectly, if you have followed the directions above.

The best way to install SASL for Fedora is to use the following configure script. Change the paths for MySQL and Berkeley DB to reflect your paths.

Get SASL here: ftp://ftp.andrew.cmu.edu/pub/cyrus-mail/cyrus-sasl-2.1.19.tar.gz

Then get this patch: http://frost.ath.cx/software/cyrus-sasl-patches/dist/2.1.19/cyrus-sasl-2.1.19-checkpw.c.patch

Apply the patch (from INSIDE the cyrus-sasl-2.1.19 directory):

$ patch -p0 < ../cyrus-sasl-2.1.19-checkpw.c.patch

Install:

$ export CPPFLAGS="-I/usr/local/mysql/include"

$ ./configure \
 --prefix=/usr/local/sasl2 \
 --disable-cmulocal \
 --enable-sample \
 --enable-static=no \
 --enable-shared=yes \
 --enable-fast-install=yes \
 --without-gnu-ld \
 --disable-libtool-lock \
 --enable-staticdlopen=no \
 --without-purecov \
 --without-purify \
 --enable-java=no \
 --with-javabase=no \
 --without-dbpath \
 --with-dblib=berkeley \
 --with-bdb-libdir=/usr/local/bdb/lib \
 --with-bdb-incdir=/usr/local/bdb/include \
 --with-gdbm=no \
 --with-pam=no \
 --with-saslauthd=no \
 --with-pwcheck=no \
 --with-ipctype=unix \
 --disable-alwaystrue \
 --disable-checkapop \
 --disable-cram \
 --with-des=yes \
 --disable-digest \
 --with-openssl=/usr/bin/openssl \
 --disable-otp \
 --with-opie=no \
 --disable-srp \
 --disable-srp-setpass \
 --disable-krb4 \
 --disable-gssapi \
 --enable-plain \
 --disable-anon \
 --disable-login \
 --disable-ntlm \
 --with-ldap=no \
 --enable-sql \
 --with-authdaemon=yes \
 --with-mysql=/usr/local/mysql/lib \
 --with-plugindir=/usr/local/lib/sasl2 \
 --with-rc4 \
 --without-dmalloc \
 --without-sfio

In order to start saslauthd (this is only valid for cyrus-sasl when using the saslauthd, version >= 2.1.19), remember to use the -r option: this causes the daemon to pass user@domain.tld (instead of just the user part) to the authentication mechanizm, this is especially important when authenticating via PAM.

Another usefull option is to start the daemon in debug mode using -d, which allows you to monitor precisely what data is being used to authenticate your user.

SASL2 Setup

Postfix main.cf

The below example is the part that goes into your main.cf file of Postfix. There are also some additional UCE examples to block some spam. For more information on UCE check:


broken_sasl_auth_clients = yes
smtpd_recipient_restrictions = 
  permit_mynetworks,
  permit_sasl_authenticated,
  reject_non_fqdn_hostname,
  reject_non_fqdn_sender,
  reject_non_fqdn_recipient,  
  reject_unauth_destination,
  reject_unauth_pipelining,   
  reject_invalid_hostname,
  reject_rbl_client opm.blitzed.org,
  reject_rbl_client list.dsbl.org,
  reject_rbl_client bl.spamcop.net,
  reject_rbl_client sbl-xbl.spamhaus.org
smtpd_sasl_auth_enable = yes
smtpd_sasl_local_domain = $myhostname
smtpd_sasl_security_options = noanonymous

SASL2 smtpd.conf

smtpd.conf locations:

  • FreeBSD is in /usr/local/lib/sasl2
  • Debian Sarge is in /etc/postfix/sasl

Your smtpd.conf should contain something like:

pwcheck_method: authdaemond
log_level: 3
mech_list: PLAIN LOGIN
authdaemond_path:/usr/local/var/spool/authdaemon/socket

socket locations:

  • FreeBSD with courier-authlib-mysql-0.55 is /var/run/authdaemond/socket
  • Debian Sarge is /var/run/courier/authdaemon/socket

Tip for FreeBSD (tested in 6.0-RELEASE by Jett Tayer)

Courier's autdaemond socket and pid directory must be readable by Postfix

chmod 755 /var/run/authdaemond

Tip for Debian (tested in sarge)

As for the mysql socket, you must create a link to the authdaemon socket in postfix's jail (as root):

mkdir -p /var/spool/postfix/var/run/courier/authdaemon
ln /var/run/courier/authdaemon/socket /var/spool/postfix/var/run/courier/authdaemon/socket
chown -R daemon:daemon /var/spool/postfix/var/run/courier

Don't forget check the permission of the path of /var/run/authdaemond/socket.


Please note

If your postfix runs in a jailed environment (like in Debian default package), you must recreate the hard link everytime the authdaemond restarts, or you will get lots of "Connection refused" on the log. This happens because authdaemond recreates the socket every time it starts, invalidating the old link.

In Debian, the simplest way is to put this on /etc/init.d/courier-authdaemond after authdaemond start

ln -f /var/run/courier/authdaemon/socket /var/spool/postfix/var/run/courier/authdaemon/socket


Please note you must restart saslauthd whenever making any changes are made to the smtpd.conf file. If you followed the above instructions to build cyrus-sasl, you will have disabled saslauthd, so this note does not apply.

Reload postfix:

  • FreeBSD "postfix reload" or "/usr/local/etc/postfix reload" or "/etc/rc.d/sendmail restart" depending on how your /etc/rc.conf is configured.
  • Debian Sarge "postfix reload" or "/etc/init.d/postfix reload"
  • from chatran on slackware 10: I do this way:
pwcheck_method: saslauthd auxprop
mech_list: login plain
auxprop_plugin: sql
sql_engine: mysql
sql_hostnames: localhost
sql_user: postfix
sql_database: postfix
sql_passwd: postfix
sql_select: select password from mailbox where username = '%u@%r'

Note: this will only work if you use clear-text password, or if you patch the sql plugin as described in this article. --Beuc

  • Also remember to chown 777 the directory of the socket, otherwise postfix will not be able to read it.

With FreeBSD you can add postfix to the group courier. Edit /etc/group and change the line courier:*:465: in courier:*:465:postfix

SASL2 PAM install

Alternatively (for example if you don't want to recompile sasl package and use your distribution's -- tested on Debian), you can use PAM authentication. Install pam_mysql.so in addition to sasl2 (In debian, you need: sasl2-bin libsasl2-modules libpam-mysql). Then set up /etc/postfix/sasl/smtpd.conf like this:

pwcheck_method: saslauthd
mech_list: PLAIN LOGIN
minimum_layer: 0

As mentioned above, if you don't want saslauthd to strip the domain name from logins, it needs to be run with the -r option. In Debian Stable (Sarge) you can update /etc/default/saslauthd:

START=yes
MECHANISMS="pam"
PARAMS="-r"

In Debian Stable (Etch) you can update "/etc/default/saslauthd":

START=yes
MECHANISMS="pam"
OPTIONS="-c -m /var/spool/postfix/var/run/saslauthd -r"

Then create /etc/pam.d/smtp and include something like this:

auth       required     pam_nologin.so
auth       required     pam_mysql.so user=postfix passwd=password host=localhost db=postfix table=mailbox usercolumn=username passwdcolumn=password crypt=1
#auth       required     pam_unix.so
auth       required     pam_env.so # [1]

account       sufficient   pam_mysql.so user=postfix passwd=password host=localhost db=postfix table=mailbox usercolumn=username passwdcolumn=password crypt=1
account    required     pam_unix.so

(change password= to your database password). Everything else can be done as if you were using authdaemon from sasl (including main.cf changes).

You might also need to start SASL AuthDaemon in the Postfix chrooted environment for Debian. Taken from:
http://groups.google.com/group/mailing.postfix.users/browse_thread/thread/bc6dc028246f3bd7/32330aec53659ef6?lnk=st&q=warning%3A+SASL+authentication+failure%3A+cannot+connect+to+saslauthd+server%3A+No+such+file+or+directory&rnum=1#32330aec53659ef6

I had to edit /etc/init.d/saslauthd to include:

PARAMS="-m /var/spool/postfix/var/run/saslauthd"

keep in mind i'm not using sasl for anything other than postfix. this might not be where you want things if sasl is used by other processes..

and of course setup the necessary directories to accommodate the above:

mkdir /var/spool/postfix/var/
mkdir /var/spool/postfix/var/run/
mkdir /var/spool/postfix/var/run/saslauthd
chown -R root:sasl /var/spool/postfix/var/

you might have to add postfix to the sasl group as well - i'm sure someone will chime in if this is not necessary. adduser postfix sasl


If you are running Debian Stable (Etch) I would recomend readin "/usr/share/doc/sasl2-bin/README.Debian" it states

Using saslauthd with Postfix:

 If you run a chrooted server such as Postfix and wish to use saslauthd, you
 must place the saslauthd socket ("mux") inside the Postfix chroot. You must
 also set correct overrides for the run directory inside the chroot, using
 dpkg-statoverride. Finally, you must add the postfix user to the sasl group.
 These steps ensure that the Debian subsystems know how you want things to be
 laid out.

 To place the saslauthd socket inside the Postfix chroot, edit
 /etc/default/saslauthd and set OPTIONS like this (you may omit -c):
  OPTIONS="-c -m /var/spool/postfix/var/run/saslauthd"

 To set the run directory using dpkg-statoverride, run this command as root:
  dpkg-statoverride --add root sasl 710 /var/spool/postfix/var/run/saslauthd

 Finally, to add the postfix user to the sasl group:
  adduser postfix sasl

 The init script will automatically create the run directory with the
 permissions you have set using dpkg-statoverride. Please note that you must
 also configure Postfix correctly. There are many options related to SASL. See
 the Postfix documentation for how to do this.


once things are working, you should see some *mux* files in /var/spool/postfix/var/run/saslauthd/

Courier-IMAP Install

Build and install Courier-IMAP, make sure that this is built with MySQL. The authentication daemon of Courier-IMAP is now distributed seperately, courier-authlib. This means that the MySQL integration has moved from Courier-IMAP to courier-authlib. Make sure that you build courier-authlib with MySQL support.
On FreeBSD: make WITH_MYSQL=yes install
Also on FreeBSD: Don't forget to add "courier_authdaemond_enable="YES" to your /etc/rc.conf. It's not obvious because courier-authlib is installed with courier-imap during a "make install", and the status message after install only reminds you to add courier-imap to your startup config.
On Debian: apt-get install courier-authdaemon courier-authmysql

Courier-IMAP Setup

First, make sure you include authmysql in authmodulelist of Courier's authdaemonrc

authmysqlrc

NOTE: Make sure that there are no (trailing) spaces in this file, only tabs!!

The below is a part of the authmysqlrc file that is relevant to our setup. The things that you might need to change are the default_domain, mysql_password, mysql_uid and mysql_gid.

#DEFAULT_DOMAIN         domain.tld
MYSQL_CRYPT_PWFIELD     password
MYSQL_DATABASE          postfix
MYSQL_GID_FIELD         '1001'
MYSQL_HOME_FIELD        '/usr/local/virtual'
MYSQL_LOGIN_FIELD       username
MYSQL_MAILDIR_FIELD     maildir
MYSQL_NAME_FIELD        name
MYSQL_OPT               0
MYSQL_PASSWORD          postfix
#MYSQL_PORT             0
# Uncomment below if you want quota support.
#MYSQL_QUOTA_FIELD      quota
MYSQL_SERVER            localhost
# Default FreeBSD Socket
#MYSQL_SOCKET           /var/mysql/mysql.sock
# Default RedHat Socket
#MYSQL_SOCKET           /var/lib/mysql/mysql.sock
# Default Debian Sarge Socket
#MYSQL_SOCKET           /var/run/mysqld/mysqld.sock
MYSQL_UID_FIELD         '1001'
MYSQL_USERNAME          postfix
MYSQL_USER_TABLE        mailbox
#MYSQL_WHERE_CLAUSE     server='example.domain.com'


  • Make sure that there are NO spaces in the authmysqlrc file, only tabs.
  • Make sure that there are only single quotes ' around static values like: '/usr/local/virtual', 'UID', 'GID'
  • NO single quotes around localhost!
  • Make sure that localhost exists in your /etc/hosts file.
  • Including IPv6 during the compilation could cause a problem.
  • The MYSQL_GID_FIELD and MYSQL_UID_FIELD are for the UID and GID of the postfix user and group, NOT for the MySQL user and group.
  • If you have more than one authdaemon in /usr/lib/courier-imap/authlib, make sure that version="authdaemond.mysql" in /etc/courier/authdaemonrc

User question/comment: I thought MYSQL_CRYPT_PWFIELD only handles the ENCRYPT() function in stead of MD5() (see postfix-mysql setup). Correct me when I'm wrong

User question/comment: MYSQL_CRYPT_PWFIELD only specifies the name of database field, it has nothing to do with crypt format. authlib can automatically detect several different formats of password hash, please refer to cryptpassword.c inside courier authlib source code for more info. Basically it checks if the first few characters of password hash is:

  • "$1$": password is MD5 format password used by all Linux systems.
  • "{MD5}": this is followed by standard MD5 hash of password phrase.
  • "{SHA}": this is followed by standard SHA hash of password phrase.
  • "{SHA256}": this is followed by standard SHA256 hash of password phrase.
  • "{CRYPT}": this is followed by standard DES crypt() hash of password phrase.

User question/comment: MYSQL_HOME_FIELD '/usr/local/virtual' is not a field name and is not used as a literal by courier-authlib-0.57 although it should not be deleted or changed to ' ' as the default field of 'home' will be substituted causing errors. Try MYSQL_MAILDIR_FIELD CONCAT("/usr/local/virtual/",maildir) as a workaround.

imapd

Your Courier-IMAP imapd file should have a line similar to this.

IMAP_CAPABILITY="IMAP4rev1 UIDPLUS CHILDREN NAMESPACE THREAD=ORDEREDSUBJECT THREAD=REFERENCES SORT QUOTA IDLE"

Dovecot IMAP Install

Recently I switched from courier imap to dovecot, which is a lot easier to configure than courier, and performs much better. You will find dovecot packages in most linux distributions and in the *BSD ports. Please make sure you compile dovecot with at least the mysql support. For freebsd:

cd /usr/ports/mail/dovecot && make install clean

And compile with the mysql module.

Dovecot IMAP Setup

You need to edit only three files (Thanks to [sylhouette] for his examples).

dovecot-openssl.cnf

First: /usr/local/share/dovecot/dovecot-openssl.cnf to make the certificates for imap-ssl and pop-ssl.

[ req ]
default_bits = 1024
encrypt_key = yes
distinguished_name = req_dn
x509_extensions = cert_type
prompt = no

[ req_dn ]
# country (2 letter code)
C=NL

# State or Province Name (full name)
ST=nh

# Locality Name (eg. city)
L=Amsterdam

# Organization (eg. company)
O=FriendsOfTheMoose

# Organizational Unit Name (eg. section)
OU=IMAP server

# Common Name (*.example.com is also possible)
CN=hostname.yourdomain.tld

# E-mail contact
emailAddress=postmaster@yourdomain.tld

[ cert_type ]
nsCertType = server

Create /etc/ssl/certs and /etc/ssl/private directories if nessecary and run:

cd /usr/local/share/dovecot/
./mkcert.sh

dovecot.conf

Then we edit /usr/local/etc/dovecot.conf. This configuration can be used, please make sure that first_valid_uid and first_valid_gid are set to those of postfix. Fill in the postmaster email addresse. Another thing: the namespace parameters that I filled in was to keep it compatible with courier imap, from which I migrated, without the need to change all clients. If you do not need this, read the section on namespaces: http://wiki.dovecot.org/Namespaces .

protocols = imap imaps
listen = *
login_process_size = 64
default_mail_env = maildir:/usr/local/virtual/%u/

namespace private {
  separator = .
  prefix = INBOX.
  inbox = yes
}

namespace private {
  separator = .
  prefix =
  inbox = yes
  hidden = yes
}

mail_extra_groups = postfix
verbose_proctitle = yes
first_valid_uid = 125
first_valid_gid = 125
#umask = 0077
mbox_read_locks = fcntl
mbox_write_locks = fcntl

ssl_disable = no

ssl_cert_file = /etc/ssl/certs/dovecot.pem
ssl_key_file = /etc/ssl/private/dovecot.pem

# If key file is password protected, give the password here. Alternatively
# give it when starting dovecot with -p parameter.
ssl_key_password =

protocol imap {
  imap_client_workarounds = delay-newmail outlook-idle netscape-eoh tb-extra-mailbox-sep
}

#People who use outlook are fux0red anyway...

#protocol pop3 {
#  pop3_uidl_format = %08Xu%08Xv
#  pop3_client_workarounds = outlook-no-nuls oe-ns-eoh
#}

protocol lda {
  postmaster_address = postmaster@yourdomain.tld
  sendmail_path = /usr/local/sbin/sendmail
}

auth_username_chars = abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ01234567890.-_@

auth_debug = yes
auth_verbose = yes
auth default {

passdb sql {
        args = /usr/local/etc/dovecot-mysql.conf
  }
  
userdb sql {
        args = /usr/local/etc/dovecot-mysql.conf
  }
}

dovecot-mysql.conf

And then we edit /usr/local/etc/dovecot-mysql.conf.

# Database driver: mysql, pgsql
driver = mysql

# Currently supported schemes include PLAIN, PLAIN-MD5, DIGEST-MD5, and CRYPT.
default_pass_scheme = CRYPT

# Database options
connect = host=localhost dbname=postfix user=dovecot password=assword

password_query = SELECT password FROM mailbox WHERE username = '%u' AND active = '1'
user_query = SELECT maildir, 125 AS uid, 125 AS gid FROM mailbox WHERE username = '%u' AND active = '1'

# eof

Fill in your proper password, and make sure that

125 AS uid
125 AS gid

contain the userid and group id of postfix.

database

Finally we make some adjustments to the database. There is a nasty bug that I struggled with. Apparantly, mysql 4.1 and higher use a new password scheme, while dovecot uses the old one. So this is what I do:

grant select on postfix.* to 'dovecot'@'localhost' identified by 'assword';
set password for 'dovecot'@'localhost' = old_password('assword');


remarks

Make note: there are a few instances of the number 125 in dovecot.conf and dovecot-mysql.conf. Make sure this is set to the uid and guid of postfix. In FreeBSD 6 this is 125. In FreeBSD 4.x this is 1001. Check /etc/passwd to make sure.

Check out the namespace thing that I mention above, if you are migrating from courier. Or using mail.app. Well, check it anyway, differences in namespaces seems to break certain clients.

Mutt may break with dovecot. I find that mutt-ng works without problems. You might need to set in .muttrc (or .muttngrc):

set imap_idle=no                #May be nessecary for older mutt versions
set folder=imap://localhost/    #Or whatever host dovecot is running
set spoolfile=+INBOX            #If you use the namespaces I use

Testing your setup

Below are the basic telnet commands for testing your setup:

SMTP:

telnet 127.0.0.1 25

Trying 127.0.0.1...
Connected to 127.0.0.1.
Escape character is '^]'.
220 localhost.localdomain ESMTP Postfix 
> EHLO test.com
250-localhost.localdomain
250-PIPELINING
250-SIZE 10240000
250-VRFY
250-ETRN
250-AUTH LOGIN PLAIN
250-AUTH=LOGIN PLAIN
250 8BITMIME
> AUTH PLAIN AHVzZXJAZG9tYWluLnRsZABzZWNyZXQ=
235 Authentication successful

A common question is: how do i obtain the auth code ?

Simple, just use this perl script, inserting the username and password in appropriate places:

perl -MMIME::Base64 -e 'print encode_base64("\0user\@domain.tld\0secret")'

Filtering

Postfix as well as Courier/Dovecot (make your life easy, choose Dovecot) are unable to do anykind of filtering in terms of putting certain mail in certain imap directories. I have been searching for possibilities to do so. I'm not an expert at all, but you can find my solution here: Combine With Maildrop Howto

This document was started by Mischa 10:07, 20 Apr 2005 (CEST)

Personal tools