Postfix Virtual domains and accounts postfixadmin and mysql

From Luniwiki
Jump to: navigation, search

Software installation

Base packages

postfix-mysql dovecot-mysql dovecot-imapd postgrey amavis clamav clamav-daemon spamassassin libdbi-perl libdbd-mysql-perl php7.0-imap postfix-policyd-spf-python

Utils that extend the abilities of the spam and virus detection packages by allowing greater inspection of attached files

pyzor razor arj cabextract lzop nomarch p7zip-full ripole rpm2cpio tnef unzip unrar-free zip zoo

Package for postfixadmin

apache2 libapache2-mod-php dbconfig-common wwwconfig-common php-mbstring php-mysql

Package for opendkim

opendkim opendkim-tools

Package for opendmarc

opendmarc

Package for dovecot sieve

dovecot-sieve

Package to mysqmail-postfix-logger

apt install mysqmail-postfix-logger

All packages

apt install postfix-mysql dovecot-mysql postgrey amavis clamav clamav-daemon spamassassin libdbi-perl libdbd-mysql-perl php7.0-imap postfix-policyd-spf-python pyzor razor arj cabextract lzop nomarch p7zip-full ripole rpm2cpio tnef unzip unrar-free zip zoo apache2 libapache2-mod-php dbconfig-common wwwconfig-common php-mbstring php-mysql opendkim opendkim-tools dovecot-imapd opendmarc dovecot-sieve

Database creation

The database mail will be used by postfixadmin, postfix, dovecot

mysql -p
Enter password:
Welcome to the MySQL monitor.  Commands end with ; or \g.
Your MySQL connection id is 39
Server version: 5.7.23-0ubuntu0.16.04.1 (Ubuntu)
Copyright (c) 2000, 2018, Oracle and/or its affiliates. All rights reserved.
Oracle is a registered trademark of Oracle Corporation and/or its
affiliates. Other names may be trademarks of their respective
owners.
Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.
mysql> create database mail;
create database mail;
mysql> grant all on mail.* to 'postfixadmin'@'localhost' identified by 'postfixadminpassword';
mysql> grant select on mail.* to 'postfix'@'localhost' identified by 'postfixpassword';

Postfixadmin

Download software

wget https://sourceforge.net/projects/postfixadmin/files/postfixadmin/postfixadmin-3.2/postfixadmin-3.2.tar.gz/download

Tar extraction

mkdir -p /var/www/rigel.oamis.net/webroot
cd /var/www/rigel.oamis.net
tar xvfz postfixadmin-3.2.tar.gz
mkdir postfixadmin-3.2/templates_c
chown www-data:www-data postfixadmin-3.2/templates_c
cd webroot/
ln -s /var/www/rigel.oamis.net/postfixadmin-3.2/public/ postfixadmin

DB access configuration

cd /var/www/rigel.oamis.net/postfixadmin-3.2

Create file config.local.php

cat config.local.php
<?php
$CONF['database_type'] = 'mysqli';
$CONF['database_user'] = 'postfixadmin';
$CONF['database_password'] = 'postfixadminpassowrd';
$CONF['database_name'] = 'mail';
$CONF['configured'] = true;
?>

Additional settings

We create settings in config.local.php to avoid to modify original file.

// Default Aliases
// The default aliases that need to be created for all domains.
// You can specify the target address in two ways:
// a) a full mail address
// b) only a localpart ('postmaster' => 'admin') - the alias target will point to the same domain
$CONF['default_aliases'] = array (
   'abuse' => 'domain@oamis.net',
   'hostmaster' => 'domain@oamis.net',
   'postmaster' => 'domain@oamis.net',
   'webmaster' => 'domain@oamis.net'
);
// Mailboxes
// If you want to store the mailboxes per domain set this to 'YES'.
// Examples:
//   YES: /usr/local/virtual/domain.tld/username@domain.tld
//   NO:  /usr/local/virtual/username@domain.tld
$CONF['domain_path'] = 'NO';
// If you don't want to have the domain in your mailbox set this to 'NO'.
// Examples:
//   YES: /usr/local/virtual/domain.tld/username@domain.tld
//   NO:  /usr/local/virtual/domain.tld/username
// Note: If $CONF['domain_path'] is set to NO, this setting will be forced to YES.
$CONF['domain_in_mailbox'] = 'YES';
$CONF['show_footer_text'] = 'NO';
// Specify '' for Dovecot and 'INBOX.' for Courier.
$CONF['create_mailbox_subdirs_prefix']='';

Check configuration and database creation

https://rigel.oamis.net/postfixadmin/setup.php

This will create the database, and at the end will ask you for Setup password.
Once the password is created, the installer will provide the line to add to file config.local.php (before the ?> )

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

The set will ask to create an super admin user, once created, the message provided will be:

The admin superadmin@email has been added!
You are done with your basic setup.

Now you can access the application from

https://rigel.oamis.net/postfixadmin/

User to handle mail directory

useradd -r -u 150 -g mail -d /var/vmail -s /sbin/nologin -c "Virtual maildir handler" vmail
mkdir /var/vmail
chmod 770 /var/vmail
chown vmail:mail /var/vmail

We check

ls -ld /var/vmail/
drwxrwx--- 2 vmail mail 4096 Aug 26 17:12 /var/vmail/
id vmail
uid=150(vmail) gid=8(mail) groups=8(mail)

Dovecot

dovecot-sql.conf.ext

We configure dovecot to fetch in the database.

cat /etc/dovecot/dovecot-sql.conf.ext | grep -v "^#" | grep -v "^$"
driver = mysql
connect = host=localhost dbname=mail user=postfix password=postfixpassowrd
default_pass_scheme = MD5-CRYPT
password_query = SELECT username as user, password, '/var/vmail/%u' as userdb_home, 'maildir:/var/vmail/%u/Maildir' as userdb_mail, 150 as userdb_uid,  8 as userdb_gid FROM mailbox WHERE username = '%u' AND active = '1'
user_query = SELECT '/var/vmail/%u' as home, 'maildir:/var/vmail/%u/Maildir' as mail, 150 AS uid, 8 AS gid, concat('dirsize:storage=', quota) AS quota 
FROM mailbox WHERE username = '%u' AND active = '1'

10-auth.conf

We disable plain text unless from the local machine or encrypted. We also disable the system authentication and replace it by SQL authentication

cat /etc/dovecot/conf.d/10-auth.conf | grep -v "^#" | grep -v "^$"
disable_plaintext_auth = yes
auth_mechanisms = plain login
!include auth-sql.conf.ext

10-mail.conf

We change mail location to mail dir and only user vmail can create mails.

cat /etc/dovecot/conf.d/10-mail.conf | grep -v "#" | grep -v "^$"
mail_location = maildir:/var/vmail/%u/Maildir
namespace {
   type = private
   separator = /
   prefix =
   inbox = yes
   mailbox Sent {
       auto = subscribe
       special_use = \Sent
   }
   mailbox "Sent Messages" {
       auto = no
       special_use = \Sent
   }
   mailbox "Sent Items" {
       auto = no
       special_use = \Sent
   }
   mailbox Drafts {
       auto = subscribe
       special_use = \Drafts
   }
   mailbox Trash {
       auto = subscribe
       special_use = \Trash
   }
   mailbox "Deleted Messages" {
       auto = no
       special_use = \Trash
   }
   mailbox Junk {
       auto = subscribe
       special_use = \Junk
   }
   mailbox Spam {
       auto = no
       special_use = \Junk
   }
   mailbox "Junk E-mail" {
       auto = no
       special_use = \Junk
   }
   mailbox Archive {
       auto = no
       special_use = \Archive
   }
   mailbox Archives {
       auto = no
       special_use = \Archive
   }
}
mail_uid = vmail
mail_gid = mail
first_valid_uid = 150
last_valid_uid = 150

15-mailboxes.conf

MailBox setting have been defined in 10-mail.conf, so we disable 15-mailboxes.conf.

mv 15-mailboxes.conf 15-mailboxes.conf.bak

10-ssl.conf

cat /etc/dovecot/conf.d/10-ssl.conf | grep -v "^#" | grep -v "^$"
ssl = yes
ssl_cert = </etc/dovecot/ssl/rigel.oamis.net.cer
ssl_key = </etc/dovecot/ssl/rigel.oamis.net.key
ssl_ca = </etc/dovecot/ssl/ca.cer
ssl_dh_parameters_length = 4096
ssl_protocols = !SSLv2 !SSLv3 !TLSv1 !TLSv1.1
ssl_cipher_list = HIGH:!RSA:!anull
ssl_prefer_server_ciphers = yes

10-master.conf

cat /etc/dovecot/conf.d/10-master.conf | grep -v "#" | grep -v "^$"
service imap-login {
 inet_listener imap {
   port = 0
 }
 inet_listener imaps {
   port = 993
   ssl = yes
 }
}
service pop3-login {
 inet_listener pop3 {
 }
 inet_listener pop3s {
 }
}
service lmtp {
 unix_listener lmtp {
 }
}
service imap {
}
service pop3 {
}
service auth {
 unix_listener auth-userdb {
   mode = 0666
   user = vmail
   group = mail
 }
 unix_listener /var/spool/postfix/private/auth {
   mode = 0666
   user = postfix
   group = postfix
 }
}
service auth-worker {
}
service dict {
 unix_listener dict {
 }
}

Add user vmail to group dovecot

adduser vmail dovecot
Adding user `vmail' to group `dovecot' ...
Adding user vmail to group dovecot
Done.

And we check

id vmail
uid=150(vmail) gid=8(mail) groups=8(mail),121(dovecot)

Sieve

cat /etc/dovecot/conf.d/90-sieve.conf | grep -v "#" | grep -v "^ *$"
plugin {
 sieve_dir = ~/sieve
 sieve = ~/sieve/dovecot.sieve
 sieve_global_dir = /var/vmail/sieve
 sieve_before = /var/vmail/sieve/dovecot.sieve
 sieve_max_redirects = 30
 sieve_vacation_send_from_recipient = yes
}

Activate sieve for lmtp protocol

cat 10-director.conf | grep -v "^ *#" | grep -v "^ *$"
service director {
 unix_listener login/director {
 }
 fifo_listener login/proxy-notify {
 }
 unix_listener director-userdb {
 }
 inet_listener {
 }
}
service imap-login {
}
service pop3-login {
}
protocol lmtp {
 mail_plugins = $mail_plugins sieve
}

And for protocol lda

cat 15-lda.conf | grep -v "^ *#" | grep -v "^ *$"
postmaster_address = domain@oamis.net
protocol lda {
 mail_plugins = $mail_plugins sieve
}

We create the master sieve file

cd /var/vmail/
mkdir sieve
chown vmail:mail sieve/
cd sieve/
cat dovecot.sieve
require ["fileinto"];

# rule:[Move Spam to Junk Folder] if header :is "X-Spam-Flag" "YES" { fileinto "Junk"; } # rule:[Openmarc fails to Junk Folder] if header :contains "Authentication-Results" "OpenDmarc; dmarc=fail" { fileinto "Junk"; } chmod 500 dovecot.sieve chown vmail:mail dovecot.sieve

SpamAssassin

spamassassin -V
SpamAssassin version 3.4.2
 running on Perl version 5.26.1

Download schema and create database

wget http://svn.apache.org/repos/asf/spamassassin/tags/spamassassin_release_3_4_2/sql/bayes_mysql.sql
mysql -p
Enter password:
Welcome to the MySQL monitor.  Commands end with ; or \g.
Your MySQL connection id is 3267
Server version: 5.7.27-0ubuntu0.18.04.1-log (Ubuntu)
Copyright (c) 2000, 2019, Oracle and/or its affiliates. All rights reserved.
Oracle is a registered trademark of Oracle Corporation and/or its
affiliates. Other names may be trademarks of their respective
owners.
Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.
mysql> create database sa_bayes;
Query OK, 1 row affected (0.01 sec)
mysql> use sa_bayes;
Database changed
mysql> source bayes_mysql.sql
Query OK, 0 rows affected (0.09 sec)
Query OK, 0 rows affected (0.02 sec)
Query OK, 1 row affected (0.02 sec)
Query OK, 0 rows affected (0.02 sec)
Query OK, 0 rows affected (0.03 sec)
Query OK, 0 rows affected (0.03 sec)
mysql> GRANT SELECT, INSERT, UPDATE, DELETE ON sa_bayes.* TO sa_user@localhost IDENTIFIED BY 'xxxxxxxxxxxxxxxxx';
Query OK, 0 rows affected, 1 warning (0.07 sec)
mysql> flush privileges;
Query OK, 0 rows affected (0.02 sec)
mysql> quit
Bye

Modify setup

cd /etc/spamassassin/
 cat /etc/spamassassin/local.cf | grep -v "^#" | grep -v "^ *$"
ifplugin Mail::SpamAssassin::Plugin::Shortcircuit
endif # Mail::SpamAssassin::Plugin::Shortcircuit
required_score      5.0
rewrite_header      subject πŸš½β›”
report_safe         0
lock_method         flock
use_bayes          1
bayes_auto_learn   1
bayes_auto_expire  1
bayes_store_module Mail::SpamAssassin::BayesStore::MySQL
bayes_sql_dsn      DBI:mysql:sa_bayes:127.0.0.1:3306
bayes_sql_username sa_user
bayes_sql_password xxxxxxxxxxxxxxxxx
score DNS_FROM_AHBL_RHSBL 0
score URIBL_AB_SURBL 0 0.3306 0 0.3812
score URIBL_JP_SURBL 0 0.3360 0 0.4087
score URIBL_OB_SURBL 0 0.2617 0 0.3008
score URIBL_PH_SURBL 0 0.2240 0 0.2800
score URIBL_SBL 0 0.1094 0 0.1639
score URIBL_SC_SURBL 0 0.3600 0 0.4498
score URIBL_WS_SURBL 0 0.1533 0 0.2140
loadplugin Mail::SpamAssassin::Plugin::DKIM
whitelist_from_dkim *@paypal.com
whitelist_from_dkim *@linkedin.com
whitelist_from_dkim *@twitter.com
whitelist_from_dkim *@bounce.twitter.com
ok_locales          all
score RP_MATCHES_RCVD 0

Amavis, ClamAV, and SpamAssassin

add users to group of each other

adduser amavis clamav
Adding user `amavis' to group `clamav' ...
Adding user amavis to group clamav
Done.

We check

id clamav
uid=115(clamav) gid=123(clamav) groups=123(clamav),127(amavis)
id amavis
uid=119(amavis) gid=127(amavis) groups=127(amavis),123(clamav)

Enable amavis

Enable antivirus and spam check

cat /etc/amavis/conf.d/15-content_filter_mode |  grep -v "#" | grep -v "^$"
use strict;
@bypass_virus_checks_maps = (
  \%bypass_virus_checks, \@bypass_virus_checks_acl, \$bypass_virus_checks_re);
@bypass_spam_checks_maps = (
  \%bypass_spam_checks, \@bypass_spam_checks_acl, \$bypass_spam_checks_re);

Enable spamassasin

We change the option ENABLE from 0 to 1 and CRON from 0 to 1 in file /etc/default/spamassassin

cat /etc/default/spamassassin |  grep -v "#" | grep -v "^$"
ENABLED=1
OPTIONS="--create-prefs --max-children 5 --helper-home-dir"
PIDFILE="/var/run/spamd.pid"
CRON=1

Enable it.

systemctl enable spamassassin.service
Synchronizing state of spamassassin.service with SysV init with /lib/systemd/systemd-sysv-install...
Executing /lib/systemd/systemd-sysv-install enable spamassassin

Ensure mails through amavis are for local delivery

We let deliver spam mails

cat /etc/amavis/conf.d/50-user |  grep -v "^#" | grep -v "^$"
use strict;
$final_virus_destiny      = D_DISCARD;  # (data not lost, see virus quarantine)
$final_banned_destiny     = D_BOUNCE;   # D_REJECT when front-end MTA
$final_spam_destiny       = D_PASS;
$final_bad_header_destiny = D_PASS;     # False-positive prone (for spam)
$max_servers  = 3;
$sa_tag_level_deflt  = -9999;
@lookup_sql_dsn = (
   ['DBI:mysql:database=mail;host=127.0.0.1;port=3306',
    'postfix',
    'postfixpassowrd']);
$sql_select_policy = 'SELECT domain from domain WHERE CONCAT("@",domain) IN (%k)';
1;  # ensure a defined return

Update clamav database

freshclam
Sun Aug 26 20:42:35 2018 -> ClamAV update process started at Sun Aug 26 20:42:35 2018
Sun Aug 26 20:42:38 2018 -> Downloading main.cvd [100%]
Sun Aug 26 20:42:49 2018 -> main.cvd updated (version: 58, sigs: 4566249, f-level: 60, builder: sigmgr)
Sun Aug 26 20:42:51 2018 -> Downloading daily.cvd [100%]
Sun Aug 26 20:42:57 2018 -> daily.cvd updated (version: 24875, sigs: 2064192, f-level: 63, builder: neo)
Sun Aug 26 20:42:57 2018 -> Downloading bytecode.cvd [100%]
Sun Aug 26 20:42:58 2018 -> bytecode.cvd updated (version: 327, sigs: 91, f-level: 63, builder: neo)
Sun Aug 26 20:43:03 2018 -> Database updated (6630532 signatures) from db.local.clamav.net (IP: 2400:cb00:2048:1::6810:bd8a)
Sun Aug 26 20:43:03 2018 -> ^Clamd was NOT notified: Can't connect to clamd through /var/run/clamav/clamd.ctl: No such file or directory

Restart services

  • clamav
systemctl restart clamav-daemon
systemctl status clamav-daemon
● clamav-daemon.service - Clam AntiVirus userspace daemon
  Loaded: loaded (/lib/systemd/system/clamav-daemon.service; enabled; vendor preset: enabled)
 Drop-In: /etc/systemd/system/clamav-daemon.service.d
          └─extend.conf
  Active: active (running) since Sun 2018-08-26 20:44:08 CEST; 6s ago
    Docs: man:clamd(8)
          man:clamd.conf(5)
          https://www.clamav.net/documents/
 Process: 13261 ExecStartPre=/bin/chown clamav /run/clamav (code=exited, status=0/SUCCESS)
 Process: 13256 ExecStartPre=/bin/mkdir /run/clamav (code=exited, status=0/SUCCESS)
Main PID: 13264 (clamd)
  CGroup: /system.slice/clamav-daemon.service
          └─13264 /usr/sbin/clamd --foreground=true

Aug 26 20:44:08 rigel systemd[1]: Starting Clam AntiVirus userspace daemon... Aug 26 20:44:08 rigel systemd[1]: Started Clam AntiVirus userspace daemon.

  • amavis
systemctl restart amavis
systemctl status amavis
● amavis.service - LSB: Starts amavisd-new mailfilter
  Loaded: loaded (/etc/init.d/amavis; bad; vendor preset: enabled)
  Active: active (running) since Sun 2018-08-26 20:45:21 CEST; 9s ago
    Docs: man:systemd-sysv-generator(8)
 Process: 13272 ExecStop=/etc/init.d/amavis stop (code=exited, status=0/SUCCESS)
 Process: 13280 ExecStart=/etc/init.d/amavis start (code=exited, status=0/SUCCESS)
  CGroup: /system.slice/amavis.service
          β”œβ”€  635 /usr/sbin/amavisd-new (master
          β”œβ”€13293 /usr/sbin/amavisd-new (master
          β”œβ”€13295 /usr/sbin/amavisd-new (virgin child
          β”œβ”€13296 /usr/sbin/amavisd-new (virgin child
          β”œβ”€13297 /usr/sbin/amavisd-new (virgin child
          β”œβ”€37445 /usr/sbin/amavisd-new (master
          β”œβ”€43440 /usr/sbin/amavisd-new (virgin child
          β”œβ”€43464 /usr/sbin/amavisd-new (virgin child
          β”œβ”€43577 /usr/sbin/amavisd-new (virgin child
          └─43578 /usr/sbin/amavisd-new (virgin child
Aug 26 20:45:21 rigel amavis[13293]: Found decoder for    .lha  at /usr/bin/7z
Aug 26 20:45:21 rigel amavis[13293]: Found decoder for    .iso  at /usr/bin/7z
Aug 26 20:45:21 rigel amavis[13293]: Found decoder for    .exe  at /usr/bin/unrar-free; /usr/bin/arj
Aug 26 20:45:21 rigel amavis[13293]: No decoder for       .F
Aug 26 20:45:21 rigel amavis[13293]: No decoder for       .lrz
Aug 26 20:45:21 rigel amavis[13293]: No decoder for       .lz4
Aug 26 20:45:21 rigel amavis[13293]: Using primary internal av scanner code for ClamAV-clamd
Aug 26 20:45:21 rigel amavis[13293]: Found secondary av scanner ClamAV-clamscan at /usr/bin/clamscan
Aug 26 20:45:21 rigel amavis[13293]: Deleting db files snmp.db,__db.003,__db.001,nanny.db,__db.002 in /var/lib/amavis/db
Aug 26 20:45:21 rigel amavis[13293]: Creating db in /var/lib/amavis/db/; BerkeleyDB 0.55, libdb 5.3
  • spamassassin
systemctl restart spamassassin
systemctl status spamassassin
● spamassassin.service - Perl-based spam filter using text analysis
  Loaded: loaded (/lib/systemd/system/spamassassin.service; enabled; vendor preset: enabled)
  Active: active (running) since Sun 2018-08-26 20:47:02 CEST; 6s ago
 Process: 13304 ExecStart=/usr/sbin/spamd -d --pidfile=/var/run/spamassassin.pid $OPTIONS (code=exited, status=0/SUCCESS)
Main PID: 13307 (/usr/sbin/spamd)
  CGroup: /system.slice/spamassassin.service
          β”œβ”€13307 /usr/sbin/spamd -d --pidfile=/var/run/spamassassin.pid --create-prefs --max-children 5 --helper-home-di
          β”œβ”€13309 spamd chil
          └─13310 spamd chil
Aug 26 20:46:58 rigel systemd[1]: Starting Perl-based spam filter using text analysis...
Aug 26 20:46:58 rigel spamd[13304]: logger: removing stderr method
Aug 26 20:46:59 rigel spamd[13307]: zoom: able to use 353/353 'body_0' compiled rules (100%)
Aug 26 20:47:02 rigel spamd[13307]: spamd: server started on IO::Socket::IP [::1]:783, IO::Socket::IP [127.0.0.1]:783 (running version 3.4.1)
Aug 26 20:47:02 rigel spamd[13307]: spamd: server pid: 13307
Aug 26 20:47:02 rigel spamd[13307]: spamd: server successfully spawned child process, pid 13309
Aug 26 20:47:02 rigel systemd[1]: Started Perl-based spam filter using text analysis.
Aug 26 20:47:02 rigel spamd[13307]: spamd: server successfully spawned child process, pid 13310
Aug 26 20:47:02 rigel spamd[13307]: prefork: child states: IS
Aug 26 20:47:02 rigel spamd[13307]: prefork: child states: II

Automatic learning from spam or ham

Feed spamassassin with a spam message to initialize the database

cd /usr/share/doc/spamassassin/examples/
sa-learn --spam --username=vmail sample-spam.txt
Learned tokens from 1 message(s) (1 message(s) examined)

We check version to be sure dovecot is over 2.2.24

dovecot --version
2.2.33.2 (d6601f4ec)

We add the parameter mail_attribute_dict

cat /etc/dovecot/conf.d/10-mail.conf | grep mail_attribute_dict
mail_attribute_dict = file:%h/dovecot-attributes

We add the plugin imap_sieve to imap protocol

cat /etc/dovecot/conf.d/20-imap.conf | grep -v "^ *#" | grep -v "^ *$"
protocol imap {
 mail_plugins = imap_sieve
}

Setup of plugin imapsieve

cat /etc/dovecot/conf.d/90-sieve-extprograms.conf | grep -v "^ *#" | grep -v "^ *$"
plugin {
 sieve_plugins = sieve_imapsieve sieve_extprograms
 imapsieve_url = sieve://127.0.0.1:4190
 imapsieve_mailbox1_name = Junk
 imapsieve_mailbox1_causes = COPY APPEND
 imapsieve_mailbox1_before = file:/var/vmail/sieve/report_spam.sieve
 imapsieve_mailbox2_name = *
 imapsieve_mailbox2_from = Junk
 imapsieve_mailbox2_causes = COPY
 imapsieve_mailbox2_before = file:/var/vmail/sieve/report_ham.sieve
 sieve_pipe_bin_dir = /etc/dovecot/sieve/pipe
 sieve_global_extensions = +vnd.dovecot.pipe +vnd.dovecot.environment
}

We create needed folder

mkdir -p /etc/dovecot/sieve/pipe
mkdir -p /var/vmail/imapsieve_copy
chown vmail:mail /var/vmail/imapsieve_copy
chmod 0700 /var/vmail/imapsieve_copy

We create sieve rule for spam

cat /var/vmail/sieve/report_spam.sieve
require ["vnd.dovecot.pipe", "copy", "imapsieve", "environment", "variables"];
if environment :matches "imap.user" "*" {
  set "username" "${1}";
}
pipe :copy "imapsieve_copy" [ "${username}", "spam" ]; 

We create the sieve rules for ham

cat /var/vmail/sieve/report_ham.sieve
require ["vnd.dovecot.pipe", "copy", "imapsieve", "environment", "variables"];
if environment :matches "imap.mailbox" "*" {
  set "mailbox" "${1}";
}
if string "${mailbox}" "Trash" {
  stop;
}
if environment :matches "imap.user" "*" {
  set "username" "${1}";
}
pipe :copy "imapsieve_copy" [ "${username}", "ham" ];

Script pipe run when move mails from/to spam

cat /etc/dovecot/sieve/pipe/imapsieve_copy
#!/usr/bin/env bash
# Author: Zhang Huangbin <zhb@iredmail.org>
# Purpose: Read full email message from stdin, and save to a local file.

# Usage: bash imapsieve_copy <email> <spam|ham> <output_base_dir>
export USER="$1" export MSG_TYPE="$2"
export OUTPUT_BASE_DIR="/var/vmail/imapsieve_copy" export OUTPUT_DIR="${OUTPUT_BASE_DIR}/${MSG_TYPE}" export FILE="${OUTPUT_DIR}/${USER}-$(date +%Y%m%d%H%M%S)-${RANDOM}${RANDOM}.eml"
export OWNER="vmail" export GROUP="mail"
for dir in "${OUTPUT_BASE_DIR}" "${OUTPUT_DIR}"; do if [[ ! -d ${dir} ]]; then mkdir -p ${dir} chown ${OWNER}:${GROUP} ${dir} chmod 0700 ${dir} fi done
cat > ${FILE} < /dev/stdin
# Logging export LOG='logger -p local5.info -t imapsieve_copy' [[ $? == 0 ]]> && ${LOG} "Copied one ${MSG_TYPE} email reported by ${USER}: ${FILE}"

Fix file rights

chown vmail:mail /var/vmail/sieve/report_spam.sieve /var/vmail/sieve/report_ham.sieve /etc/dovecot/sieve/pipe/imapsieve_copy
chmod 700 /var/vmail/sieve/report_spam.sieve /var/vmail/sieve/report_ham.sieve /etc/dovecot/sieve/pipe/imapsieve_copy

Restart dovecot service

systemctl restart dovecot
systemctl status dovecot
● dovecot.service - Dovecot IMAP/POP3 email server
  Loaded: loaded (/lib/systemd/system/dovecot.service; enabled; vendor preset: enabled)
  Active: active (running) since Wed 2019-08-21 22:54:25 EDT; 4s ago
    Docs: man:dovecot(1)
          http://wiki2.dovecot.org/
 Process: 61281 ExecStop=/usr/bin/doveadm stop (code=exited, status=0/SUCCESS)
Main PID: 61284 (dovecot)
   Tasks: 4 (limit: 1114)
  CGroup: /system.slice/dovecot.service
          β”œβ”€61284 /usr/sbin/dovecot -F
          β”œβ”€61285 dovecot/anvil
          β”œβ”€61286 dovecot/log
          └─61288 dovecot/config
Aug 21 22:54:25 rigel systemd[1]: Started Dovecot IMAP/POP3 email server.
Aug 21 22:54:25 rigel dovecot[61284]: doveconf: Warning: SSLv2 not supported by OpenSSL. Please consider removing it from ssl_protocols.
Aug 21 22:54:25 rigel dovecot[61284]: master: Dovecot v2.2.33.2 (d6601f4ec) starting up for imap (core dumps disabled)
Aug 21 22:54:25 rigel dovecot[61286]: config: Warning: SSLv2 not supported by OpenSSL. Please consider removing it from ssl_protocols.
cat /etc/dovecot/sieve/scan_reported_mails.sh
#!/usr/bin/env bash
# Author: Zhang Huangbin <zhb@iredmail.org>
# Purpose: Copy spam/ham to another directory and call sa-learn to learn.

# Paths to find program. export PATH="/bin:/usr/bin:/usr/local/bin:$PATH"
export OWNER="vmail" export GROUP="vmail"
# The Amavisd daemon user. # Note: on OpenBSD, it's "_vscan". On FreeBSD, it's "vscan". export AMAVISD_USER='amavis'
# Kernel name, in upper cases. export KERNEL_NAME="$(uname -s | tr '[a-z]' '[A-Z]')"
# A temporary lock file. should be removed after successfully examed messages. export LOCK_FILE='/tmp/scan_reported_mails.lock'
# Logging to syslog with 'logger' command. export LOG='logger -p local5.info -t scan_reported_mails'
# `sa-learn` command, with optional arguments. export SA_LEARN="sa-learn -u ${AMAVISD_USER}"
# Spool directory. # Must be owned by vmail:vmail. export SPOOL_DIR='/var/vmail/imapsieve_copy'
# Directories which store spam and ham emails. # These 2 should be created while setup Dovecot antispam plugin. export SPOOL_SPAM_DIR="${SPOOL_DIR}/spam" export SPOOL_HAM_DIR="${SPOOL_DIR}/ham"
# Directory used to store emails we're going to process. # We will copy new spam/ham messages to these directories, scan them, then # remove them. export SPOOL_LEARN_SPAM_DIR="${SPOOL_DIR}/processing/spam" export SPOOL_LEARN_HAM_DIR="${SPOOL_DIR}/processing/ham"
if [ -e ${LOCK_FILE} ]; then find $(dirname ${LOCK_FILE}) -maxdepth 1 -ctime 1 "$(basename ${LOCK_FILE})" >/dev/null 2>&1 if [ X"$?" == X'0' ]; then rm -f ${LOCK_FILE} >/dev/null 2>&1 else ${LOG} "Lock file exists (${LOCK_FILE}), abort." exit fi fi
for dir in "${SPOOL_DIR}" "${SPOOL_LEARN_SPAM_DIR}" "${SPOOL_LEARN_HAM_DIR}"; do if [[ ! -d ${dir} ]]; then mkdir -p ${dir} fi
chown ${OWNER}:${GROUP} ${dir} chmod 0700 ${dir} done
# If there're a lot files, direct `mv` command may fail with error like # `argument list too long`, so we need `find` in this case. if [[ X"${KERNEL_NAME}" == X'OPENBSD' ]]; then [[ -d ${SPOOL_SPAM_DIR} ]] && find ${SPOOL_SPAM_DIR} -name '*.eml' -exec mv {} ${SPOOL_LEARN_SPAM_DIR}/ \; [[ -d ${SPOOL_HAM_DIR} ]] && find ${SPOOL_HAM_DIR} -name '*.eml' -exec mv {} ${SPOOL_LEARN_HAM_DIR}/ \; else [[ -d ${SPOOL_SPAM_DIR} ]] && find ${SPOOL_SPAM_DIR} -name '*.eml' -exec mv -t ${SPOOL_LEARN_SPAM_DIR}/ {} + [[ -d ${SPOOL_HAM_DIR} ]] && find ${SPOOL_HAM_DIR} -name '*.eml' -exec mv -t ${SPOOL_LEARN_HAM_DIR}/ {} + fi
# Try to delete empty directory, if failed, that means we have some messages to # scan. rmdir ${SPOOL_LEARN_SPAM_DIR} &>/dev/null if [[ X"$?" != X'0' ]]; then output="$(${SA_LEARN} --spam ${SPOOL_LEARN_SPAM_DIR})" rm -rf ${SPOOL_LEARN_SPAM_DIR} &>/dev/null ${LOG} '[SPAM]' ${output} fi
rmdir ${SPOOL_LEARN_HAM_DIR} &>/dev/null if [[ X"$?" != X'0' ]]; then output="$(${SA_LEARN} --ham ${SPOOL_LEARN_HAM_DIR})" rm -rf ${SPOOL_LEARN_HAM_DIR} &>/dev/null ${LOG} '[CLEAN]' ${output} fi
rm -f ${LOCK_FILE} &>/dev/null

We add a crontab to process them each 10 minutes

crontab -l | grep sieve
*/10   *   *   *   *   /bin/bash /etc/dovecot/sieve/scan_reported_mails.sh

Sfp

We want SFP check the headers, but not reject the email.

cat /etc/postfix-policyd-spf-python/policyd-spf.conf | grep -v "^$"
#  For a fully commented sample config file see policyd-spf.conf.commented
debugLevel = 1
defaultSeedOnly = 1
HELO_reject = False
Mail_From_reject = False
PermError_reject = False
TempError_Defer = False
skip_addresses = 127.0.0.0/8,::ffff:127.0.0.0/104,::1

Opendkim

/etc/opendkim.conf

cat opendkim.conf | grep -v "#" | grep -v "^$"
Syslog                  yes
LogWhy                  yes
UMask                   022
UserID                  opendkim:opendkim
Canonicalization        relaxed/simple
Mode                    sv
SubDomains              yes
OversignHeaders         From
KeyTable                /etc/opendkim/KeyTable
SigningTable            /etc/opendkim/SigningTable
ExternalIgnoreList      /etc/opendkim/TrustedHosts
InternalHosts           /etc/opendkim/TrustedHosts
Socket                  inet:8891@localhost

TrustedHosts file

mkdir /etc/opendkim
cd /etc/opendkim
cat TrustedHosts
127.0.0.1
localhost
93.90.207.52
2001:8d8:1800:8008::1
rigel.oamis.net

/etc/default/opendkim

cat /etc/default/opendkim | grep -v "^#"
SOCKET="inet:8891@localhost"

create keys for a domain

mkdir -p /etc/opendkim/keys
cd /etc/opendkim/keys
mkdir oamis.net
cd oamis.net
opendkim-genkey -h rsa-sha256 -b 2048 -s dkim -d oamis.net  
chown opendkim:opendkim dkim.private
echo "dkim._domainkey.oamis.net oamis.net:dkim:/etc/opendkim/keys/oamis.net/dkim.private" >> /etc/opendkim/KeyTable
echo "oamis.net dkim._domainkey.oamis.net" >> /etc/opendkim/SigningTable
echo oamis.net >> /etc/opendkim/TrustedHosts

Add DNS record for a domain

cat /etc/opendkim/keys/oamis.net/dkim.txt >> oamis.net
/etc/init.d/bind9 restart

restart service

/etc/init.d/bind9 restart

Opendmarc

Create database

cat /usr/share/doc/opendmarc/schema.mysql | mysql -p
Enter password:
mysql -p
Enter password:
Welcome to the MySQL monitor.  Commands end with ; or \g.
Your MySQL connection id is 418
Server version: 5.7.27-0ubuntu0.18.04.1-log (Ubuntu)
Copyright (c) 2000, 2019, Oracle and/or its affiliates. All rights reserved.
Oracle is a registered trademark of Oracle Corporation and/or its
affiliates. Other names may be trademarks of their respective
owners.
Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.
mysql> CREATE USER 'opendmarc'@'localhost' IDENTIFIED BY 'xxxxxxxxxxxx';
Query OK, 0 rows affected (0.01 sec)
mysql>  GRANT ALL ON opendmarc.* to 'opendmarc'@'localhost';
Query OK, 0 rows affected (0.00 sec)
mysql> quit
Bye

/etc/opendmarc.conf

cat /etc/opendmarc.conf | grep -v "^#" | grep -v "^$"
AuthservID OpenDmarc
PidFile /var/run/opendmarc/opendmarc.pid
PublicSuffixList /usr/share/publicsuffix
RejectFailures false
Socket inet:8892@localhost
Syslog true
SyslogFacility mail
UMask 0002
UserID opendmarc
IgnoreAuthenticatedClients true
HistoryFile /var/vmail/opendmarc/opendmarc.dat
FailureReports true
FailureReportsBcc servers@oamis.net
FailureReportsSentBy noreply@oamis.net
CopyFailuresTo servers@oamis.net

/etc/default/opendmarc

cat /etc/default/opendmarc | grep -v "^#"
RUNDIR=/var/run/opendmarc
SOCKET=inet:8892@localhost
USER=opendmarc
GROUP=opendmarc
PIDFILE=$RUNDIR/$NAME.pid
EXTRAAFTER=

Create opendmarc folder

mkdir -p /var/vmail/opendmarc
chown opendmarc:opendmarc /var/vmail/opendmarc
chmod 700 /var/vmail/opendmarc

Enable service

systemctl enable opendmarc
Synchronizing state of opendmarc.service with SysV service script with /lib/systemd/systemd-sysv-install.
Executing: /lib/systemd/systemd-sysv-install enable opendmarc

Create script to transmit reports

Parameters file

rigel:~# cat /etc/luniel/etc/opendmarc-send-reports.conf
DBHOST='localhost'
DBUSER='opendmarc'
DBPASS='xxxxxxxxxxxxxxxxx'
DBNAME='opendmarc'
HISTDIR='/var/vmail/opendmarc'
HISTFILE='opendmarc'

Script file

rigel:~# cat /etc/luniel/opendmarc-send-reports.sh
#!/bin/bash
# Imports data from OpenDMARC's opendmarc.dat file into a local MySQL DB
# and sends DMARC failure reports to domain owners.
# Based on a script from Hamzah Khan (http://blog.hamzahkhan.com/)
source /etc/luniel/etc/opendmarc-send-reports.conf
UTCHOUR=`date -u +"%H"`
#We check that it is 00:00 UTC time
if [ ! "#${UTCHOUR}#" = "#00#" ] ; then
       exit 0
fi
set -e
DATESTAMP=`date +%Y%m%d%H%M%S`
# Make sure history file exists
touch ${HISTDIR}/${HISTFILE}.dat
# Move history file temp dir for processing
mv ${HISTDIR}/${HISTFILE}.dat ${HISTDIR}/${HISTFILE}.${DATESTAMP}
# Import temp history file data and send reports
/usr/sbin/opendmarc-import -dbhost=${DBHOST} -dbuser=${DBUSER} -dbpasswd=${DBPASS} -dbname=${DBNAME} -verbose < ${HISTDIR}/${HISTFILE}.${DATESTAMP}
/usr/sbin/opendmarc-reports -dbhost=${DBHOST} -dbuser=${DBUSER} -dbpasswd=${DBPASS} -dbname=${DBNAME} --utc --keepfiles -verbose -interval=86400 -report-email 'noreply@oamis.net' -report-org 'oamis.net'
#/usr/sbin/opendmarc-expire -dbhost=${DBHOST} -dbuser=${DBUSER} -dbpasswd=${DBPASS} -dbname=${DBNAME} -verbose
# Delete temp history file
# rm -rf *.$$

Add crontab line to run reports each hour but reports will be only sent at 0:00 UTC

rigel:~# crontab -l | grep opendmarc
0 * * * * /etc/luniel/opendmarc-send-reports.sh

Postfix

SQL maps files

  • /etc/postfix/mysql_virtual_alias_domainaliases_maps.cf
cat /etc/postfix/mysql_virtual_alias_domainaliases_maps.cf
user = postfix
password = postfixpassword
hosts = 127.0.0.1
dbname = mail
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
  • /etc/postfix/mysql_virtual_alias_maps.cf
cat /etc/postfix/mysql_virtual_alias_maps.cf
user = postfix
password = postfixpassword
hosts = 127.0.0.1
dbname = mail
table = alias
select_field = goto
where_field = address
additional_conditions = and active = '1'
  • /etc/postfix/mysql_virtual_domains_maps.cf
cat /etc/postfix/mysql_virtual_domains_maps.cf
user = postfix
password = postfixpassword
hosts = 127.0.0.1
dbname = mail
table = domain
select_field = domain
where_field = domain
additional_conditions = and backupmx = '0' and active = '1'
  • /etc/postfix/mysql_virtual_mailbox_domainaliases_maps.cf
cat /etc/postfix/mysql_virtual_mailbox_domainaliases_maps.cf
user = postfix
password = postfixpassword
hosts = 127.0.0.1
dbname = mail
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
  • /etc/postfix/mysql_virtual_mailbox_maps.cf
cat /etc/postfix/mysql_virtual_mailbox_maps.cf
user = postfix
password = postfixpassword
hosts = 127.0.0.1
dbname = mail
table = mailbox
select_field = CONCAT(domain, '/', local_part)
where_field = username
additional_conditions = and active = '1'
  • /etc/postfix/mysql_virtual_sender_login_maps.cf
cat /etc/postfix/mysql_virtual_sender_login_maps.cf
user = postfix
password = postfixpassword
hosts = 127.0.0.1
dbname = mail
query = SELECT goto FROM alias WHERE address='%s'

Headers check

  • /etc/postfix/header_checks
cat /etc/postfix/header_checks
/^Received:/                 IGNORE
/^User-Agent:/               IGNORE
/^X-Mailer:/                 IGNORE
/^X-Originating-IP:/         IGNORE
/^x-cr-[a-z]*:/              IGNORE
/^Thread-Index:/             IGNORE

Generating DH file

cd /etc/postfix/ssl/
openssl dhparam -out dh4096.pem 4096
Generating DH parameters, 4096 bit long safe prime, generator 2
This is going to take a long time

main.cf

cat main.cf |  grep -v "#" | grep -v "^$"
smtpd_banner = $myhostname ESMTP $mail_name (Ubuntu)
biff = no
append_dot_mydomain = no
readme_directory = no
smtpd_sasl_type = dovecot
smtpd_sasl_path = private/auth
smtpd_sasl_auth_enable = yes
broken_sasl_auth_clients = yes
smtpd_sasl_security_options = noanonymous
smtpd_sasl_local_domain =
smtpd_sasl_authenticated_header = yes
smtpd_tls_cert_file = /etc/postfix/ssl/rigel.oamis.net.cer
smtpd_tls_key_file = /etc/postfix/ssl/rigel.oamis.net.key
smtpd_tls_CAfile = /etc/postfix/ssl/ca.cer
smtpd_tls_mandatory_protocols = !SSLv2,!SSLv3
smtp_tls_note_starttls_offer = yes
smtpd_tls_loglevel = 1
smtpd_tls_received_header = yes
smtpd_tls_session_cache_timeout = 3600s
tls_random_source = dev:/dev/urandom
smtpd_tls_security_level = may
smtp_tls_security_level = may
smtpd_tls_exclude_ciphers = aNULL, eNULL, EXPORT, DES, RC4, MD5, PSK, aECDH, EDH-DSS-DES-CBC3-SHA, EDH-RSA-DES-CDC3-SHA, KRB5-DE5, CBC3-SHA
smtpd_tls_dh1024_param_file = /etc/postfix/ssl/dh4096.pem
unknown_local_recipient_reject_code = 450
maximal_queue_lifetime = 7d
minimal_backoff_time = 300s
maximal_backoff_time = 8000s
smtp_helo_timeout = 60s
smtpd_recipient_limit = 16
smtpd_soft_error_limit = 3
smtpd_hard_error_limit = 12
smtpd_helo_restrictions = permit_mynetworks, warn_if_reject reject_non_fqdn_hostname, reject_invalid_hostname, permit
smtpd_sender_restrictions = permit_mynetworks, reject_authenticated_sender_login_mismatch, permit_sasl_authenticated, warn_if_reject reject_non_fqdn_sender, reject_unknown_sender_domain, reject_unauth_pipelining, permit
smtpd_client_restrictions = reject_rbl_client sbl.spamhaus.org, reject_rbl_client blackholes.easynet.nl
smtpd_recipient_restrictions = reject_unauth_pipelining, permit_mynetworks, permit_sasl_authenticated, reject_non_fqdn_recipient, reject_unknown_recipient_domain, reject_unauth_destination, check_policy_service 
unix:private/policy-spf, check_policy_service inet:127.0.0.1:10023, permit
smtpd_data_restrictions = reject_unauth_pipelining
smtpd_relay_restrictions = reject_unauth_pipelining, permit_mynetworks, permit_sasl_authenticated, reject_non_fqdn_recipient, reject_unknown_recipient_domain, reject_unauth_destination, check_policy_service unix:private/policy-spf, check_policy_service inet:127.0.0.1:10023, permit
smtpd_helo_required = yes
smtpd_delay_reject = yes
disable_vrfy_command = yes
myhostname = rigel.oamis.net
mydestination =
myorigin = /etc/mailname
mydestination = $myhostname, rigel.oamis.net, rigel, localhost.localdomain, localhost
relayhost =
mynetworks = 127.0.0.0/8 [::ffff:127.0.0.0]/104 [::1]/128
mailbox_size_limit = 0
recipient_delimiter = +
inet_interfaces = all
inet_protocols = all
mynetworks_style = host
virtual_mailbox_base = /var/vmail
virtual_mailbox_maps = mysql:/etc/postfix/mysql_virtual_mailbox_maps.cf, mysql:/etc/postfix/mysql_virtual_mailbox_domainaliases_maps.cf
virtual_uid_maps = static:150
virtual_gid_maps = static:8
virtual_alias_maps = mysql:/etc/postfix/mysql_virtual_alias_maps.cf, mysql:/etc/postfix/mysql_virtual_alias_domainaliases_maps.cf
virtual_mailbox_domains = mysql:/etc/postfix/mysql_virtual_domains_maps.cf
smtpd_sender_login_maps = mysql:/etc/postfix/mysql_virtual_sender_login_maps.cf
virtual_transport = dovecot
dovecot_destination_recipient_limit = 1
content_filter = amavis:[127.0.0.1]:10024
policy-spf_time_limit = 3600s
header_checks = regexp:/etc/postfix/header_checks
enable_original_recipient = no
milter_default_action = accept
milter_protocol = 6
smtpd_milters = inet:localhost:8891,inet:localhost:8892
non_smtpd_milters = inet:localhost:8891,inet:localhost:8892
alias_maps = hash:/etc/aliases
# Increase message size limit to 200MB
message_size_limit = 209715200

master.cf

cat master.cf |  grep -v "#" | grep -v "^$"
smtp      inet  n       -       y       -       -       smtpd
submission inet n       -       y       -       -       smtpd
 -o syslog_name=postfix/submission
 -o smtpd_tls_security_level=encrypt
 -o smtpd_sasl_auth_enable=yes
 -o smtpd_reject_unlisted_recipient=yes
 -o smtpd_client_restrictions=permit_sasl_authenticated,reject_unauth_destination,reject
 -o smtpd_helo_restrictions = permit_mynetworks, warn_if_reject reject_non_fqdn_hostname, reject_invalid_hostname, permit
 -o smtpd_sender_restrictions = permit_mynetworks, reject_authenticated_sender_login_mismatch, permit_sasl_authenticated, warn_if_reject reject_non_fqdn_sender, reject_unknown_sender_domain, reject_unauth_pipelining, permit
 -o smtpd_recipient_restrictions=permit_sasl_authenticated,reject_unauth_destination,reject
 -o smtpd_relay_restrictions=permit_sasl_authenticated,reject
 -o milter_macro_daemon_name=ORIGINATING
pickup    unix  n       -       y       60      1       pickup
cleanup   unix  n       -       y       -       0       cleanup
qmgr      unix  n       -       n       300     1       qmgr
tlsmgr    unix  -       -       y       1000?   1       tlsmgr
rewrite   unix  -       -       y       -       -       trivial-rewrite
bounce    unix  -       -       y       -       0       bounce
defer     unix  -       -       y       -       0       bounce
trace     unix  -       -       y       -       0       bounce
verify    unix  -       -       y       -       1       verify
flush     unix  n       -       y       1000?   0       flush
proxymap  unix  -       -       n       -       -       proxymap
proxywrite unix -       -       n       -       1       proxymap
smtp      unix  -       -       y       -       -       smtp
relay     unix  -       -       y       -       -       smtp
showq     unix  n       -       y       -       -       showq
error     unix  -       -       y       -       -       error
retry     unix  -       -       y       -       -       error
discard   unix  -       -       y       -       -       discard
local     unix  -       n       n       -       -       local
virtual   unix  -       n       n       -       -       virtual
lmtp      unix  -       -       y       -       -       lmtp
anvil     unix  -       -       y       -       1       anvil
scache    unix  -       -       y       -       1       scache
maildrop  unix  -       n       n       -       -       pipe
 flags=DRhu user=vmail argv=/usr/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/lib/bsmtp/bsmtp -t$nexthop -f$sender $recipient
scalemail-backend unix  -       n       n       -       2       pipe
 flags=R user=scalemail argv=/usr/lib/scalemail/bin/scalemail-store ${nexthop} ${user} ${extension}
mailman   unix  -       n       n       -       -       pipe
 flags=FR user=list argv=/usr/lib/mailman/bin/postfix-to-mailman.py
 ${nexthop} ${user}
amavis      unix    -       -       y       -       3       smtp
 -o smtp_data_done_timeout=1200
 -o smtp_send_xforward_command=yes
 -o disable_dns_lookups=yes
 -o max_use=20
127.0.0.1:10025 inet    n       -       y       -       -       smtpd
 -o content_filter=
 -o local_recipient_maps=
 -o relay_recipient_maps=
 -o smtpd_restriction_classes=
 -o smtpd_delay_reject=no
 -o smtpd_client_restrictions=permit_mynetworks,reject
 -o smtpd_helo_restrictions=
 -o smtpd_sender_restrictions=
 -o smtpd_recipient_restrictions=permit_mynetworks,reject
 -o smtpd_data_restrictions=reject_unauth_pipelining
 -o smtpd_end_of_data_restrictions=
 -o mynetworks=127.0.0.0/8
 -o smtpd_error_sleep_time=0
 -o smtpd_soft_error_limit=1001
 -o smtpd_hard_error_limit=1000
 -o smtpd_client_connection_count_limit=0
 -o smtpd_client_connection_rate_limit=0
 -o receive_override_options=no_header_body_checks,no_unknown_recipient_checks,no_milters
dovecot      unix   -        n      n       -       -   pipe
 flags=DRhu user=vmail:mail argv=/usr/lib/dovecot/dovecot-lda -d $(recipient)
policy-spf  unix  -       n       n       -       -       spawn
    user=nobody argv=/usr/bin/policyd-spf

Restart all services

service postfix restart
service opendkim restart
service opendmarc restart
service spamassassin restart
service clamav-daemon restart
service amavis restart
service dovecot restart

References

Daniel Simao (talk) 18:45, 29 July 2018 (EDT)