Postfix Virtual domains and accounts postfixadmin and mysql
Contents
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 } mailbox NotAuthenticated { auto = subscribe } } 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 { } } service stats { client_limit = 10000 unix_listener stats-writer { user = vmail } unix_listener stats-reader { user = vmail } }
The service stats is needed after upgrade at version 2.3 to avoid error
Error: net_connect_unix(/var/run/dovecot/stats-writer) failed: Permission deni))
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 "NotAuthenticated"; } 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
- A Mailserver on Ubuntu 16.04 Postfix, Dovecot, MySQL
- Guide to Deploying Diffie-Hellman for TLS
- Using DKIM in my server for multiple domains (websites)
- HOW TO ELIMINATE SPAM AND PROTECT YOUR NAME WITH DMARC
- Virtual Users And Domains With Postfix, Courier, MySQL And SquirrelMail (Ubuntu 14.04LTS)
- Ubuntu 16.04 auth_pam broken
- How to set up a mail server on a GNU / Linux system
- PostfixCompleteVirtualMailSystemHowto
- Mail Filtering
- How to enable port 587 (submission) in postfix
- PostfixVirtualMailBoxClamSmtpHowto
- PostfixBasicSetupHowto
- Security & compliance
- Authenticate email with DKIM
- Mailserver with virtual users and domains using Postfix and Dovecot on a CentOS 6 VPS
- Temporarily disabling Postgrey
- Disable greylisting/modify whitelist?
- Postfix and TLS encryption
- Set Up OpenDMARC with Postfix on Ubuntu to Block Email Spoofing/Spam
- https://stackoverflow.com/questions/24256008/how-to-move-spam-to-spam-folder
- How can I purposely send a message that will be marked as spam in Gmail?
- Generic Test for Unsolicited Bulk Email
- EICAR Anti-Virus Test File
- invoke filtering manually in dovecot+sieve
- Upgrading Dovecot v2.2 to v2.3
Daniel Simao (talk) 18:45, 29 July 2018 (EDT)