Home | Scripts | How To's | About


Postifx w/ SASL + Courier IMAP w/ SSL + Maildrop + MySQL + SpamAssassin

View Comments

Updated as of October 1, 2011.

In this document I am going to share my experience with setting up a viable virtual mail server solution for a Linux system. I have made countless attempts at trying to setup mail servers in the past, but have never been able to achieve the results that I desired. Whether it was the incomplete HOWTOs that I was reading or my own lack of knowledge on the subject, I don't know; but after eventually reading through enough document I was able to conceive a solution that works great (so far on two boxes). Most importantly, however, I will attempt to cover and help you resolve the common problems that myself and others have ran into.

Before asking a question, check this article for some fixes.

By the end of this document you will hopefully achieve what I have:

  • A MySQL database which mail users will be primarily authenticated against.
  • Postfix MTA will allow SMTP AUTH SASL connections instead of using relay-domains. Authentication will be done against MySQL and PAM.
  • Maildrop will deliver mail to the users' home directories using Maildir style mail boxes. Any missing Maildir folders will be created.
  • Invoked within Maildrop, SpamAssassin will do its best to flag spam. Then maildrop will deliver it into a separate Maildir.
  • Users will retrieve their mail using IMAP. Authentication will be done against MySQL and PAM.
  • You will NOT need to use PAM-MySQL.

To start off, you will use the following applications:

Installing Sources

First and foremost, you need to compile the sources of the top applications. I will not go through installing MySQL since it is outside of the scope of this document.

OpenSSL

Compile and install OpenSSL.

$ tar zxvf openssl-yourversion.tar.gz
$ ./config
$ make
$ make test
$ make install (as root)

Cyrus SASL v2

The following assumes that you have MySQL setup in: /usr/local/mysql. We will be using the built in MySQL authentication module, instead of having to redirect the authentication to PAM-MySQL. Prior to installing these modules, attempt to remove any pre-installed ones (look in: /usr/lib, /usr/local/lib, /usr/local/lib/sasl2).

$ tar zxvf cyrus-sasl-2.1.22.tar.gz
$ ./configure
--enable-anon
--enable-plain
--enable-login
--enable-sql
--disable-krb4
--disable-otp
--disable-cram
--disable-digest
--with-mysql=/usr/local/mysql/lib
--without-pam
--with-saslauthd
--without-pwcheck
--with-dblib=berkeley
--with-openssl=/usr/local/ssl
--with-plugindir=/usr/local/lib/sasl2
$ make
$ make install (as root)

Check to make sure that the path /usr/local/lib is in /etc/ld.so.conf. If it is not, append that path to the file and run ldconfig (as root):

$ echo "/usr/local/lib" >> /etc/ld.so.conf
$ ldconfig

Double check to make sure libsql.so is in /usr/local/lib/sasl2.

Postfix

Compiling Postfix is pretty straight forward. Be sure to add the postfix user and group. During the install it will ask for the user and group, use postfix and postdrop. For the configuration directory, specify /etc/postfix.

$ groupadd postdrop -g 1001 (as root)
$ useradd postfix -u 1001 -g 1001 (as root)
$ tar zxvf postfix-2.5.5.tar.gz
$ make -f Makefile.init makefiles
$ make makefiles 'CCARGS=-DHAS_MYSQL -I/usr/local/mysql/include -I/usr/local/mysql/include/mysql -DUSE_CYRUS_SASL -DUSE_SASL_AUTH -I/usr/local/include/sasl -I/usr/local/bdb/include' 'AUXLIBS=-L/usr/local/mysql/lib -lmysqlclient -lz -lm -L/usr/local/lib -lsasl2'
$ make install (as root)

After you finish with the interactive installation, you will need to check and see if Postfix is linked against SASL. To do this run the following from within the root of Postfix's source:

$ ldd ./bin/postconf

If everything worked well (and I hope it did), one of the lines should read:

libsasl2.so.2 => /usr/local/lib/libsasl2.so.2

Courier Authlib

Authlib is now responsible for all MySQL authentication. Maildrop and imapd are not longer responsible of it (which actually makes it easier to compile). Maildrop and imapd need to be aware of authlib! Be sure to specify the user and group when compiling authlib, otherwise maildrop will have no access to authlib. DO NOT put the uid and gid, it must be the name, authdaemond will work but courierauthconfig will fail (hence the rest of the apps).

$ export CPPFLAGS="-I/usr/local/mysql/include/mysql"
$ export LDFLAGS="-L/usr/local/mysql/lib"
$ ./configure --prefix=/usr/local/courier --with-mysql-libs=/usr/local/mysql/lib/mysql --with-mysql-includes=/usr/local/mysql/include/mysql/ --with-authmysql --with-authmysql=yes --with-authchangepwdir --with-mailuser=vmail --with-mailgroup=vmail
$ make
$ make install

Once compiled there are only a few files to modify. In /usr/local/courier/etc/authlib copy the files by removing the ".dist" at the end.

Courier IMAP

As with Postfix, Courier IMAP should be simple to install. We will use /usr/local/courier as the base directory for the IMAPd and for Maildrop. This also allows you to use Courier MTA in the future without having to re-do a lot of things.

$ bzip2 -dc courier-imap-4.4.1.tar.bz2 | tar xvf -
$ ./configure --prefix=/usr/local/courier COURIERAUTHCONFIG=/usr/local/courier/bin/courierauthconfig
$ make
$ make install
$ make install-config

Courier Maildrop

Now we go to installing Maildrop, our local delivery agent. First you create the user and group that Maildrop will deliver as. For our application, we will use vmail for both.

$ bzip2 -dc maildrop-2.0.4.20080726.tar.bz2 | tar xvf -
$ groupadd vmail -g 1004 (as root)
$ useradd vmail -u 1004 -g 1004 (as root)
$ tar zxvf maildrop-1.6.3.tar.gz
$ ./configure --prefix=/usr/local/courier --enable-maildrop-uid=1004 --enable-maildrop-gid=1004 --with-authpwd=yes COURIERAUTHCONFIG=/usr/local/courier/bin/courierauthconfig
$ make
$ make install

SpamAssassin

We're almost done installing our sources, just one more to go. Prior to installing SpamAssassin, get all the required Perl modules (check the INSTALL file).

$ tar zxvf Mail-SpamAssassin-2.63.tar.gz
$ perl MakeFile.PL
$ make
$ make install (as root)

Database and Virtual Mail Directory Setup

Database

The database side of this was used from Martin List-Petersen's excellent document, ISP Mailserver Solution Howto that helped me most of the way. I will just copy the CREATE TABLE syntax that is provided in his HOWTO and explain the use of two main tables (things such as the default uid and gid have been modified to fit this document).

I expect that you have created the database (in this document we will use "mail" as our database) and have secured it with a user and password.

CRITICAL NOTE: When creating the user for our database, create TWO: one that authenticates from "localhost" and one from "127.0.0.1"
I don't know why it doesn't see it as the same, but on two different boxes (RedHat 9 and Slackware 9) it needed two seperate entries.

CREATE TABLE postfix_alias (
  id int(11) unsigned NOT NULL auto_increment,
  alias varchar(128) NOT NULL default '',
  destination varchar(128) NOT NULL default '',
  PRIMARY KEY (id)
) TYPE=MyISAM;

CREATE TABLE postfix_relocated (
  id int(11) unsigned NOT NULL auto_increment,
  email varchar(128) NOT NULL default '',
  destination varchar(128) NOT NULL default '',
  PRIMARY KEY (id)
) TYPE=MyISAM;

CREATE TABLE postfix_transport (
  id int(11) unsigned NOT NULL auto_increment,
  domain varchar(128) NOT NULL default '',
  destination varchar(128) NOT NULL default '',
  PRIMARY KEY (id),
  UNIQUE KEY domain (domain)
) TYPE=MyISAM;

CREATE TABLE postfix_users (
  id int(11) unsigned NOT NULL auto_increment,
  email varchar(128) NOT NULL default '',
  clear varchar(128) NOT NULL default '',
  crypt varchar(128) NOT NULL default '',
  name tinytext NOT NULL,
  uid int(11) unsigned NOT NULL default '1004',
  gid int(11) unsigned NOT NULL default '1004',
  homedir tinytext NOT NULL,
  maildir tinytext NOT NULL,
  quota tinytext NOT NULL,
  access enum('Y','N') NOT NULL default 'Y',
  postfix enum('Y','N') NOT NULL default 'Y',
  PRIMARY KEY (id),
  UNIQUE KEY email (email)
) TYPE=MyISAM;

CREATE TABLE postfix_virtual (
  id int(11) unsigned NOT NULL auto_increment,
  email varchar(128) NOT NULL default '',
  destination varchar(128) NOT NULL default '',
  PRIMARY KEY (id)
) TYPE=MyISAM;

CREATE TABLE postfix_access (
  id int(10) unsigned NOT NULL auto_increment,
  source varchar(128) NOT NULL default '',
  access varchar(128) NOT NULL default '',
  type enum('recipient','sender','client') NOT NULL default 'recipient',
  PRIMARY KEY (id)
) TYPE=MyISAM

In this document I will only go over the postfix_users and postfix_virtual tables, as Martin List-Petersen does a great job at describing the rest. When we get to testing, I will go into detail on what goes into those tables, for now just leave them empty.

Virtual Mail Directory

All mail for our virtual users will be stored in the following format:

/home/vmail
- domain.tld
-- user1
-- user2
--- Maildir
- domain2.tld

Seems simple enough, right? Go ahead and create only the top-most directory (/home/vmail) and set it's owner and group to vmail. For security reasons, chmod it 700.

$ mkdir /home/vmail
$ chown vmail.vmail /home/vmail
$ chmod 700 /home/vmail

Next we move onto configuration.

Configuring Installed Packages

The majority of this was found in Martin List-Petersen's document.

Postfix

Most configuration will be done to Postfix. Open up /etc/postfix/master.cf and change the following:

flags=DRhu user=vmail argv=/usr/local/bin/maildrop -d ${recipient}

To:

flags=R user=vmail argv=/usr/local/courier/bin/maildrop -d ${recipient}

CRITICAL NOTE: Be sure that the two leading spaces on those lines remain present.

Now open up /etc/postfix/main.cf and configure the following:

myhostname = mail.example.com
mydomain = example.com
myorigin = $mydomain
mydestination = example.com, $transport_maps
local_recipient_maps = $alias_maps $virtual_mailbox_maps unix:passwd.byname
home_mailbox = Maildir/

# Add the following to the bottom

smtpd_sasl_auth_enable = yes
smtpd_recipient_restrictions = permit_sasl_authenticated, permit_mynetworks, reject_unauth_destination, reject_maps_rbl
smtpd_sasl_security_options = noanonymous
smtpd_sasl_local_domain = $myhostname
broken_sasl_auth_clients = yes

smtpd_recipient_restrictions = permit_mynetworks, reject_unauth_pipelining, reject_non_fqdn_recipient, reject_unknown_recipient_domain, permit_sasl_authenticated, reject_rbl_client sbl-xbl.spamhaus.org, reject_rbl_client bl.spamcop.net, #reject_rbl_client ips.backscatterer.org, check_recipient_access mysql:/etc/postfix/mysql-recipient.cf, reject_unauth_destination

smtpd_sender_restrictions = check_sender_access mysql:/etc/postfix/mysql-sender.cf
smtpd_client_restrictions = check_client_access mysql:/etc/postfix/mysql-client.cf
smtpd_sasl_authenticated_header = yes
smtpd_sasl_path = smtpd

alias_maps = mysql:/etc/postfix/mysql-aliases.cf
relocated_maps = mysql:/etc/postfix/mysql-relocated.cf
transport_maps = mysql:/etc/postfix/mysql-transport.cf
virtual_maps = mysql:/etc/postfix/mysql-virtual.cf
virtual_mailbox_base = /home/vmail
virtual_mailbox_maps = mysql:/etc/postfix/mysql-virtual-maps.cf
virtual_uid_maps = mysql:/etc/postfix/mysql-virtual-uid.cf
virtual_gid_maps = mysql:/etc/postfix/mysql-virtual-gid.cf

maps_rbl_domains = dev.null.dk, opm.blitzed.org, sbl.spamhaus.org

smtpd_delay_reject = yes
smtpd_helo_required = yes
smtpd_helo_restrictions = reject_invalid_hostname, permit

Once again, the following is borrowed from Martin List-Petersen's document. To conserve time writing this document, I will paste the contents of the required files that allow Postfix to communicate with MySQL.

# mysql-aliases.cf
user = mysql-postfix-user
password = mysql-postfix-pass
dbname = mail
table = postfix_alias
select_field = destination
where_field = alias
hosts = 127.0.0.1

# mysql-relocated.cf
user = mysql-postfix-user
password = mysql-postfix-pass
dbname = mail
table = postfix_relocated
select_field = destination
where_field = email
hosts = 127.0.0.1

# mysql-transport.cf
user = mysql-postfix-user
password = mysql-postfix-pass
dbname = mail
table = postfix_transport
select_field = destination
where_field = domain
hosts = 127.0.0.1

# mysql-virtual.cf
user = mysql-postfix-user
password = mysql-postfix-pass
dbname = mail
table = postfix_virtual
select_field = destination
where_field = email
hosts = 127.0.0.1

# mysql-recipient.cf
user = mysql-postfix-user
password = mysql-postfix-pass
dbname = mail
table = postfix_access
select_field = access
where_field = source
additional_conditions = and type = 'recipient'
hosts = 127.0.0.1

# mysql-sender.cf
user = mysql-postfix-user
password = mysql-postfix-pass
dbname = mail
table = postfix_access
select_field = access
where_field = source
additional_conditions = and type = 'sender'
hosts = 127.0.0.1

# mysql-client.cf
user = mysql-postfix-user
password = mysql-postfix-pass
dbname = mail
table = postfix_access
select_field = access
where_field = source
additional_conditions = and type = 'client'
hosts = 127.0.0.1

# mysql-virtual-maps.cf
user = mysql-postfix-user
password = mysql-postfix-pass
dbname = mail
table = postfix_users
select_field = maildir
where_field = email
additional_conditions = and postfix = 'y'
hosts = 127.0.0.1

# mysql-virtual-uid.cf
user = mysql-postfix-user
password = mysql-postfix-pass
dbname = mail
table = postfix_users
select_field = uid
where_field = email
additional_conditions = and postfix = 'y'
hosts = 127.0.0.1

# mysql-virtual-gid.cf
user = mysql-postfix-user
password = mysql-postfix-pass
dbname = mail
table = postfix_users
select_field = gid
where_field = email
additional_conditions = and postfix = 'y'
hosts = 127.0.0.1

Of course you will have to substitue the username and password for the one you created earlier.

Courier IMAP

Open /usr/local/courier/etc/authlib/authdaemonrc (if it doesn't exist, make a copy from authdaemonrc.dist located in the same directory). Change the line that starts with "authmodulelist" to read:

"authmysql authshadow"

Next create a file called authmysqlrc (in the same directory) and put the following in:

MYSQL_USERNAME          USRENAME
MYSQL_PASSWORD          PASSWORD
MYSQL_PORT              0
MYSQL_OPT               0
MYSQL_DATABASE          mail
MYSQL_USER_TABLE        postfix_users
MYSQL_LOGIN_FIELD       email
MYSQL_CRYPT_PWFIELD     crypt
MYSQL_CLEAR_PWFIELD     clear
MYSQL_UID_FIELD         uid
MYSQL_GID_FIELD         gid
MYSQL_HOME_FIELD        homedir
MYSQL_MAILDIR_FIELD     maildir
MYSQL_WHERE_CLAUSE      access='y'

Open /usr/local/courier/etc/imapd and make sure the following lines looks like this:

# This is all on one line
IMAP_CAPABILITY="IMAP4rev1 CHILDREN NAMESPACE THREAD=ORDEREDSUBJECT THREAD=REFERENCES SORT AUTH=CRAM-MD5 AUTH=CRAM-SHA1 IDLE"

# Seperate line
IMAPDSTART=YES

Open /usr/local/courier/etc/imapd-ssl and make sure the following line looks like this:

IMAPDSSLSTART=YES

Courier Maildrop

While we're still in /usr/local/courier/etc edit or create a file called maildropmysql.config and put in the following:

hostname             localhost
port                 3306
database             mail
dbuser               USERNAME
dbpw                 PASSWORD
dbtable              postfix_users
default_uidnumber    1004
default_gidnumber    1004
uid_field            email
uidnumber_field      uid
gidnumber_field      gid
maildir_field        maildir
homedirectory_field  homedir
quota_field          quota
# unused for now, but needs to be a valid field.
mailstatus_field     postfix
where_clause         AND postfix = 'y'

Now we need to setup Maildrop to deliver our mail. Create (or edit) /etc/maildroprc and put in the following:

NOTE: Take care in bracket placement, Maildrop is very picky in this sense.

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

# if the user in the domain does not exist, create it
# create the domain with parents, then remove Maildir/
# finally create a new maildir

`test -d $HOME/$DEFAULT`
if ( $RETURNCODE == 1 )
    {
    `mkdir -p $HOME/$DEFAULT`
    `rm -rf $HOME/$DEFAULT`
    `/usr/local/courier/bin/maildirmake $HOME/$DEFAULT`
    `/usr/local/courier/bin/maildirmake $HOME/$MAILDIR/.Spam/`
    `/usr/local/courier/bin/maildirmake $HOME/$MAILDIR/.Spam/.New`
}

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

exception {
    to "$HOME/$DEFAULT"
}

Cyrus SASL

Since Cyrus will be doing some authenticating, we need to configure it as well. Create the file smtpd.conf in /usr/local/lib/sasl2 and put in the following:

sasl_pwcheck_method: auxprop
sasl_auxprop_plugin: mysql login plain crammd6 digestmd5
mysql_user: USERNAME
mysql_passwd: PASSWORD
mysql_hostnames: localhost
mysql_database: mail
mysql_statement: SELECT clear FROM postfix_users WHERE email = '%u@%r'
mysql_verbose: yes

Finalizing

Adding a user

You are now ready to add a test user to the database, start the mailserver and test the whole system out.

Populate the postfix_transport table with the following:

domain: "test.com"
destination: "maildrop:"

The domain field needs no explination, however, the destination field does. In the destination field Postfix is told what program the message should be carried to.

For instance, if you wanted delivery to be handled by Postfix's internal system, you would put "virtual:" In our example however, we are using maildrop, so we put "maildrop:"

Next add a test user by populating the postfix_users table.

email: "test@test.com"
clear: "mypassword"
homedir: "/home/vmail"
maildir: "test.com/test/Maildir/"

That's all there is to adding a new user. The "email" field stores just that, the user's email (it must include the TLD). The "clear" field stores the user's password in clear text. "Homedir" contains the root directory for all virtual mail, if we were using "local:" delivery, this would be set to the user's home directory. Finally, "maildir" is the Maildir location relative to the homedir.

Starting the Daemons

Open up your system log (/var/log/messages or /var/log/maillog) and monitor it as you start your daemons:

$ /usr/local/sbin/saslauthd -a shadow
$ /usr/local/courier/sbin/authdaemond start
$ /usr/local/courier/libexec/imapd.rc start
$ /usr/sbin/postfix start

If everything went right, the daemons should start without any failures. nmap yourself and see what ports are open, there should be three (smtp, imap and imap-ssl)

Testing the SMTP and IMAP Servers

Everything not starting with a ">" or "$" is a response from the server.

$ telnet localhost 25
Trying 127.0.0.1...
Connected to localhost.
Escape character is '^]'.
220 mail.test.com ESMTP Postfix
> EHLO test.com
250-mail.test.com
250-PIPELINING
250-SIZE 10240000
250-VRFY
250-ETRN
250-AUTH LOGIN PLAIN
250-AUTH=LOGIN PLAIN
250-XVERP
250 8BITMIME

If you see something like that, congratulations, you're still in good shape!
Create all directories all the way up to "/home/vmail/test.com/test" after which create a Maildir in that directory.
Maildrop will NOT create Maildir's by default (source has to be patched to do so). Continue the telnet session and type in the following:

> MAIL FROM: test@test.com
250 Ok
> RCPT TO: test@test.com
250 Ok
> DATA
354 End data with <CR><LF>.<CR><LF>
> .
250 Ok: queued as 6CE4223727

Now, if everything is setup right, the mailserver should queue your message. Check inside of /home/vmail/test.com/test/Maildir/new for anything, if you see a file, congratulations, the message was delivered!

When using a mail client (such as Outlook or Mozilla), be sure to use the full email address for the username (ie: "user@domain.com", not just "user").

 


Comments (Last 10): [Show All]


January 09th, 2014 @ 09:50AM


January 08th, 2014 @ 04:46PM


January 07th, 2014 @ 10:41PM


January 07th, 2014 @ 05:15AM


January 06th, 2014 @ 11:34AM


January 05th, 2014 @ 05:57PM


January 05th, 2014 @ 02:47AM


January 05th, 2014 @ 12:46AM


January 04th, 2014 @ 09:03PM


January 04th, 2014 @ 07:30AM


 
Post Comment:

Use the following verification number: 3wg8o0tww32

Name 
Comment 
Verification 



eBay Sniper