Virtual Users And Domains With Postfix, Courier And MySQL (Fedora 8) - Page 2

7 Create The MySQL Database For Postfix/Courier

We create a database called mail:

mysqladmin -u root -p create mail

Next, we go to the MySQL shell:

mysql -u root -p

On the MySQL shell, we create the user mail_admin with the passwort mail_admin_password (replace it with your own password) who has SELECT,INSERT,UPDATE,DELETE privileges on the mail database. This user will be used by Postfix and Courier to connect to the mail database:

GRANT SELECT, INSERT, UPDATE, DELETE ON mail.* TO 'mail_admin'@'localhost' IDENTIFIED BY 'mail_admin_password';
GRANT SELECT, INSERT, UPDATE, DELETE ON mail.* TO 'mail_admin'@'localhost.localdomain' IDENTIFIED BY 'mail_admin_password';

Still on the MySQL shell, we create the tables that Postfix and Courier need:

USE mail;

CREATE TABLE domains (
domain varchar(50) NOT NULL,
PRIMARY KEY (domain) )

CREATE TABLE forwardings (
source varchar(80) NOT NULL,
destination TEXT NOT NULL,
PRIMARY KEY (source) )

email varchar(80) NOT NULL,
password varchar(20) NOT NULL,
quota INT(10) DEFAULT '10485760',

CREATE TABLE transport (
domain varchar(128) NOT NULL default '',
transport varchar(128) NOT NULL default '',
UNIQUE KEY domain (domain)


As you may have noticed, with the quit; command we have left the MySQL shell and are back on the Linux shell.

The domains table will store each virtual domain that Postfix should receive emails for (e.g.


The forwardings table is for aliasing one email address to another, e.g. forward emails for to

source destination

The users table stores all virtual users (i.e. email addresses, because theemail address and user name is the same) and passwords (in encrypted form!) and a quota value for each mail box (in this example the default value is 10485760 bytes which means 10MB).

email password quota No9.E4skNvGa. ("secret" in encrypted form) 10485760

The transport table is optional, it is for advanced users. It allows to forward mails for single users, whole domains or all mails to another server. For example,

domain transport smtp:[]

would forward all emails for via the smtp protocol to the server with the IP address (the square brackets [] mean "do not make a lookup of the MX DNS record" (which makes sense for IP addresses...). If you use a fully qualified domain name (FQDN) instead you would not use the square brackets.).


8 Configure Postfix

Now we have to tell Postfix where it can find all the information in the database. Therefore we have to create six text files. You will notice that I tell Postfix to connect to MySQL on the IP address instead of localhost. This is because Postfix is running in a chroot jail and does not have access to the MySQL socket which it would try to connect if I told Postfix to use localhost. If I use Postfix uses TCP networking to connect to MySQL which is no problem even in a chroot jail (the alternative would be to move the MySQL socket into the chroot jail which causes some other problems).

Now let's create our six text files.

vi /etc/postfix/

user = mail_admin
password = mail_admin_password
dbname = mail
query = SELECT domain AS virtual FROM domains WHERE domain='%s'
hosts =

vi /etc/postfix/

user = mail_admin
password = mail_admin_password
dbname = mail
query = SELECT destination FROM forwardings WHERE source='%s'
hosts =

vi /etc/postfix/

user = mail_admin
password = mail_admin_password
dbname = mail
query = SELECT CONCAT(SUBSTRING_INDEX(email,'@',-1),'/',SUBSTRING_INDEX(email,'@',1),'/') FROM users WHERE email='%s'
hosts =

vi /etc/postfix/

user = mail_admin
password = mail_admin_password
dbname = mail
query = SELECT email FROM users WHERE email='%s'
hosts =

vi /etc/postfix/

user = mail_admin
password = mail_admin_password
dbname = mail
query = SELECT transport FROM transport WHERE domain='%s'
hosts =

vi /etc/postfix/

user = mail_admin
password = mail_admin_password
dbname = mail
query = SELECT quota FROM users WHERE email='%s'
hosts =

chmod o= /etc/postfix/mysql-virtual_*.cf
chgrp postfix /etc/postfix/mysql-virtual_*.cf

Now we create a user and group called vmail with the home directory /home/vmail. This is where all mail boxes will be stored.

groupadd -g 5000 vmail
useradd -g vmail -u 5000 vmail -d /home/vmail -m

Next we do some Postfix configuration. Go sure that you replace with a valid FQDN, otherwise your Postfix might not work properly!

postconf -e 'myhostname ='
postconf -e 'mydestination =, localhost, localhost.localdomain'
postconf -e 'mynetworks ='
postconf -e 'virtual_alias_domains ='
postconf -e ' virtual_alias_maps = proxy:mysql:/etc/postfix/, mysql:/etc/postfix/'
postconf -e 'virtual_mailbox_domains = proxy:mysql:/etc/postfix/'
postconf -e 'virtual_mailbox_maps = proxy:mysql:/etc/postfix/'
postconf -e 'virtual_mailbox_base = /home/vmail'
postconf -e 'virtual_uid_maps = static:5000'
postconf -e 'virtual_gid_maps = static:5000'
postconf -e 'smtpd_sasl_auth_enable = yes'
postconf -e 'broken_sasl_auth_clients = yes'
postconf -e 'smtpd_recipient_restrictions = permit_mynetworks, permit_sasl_authenticated, reject_unauth_destination'
postconf -e 'smtpd_use_tls = yes'
postconf -e 'smtpd_tls_cert_file = /etc/postfix/smtpd.cert'
postconf -e 'smtpd_tls_key_file = /etc/postfix/smtpd.key'
postconf -e 'transport_maps = proxy:mysql:/etc/postfix/'
postconf -e 'virtual_create_maildirsize = yes'
postconf -e 'virtual_maildir_extended = yes'
postconf -e 'virtual_mailbox_limit_maps = proxy:mysql:/etc/postfix/'
postconf -e 'virtual_mailbox_limit_override = yes'
postconf -e 'virtual_maildir_limit_message = "The user you are trying to reach is over quota."'
postconf -e 'virtual_overquota_bounce = yes'
postconf -e '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'
postconf -e 'inet_interfaces = all'

Afterwards we create the SSL certificate that is needed for TLS:

cd /etc/postfix
openssl req -new -outform PEM -out smtpd.cert -newkey rsa:2048 -nodes -keyout smtpd.key -keyform PEM -days 365 -x509

Country Name (2 letter code) [GB]: <-- Enter your Country Name (e.g., "DE").
State or Province Name (full name) [Berkshire]:
<-- Enter your State or Province Name.
Locality Name (eg, city) [Newbury]:
<-- Enter your City.
Organization Name (eg, company) [My Company Ltd]:
<-- Enter your Organization Name (e.g., the name of your company).
Organizational Unit Name (eg, section) []:
<-- Enter your Organizational Unit Name (e.g. "IT Department").
Common Name (eg, your name or your server's hostname) []:
<-- Enter the Fully Qualified Domain Name of the system (e.g. "").
Email Address []:
<-- Enter your Email Address.

Then change the permissions of the smtpd.key:

chmod o= /etc/postfix/smtpd.key


9 Configure Saslauthd

Edit /usr/lib/sasl2/smtpd.conf. It should look like this:

vi /usr/lib/sasl2/smtpd.conf

pwcheck_method: authdaemond
log_level: 3
mech_list: PLAIN LOGIN

Then turn off Sendmail and start Postfix, saslauthd, and courier-authlib:

chmod 755 /var/spool/authdaemon
chkconfig --levels 235 courier-authlib on
/etc/init.d/courier-authlib start

chkconfig --levels 235 sendmail off
chkconfig --levels 235 postfix on
chkconfig --levels 235 saslauthd on
/etc/init.d/sendmail stop
/etc/init.d/postfix start
/etc/init.d/saslauthd start


10 Configure Courier

Now we have to tell Courier that it should authenticate against our MySQL database. First, edit /etc/authlib/authdaemonrc and change the value of authmodulelist so that it reads

vi /etc/authlib/authdaemonrc

#authmodulelist="authuserdb authpam authpgsql authldap authmysql authcustom authpipe"

Then edit /etc/authlib/authmysqlrc. It should look exactly like this (again, make sure to fill in the correct database details):

cp /etc/authlib/authmysqlrc /etc/authlib/authmysqlrc_orig
cat /dev/null > /etc/authlib/authmysqlrc
vi /etc/authlib/authmysqlrc

MYSQL_SERVER localhost
MYSQL_PASSWORD mail_admin_password
MYSQL_HOME_FIELD "/home/vmail"

Then restart Courier:

chkconfig --levels 235 courier-imap on
/etc/init.d/courier-authlib restart
/etc/init.d/courier-imap restart

By running

telnet localhost pop3

you can see if your POP3 server is working correctly. It should give back +OK Hello there. (type quit to get back to the Linux shell):

[root@server1 postfix]# telnet localhost pop3
Connected to localhost.
Escape character is '^]'.
+OK Hello there.
+OK Better luck next time.
Connection closed by foreign host.
[root@server1 postfix]#

Share this page:

4 Comment(s)

Add comment


From: at: 2008-08-27 18:19:29

I also needed to install cyrus-sasl:

yum -y install cyrus-sasl

It wasn´t installed before and  isn´t present in paragraph 3 of this article.

From: Slawek at: 2008-09-17 08:16:19

You don't have to disable selinux !

I found help here:

I had problems during configure postfix + mysql + courier-imap
(elements: postfix_virtual_t , courier_authdaemon_t , postfix_smtpd_t)

You do have to analyze logs: /var/log/messages and /var/log/audit/audit.log
You have to make sure, that it isn't file permision problem.
You have to make sure, that it isn't selinux file context problem. 

  1. after problem occurs check logs:
    tail /var/log/messages
    and find:
    setroubleshoot: SELinux is preventing xxxx (<element>) .... For complete SELinux messages. run sealert -l <alert_identifier>
    e.g. <element> = postfix_virtual_t
  2. create selinux policy module:
    grep <element> /var/log/audit/audit.log | audit2allow -M <module_name>
    grep postfix_virtual_t /var/log/audit/audit.log | audit2allow -M postfixvirtual
    it creates two files .te (text version of module), .pp (module)
  3. display and check .te file
    cat postfixvirtual.te
  4. if it is ok load module:
    semodule -i postfixvirtual.pp
  5. test if it works, if not go back to step 1 and repeat (I had to do 3 times for every element)

From: at: 2008-08-02 23:53:22

need to update

cd dcc-dccproc-1.3.72


cd dcc-dccproc-1.3.92



From: LifePoisons at: 2009-01-15 01:52:57

I can't tell you how much I appreciate this guide.  You are a fine human being.  I ran into an issue at the last step with testing out utilizing "telnet localhost 25".  I'd connect then just hang.  Upon inspection of my /var/log/mailog I was getting an error stating:

fatal: unsupported dictionary type: mysql

Upon running "postconf -m" I realized that the mysql update never even took so no wonder it was unsupported.  I reviewed the  patch process and was getting "xxx file conflicts with previous install of xxxx" when I tried to rpm that beast up.  I followed the wisdom of another fella stating that I needed to update the "Release 2" line in the postfix.spec then rebuild and rerun.  Still no luck...fewer conflict listings but still no support.  After several other pathetic attempts on my part, I just gave up and used the "rpm --force -ivh" flag.  I'm sure this is gonna wreak havoc on my soul in the future for something but for those of you in a pickle just know that it was the only way my baby would cooperate.  Just wanted to help this possibly not happen to someone else.  Hopefully you can find a more intelligent way to solve it.  This was the quick and dirty.  Happy mailing.