Home   Profile   Fun
#149 Linux  07.01.2008

How to set up a Postfix mail server with MySQL backend on Gentoo


This tutorial explains the basic setup of a Postfix mail server with the following services:
Port 995: POP3S (TLS protocol: SSLv3) with Clear text, LOGIN, PLAIN and CRAM-MD5
Port 993: IMAPS (TLS protocol: SSLv3) with Clear text, LOGIN, PLAIN and CRAM-MD5
Port 025: SMTPS (TLS protocol: TLSv1) with CRAM-MD5 und DIGEST-MD5

In all cases the authentication data and the actual mail traffic is encrypted, no matter which mechanism is used. All authentication data is stored in a MySQL database. No system accounts are needed. Nevertheless each mail account has its own UID/GID in the filesystem. Currently Cyrus SASL can handle plain text passwords only in the MySQL backend. There is a patch available but I haven't tested it yet.

Here is a diagram that illustrates the authentication procedure:

mailserver overview

POP3S / IMAPS:
Mail client -> Courier-IMAP -> Courier-authlib(authdaemond) -> MySQL
(Mapping with MySQL tables is done in /etc/courier/authlib/authmysqlrc)

SMTPS:
Mail client -> Postfix -> Cyrus SASL(auxprop plugin) -> MySQL
(Mapping with MySQL tables is done in /etc/sasl2/smptd.conf)
(Cyrus SASL is run by Postfix, it has no separate daemon)

Emails are written in Qmail style to /var/mail/vdomains/DOMAIN/USER. We will create our own certificate authority (CA) for the test certificates.


1. Postfix,MySQL,syslog-ng: basic setup
2. Courier-IMAP
3. MySQL
4. Postfix
5. Cyrus SASL
6. Courier-authlib
7. SSL certificate for Postfix
8. Configure Postfix to use TLS and Cyrus SASL
9. Virtual mail hosting
10. Final configuration
11. Clean-up



1. Postfix, MySQL, syslog-ng: basic setup

We start with a fresh Gentoo system.
emerge -uav world

Test that all security updates are installed:
glsa-check -l affected

Then make sure no other MTA is installed like ssmtp, exim or qmail.

Install MySQL, Cyrus SASL and Postfix
USE="berkdb perl ssl" emerge -va mysql
USE="berkdb crypt gdbm mysql pam ssl" emerge -va cyrus-sasl
USE="mysql pam sasl ssl -ipv6" emerge -va postfix

It is important that the command "hostname" prints the correct hostname. In this tutorial we use "postfix". The FQDN name of this system is postfix.test.acodedb.com. If the hostname is not correct you can change it in "/etc/conf.d/hostname" and activate the changes with
/etc/init.d/hostname restart

Make sure that the FQDN resolves to the IP Postfix should bind to. If the DNS record is not yet configured you can add a record in /etc/hosts for the testing period.
vi /etc/hosts
[IP]	postfix.test.acodedb.com postfix

Now, as Postfix is installed we do the basic setup. The main configuration file of postfix is /etc/postfix/main.cf.
Add the following lines at the end of the file and adapt the values according to your system:
myorigin=$myhostname
myhostname = postfix.test.acodedb.com
mydomain = test.acodedb.com
inet_interfaces = $myhostname
mydestination = $myhostname, localhost.$mydomain, localhost
mynetworks = 127.0.0.0/8, 192.168.1.0/24
home_mailbox = Maildir/
local_destination_concurrency_limit = 2
default_destination_concurrency_limit = 10

Now we have to set the root alias. This is necessary because Postfix wants to know the target account for mails for the user root. This has a security background, set it to a local nonroot account:
vi /etc/mail/aliases
# Well-known aliases -- these should be filled in!
root:           test1
# operator:

So that Postfix has fast access to the alias data we create the aliase database
newaliases

Postfix is logging to /var/log/messages per default. For a better overview I want it to log into a seperate log file: /var/log/mail.log. Therefore we must configure syslog-ng so that it writes Postfix logs solely to this file and keeps /var/log/messages free from Postfix entries. Additionally I want the authdaemond to log into /var/log/authdaemond.log:
vi /etc/syslog-ng/syslog-ng.conf:
options {
        chain_hostnames(off);
        sync(0);
        stats(43200);
};

source src {
    unix-stream("/dev/log" max-connections(256));
    internal();
};

# direct postfix logs to /var/log/mail.log
destination postfix { file("/var/log/mail.log"); };
filter postfix { match("^postfix"); };
log { source(src); filter(postfix); destination(postfix); };

destination messages { file("/var/log/messages"); };
filter nopostfix { not match("^postfix"); };

# direct authdaemond logs to /var/log/authdaemond.log
destination authdaemond { file("/var/log/authdaemond.log"); };
filter authdaemond { match("^authdaemond"); };
log { source(src); filter(authdaemond); destination(authdaemond); };

filter noauthdaemond { not match("^authdaemond"); };

# By default messages are logged to tty12...
destination console_all { file("/dev/tty12"); };

log { source(src); filter(nopostfix); filter(noauthdaemond); destination(messages); };
log { source(src); destination(console_all); };


The file master.cf contains the configuration of the master processes of Postfix. To get debug output for SMTP we add -v to the smpt line:
vi /etc/postfix/master.cf:
smtp      inet  n       -       n       -       -       smtpd -v

Restart both services
# /etc/init.d/syslog-ng restart
# /etc/init.d/postfix restart

It's a good idea to test everything that has been set up so far. Just send a mail locally to a local account. Make also sure that logging data is written to /var/log/mail.log
(If the mail command is not available emerge -va mail-client/mailx)
# cd /home/test1
# chmod o+w .
# mail root@test.acodedb.com
Subject: test1
test1
.
Cc:
# chmod o-w .

The next test is to use telnet from another system and send a mail to a local account. And also make sure that you can send to external addresses. Here you see the telnet session for the local account (root@postfix.test.acodedb.com).
# telnet postfix.test.acodedb.com 25
Trying 78.47.177.74...
Connected to postfix.test.acodedb.com.
Escape character is '^]'.
220 postfix.test.acodedb.com ESMTP Postfix
helo client.homenet
250 postfix.test.acodedb.com
mail from: <test@homenet>
250 2.1.0 Ok
rcpt to: <root@postfix.test.acodedb.com>
250 2.1.5 Ok
data
354 End data with <CR><LF>.<CR><LF>
This is a testmail from home.
.
250 2.0.0 Ok: queued as BD162A0294
quit
221 2.0.0 Bye
Connection closed by foreign host.
#


Because of the root alias the mail must be delivered to /home/test1/Maildir/new/. You can "cat" the message an verify that it is the one you have just sent.
To make sure that we have not created an open relay which can be used by everyone to send mail/spam it is important to test wether you can send a mail via telnet to an external account without authentication. The server must respond with "Relay access denied" if you run telnet from a host which is not part of the networks defined with mynetworks in /etc/postfix/main.cf.
# telnet postfix.test.acodedb.com 25
Trying 78.47.177.74...
Connected to postfix.test.acodedb.com.
Escape character is '^]'.
220 postfix.test.acodedb.com ESMTP Postfix
helo client.homenet
250 postfix.test.acodedb.com
mail from: <test@homenet>
250 2.1.0 Ok
rcpt to: <remoteaccount@somewhere.xyz>
554 5.7.1 <remoteaccount@somewhere.xyz>: Relay access denied
quit
221 2.0.0 Bye
Connection closed by foreign host.
#

If everything works, great! If not, verify every single step and make sure you have adapted it to your scenario.

The last operation of this step is to add the daemons to the default runlevel.
rc-update add postfix default
rc-update add mysql default
rc-update add syslog-ng default



2. Courier-IMAP

The Courier-IMAP package allows mail clients access to their mailboxes via IMAP(S) and POP3(S).

To make it secure so that the authentication data and the actual mail traffic is encrypted we use SSLv3 for transport layer security (TLS). For SSL a SSL certificate is needed. In this tutorial we create only a self-signed certificate for testing. Later it makes sense to use a certificate which is signed by an official certificate authority (CA).

emerge -va courier-imap courier-authlib

The short way to create self signed test certificates for Courier-IMAP is to use its build-in programs, mkpop3dcert and mkimapdcert.
Just fill out two configuration files:
cd /etc/courier-imap
vi ./pop3d.cnf

RANDFILE = /usr/share/pop3d.rand

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

[ req_dn ]
C=DE
ST=HESSEN
L=WIESBADEN
O=Courier Mail Server
OU=Automatically-generated POP3 SSL key
CN=postfix.test.acodedb.com
emailAddress=postmaster@test.acodedb.com


[ cert_type ]
nsCertType = server


vi ./imapd.cnf

RANDFILE = /usr/share/imapd.rand

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

[ req_dn ]
C=DE
ST=HESSEN
L=WIESBADEN
O=Courier Mail Server
OU=Automatically-generated IMAP SSL key
CN=postfix.test.acodedb.com
emailAddress=postmaster@postfix.test.acodedb.com


[ cert_type ]
nsCertType = server


Run these programs to create the certificates:
# mkpop3dcert
# mkimapdcert

Two files have been created which contain both the certificates and the private keys.
pop3d.pem
imapd.pem


In case you want to do things manually here are some instructions. The following steps are just a dirty way to generate test certificates! A complete and secure setup of CAs and certificates is beyond the scope of this article. The first step is to create your own certificate authority (CA):
# cd /root
# /etc/ssl/misc/CA.sh -newca
...

Some explanations here. The first password "Enter PEM pass phrase:" is the password for the CA itself. Then all the information which should appear in the certificate must be entered like country etc. The field "Common Name (eg, YOUR name) []:" must contain the FQDN of your mail server: "postfix.test.acodedb.com". I leave the following fields empty "A challenge password []:" and "An optional company name []:".

Now the certificate for the certificate authority is issued: ./demoCA/cacert.pem. This certificate authority is not officially known. So for the testing its certificate should be imported into the local mail client. If this certificate is signed by an official authority this last step is not necessary of course,

The next step is to generate the actual certificate for the mail server:
# openssl req -new -nodes -keyout imapd_privatekey.pem -out imapd_req.pem -days 9999
...

Two files have been created, the private key and the certificate request:
imapd_privatekey.pem
imapd_req.pem

Now we have to transform the certificate request into a certificate with our own CA:
# openssl ca -policy policy_anything -out imapd_cert.pem -infiles imapd_req.pem
...

The new certificate is in the new file imapd_cert.pem.

For the testing phase I store all certificates in subfolders of /root:
mkdir /root/certs_imapd/
mv imapd_cert.pem imapd_privatekey.pem imapd_req.pem /root/certs_imapd/

To protect the private key use
chown root:root /root/certs_imapd/imapd_privatekey.pem
chmod 600 /root/certs_imapd/imapd_privatekey.pem

Now we check where courier-imap expects the SSL certificate by opening the SSL configuration file:
vi /etc/courier-imap/imapd-ssl
TLS_CERTFILE=/etc/courier-imap/imapd.pem

courier-imap wants to have private key and certificate together in this file. So we go to /root/certs_imapd:
cat imapd_privatekey.pem imapd_cert.pem > /etc/courier-imap/imapd.pem
chown root:root /etc/courier-imap/imapd.pem
chmod 600 /etc/courier-imap/imapd.pem

imapd-ssl shall listen only on one IP:
vi /etc/courier-imap/imapd-ssl
SSLADDRESS=192.168.1.56

Finally courier-imapd with SSL support can be started:
/etc/init.d/courier-imapd-ssl start

To make sure we have access to the server via IMAPS(993) check the connection with telnet.
# telnet postfix.test.acodedb.com 993
Trying 78.47.177.74...
Connected to postfix.test.acodedb.com.
Escape character is '^]'.


The same has to be done for pop3d.

Creating the certificate:
# cd /root
# openssl req -new -nodes -keyout pop3d_privatekey.pem -out pop3d_req.pem -days 9999
...

Two files have been created, the private key and the certificate request:
pop3d_privatekey.pem
pop3d_req.pem

Again we have to transform the certificate request into a certificate with our own CA:
# openssl ca -policy policy_anything -out pop3d_cert.pem -infiles pop3d_req.pem
...

The new certificate is in the file pop3d_cert.pem.

Like in the previous step we create a tempory directory for the certificate:
mkdir /root/certs_pop3d
mv pop3d_cert.pem pop3d_privatekey.pem pop3d_req.pem /root/certs_pop3d
chown root:root /root/certs_pop3d/pop3d_privatekey.pem
chmod 600 /root/certs_pop3d/pop3d_privatekey.pem

Also courier-imap wants to have private key and certificate together in one file.
So we go to /root/certs_pop3d:
cat pop3d_privatekey.pem pop3d_cert.pem > /etc/courier-imap/pop3d.pem
chown root:root /etc/courier-imap/pop3d.pem
chmod 600 /etc/courier-imap/pop3d.pem

pop3d-ssl shall listen only on one IP:
vi /etc/courier-imap/pop3d-ssl
SSLADDRESS=192.168.1.56

Finally courier-pop3d with SSL support can be started:
/etc/init.d/courier-pop3d-ssl start

To make sure we have access to the server via POP3S(995) check the connection with telnet.
# telnet postfix.test.acodedb.com 995
Trying 78.47.177.74...
Connected to postfix.test.acodedb.com.
Escape character is '^]'.


So far we have configured and running:
Postfix (MTA)
Syslog-ng (system logging daemon)
Courier-IMAP (courier-imap-ssl, courier-pop3d-ssl)

Summary and overview:
Postfix: responsible for sending and receiving emails.
Courier-IMAP: responsible for the access to mailboxes via POP3S and IMAPS.
Courier-authlib: authentication backend for Courier-IMAP (not yet configured)



The relevant process list at this point should look like this:
USER       PID %CPU %MEM    VSZ   RSS TTY      STAT START   TIME COMMAND
...
root      6545  0.0  0.1  40520  2348 ?        Ss   Nov26   0:00 /usr/lib/postfix/master
postfix   6551  0.0  0.1  40620  2420 ?        S    Nov26   0:00 qmgr -l -t fifo -u
root      8243  0.0  0.0  12476   724 ?        Ss   Nov27   0:00 /usr/sbin/syslog-ng
root     31044  0.0  0.0   3704   424 ?        S    Nov27   0:00 /usr/sbin/courierlogger -pid=/var/run/authdaemon.pid -start /usr/lib/courier
root     31045  0.0  0.0  28540  1660 ?        S    Nov27   0:00 /usr/lib/courier/courier-authlib/authdaemond
root     31046  0.0  0.0  28540   552 ?        S    Nov27   0:00 /usr/lib/courier/courier-authlib/authdaemond
root     31047  0.0  0.0  28540   552 ?        S    Nov27   0:00 /usr/lib/courier/courier-authlib/authdaemond
root     31048  0.0  0.0  28540   552 ?        S    Nov27   0:00 /usr/lib/courier/courier-authlib/authdaemond
root     31049  0.0  0.0  28540   552 ?        S    Nov27   0:00 /usr/lib/courier/courier-authlib/authdaemond
root     31050  0.0  0.0  28540   552 ?        S    Nov27   0:00 /usr/lib/courier/courier-authlib/authdaemond
root     13203  0.0  0.0   6876   552 ?        S    04:53   0:00 /usr/lib64/courier-imap/couriertcpd -address=0 -stderrlogger=/usr/lib64/cour
root     13205  0.0  0.0   3576   380 ?        S    04:53   0:00 /usr/lib64/courier-imap/courierlogger imapd-ssl
root     13307  0.0  0.0   6880   568 ?        S    04:54   0:00 /usr/lib64/courier-imap/couriertcpd -address=0 -stderrlogger=/usr/lib64/cour
root     13309  0.0  0.0   3708   520 ?        S    04:54   0:00 /usr/lib64/courier-imap/courierlogger pop3d-ssl
postfix  13930  0.0  0.1  40568  2324 ?        S    05:16   0:00 pickup -l -t fifo -u
...

Note that the command lines are not displayed in their full lenght due to the limited screen size. You can view the full command line bei using the proc pseudo filesystem and the PID:
# cat /proc/13203/cmdline
/usr/lib64/courier-imap/couriertcpd-address=0-stderrlogger=/usr/lib64/courier-imap/courierlogger-stderrloggername=imapd-ssl-maxprocs=
40-maxperip=4-pid=/var/run/imapd-ssl.pid-nodnslookup-noidentlookup993/usr/sbin/couriertls-server-tcpd/usr/sbin/imaplogin/usr/lib64/
courier-imap/courier-imapd.indirect.maildir



3. MySQL

The MySQL server is used as the authentication backend for courier-authlib and Cyrus sasl(auxprop plugin). There will be only one single database which is used for authentication by both services.
I want to put as much configuration and authentication data as possible into MySQL so that everything can be configured via PHPmyadmin.

Let's create the database and tables for that.
Database:
mysql> CREATE DATABASE mailserver;
mysql> USE mailserver;

Virtual domains:
mysql>
CREATE TABLE virtualdomains (
id int unsigned NOT NULL auto_increment,
domain varchar(255) default NULL,
PRIMARY KEY (id),
FULLTEXT KEY `indexdomain` (domain)) ENGINE=MyISAM;

Virtual users:
mysql>  
CREATE TABLE virtualusers (
id int unsigned NOT NULL auto_increment,
name varchar(255) NOT NULL default '-',
realm varchar(255) NOT NULL default 'test.acodedb.com',
userpassword varchar(255) NOT NULL default 'CHANGETHIS',
email varchar(255) NOT NULL default '-',
auth tinyint(1) default 1,
enabled tinyint(1) default 1,
uid int default 9000,
gid int default 9000,
mbox varchar(255) default NULL,
home varchar(255) default '/var/mail/vdomains/',
PRIMARY KEY (id),
FULLTEXT KEY `indexemail` (email)) ENGINE=MyISAM;

Virtual aliases:
mysql>	
CREATE TABLE virtualaliases (
id int unsigned NOT NULL auto_increment,
alias varchar(255) default NULL,
email varchar(255) default '-',
PRIMARY KEY (id),
FULLTEXT KEY `indexalias` (alias)) ENGINE=MyISAM;

Create a read-only user for the mail system to access these tables.
(USAGE means "no privileges")
GRANT USAGE ON *.* TO 'mailserver'@'localhost' IDENTIFIED BY 'CHANGETHIS';
GRANT SELECT ON mailserver.virtualdomains TO 'mailserver'@'localhost';
GRANT SELECT ON mailserver.virtualusers TO 'mailserver'@'localhost';
GRANT SELECT ON mailserver.virtualaliases TO 'mailserver'@'localhost';


Create a virtual test domain, a test user and a test alias.
mysql> 
INSERT INTO virtualdomains VALUES (1,'test.acodedb.com');
INSERT INTO virtualusers VALUES (1,'test1','test.acodedb.com',
'CHANGETHIS','test1@test.acodedb.com',1,1,9001,9001,
'test.acodedb.com/test1/','/var/mail/vdomains/');
INSERT INTO virtualaliases VALUES (1,'aliastest1@test.acodedb.com','test1@test.acodedb.com');


mysql> select * from virtualusers where id=1 \G
*************************** 1. row ***************************
          id: 1
        name: test1
       realm: test.acodedb.com
userpassword: CHANGETHIS
       email: test1@test.acodedb.com
        auth: 1
     enabled: 1
         uid: 9001
         gid: 9001
        mbox: test.acodedb.com/test1/
        home: /var/mail/vdomains/
1 row in set (0.01 sec)

Remember to use plain text passwords in the MySQL database.



4. Postfix

We must tell Postfix that it has to look in the MySQL database for the user authentication data. For that we create a file for each map.
# mkdir /etc/postfix/maps
# chown postfix:root /etc/postfix/maps
# chmod 500 /etc/postfix/maps
# cd /etc/postfix/maps

# vi virtualdomains.cf
user=mailserver
password=CHANGETHIS
hosts=localhost
dbname=mailserver
table=virtualdomains
query=SELECT domain FROM virtualdomains WHERE domain like '%s'

# vi virtualuids.cf
user=mailserver
password=CHANGETHIS
hosts=localhost
dbname=mailserver
table=virtualusers
query=SELECT uid FROM virtualusers WHERE email like '%s' AND enabled like '1'

# vi virtualgids.cf
user=mailserver
password=CHANGETHIS
hosts=localhost
dbname=mailserver
table=virtualusers
query=SELECT gid FROM virtualusers WHERE email like '%s' AND enabled like '1'

# vi virtualaliases.cf
user=mailserver
password=CHANGETHIS
hosts=localhost
dbname=mailserver
table=virtualaliases
query=SELECT email FROM virtualaliases WHERE alias like '%s'

# vi virtualrecipients.cf
user=mailserver
password=CHANGETHIS
hosts=localhost
dbname=mailserver
table=virtualusers
query=SELECT mbox FROM virtualusers WHERE email like '%s' AND enabled like '1'

Protect the files:
# chmod 640 /etc/postfix/maps/*
# chown root:postfix /etc/postfix/maps/*

The permissions should look like this.
# cd /etc/postfix/maps/
# ls -lha
total 28K
dr-x------ 2 postfix root    4.0K Dec  6 11:07 .
drwxr-xr-x 4 root    root    4.0K Dec 14 10:33 ..
-rw-r----- 1 root    postfix  155 Dec  5 11:03 virtualaliases.cf
-rw-r----- 1 root    postfix  157 Dec  1 10:18 virtualdomains.cf
-rw-r----- 1 root    postfix  170 Dec  1 10:19 virtualgids.cf
-rw-r----- 1 root    postfix  171 Dec  1 10:22 virtualrecipients.cf
-rw-r----- 1 root    postfix  170 Dec  1 10:17 virtualuids.cf

Set the following parameters in /etc/postfix/main.cf to tell Postfix how to access the maps
virtual_mailbox_domains=mysql:/etc/postfix/maps/virtualdomains.cf
virtual_mailbox_maps=mysql:/etc/postfix/maps/virtualrecipients.cf
virtual_uid_maps=mysql:/etc/postfix/maps/virtualuids.cf
virtual_gid_maps=mysql:/etc/postfix/maps/virtualgids.cf
virtual_alias_maps=mysql:/etc/postfix/maps/virtualaliases.cf
virtual_mailbox_base=/var/mail/vdomains

Now test if Postfix can access the data in MySQL.
# postmap -q "test.acodedb.com" mysql:/etc/postfix/maps/virtualdomains.cf
test.acodedb.com
# postmap -q "test1@test.acodedb.com" mysql:/etc/postfix/maps/virtualuids.cf
9001
# postmap -q "test1@test.acodedb.com" mysql:/etc/postfix/maps/virtualgids.cf
9001
# postmap -q "test1@test.acodedb.com" mysql:/etc/postfix/maps/virtualrecipients.cf
test.acodedb.com/test1/



5. Cyrus SASL

Until now we have nothing running that realises authentication for SMTPS connections (SMTP AUTH). Courier-IMAP does this for IMAPS and POP3S access only. For SMTPS we will use the authentication framework Cyrus SASL and offer the following mechanisms: CRAM-MD5, DIGEST-MD5. The auxprop plugin is used as the password verification service to communicate with MySQL. The communication with the client will be encrypted with transport layer security (TLSv1).

Cyrus SASL will use the same structure in MySQL we have already created, no need to store redundant data.

Install Cyrus SASL.
emerge -va cyrus-sasl

As mentioned before Cyrus SASL does not run as a daemon. It is just used by Postfix. Therefore we have to tell the smtpd daemon from Postfix how to access MySQL via Cyrus SASL. Postfix looks up this information in the Cyrus SASL configuration file which has the name of the corresponding Postfix daemon:
vi /etc/sasl2/smtpd.conf

Remove all existing records and copy/paste the following:
pwcheck_method: auxprop
mech_list: CRAM-MD5 DIGEST-MD5
log_level: 3
# auxprop parameters for database access
auxprop_plugin: sql
sql_engine: mysql
sql_hostnames: localhost
sql_database: mailserver
sql_user: mailserver
sql_passwd: CHANGETHIS
sql_select: select %p from virtualusers where name like '%u' and realm like '%r' and auth = '1'
sql_usessl: no



6. Courier-authlib

Courier-authlib is the IMAPS/POP3S counterpart for Cyrus SASL(auxprop plugin).
First tell Courier-authlib to use the MySQL module for authentication.
vi /etc/courier/authlib/authdaemonrc
authmodulelist="authmysql "
DEBUG_LOGIN=1

Then configure the module itself. The first part is the login data for the MySQL server so that Courier-authlib has access to the database. The second part tells Courier-authlib the database name and table names where it finds the authentication data of the email accounts. The third part contains the relevant table columns.
vi /etc/courier/authlib/authmysqlrc
MYSQL_SERVER            localhost
MYSQL_USERNAME          mailserver
MYSQL_PASSWORD          CHANGETHIS
MYSQL_PORT              0
MYSQL_OPT               0

MYSQL_DATABASE          mailserver
MYSQL_USER_TABLE        virtualusers

MYSQL_CLEAR_PWFIELD     userpassword
MYSQL_UID_FIELD         uid
MYSQL_GID_FIELD         gid
MYSQL_LOGIN_FIELD       email
MYSQL_HOME_FIELD        home
MYSQL_NAME_FIELD        name
MYSQL_MAILDIR_FIELD	mbox




7. SSL certificate for Postfix

Up to this point we have certificates for the CA and Courier-IMAP but still not for Postfix.
The procedure ist the same as for the previous certificates. First, let's create the private key and certificate request.
# cd /root
# openssl req -new -nodes -keyout postfix_privatekey.pem -out postfix_req.pem -days 9999
...

We get the files postfix_privatekey.pem and postfix_req.pem.
Now our own CA is used again to create the certificate from the certificate request:
# openssl ca -policy policy_anything -out postfix_cert.pem -infiles postfix_req.pem
...

The result is postfix_cert.pem

To have all relevant files together create another new directory:
mkdir /root/certs_postfix
mv  postfix_cert.pem postfix_privatekey.pem postfix_req.pem /root/certs_postfix
chown root:root /root/certs_postfix/postfix_privatekey.pem
chmod 600 /root/certs_postfix/postfix_privatekey.pem

The next step is to make the certificate for Postfix and the certificate for our CA available for Postfix:
mkdir /etc/postfix/certs
cp /root/certs_postfix/postfix_privatekey.pem /etc/postfix/certs
cp /root/certs_postfix/postfix_cert.pem /etc/postfix/certs
cp /root/demoCA/cacert.pem /etc/postfix/certs
chown root:root /etc/postfix/certs/postfix_privatekey.pem
chmod 600 /etc/postfix/certs/postfix_privatekey.pem



8. Configure Postfix to use TLS and Cyrus SASL

The use of TLS and Cyrus SASL is configured in the main Postfix configuration file:
vi /etc/postfix/main.cf

Add the following lines at the end of the file:
# local accounts
alias_maps = hash:/etc/mail/aliases

smtpd_sasl_auth_enable = yes
smtpd_sasl2_auth_enable = yes
smtpd_sasl_security_options = noanonymous, noplaintext
smtpd_sasl_tls_security_options = noanonymous, noplaintext
broken_sasl_auth_clients = yes
smtpd_sasl_local_domain = $myhostname
# must be left without value
smtpd_sasl_local_domain =
smtpd_recipient_restrictions =
 permit_sasl_authenticated,
 permit_mynetworks,
 reject_unauth_destination

smtp_use_tls = yes
smtp_tls_note_starttls_offer = yes
smtpd_tls_security_level = may
smtpd_tls_auth_only = yes
smtpd_tls_key_file = /etc/postfix/certs/postfix_privatekey.pem
smtpd_tls_cert_file = /etc/postfix/certs/postfix_cert.pem
smtpd_tls_CAfile = /etc/postfix/certs/cacert.pem
smtpd_tls_loglevel = 3
smtpd_tls_received_header = yes
smtpd_tls_session_cache_timeout = 3600s
tls_random_source = dev:/dev/random

At this point we have finished the configuration of the basic components of our mail system.
# /etc/init.d/postfix restart
# /etc/init.d/courier-authlib restart



9. Virtual mail hosting

So far Postfix can handle only one domain. Postfix must be configured for virtual domains which means that email accounts for several domains can be hosted. The first step is to define a directory which will contain all emails of all domains.
# cd /var/mail
# mkdir vdomains
# chmod 755 vdomains/

The Postfix virtual daemon will write into this directory with different UID and GID for each email account. The users don't need a system account. All information is stored in the MySQL database.
For each new virtual account the user data must be stored in MySQL and a directory structure must be created. The data for test1@test.acodedb.com is already stored in MySQL. So here we just create the structure in the filesystem:
cd /var/mail/vdomains
mkdir test.acodedb.com
chgrp mail test.acodedb.com/
chmod g+w test.acodedb.com/

cd test.acodedb.com/
mkdir test1
chown 9001:9001 test1/
chmod 700 test1/

cd test1/
mkdir cur new tmp
chmod 700 *
chown 9001:9001 *

The result should look like this:
# cd /var/mail/vdomains
postfix vdomains # ls -lha
total 16K
drwxrwxr-x 4 root mail 4.0K Dec  8 14:20 .
drwxrwxr-x 3 root mail 4.0K Dec  5 11:11 ..
drwxrwxr-x 7 root mail 4.0K Dec 10 10:59 test.acodedb.com
# tree -Apug -L 4
.
??? [drwxrwxr-x root     mail    ]  test.acodedb.com
    ??? [drwx------ 9001     9001    ]  test1
        ??? [drwx------ 9001     9001    ]  cur
        ??? [drwx------ 9001     9001    ]  new
        ??? [drwx------ 9001     9001    ]  tmp



10. Final configuration

We are almost done.
To enable LOGIN and CRAM-MD5 for imap-ssl a line in /etc/courier-imap/imapd must be changed
(yes it's /etc/courier-imap/imapd not /etc/courier-imap/imapd-ssl):
IMAP_CAPABILITY="IMAP4rev1 UIDPLUS CHILDREN NAMESPACE THREAD=ORDEREDSUBJECT 
THREAD=REFERENCES SORT QUOTA AUTH=LOGIN AUTH=CRAM-MD5 IDLE"

pop3-ssl has CRAM-MD5 activated per default. To configure it according to our plan I remove the CRAM-SHA mechanisms:
vi  /etc/courier-imap/pop3d
POP3AUTH_ORIG="PLAIN LOGIN CRAM-MD5"

If you have configured /etc/sasl2/smtpd.conf as described before no plain mechanisms are available via SMTPS. Actually this is not necessary because Postfix is also configured to not offer plain mechanisms for SMTPS in /etc/postfix/main.cf. Just to be on the safe side.
# /etc/init.d/courier-authlib restart


Now, I assume there are no problems and we can switch to production mode. Therefore we reduce the debug level for all involved services:
vi /etc/postfix/main.cf
smtpd_tls_loglevel = 0

vi /etc/sasl2/smtpd.conf
log_level: 1

vi /etc/courier/authlib/authdaemonrc
DEBUG_LOGIN=0

vi /etc/postfix/master.cf
smtp      inet  n       -       n       -       -       smtpd

To make sure all involved services come up after a reboot we add them to the default runlevel:
# rc-update add syslog-ng default
# rc-update add vixie-cron default
# rc-update add mysql default
# rc-update add postfix default
# rc-udpate add courier-authlib default
# rc-update add courier-pop3d-ssl default
# rc-update add courier-imapd-ssl default

Disable all unwanted services:
cd /etc/init.d/
mv saslauthd saslauthd_OFF
mv courier-pop3d courier-pop3d_OFF
mv courier-imapd courier-imapd_OFF



11. Clean-up

Finally we remove the temporary folders for the certificates:
rm -r /root/certs_imapd
rm -r /root/certs_pop3d


The setup is complete. All necessary services are installed and configured.
# netstat -tulpen
Active Internet connections (only servers)
Proto Recv-Q Send-Q Local Address           Foreign Address         State       User       Inode      PID/Program name
tcp        0      0 192.168.1.56:993            0.0.0.0:*               LISTEN      0          11497695   6952/couriertcpd
tcp        0      0 192.168.1.56:995            0.0.0.0:*               LISTEN      0          11497819   7063/couriertcpd
tcp        0      0 192.168.1.56:3306           0.0.0.0:*               LISTEN      60         2743167    9666/mysqld
tcp        0      0 192.168.1.56:22             0.0.0.0:*               LISTEN      0          6551044    29456/sshd
tcp        0      0 192.168.1.56:25             0.0.0.0:*               LISTEN      0          11497211   6600/master


In case of problems increase the log levels and look in the following files:
/var/log/messages
/var/log/mail.log
/var/log/authdaemond.log