Mail Server Virtual Users with MySQL Postfix Dovecot on CentOS

We will be setting up a Postfix/Dovecot Mail server with Virtual Domains, MySQL as backend.

This setup is part of a Mail Setup Series:

  • Mail Server with Postfix/Dovecot and MySQL
  • Mail scanner with MailScanner, ClamAV and Spamassassin (In Progress)
  • Mail Scanner with Amavisd, ClamAV and Spamassassin (Coming Soon)

Dependencies:

$ yum update -y
$ yum --enablerepo=centosplus install postfix mailx mutt -y
$ yum install dovecot mysql-server dovecot-mysql cyrus-sasl cyrus-sasl-devel -y

MySQL Configuration:

$ chkconfig mysqld on
$ service mysqld start
$ mysql_secure_installation

Access MySQL, and create the database that Postfix will use:

$ mysql -u root -p
mysql> CREATE DATABASE mail;
Query OK, 1 row affected (0.00 sec)

Select the database that we've created, and apply the needed permissions:

mysql> use mail
mysql> GRANT SELECT, INSERT, UPDATE, DELETE ON mail.* TO 'mail_admin'@'localhost' IDENTIFIED BY 'password';
mysql> GRANT SELECT, INSERT, UPDATE, DELETE ON mail.* TO 'mail_admin'@'localhost.localdomain' IDENTIFIED BY 'password';
mysql> FLUSH PRIVILEGES;

Create the tables:

mysql> CREATE TABLE domains (domain varchar(50) NOT NULL, PRIMARY KEY (domain) );
mysql> CREATE TABLE forwardings (source varchar(80) NOT NULL, destination TEXT NOT NULL, PRIMARY KEY (source) );
mysql> CREATE TABLE users (email varchar(80) NOT NULL, password varchar(20) NOT NULL, PRIMARY KEY (email) );
mysql> CREATE TABLE transport ( domain varchar(128) NOT NULL default '', transport varchar(128) NOT NULL default '', UNIQUE KEY domain (domain) );
mysql> quit

Postfix MySQL Configuration:

Now we will create the MySQL Configuration for Postfix:

mysql-virtual_domains.cf

$ cat > /etc/postfix/mysql-virtual_domains.cf << EOF
user = mail_admin
password = password
dbname = mail
query = SELECT domain AS virtual FROM domains WHERE domain='%s'
hosts = 127.0.0.1
EOF

mysql-virtual_forwardings.cf

$ cat > /etc/postfix/mysql-virtual_forwardings.cf << EOF
user = mail_admin
password = password
dbname = mail
query = SELECT destination FROM forwardings WHERE source='%s'
hosts = 127.0.0.1
EOF

mysql-virtual_mailboxes.cf

$ cat > /etc/postfix/mysql-virtual_mailboxes.cf << EOF
user = mail_admin
password = password
dbname = mail
query = SELECT CONCAT(SUBSTRING_INDEX(email,'@',-1),'/',SUBSTRING_INDEX(email,'@',1),'/') FROM users WHERE email='%s'
hosts = 127.0.0.1
EOF

mysql-virtual_email2email.cf

$ cat > /etc/postfix/mysql-virtual_email2email.cf << EOF
user = mail_admin
password = password
dbname = mail
query = SELECT email FROM users WHERE email='%s'
hosts = 127.0.0.1
EOF

Change Permissions:

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

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

Note: If using your own SSL Cert and Key, replace /etc/pki/dovecot/private/dovecot.pem

Postfix Configuration:

Review the below information to suit your environment:

postconf -e 'myhostname = mail.bekkersolutions.com'
postconf -e 'mydestination = localhost'
postconf -e 'mynetworks = 127.0.0.0/8'
postconf -e 'inet_interfaces = all'
postconf -e 'message_size_limit = 30720000'
postconf -e 'virtual_alias_domains ='
postconf -e 'virtual_alias_maps = proxy:mysql:/etc/postfix/mysql-virtual_forwardings.cf, mysql:/etc/postfix/mysql-virtual_email2email.cf'
postconf -e 'virtual_mailbox_domains = proxy:mysql:/etc/postfix/mysql-virtual_domains.cf'
postconf -e 'virtual_mailbox_maps = proxy:mysql:/etc/postfix/mysql-virtual_mailboxes.cf'
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_type = dovecot'
postconf -e 'smtpd_sasl_path = private/auth'
postconf -e 'smtpd_sasl_auth_enable = yes'
postconf -e 'broken_sasl_auth_clients = yes'
postconf -e 'smtpd_sasl_authenticated_header = 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/pki/dovecot/certs/dovecot.pem'
postconf -e 'smtpd_tls_key_file = /etc/pki/dovecot/private/dovecot.pem'
postconf -e 'virtual_create_maildirsize = yes'
postconf -e 'virtual_maildir_extended = 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 'virtual_transport = virtual'
postconf -e 'dovecot_destination_recipient_limit = 1'

Update Postfix Master.cf:

$ echo "
dovecot   unix  -       n       n       -       -       pipe
    flags=DRhu user=vmail:vmail argv=/usr/libexec/dovecot/deliver -f ${sender} -d ${recipient}
" >> /etc/postfix/master.cf

Enable Postfix:

$ service sendmail stop
$ chkconfig sendmail off
$ chkconfig postfix on
$ service postfix start

Dovecot Configuration:

Backup your original configuration:

$ mv /etc/dovecot/dovecot.conf /etc/dovecot/dovecot.conf-backup

Generate dovecot.conf

$ cat > /etc/dovecot/dovecot.conf << EOF
listen = *
protocols = imap pop3
log_timestamp = "%Y-%m-%d %H:%M:%S "
mail_location = maildir:/home/vmail/%d/%n
maildir_stat_dirs = yes
mail_privileged_group = postfix


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

passdb {
  args = /etc/dovecot/dovecot-sql.conf
  driver = sql
}

service auth {
  unix_listener /var/spool/postfix/private/auth {
    group = postfix
    mode = 0660
    user = postfix
  }
  unix_listener auth-master {
    mode = 0600
    user = vmail
  }
  user = root
}

ssl_cert = </etc/pki/dovecot/certs/dovecot.pem
ssl_key = </etc/pki/dovecot/private/dovecot.pem

userdb {
  args = uid=5000 gid=5000 home=/home/vmail/%d/%n allow_all_users=yes
  driver = static
}

protocol lda {
  auth_socket_path = /var/run/dovecot/auth-master
  log_path = /home/vmail/dovecot-deliver.log
  postmaster_address = postmaster@sysadmins.co.za
}

protocol pop3 {
  pop3_uidl_format = %08Xu%08Xv
}

EOF

Generate dovecot-sql.conf

$ cat > /etc/dovecot/dovecot-sql.conf << EOF
driver = mysql
connect = host=127.0.0.1 dbname=mail user=mail_admin password=password
default_pass_scheme = CRYPT
password_query = SELECT email as user, password FROM users WHERE email='%u';

EOF

Change Permissions:

$ chgrp dovecot /etc/dovecot/dovecot-sql.conf
$ chmod o= /etc/dovecot/dovecot-sql.conf

$ chkconfig dovecot on
$ service dovecot start

Alias Configuration:

We would like to forward all mail for root to our postmaster account:

Append the following in your /etc/aliases

postmaster: root
root: postmaster@sysadmins.co.za

After any changes we need to run newaliases

$ newaliases
$ service postfix restart

Create Mail Account:

Create the first mail account:

mysql -u root -p
mysql> INSERT INTO domains (domain) VALUES ('sysadmins.co.za');
mysql> INSERT INTO users (email, password) VALUES ('ruan@sysadmins.co.za', ENCRYPT('mypassword'));
mysql> quit

Testing:

Test your mailflow by sending a mail to yourself, use Ctrl+D when you are done:

$ mailx ruan@sysadmins.co.za
Subject: Test Mail
Hello there
EOT

Access your mailbox with Mutt:

$ mutt -f /home/vmail/sysadmins.co.za/ruan