Mail Server Setup - RHEL

/ linux, centos, mail, postfix, rhel, rocky-linux

This Mail Server set up originally prepared and tested for Centos and Rocky Linux 7-8-9.

Contents


DNS Domain Setup

Example domain name:

YOURMAIL.TLD

A Record:

Type: A
Host: mail.YOURMAIL.TLD
Value: 123.123.123.123

MX Record:

Type: MX
Priority: 10
Host: @
Value: mail.YOURMAIL.TLD

SPF Record:

Type: TXT Record
Host: @
Value: v=spf1 ip4:123.123.123.123 ~all

DKIM Record:

Type: TXT Record
Host: default._domainkey
Value: v=DKIM1; k=rsa; p=MxkdvNMhmnvh8gpdwW7AwzD9BzHHZUhWDCiL7N8rQVO9QIDAQAB

Centos 8 Minimal Setup

Install base packages:

dnf update
dnf install epel-release
dnf update
dnf install mc htop wget

Set hostname:

/etc/hostname

mail.YOURMAIL.TLD

Install Rsyslog:

Rsyslog is not installed in centos 8 minimal

dnf install rsyslog
systemctl start rsyslog

Disable Firewall:

service firewalld stop
service firewalld disable

Disable Selinux:

/etc/selinux/config

SELINUX=disabled

Mail Dir and Virtual User

mkdir -p /mail/vmail
chmod 770 /mail/vmail
useradd -r -u 101 -g mail -d /mail/vmail -s /sbin/nologin -c "Virtual mailbox" vmail
chown vmail:mail /mail/vmail

Web Server

Install Apache:

dnf install httpd mod_ssl
systemctl start httpd
systemctl status httpd

Install Certbot and get SSL cert:

dnf install certbot
certbot certonly

Add to the end of /etc/httpd/conf.d/ssl.conf and before of </VirtualHost>

###
ServerName mail.YOURMAIL.TLD
SSLCertificateFile /etc/letsencrypt/live/mail.YOURMAIL.TLD/cert.pem
SSLCertificateKeyFile /etc/letsencrypt/live/mail.YOURMAIL.TLD/privkey.pem
SSLCertificateChainFile /etc/letsencrypt/live/mail.YOURMAIL.TLD/chain.pem
###
</VirtualHost>

Restart Apache:

systemctl restart httpd

PHP

Install PHP:

wget https://rpms.remirepo.net/enterprise/remi-release-8.rpm
dnf install remi-release-8.rpm
dnf module reset php
dnf module list php
dnf module enable php:remi-8.1
dnf install php php-imap php-mysqlnd php-gd php-intl php-ldap php-zip php-pecl-imagick

Setup PHP:

/etc/php.ini

date.timezone = Europe/Kiev

Restart PHP and Apache:

systemctl restart php-fpm
systemctl restart httpd

MariaDB

Read repo setup guide at Official MariaDB Repo

/etc/yum.repos.d/MariaDB.repo
# MariaDB 10.5 CentOS repository list - created 2020-10-01 06:49 UTC
# http://downloads.mariadb.org/mariadb/repositories/
[mariadb]
name = MariaDB
baseurl = http://yum.mariadb.org/10.5/centos8-amd64
module_hotfixes=1
gpgkey=https://yum.mariadb.org/RPM-GPG-KEY-MariaDB
gpgcheck=1
dnf install MariaDB-server
systemctl start mariadb
systemctl enable mariadb
mysql_secure_installation

wget phpMyAdmin and unpack to /var/www/html/phpmyadmin

chown -R apache:apache /var/www/html/phpmyadmin
cd /var/www/html/phpmyadmin
cp config.sample.inc.php config.inc.php
/var/www/html/phpmyadmin/config.inc.php
$cfg['blowfish_secret'] = 'vUASDFSDga4wsdghhgfhtfr56kmZ8cuvGKX8K0Ed';

blowfish_secret must be 32 chars long

Postfix Admin

Download last stable Postfix Admin Release.

Unpack package to /var/www/html/postfixadmin.

Create database postfix with user postfix

Configure Postfix Admin

cd /var/www/html/postfixadmin
touch config.local.php
mkdir templates_c
chown -R apache:apache /var/www/html/postfixadmin

Configure Postfix Admin:

/var/www/html/postfixadmin/config.local.php

<?php
//
// ANDRW POSTFIXADMIN CONFIG
//

$CONF['configured'] = true;
$CONF['setup_password'] = '';
$CONF['database_password'] = 'PASSWORD';
$CONF['admin_email'] = 'postmaster@YOURMAIL.TLD';
$CONF['password_validation'] = array(
#    '/regular expression/' => '$PALANG key (optional: + parameter)',
    '/.{5}/'                => 'password_too_short 5',      # minimum length 5 characters
    '/([a-zA-Z].*){2}/'     => 'password_no_characters 2',  # must contain at least 2 characters
    '/([0-9].*){1}/'        => 'password_no_digits 1',      # must contain at least 1 digits
);
$CONF['page_size'] = '200';
$CONF['default_aliases'] = array (
    'abuse' => 'abuse@YOURMAIL.TLD',
    'hostmaster' => 'hostmaster@YOURMAIL.TLD',
    'postmaster' => 'postmaster@YOURMAIL.TLD',
    'webmaster' => 'webmaster@YOURMAIL.TLD'
);
$CONF['aliases'] = '50';
$CONF['mailboxes'] = '50';
$CONF['maxquota'] = '1024';
$CONF['domain_quota'] = 'NO';
$CONF['create_mailbox_subdirs'] = array('Drafts','Junk','Sent','Trash');
$CONF['create_mailbox_subdirs_prefix']='';
$CONF['new_quota_table'] = 'NO';

# create mailbox - not work anymore, FIX
# $CONF['create_mailbox_subdirs_hostoptions'] = array('novalidate-cert','norsh');
$CONF['create_mailbox_subdirs_hostoptions']=array('notls');

//
// END OF CONFIG FILE
//

Open installer URL http://IP/postfixadmin/public/setup.php, check everything is OK and enter Setup password.

Add to /var/www/html/postfixadmin/config.local.php:

$CONF['setup_password'] = 'xxx';

Return to web installer and create superadmin account.

Login to web admin page http://IP/postfixadmin/public/login.php

Postfix

Postfix - Install

dnf install postfix postfix-mysql postfix-pcre
cd /etc/postfix/
cp master.cf master.cf.bak
cp main.cf main.cf.bak

Postfix - main .cf

/etc/postfix/main.cf

# POSTFIX MAIN.CF CONFIG

# COMPATIBILITY
compatibility_level = 2

# LOCAL PATHNAME INFORMATION
queue_directory = /var/spool/postfix
command_directory = /usr/sbin
daemon_directory = /usr/libexec/postfix
data_directory = /var/lib/postfix

# QUEUE AND PROCESS OWNERSHIP
mail_owner = postfix

# INTERNET HOST AND DOMAIN NAMES
myhostname = mail.YOURMAIL.TLD
mydomain = YOURMAIL.TLD

# RECEIVING MAIL
inet_interfaces = all
inet_protocols = ipv4
mydestination = $myhostname, localhost.$mydomain, localhost

# REJECTING MAIL FOR UNKNOWN LOCAL USERS
unknown_local_recipient_reject_code = 550

# TRUST AND RELAY CONTROL
mynetworks = 168.100.189.0/28, 127.0.0.0/8
relay_domains = proxy:mysql:/etc/postfix/mysql-relay_domains_maps.cf

# ALIAS DATABASE
alias_maps = hash:/etc/aliases
alias_database = hash:/etc/aliases

# ADDRESS EXTENSIONS (e.g., user+foo)
recipient_delimiter = +

# DEBUGGING CONTROL
debug_peer_level = 2
debugger_command =
    PATH=/bin:/usr/bin:/usr/local/bin:/usr/X11R6/bin
    ddd $daemon_directory/$process_name $process_id & sleep 5

# INSTALL-TIME CONFIGURATION INFORMATION
sendmail_path = /usr/sbin/sendmail.postfix
newaliases_path = /usr/bin/newaliases.postfix
mailq_path = /usr/bin/mailq.postfix
setgid_group = postdrop
html_directory = no
manpage_directory = /usr/share/man
sample_directory = /usr/share/doc/postfix/samples
readme_directory = /usr/share/doc/postfix/README_FILES

# VIRTUAL MAILBOX
virtual_alias_maps =
    proxy:mysql:/etc/postfix/mysql-virtual_alias_maps.cf,
    regexp:/etc/postfix/virtual_regexp
virtual_mailbox_base = /mail/vmail
virtual_mailbox_domains = proxy:mysql:/etc/postfix/mysql-virtual_domains_maps.cf
virtual_mailbox_maps = proxy:mysql:/etc/postfix/mysql-virtual_mailbox_maps.cf
#virtual_mailbox_limit_maps = proxy:mysql:/etc/postfix/mysql-virtual_mailbox_limit_maps.cf
virtual_minimum_uid = 101
virtual_uid_maps = static:101
virtual_gid_maps = static:12
virtual_transport = dovecot
dovecot_destination_recipient_limit = 1

# AUTHENTICATION
smtpd_sasl_auth_enable = yes
smtpd_sasl_security_options = noanonymous
smtpd_sasl_local_domain = $myhostname
broken_sasl_auth_clients = yes
smtpd_sasl_type = dovecot
smtpd_sasl_path = private/auth

# TLS CONFIG
smtpd_use_tls = yes
smtpd_tls_security_level = may
smtpd_tls_loglevel = 1
smtpd_tls_received_header = yes
smtpd_tls_session_cache_timeout = 3600s
tls_random_source = dev:/dev/urandom
smtp_tls_session_cache_database = btree:$data_directory/smtp_tls_session_cache
smtpd_tls_key_file = /etc/letsencrypt/live/mail.YOURMAIL.TLD/privkey.pem
smtpd_tls_cert_file = /etc/letsencrypt/live/mail.YOURMAIL.TLD/fullchain.pem

# OUTGOING TLS SECURITY
smtp_tls_security_level = may
smtp_tls_ciphers = export
smtp_tls_protocols = !SSLv2, !SSLv3
smtp_tls_loglevel = 1

# RESTRICTIONS

smtpd_client_restrictions =
    permit_mynetworks,
    permit_sasl_authenticated,
    # Whitelist
    check_client_access hash:/etc/postfix/whitelist,
    # Rejects request when client sends SMTP commands before knowing if Postfix supports pipelining ESMTP commands
    reject_unauth_pipelining,
    # Block clients with from addresses whose domains do not have A/MX records
    reject_unknown_address,
    reject_unknown_client_hostname

smtpd_helo_restrictions =
    permit_mynetworks,
    permit_sasl_authenticated,
    # Deny servers that have a non-existent or non-FQDN address in HELO
    reject_invalid_helo_hostname,
    reject_non_fqdn_helo_hostname,
    # Prohibit messages from servers for which there is no A or MX record
    reject_unknown_helo_hostname

smtpd_sender_restrictions =
    permit_mynetworks,
    permit_sasl_authenticated,
    # Prohibit sending messages when MAIL FROM address does not match username
    reject_authenticated_sender_login_mismatch,
    # Reject messages from non-existent domains
    reject_unknown_sender_domain,
    # Reject messages from domains in non-FQDN format
    reject_non_fqdn_sender,
    # Reject messages with non-existent sender address
    reject_unlisted_sender,
    reject_unauth_destination

smtpd_recipient_restrictions =
    permit_mynetworks,
    permit_sasl_authenticated,
    # Reject all messages that is not addressed to our domains
    reject_unauth_destination,
    # Reject messages with a non-existent recipient address
    reject_unlisted_recipient,
    # Reject messages to non-existent domains
    reject_unknown_recipient_domain,
    # Rejects messages if recipient is not in FQDN format
    reject_non_fqdn_recipient,
    # Reject messages from sender with blank address for multiple recipients
    reject_multi_recipient_bounce,
    reject_non_fqdn_sender,
    check_sender_access hash:/etc/postfix/reject-sender,
    # check_policy_service unix:/var/spool/postfix/postgrey/socket,
    reject_rbl_client bl.spamcop.net,
    check_policy_service unix:private/policyd-spf

smtpd_helo_required = yes
unknown_local_recipient_reject_code = 550
disable_vrfy_command = yes
smtpd_data_restrictions = reject_unauth_pipelining

# OTHER OPTIONS

# Message size limit ~20Meg
message_size_limit = 204800000

# Mailbox size limit ~100G
mailbox_size_limit = 1024000000000

# Backup all messages
#always_bcc = backup@YOURMAIL.TLD

# SPF
policyd-spf_time_limit = 3600

# DKIM
milter_default_action = accept
smtpd_milters = inet:127.0.0.1:8891

# Reject Ext
# mime_header_checks = regexp:/etc/postfix/mime_header_checks

# Header checks
header_checks = pcre:/etc/postfix/header_checks

# Amavisd
content_filter=smtp-amavis:[127.0.0.1]:10024

# BCC
sender_bcc_maps = hash:/etc/postfix/sender_bcc
recipient_bcc_maps = hash:/etc/postfix/recipient_bcc

Postfix - master .cf

/etc/postfix/master.cf

# POSTFIX MASTER.CF CONFIG

# ==========================================================================
# service type  private unpriv  chroot  wakeup  maxproc command + args
#               (yes)   (yes)   (yes)   (never) (100)
# ==========================================================================
smtp      inet  n       -       n       -       -       smtpd
smtps     inet  n       -       n       -       -       smtpd
  -o smtpd_tls_wrappermode=yes
pickup    unix  n       -       n       60      1       pickup
cleanup   unix  n       -       n       -       0       cleanup
qmgr      unix  n       -       n       300     1       qmgr
tlsmgr    unix  -       -       n       1000?   1       tlsmgr
rewrite   unix  -       -       n       -       -       trivial-rewrite
bounce    unix  -       -       n       -       0       bounce
defer     unix  -       -       n       -       0       bounce
trace     unix  -       -       n       -       0       bounce
verify    unix  -       -       n       -       1       verify
flush     unix  n       -       n       1000?   0       flush
proxymap  unix  -       -       n       -       -       proxymap
proxywrite unix -       -       n       -       1       proxymap
smtp      unix  -       -       n       -       -       smtp
relay     unix  -       -       n       -       -       smtp
showq     unix  n       -       n       -       -       showq
error     unix  -       -       n       -       -       error
retry     unix  -       -       n       -       -       error
discard   unix  -       -       n       -       -       discard
local     unix  -       n       n       -       -       local
virtual   unix  -       n       n       -       -       virtual
lmtp      unix  -       -       n       -       -       lmtp
anvil     unix  -       -       n       -       1       anvil
scache    unix  -       -       n       -       1       scache
maildrop  unix  -       n       n       -       -       pipe
  flags=DRhu user=vmail argv=/usr/local/bin/maildrop -d ${recipient}
uucp      unix  -       n       n       -       -       pipe
  flags=Fqhu user=uucp argv=uux -r -n -z -a$sender - $nexthop!rmail ($recipient)
ifmail    unix  -       n       n       -       -       pipe
  flags=F user=ftn argv=/usr/lib/ifmail/ifmail -r $nexthop ($recipient)
bsmtp     unix  -       n       n       -       -       pipe
  flags=Fq. user=bsmtp argv=/usr/local/sbin/bsmtp -f $sender $nexthop $recipient

# ANDRW

# DOVECOT LDA
dovecot   unix  -       n       n       -       -       pipe
  flags=DRhu user=vmail:mail argv=/usr/libexec/dovecot/deliver -d ${recipient}

# SPF
policyd-spf  unix  -       n       n       -       0       spawn
  user=nobody argv=/usr/bin/python3.6 /usr/libexec/postfix/policyd-spf

# AMAVISD
smtp-amavis unix -    -    n    -    2 smtp
    -o smtp_data_done_timeout=1200
    -o smtp_send_xforward_command=yes
    -o disable_dns_lookups=yes
127.0.0.1:10025 inet n    -    n    -    - smtpd
    -o content_filter=
    -o local_recipient_maps=
    -o relay_recipient_maps=
    -o smtpd_restriction_classes=
    -o smtpd_client_restrictions=
    -o smtpd_helo_restrictions=
    -o smtpd_sender_restrictions=
    -o smtpd_recipient_restrictions=permit_mynetworks,reject
    -o mynetworks=127.0.0.0/8
    -o strict_rfc821_envelopes=yes
    -o smtpd_error_sleep_time=0
    -o smtpd_soft_error_limit=1001
    -o smtpd_hard_error_limit=1000
    -o cleanup_service_name=altbcc
    -o smtpd_milters=
#    -o receive_override_options=no_address_mappings
altbcc unix    n       -       n       -       0       cleanup
    -o always_bcc=backup@YOURMAIL.TLD

Postfix - header_checks

Add to the end of /etc/postfix/header_checks

# amavisd добавляет лишние заголовки на которые срабатывает антиспам, убираем
/^Received: from localhost .*/ IGNORE

Postfix - mime_header_checks

/etc/postfix/mime_header_checks

/name=[^>]*\.(bat|com|exe|dll|vbs|js|rar)/ REJECT
postmap /etc/postfix/mime_header_checks

Postfix - recipient_bcc

/etc/postfix/recipient_bcc

# from.name@YOURMAIL.TLD to.name_bcc@YOURMAIL.TLD
postmap /etc/postfix/recipient_bcc

Postfix - sender_bcc

/etc/postfix/sender_bcc

# from.name@YOURMAIL.TLD to.name_bcc@YOURMAIL.TLD
postmap /etc/postfix/sender_bcc

Postfix - reject-sender

/etc/postfix/reject-sender

# somedomain.tld REJECT
postmap /etc/postfix/reject-sender

Postfix - whitelist

/etc/postfix/whitelist

# 8.8.8.8 OK
# somedomain.tld OK
postmap /etc/postfix/whitelist

Postfix - virtual_regexp

/etc/postfix/virtual_regexp

touch /etc/postfix/virtual_regexp

Postfix mySQL - mysql-virtual_alias_maps.cf

/etc/postfix/mysql-virtual_alias_maps.cf

hosts = localhost
user = postfix
password = PASSWORD
dbname = postfix
query = SELECT goto FROM alias WHERE address='%s' AND active = '1'

Postfix mySQL - mysql-virtual_domains_maps.cf

/etc/postfix/mysql-virtual_domains_maps.cf

hosts = localhost
user = postfix
password = PASSWORD
dbname = postfix
query = SELECT domain FROM domain WHERE domain='%s' AND backupmx = '0' AND active = '1'

Postfix mySQL - mysql-relay_domains_maps.cf

/etc/postfix/mysql-relay_domains_maps.cf

hosts = localhost
user = postfix
password = PASSWORD
dbname = postfix
query = SELECT domain FROM domain WHERE domain='%s' and backupmx = '1'

Postfix mySQL - mysql-virtual_mailbox_maps.cf

/etc/postfix/mysql-virtual_mailbox_maps.cf

hosts = localhost
user = postfix
password = PASSWORD
dbname = postfix
query = SELECT maildir FROM mailbox WHERE username='%s' AND active = '1'

Postfix mySQL - mysql-virtual_mailbox_limit_maps.cf

/etc/postfix/mysql-virtual_mailbox_limit_maps.cf

hosts = localhost
user = postfix
password = PASSWORD
dbname = postfix
query = SELECT quota FROM mailbox WHERE username='%s' AND active = '1'

SPF check

Install policyd-spf:

dnf install pypolicyd-spf
cp /etc/python-policyd-spf/policyd-spf.conf /etc/python-policyd-spf/policyd-spf.conf.bak

Use default setting of /etc/python-policyd-spf/policyd-spf.conf See man 1 policyd-spf for information on setting up and using this policy server. See man 5 policyd-spf.conf for configuration file information.

DKIM

Install:

dnf install opendkim opendkim-tools
cp /etc/opendkim.conf /etc/opendkim.conf.bak

Configure:

/etc/opendkim.conf

Mode                sv
# KeyFile           /etc/opendkim/keys/default.private
KeyTable            /etc/opendkim/KeyTable
SigningTable            refile:/etc/opendkim/SigningTable
ExternalIgnoreList      refile:/etc/opendkim/TrustedHosts
InternalHosts           refile:/etc/opendkim/TrustedHosts

Generate key for your domain:

cd /etc/opendkim/keys
opendkim-genkey -d YOURMAIL.TLD -s YOURMAIL.TLD
chown opendkim:opendkim YOURMAIL.TLD*

echo "default._domainkey.YOURMAIL.TLD YOURMAIL.TLD:default:/etc/opendkim/keys/YOURMAIL.TLD.private" >> /etc/opendkim/KeyTable
echo "*@YOURMAIL.TLD default._domainkey.YOURMAIL.TLD" >> /etc/opendkim/SigningTable
echo "mail.YOURMAIL.TLD" >> /etc/opendkim/TrustedHosts

Enable and start service:

systemctl start opendkim
systemctl enable opendkim

Dovecot

Install:

dnf install dovecot dovecot-mysql dovecot-pigeonhole
cp /etc/dovecot/dovecot.conf /etc/dovecot/dovecot.conf.bak

Dovecot - dovecot.conf

/etc/dovecot/dovecot.conf

# ANDRW DOVECOT DOVECOT.CF CONFIG

protocols = imap pop3 lmtp sieve
auth_mechanisms = plain login
passdb {
  driver = sql
  args = /etc/dovecot/dovecot-mysql.conf
}
userdb {
  driver = prefetch
}
userdb {
  driver = sql
  args = /etc/dovecot/dovecot-mysql.conf
}
mail_location = maildir:/mail/vmail/%d/%n
first_valid_uid = 101
#last_valid_uid = 0
first_valid_gid = 12
#last_valid_gid = 0
#mail_plugins =
mailbox_idle_check_interval = 30 secs
maildir_copy_with_hardlinks = yes
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 lmtp {
  unix_listener lmtp {
    #mode = 0666
  }
}
service imap {
  vsz_limit = 256M
}
service pop3 {
}
service auth {
  unix_listener auth-userdb {
    mode = 0666
    user = vmail
    group = mail
  }

  # Postfix smtp-auth
  unix_listener /var/spool/postfix/private/auth {
    mode = 0666
    user = postfix
    group = postfix
  }
}
service auth-worker {
}
service dict {
  unix_listener dict {
    mode = 0666
    user = vmail
    group = mail
  }
}
service managesieve-login {
  inet_listener sieve {
    port = 4190
  }
  service_count = 1
  process_min_avail = 0
  vsz_limit = 64M
}
service managesieve {
}
service stats {
  unix_listener stats-reader {
    user = vmail
    group = mail
    mode = 0660
    }
  unix_listener stats-writer {
    user = vmail
    group = mail
    mode = 0660
    }
}
ssl = yes
ssl_cert = </etc/letsencrypt/live/mail.YOURMAIL.TLD/fullchain.pem
ssl_key = </etc/letsencrypt/live/mail.YOURMAIL.TLD/privkey.pem
ssl_verify_client_cert = no
#ssl_ca =
lda_mailbox_autocreate = yes
lda_mailbox_autosubscribe = yes
protocol lda {
  mail_plugins = quota sieve
  postmaster_address = postmaster@YOURMAIL.TLD
}
protocol imap {
  mail_plugins = quota imap_quota trash
  imap_client_workarounds = delay-newmail
}
lmtp_save_to_detail_mailbox = yes
protocol lmtp {
  mail_plugins = sieve
}
protocol pop3 {
  mail_plugins = quota
  pop3_client_workarounds = outlook-no-nuls oe-ns-eoh
}
protocol sieve {
  managesieve_max_line_length = 65536
  managesieve_implementation_string = Dovecot Pigeonhole
  managesieve_max_compile_errors = 5
}
dict {
  quotadict = mysql:/etc/dovecot/dovecot-dict-quota.conf
}
plugin {
  #quota = dict:user::proxy::quotadict
  acl = vfile:/etc/dovecot/acls
  #trash = /etc/dovecot/trash.conf
  sieve_global_path = /data/sieve/globalfilter.sieve
  sieve = ~/dovecot.sieve
  sieve_dir = ~/sieve
  sieve_global_dir = /data/sieve/
  #sieve_extensions = +notify +imapflags
  sieve_max_script_size = 1M
  #sieve_max_actions = 32
  #sieve_max_redirects = 4
}
# andrw
protocol imap {
  mail_max_userip_connections = 50
}
service imap-login {
  process_limit = 512
}
# логирование неудачных попыток входа
auth_verbose = yes
# listening IPv4 only
listen = *

Dovecot - trash.conf

/etc/dovecot/trash.conf

1 Spam
# Uncomment if you want trash as well
# 2 Trash

Dovecot - dovecot-mysql.conf

/etc/dovecot/dovecot-mysql.conf

driver = mysql
connect = host=localhost dbname=postfix user=postfix password=PASSWORD
default_pass_scheme = MD5-CRYPT

# following should all be on one line.
password_query = SELECT username as user, password, concat('/mail/vmail/', maildir) as userdb_home, concat('maildir:/mail/vmail/', maildir) as userdb_mail, 101 as userdb_uid, 12 as userdb_gid FROM mailbox WHERE username = '%u' AND active = '1'

# following should all be on one line
user_query = SELECT concat('/mail/vmail/', maildir) as home, concat('maildir:/mail/vmail/', maildir) as mail, 101 AS uid, 12 AS gid, CONCAT('*:messages=10000:bytes=', quota) as quota_rule FROM mailbox WHERE username = '%u' AND active = '1'

Dovecot - dovecot-dict-quota.conf

/etc/dovecot/dovecot-dict-quota.conf

connect = host=localhost dbname=postfix user=postfix password=PASSWORD
map {
    pattern = priv/quota/storage
    table = quota2
    username_field = username
    value_field = bytes
}
map {
    pattern = priv/quota/messages
    table = quota2
    username_field = username
    value_field = messages
}

Dovecot - Create the sieve filter for SPAM filtering

mkdir /mail/sieve

Create file

/mail/sieve/globalfilter.sieve

require "fileinto";
if exists "X-Spam-Flag" {
    if header :contains "X-Spam-Flag" "NO" {
    }
    else {
        fileinto "Trash";
        stop;
        }
}
if header :contains "subject" ["***SPAM***"] {
    fileinto "Trash";
    stop;
}
chown -R vmail:mail /mail/sieve

Dovecot - Start and enable

systemctl start dovecot
systemctl enable dovecot

Amavis

There are error during dnf install amavisd-new:
Error: - nothing provides perl(IO::Stringy) needed by amavis-2.12.0-9.el8.noarch

To Fix this error enable PowerTools Repo:

dnf config-manager --enable PowerTools

Install

dnf install amavisd-new clamav clamav-update clamd

Optional: install without Antivirus

dnf install amavisd-new

Update ClamAV

freshclam

Backup Amavis config

cp /etc/amavisd/amavisd.conf /etc/amavisd/amavisd.conf.bak

Configure Amavis

/etc/amavisd/amavisd.conf

$mydomain = 'mail.YOURMAIL.TLD';
$myhostname = 'YOURMAIL.TLD';
$notify_method  = 'smtp:[127.0.0.1]:10025';
$forward_method = 'smtp:[127.0.0.1]:10025';

Start and enable Amavis

systemctl start amavisd
systemctl enable amavisd

Start and enable Postfix

systemctl start postfix
systemctl enable postfix

Roundcube

  • Download stable complete Roundcube package.
  • Unpack package to /var/www/html/webmail
  • chown to apache:apache
chown -R apache:apache /var/www/html/webmail
  • open installer URL http://IP/webmail/installer/, check everything is OK, complete setup.

Extras

PostfixAdmin: Dont forget to create backup@YOURMAIL.TLD user.

Previous Post