Install postfix + dovecot auth + tls + mysql + postfixadmin + postgrey + spamassassin and clamav on Centos 7

In this post we will install a mail server using virtual users with authentication using dovecot and ssl. After we are able to successfully send and retrieve e-mails we will securing the server with postgrey, spamassassin and clamav antivirus.
We will be using in this example a mysql backend. We will make sure that we have all the related packages installed. Will achieve that using yum:

#yum -y install postfix dovecot dovecot-mysql mariadb-server 

When that's done we need to edit the /etc/postfix/main.cf to match your needed configuration:

alias_database = hash:/etc/aliases
alias_maps = hash:/etc/aliases
broken_sasl_auth_clients = yes
command_directory = /usr/sbin
config_directory = /etc/postfix
daemon_directory = /usr/libexec/postfix
data_directory = /var/lib/postfix
debug_peer_level = 2
debugger_command = PATH=/bin:/usr/bin:/usr/local/bin:/usr/X11R6/bin$daemon_directory/$process_name $process_id & sleep 5
disable_vrfy_command = yes
html_directory = no
inet_interfaces = all
inet_protocols = all
mail_owner = postfix
mailq_path = /usr/bin/mailq.postfix
manpage_directory = /usr/share/man
milter_default_action = accept
mydestination = $myhostname, localhost.$mydomain, localhost
mydomain = example.com
myhostname = mail.example.com
myorigin = $myhostname
newaliases_path = /usr/bin/newaliases.postfix
queue_directory = /var/spool/postfix
readme_directory = /usr/share/doc/postfix-2.10.1/README_FILES
sample_directory = /usr/share/doc/postfix-2.10.1/samples
sendmail_path = /usr/sbin/sendmail.postfix
setgid_group = postdrop
smtpd_delay_reject = yes
smtpd_error_sleep_time = 1s
smtpd_hard_error_limit = 20
smtpd_helo_required = yes
smtpd_helo_restrictions = permit_mynetworks,
                          reject_non_fqdn_hostname,
                          reject_invalid_hostname,
                          permit
smtpd_recipient_restrictions = permit_sasl_authenticated,
                          permit_mynetworks,
                          check_policy_service unix:postgrey/socket,
                          reject_invalid_hostname,
                          reject_non_fqdn_hostname,
                          reject_unauth_destination,
                          reject_rbl_client list.dsbl.org,
                          reject_rbl_client sbl.spamhaus.org,
                          reject_rbl_client cbl.abuseat.org,
                          reject_rbl_client dul.dnsbl.sorbs.net,
                          permit
smtpd_sasl_auth_enable = yes
smtpd_sasl_path = private/auth
smtpd_sasl_security_options = noanonymous
smtpd_sasl_type = dovecot
smtpd_soft_error_limit = 10
smtpd_tls_CAfile = /etc/pki/tls/cert.pem
smtpd_tls_auth_only = yes
smtpd_tls_cert_file = /etc/pki/tls/certs/mail.example.com.crt
smtpd_tls_key_file = /etc/pki/tls/private/mail.example.com.key
smtpd_tls_loglevel = 1
smtpd_tls_received_header = yes
smtpd_tls_security_level = may
smtpd_tls_session_cache_timeout = 3600s
tls_random_source = dev:/dev/urandom
unknown_local_recipient_reject_code = 550
virtual_alias_maps = mysql:/etc/postfix/sql/mysql_virtual_alias_maps.cf
virtual_gid_maps = static:12
virtual_mailbox_base = /home/vmail
virtual_mailbox_domains = mysql:/etc/postfix/sql/mysql_virtual_domains_maps.cf
virtual_mailbox_limit = 51200000
virtual_mailbox_maps = mysql:/etc/postfix/sql/mysql_virtual_mailbox_maps.cf
virtual_minimum_uid = 8
virtual_transport = virtual
virtual_uid_maps = static:8
virtual_maildir_limit_message=Sorry, Your maildir has overdrawn your diskspace quota, please free some space of your mailbox and try again.
virtual_mailbox_limit_maps=mysql:/etc/postfix/sql/mysql_virtual_mailbox_limit_maps.cf
virtual_mailbox_limit_override=yes
virtual_overquota_bounce=yes
virtual_create_maildirsize=yes
smptd_tls_session_cache_database=btree:/var/spool/postfix/smtpd_tls_cache
virtual_mailbox_extended=yes
smtpd_tls_note_starttls_offer=yes

You need to add create the following files:
/etc/postfix/sql/mysql_relay_domains_maps.cf

user = postfix
password = someotherpass
hosts = 127.0.0.1
dbname = mail
table = domain
select_field = domain
additional_conditions = and backupmx = '1'

/etc/postfix/sql/mysql_virtual_alias_maps.cf

user = postfix
password = someotherpass
hosts = 127.0.0.1
dbname = mail
table = alias
select_field = goto
where_field = address

/etc/postfix/sql/mysql_virtual_mailbox_limit_maps.cf

user = postfix
password = someotherpass
hosts = 127.0.0.1
dbname = virtual_mail
table = mailbox
select_field = quota
where_field = username
#additional_conditions = and active = '1'

/etc/postfix/sql/mysql_virtual_mailbox_maps.cf

user = postfix
password = someotherpass
hosts = 127.0.0.1
dbname = mail
table = mailbox
select_field = maildir
where_field = username
#additional_conditions = and active = '1'

/etc/postfix/sql/mysql_virtual_domains_maps.cf

user = postfix
password = someotherpass
hosts = 127.0.0.1
dbname = mail
table = domain
select_field = domain
where_field = domain
additional_conditions = and backupmx = '0' and active = '1'

To configure the smtps and submission for local delivery change the following in /etc/postfix/master.cf

submission inet n       -       n       -       -       smtpd
  -o syslog_name=postfix/submission
  -o smtpd_tls_security_level=encrypt
  -o smtpd_sasl_auth_enable=yes
  -o smtpd_client_restrictions=permit_sasl_authenticated,reject

smtps     inet  n       -       n       -       -       smtpd
  -o syslog_name=postfix/smtps
  -o smtpd_tls_wrappermode=yes
  -o smtpd_sasl_auth_enable=yes
  -o smtpd_client_restrictions=permit_sasl_authenticated, reject
  -o milter_macro_daemon_name=ORIGINATING

Next we start the mysql database and create a database for our installation. To start the database please run the following command:

 #systemctl start mariadb.service

Make sure that your database will start upon startup:

#systemctl enable mariadb.service

Now it's time to create our database and the proper users with the needed privileges:

#mysql -u root
MariaDB> create database mail;
MariaDB> grant all privileges on mail.* to 'postfixadmin'@'localhost' identified by 'somepass';
MariaDB> grant select on mail.* to 'postfix'@'localhost' identified by 'someotherpass';
MariaDB> flush privileges;

We have created two users with different privileges. First the postfixadmin is the user which would be used by the webgui called postfixadmin which need write privileges into the database because you will be adding the domains, users and aliases which will be holded by this installation via it. The other user is postfix, which will be used by the postfix and dovecot to query the users from the database. These queries does not write to the database therefore this user will only have select right granted.

Now it's time to install nginx and the postfixadmin. First you need to add a new repo for nginx cause we want to use the latest version available. For that create a new file /etc/yum.repos.d/nginx.repo with the following value in it.

[nginx]
name=nginx repo
baseurl=http://nginx.org/packages/mainline/centos/7/$basearch/
gpgcheck=0
enabled=1

Now we need to add the epel repo so that we can install the full list of needed packages.

#yum -y install http://dl.fedoraproject.org/pub/epel/7/x86_64/e/epel-release-7-2.noarch.rpm

Now we can install all the needed packages. To do so you will need to run the command:

#yum -y install php php-fpm php-common php-pdo php-mbstring php-imap php-cli php-mysql nginx

Edit the /etc/php-fpm.d/www.conf and change the following entries:

;listen = 127.0.0.1:9000 
listen = /var/run/php-fpm.sock
...
listen.owner = nginx
listen.group = nginx
listen.mode = 0666
...
user = nginx
group = nginx

Download the postfixadmin from here.
Unarchive the downloaded package and move it to /var/www/html:

#tar -xzvf postfixadmin-2.91.tar.gz
#mv postfixadmin-2.91 /var/www/html/postfixadmin

Now it's time to set up the nginx and start the services. Edit the /etc/nginx/conf.d/default.conf and change it to look like this:

server {
  listen       80;
  server_name  0.0.0.0;

  error_log /var/log/nginx/error.log debug;

  root   /var/www/html/postfixadmin;
  index  index.php index.html index.htm;

  location ~ \.php$ {
    fastcgi_pass   unix:/var/run/php-fpm.sock;
    fastcgi_index  index.php;
    fastcgi_param  SCRIPT_FILENAME      $document_root$fastcgi_script_name;
    include        fastcgi_params;
  }

}

Now go and edit the postfixadmin config file /var/www/potfixadmin/html/config.inc.php. Change the value false to true at the line:

$CONF['configured'] = true;

Edit the entries in the config file related to the database:

$CONF['database_type'] = 'mysqli';
$CONF['database_host'] = 'localhost';
$CONF['database_user'] = 'postfixadmin';
$CONF['database_password'] = 'somepassword';
$CONF['database_name'] = 'mail';

Save the changes. Disable your selinux by editing the config file and change the SELINUX value from enforcing to permissive or disabled. To apply the change you will need to reboot your server. You will have to add a rule to the firewall to open the port 80 for web access. To do that you need to run the following command:

#firewall-cmd --zone=public --add-service=http --permanent

Then restart the firewall and start the php-fpm and nginx services:

#systemctl restart firewalld
#systemctl start php-fpm
#systemctl start nginx

Now you can open a browser and type the IP address of your system to get to the postfixadmin page.
It's time to create the self signed certificate for your domain in this example mail.example.com.

#genkey --days 3650 mail.example.com

This certificate is valid for 10 years.
Now it's time to configure the dovecot. First create a file called /etc/dovecot/dovecot-sql.conf.ext

driver = mysql
connect = host=localhost dbname=mail user=postfix password=P0stf1x
default_pass_scheme = CRAM-MD5 
user_query = SELECT '/home/vmail/%n@%d' AS home, 8 AS uid, 12 AS gid FROM mailbox WHERE username = '%u'
password_query = SELECT password FROM mailbox WHERE username = '%u'

Next file to edit /etc/dovecot/conf.d/10-auth.conf
Change the values to match the following:

auth_mechanisms = plain login cram-md5
#!include auth-deny.conf.ext
#!include auth-master.conf.ext

#!include auth-system.conf.ext
!include auth-sql.conf.ext
#!include auth-ldap.conf.ext
#!include auth-passwdfile.conf.ext
#!include auth-checkpassword.conf.ext
#!include auth-vpopmail.conf.ext
#!include auth-static.conf.ext

Next file to change is /etc/dovecot/conf.d/10-mail.conf:

mail_location = maildir:/home/vmail/%d/%n/:INDEX=/home/vmail/%d/%n/indexes
mail_uid = 8
mail_gid = 12
first_valid_uid = 8
last_valid_uid = 8
first_valid_gid = 12
last_valid_gid = 12

Next file to change is /etc/dovecot/conf.d/10-master.conf:

service imap-login {
    inet_listener imap {
         port = 143
    }
    inet_listener imaps {
         port = 993
         ssl = yes
    }
}

service pop3-login {
     inet_listener pop3 {
         #port = 110
    }
    inet_listener pop3s {
        #port = 995
        #ssl = yes
    }
}

service auth {
  unix_listener /var/spool/postfix/private/auth {
       mode = 0666
       user = mail
       group = mail
   }
}

Next file to change /etc/dovecot/conf.d/10-ssl.conf:

ssl = required
ssl_cert = </etc/pki/tls/certs/mail.example.com.crt
ssl_key = </etc/pki/tls/private/mail.example.com.key

Next file on line: /etc/dovecot/conf.d/auth-sql.conf.ext:

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

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

Note: There are only posted the values which need to be changed in the configuration files everything else should be left as it was on the config file by default.

Now before we start the services we would need to open the ports in the firewall. To do so run the following commands:

#firewall-cmd --zone=public --add-service=smtp --permanent
#firewall-cmd --zone=public --add-service=smtp --permanent
#firewall-cmd --zone=public --add-service=smtps --permanent
#firewall-cmd --zone=public --add-port=587/tcp --permanent
#firewall-cmd --zone=public --add-service=imaps --permanent
#firewall-cmd --zone=public --add-service=imap --permanent
#firewall-cmd --zone=public --add-service=pop3s --permanent

After we have added the ports we need to restart the firewall so that the rules would get applied.

#systemctl restart firewalld

Now we are ready to start up the service and enable them on boot.

#systemctl enable postfix
#systemctl restart postfix
#systemctl enable dovecot
#systemctl restart dovecot

We test the smtp by telneting into the domain on port 25

#telnet localhost 25
Connected to localhost.
Escape character is '^]'.
220 mail.example.com ESMTP Postfix
ehlo me
250-mail.example.com
250-PIPELINING
250-SIZE 10240000
250-ETRN
250-STARTTLS
250-ENHANCEDSTATUSCODES
250-8BITMIME
250 DSN

First need to install the required software. For this we use yum:

#yum install -y postgrey spamassassin spamass-milter-postfix spamass-milter clamav-filesystem clamav-server clamav-update clamav-milter-systemd clamav-data clamav-server-systemd clamav-scanner-systemd clamav clamav-milter clamav-lib clamav-scanner

Once that is done it's time to configure the software. Let's start with the spamassassin. Fist edit the /etc/mail/spamassassin/local.cf match your configuration with this:

required_hits 5.0
report_safe 0
required_score 5
#rewrite_header Subject *** SPAM ***
remove_header ham Status
remove_header ham Level

Next file to configure is /etc/sysconfig/spamassassin:

# Options to spamd
SPAMDOPTIONS="-d -c -m5 -H"

and /etc/sysconfig/spamass-milter:

EXTRA_FLAGS="-i 127.0.0.1 -m -r -1 -I "

and the last one for spamassassin is: /etc/sysconfig/spamass-milter-postfix:

SOCKET="/run/spamass-milter/postfix/sock"
SOCKET_OPTIONS="-g postfix"

Next software to configure is clamav. First will edit the /etc/clamd.d/scan.conf

First thing to do is to comment or remove the Example in the cofiguration file. These are the configuration options which should be uncommented:

LogFile /var/log/clamd.scan
LogFileMaxSize 0 
LogTime yes
LogSyslog yes
LogFacility LOG_MAIL
PidFile /var/run/clamd.scan/clamd.pid
TemporaryDirectory /var/tmp
DatabaseDirectory /var/lib/clamav
OfficialDatabaseOnly no
LocalSocket /var/run/clamd.scan/clamd.sock
LocalSocketGroup clamscan
LocalSocketMode 666
FixStaleSocket yes
TCPSocket 3310
TCPAddr 127.0.0.1
SelfCheck 600
User clamscan
AllowSupplementaryGroups yes
StatsEnabled yes

Next file to configure is /etc/mail/clamav-milter.conf:

MilterSocket /var/run/clamav-milter/clamav-milter.socket
MilterSocket inet:7357
MilterSocketMode 777
FixStaleSocket yes
AllowSupplementaryGroups yes
PidFile /var/run/clamav-milter/clamav-milter.pid
TemporaryDirectory /var/tmp
ClamdSocket unix:/var/run/clamd.scan/clamd.sock
OnClean Accept
OnInfected Reject
OnFail Defer
RejectMsg Rejecting harmful email: %v found.
AddHeader Add
VirusAction /usr/local/bin/my_infected_message_handler
LogFileMaxSize 0
LogTime yes
LogSyslog yes
LogFacility LOG_MAIL

Now let's try and configure postgrey. For this you need to edit /etc/sysconfig/postgrey:

POSTGREY_OPTS="--delay=60"

Last piece of software to install and configure is the opendkim to do this you need to run the following command:

#yum install -y libopendkim opendkim

Let's start to configure /etc/opendkim.conf:

PidFile /var/run/opendkim/opendkim.pid
Mode    sv
Syslog  yes
SyslogSuccess   yes
UserID  opendkim:opendkim
Socket  inet:8891@localhost
Umask   002
Canonicalization    relaxed/simple
MinimumKeyBits 1024
KeyTable    refile:/etc/opendkim/KeyTable
SigningTable    refile:/etc/opendkim/SigningTable
ExternalIgnoreList  refile:/etc/opendkim/TrustedHosts
InternalHosts   refile:/etc/opendkim/TrustedHosts

Let's specify the TrustedHosts at /etc/opendkim/TrustedHosts:

127.0.0.1
mail.example.com
example.com
#192.168.1.0/24

This file is used to define the internal ignore list and external ignore list. All emails which are orriginating from these hosts or domains will be automatically trusted and signed.
Now we create the keytable on /etc/opendkim/KeyTable:

default._domainkey.example.com example.com:default:/etc/opendkim/keys/default.private

Next we create the signing table at /etc/opendkim/SigningTable:

This file is used to declare the domains/email addresses and their selector

*@example.com default._domainkey.example.com

Now it's time to create the private and public keys. First change the directory to the /etc/opendkim/keys and create a folder in there named as your domain for which you want to create the keys and fix the ownership of the folder:

#cd /etc/opendkim/keys
#mkdir example.com
#cd example.com
#chown -R opendkim.opendkim /etc/opendkim/keys/example.com

Now let's generate the keys for the domain and set the proper ownership:

#opendkim-genkey -s default -d example.com
#chown -R opendkim. /etc/opendkim/keys/

The -s option would give the name of the selector and the -d would give the domain of the keys.
Now open the /etc/opendkim/keys/example.com/mail.txt and create a TXT entry on your domain DNS server according to what is in the file:

mail._domainkey      IN      TXT    "v=DKIM1; k=rsa; p=MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDFRSHhfOAoqsF5t8dmpzuYGQAIP3JoLRVLQ5r8SsI6SrS90VGAsAByJsERImLxV68D3b+iDUhaw+PmQ/9L4atgnp3cbqgNRD++lAOvloGe+I1GlN6iKYNm4Xr3IYPPvaZYlT0JgawRZKGffP0l0bCqJT/36ZCcI+Shoq+l/H0LfwIDAQAB"

Now let's do the final changes in the postfix config file /etc/postfix/main.cf. Add the following entries to the file:

milter_default_action = accept
smtpd_milters = unix:/var/run/clamav-milter/clamav-milter.socket,
              unix:/var/run/spamass-milter/postfix/sock,
              inet:127.0.0.1:8891

Once that is done it's time to enable the services for startup and start them and restart the postfix.

#systemctl enable spamassassin.service
#systemctl start spamassassin.service
#systemctl enable spamass-milter.service
#systemctl enable spamass-milter-root.service
#systemctl start spamass-milter.service
#systemctl start spamass-milter-root.service
#systemctl enable clamd@scan.service
#systemctl start clamd@scan.service
#systemctl enable clamav-milter.service
#systemctl start clamav-milter.service
#systemctl enable postgrey.service
#systemctl start postgrey.service
#systemctl enable opendkim.service
#systemctl start opendkim.service
#systemctl restart postfix.service