Postfix configuration

Install the required software:

$ yum install postfix postgrey dovecot fail2ban spamassassin 
spamass-milter-postfix opendkim

Create TLS certificate, key and CA authority (replace mail.domain.tld with a valid domain name):

$ mkdir /etc/postfix/ssl
$ cd /etc/postfix/ssl
$ openssl genrsa -aes256 -out mail.domain.tld.key 4096
$ chmod 600 mail.domain.tld.key
$ openssl req -sha256 -new -key mail.domain.tld.key -out mail.domain.tld.csr
$ openssl x509 -sha256 -req -days 1825 -in mail.domain.tld.csr -signkey mail.domain.tld.key -out mail.domain.tld.crt
$ openssl rsa -in mail.domain.tld.key -out mail.domain.tld.key.nopass
$ mv mail.domain.tld.key.nopass mail.domain.tld.key
$ openssl req -new -x509 -extensions v3_ca -keyout cakey.pem -out cacert.pem -days 3650 -sha256
$ chmod 600 mail.domain.tld.key
$ chmod 600 cakey.pem
$ openssl dhparam -out dhparams.pem 4096
$ chmod 600 dhparams.pem

Edit main.cf file accordingly (the other lines should be ok by default).
No SQL database is used, for user authentication postfix relies on Linux users, email data are stored in ~/Maildir.

$ vi /etc/postfix/main.cf
---
myhostname = something.com
mydomain = something.com
myorigin = $mydomain
mydestination = $myhostname, localhost.$mydomain, localhost, $mydomain
relay_domains = $mydestination
alias_maps = hash:/etc/postfix/aliases
alias_database = $alias_maps
home_mailbox = Maildir/

mynetworks = 127.0.0.0/24

milter_default_action = accept
smtpd_milters = inet:127.0.0.1:8891,
                inet:127.0.0.1:8893,
                unix:/run/spamass-milter/postfix/sock
non_smtpd_milters = $smtpd_milters

smtpd_sasl_type = dovecot
smtpd_sasl_path = private/auth
smtpd_sasl_auth_enable = yes
#smtpd_sasl_security_options = noanonymous,noplaintext
smtpd_recipient_restrictions = permit_mynetworks,
    permit_sasl_authenticated,
    reject_unauth_destination,
    # uncomment to enable domains blacklist check
    #check_sender_access hash:/etc/postfix/blacklist,
    # uncomment to enable postgrey
    #check_policy_service unix:/var/spool/postfix/postgrey/socket
    permit
#broken_sasl_auth_clients = yes

smtpd_tls_protocols = !SSLv2,!SSLv3
smtpd_tls_mandatory_protocols = !SSLv2,!SSLv3
smtpd_tls_auth_only = yes
smtpd_tls_security_level = may
smtpd_tls_key_file = /etc/postfix/ssl/mail.domain.tld.key
smtpd_tls_cert_file = /etc/postfix/ssl/mail.domain.tld.crt
#smtpd_tls_CAfile = /etc/postfix/ssl/cacert.pem
smtpd_tls_loglevel = 3
smtpd_tls_received_header = yes
smtpd_tls_session_cache_timeout = 3600s
smtpd_tls_eecdh_grade=ultra
smtpd_tls_mandatory_ciphers = high
smtpd_tls_dh1024_param_file = /etc/postfix/ssl/dhparams4096.pem
smtpd_error_sleep_time = 1s
smtpd_soft_error_limit = 10
smtpd_hard_error_limit = 20

smtp_tls_security_level = may
smtp_tls_protocols = !SSLv2,!SSLv3
smtp_tls_mandatory_protocols = !SSLv2,!SSLv3
smtp_tls_mandatory_ciphers = high
smtp_tls_loglevel = 1

lmtp_tls_security_level = may
lmtp_tls_mandatory_protocols = !SSLv2,!SSLv3
lmtp_tls_protocols = !SSLv2,!SSLv3
lmtp_tls_mandatory_ciphers = high

tls_random_source = dev:/dev/urandom
tls_ssl_options = NO_COMPRESSION

mailbox_size_limit = 500000000
message_size_limit = 50000000

mime_header_checks = regexp:/etc/postfix/smtp_header_checks
header_checks = regexp:/etc/postfix/smtp_header_checks

disable_vrfy_command = yes

Edit master.cf accordingly to enable smtps:

$ vi /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 tls_preempt_cipherlist=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 tls_preempt_cipherlist=yes # force the client to use server's cipherlist
  -o smtpd_client_restrictions=permit_sasl_authenticated,reject

Configure spamassassin to filter incoming emails.

$ vi /etc/sysconfig/spamass-milter-postfix
---
SOCKET="/run/spamass-milter/postfix/sock"
SOCKET_OPTIONS="-g postfix"

$ vi /etc/sysconfig/spamassassin
---
SPAMDOPTIONS="-d -c -m5 -H"

Configure a manual blacklist, this can be useful in case we want to block spam not filtered by spamassassin (edit /etc/postfix/main.cf accordingly to enable postgrey): remember to run postmap command and reload postfix service every time blacklist file is modified.

$ vi /etc/postfix/blackilist
---
certaing.address.something REJECT

$ postmap /etc/postfix/blacklist

Another important thing to do is tell postfix to strip some informations off email headers.
To do so create the following file:

$ vi /etc/postfix/smtp_header_checks
---
/^Received:.*with ESMTPSA/ IGNORE
/^X-Originating-IP:/ IGNORE
/^X-Mailer:/ IGNORE
/^User-Agent:/	IGNORE
/^Mime-Version:/ IGNORE

Rebuild Postfix lookup tables using postmap command:

$ postmap /etc/postfix/header_checks

Postfix should now be working correctly, enable the service and start it:

$ systemctl enable spamass-milter spamassassin postfix _postgrey_ && systemctl 
start spamass-milter spamassassin postfix _postgrey_

Dovecot

Even for Dovecot no database is used. Copy sample config files:

$ cp /usr/share/doc/dovecot/example-config/dovecot.conf /etc/dovecot
$ cp -r /usr/share/doc/dovecot/example-config/conf.d /etc/dovecot

Generate TLS certificates:

### copy the config file
$ cp /etc/ssl/dovecot-openssl.cnf{.sample,} .
### edit the configuration
$ vi /etc/ssl/dovecot-openssl.cnf
### generate the certificate/key pair - /etc/ssl/certs/dovecot.pem and /etc/ssl/private/dovecot.pem
### /usr/lib/dovecot/mkcert.sh to generate the certificate.
$ mv /etc/ssl/certs/dovecot.pem /etc/ca-certificates/trust-source/anchors/dovecot.crt
$ trust extract-compat

Edit 10-mail.conf:

$ vi /etc/dovecot/conf.d/10-mail.conf
---
mail_location = maildir:~/Maildir
namespace inbox {
  inbox = yes
}

Edit 10-master.conf:

$ vi /etc/dovecot/conf.d/10-master.conf
---
service auth {
  # Postfix smtp-auth
  unix_listener /var/spool/postfix/private/auth {
    mode = 0666
    user = postfix
    group = postfix
  }
}

Edit 10-ssl.conf:

$ vi /etc/dovecot/conf.d/10-ssl.conf
---
ssl_cipher_list=ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-DSS-AES128-GCM-SHA256:kEDH+AESGCM:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA:ECDHE-ECDSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES128-SHA:DHE-DSS-AES128-SHA256:DHE-RSA-AES256-SHA256:DHE-DSS-AES256-SHA:DHE-RSA-AES256-SHA:AES128-GCM-SHA256:AES256-GCM-SHA384:AES128-SHA256:AES256-SHA256:AES128-SHA:AES256-SHA:AES:CAMELLIA:DES-CBC3-SHA:!aNULL:!eNULL:!EXPORT:!DES:!RC4:!MD5:!PSK:!aECDH:!EDH-DSS-DES-CBC3-SHA:!EDH-RSA-DES-CBC3-SHA:!KRB5-DES-CBC3-SHA
ssl_dh_parameters_length = 4096

Edit 10-auth.conf to prevent downgrade attacks performed against STARTTLS:

$ vi /etc/dovecot/conf.d/10-auth.conf
---
disable_plaintext_auth = yes
ssl=required

SPF record

Set a SPF record to not get sent emails marked as spam.
Create a TXT record, put @ in the hostname field and the following string (replace xxx.xxx.xxx.xxx with and IP address) in the IP/url field.

v=spf1 a mx ip4:xxx.xxx.xxx.xxx -all

DKIM

Generate the keys for domain.tld and edit opendkim configuration file:

$ opendkim-genkey -D /etc/opendkim/keys/ -d domain.tld -s default
$ chown -R opendkim:opendkim /etc/opendkim/keys/
$ restorecon -Rv /etc/opendkim/keys
$ vi /etc/opendkim.conf
---
Mode    sv
Socket  inet:8891@localhost
Canonicalization        relaxed/simple
Domain  domain.tld
#KeyFile        /etc/opendkim/keys/default.private
KeyTable        refile:/etc/opendkim/KeyTable
SigningTable    refile:/etc/opendkim/SigningTable
ExternalIgnoreList      refile:/etc/opendkim/TrustedHosts
InternalHosts   refile:/etc/opendkim/TrustedHosts
SignatureAlgorithm      rsa-sha256

Edit KeyTable file and set the correct domain name:

$ vi /etc/opendkim/KeyTable
---
default._domainkey.domain.tld domain.tld:default:/etc/opendkim/keys/default.private

Edit SigningTable file and set the correct domain name:

$ vi /etc/opendkim/SigningTable
---
*@domain.tld default._domainkey.domain.tld

Edit TrustedHosts file and set the correct domain name:

$ vi /etc/opendkim/TrustedHosts
---
127.0.0.1
::1
domain.tld
mail.domain.tld

Use the content of /etc/opendkim/keys/default.txt to create a new TXT record on the DNS.
Enable opendkim service:

$ systemctl enable opendkim && systemctl start opendkim

DMARC

Install and start opendmarc:

$ yum install opendmarc
$ systemctl enable opendmarc && systemct start opendmarc && systemctl restart postfix

Also add a new TXT DNS record:

TXT _dmarc v=DMARC1; aspf=r; p=quarantine; pct=100; rua=mailto:postmaster@domain.tld; ruf=mailto:postmaster@domain.tld 1800

Where:

TXT=record type (required)
v=Protocol version (required)
p=Policy for domain (required) - available options: "none", "quarantine", "reject"
aspf=Alignment mode for SPF (optional) - available options: "r" (relaxed) to allow subdomains and "s" (strict) to filter subdomains
pct=percentage of messages subjected to filtering (optional)
rua=Reporting URI of aggregate reports
ruf=tag for the distribution of forensic reports

For some reason namecheap knowledge base is wrong, when defining a TXT record for DMARC the host must be set to _dmarc instead of _dmarc.domain.tld<.>.

Fail2ban

Fail2ban is a useful tool capable of mitigating brute force attacks performed against a multitude of services (openssh, dovecot, postfix, etc).
Create a file named jail.local.

$ cd /etc/fail2ban
$ vi jail.clocal
---
[DEFAULT]
ignoreip = 127.0.0.1/8
backend = systemd

[dovecot]
enabled = true
port    = 25,143,465,587,993
action 	= iptables-multiport[name=dovecot, port="25,143,465,587,993", protocol=tcp]
logpath = /var/log/maillog
maxretry = 5
findtime = 7200
bantime = 7200

[postfix]
enabled	= true
port   	= 25,143,465,587,993
action  = iptables-multiport[name=postfix, port="25,143,465,587,993", protocol=tcp]
logpath	= /var/log/maillog
maxretry = 5
findtime = 7200
bantime = 7200

[postfix-sasl]
enabled = true
port    = 25,143,465,587,993
action  = iptables-multiport[name=postfix-sasl, port="25,143,465,587,993", protocol=tcp]
logpath = /var/log/maillog
maxretry = 5
findtime = 7200
bantime = 7200
---
$ systemctl enable iptables fail2ban && systemctl restart iptables fail2ban

Add some custom rules to failregex line:

$ vi /etc/fail2ban/filter.d/postfix.conf
---
^%(__prefix_line)slost connection after AUTH from (.*)\[<HOST>\]$
^%(__prefix_line)slost connection after CONNECT from (.*)\[<HOST>\]$
^%(__prefix_line)slost connection after EHLO from (.*)\[<HOST>\]$

Iptables

Edit iptables.rules and add:

$ systemctl stop iptables
$ vi /etc/iptables/iptables.rules
---
-A INPUT -p tcp -m tcp --dport 143 -j ACCEPT
-A INPUT -p tcp -m tcp --dport 25 -j ACCEPT
-A INPUT -p tcp -m tcp --dport 587 -j ACCEPT
-A INPUT -p tcp -m tcp --dport 993 -j ACCEPT
-A INPUT -p tcp -m tcp --dport 465 -j ACCEPT
---
$ systemctl start iptables

This is it, if I didn’t got anything wrong the IMAP/SMTP services should be up and running and accesible from every client supporting TLS and IMAP.
I personally use K-9 mail on Android and Thunderbird on Linux computers.

Usefull links:

  1. test postfix encryption: checktls.com
  2. test email server capabilities: mail-tester.com
  3. test mail server for IP leak: emailipleak.com