Introduction
This tutorial has been compiled after many months of searching the Internet for a viable virtual mail server
installation. Many websites had information on this subject but the tutorials that were found either were incomplete
or just did not work for us.
The tutorial presented below provides a working installation of Postfix, Cyrus SASL and Courier IMAP, connecting
to a MySQL database to provide virtual email services on RedHat 9/Fedora Core 2. Cyrus SASL is used to provide
authenticated SMTP, also via a MySQL database.
We will guide you through the installation of all the software that we used together with details of problems we
encountered and then provide the required configuration to provide a virtual mail server solution for your Red Hat 9
or Fedora Core 2 server.
Overview of the virtual mail solution
- Provides authenticated SMTP via Cyrus SASL. Cyrus SASL authenticates users by connecting to a MySQL database
- The Postfix MTA will allow SMTP Authenticated SASL connections instead of using relay domains.
- Postfix maildrop will deliver email to both real and virtual users home directories using Maildir style mail boxes.
- Courier IMAP and POP3 will be available to allow users to retrieve their email. Authentication will again be provided by a MySQL database.
- Virtual users will not require an actual real Linux user account.
Software Required for this Tutorial
Installation and Configuration
MySQL Installation and Configuration
Installing the software
Generally, it is easier to install MySQL via the RPM distibution than to compile from source. This tutorial will
provide information on installing the RPM version only although there is no reason why a version built from source
would not work so long as shared libraries are included.
$ cd /software-install/
$ wget http://dev.mysql.com/get/Downloads/MySQL-4.0/MySQL-client-4.0.18-0.i386.rpm \
/from/http://www.mirror.ac.uk/sites/ftp.mysql.com/
$ wget http://dev.mysql.com/get/Downloads/MySQL-4.0/MySQL-server-4.0.18-0.i386.rpm \
/from/http://www.mirror.ac.uk/sites/ftp.mysql.com/
$ wget http://dev.mysql.com/get/Downloads/MySQL-4.0/MySQL-devel-4.0.18-0.i386.rpm \
/from/http://www.mirror.ac.uk/sites/ftp.mysql.com/
$ wget http://dev.mysql.com/get/Downloads/MySQL-4.0/MySQL-shared-4.0.18-0.i386.rpm \
/from/http://www.mirror.ac.uk/sites/ftp.mysql.com/
$ rpm -i MySQL-client-4.0.18-0.i386.rpm \
MySQL-server-4.0.18-0.i386.rpm \
MySQL-devel-4.0.18-0.i386.rpm \
MySQL-shared-4.0.18-0.i386.rpm
This will install the four MySQL RPM packages and provide all the required startup scripts. MySQL is now fully installed and ready for use.
MySQL can be started and stopped using the /etc/init.d/ scripts as installed as part of the RPM.
$ /etc/init.d/mysql start
$ /etc/init.d/mysql stop
Ensure that MySQL has been started in preparation for the next section.
Configuring the software
You will first need to change the root password for MySQL for security reasons.
$ mysql
mysql> use mysql;
mysql> UPDATE user SET password ='your-password' WHERE User='root';
You now need to create a new user and a database for the tables that will be used by Postfix, Cyrus SASL and
Courier IMAP. This user must have full grant permissions on this database. Note that the grant tables will also
need to be reloaded.
mysql> CREATE DATABASE postfix;
mysql> GRANT ALL on postfix.* to postfix@localhost identified by 'choose-a-password';
mysql> quit;
$ mysqladmin -uroot -pyour-password reload
We now need to create the required tables in our new database. Begin by logging back into MySQL as the postfix user.
$ mysql -upostfix -pyour-password postfix;
mysql> DROP TABLE IF EXISTS postfix_access;
mysql> 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;
mysql> DROP TABLE IF EXISTS postfix_alias;
mysql> 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;
mysql> DROP TABLE IF EXISTS postfix_relocated;
mysql> 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;
mysql> DROP TABLE IF EXISTS postfix_smtp;
mysql> CREATE TABLE postfix_smtp ( \
id int(11) NOT NULL auto_increment, \
email varchar(100) NOT NULL default '', \
clear varchar(20) NOT NULL default '', \
PRIMARY KEY (id) \
) TYPE=MyISAM;
mysql> DROP TABLE IF EXISTS postfix_transport;
mysql> 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;
mysql> DROP TABLE IF EXISTS postfix_users;
mysql> CREATE TABLE postfix_users ( \
id int(11) unsigned NOT NULL auto_increment, \
email varchar(128) NOT NULL default '', \
clear varchar(128) NOT NULL default '', \
uid int(11) unsigned NOT NULL default '0', \
gid int(11) unsigned NOT NULL default '0', \
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;
mysql> DROP TABLE IF EXISTS postfix_virtual;
mysql> 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;
We recommend that you create a my.cnf file in your /etc/ directory and enable MySQL logging. This will aid you
in testing the installation once all the software is installed. I have provided an example my.cnf file below.
This will create a file in /var/log/mysql.log and will show all activity on the MySQL database:
Example my.cnf file with logging enabled
Cyrus SASL Installation and Configuration
Installing the software
The following assumes that you have MySQL setup in /usr/local/mysql. We will be installing Cyrus SASL from source
with built in MySQL support to allow authentication via a MySQL database. Prior to installing these modules, you
should remove any pre-installed ones that may exist in /usr/lib, /usr/local/lib, and /usr/local/lib/sasl2.
$ wget ftp://ftp.andrew.cmu.edu/pub/cyrus-mail/cyrus-sasl-2.1.18.tar.gz
$ export CPPFLAGS="-I/usr/include/mysql"
$ export LDFLAGS="-L/usr/lib/mysql -lmysqlclient -lz -lm"
$ ./configure \
--enable-anon \
--enable-plain \
--enable-login \
--enable-sql \
--disable-krb4 \
--disable-otp \
--disable-cram \
--disable-digest \
--with-mysql=/usr/lib/mysql \
--without-pam \
--without-saslauthd \
--without-pwcheck \
--with-plugindir=/usr/local/lib/sasl2
$ make
$ make install
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).
If this step is not carried out then the postfix mail log will report the following error when attempting
to send email via SMTP: fatal: no SASL authentication mechanisms
$ echo "/usr/local/lib" >> /etc/ld.so.conf
$ ldconfig
You may need to add the following symbolic link. I think you can just do away with /usr/lib/sasl and /usr/lib/sasl2
if they are present as part of the Red Hat/Fedora installation.
$ ln -s /usr/local/lib/sasl2/ /usr/lib/sasl2
NOTE: If an error is seen during make you will need to edit lib/checkpw.c file and add an include
for errno.h. This appears to be a bug in the distribution that we used for this tutorial.
$ vi /root/tar.gz/cyrus-sasl-2.1.18/lib/checkpw.c
#include <errno.h>
Configuring the software
We now need to tell Cyrus SASL to use a MySQL database for authentication and which table contains the authentication details.
$ vi /usr/local/lib/sasl2/smtpd.conf
pwcheck_method: auxprop
auxprop_plugin: sql
sql_engine: mysql
mech_list: sql plain login
sql_hostnames: localhost
sql_user: postfix
sql_passwd: your-password
sql_database: postfix
sql_statement: SELECT clear FROM postfix_smtp WHERE email = '%u@%r'
sql_verbose: yes
Postfix Installation and Configuration
Installing the software
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.
Add the postfix user and postdrop group. You may need to change the UID and GID accordinly.
$ vi /etc/passwd
postfix:x:101:103::/var/spool/postfix:/bin/true
$ vi /etc/group
postdrop:x:102:
We will be compiling Postfix from source with MySQL support.
$ wget ftp://postfix.ulimit.org/postfix/official/postfix-2.1.1.tar.gz
$ tar zxvf postfix-2.0.16.tar.gz
$ make makefiles \
'CCARGS=-DHAS_MYSQL -I/usr/include/mysql -DUSE_SASL_AUTH -I/usr/local/include/sasl' \
'AUXLIBS=-L/usr/lib/mysql -lmysqlclient -lz -lm -L/usr/local/lib -lsasl2'
$ make install
Configuring the software
We now need to configure Postfix to use MySQL. We will be editing the /etc/postfix/main.cf file and also creating
a series of MySQL specific .cf files that are used to connect to the MySQL tables we created earlier.
$ vi /etc/postfix/main.cf
home_mailbox = Maildir/
recipient_delimiter = +
mydestination = $myhostname, $transport_maps
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
local_recipient_maps = $alias_maps $virtual_mailbox_maps unix:passwd.byname
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
queue_directory = /var/spool/postfix
command_directory = /usr/sbin
daemon_directory = /usr/libexec/postfix
mail_owner = postfix
myhostname = domain.com
mydomain = sub.domain.com
myorigin = $mydomain
unknown_local_recipient_reject_code = 550
debug_peer_level = 2
debugger_command = \
PATH=/bin:/usr/bin:/usr/local/bin:/usr/X11R6/bin \
xxgdb $daemon_directory/$process_name $process_id & sleep 5 \
sendmail_path = /usr/sbin/sendmail
newaliases_path = /usr/bin/newaliases
mailq_path = /usr/bin/mailq
setgid_group = postdrop
html_directory = no
manpage_directory = /usr/local/man
sample_directory = /etc/postfix
readme_directory = no
broken_sasl_auth_clients = yes
smtpd_sasl_auth_enable = yes
smtpd_sasl_security_options = noanonymous
smtpd_recipient_restrictions = permit_sasl_authenticated, reject_unauth_destination
smtpd_sasl_local_domain = $myhostname
$ vi /etc/postfix/mysql-aliases.cf
user = postfix
password = your-password
dbname = postfix
table = postfix_alias
select_field = destination
where_field = alias
hosts = localhost
$ vi /etc/postfix/mysql-client.cf
user = postfix
password = your-password
dbname = postfix
table = postfix_access
select_field = access
where_field = source
additional_conditions = and type = 'client'
hosts = localhost
$ vi /etc/postfix/mysql-recipient.cf
user = postfix
password = your-password
dbname = postfix
table = postfix_access
select_field = access
where_field = source
additional_conditions = and type = 'recipient'
hosts = localhost
$ vi /etc/postfix/mysql-relocated.cf
user = postfix
password = your-password
dbname = postfix
table = postfix_relocated
select_field = destination
where_field = email
hosts = localhost
$ vi /etc/postfix/mysql-sender.cf
user = postfix
password = your-password
dbname = postfix
table = postfix_access
select_field = access
where_field = source
additional_conditions = and type = 'sender'
hosts = localhost
$ vi /etc/postfix/mysql-transport.cf
user = postfix
password = your-password
dbname = postfix
table = postfix_transport
select_field = destination
where_field = domain
hosts = localhost
$ vi /etc/postfix/mysql-virtual.cf
user = postfix
password = your-password
dbname = postfix
table = postfix_virtual
select_field = destination
where_field = email
hosts = localhost
$ vi /etc/postfix/mysql-virtual-gid.cf
user = postfix
password = your-password
dbname = postfix
table = postfix_users
select_field = gid
where_field = email
additional_conditions = and postfix = 'y'
hosts = localhost
$ vi /etc/postfix/mysql-virtual-maps.cf
user = postfix
password = your-password
dbname = postfix
table = postfix_users
select_field = maildir
where_field = email
additional_conditions = and postfix = 'y'
hosts = localhost
$ vi /etc/postfix/mysql-virtual-uid.cf
user = postfix
password = your-password
dbname = postfix
table = postfix_users
select_field = uid
where_field = email
additional_conditions = and postfix = 'y'
hosts = localhost
You will now need to create the vmail user and group. You may need to change the UID and GID accordinly.
$ vi /etc/passwd
vmail:x:1008:1008::/home/vmail:/bin/true
$ vi /etc/group
vmail:x:1008:
We must now create the directory where email for virtual users will be stored.
$ mkdir /home/vmail/
$ chown vmail.vmail /home/vmail/
Courier IMAP Installation and Configuration
Installing the software
As with Postfix, Courier IMAP should be simple to install. We will be installing Courier IMAP from source with
MySQL support. We will use /usr/local/courier as the base directory for the installation.
<as non root user>
$ wget http://www.courier-mta.org/beta/imap/courier-imap-3.0.4.20040524.tar.bz2
$ bzip2 -dc courier-imap-3.0.4.20040524.tar.bz2 | tar xvf -
$ cd courier-imap-3.0.4.20040524
$ ./configure \
--prefix=/usr/local/courier \
--with-authmysql \
--with-mysql-libs=/usr/lib/mysql/ \
--with-mysql-includes=/usr/include/mysql \
--with-redhat
$ make
<as root>
$ make install
$ make install-configure
The /usr/local/courier/bin/ contains the IMAP and POP init.d scripts. Follow the following steps to copy them
into the correct location.
$ cp /usr/local/courier/bin/imapd /etc/init.d/imapd
$ cp /usr/local/courier/bin/ipop3d /etc/init.d/ipop3d
$ chmod 700 /etc/init.d/imapd
$ chmod 700 /etc/init.d/ipop3d
The IMAP and POP3 daemons can be started and stopped using the following commands.
$ /etc/init.d/imapd start
$ /etc/init.d/imapd stop
$ /etc/init.d/ipop3d start
$ /etc/init.d/ipop3d stop
Configuring the software
We now need to tell Courier IMAP to use a MySQL database for authentication and which table contains the authentication details.
$ vi /usr/local/courier/etc/authdaemonrc
authmodulelist="authmysql authpam"
$ vi /usr/local/courier/etc/authmysqlrc
MYSQL_SERVER localhost
MYSQL_USERNAME postfix
MYSQL_PASSWORD your-password
MYSQL_PORT 0
MYSQL_DATABASE postfix
MYSQL_USER_TABLE postfix_users
MYSQL_CLEAR_PWFIELD clear
MYSQL_UID_FIELD uid
MYSQL_GID_FIELD gid
MYSQL_LOGIN_FIELD email
MYSQL_HOME_FIELD homedir
MYSQL_MAILDIR_FIELD maildir
MYSQL_QUOTA_FIELD quota
Testing The Installation
You should now have a fully functioning virtual mail server. We will now test the installation with some test
accounts. The first step will be to add some test domain names into the /etc/hosts file to allow us to test the sending
of email to virtual domains.
$ vi /etc/hosts
domain1.com 100.0.0.1 (enter your ip address here)
domain2.com 100.0.0.1 (enter your ip address here)
Next we need to add some real unix users so that we can test the sending of email to local accounts (i.e. non virtual accounts).
$ vi /etc/passwd
user1:x:1013:1013::/home/user1:/bin/bash
user2:x:1014:1014::/home/user2:/bin/bash
$ vi /etc/group
user1:x:1013:
user2:x:1014:
We now need to populate our MySQL database with some test data
INSERT INTO postfix_alias VALUES (1,'root','user1');
INSERT INTO postfix_smtp VALUES (1,'user1@domain1.com','user1');
INSERT INTO postfix_smtp VALUES (2,'user2@domain2.com','user2');
INSERT INTO postfix_transport VALUES (1,'localdomain.co.uk','local:');
INSERT INTO postfix_transport VALUES (2,'domain1.com','virtual:');
INSERT INTO postfix_transport VALUES (3,'domain2.com','virtual:');
INSERT INTO postfix_users VALUES (1,'vuser@domain1.com','vuser', \
1008 (vmail_uid),1013 (main_user_gid),'/home/vmail','domain1.com/vuser/','','Y','Y');
INSERT INTO postfix_users VALUES (2,'vuser@domain2.com','vuser', \
1008 (vmail_uid),1014 (main_user_gid),'/home/vmail','domain2.com/vuser/\r\n','','Y','Y');
INSERT INTO postfix_virtual VALUES (1,'@domain1.com','user1');
INSERT INTO postfix_virtual VALUES (2,'user1@domain1.com','user1');
INSERT INTO postfix_virtual VALUES (3,'vuser@domain1.com','vuser@domain1.com');
INSERT INTO postfix_virtual VALUES (4,'forwarding@domain1.com','you@hotmail.com');
INSERT INTO postfix_virtual VALUES (1,'@domain2.com','user2');
INSERT INTO postfix_virtual VALUES (2,'user1@domain2.com','user2');
INSERT INTO postfix_virtual VALUES (3,'vuser@domain2.com','vuser@domain2.com');
INSERT INTO postfix_virtual VALUES (4,'forwarding@domain2.com','you@hotmail.com');
You will now need to start all the programs that have just been installed.
$ /etc/init.d/mysql start
$ /etc/init.d/postfix start
$ /etc/init.d/impad start
$ /etc/init.d/pop3d start
Send an email to vuser@domain1.com by performing the following.
$ smtp localhost 25
Trying 127.0.0.1...
Connected to localhost.
Escape character is '^]'.
220 localdomain.co.uk ESMTP Postfix
mail from: user1@domain1.com
250 Ok
rcpt to: user1@domain2.com
250 Ok
data
354 End data with .
Hello user1 at domain2 this is user1 at domain1
.
250 Ok: queued as 9F0861531F
This should have sent an email to virtual user user1@domain2.com. This can be confirmed by looking in the
/home/vmail/domain2/user1/new/ directory. If an email is present then it would appear that everything is working.
WELL DONE!
Knowledgebase
|