Jump to content

Recommended Posts

[Updated as of 27 AUG 2018]

There are many email server install tutorials out there but none tell you how to configure the server for blocking all Spam and malicious users, while running a very secure, reliable and smooth server.. They also don't tell you how to get all aspects up and running like DCC, Pyzor, postgrey, etc.. Just running apt or yum to install these programs is not installing or configuring a mail server.

The following is a culmination of 20 years of installing and configuring these servers to the point that they are 100% spam free and 100% secure from spammers and hackers. If you are not using forced TLS or your mail carrier doesn't support it than find a new carrier. 100% SSL encryption is standard and necessary in todays internet climate. This will take anywhere from 2 to 4 hours to complete, test, and put into production. Depending on your skill at the command line will determine the total time.

Don't have the time and want a secure and smoothly running mail server? Need an evaluation for a current server? Hire Us

Using Ubuntu 18.04 but should work on any Debian based OS, You can use this to reconfigure currently installed mail servers as well that aren't doing the job.

Ruining latest

Bind9 for mail server DNS,

Nginx 1.15.2,  Percona MySQL 5.7, and PHP 7.2 (For Postifx Admin)


We will be installing and configuring the following:

  • Postfix
  • Postfix Admin with allowed nets security
  • Dovecot
  • Dovecot-sieve
  • Postgrey
  • Amavis
  • Spamassassin
  • Bayes Database
  • Pyzor
  • Razor2
  • DCC
  • OpenDKIM
  • Letsencrypt SSL
  • Configure UFW Firewall
  • Custom Sieve Scripts
  • Configure DNS for SPF | DMARC | DKIM
  • Configure DNS Reverse for Mail Server
  • Configure CRON jobs for bayes and rcDCC


SSL First

Security is first and foremost; generate your SSL with letsencrypt via certbot  or have your premium cert ready. We are using the sub domain mail.domain.com for this example. Make sure to adjust all files and configurations for your SSL paths as well as domain name, directory/file paths, IP address and users where applicable.

Generate a strong 2048bit  4096bit dhparam.pem ( We will link to it later in Postfix / Dovecot, and Nginx ) [Updated 12/13/18]

# openssl dhparam 4096 -out /etc/ssl/certs/dhparam.pem


Step one: Install

Change to root user

# sudo su

Run an update

# apt update && apt upgrade

Install the required applications and dependencies. I am assuming php 7.2 [recommended] (Update the application list accordingly if other version)

# apt install postfix postfix-mysql getmail4 dovecot-antispam rkhunter binutils dovecot-imapd dovecot-pop3d dovecot-mysql dovecot-sieve sudo postgrey pyzor razor amavisd-new spamassassin clamav clamav-daemon unzip bzip2 arj nomarch lzop cabextract libnet-ldap-perl libauthen-sasl-perl clamav-docs daemon libio-string-perl libio-socket-ssl-perl libnet-ident-perl zip libnet-dns-perl php7.2-fpm php7.2-mysql php7.2-curl php7.2-gd php7.2-intl php-pear php-imagick php7.2-imap php7.2-mbstring php-memcache php-sqlite3 php-apcu php7.2-tidy php7.2-xmlrpc php7.2-xml dovecot-managesieved postfix-ldap postfix-pcre sasl2-bin arj p7zip-full ripole rpm2cpio tnef unrar-free libmysqlclient-dev opendkim opendkim-tools rblcheck postfix-policyd-spf-python

During install you will prompted by Postfix to choose your setup

Internet Site

And hostname

mail.domain.com (Obviously your domain and your mail servers hostname)

If you see this during install don't worry, we never set a home path so its a postgrey error and we will deal with it later.


The most important part of this entire install is proper permissions for functionality as well as security, 99% of mail server issues are improper permissions,.


Make sure php imap is enabled (Issues on random systems, most are good but just to make sure)

# phpenmod imap


Now we must install our PERL modules

# cpan -i DBI
# cpan -i DBD::mysql
# cpan -i Geo::IP
# cpan -i Net::CIDR::Lite
# cpan -i Encode::Detect::Detector
# cpan -i Net::Patricia
# cpan -fi Mail::SpamAssassin::Bayes::CombineChi
# cpan -i Mail::SpamAssassin::Plugin::SPF
# cpan -i Mail::SpamAssassin::Plugin::Shortcircuit
# cpan -i Mail::SpamAssassin::CompiledRegexps::body_0
# cpan -i Mail::DKIM::Verifier
# cpan -i Mail::DKIM

When installing Mail::SpamAssassin::Bayes::CombineChi you will see hundreds of these. Its a deprecation warning and must be fixed by the maintainer. It will install properly and we will fix that error later in this tutorial,

Unescaped left brace in regex is deprecated here (and will be fatal in Perl 5.30), passed through in regex; marked by <-- HERE in m/^(.{ <-- HERE ,200}).*$/ at ../blib/lib/Mail/SpamAssassin/PerMsgStatus.pm line 921


Everything installed for now lets jump in...

  • Upvote 1

Share this post

Link to post
Share on other sites

Step two: Postfix Configure

For easy install I am including the complete configuration files for copy and paste into console. You must edit certain portions of each file to meet your needs. I will discuss all edits in detail.

Relevant files

# nano /etc/postfix/main.cf


# See /usr/share/postfix/main.cf.dist for a commented, more complete version

# The first text sent to a connecting process.
smtpd_banner = $myhostname ESMTP $mail_name
biff = no
# appending .domain is the MUA's job.
append_dot_mydomain = no
readme_directory = /usr/share/doc/postfix

# ---------------------------------
# SASL parameters
# ---------------------------------
# Use Dovecot to authenticate.
smtpd_sasl_type = dovecot
# Referring to /var/spool/postfix/private/auth
smtpd_sasl_path = private/auth
smtpd_sasl_auth_enable = yes
broken_sasl_auth_clients = yes
smtpd_sasl_security_options = noanonymous
smtpd_sasl_tls_security_options = $smtpd_sasl_security_options
smtpd_sasl_local_domain = $myhostname
smtpd_sasl_authenticated_header = yes
# TLS parameters
# ---------------------------------
# The default snakeoil certificate. Comment if using a purchased 
# SSL certificate.
#smtpd_tls_cert_file = /etc/ssl/certs/postfix.pem
#smtpd_tls_key_file = /etc/ssl/private/postfix.pem
# Uncomment if using a premium/purchased SSL certificate.

# The snakeoil self-signed certificate has no need for a CA file. But
# if you are using your own SSL certificate, then you probably have
# a CA certificate bundle from your provider. The path to that goes here.

# trusted CA path, where your server has the trust store of commercial certs
smtp_tls_CApath = /etc/ssl/certs
smtpd_tls_CApath = /etc/ssl/certs

smtpd_use_tls = yes
smtp_use_tls = yes

smtpd_tls_session_cache_database = btree:${data_directory}/smtpd_scache
smtp_tls_session_cache_database = btree:${data_directory}/smtp_scache

#enable ECDH
smtpd_tls_eecdh_grade = strong
#enabled SSL protocols, don't allow SSLv2 and SSLv3
smtpd_tls_protocols= !SSLv2, !SSLv3
smtpd_tls_mandatory_protocols= !SSLv2, !SSLv3
#allowed ciphers for smtpd_tls_security_level=encrypt
smtpd_tls_mandatory_ciphers = high
#allowed ciphers for smtpd_tls_security_level=may
smtpd_tls_ciphers = high
#enforce the server cipher preference
tls_preempt_cipherlist = yes
#disable following ciphers for smtpd_tls_security_level=encrypt
smtpd_tls_mandatory_exclude_ciphers = aNULL, MD5 , DES, ADH, RC4, PSD, SRP, 3DES, eNULL
#disable following ciphers for smtpd_tls_security_level=may
#enable TLS logging to see the ciphers for inbound connections
smtpd_tls_loglevel = 1
#enable TLS logging to see the ciphers for outbound connections
smtp_tls_loglevel = 1
smtp_tls_note_starttls_offer = yes
smtpd_tls_received_header = yes
smtpd_tls_session_cache_timeout = 3600s
tls_random_source = dev:/dev/urandom
smtpd_tls_dh1024_param_file = /etc/ssl/certs/dhparam.pem
#smtpd_tls_session_cache_database = btree:${data_directory}/smtpd_scache
#smtp_tls_session_cache_database = btree:${data_directory}/smtp_scache
# Note that forcing use of TLS is going to cause breakage - most mail servers
# don't offer it and so delivery will fail, both incoming and outgoing. This is
# unfortunate given what various governmental agencies are up to these days.

# For MTAs that reject based on encrypt TLS setting, lets do 'may' to get the mail delivered
smtp_tls_policy_maps = hash:/etc/postfix/tls_policy

# AUTH only must be enabled when using smtpd encrypt
smtpd_tls_auth_only = yes
# Enable and force all incoming smtpd connections to use TLS.
smtpd_tls_security_level = encrypt
# Enable and force all outgoing smtp connections to use TLS.
smtp_tls_security_level = encrypt
# Enable (but don't force all incoming smtpd connections to use TLS.
#smtpd_tls_security_level = encrypt
# Enable (but don't force) all outgoing smtp connections to use TLS.
#smtp_tls_security_level = encrypt
# See /usr/share/doc/postfix/TLS_README.gz in the postfix-doc package for
# information on enabling SSL in the smtp client.
# SMTPD parameters
# ---------------------------------
# Uncomment the next line to generate "delayed mail" warnings
#delay_warning_time = 4h
# will it be a permanent error or temporary
unknown_local_recipient_reject_code = 450
# how long to keep message on queue before return as failed.
# some have 3 days, I have 16 days as I am backup server for some people
# whom go on holiday with their server switched off.
maximal_queue_lifetime = 7d
# max and min time in seconds between retries if connection failed
minimal_backoff_time = 1000s
maximal_backoff_time = 8000s
# how long to wait when servers connect before receiving rest of data
smtp_helo_timeout = 60s
# how many address can be used in one message.
# effective stopper to mass spammers, accidental copy in whole address list
# but may restrict intentional mail shots.
smtpd_recipient_limit = 16
# how many error before back off.
smtpd_soft_error_limit = 3
# how many max errors before blocking it.
smtpd_hard_error_limit = 12

# --------------------------------------   
milter_default_action = accept
milter_protocol = 6
smtpd_milters = unix:/var/run/opendkim/opendkim.sock
# unix:/var/run/opendkim/opendkim.sock
# inet:
non_smtpd_milters = $smtpd_milters

# Extending with selective greylisting
# Selective greylisting means that not every delivery attempt 
# will be checked by greylisting but only those which look “suspicous� 
# (servers without names, dial-up addresses, web servers etc.).
smtpd_restriction_classes = check_greylist
check_greylist = check_policy_service inet:

# This next set are important for determining who can send mail and relay mail
# to other servers. It is very important to get this right - accidentally producing
# an open relay that allows unauthenticated sending of mail is a Very Bad Thing.
# You are encouraged to read up on what exactly each of these options accomplish.
# rules restrictions
smtpd_relay_restrictions = permit_mynetworks, 

smtpd_helo_restrictions = permit_mynetworks,
                                check_helo_access hash:/etc/postfix/helo_access,

smtpd_recipient_restrictions =  permit_mynetworks,
                                check_client_access regexp:/etc/postfix/check_client_greylist,
                                check_sender_access hash:/etc/postfix/sender_access,
                                check_client_access hash:/etc/postfix/client_checks,
                                check_sender_access hash:/etc/postfix/sender_checks,
                                reject_rbl_client bl.spamcop.net,
                                reject_rbl_client zen.spamhaus.org,
                                reject_rbl_client b.barracudacentral.org,
                                reject_rbl_client cbl.abuseat.org,

smtpd_client_restrictions = permit_mynetworks,

smtpd_sender_restrictions =  permit_mynetworks,

smtpd_data_restrictions = reject_unauth_pipelining,
default_process_limit = 400
smtpd_helo_required = yes
disable_vrfy_command = yes
# General host and delivery info
# ----------------------------------
myhostname = mail.domain.com
mydomain = domain.com
myorigin = $mydomain
mydestination =$mydomain,, $myhostname
mynetworks = 111.222.333.0/24 [::ffff:]/104 [::1]/128
mailbox_size_limit = 0
recipient_delimiter = +
inet_interfaces = all
inet_protocols = all
relayhost =
#mynetworks_style = host
# This specifies where the virtual mailbox folders will be located.
virtual_mailbox_base = /var/vmail
# This is for the mailbox location for each user. The domainaliases
# map allows us to make use of Postfix Admin's domain alias feature.
virtual_mailbox_maps = mysql:/etc/postfix/mysql_virtual_mailbox_maps.cf, mysql:/etc/postfix/mysql_virtual_mailbox_domainaliases_maps.cf
# and their user id
virtual_uid_maps = static:150
# and group id
virtual_gid_maps = static:8
# This is for aliases. The domainaliases map allows us to make 
# use of Postfix Admin's domain alias feature.
virtual_alias_maps = mysql:/etc/postfix/mysql_virtual_alias_maps.cf, mysql:/etc/postfix/mysql_virtual_alias_domainaliases_maps.cf
# This is for domain lookups.
virtual_mailbox_domains = mysql:/etc/postfix/mysql_virtual_domains_maps.cf

# Currently disabled
#transport_maps = hash:/etc/postfix/transports
# Integration with other packages
# ---------------------------------------
# Tell postfix to hand off mail to the definition for dovecot in master.cf
virtual_transport = spamass-dovecot
spamass-dovecot_destination_recipient_limit = 1
#dovecot_destination_recipient_limit = 1
#dspam_destination_recipient_limit = 1
# Use amavis for virus and spam scanning
content_filter = amavis:[]:10024
#content_filter = smtp-amavis:[]:10024
# Header manipulation
# --------------------------------------
# Getting rid of unwanted headers. See: https://posluns.com/guides/header-removal/
header_checks = regexp:/etc/postfix/header_checks
mime_header_checks = regexp:/etc/postfix/mime_header_checks
# getting rid of x-original-to
enable_original_recipient = no
alias_maps = hash:/etc/aliases
alias_database = hash:/etc/aliases
html_directory = /usr/share/doc/postfix/html
message_size_limit = 104857600

compatibility_level = 2

Edit the following to match your domain, we will generate our certs later with certbot for letsencrypt. If you have a private SSL cert and key than use them and set the proper path. The certs must be generated for the mail server domain.

# Uncomment if using a premium/purchased SSL certificate.

# The snakeoil self-signed certificate has no need for a CA file. But
# if you are using your own SSL certificate, then you probably have
# a CA certificate bundle from your provider. The path to that goes here.

Edit the following to match your server main IP

mynetworks = 111.222.333.0/24 [::ffff:]/104 [::1]/128

Save the file


# nano /etc/postfix/master.cf



# Postfix master process configuration file.  For details on the format
# of the file, see the master(5) manual page (command: "man 5 master").
# Do not forget to execute "postfix reload" after editing this file.
# ==========================================================================
# service type  private unpriv  chroot  wakeup  maxproc command + args
#               (yes)   (yes)   (yes)   (never) (100)
# ==========================================================================

# SMTP on port 25, unencrypted.
smtp      inet  n       -       -       -       -       smtpd
#smtp      inet  n       -       -       -       1       postscreen
#smtpd     pass  -       -       -       -       -       smtpd
#dnsblog   unix  -       -       -       -       0       dnsblog
#tlsproxy  unix  -       -       -       -       0       tlsproxy

# SMTP with TLS on port 587. Currently commented.
submission inet n       -       -       -       -       smtpd
  -o syslog_name=postfix/submission
  -o smtpd_tls_security_level=encrypt
  -o smtpd_sasl_auth_enable=yes
  -o smtpd_tls_auth_only=yes
  -o smtpd_reject_unlisted_recipient=no
  -o smtpd_client_restrictions=permit_sasl_authenticated,reject_unauth_destination,reject
#  -o smtpd_helo_restrictions=$mua_helo_restrictions
#  -o smtpd_sender_restrictions=$mua_sender_restrictions
  -o smtpd_recipient_restrictions=
  -o smtpd_relay_restrictions=permit_sasl_authenticated,reject
  -o milter_macro_daemon_name=ORIGINATING
  -o smtpd_sasl_tls_security_options=noanonymous

# SMTP over SSL on port 465.
smtps     inet  n       -       -       -       -       smtpd
  -o syslog_name=postfix/smtps
  -o smtpd_tls_wrappermode=yes
  -o smtpd_sasl_auth_enable=yes
  -o smtpd_tls_auth_only=yes
  -o smtpd_reject_unlisted_recipient=no
  -o smtpd_client_restrictions=permit_sasl_authenticated,reject_unauth_destination,reject
#  -o smtpd_helo_restrictions=$mua_helo_restrictions
#  -o smtpd_sender_restrictions=$mua_sender_restrictions
#  -o smtpd_recipient_restrictions=
  -o smtpd_relay_restrictions=permit_sasl_authenticated,reject
  -o milter_macro_daemon_name=ORIGINATING
  -o smtpd_sasl_security_options=noanonymous,noplaintext
  -o smtpd_sasl_tls_security_options=noanonymous

#628       inet  n       -       -       -       -       qmqpd
pickup    fifo  n       -       -       60      1       pickup
  -o content_filter=
  -o receive_override_options=no_header_body_checks
cleanup   unix  n       -       -       -       0       cleanup
qmgr      fifo  n       -       n       300     1       qmgr
#qmgr     fifo  n       -       n       300     1       oqmgr
tlsmgr    unix  -       -       -       1000?   1       tlsmgr
rewrite   unix  -       -       -       -       -       trivial-rewrite
bounce    unix  -       -       -       -       0       bounce
defer     unix  -       -       -       -       0       bounce
trace     unix  -       -       -       -       0       bounce
verify    unix  -       -       -       -       1       verify
flush     unix  n       -       -       1000?   0       flush
proxymap  unix  -       -       n       -       -       proxymap
proxywrite unix -       -       n       -       1       proxymap
smtp      unix  -       -       -       -       -       smtp
relay     unix  -       -       -       -       -       smtp
        -o syslog_name=postfix/$service_name
#       -o smtp_helo_timeout=5 -o smtp_connect_timeout=5
showq     unix  n       -       -       -       -       showq
error     unix  -       -       -       -       -       error
retry     unix  -       -       -       -       -       error
discard   unix  -       -       -       -       -       discard
local     unix  -       n       n       -       -       local
virtual   unix  -       n       n       -       -       virtual
lmtp      unix  -       -       -       -       -       lmtp
anvil     unix  -       -       -       -       1       anvil
scache    unix  -       -       -       -       1       scache
# ====================================================================
# Interfaces to non-Postfix software. Be sure to examine the manual
# pages of the non-Postfix software to find out what options it wants.
# Many of the following services use the Postfix pipe(8) delivery
# agent.  See the pipe(8) man page for information about ${recipient}
# and other message envelope options.
# ====================================================================
# maildrop. See the Postfix MAILDROP_README file for details.
# Also specify in main.cf: maildrop_destination_recipient_limit=1
maildrop  unix  -       n       n       -       -       pipe
  flags=DRhu user=vmail argv=/usr/bin/maildrop -d ${recipient}
# ====================================================================
# Recent Cyrus versions can use the existing "lmtp" master.cf entry.
# Specify in cyrus.conf:
#   lmtp    cmd="lmtpd -a" listen="localhost:lmtp" proto=tcp4
# Specify in main.cf one or more of the following:
#  mailbox_transport = lmtp:inet:localhost
#  virtual_transport = lmtp:inet:localhost
# ====================================================================
# Cyrus 2.1.5 (Amos Gouaux)
# Also specify in main.cf: cyrus_destination_recipient_limit=1
#cyrus     unix  -       n       n       -       -       pipe
#  user=cyrus argv=/cyrus/bin/deliver -e -r ${sender} -m ${extension} ${user}
# ====================================================================
# ====================================================================
# Old example of delivery via Cyrus.
#old-cyrus unix  -       n       n       -       -       pipe
#  flags=R user=cyrus argv=/cyrus/bin/deliver -e -m ${extension} ${user}
# ====================================================================
# See the Postfix UUCP_README file for configuration details.
uucp      unix  -       n       n       -       -       pipe
  flags=Fqhu user=uucp argv=uux -r -n -z -a$sender - $nexthop!rmail ($recipient)
# Other external delivery methods.
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}
# The next two entries integrate with Amavis for anti-virus/spam checks.
amavis      unix    -       -       -       -       3       smtp
  -o smtp_data_done_timeout=1200
  -o smtp_send_xforward_command=yes
  -o disable_dns_lookups=yes
  -o smtp_tls_security_level=none
  -o max_use=20 inet    n       -       -       -       -       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=
  -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 smtpd_tls_security_level=none
  -o receive_override_options=no_header_body_checks,no_unknown_recipient_checks,no_milters

# Integration with Dovecot - hand mail over to it for local delivery, and
# run the process under the vmail user and mail group.
#dovecot      unix   -        n      n       -       -   pipe
#  flags=DRhu user=vmail:mail argv=/usr/lib/dovecot/dovecot-lda -d $(recipient)
#dspam     unix  -       n       n       -       32      pipe
#  flags=Ru user=vmail:mail argv=/usr/bin/dspam --client --deliver=innocent,spam --user ${recipient} --mail-from=${sender}
#spamassassin unix -     n       n       -       -       pipe   
#  user=spamd argv=/usr/bin/spamc -f -e  /usr/sbin/sendmail -oi -f ${sender} ${recipient}

# Transport: Postfix -> Spamassassin -> Dovecot
spamass-dovecot unix -     n       n       -       -       pipe
  flags=DRhu user=vmail:mail argv=/usr/bin/spamc -u debian-spamd -e /usr/lib/dovecot/deliver -d ${recipient}

No edits necessary unless you have custom tweaks.

More postfix next post...

Share this post

Link to post
Share on other sites

Now that we have our main configuration done we need to create all the access filters and database files that our main.cf needs to function.

First lets create the user and mail directories

# 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


Access Filters

# nano /etc/postfix/check_client_greylist

Insert the following

# regex to check clients which seem to be dynamic
# only those will be greylisted
# regex type, no postmap needed

/^unknown$/                                   check_greylist
/([0-9]{1,3}[.-]){3,4}[^0-9.]+/               check_greylist
/^(dhcp|dialup|ppp|adsl|host|static|www|server|client)[^.]*[0-9]/     check_greylist
/^[^.]*[0-9]{5}/                              check_greylist

Save file


# nano /etc/postfix/client_checks

Insert the following - You can add IPs and hosts you want to allow or block here. I already added a few well known spamming IPs and hosts to start you off.

### client restrictions
### check_client_access regexp:/etc/postfix/client_restrictions

### WHITE LIST mostly trusted host names ###
/\.google\.com$/       OK
/\.paypal\.com$/        OK

### Generic Block of DHCP machines or those with many numbers in the hostname ###
/^(dhcp|dialup|ppp|adsl|pool)[^.]*[0-9]/  550 S25R6 check

### BLACK LIST known spammer friendly ISPs ###
/\.(internetdsl|adsl|sdi)\.tpnet\.pl$/  550 domain check tpnet
/^user.+\.mindspring\.com$/             550 domain check mind
/[0-9a-f]{4}\.[a-z]+\.pppool\.de$/      550 domain check pppool
/\.dip\.t-dialin\.net$/                 550 domain check t-dialin
/\.(adsl|cable)\.wanadoo\.nl$/          550 domain check wanadoo

# Restricts which clients this system accepts SMTP connections from.
# example.com               REJECT No spammers
# .example.com              REJECT No spammers, from your subdomain
# 123.456.789.123           REJECT Your IP is spammer
# 123.456.789.0/24          REJECT Your IP range is documented spammer
# 321.987.654.321           OK
# example1.com              OK              REJECT No spammers               REJECT No spammers
.iusacell.net              REJECT No spammers             REJECT Your IP range is documented spammer
planet-telecom.eu          REJECT Your IP range is documented spammer
umich.edu                  REJECT Your IP range is spammer            REJECT Your IP range is documented spammer             REJECT Your IP range is documented spammer
dataclub.biz               REJECT You cant use our mailserver for your spam              REJECT Your IP range is documented spammer             REJECT Your IP range is documented spammer              REJECT Your IP range is documented spammer
legacymerchant.com         REJECT You cant use our mailserver for your spam              REJECT Your IP range is documented spammer
worldwebvideos.com         REJECT You cant use our mailserver for your spam              REJECT Your IP range is documented spammer               REJECT Your IP range is documented spammer

Save the file and make it a database file for postfix by running

# postmap /etc/postfix/client_checks


# nano /etc/postfix/header_checks

Insert the following

#### Header checks file
#### header_checks = regexp:/etc/postfix/header_checks
#### Checks are done in order, top to bottom.

#### Remove the following from the header to protect internal lans
#/^Received:.*.internal.lan/ IGNORE

#### non-RFC Compliance headers
/[^[:print:]]{7}/  REJECT 2047rfc
/^.*=20[a-z]*=20[a-z]*=20[a-z]*=20[a-z]*/ REJECT 822rfc1
/(.*)?\{6,\}/ REJECT 822rfc2
/(.*)[X|x]\{3,\}/ REJECT 822rfc3

#### Unreadable Language Types? -- NON-acsii un-printable
/^Subject:.*=\?(GB2312|big5|euc-kr|ks_c_5601-1987|koi8)\?/ REJECT NotReadable1
/^Content-Type:.*charset="?(GB2312|big5|euc-kr|ks_c_5601-1987|koi8)/ REJECT NotReadable2

#### Hidden Word Subject checks
/^Subject:.*      / REJECT TooManySpaces
/^Subject:.*r[ _\.\*\-]+o[ _\.\*\-]+l[ _\.\*\-]+e[ _\.\*\-]+x/ REJECT NoHiddenWords1
/^Subject:.*p[ _\.\*\-]+o[ _\.\*\-]+r[ _\.\*\-]+n/ REJECT NoHiddenWords2

#### Do not accept these types of attachments
/^Content-(Type|Disposition):.*(file)?name=.*\.(bat|com|exe)/ REJECT Bad Attachment .${3}

/^Received:/                 IGNORE
/^User-Agent:/               IGNORE
/^X-Mailer:/                 IGNORE
/^X-Originating-IP:/         IGNORE
/^x-cr-[a-z]*:/              IGNORE
/^Thread-Index:/             IGNORE
/^(X-DSPAM-.*)/              IGNORE

Save file


# nano /etc/postfix/helo_access

Insert the following altering domain.com and IP to match your domain/server IP

### helo access
### check_helo_access hash:/etc/postfix/helo_access

localhost             REJECT 554 Get lost asshole             REJECT 554 Get lost asshole
domain.com           REJECT 554 Get lost asshole
111.222.333.444       REJECT 554 Get lost asshole

Save the file and make it a database file for postfix by running

# postmap /etc/postfix/helo_access


# nano /etc/postfix/mime_header_checks

Insert the following

/name=[^>]*\.(bat|com|exe|dll|vbs)/ REJECT

Save file


# nano /etc/postfix/sender_access

Insert the following adjusting domain.com to your domain. Added some common creeps in there for you to get started

domain.com  OK
musclegainx.com  REJECT
telecomitalia.it  REJECT  REJECT  REJECT
secureserver.net   REJECT
stuckinyemen.com  REJECT   REJECT   REJECT   REJECT
legacymerchant.com    REJECT    REJECT
mailgeek.org    REJECT

Save the file and make it a database file for postfix by running

# postmap /etc/postfix/sender_access


# nano /etc/postfix/sender_checks

Insert the following note the examples

# Restricts sender addresses this system accepts in MAIL FROM commands.

# example.com              REJECT env. from addr any@example.com rejected
# .example.com             REJECT env. from addr any@sub.example.com rejected
# user@example.com         REJECT We don't want your email
# example2.com             OK
.iusacell.net                REJECT We don't want your email
Leopoldo050@cutorrent.com    REJECT You cant use our mailserver for your spam
daycareworks.com             REJECT You cant use our mailserver for your spam
dataclub.biz                 REJECT You cant use our mailserver for your spam
paypal.com                   OK

Save the file and make it a database file for postfix by running

# postmap /etc/postfix/sender_checks


# nano /etc/postfix/tls_policy

Insert the following - This file lists mail servers that do not use forced encryption and really you shouldn't allow them because they don't care about client security. So if using forced encryption [encrypt] this list will fall back to [may] if they are not using forced encryption. You can add or take away hosts.

live.co.uk             may
internode.on.net       may
extmail.bigpond.com    may
exemail.com.au         may
live.com               may
charter.net            may
mx.west.cox.net        may
mxin.mygrande.net      may
bigpond.com            may
grandecom.net          may
cox.net                may

Save the file and make it a database file for postfix by running

# postmap /etc/postfix/tls_policy


Now for the database and administration...

Share this post

Link to post
Share on other sites

Step three: Postfix Admin

Now time to setup our database to connect with Postfix Admin, You will need to create a database and user via mysql cli or phpmysqladmin. Make sure the user has all privileges on the database. Use a UNIX password please! DO NOT use a weak or easy pass or you will be sorry. Why bother doing all this configuring if you are going to use a weak pass and eliminate all the security we are adding? Be smart and generate a 20 character pass from here





DB: mailserver

User: mymail7395

Pass: y+jUcoy050u&A-lStiSe


Best practice is to have your mail admin in a shared directory on the server IE /usr/shared. Avoid installing on you domain root please! I will show how to connect via SSL and .htaccess with Nginx.

Lets grab the files, check the Github project site for the latest version and append your wget URL accordingly

# cd /usr/share
# wget -O postfixadmin.tgz https://github.com/postfixadmin/postfixadmin/archive/postfixadmin-3.2.tar.gz
# tar -zxvf postfixadmin.tgz

--- We want to change the name of the directory. You can name it what you want ---

# mv postfixadmin-postfixadmin-3.2 mymailadmin


We are installed, now we need to configure and connect to the database. This can only be done via your browser so time to setup the server block in Nginx or virtual host in Apache

For NGINX users it is best to create a new user that is the only one that has access to the mail admin. Name it whatever you want, I will use appalosa45  (don't ask).

Create user, directories, vhosts, and FPM conf

# adduser appalosa45
# mkdir /home/appalosa45/logs
# mkdir /home/appalosa45/_sessions
# mkdir /home/appalosa45/backup
# chown appalosa45:appalosa45 /home/appalosa45/logs
# chown appalosa45:appalosa45 /home/appalosa45/_sessions
# nano etc/nginx/sites-available/appalosa45.vhost
--- Paste in file below user.vhost then save---

# ln -s /etc/nginx/sites-available/appalosa45.vhost /etc/nginx/sites-enabled/

# nano /etc/php/7.2/fpm/pool.d/appalosa45.conf
--- Paste in file below user.conf then save---

# /etc/init.d/nginx restart
# /etc/init.d/php7.2-fpm restart

user.vhsot - Edit to your settings, IE domain.com / mail.domain.com , log paths, IP access (optional, comment out if not using), etc.

server {
        listen 8100 ssl;

        server_name mail.domain.com;

########## SSL Directives
        ssl_certificate  /etc/letsencrypt/live/domain.com/fullchain.pem;
        ssl_certificate_key  /etc/letsencrypt/live/domain.com/privkey.pem;
        ssl_trusted_certificate /etc/letsencrypt/live/domain.com/chain.pem;
        ssl_stapling on;
        ssl_stapling_verify on;
        resolver valid=300s;
        resolver_timeout 5s;
        ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
        ssl_dhparam /etc/ssl/certs/dhparam.pem;
        ssl_prefer_server_ciphers On;
        add_header Strict-Transport-Security "max-age=63072000; includeSubdomains; preload";
        ssl_session_cache shared:SSL:50m;
        ssl_session_timeout 5m;

        root   /usr/share/mymailadmin/public;
        allow 11.222.333.44;
        deny all;

        client_max_body_size 15M;

        error_log /home/appalosa45/logs/mymailadmin_error.log;
        access_log /home/appalosa45/logs/mymailadmin_access.log combined;

        # serve static files directly
        location ~* ^.+.(jpg|jpeg|gif|css|png|js|ico|html|xml|txt)$ {
               access_log        off;

############################### Auth Basic for Mail Admin Area with IP Protection ###################################

                                 location / {
                 allow 11.222.333.44;
                 deny all;
                                                auth_basic                                        "Admin Restricted Area";
                                                auth_basic_user_file  /etc/nginx/domain.com/.htpasswd;

                                 location ~ ^/.*\.php$ {
                 allow 11.222.333.44;
                 deny all;
                                                auth_basic                                        "Admin Restricted Area";
                                                auth_basic_user_file  /etc/nginx/domain.com/.htpasswd;
                                                fastcgi_pass   unix:/var/run/appalosa45_fpm.sock;
                                                fastcgi_index index.php;
                                                fastcgi_param SCRIPT_FILENAME  $document_root$fastcgi_script_name;
                                                include   fastcgi_params;

        location ~ /\. {
               deny  all;


Create the .htpasswd file

# mkdir /etc/nginx/domain.com

# nano /etc/nginx/domain.com/.htpasswd

Create your hash the easy way

Go to https://my.norton.com/extspa/idsafe?path=pwd-gen#password_generator

create pass and copy it

Go to http://www.htaccesstools.com/htpasswd-generator/

copy pass into field and add username you created, generate, should look like this



Paste that into .htpasswd and save


Now setup Postfix Admin [More Info https://raw.githubusercontent.com/postfixadmin/postfixadmin/master/INSTALL.TXT]

Make sure you can connect securely.


Lets connect to database

# nano /usr/share/mymailadmin/config.inc.php

Set your database values and enable


$CONF['database_type'] = 'mysqli';
$CONF['database_user'] = 'postfix';
$CONF['database_password'] = 'postfixadmin';
$CONF['database_name'] = 'postfix';

$CONF['configured'] = true;

Create template cache directory and set permissions

# mkdir /usr/share/mymailadmin/templates_c
# chmod -R 0777 /usr/share/mymailadmin/templates_c

--- Create Custom Includes ---
# touch /usr/share/mymailadmin/config.local.php

Setup login


This will display any errors, should be 0 if you followed the guide to the tee.


Set up a setup pass hash, copy into the config.inc.php will look like this


$CONF['setup_password'] = 'bb3a3e9eca28d1df79116dbb8967a416:b4c8a5fd58a5a294a72fe97e46fe1d645eb012e8';

Finish the setup, use yourdomain.com as user and a pass. LOGIN and add your virtual domain(s) and email accounts.


Share this post

Link to post
Share on other sites

Now lets connect Postfix to the database. Use the database name, user, and pass we created for postfix admin.

# cd /etc/postfix
# nano mysql_virtual_alias_domainaliases_maps.cf
user = postfixuser
password = databasepassword
hosts =
dbname = mailservename
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
# nano mysql_virtual_alias_maps.cf
user = postfixuser
password = databasepassword
hosts =
dbname = mailservename
table = alias
select_field = goto
where_field = address
additional_conditions = and active = '1'
# nano mysql_virtual_domains_maps.cf
user = postfixuser
password = databasepassword
hosts =
dbname = mailservename
table = domain
select_field = domain
where_field = domain
additional_conditions = and backupmx = '0' and active = '1'
# nano mysql_virtual_mailbox_domainaliases_maps.cf
user = postfixuser
password = databasepassword
hosts =
dbname = mailservename
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
# nano mysql_virtual_mailbox_maps.cf
user = postfixuser
password = databasepassword
hosts =
dbname = mailservename
table = mailbox
select_field = CONCAT(domain, '/', local_part)
where_field = username
additional_conditions = and active = '1'


If enabled, open ports in ufw or your preferred firewall

# sudo ufw allow to any port 465
# sudo ufw allow to any port 110
# sudo ufw allow to any port 25
# sudo ufw allow to any port 143
# sudo ufw allow to any port 993
# sudo ufw allow to any port 995


Now restart Postifx

# /etc/init.d/postfix restart

And the result should be flawless; 0 errors


Check logs for errors /var/log/mail.log && /var/log/mail.err

Thats it for now with Postfix get some coffee we now move to Dovecot...

Share this post

Link to post
Share on other sites

Step four: Dovecot Configure

Dovecot is a bit easier than Postfix, we just have to change a few config files to get it up and running properly.

If config is not in the list, leave as is!
# cd /etc/dovecot/conf.d
# nano 10-auth.conf

Uncomemnt -Enable
disable_plaintext_auth = yes

Adjust - Add login
auth_mechanisms = plain login

Switch - Comment all others
!include auth-sql.conf.ext
# nano 10-logging.conf

log_path = syslog
auth_verbose = yes
auth_verbose_passwords = plain
auth_debug = yes
auth_debug_passwords = yes
mail_debug = yes
verbose_ssl = yes
log_timestamp = "%b %d %H:%M:%S "
login_log_format_elements = user=<%u> method=%m rip=%r lip=%l mpid=%e %c %k
# nano 10-mail.conf

mail_location = maildir:/var/vmail/%d/%n

type = private

mail_uid = vmail
mail_gid = mail

#mail_privileged_group = mail

first_valid_uid = 150
last_valid_uid = 150

mail_plugin_dir = /usr/lib/dovecot/modules


# nano 10-master.conf

Copy and paste

#default_process_limit = 100
#default_client_limit = 1000

# Default VSZ (virtual memory size) limit for service processes. This is mainly
# intended to catch and kill processes that leak memory before they eat up
# everything.
#default_vsz_limit = 256M

# Login user is internally used by login processes. This is the most untrusted
# user in Dovecot system. It shouldn't have access to anything at all.
#default_login_user = dovenull

# Internal user is used by unprivileged processes. It should be separate from
# login user, so that login processes can't disturb other processes.
#default_internal_user = dovecot

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

  # Number of connections to handle before starting a new process. Typically
  # the only useful values are 0 (unlimited) or 1. 1 is more secure, but 0
  # is faster. <doc/wiki/LoginProcess.txt>
  service_count = 1

  # Number of processes to always keep waiting for more connections.
  process_min_avail = 4

  # If you set service_count=0, you probably need to grow this.
  #vsz_limit = $default_vsz_limit

service pop3-login {
  service_count = 1
  inet_listener pop3 {
    port = 110
  inet_listener pop3s {
    port = 995
    ssl = yes

service lmtp {
  unix_listener lmtp {
    #mode = 0666

  # Create inet listener only if you can't use the above UNIX socket
  #inet_listener lmtp {
    # Avoid making LMTP visible for the entire internet
    #address =
    #port = 

service imap {
  # Most of the memory goes to mmap()ing files. You may need to increase this
  # limit if you have huge mailboxes.
  #vsz_limit = $default_vsz_limit

  # Max. number of IMAP processes (connections)
  #process_limit = 1024

service pop3 {
  # Max. number of POP3 processes (connections)
  #process_limit = 1024

service auth {
  # auth_socket_path points to this userdb socket by default. It's typically
  # used by dovecot-lda, doveadm, possibly imap process, etc. Users that have
  # full permissions to this socket are able to get a list of all usernames and
  # get the results of everyone's userdb lookups.
  # The default 0666 mode allows anyone to connect to the socket, but the
  # userdb lookups will succeed only if the userdb returns an "uid" field that
  # matches the caller process's UID. Also if caller's uid or gid matches the
  # socket's uid or gid the lookup succeeds. Anything else causes a failure.
  # To give the caller full permissions to lookup all users, set the mode to
  # something else than 0666 and Dovecot lets the kernel enforce the
  # permissions (e.g. 0777 allows everyone full permissions).
  unix_listener auth-userdb {
    mode = 0666
    user = vmail
    group = mail

  unix_listener /var/spool/postfix/private/auth {
    mode = 0666
    # Assuming the default Postfix user and group
    user = postfix
    group = postfix

  # Postfix smtp-auth
  unix_listener /var/spool/postfix/private/auth {
    mode = 0666

  # Auth process is run as this user.
  user = root

service auth-worker {
  # Auth worker process is run as root by default, so that it can access
  # /etc/shadow. If this isn't necessary, the user should be changed to
  # $default_internal_user.
  user = root

service dict {
  # If dict proxy is used, mail processes should have access to its socket.
  # For example: mode=0660, group=vmail and global mail_access_groups=vmail
  unix_listener dict {
    #mode = 0600
    #user = 
    #group = 


# nano 10-ssl.conf

Copy and paste, make sure you update all SSL paths!

## SSL settings

# SSL/TLS support: yes, no, required. <doc/wiki/SSL.txt>
ssl = required

# PEM encoded X.509 SSL/TLS certificate and private key. They're opened before
# dropping root privileges, so keep the key file unreadable by anyone but
# root. Included doc/mkcert.sh can be used to easily generate self-signed
# certificate, just make sure to update the domains in dovecot-openssl.cnf
ssl_cert = </etc/letsencrypt/live/mail.domain.com/fullchain.pem
ssl_key = </etc/letsencrypt/live/mail.domain.com/privkey.pem

# If key file is password protected, give the password here. Alternatively
# give it when starting dovecot with -p parameter. Since this file is often
# world-readable, you may want to place this setting instead to a different
# root owned 0600 file by using ssl_key_password = <path.
#ssl_key_password =

# PEM encoded trusted certificate authority. Set this only if you intend to use
# ssl_verify_client_cert=yes. The file should contain the CA certificate(s)
# followed by the matching CRL(s). (e.g. ssl_ca = </etc/ssl/certs/ca.pem)
ssl_ca = </etc/letsencrypt/live/mail.domain.com/chain.pem

# Require that CRL check succeeds for client certificates.
#ssl_require_crl = yes

# Directory and/or file for trusted SSL CA certificates. These are used only
# when Dovecot needs to act as an SSL client (e.g. imapc backend). The
# directory is usually /etc/ssl/certs in Debian-based systems and the file is
# /etc/pki/tls/cert.pem in RedHat-based systems.
ssl_client_ca_dir = /etc/ssl/certs
#ssl_client_ca_file =

# Request client to send a certificate. If you also want to require it, set
# auth_ssl_require_client_cert=yes in auth section.
#ssl_verify_client_cert = no

# Which field from certificate to use for username. commonName and
# x500UniqueIdentifier are the usual choices. You'll also need to set
# auth_ssl_username_from_cert=yes.
#ssl_cert_username_field = commonName

# DH parameters length to use.
ssl_dh_parameters_length = 2048

# SSL protocols to use
#ssl_protocols = !SSLv2 !SSLv3

# SSL ciphers to use
ssl_cipher_list = ALL:!ADH:!LOW:!SSLv2:!SSLv3:!EXP:!aNULL:+HIGH:+MEDIUM

# Prefer the server's order of ciphers over client's.
ssl_prefer_server_ciphers = yes

# SSL crypto device to use, for valid values run "openssl engine"
#ssl_crypto_device =

# SSL extra options. Currently supported options are:
#   no_compression - Disable compression.
#   no_ticket - Disable SSL session tickets.
#ssl_options =


# nano 15-mailboxes.conf

Copy and paste

## Mailbox definitions

# Each mailbox is specified in a separate mailbox section. The section name
# specifies the mailbox name. If it has spaces, you can put the name
# "in quotes". These sections can contain the following mailbox settings:
# auto:
#   Indicates whether the mailbox with this name is automatically created
#   implicitly when it is first accessed. The user can also be automatically
#   subscribed to the mailbox after creation. The following values are
#   defined for this setting:
#     no        - Never created automatically.
#     create    - Automatically created, but no automatic subscription.
#     subscribe - Automatically created and subscribed.
# special_use:
#   A space-separated list of SPECIAL-USE flags (RFC 6154) to use for the
#   mailbox. There are no validity checks, so you could specify anything
#   you want in here, but it's not a good idea to use flags other than the
#   standard ones specified in the RFC:
#     \All      - This (virtual) mailbox presents all messages in the
#                 user's message store. 
#     \Archive  - This mailbox is used to archive messages.
#     \Drafts   - This mailbox is used to hold draft messages.
#     \Flagged  - This (virtual) mailbox presents all messages in the
#                 user's message store marked with the IMAP \Flagged flag.
#     \Junk     - This mailbox is where messages deemed to be junk mail
#                 are held.
#     \Sent     - This mailbox is used to hold copies of messages that
#                 have been sent.
#     \Trash    - This mailbox is used to hold messages that have been
#                 deleted.
# comment:
#   Defines a default comment or note associated with the mailbox. This
#   value is accessible through the IMAP METADATA mailbox entries
#   "/shared/comment" and "/private/comment". Users with sufficient
#   privileges can override the default value for entries with a custom
#   value.

# NOTE: Assumes "namespace inbox" has been defined in 10-mail.conf.
namespace inbox {
  # These mailboxes are widely used and could perhaps be created automatically:
  mailbox Trash {
    auto = no
    special_use = \Trash
  mailbox Junk {
    auto = no
    special_use = \Junk
  mailbox Drafts {
    auto = no
    special_use = \Drafts
  mailbox Sent {
    auto = subscribe # autocreate and autosubscribe the Sent mailbox
    special_use = \Sent
  mailbox "Sent Messages" {
    auto = no
    special_use = \Sent
  mailbox Spam {
    auto = create # autocreate Spam, but don't autosubscribe
    special_use = \Junk

  # If you have a virtual "All messages" mailbox:
  #mailbox virtual/All {
  #  special_use = \All
  #  comment = All my messages

  # If you have a virtual "Flagged" mailbox:
  #mailbox virtual/Flagged {
  #  special_use = \Flagged
  #  comment = All my flagged messages


# nano 20-imap.conf

Replace at bottom

protocol imap {
    mail_max_userip_connections = 512
    imap_idle_notify_interval = 24 mins
    mail_plugins = $mail_plugins antispam


# nano 20-pop3.conf

Replace at bottom

protocol pop3 {
    mail_max_userip_connections = 512
    #mail_plugins = $mail_plugins sieve


# nano 90-plugin.conf

Copy and paste

## Plugin settings

# All wanted plugins must be listed in mail_plugins setting before any of the
# settings take effect. See <doc/wiki/Plugins.txt> for list of plugins and
# their configuration. Note that %variable expansion is done for all values.

plugin {
    sieve = ~/.dovecot.sieve
    sieve_dir = ~/sieve
    sieve_global_dir = /var/lib/dovecot/sieve.d/
    sieve_global_path = /var/lib/dovecot/sieve.d/default.sieve
plugin {
    antispam_backend = pipe
    antispam_signature = X-Spam-Flag
    antispam_signature_missing = move

    antispam_trash = trash;Trash;Deleted Items;Deleted Messages
    antispam_trash_pattern = trash;Trash;Deleted *
    antispam_trash_pattern_ignorecase = TRASH

    antispam_spam = Spam;Junk
    antispam_spam_pattern = spam;Spam;junk;Junk
    antispam_spam_pattern_ignorecase = SPAM;JUNK

    antispam_pipe_tmpdir = /var/tmp
    antispam_pipe_program = /usr/bin/spamc
    antispam_pipe_program_args = --username;debian-spamd
    antispam_pipe_program_spam_arg = --learntype=spam
    antispam_pipe_program_notspam_arg = --learntype=ham

    antispam_debug_target = syslog
    antispam_verbose_debug = 1


# nano 90-sieve.conf

sieve_before = /var/lib/dovecot/sieve.d/
sieve_extensions = +notify +imapflags +fileinto +mailbox +variables
sieve_global_extensions = +spamtest +spamtestplus +virustest +relational +comparator-i;ascii-numeric +reject +regex +body
sieve_max_script_size = 1M


And finally connect to the database

# nano /etc/dovecot/dovecot-sql.conf.ext


driver = mysql

connect = host=localhost dbname=mailservename user=postfixuser password=databasepassword

password_query = \
  SELECT username as user, password, '/var/vmail/%d/%n' as userdb_home, \
  'maildir:/var/vmail/%d/%n' as userdb_mail, 150 as userdb_uid, \
   8 as userdb_gid, allow_nets \
  FROM mailbox WHERE username = '%u' AND active = '1'

user_query = \
  SELECT '/var/vmail/%d/%n' as home, 'maildir:/var/vmail/%d/%n' as mail, \
  150 AS uid, 8 AS gid, concat('dirsize:storage=', quota) AS quota \
  FROM mailbox WHERE username = '%u' AND active = '1'


Enabling Sieve

# nano 15-lda.conf


postmaster_address = postmaster@domain.com
hostname = mail.domain.com


protocol lda {
    log_path = syslog
    mail_plugins = $mail_plugins sieve
    mail_fsync = optimized


 Restart Dovecot

# /etc/init.d/dovecot restart

Should start with no problems. I have verbose debugging enabled for testing and correcting. You can alter this in the logging.conf when all is well.

Most common error is path to SSL, make sure it is correct or server wont start.

Now we want to connect to the mail server from a client such as Outlook but first we need to take some security steps and create a new row in the mailserver database

Create a new row in the mailboxes table

allow_nets varchar(255) NOT Null

This is where you can add you IP address block to connect from, it ensures no one else can access your email unless its from your own work station/machine

In allow_nets for each mailbox add your public IP block like so


For convenience you can also add it from the mail admin submission form when editing or creating new account but we will need to alter the postfix admin code to achieve this. And upon each update it will need to be added in as it is over written.

At the command line (Path to mymailadmin)

# nano /usr/share/mymailadmin/model/MailboxHandler.php
            'name'             => pacol(1,          1,      1,      'text', 'name'                          , 'pCreate_mailbox_name_text'       , '' ),

Add underneath
            'allow_nets'    => pacol(   1,          1,      1,      'text', 'pCreate_allowed_nets'          , 'pCreate_allow_nets'            , '' ),
# nano /usr/share/mymailadmin/templates/list-virtual_mailbox.tpl
                {if $CONF.quota===YES}<td>{$PALANG.pOverview_mailbox_quota}</td>{/if}

Below it add


Above it add
# nano /usr/share/mymailadmin/languages/en.lang

Adjust for your own language
Add at top

$PALANG['allownet'] = 'Allowed Nets';
$PALANG['pCreate_allowed_nets'] = 'Allowed IP Nets (comma separated list)';


Save the files and login to Postfix admin and see the fields available now.

Moving on...

Share this post

Link to post
Share on other sites

Step five: OpenDKIM | SPF | DMARC

Now that mail is working we need to setup our filtering and security applications to make sure mail is marked clean from our server and to catch those marked dirty to our server. Lets start with the three settings that need to be part of your domains DNS.

I am using bind here but for the most part the lines you need to add are the same for any DNS server. Open your domain DNS file and add the following.


change domain.com and IP to yours

domain.com. IN TXT "v=spf1 include:domain.com ip4:111.222.333.444 ip6:fe80::a6bf:1ff:fe1d:ad5e ~all"


change domain.com to yours

_dmarc.domain.com. IN TXT "v=DMARC1; p=none; sp=reject; ruf=mailto:postmaster@domain.com; rua=mailto:postmaster@domain.com; aspf=r; rf=afrf; pct=20; ri=86400"



# mkdir -pv /etc/opendkim/domain.com/
# chown -R opendkim:opendkim /etc/opendkim
# cd /etc/opendkim/domain.com
# opendkim-genkey -r -h sha256 -d domain.com -s email
# mv -v email.private email.key

Now open the email.txt and copy the entire contents to your DNS, it will look something like this

# nano /etc/opendkim/domain.com/email.txt
email._domainkey	IN	TXT	( "v=DKIM1; h=sha256; k=rsa; s=email; "
	  "MWNwZ2sPsd4voNq72Uo3xgf35gxx35gMMr6PDgsxR1gRJ87QZOBnIvTquH12K2cLanTFm6O93PrRhbmtiy+H3WnNu+mazajSFFsv0/xEW7QncromsvRsVlfEs4QfPMjNUtDHUMeB0LQwGwIDAQAB" )  ; ----- DKIM key email for domain.com

All three of these should be pasted in consecutive lines in DNS zone file and before the mail entries

Restart the DNS server and check the logs for errors in syntax.

# /etc/init.d/bind9 restart

More on OpneDKIM we are not done yet if we want it working properly

Test Key

# opendkim-testkey -vvv -d domain.com -s email -k /etc/opendkim/domain.com/email.key


Now lets finish it up

# cd /etc/opendkim/


# nano KeyTable


#key_name domain:selector:/etc/opendkim/domain.com/email.key
email._domainkey.domain.com domain.com:email:/etc/opendkim/domain.com/email.key
# nano SigningTable


#*@domain.com domain.com
*@domain.com email._domainkey.domain.com
# nano TrustedHosts

Add your domain, hostname, mailserver name, local host, and server IPs


chown opendkim:opendkim /etc/opendkim/{KeyTable,SigningTable,TrustedHosts}

Configure DKIM

# nano /etc/opendkim.conf

Copy and paste

# Required to use local socket with MTAs that access the socket as a non-
# privileged user (e.g. Postfix)
OversignHeaders         From,Subject
SignatureAlgorithm      rsa-sha256
AutoRestart             Yes
Canonicalization        relaxed/relaxed
ExternalIgnoreList      refile:/etc/opendkim/TrustedHosts
InternalHosts           refile:/etc/opendkim/TrustedHosts
KeyTable                refile:/etc/opendkim/KeyTable
LogWhy                  Yes
MinimumKeyBits          1024
Mode                    sv
PidFile                 /var/run/opendkim/opendkim.pid
SigningTable            refile:/etc/opendkim/SigningTable
Socket                  local:/var/run/opendkim/opendkim.sock
Syslog                  Yes
SyslogSuccess           Yes
LogWhy                  Yes
TemporaryDirectory      /var/tmp
UMask                   0002
UserID                  opendkim:opendkim

TrustAnchorFile       /usr/share/dns/root.key

Run the following commands, replace domain.com with yours

mkdir -p /var/run/opendkim/
chown opendkim:opendkim /var/run/opendkim/
chown opendkim:opendkim /var/run/opendkim/opendkim.sock
chown opendkim:opendkim /etc/opendkim/domain.com/email.key
chown opendkim:opendkim /etc/opendkim/domain.com/email.txt
usermod -a -G opendkim postfix
chmod 775 /var/run/opendkim/


# nano /etc/default/opendkim

Copy and paste

# Command-line options specified here will override the contents of
# /etc/opendkim.conf. See opendkim(8) for a complete list of options.
# Change to /var/spool/postfix/var/run/opendkim to use a Unix socket with
# postfix in a chroot:
# Uncomment to specify an alternate socket
# Note that setting this will override any Socket value in opendkim.conf
# default:
# SOCKET="local:/var/spool/postfix/var/run/opendkim/opendkim.sock"
# listen on all interfaces on port 54321:
# listen on loopback on port 12345:
# listen on on port 12345:


# /etc/init.d/opendkim restart




We are now in the home stretch...

Share this post

Link to post
Share on other sites

Step Six: Amavis

# nano /etc/amavis/conf.d/15-content_filter_mode

Replace with

use strict;

# You can modify this file to re-enable SPAM checking through spamassassin
# and to re-enable antivirus checking.

# Default antivirus checking mode
# Uncomment the two lines below to enable it

@bypass_virus_checks_maps = (
   \%bypass_virus_checks, \@bypass_virus_checks_acl, \$bypass_virus_checks_re);

# Default SPAM checking mode
# Uncomment the two lines below to enable it

@bypass_spam_checks_maps = (
   \%bypass_spam_checks, \@bypass_spam_checks_acl, \$bypass_spam_checks_re);

1;  # insure a defined return


# nano /etc/amavis/conf.d/20-debian_defaults

$final_spam_destiny       = D_BOUNCE;

Change to
$final_spam_destiny       = D_DISCARD;
# nano /etc/amavis/conf.d/40-policy_banks

Adjust to your liking
# nano /etc/amavis/conf.d/50-user

Replace with, be sure to update domain and database info

use strict;

# Place your configuration directives here.  They will override those in
# earlier files.
# See /usr/share/doc/amavisd-new/ for documentation and examples of
# the directives you can use in this file
$mydomain = 'domain.com';
$myhostname = 'mail.domain.com';
#@local_domains_acl = ( "domain.com", "domain2.com, "domain3.net" );
@local_domains_acl = qw(.);
# Three concurrent processes. This should fit into the RAM available on an
# AWS micro instance. This has to match the number of processes specified
# for Amavis in /etc/postfix/master.cf.
$max_servers  = 3;

# Add spam info headers if at or above that level - this ensures they
# are always added.
$sa_tag_level_deflt  = -9999;

# Check the database to see if mail is for local delivery, and thus
# should be spam checked.
@lookup_sql_dsn = (
$sql_select_policy = 'SELECT domain from domain WHERE CONCAT("@",domain) IN (%k)';

# Uncomment to bump up the log level when testing.
 $log_level = 2;
 $sa_debug = 1;

#------------ Do not modify anything below this line -------------
1;  # ensure a defined return


Add the clamav user to the amavis group in order for Amavisd-new to have the appropriate access to scan files:

# adduser clamav amavis
# adduser amavis clamav


Share this post

Link to post
Share on other sites

Step Seven: Postgrey

Quick and simple

# nano /etc/default/postgrey

Copy and paste

# postgrey startup options, created for Debian

# you may want to set
#   --delay=N   how long to greylist, seconds (default: 300)
#   --max-age=N delete old entries after N days (default: 35)
# see also the postgrey(8) manpage

POSTGREY_OPTS="--inet=10023 --delay=60 --privacy --x-greylist-header=Mail delayed %t seconds by postgrey-%v at %h; %d"

# the --greylist-text commandline argument can not be easily passed through
# POSTGREY_OPTS when it contains spaces.  So, insert your text here:
POSTGREY_TEXT="This email was rejected by our greylisting server"
Restart Postgrey

# /etc/init.d/postgrey restart




Share this post

Link to post
Share on other sites

Step Eight: Spamassassin


# nano /etc/spamassassin/local.cf

Replace with, make sure to add your main server IP

#No user rules
allow_user_rules 0

# Trusted
trusted_networks        111.222.333.444
internal_networks       111.222.333.444
whitelist_from *@gmail.com

# alter the mails subject
rewrite_header Subject [***** SPAM _SCORE_ *****]

# do not alter the body (0=do nothing, 1=add as attachment, 2=...)
report_safe 0

# the required spam score is 2.0 points... lets start with that
required_score 2.9

# Enable the Bayes system
use_bayes               1
use_bayes_rules         1
bayes_auto_learn        1
bayes_auto_learn_threshold_nonspam -0.001
bayes_auto_learn_threshold_spam 2.9
bayes_path              /var/lib/amavis/.spamassassin/bayes
bayes_file_mode         0770

# Disable network checks
skip_rbl_checks         0
skip_uribl_checks       0

# Enable razor2 and make use of it
use_razor2              1
razor_config /var/lib/spamassassin/.razor/razor-agent.conf

# Enable pyzor and make use of it
ifplugin Mail::SpamAssassin::Plugin::Pyzor
use_pyzor               1
pyzor_path /usr/bin/pyzor
pyzor_timeout 20
pyzor_options --homedir /var/lib/spamassassin/.pyzor

# Enable DCC and make use of it
loadplugin Mail::SpamAssassin::Plugin::DCC
use_dcc                 1
dcc_path                /bin/dccproc
dcc_dccifd_path         /lib/dcc/dccifd
dcc_home                /var/lib/dcc
dcc_learn_score         0
dcc_timeout             10
full DCC_CHECK eval:check_dcc()

add_header all Status _YESNO_, score=_SCORE_ required=_REQD_ tests=_TESTSSCORES(,)_ _DCCR_ _PYZOR_ _RBL_ autolearn=_AUTOLEARN_ version=_VERSION_

# Set headers which may provide inappropriate cues to the Bayesian classifier
bayes_ignore_header X-Bogosity
bayes_ignore_header X-Spam-Flag
bayes_ignore_header X-Spam-Stat

ifplugin Mail::SpamAssassin::Plugin::Shortcircuit
#   default: strongly-whitelisted mails are *really* whitelisted now, if the
#   shortcircuiting plugin is active, causing early exit to save CPU load.
#   Uncomment to turn this on
shortcircuit USER_IN_WHITELIST       on
shortcircuit USER_IN_DEF_WHITELIST   on
shortcircuit USER_IN_ALL_SPAM_TO     on
shortcircuit SUBJECT_IN_WHITELIST    on

#   the opposite; blacklisted mails can also save CPU
shortcircuit USER_IN_BLACKLIST       on
shortcircuit USER_IN_BLACKLIST_TO    on
shortcircuit SUBJECT_IN_BLACKLIST    on

#   if you have taken the time to correctly specify your "trusted_networks",
#   this is another good way to save CPU
shortcircuit ALL_TRUSTED             on

#   and a well-trained bayes DB can save running rules, too
shortcircuit BAYES_99                spam
shortcircuit BAYES_00                ham

endif # Mail::SpamAssassin::Plugin::Shortcircuit


# nano /etc/default/spamassassin



# /etc/init.d/spamassassin restart

Remember that deprecated warning during the Perl install? We will now we will fix it now.


root@thundersnow:/etc/opendkim# sa-update
Unescaped left brace in regex is deprecated here (and will be fatal in Perl 5.30), passed through in regex; marked by <-- HERE in m/^(.{ <-- HERE ,200}).*$/ at /usr/local/share/perl/5.26.1/Mail/SpamAssassin/PerMsgStatus.pm line 921.


# nano -c /usr/local/share/perl/5.26.1/Mail/SpamAssassin/PerMsgStatus.pm
Go to line 921 and change this

  $str =~ s/^(.{,200}).*$/$1/gs;

to this

  $str =~ s/^(.\{,200}).*$/$1/gs;


Now update Spamassassin rules

# sa-update


Share this post

Link to post
Share on other sites

Step Nine: DCC | Pyzor | Razor2 | Bayes

Easy and quick, lets go...


# wget http://www.dcc-servers.net/dcc/source/dcc-dccproc.tar.Z
# tar xfvz dcc-dccproc.tar.Z
# cd dcc-dccproc-*
# ./configure \
    --bindir=$(PREFIX)/bin \
    --libexecdir=$(PREFIX)/lib/dcc \
    --mandir=$(PREFIX)/man \

# make
# make install
# chown -R postfix:postfix /var/lib/dcc

--- Allow through Firewall ---

# sudo ufw allow to any port 6277 proto udp

# nano /var/lib/dcc/dcc_conf

DCCUID=root  -->  DCCUID=postfix


DNSBL_ARGS="'-Bset:rej-msg=5.7.1 550 mail %s from %s rejected; see http://www.spamhaus.org/xbl/' -Bsbl-xbl.spamhaus.org,any"


Configure_DCCUID=root  -->  Configure_DCCUID=postfix

Test, you should see a server list

# cdcc info


# /lib/dcc/rcDCC start





Lets cleanup the spamassassin compile directory and start fresh

# rm -rf /var/lib/spamassassin/*
# su debian-spamd -c '/usr/bin/sa-update --gpghomedir /var/lib/spamassassin/sa-update-key'
# su debian-spamd -c '/usr/bin/sa-compile --quiet'

# sa-learn --sync
# usermod -a -G amavis debian-spamd
# chown amavis:amavis /var/lib/amavis/.spamassassin/bayes_seen
# chown amavis:amavis /var/lib/amavis/.spamassassin/bayes_toks
# chmod 0600 /var/lib/amavis/.spamassassin/bayes_seen
# chmod 0600 /var/lib/amavis/.spamassassin/bayes_toks
# sa-learn -u debian-spamd --dbpath /var/lib/amavis/.spamassassin/bayes --dump magic

-- Test Bayes --

# spamassassin -D -t < /usr/share/doc/spamassassin/examples/sample-spam.txt 2>&1 | egrep '(bayes:|whitelist:|AWL)'

Setup cron tab entries to learn spam and ham every day at midnight, edit domain.com. Add all your accounts for best database building.

0 0 * * * /usr/bin/sa-learn --spam -u debian-spamd --showdots --dir /var/vmail/domain.com/support/.Spam/cur/*
0 0 * * * /usr/bin/sa-learn --ham -u debian-spamd --showdots --dir /var/vmail/domain.com/support/cur/*



# mkdir /var/lib/spamassassin/.razor


# razor-admin -home=/var/lib/spamassassin/.razor -register

# razor-admin -home=/var/lib/spamassassin/.razor -create

# razor-admin -home=/var/lib/spamassassin/.razor -discover



Test, should be running out of the box

# echo "test" | spamassassin -D pyzor 2>&1 | less


Share this post

Link to post
Share on other sites

Step Ten: Dovecot-Sieve

Already configured this when we did Dovecot configure. But now we want to add custom scripts to further lock down our mail server and make sure only good email gets in.

For more info an additional filtering options




The first script is for individual email accounts, it is used to prevent spoofing email addresses on the server as wella s some additional filtering rules.

So lets say you setup an account in Postfix Admin name support@domain.com

Lets create a sieve script in supports mail directory

# nano /var/vmail/domain.com/support/.dovecot.sieve

Insert the following and save.

require ["fileinto"];

if anyof (not address :all :contains ["To", "Cc", "Bcc"] "support@domain.com",
header :matches "X-Spam-Status" ["T_DKIM_INVALID", "FORGED_HOTMAIL_RCVD2","MISSING_HEADERS"],
header :matches "Authentication-Results" ["fail", "dkim=none", "header.d=none" ,"dmarc=none"],
header :matches "Subject" ["*spam*","*Viagra*","*offshore*","*gambling*","*porno*","*capital*"]) {
       fileinto "Spam";

The above does the following:

if the address (To, Cc Bcc) doesn't contain support@domain.com it will go to Spam folder

If the header contain invalid DKIM, or a forged Hotmail (very common) or Missing headers it will go to Spam folder

If authentication says fail, dkim none, header none, or dmarc none it will go to Spam folder

If subject matches the above keywords it will go to Spam folder

You can remove or add filters. Every domains mail directory should have this unique file. Make sure the domain is correct in script.

Now make it a readable database for Sieve (you must do this for scripts and every time you alter the script)

# sievec -D /var/vmail/domain.com/support/.dovecot.sieve

Example output



More script examples, these are run before the user script above.

# mkdir /var/lib/dovecot/sieve.d
# nano /var/lib/dovecot/sieve.d/emails.sieve

--Insert-- This is a list of emails you can send directly to spam

require ["fileinto"];
if address :is "from" "godaddydesign@gmail.com, johnfrancisthestud@gmail.com, mayswihart3269@gmail.com, gilbertepxmaria@gmail.com, mlika@creativenetapp.com, aarohi.webconsultant@hotmail.com"
    fileinto "Spam";
# nano /var/lib/dovecot/sieve.d/general.sieve

--Insert-- More header and body checks

require ["regex", "body", "fileinto", "mailbox"];

if header :contains "X-Spam-Flag" "YES" {
    # move mail into Folder Spam, create folder if not exists
    fileinto :create "Spam";

if header :contains "X-Spam-Level" "**" {
    fileinto :create "Spam";

if allof (
  not header :regex "Subject" "[[:graph:]]",
  body :regex "^[[:space:]]*http://[[:graph:]]+[[:space:]]*$"
  fileinto "Spam";
# nano /var/lib/dovecot/sieve.d/spam.sieve

--Insert-- Spamtestplus

require ["spamtestplus", "fileinto", "mailbox", "relational", "comparator-i;ascii-numeric"];

if spamtest :value "eq" :comparator "i;ascii-numeric" "0" {

} elsif spamtest :value "ge" :comparator "i;ascii-numeric" "2" {
  fileinto "Spam";
# nano /var/lib/dovecot/sieve.d/virus.sieve

--Insert-- Virustest

require ["virustest", "fileinto", "mailbox", "relational", "comparator-i;ascii-numeric"];

/* Not scanned ? */
if virustest :value "eq" :comparator "i;ascii-numeric" "0" {

/* Infected with high probability (value range in 1-5) */
} if virustest :value "eq" :comparator "i;ascii-numeric" "4" {
  /* Quarantine it in special folder (still somewhat dangerous) */
  fileinto :create "INBOX.Quarantine";

/* Definitely infected */
} elsif virustest :value "eq" :comparator "i;ascii-numeric" "5" {
  /* Just get rid of it */

Now don't forget to use sievec

# sievec -D /var/lib/dovecot/sieve.d/emails.sieve

# sievec -D /var/lib/dovecot/sieve.d/spam.sieve

# sievec -D /var/lib/dovecot/sieve.d/general.sieve

# sievec -D /var/lib/dovecot/sieve.d/virus.sieve

There are many different filters you can add, please see more at





Share this post

Link to post
Share on other sites

Restart all services and check the /var/log/mail.log \\ mail.err for any errors

# /etc/init.d/postgrey restart
# /etc/init.d/rcDCC restart
# /etc/init.d/spamassassin restart
# /etc/init.d/amavis restart
# /etc/init.d/clamav-daemon restart
# /etc/init.d/postfix restart
# /etc/init.d/dovecot restart
# /etc/init.d/opendkim restart

Share this post

Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

  • Recently Browsing   0 members

    No registered users viewing this page.