Install postfixadmin on RHEL7

Download postfixadmin and follow official instructions to install

cd /srv/
wget -O postfixadmin.tgz https://github.com/postfixadmin/postfixadmin/archive/postfixadmin-3.3.10.tar.gz
tar -zxvf postfixadmin.tgz
mv postfixadmin-postfixadmin-3.3 /usr/share/nginx/html/postfixadmin

Create postfixadmin database and update PHP configuration

# Create postfixadmin database
CREATE DATABASE postfix;
# Create postfix admin user in MySQL
CREATE USER `postfix`@`localhost` IDENTIFIED WITH mysql_native_password BY 'password_here';
# Grant access to postfix user
GRANT ALL ON postfix.* TO `postfix`@`localhost`;
# Flush
FLUSH PRIVILEGES;
# Exit
EXIT;
# Update postfixadmin database configuration
# Create /usr/share/nginx/html/postfixadmin/config.local.php file, add below content, update your database information
sudo vi /usr/share/nginx/html/postfixadmin/config.local.php

<?php
$CONF['database_type'] = 'mysqli';
$CONF['database_host'] = 'localhost';
$CONF['database_user'] = 'postfix';
$CONF['database_password'] = 'postfixadmin';
$CONF['database_name'] = 'postfix';
$CONF['encrypt'] = 'php_crypt:SHA512:1000';
$CONF['configured'] = true;
?>

# I'll explain why using 'php_crypt:SHA512:1000' in my case later

Create folder for postfixadmin to read and write files.

# Create templates_c to let postfixadmin write files.
mkdir -p /usr/share/nginx/html/postfixadmin/templates_c
chown -R nginx /usr/share/nginx/html/postfixadmin/templates_c

# Update SELinux configuration
semanage fcontext -a -t httpd_sys_content_t "/usr/share/nginx/html/postfixadmin(/.*)?"
semanage fcontext -a -t httpd_sys_rw_content_t "/usr/share/nginx/html/postfixadmin/templates_c(/.*)?"
restorecon -R /usr/share/nginx/html/postfixadmin
# Give nginx read/execute permission to letsencrypt folder
sudo setfacl -R -m u:nginx:rx /etc/letsencrypt/live/
sudo setfacl -R -m u:nginx:rx /etc/letsencrypt/archive/

Visit setup.php, follow instructions to create setup password and superuser

Visit postfixadmin.yourwebsite.com/setup.php ,follow instructions to create setup password, once done, update config.local.php, add ‘$CONF[‘setup_password’]’ line that generated to this file.

<?php
$CONF['database_type'] = 'mysqli';
$CONF['database_host'] = 'localhost';
$CONF['database_user'] = 'postfix';
$CONF['database_password'] = 'postfixadmin';
$CONF['database_name'] = 'postfix';
$CONF['encrypt'] = 'php_crypt:SHA512:1000';
$CONF['configured'] = true;
$CONF['setup_password'] = '$2y$10$dd32I4Mk2l3/rAPWdTmmOHEuWuxUllecy/sjdmQessJHGzYZfwdy';
?>

Hit setup.php page again and login with setup password, then you can create superuser or normal user

Use PHP default password encrypt method to match dovecot encryption mechanism

I’m using RHEL7 with nginx as web server and not able to read dovecot conf file due to SELinux restrictions, but seems there’s a walk around solution. The idea is to let PHP generated password match the format of dovecot encryption machenism, which adding password scheme as prefix for each encrypted plain password.


# refer to this article: https://mad9scientist.com/dovecot-password-creation-php/

# Test dovecot password encryption
doveadm pw -s SHA512-CRYPT -r 12 -p Welcome123!
# you'll get something like below:
{SHA512-CRYPT}$6$rounds=1000$Tfbt5HzuiNB5P2Yx$ioCpMza.Adu3N3QpzQxNqiEIaxsMlyTAJdFmT7t5Hq6XOGWrySUY2gP8ZjbsJeZ0z.0.CkjeJB49DOQA43dFu/

# The key part is dovecot add an extra string "{SHA512-CRYPT}" before hashed password, and rounds=1000(that's why there's a 1000 number after
# SHA512: $CONF['encrypt'] = 'php_crypt:SHA512:1000'), so in your postfixadmin, you'll need to add this string
# before the generated hashed password in order to be verified successfully by dovecot.

# You can verify this by manually login to mysql database, get hashed password from admin table and use it for verifying purpose, example as below:

doveadm pw -t '{SHA512-CRYPT}$6$rounds=1000$qJt7DaZ8x7FizYiM$eg58DcbTl4IttQjGbfRbknpcDm8WcC1ufC0uu5TJFOkHg.Jc3j5NcXVJPMcSFPvuwAU66gEQ8j.IwKce/tdlj1' -p Welcome123!

# Output
{SHA512-CRYPT}$6$rounds=1000$qJt7DaZ8x7FizYiM$eg58DcbTl4IttQjGbfRbknpcDm8WcC1ufC0uu5TJFOkHg.Jc3j5NcXVJPMcSFPvuwAU66gEQ8j.IwKce/tdlj1 (verified)

And this is useful when you have a separate server that running postfixadmin without any dovecot or postfix installed.

Update dovecot statistics configuration

# Update /etc/dovecot/conf.d/10-master.conf file
service stats {
    unix_listener stats-reader {
      user = nginx
      group = nginx
      mode = 0660
    }

    unix_listener stats-writer {
      user = nginx
      group = nginx
      mode = 0660
    }
}

# Add nginx to dovecot group
sudo gpasswd -a nginx dovecot

Update postfix configuration to connect to MySQL database

# Install postfix mysql module
sudo yum install postfix-mysql
# Update postfix main configuration file.
sudo vi /etc/postfix/main.cf

# Add below conf files.
virtual_mailbox_domains = proxy:mysql:/etc/postfix/sql/mysql_virtual_domains_maps.cf
virtual_mailbox_maps =
   proxy:mysql:/etc/postfix/sql/mysql_virtual_mailbox_maps.cf,
   proxy:mysql:/etc/postfix/sql/mysql_virtual_alias_domain_mailbox_maps.cf
virtual_alias_maps =
   proxy:mysql:/etc/postfix/sql/mysql_virtual_alias_maps.cf,
   proxy:mysql:/etc/postfix/sql/mysql_virtual_alias_domain_maps.cf,
   proxy:mysql:/etc/postfix/sql/mysql_virtual_alias_domain_catchall_maps.cf

virtual_transport = lmtp:unix:private/dovecot-lmtp

# Save and close, then create conf file for each one added above.
# /etc/postfix/sql/mysql_virtual_domains_maps.cf
sudo vi /etc/postfix/sql/mysql_virtual_domains_maps.cf

# Add database configuration info
user = postfix
password = postfixadmin_password
hosts = localhost
dbname = postfix
query = SELECT domain FROM domain WHERE domain='%s' AND active = '1'
#query = SELECT domain FROM domain WHERE domain='%s'
#optional query to use when relaying for backup MX
#query = SELECT domain FROM domain WHERE domain='%s' AND backupmx = '0' AND active = '1'
#expansion_limit = 100
# /etc/postfix/sql/mysql_virtual_mailbox_maps.cf
sudo vi /etc/postfix/sql/mysql_virtual_mailbox_maps.cf

# Add database configuration info
user = postfix
password = postfixadmin_password
hosts = localhost
dbname = postfix
query = SELECT maildir FROM mailbox WHERE username='%s' AND active = '1'
#expansion_limit = 100
# /etc/postfix/sql/mysql_virtual_alias_domain_mailbox_maps.cf
sudo vi /etc/postfix/sql/mysql_virtual_alias_domain_mailbox_maps.cf

# Add database configuration info
user = postfix
password = postfixadmin_password
hosts = localhost
dbname = postfix
query = SELECT maildir FROM mailbox,alias_domain WHERE alias_domain.alias_domain = '%d' and mailbox.username = CONCAT('%u', '@', alias_domain.target_domain) AND mailbox.active = 1 AND alias_domain.active='1'
# /etc/postfix/sql/mysql_virtual_alias_maps.cf
sudo vi /etc/postfix/sql/mysql_virtual_alias_maps.cf

# Add database configuration info
user = postfix
password = postfixadmin_password
hosts = localhost
dbname = postfix
query = SELECT goto FROM alias WHERE address='%s' AND active = '1'
#expansion_limit = 100
# /etc/postfix/sql/mysql_virtual_alias_domain_maps.cf
sudo vi /etc/postfix/sql/mysql_virtual_alias_domain_maps.cf

# Add database configuration info
user = postfix
password = postfixadmin_password
hosts = localhost
dbname = postfix
query = SELECT goto FROM alias,alias_domain WHERE alias_domain.alias_domain = '%d' and alias.address = CONCAT('%u', '@', alias_domain.target_domain) AND alias.active = 1 AND alias_domain.active='1'
# /etc/postfix/sql/mysql_virtual_alias_domain_catchall_maps.cf
sudo vi /etc/postfix/sql/mysql_virtual_alias_domain_catchall_maps.cf

# Add database configuration info
user = postfix
password = postfixadmin_password
hosts = localhost
dbname = postfix
query = SELECT goto FROM alias,alias_domain WHERE alias_domain.alias_domain = '%d' and alias.address = CONCAT('@', alias_domain.target_domain) AND alias.active = 1 AND alias_domain.active='1'

Update sql folder to allow only postfix and root user have access to sensitive files.

sudo chmod 0640 /etc/postfix/sql/*
sudo setfacl -R -m u:postfix:rx /etc/postfix/sql/

Add virtual mail configuration to postfix

# /etc/postfix/main.cf
sudo vi /etc/postfix/main.cf

virtual_mailbox_base = /var/vmail
virtual_minimum_uid = 2000
virtual_uid_maps = static:2000
virtual_gid_maps = static:2000

# Add vmail user
sudo adduser vmail --system --uid 2000 --user-group --no-create-home

# Creat vmail location 
sudo mkdir /var/vmail/

# Change folder ownership
sudo chown vmail:vmail /var/vmail/ -R

# Update SELinux to be able to write vmail folder
sudo chcon -t mail_spool_t /var/vmail/ -R

Update dovecot configuration to connect to MySQL database

# Install dependency module
sudo yum install dovecot-mysql
# /etc/dovecot/conf.d/10-mail.conf
sudo vi /etc/dovecot/conf.d/10-mail.conf

# Add/update below change
mail_home = /var/vmail/%d/%n

mail_privileged_group = mail

# Save and close
# /etc/dovecot/conf.d/10-auth.conf
sudo vi /etc/dovecot/conf.d/10-auth.conf

# Add/update below change
auth_username_format = %u

# Make below 2 lines change
#!include auth-system.conf.ext
!include auth-sql.conf.ext

# Save and close
# /etc/dovecot/dovecot-sql.conf.ext
sudo vi /etc/dovecot/dovecot-sql.conf.ext

# Add/update below change

driver = mysql

connect = host=localhost dbname=postfix user=postfix password=postfixadmin_password

default_pass_scheme = SHA512-CRYPT

password_query = SELECT username AS user,CONCAT('{SHA512-CRYPT}',password) AS password FROM mailbox WHERE username = '%u' AND active='1'

user_query = SELECT maildir, 2000 AS uid, 2000 AS gid FROM mailbox WHERE username = '%u' AND active='1'

iterate_query = SELECT username AS user FROM mailbox

# Save and close

# I manually add prefix to mysql password generated by postfix 'CONCAT('{SHA512-CRYPT}',password) AS password'

Save change and restart dovecot

Now your dovecot configuration for postfixadmin is done.

You can start to configure your postfixadmin virtual mailbox

Leave a Comment