Install Mail Server On Ubuntu 20.04 LTS Using Postfix, Dovecot, and Roundcube
Install Mail Server On Ubuntu 20.04 LTS Using Postfix, Dovecot, and Roundcube
June 25, 2020

The major components or activities involved in setting or installing a complete email server for production usage involves MTA (Mail Transfer Agent), MDA (Mail Delivery Agent), Antivirus, Spam Detection, and Web Interface. The MTA is required to communicate or relay emails between the internet and the email server to send and receive emails to other servers. At the same time, the MDA reads the email queue of the MTA and save the emails to the inboxes by identifying the corresponding users. Apart from the MTA and MDA, an email server also need tools for greylisting the servers, antivirus to scan the emails for well-known viruses, and spam detection tool to filter the spam emails. A web-based interface is also required to access the email server to read/write/manage emails.

This guide provides the complete steps required to install an email or mail server on Ubuntu 20.04 LTS. This tutorial provides all the steps required to install Postfix as MTA, Dovecot as MDA, Postgrey as Greylisting Policy Server, Clam as Antivirus, Amavis as Content Filter, SpamAssassin as Email Spam Filter, Postfix Admin to admin domains and users, and RoundCube as Web Interface for the users. It also provides configurations to manage virtual domains and virtual users using the MySQL database management system. In the end, it also shows how to check the email server health using the standard tools. All the steps mentioned in this tutorial are executed and tested on Ubuntu 20.04 LTS.

Notes:

  • This is a lengthy tutorial, involving several installation commands and configurations. Make sure that you execute all the commands and apply all the configurations since it's really difficult to debug on missing even a single configuration. Now take a deep breath, calm down, have a cup of tea/coffee or beer mug, put light music, relax, and follow the instructions in the subsequent sections of this tutorial. Also, do not rush while installing your email server and make sure that you have sufficient time to debug if something goes wrong.
  • This tutorial used example.com domain for demonstration purposes. Also, it used mail.example.com as the server hostname. Make sure to replace example.com and mail.example.com with your actual domain and server hostname.
  • This tutorial tries to cover all the major aspects to set up a production email server. You can further refine and update the configurations based on your requirements.
  • I have tried to keep this guide short without explaining the configurations and parameters.
  • You will have a complete email server ready for production usage by following this tutorial. You may further enhance it based on your actual requirements.
  • Also, add notes by submitting your valuable comments in case you have some suggestions or better configuration options.

Prerequisites

This tutorial assumes that you have already installed Ubuntu 20.04 LTS server for production usage. You can follow Spin Up Ubuntu 20.04 LTS Server On Amazon EC2 to install Ubuntu 20.04 LTS. It also assumes that you have either root privileges or a regular user with sudo privileges.

It also assumes that the MySQL, PHP, and Apache Web Server are already installed. You may follow How To Install MySQL 8 on Ubuntu 20.04 LTS, How To Install PHP 7 On Ubuntu 20.04 LTS, How To Install Apache 2 On Ubuntu 20.04 LTSConfigure Virtual Host On Apache and How To Install Let's Encrypt For Apache On Ubuntu.

Make sure that the ports 25 (SMTP), 587 (SMTP over TLS), 465 (SMTPS), 143 (IMAP), 993 (IMAPS), 110 (POP3), 995 (POP3S) are not-opened or closed using appropriate firewall tool. UFW is the default Firewall configuration tool used on the Ubuntu 20.04 LTS. On AWS EC2, the security group assigned to the instance must be updated to close these ports for external communication over the internet.

A valid domain name is pointing to your server and you have access to update the domain record. It also expects that a valid SSL certificate is installed for the hostname assigned to the email server. You may follow How To Install Let's Encrypt For Apache On Ubuntu. The SSL certificate must include example.com and mail.example.com. You can also include two additional sub-domains to host the Postfix Admin and Roundcube i.e. mailadmin.example.com and webmail.example.com.

Also, confirm with your hosting provider to allow sending bulk-emails by removing the restrictions on port 25. We are required to submit a form requesting AWS to remove the port 25 restrictions as explained here.

Update Domain Record

This section provides the configurations required at the DNS record to continue installing the email server. Make sure that your domain record is configured as shown below.

A Record - An A Record exists having the hostname and IP address of your email server. The hostname of an email server should be similar to email.example.com.

Name			Type	Value 		TTL
mail.example.com. A xx.xxx.xxx.xxx 300

CNAME Records - Add CNAME records for Postfix Admin and RoundCube.

Name			Type	Value 			TTL
mailadmin.example.com. CNAME mail.example.com 300
webmail.example.com. CNAME mail.example.com 300

MX Record - The domain record must have an MX record as shown below.

Name			Type	Value 			TTL
mail.example.com. MX 0 xx.xxx.xxx.xxx 300

SPF Record - A text record to check authorized hosts to send an email for a domain. A generic SPF record should be similar as shown below.

Name			Type	Value 			TTL
mail.example.com. TXT "v=spf1 a mx -all" 300

PTR Record - A valid PTR record should exist. You may contact your hosting provider to configure an appropriate PTR record in case the control panel does not provide options to set the reverse domain for your email server IP address. On AWS, it can be done by submitting this form. We can also check the reverse DNS using the command dig as shown below.

# Dig
dig -x <your server ip> +short

# Example
dig -x xx.xxx.xxx.xxx +short

# Output
email.example.com.

DMARC record - Add DMARC record as mentioned below.

Name				Type	Value 								TTL
_dmarc.mail.example.com. TXT "v=DMARC1;p=reject;pct=100;rua=mailto:postmaster@example.com" 300

Configure Hostname

This section provides the commands to configure the Hostname of the email server. The same hostname will be used by the Postfix and Dovecot. Now execute the below-mentioned commands to update the server hostname.

# Check Hostname
hostname

# Output
<current hostname>

# Update Hostname
sudo hostnamectl set-hostname mail.example.com

# Check Hostname
hostname

# Output
mail.example.com

Also, update the hosts file as shown below. Make sure to replace the IP address and hostname using your server IP address and domain.

# Update Hosts
sudo nano /etc/hosts

# Update
127.0.0.1 localhost

xxx.xxx.xxx.xxx mail.example.com mail

-----
-----

# Save and exit the editor by pressing Ctrl + o -> Enter -> Ctrl + x

This completes the hostname configuration. You may reboot your server and again check the hostname. It must reflect the new hostname configured by you.

Install Postfix

This section provides the steps to install Postfix MTA on Ubuntu 20.04 LTS. Now execute the commands as shown below to install Postfix.

# Refresh Packages Index
sudo apt update

# Update to most recent Ubuntu 20.04 - make sure to backup your server before executing it
sudo apt dist-upgrade

# Autoclean
sudo apt autoclean

# Autoremove
sudo apt autoremove
# OR
sudo apt --purge autoremove

# Install PHP Packages
sudo apt install php7.4-curl php7.4-gd php7.4-mbstring php7.4-imap php7.4-xml php-apcu

# Install Additional Packages
sudo apt install zip unzip rar unrar
sudo apt install pyzor razor arj cabextract lzop nomarch p7zip-full rpm2cpio tnef unzip unrar-free zip bzip2 cpio file gzip pax

# Install Postfix
sudo apt install postfix postfix-mysql

While installing Postfix, the installer asks to choose the configuration type as shown in Fig 1.

Install Postfix On Ubuntu 20.04 LTS - Configuration

Fig 1

Press the Right Arrow Key to highlight the OK option and press Enter Key. It will provide the options to choose configuration type as shown in Fig 2.

Install Postfix On Ubuntu 20.04 LTS - Configuration - Internet Site

Fig 2

Keep the option Internet Site selected and press the Right Arrow Key to highlight the OK option as shown in Fig 3 and.

Install Postfix On Ubuntu 20.04 LTS - Configuration - Internet Site

Fig 3

Now press Enter Key to configure the hostname as shown in Fig 4.

Install Postfix On Ubuntu 20.04 LTS - Configuration - Hostname

Fig 4

Now press Enter to complete the installation. The installation logs should be similar as shown below.

Reading package lists... Done
Building dependency tree
Reading state information... Done
The following additional packages will be installed:
libmysqlclient21
Suggested packages:
procmail postfix-pgsql postfix-ldap postfix-pcre postfix-lmdb postfix-sqlite sasl2-bin | dovecot-common resolvconf postfix-cdb mail-reader
postfix-doc
The following NEW packages will be installed:
libmysqlclient21 postfix postfix-mysql
-----
-----
Postfix (main.cf) is now set up with a default configuration. If you need to
make changes, edit /etc/postfix/main.cf (and others) as needed. To view
Postfix configuration values, see postconf(1).

After modifying main.cf, be sure to run 'systemctl reload postfix'.

Running newaliases
Created symlink /etc/systemd/system/multi-user.target.wants/postfix.service → /lib/systemd/system/postfix.service.
Setting up postfix-mysql (3.4.10-1ubuntu1) ...
Processing triggers for rsyslog (8.2001.0-1ubuntu1) ...
Processing triggers for ufw (0.36-6) ...
Processing triggers for systemd (245.4-4ubuntu3.1) ...
Processing triggers for man-db (2.9.1-1) ...
Processing triggers for libc-bin (2.31-0ubuntu9) ...

# Additional Package
sudo apt install postfix-policyd-spf-python

This completes the installation of Postfix on Ubuntu 20.04 LTS.

Install Dovecot

This section provides the steps to install Dovecot on Ubuntu 20.04 LTS. The below-mentioned commands are required to install Dovecot.

# Install Dovecot
sudo apt install dovecot-core dovecot-imapd dovecot-pop3d dovecot-lmtpd dovecot-mysql

# Output
Reading package lists... Done
Building dependency tree
Reading state information... Done
The following additional packages will be installed:
libexttextcat-2.0-0 libexttextcat-data liblua5.3-0
Suggested packages:
dovecot-gssapi dovecot-ldap dovecot-lucene dovecot-managesieved dovecot-pgsql dovecot-sieve dovecot-solr dovecot-sqlite dovecot-submissiond
ntp
The following NEW packages will be installed:
dovecot-core dovecot-imapd dovecot-lmtpd dovecot-mysql dovecot-pop3d libexttextcat-2.0-0 libexttextcat-data liblua5.3-0
----
----
Setting up dovecot-mysql (1:2.3.7.2-1ubuntu3.1) ...
Processing triggers for ufw (0.36-6) ...
Processing triggers for systemd (245.4-4ubuntu3.1) ...
Processing triggers for man-db (2.9.1-1) ...
Processing triggers for libc-bin (2.31-0ubuntu9) ...
Processing triggers for dovecot-core (1:2.3.7.2-1ubuntu3.1) ...

This completes the installation of Dovecot on Ubuntu 20.04 LTS.

Install Postgrey, Clam, Amavis, and SpamAssassin

Now install Postgrey, Clam, Amavis, and SpamAssassin on Ubuntu 20.04 LTS as shown below.

# Install Postgrey
sudo apt install postgrey

# Install Clam
sudo apt install clamav clamav-daemon

# Install Amavis
sudo apt install amavis

# Install SpamAssassin
sudo apt install spamassassin

# Install additional packages
sudo apt install libdbi-perl libdbd-mysql-perl

Install OpenDKIM

We need to install and configure DKIM on our mail server so that the other email servers can authenticate the emails sent from our server and confirm that the emails are not forged or altered and the emails are authorized by the domain owner. Use the below-mentioned commands to install OpenDKIM.

# Install OpenDKIM
sudo apt install opendkim opendkim-tools

# Output
Reading package lists... Done
Building dependency tree
Reading state information... Done
The following additional packages will be installed:
dns-root-data libmemcached11 libmilter1.0.1 libopendbx1 libopendbx1-sqlite3 libopendkim11 librbl1 libunbound8 libvbr2
Suggested packages:
unbound
The following NEW packages will be installed:
dns-root-data libmemcached11 libmilter1.0.1 libopendbx1 libopendbx1-sqlite3 libopendkim11 librbl1 libunbound8 libvbr2 opendkim opendkim-tools
----
----
Setting up opendkim-tools (2.11.0~beta2-1) ...
Setting up opendkim (2.11.0~beta2-1) ...
Created symlink /etc/systemd/system/multi-user.target.wants/opendkim.service → /lib/systemd/system/opendkim.service.
Processing triggers for systemd (245.4-4ubuntu3.1) ...
Processing triggers for man-db (2.9.1-1) ...
Processing triggers for libc-bin (2.31-0ubuntu9) ...

This completes the installation of DKIM on Ubuntu 20.04 LTS.

Configure Postfix

This section provides the steps to configure Postfix on Ubuntu 20.04 LTS. Now complete the configurations as shown below.

Default SSL Certificate - Generate the default certificate if required.

sudo apt install --assume-yes ssl-cert
sudo make-ssl-cert generate-default-snakeoil --force-overwrite

Backup Config - We must backup the main and master configuration files as shown below.

# Backup Configurations
sudo cp /etc/postfix/main.cf /etc/postfix/main.cf.orig
sudo cp /etc/postfix/master.cf /etc/postfix/master.cf.orig

Virtual Hosts - Create the virtual hosts directory as shown below.

# Mails Directory
sudo mkdir -p /<path to vmail>/vmail/

# Create mail group and user
sudo groupadd -g 5000 vmail
sudo useradd -g vmail -u 5000 vmail -d /<path to vmail>/vmail

# Change mails directory owner
sudo chmod 770 /<path to vmail>/vmail
sudo chown -R vmail:vmail /<path to vmail>/vmail

# Check permissions
ls -ld /<path to vmail>/vmail

# Output
drwxrwx--- 2 vmail vmail 4096 Jun 24 03:21 /<path to vmail>/vmail

# Check UID
id -u vmail

# Output
5000

# Check GID
id -g vmail

# Output
5000

Create Header and Content Checks - Create the header and content checks file as shown below.

# Create Header Checks
sudo nano /etc/postfix/header_checks

# Content
/^Received:/ IGNORE
/^User-Agent:/ IGNORE
/^X-Mailer:/ IGNORE
/^X-Originating-IP:/ IGNORE
/^x-cr-[a-z]*:/ IGNORE
/^Thread-Index:/ IGNORE

# MIME header checks
sudo nano /etc/postfix/mime_header_checks

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

# Body Checks
sudo nano /etc/postfix/body_checks

# Add an empty line for now

# Client Checks
sudo nano /etc/postfix/client_checks

# Add an empty line for now

# Sender Checks
sudo nano /etc/postfix/sender_checks

# Add an empty line for now

Email Database - Create the database to store the virtual users.

# Create User
CREATE USER 'mail'@'localhost' IDENTIFIED WITH caching_sha2_password BY 'dbpassword';

# Create Database
CREATE DATABASE mail CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci;

# Privileges
GRANT ALL PRIVILEGES ON mail.* TO 'mail'@'localhost';
FLUSH PRIVILEGES;

Now create and configure the virtual domains and users files as shown below.

# Virtual domains
sudo nano /etc/postfix/mysql_virtual_mailbox_domains.cf

# Content
hosts = 127.0.0.1
user = mail
password = dbpassword
dbname = mail
query = SELECT domain FROM domain WHERE domain='%s' and backupmx = 0 and active = 1

# Email addresses
sudo nano /etc/postfix/mysql_virtual_mailbox_maps.cf

# Content
hosts = 127.0.0.1
user = mail
password = dbpassword
dbname = mail
query = SELECT goto FROM alias WHERE address='%s' AND active = 1

# Aliases
sudo nano /etc/postfix/mysql_virtual_alias_maps.cf

# Content
hosts = 127.0.0.1
user = mail
password = dbpassword
dbname = mail
query = SELECT goto FROM alias WHERE address='%s' AND active = 1

# Relays
sudo nano /etc/postfix/mysql_relay_domains.cf

# Content
hosts = 127.0.0.1
user = mail
password = dbpassword
dbname = mail
query = SELECT domain FROM domain WHERE domain='%s' and backupmx = 1

Configure Postfix Main - Now configure the main file of Postfix as shown below.

# Configure Postfix Main
sudo nano /etc/postfix/main.cf

The complete configurations are shown below. You may refer to Postfix documentation to learn more about these parameters and their usage. Also, update the configurations based on your requirements.

smtpd_banner = $myhostname ESMTP $mail_name (Ubuntu)
biff = no
append_dot_mydomain = no
#delay_warning_time = 4h
readme_directory = no
compatibility_level = 2

# SASL parameters
# -----------------------
smtpd_sasl_type = dovecot
smtpd_sasl_path = private/auth
smtpd_sasl_auth_enable = yes
broken_sasl_auth_clients = yes
smtpd_sasl_security_options = noanonymous, noplaintext
smtpd_sasl_local_domain =
smtpd_sasl_authenticated_header = yes

# TLS parameters
# -----------------------
smtpd_tls_cert_file=/etc/letsencrypt/live/mail.example.com/fullchain.pem
smtpd_tls_key_file=/etc/letsencrypt/live/mail.example.com/privkey.pem
smtpd_tls_CAfile=/etc/letsencrypt/live/mail.example.com/chain.pem
smtpd_use_tls=yes
smtpd_tls_security_level = may
#smtpd_tls_session_cache_database = btree:${data_directory}/smtpd_scache
smtpd_tls_auth_only = yes
smtpd_sasl_tls_security_options = noanonymous
smtpd_tls_loglevel = 1
smtpd_tls_received_header = yes
smtpd_tls_session_cache_timeout = 3600s
smtpd_tls_protocols = !SSLv2, !SSLv3
smtpd_tls_ciphers = high
#smtp_tls_CApath=/etc/ssl/certs
smtp_use_tls=yes
smtp_tls_security_level = may
#smtp_tls_session_cache_database = btree:${data_directory}/smtp_scache
smtp_tls_note_starttls_offer = yes
tls_random_source = dev:/dev/urandom

# SMTPD parameters
# -----------------------
unknown_local_recipient_reject_code = 450
maximal_queue_lifetime = 7d
minimal_backoff_time = 1000s
maximal_backoff_time = 8000s
smtp_helo_timeout = 60s
smtpd_recipient_limit = 25
smtpd_error_sleep_time = 1s
smtpd_soft_error_limit = 3
smtpd_hard_error_limit = 12
smtpd_delay_reject = yes
disable_vrfy_command = yes

# HELO Restrictions - Reject - HELO/EHLO information
smtpd_helo_required = yes
smtpd_helo_restrictions = permit_mynetworks, warn_if_reject reject_non_fqdn_hostname, reject_invalid_hostname, permit

# Sender Restrictions - Reject - MAIL FROM
smtpd_sender_restrictions = permit_mynetworks, permit_sasl_authenticated, check_sender_access hash:/etc/postfix/sender_checks, warn_if_reject reject_non_fqdn_sender, reject_authenticated_sender_login_mismatch, reject_unknown_sender_domain, reject_unauth_pipelining, permit

# Client Restrictions - Connecting server - Reject client host
#smtpd_client_restrictions = reject_rbl_client relays.ordb.org, reject_rbl_client blackholes.easynet.nl, reject_rbl_client cbl.abuseat.org, reject_rbl_client proxies.blackholes.wirehub.net, reject_rbl_client bl.spamcop.net, reject_rbl_client sbl.spamhaus.org, reject_rbl_client opm.blitzed.org, reject_rbl_client dnsbl.njabl.org, reject_rbl_client list.dsbl.org, reject_rbl_client multihop.dsbl.org, permit

smtpd_client_restrictions = check_client_access hash:/etc/postfix/client_checks, reject_rbl_client sbl.spamhaus.org, reject_rbl_client blackholes.easynet.nl, reject_rbl_client dnsbl.njabl.org

# Recipient Restrictions - Reject - RCPT TO
smtpd_recipient_restrictions = permit_mynetworks, permit_sasl_authenticated, reject_non_fqdn_hostname, reject_non_fqdn_sender, reject_non_fqdn_recipient, reject_unknown_recipient_domain, reject_unauth_destination, reject_unauth_pipelining, reject_invalid_hostname, check_policy_service unix:private/policy-spf, check_policy_service inet:127.0.0.1:10023, permit

# Reject - DATA
smtpd_data_restrictions = reject_unauth_pipelining

# Relay Restrictions - Reject - RCPT TO
smtpd_relay_restrictions = permit_mynetworks, reject_unauth_pipelining, 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

# General parameters
# -----------------------
myhostname = mail.example.com
alias_maps = hash:/etc/aliases
alias_database = hash:/etc/aliases
mydomain = mail.example.com
myorigin = /etc/mailname
#mydestination = $myhostname, mail.example.com, localhost.example.com, , localhost
mydestination = localhost.$mydomain, , 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
message_size_limit = 10240000

# Dovecot
# -----------------------
virtual_transport = lmtp:unix:private/dovecot-lmtp

# Virtual Mailbox
# -----------------------
virtual_uid_maps = static:5000
virtual_gid_maps = static:5000
virtual_mailbox_base = /<path to vmail>/vmail
virtual_mailbox_domains = mysql:/etc/postfix/mysql_virtual_mailbox_domains.cf
virtual_mailbox_maps = mysql:/etc/postfix/mysql_virtual_mailbox_maps.cf
virtual_alias_maps = mysql:/etc/postfix/mysql_virtual_alias_maps.cf
relay_domains = mysql:/etc/postfix/mysql_relay_domains.cf

queue_directory = /var/spool/postfix

# Header Checks
# -----------------------
header_checks = regexp:/etc/postfix/header_checks
mime_header_checks = regexp:/etc/postfix/mime_header_checks

# x-original-to
# -----------------------
enable_original_recipient = no

# Content Checks
# -----------------------
body_checks = regexp:/etc/postfix/body_checks

# Amavis
# -----------------------
content_filter = smtp-amavis:[127.0.0.1]:10024
#content_filter = amavis:[127.0.0.1]:10024
receive_override_options = no_address_mappings

# DKIM
# -----------------------
milter_protocol = 6
milter_default_action = accept
smtpd_milters = inet:localhost:8891
non_smtpd_milters = inet:localhost:8891

Configure Postfix Master - Now configure the master file of Postfix as shown below.

# Configure Postfix Master
sudo nano /etc/postfix/master.cf

The complete configurations are shown below.

#
# Postfix master process configuration file. For details on the format
# of the file, see the master(5) manual page (command: "man 5 master" or
# on-line: http://www.postfix.org/master.5.html).
#
# Do not forget to execute "postfix reload" after editing this file.
#
# ==========================================================================
# service type private unpriv chroot wakeup maxproc command + args
# (yes) (yes) (no) (never) (100)
# ==========================================================================
smtp inet n - y - - smtpd
#smtp inet n - y - 1 postscreen
#smtpd pass - - y - - smtpd
#dnsblog unix - - y - 0 dnsblog
#tlsproxy unix - - y - 0 tlsproxy
#submission inet n - y - - 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=$mua_client_restrictions
# -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
#smtps inet n - y - - smtpd
# -o syslog_name=postfix/smtps
# -o smtpd_tls_wrappermode=yes
# -o smtpd_sasl_auth_enable=yes
# -o smtpd_reject_unlisted_recipient=no
# -o smtpd_client_restrictions=$mua_client_restrictions
# -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

# SMTP with TLS on port 587
submission inet n - - - - smtpd
-o syslog_name=postfix/submission
-o smtpd_tls_security_level=encrypt
-o smtpd_sasl_auth_enable=yes
-o smtpd_sasl_type=dovecot
-o smtpd_tls_auth_only=yes
-o smtpd_client_restrictions=permit_sasl_authenticated,reject_unauth_destination,reject
-o smtpd_sasl_tls_security_options=noanonymous
-o milter_macro_daemon_name=ORIGINATING

# 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_client_restrictions=permit_sasl_authenticated,reject_unauth_destination,reject
-o smtpd_sasl_security_options=noanonymous,noplaintext
-o smtpd_sasl_tls_security_options=noanonymous
-o milter_macro_daemon_name=ORIGINATING

#628 inet n - y - - qmqpd
#pickup unix n - y 60 1 pickup
pickup fifo n - - 60 1 pickup
-o content_filter=
-o receive_override_options=no_header_body_checks
cleanup unix n - y - 0 cleanup
qmgr unix n - n 300 1 qmgr
#qmgr unix n - n 300 1 oqmgr
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
-o syslog_name=postfix/$service_name
# -o smtp_helo_timeout=5 -o smtp_connect_timeout=5
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
postlog unix-dgram n - n - 1 postlogd
#
# ====================================================================
# 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}

# Amavis
smtp-amavis unix - - - - 2 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 - - - - 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
dovecot unix - n n - - pipe
flags=DRhu user=vmail:mail argv=/usr/lib/dovecot/dovecot-lda -d $(recipient)

# SPF
policy-spf unix - n n - - spawn
user=nobody argv=/usr/bin/policyd-spf

# SpamAssassin
spamassassin unix - n n - - pipe
flags=DROhu user=vmail:vmail argv=/usr/bin/spamc -f -e /usr/sbin/sendmail -oi -f ${sender} ${recipient}

Configure Dovecot

This section provides the configurations specific to Dovecot. Change the ownership of the dovecot installation as shown below.

sudo chown -R vmail:dovecot /etc/dovecot
sudo chmod -R o-rwx /etc/dovecot

Now backup the configuration files as shown below.

sudo cp /etc/dovecot/dovecot.conf /etc/dovecot/dovecot.conf.orig
sudo cp /etc/dovecot/dovecot-sql.conf.ext /etc/dovecot/dovecot-sql.conf.ext.orig
sudo cp /etc/dovecot/conf.d/10-auth.conf /etc/dovecot/conf.d/10-auth.conf.orig
sudo cp /etc/dovecot/conf.d/10-mail.conf /etc/dovecot/conf.d/10-mail.conf.orig
sudo cp /etc/dovecot/conf.d/10-master.conf /etc/dovecot/conf.d/10-master.conf.orig
sudo cp /etc/dovecot/conf.d/10-ssl.conf /etc/dovecot/conf.d/10-ssl.conf.orig
sudo cp /etc/dovecot/conf.d/15-lda.conf /etc/dovecot/conf.d/15-lda.conf.orig
sudo cp /etc/dovecot/conf.d/auth-sql.conf.ext /etc/dovecot/conf.d/auth-sql.conf.orig
sudo cp /etc/dovecot/conf.d/15-mailboxes.conf /etc/dovecot/conf.d/15-mailboxes.conf.orig
sudo cp /etc/dovecot/conf.d/20-lmtp.conf /etc/dovecot/conf.d/20-lmtp.conf.orig
sudo cp /etc/dovecot/conf.d/90-plugin.conf /etc/dovecot/conf.d/90-plugin.conf.orig

Now we will configure the files as listed above.

Main Configuration - Update the main configuration file of Dovecot as shown below.

# Main Configuration
sudo nano /etc/dovecot/dovecot.conf

# Updates
-----
# Enable installed protocols
!include_try /usr/share/dovecot/protocols.d/*.protocol
protocols = imap pop3 lmtp
-----

# Save and exit the editor

Mail Configuration - Update the mail configuration and specify the mails directory.

# Mail Configuration
sudo nano /etc/dovecot/conf.d/10-mail.conf

# Updates
-----
#mail_location = mbox:~/mail:INBOX=/var/mail/%u
mail_location = maildir:/<path to vmail>/vmail/%d/%n
-----
mail_uid = vmail
mail_gid = vmail
-----
#mail_privileged_group = mail
mail_privileged_group = vmail
-----
first_valid_uid = 5000
last_valid_uid = 5000
-----

# Save and exit the editor

Auth Configuration - Update the auth configuration.

# Auth Configuration
sudo nano /etc/dovecot/conf.d/10-auth.conf

# Updates
-----
disable_plaintext_auth = yes
auth_mechanisms = plain login
-----
#!include auth-system.conf.ext
!include auth-sql.conf.ext
#!include auth-ldap.conf.ext
-----

# Save and exit the editor

SQL Auth Configuration - Update the auth configuration.

# Mail Configuration
sudo nano /etc/dovecot/conf.d/auth-sql.conf.ext

# Updates
-----
#userdb {
# driver = sql
# args = /etc/dovecot/dovecot-sql.conf.ext
#}
-----
userdb {
driver = static
args = uid=vmail gid=vmail home=/<path to vmail>/vmail/%d/%n
}

# Save and exit the editor

DB Configuration - Update the database configuration.

# Mail Configuration
sudo nano /etc/dovecot/dovecot-sql.conf.ext

# Updates
-----
driver = mysql
-----
connect = host=localhost dbname=mail user=mail password=dbpassword
-----
default_pass_scheme = SHA512-CRYPT
-----
password_query = \
SELECT username as user, password, '/<path to vmail>/vmail/%d/%n' as userdb_home, \
'maildir:/<path to vmail>/vmail/%d/%n' as userdb_mail, 5000 as userdb_uid, 5000 as userdb_gid \
FROM mailbox WHERE username = '%u' AND active = '1'
-----

# Save and exit the editor

Master Configuration - Update the master configuration.

# Master Configuration
sudo nano /etc/dovecot/conf.d/10-master.conf

# Updates
-----
service imap-login {
inet_listener imap {
port = 143
}
inet_listener imaps {
port = 993
ssl = yes
}
-----
service pop3-login {
inet_listener pop3 {
port = 110
}
inet_listener pop3s {
port = 995
ssl = yes
}

service submission-login {
inet_listener submission {
port = 587
}
}

service lmtp {
unix_listener /var/spool/postfix/private/dovecot-lmtp {
mode = 0600
user = postfix
group = postfix
}
-----
service auth {
-----
unix_listener auth-userdb {
mode = 0600
user = vmail
group = vmail
}

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

# Auth process is run as this user.
user = dovecot
-----
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 = vmail
}
-----

# Save and exit the editor

SSL Configuration - Update the SSL configuration.

# SSL Configuration
sudo nano /etc/dovecot/conf.d/10-ssl.conf

# Updates
-----
ssl = yes
-----
#ssl_cert = </etc/dovecot/private/dovecot.pem
#ssl_key = </etc/dovecot/private/dovecot.key
ssl_cert = </etc/letsencrypt/live/mail.example.com/fullchain.pem
ssl_key = </etc/letsencrypt/live/mail.example.com/privkey.pem
-----
ssl_ca = </etc/letsencrypt/live/mail.example.com/chain.pem
-----
ssl_min_protocol = TLSv1
-----
# Refer - https://hynek.me/articles/hardening-your-web-servers-ssl-ciphers/
ssl_cipher_list = ECDH+AESGCM:ECDH+CHACHA20:DH+AESGCM:ECDH+AES256:DH+AES256:ECDH+AES128:DH+AES:RSA+AESGCM:RSA+AES:!aNULL:!MD5:!DSS:!AESCCM
-----
ssl_prefer_server_ciphers = yes
-----

# Save and exit the editor

LDA Configuration - Update the LDA configuration.

# LDA Configuration
sudo nano /etc/dovecot/conf.d/15-lda.conf

# Updates
-----
postmaster_address = postmaster@example.com
-----

# Save and exit the editor

Configure Postgrey, Clam, Amavis, SpamAssassin, and OpenDKIM

We can whitelist the well know clients as shown below.

# Whitelist Clients
sudo nano /etc/postgrey/whitelist_clients.local

# Clients List
gmail.com
yahoo.com
outlook.com
facebook.com
hotmail.com
msn.com
linkedin.com
pinterest.com
reddit.com
twitter.com

# Save and exit the editor

Create the Clam and Amavis users as shown below.

sudo adduser clamav amavis
sudo adduser amavis clamav

Configure the Amavis as shown below.

# Update filter mode
sudo nano /etc/amavis/conf.d/15-content_filter_mode

# Update
-----
@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);
-----

# Update User
sudo nano /etc/amavis/conf.d/50-user

# Update
-----
$max_servers = 3;
$sa_tag_level_deflt = -9999;

@lookup_sql_dsn = (
['DBI:mysql:database=mail;host=127.0.0.1;port=3306',
'mail',
'dbpassword']);
$sql_select_policy = 'SELECT domain from domain WHERE CONCAT("@",domain) IN (%k)';
-----
-----

Configure SpamAssassin as shown below.

# Enable SpamAssassin
sudo update-rc.d spamassassin enable

# Update Config
sudo nano /etc/default/spamassassin

# Update
CRON=1

Refresh the Clam Antivirus database.

sudo /etc/init.d/clamav-freshclam stop
sudo freshclam -v
sudo /etc/init.d/clamav-freshclam start

Now configure OpenDKIM as shown below.

# Configure OpenDKIM
sudo nano /etc/default/opendkim

# Comment out
#SOCKET=local:$RUNDIR/opendkim.sock

# Add at last
SOCKET="inet:8891@localhost"

# Save and exit the editor

# Configure OpenDKIM
sudo nano /etc/opendkim.conf

# Add at the last
SOCKET inet:8891@localhost

# Save and exit the editor

Configure Monit

This section provides the steps to configure Monit. You can follow How To Install And Configure Monit On Ubuntu 20.04 LTS.

Amavis - Add the configurations for Amavis as shown below.

# Amavis Configurations
sudo nano /etc/monit/conf.d/amavis

# Content
check process amavisd with pidfile /var/run/amavis/amavisd.pid
every 5 cycles
group mail
start program = "/usr/sbin/service amavis start"
stop program = "/usr/sbin/service amavis stop"
if failed port 10024 protocol smtp then restart
if 5 restarts within 25 cycles then timeout

Dovecot - Add the configurations for Dovecot as shown below.

# Dovecot Configurations
sudo nano /etc/monit/conf.d/dovecot

# Content
check process dovecot with pidfile /var/run/dovecot/master.pid
group mail
start program = "/usr/sbin/service dovecot start"
stop program = "/usr/sbin/service dovecot stop"
group mail
if failed port 993 for 5 cycles then restart
if 5 restarts within 25 cycles then timeout

Postfix - Add the configurations for Postfix as shown below.

# Postfix Configurations
sudo nano /etc/monit/conf.d/postfix

# Content
check process postfix with pidfile /var/spool/postfix/pid/master.pid
group mail
start program = "/usr/sbin/service postfix start"
stop program = "/usr/sbin/service postfix stop"
if failed port 25 protocol smtp then restart
if 5 restarts within 5 cycles then timeout

SpamAssassin - Add the configurations for SpamAssassin as shown below.

# SpamAssassin Configurations
sudo nano /etc/monit/conf.d/spamassassin

# Content
check process spamassassin with pidfile /var/run/spamassassin.pid
group mail
start program = "/usr/sbin/service spamassassin start"
stop program = "/usr/sbin/service spamassassin stop"
if 5 restarts within 5 cycles then timeout

Configure Fail2ban

This section provides the steps to configure Fail2ban for Postfix and Dovecot. You can follow How To Install Fail2ban On Ubuntu 20.04 LTS.

Default - Configure the DEFAULT block to send emails for the failed attempts as shown below. Make sure to create the email accounts for destemail and sender while installing the Postfix Admin.

# Local Jail
sudo nano /etc/fail2ban/jail.local

# Update
[DEFAULT]
mta = sendmail
destemail = netban@example.com
sendername = Admin
sender = admin@example.com
action = %(action_mwl)s

# Save and exit the editor

Postfix - The default filter of Postfix for Fail2ban is available at /etc/fail2ban/filter.d/postfix.conf. You may further update it if required. Now add the postfix jail as shown below.

# Add Postfix Jail
sudo nano /etc/fail2ban/jail.d/postfix.conf

# Content
[postfix]
enabled = true
logpath = /var/log/mail.log

Dovecot - The default filter of Postfix for Fail2ban is available at /etc/fail2ban/filter.d/dovecot.conf. You may further update it if required. Now add the dovecot jail as shown below.

# Add Dovecot Jail
sudo nano /etc/fail2ban/jail.d/dovecot.conf

# Content
[dovecot]
enabled = true
port = pop3,pop3s,imap,imaps
filter = dovecot
logpath = /var/log/mail.log
maxretry = 3

Restart Services

Now restart all the services as shown below.

# Restart Apache
sudo service apache2 restart

# Apache Status
sudo service apache2 status

# Output
apache2.service - The Apache HTTP Server Loaded: loaded (/lib/systemd/system/apache2.service; enabled; vendor preset: enabled) Active: active (running) since Wed 2020-06-24 14:39:08 UTC; 4s ago Docs: https://httpd.apache.org/docs/2.4/ Process: 600540 ExecStart=/usr/sbin/apachectl start (code=exited, status=0/SUCCESS) Main PID: 600561 (apache2) Tasks: 6 (limit: 4622) Memory: 33.9M CGroup: /system.slice/apache2.service
-----

# Restart Postfix
sudo service postfix restart

# Postfix Status
sudo service postfix status

# Output - It shows status active (exited) - a known bug in debian, but postfix works fine
postfix.service - Postfix Mail Transport Agent Loaded: loaded (/lib/systemd/system/postfix.service; enabled; vendor preset: enabled) Active: active (exited) since Wed 2020-06-24 14:39:29 UTC; 5s ago Process: 600985 ExecStart=/bin/true (code=exited, status=0/SUCCESS) Main PID: 600985 (code=exited, status=0/SUCCESS) Jun 24 14:39:29 mail.example.com systemd[1]: Starting Postfix Mail Transport Agent... Jun 24 14:39:29 mail.example.com systemd[1]: Finished Postfix Mail Transport Agent.

# Restart Dovecot
sudo service dovecot restart

# Dovecot Status
sudo service dovecot status

# Output
dovecot.service - Dovecot IMAP/POP3 email server Loaded: loaded (/lib/systemd/system/dovecot.service; enabled; vendor preset: enabled) Active: active (running) since Wed 2020-06-24 14:40:17 UTC; 22s ago Docs: man:dovecot(1) http://wiki2.dovecot.org/ Main PID: 601200 (dovecot) Tasks: 4 (limit: 4622) Memory: 3.5M CGroup: /system.slice/dovecot.service ----

# Restart Clam Antivirus
sudo service clamav-daemon restart

# Restart Amavis
sudo service amavis restart

# Restart SmapAssassin
sudo service spamassassin restart

# Start OpenDKIM
sudo service opendkim start

# Postgrey reload fails - patch available at - https://bugs.debian.org/cgi-bin/bugreport.cgi?att=1;bug=934068;filename=postgrey_init.patch
#sudo service postgrey reload

# Restart PostGrey
sudo service postgrey restart

# Restart Fail2ban
sudo service fail2ban restart

# Fail2ban Status
sudo service fail2ban status

# Output
fail2ban.service - Fail2Ban Service Loaded: loaded (/lib/systemd/system/fail2ban.service; enabled; vendor preset: enabled) Active: active (running) since Wed 2020-06-24 14:41:20 UTC; 4s ago Docs: man:fail2ban(1) Process: 601702 ExecStartPre=/bin/mkdir -p /run/fail2ban (code=exited, status=0/SUCCESS) Main PID: 601710 (f2b/server) Tasks: 23 (limit: 4622) Memory: 15.2M CGroup: /system.slice/fail2ban.service └─601710 /usr/bin/python3 /usr/bin/fail2ban-server -xf start ----

# Restart Monit
sudo service monit restart

Load Postfix maps.

sudo postmap /etc/postfix/client_checks
sudo postmap /etc/postfix/sender_checks
sudo postfix reload

# Output
postfix/postfix-script: refreshing the Postfix mail system

Open the Ports

After completing the installation of all the software and utilities, we can open the email ports by updating the firewall. You can either update the firewall provided by your hosting provider or use the UFW to open the ports 25, 465, 587, 995, 143, and 993.

Install Postfix Admin

This section provides the steps to install Postfix Admin. We can use the Postfix Admin to manage the virtual domains and virtual users. Now download and install the most recent version of Postfix Admin from SourceForge as shown below.

# Download Postfix Admin
sudo wget https://sourceforge.net/projects/postfixadmin/files/postfixadmin/postfixadmin-3.2/postfixadmin-3.2.tar.gz

# Extract Postfix Admin
sudo tar -xf postfixadmin-3.2.tar.gz

# Move to www
sudo mkdir -p /var/www/mailadmin.example.com/
sudo mv postfixadmin-3.2 /var/www/mailadmin.example.com/html
sudo chown -R www-data:www-data /var/www/mailadmin.example.com/html

# Create Templates Directory
sudo mkdir -p /var/www/mailadmin.example.com/html/templates_c
sudo chown -R www-data:www-data /var/www/mailadmin.example.com/html/templates_c

The above-mentioned commands installed Postfix Admin at /var/www/mailadmin.example.com/html. Now add the virtual hosts as shown below.

# Virtual Host - HTTP
sudo nano /etc/apache2/sites-available/mailadmin.example.com.conf

# Content
<VirtualHost *:80>
ServerName mailadmin.example.com
ServerAlias mailadmin.example.com
ServerAdmin admin@example.com

DocumentRoot /data/www/mailadmin.example.com/html/public
<Directory /data/www/mailadmin.example.com/html/public>
Options -Indexes +FollowSymLinks
DirectoryIndex index.php
AllowOverride All
Require all granted
</Directory>

RewriteEngine on
RewriteCond %{SERVER_NAME} =mailadmin.example.com
RewriteRule ^ https://%{SERVER_NAME}%{REQUEST_URI} [END,NE,R=permanent]
</VirtualHost>

In case you have used Let's Encrypt to install an SSL certificate, you can update your certificate by adding the mailadmin subdomain as shown below. It will also generate and enable the virtual host for SSL.

# Upgrade SSL Certificate
sudo letsencrypt --apache -d example.com,www.example.com,mail.example.com,mailadmin.example.com --email admin@example.com --agree-tos

# Output
-----
Do you want to expand and replace this existing certificate with the new
certificate?
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
(E)xpand/(C)ancel: e
-----
Please choose whether or not to redirect HTTP traffic to HTTPS, removing HTTP access.
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1: No redirect - Make no further changes to the webserver configuration.
2: Redirect - Make all requests redirect to secure HTTPS access. Choose this for
new sites, or if you're confident your site works on HTTPS. You can undo this
change by editing your web server's configuration.
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Select the appropriate number [1-2] then [enter] (press 'c' to cancel): 2
-----
IMPORTANT NOTES: - Congratulations! Your certificate and chain have been saved at: /etc/letsencrypt/live/example.com/fullchain.pem Your key file has been saved at: /etc/letsencrypt/live/example.com/privkey.pem
-----

You can also add the virtual host for SSL, in case you have generated a self-signed certificate or obtained the SSL certificate from the well-known providers. The virtual host for SSL should be similar as shown below.

# Virtual Host - HTTPS
sudo nano /etc/apache2/sites-available/mailadmin.example.com-le-ssl.conf

# Content
<IfModule mod_ssl.c>
<VirtualHost *:443>
ServerName mailadmin.example.com
ServerAlias mailadmin.example.com
ServerAdmin admin@example.com

DocumentRoot /data/www/mailadmin.example.com/html/public
<Directory /data/www/mailadmin.example.com/html/public>
Options -Indexes +FollowSymLinks
DirectoryIndex index.php
AllowOverride All
Require all granted
</Directory>

SSLCertificateFile /etc/letsencrypt/live/example.com/fullchain.pem
SSLCertificateKeyFile /etc/letsencrypt/live/example.com/privkey.pem
Include /etc/letsencrypt/options-ssl-apache.conf
</VirtualHost>
</IfModule>

Now we will configure Postfix Admin to use the database created by us in the previous sections.

# Postfix Admin - Local Config
sudo nano /var/www/mailadmin.example.com/html/config.local.php

# Content
<?php
$CONF['configured'] = true;

$CONF['postfix_admin_url'] = 'https://mailadmin.example.com';

// Database connection details.
$CONF['database_type'] = 'mysqli';
$CONF['database_host'] = 'localhost';
$CONF['database_user'] = 'mail';
$CONF['database_password'] = 'dbpassword';
$CONF['database_name'] = 'mail';

$CONF['admin_email'] = 'admin@example.com';

$CONF['smtp_server'] = 'localhost';
$CONF['smtp_port'] = '25';

$CONF['encrypt'] = 'md5crypt';

$CONF['default_aliases'] = [
'abuse' => 'admin@example.com',
'hostmaster' => 'admin@example.com',
'postmaster' => 'admin@example.com',
'webmaster' => 'admin@example.com'
];

$CONF['show_footer_text'] = 'YES';
$CONF['footer_text'] = 'Return to example.com';
$CONF['footer_link'] = 'https://www.example.com';


$CONF['domain_path'] = 'NO';
$CONF['domain_in_mailbox'] = 'YES';
$CONF['create_mailbox_subdirs_prefix']='';

# Save and exit the editor

sudo chown -R www-data:www-data /var/www/mailadmin.example.com/html/config.local.php

Now we can access the mail admin using the URL - https://mailadmin.example.com/setup.php. It will run the migration scripts and add the database tables using the configurations provided by us. The setup.php should be similar to Fig 5 and Fig 6.

Install Postfix Admin On Ubuntu 20.04 LTS - Set Up

Fig 5

Install Postfix Admin On Ubuntu 20.04 LTS - Set Up

Fig 6

Now provided the Setup password as shown in Fig 6 and click the Generate password hash button. It will generate the password hash and provide the configuration option as shown below.

$CONF['setup_password'] = 'a115c57a6d1d42b32dbb7dc57335c9f6:926c3682w5b3f2bb72aaf3cc4f86b567c17ae8ok';

Now copy the configuration line and add it to the end of the local config file - /var/www/mailadmin.example.com/html/config.local.php. It also provides the options to configure the superadmin account as shown in Fig 7.

Install Postfix Admin On Ubuntu 20.04 LTS - Super Admin

Fig 7

Fill the superadmin account form by providing the Setup password, admin email, and admin password. The admin email could be admin@example.com. After filling the form, click the Add Admin Button to create the super admin. Also, log in to Postfix Admin by using the URL - https://mailadmin.example.com/login.php. It shows the dashboard as shown in Fig 8.

Install Postfix Admin On Ubuntu 20.04 LTS - Dashboard

Fig 8

Hover the Domain List option on the Main Menu and click the New Domain option. Specify the domain name as example.com and click the Add Domain Button. It will add the virtual domain example.com.

Hover the Virtual List option on the Main Menu and click Add Mailbox option. Specify the username as admin, fill other details and click the Add Mailbox Button. It will add the virtual user admin@example.com. Similarly, add another mailbox netban@example.com as configured with Fail2ban to receive all the emails from it.

Notes: There is a known bug in PostfixAdmin as shown below.

Invalid query: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'row FROM `mailbox` LEFT JOIN `alias` ON `mailbox`.username=`alias`.address L' at line 2

In case you get the above error, it means you have already created 10 mailboxes for a domain. The workout is to increase the pagination size as shown below.

# Postfix Admin - Local Config
sudo nano /var/www/mailadmin.example.com/html/config.local.php

# Add configuration
-----
$CONF['create_mailbox_subdirs_prefix']='';
-----
$CONF['page_size'] = '1000';
-----

Configure DKIM

This section provides the steps to generate the DKIM signature for example.com. Though we can use DKIM for multiple domains, this section only provides the configurations for the single domain as shown below.

# Update OpenDKIM Conf
sudo nano /etc/opendkim.conf

# Add at the last
Domain example.com
KeyFile /etc/postfix/dkim.key
Selector dkim

Now generate the domain key as shown below.

cd /mydata/secure
sudo mkdir dkim
sudo opendkim-genkey -t -s dkim -d example.com

sudo mv dkim.private /etc/postfix/dkim.key
sudo chmod 660 /etc/postfix/dkim.key
sudo chown root:opendkim /etc/postfix/dkim.key

# Restart OpenDKIM
sudo service opendkim restart

# Reload and restart Postfix
sudo service postfix reload
sudo service postfix restart

We are also required to update the DNS record by adding the txt record as shown below.

# Read DKIM record
sudo cat dkim.txt

# Output
dkim._domainkey IN TXT ( "v=DKIM1; h=sha256; k=rsa; t=y; "
"p=KAABIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA/fedRNEFQvCdtN0akUCMG686J7Kv7DfjP6CBNYbq0zppCF+gEnXmeRIAG1BNGtqE0DnpiOaePwXpuAC+izWHE4pBltSwczhTOz7dNHxQV3YmPs3pg12Zqm4ARuD9sCdJky/Tz+uPHUYp8GUuAJPOmqmg3lWw9AooPOYfJMLte5BeQ7KtSiyxirT5VfZdYj0VJXvvlIKT8X92OY"
"WN8G0212XiFLyyQuxJixQL04BMG0bvBW8xrNDiNuiAkDGea/nUxKRMnuVKOvAa5JAhi/hNikCOP9NCibllwZLlS2E94bY9FVw+ymbBt0f4MMn/Y2LBLfEhLZq0AAx0KXkpPpkWbQIDLSRP" ) ; ----- DKIM key dkim for example.com

Remove all the double quotes and generate a single line record as shown below.

# Valid DKIM
v=DKIM1;p=KAABIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA/fedRNEFQvCdtN0akUCMG686J7Kv7DfjP6CBNYbq0zppCF+gEnXmeRIAG1BNGtqE0DnpiOaePwXpuAC+izWHE4pBltSwczhTOz7dNHxQV3YmPs3pg12Zqm4ARuD9sCdJky/Tz+uPHUYp8GUuAJPOmqmg3lWw9AooPOYfJMLte5BeQ7KtSiyxirT5VfZdYj0VJXvvlIKT8X92OYWN8G0212XiFLyyQuxJixQL04BMG0bvBW8xrNDiNuiAkDGea/nUxKRMnuVKOvAa5JAhi/hNikCOP9NCibllwZLlS2E94bY9FVw+ymbBt0f4MMn/Y2LBLfEhLZq0AAx0KXkpPpkWbQIDLSRP

Add a TXT record as shown below.

Name				Type	Value 			TTL  
dkim._domainkey.example.com. TXT "single line record" 300

This completes the DKIM configuration for example.com. Now we will verify the DKIM record using the keycheck utility. Paste your DKIM record to the Key record textarea and click the Check Button. It should successfully verify your DKIM record as shown in Fig 9.

Verify DKIM Record - Ubuntu 20.04 LTS

Fig 9

Check Email Server Health

This section provides the steps to check the emails and also check the email server health using the standard services.

DKIM Test - We can perform the DKIM testing using DKIM Test. Login to your Postfix Admin and Hover the Send Email option on the Main Menu and click Send Email option. It will show the form to send an email. Now click the Next Step Button after opening the DKIM Test webpage. It will generate the endpoint to receive the email. Now send an email to this endpoint using the Postfix Admin - Send Email form. It should successfully pass the DKIM test as shown in Fig 10.

DKIM Testing - Ubuntu 20.04 LTS

Fig 10

Now send an email to Gmail and check the Mailed By and Signed By parameters as shown in Fig 11.

DKIM Testing - Gmail - Ubuntu 20.04 LTS

Fig 11

This ensures that your emails are properly signed by your domain.

MXToolbox Test - Open MX Lookup Page, provide your domain name, and click MX Lookup Button. It will perform MX lookup and shows the MX record details. Now click the Find Problems Button. It will show the problems associated with your email server. You may further fine-tune your email server in case errors or warnings are listed by MXToolbox.

ESG Web Tool - Similar to MXToolbox, we can also use ESG Web Tool to check the Mail Server health. The ESG report of the email server setup done while writing this tutorial is shown in Fig 12.

ESG Web Tool - Ubuntu 20.04 LTS

Fig 12

Install RoundCube

In the previous sections, we have installed the software and tools required to complete the set up of an email server on Ubuntu 20.04 LTS using Postfix and Dovecot. We have also installed the mail admin to manage the virtual domains and users. Apart from all these software and tools, we also need an interface so that the email users can access their emails and send emails. This section provides the steps to install the popular web interface i.e. RoundCube on Ubuntu 20.04 LTS. The below-mentioned steps are required to install RoundCube on Ubuntu. We can also download RoundCube from the Official Website.

# Download RoundCube
sudo wget https://github.com/roundcube/roundcubemail/releases/download/1.4.6/roundcubemail-1.4.6-complete.tar.gz

# Extract RoundCube
sudo tar xvfz roundcubemail-1.4.6-complete.tar.gz

# Install RoundCube
sudo mkdir /var/www/webmail.example.com/
sudo mv roundcubemail-1.4.6 /var/www/webmail.example.com/html
sudo chown -R www-data:www-data /var/www/webmail.example.com/html

The above-mentioned commands installed RoundCube at /var/www/webmail.example.com/html. Now add the virtual hosts as shown below.

# Virtual Host - HTTP
sudo nano /etc/apache2/sites-available/webmail.example.com.conf

# Content
<VirtualHost *:80>
ServerName webmail.example.com
ServerAlias webmail.example.com
ServerAdmin admin@example.com

DocumentRoot /data/www/webmail.example.com/html
<Directory /data/www/webmail.example.com/html>
Options -Indexes +FollowSymLinks
DirectoryIndex index.php
AllowOverride All
Require all granted
</Directory>

RewriteEngine on
RewriteCond %{SERVER_NAME} =webmail.example.com
RewriteRule ^ https://%{SERVER_NAME}%{REQUEST_URI} [END,NE,R=permanent]
</VirtualHost>

In case you have used Let's Encrypt to install an SSL certificate, you can update your certificate by adding the webmail subdomain as shown below. It will also generate and enable the virtual host for SSL.

# Upgrade SSL Certificate
sudo letsencrypt --apache -d example.com,www.example.com,mail.example.com,mailadmin.example.com,webmail.example.com --email admin@example.com --agree-tos

# Output
-----
Do you want to expand and replace this existing certificate with the new
certificate?
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
(E)xpand/(C)ancel: e
-----
Please choose whether or not to redirect HTTP traffic to HTTPS, removing HTTP access.
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1: No redirect - Make no further changes to the webserver configuration.
2: Redirect - Make all requests redirect to secure HTTPS access. Choose this for
new sites, or if you're confident your site works on HTTPS. You can undo this
change by editing your web server's configuration.
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Select the appropriate number [1-2] then [enter] (press 'c' to cancel): 2
-----
IMPORTANT NOTES: - Congratulations! Your certificate and chain have been saved at: /etc/letsencrypt/live/example.com/fullchain.pem Your key file has been saved at: /etc/letsencrypt/live/example.com/privkey.pem
-----

You can also add the virtual host for SSL, in case you have generated a self-signed certificate or obtained the SSL certificate from the well-known providers. The virtual host for SSL should be similar as shown below.

# Virtual Host - HTTPS
sudo nano /etc/apache2/sites-available/webmail.example.com-le-ssl.conf

# Content
<IfModule mod_ssl.c>
<VirtualHost *:443>
ServerName webmail.example.com
ServerAlias webmail.example.com
ServerAdmin admin@example.com

DocumentRoot /data/www/webmail.example.com/html
<Directory /data/www/webmail.example.com/html>
Options -Indexes +FollowSymLinks
DirectoryIndex index.php
AllowOverride All
Require all granted
</Directory>

SSLCertificateFile /etc/letsencrypt/live/example.com/fullchain.pem
SSLCertificateKeyFile /etc/letsencrypt/live/example.com/privkey.pem
Include /etc/letsencrypt/options-ssl-apache.conf
</VirtualHost>
</IfModule>

Now we will configure RoundCube and test the login mechanism. Create the RoundCube database as shown below.

# Create User
CREATE USER 'roundcube'@'localhost' IDENTIFIED WITH caching_sha2_password BY 'rcpassword';

# Create Database
CREATE DATABASE roundcube CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci;

# Privileges
GRANT ALL PRIVILEGES ON roundcube.* TO 'roundcube'@'localhost';
FLUSH PRIVILEGES;

We can access RoundCube using the URL - https://webmail.example.com/installer. It makes several checks as shown in Fig 13.

Install RoundCube On Ubuntu 20.04 LTS - Installation Checks

Fig 13

Now click the NEXT Button to continue with the installation. The next page provides several options to configure RoundCube including the name and logo to be displayed on the web interface. It also provides the configuration options to configure the roundcube database created by us. Configure IMAP and SMTP as shown in Fig 14 and Fig 15.

Install RoundCube On Ubuntu 20.04 LTS - IMAP Settings

Fig 14

Install RoundCube On Ubuntu 20.04 LTS - SMTP Settings

Fig 15

Also, enable the plugins required by your RoundCube installation. I recommend having at least three plugins including archive, attachment_reminder, password. Now click the CREATE CONFIG Button to generate the configuration file. It will show the configuration path as shown in Fig 16.

Install RoundCube On Ubuntu 20.04 LTS - Configured

Fig 16

Click the CONTINUE Button to initialize the RoundCube database and test the connectivity. Click the Initialize Database button to initialize the RoundCube database. It initializes the database as shown in Fig 17.

Install RoundCube On Ubuntu 20.04 LTS - Database

Fig 17

Now try to send an email as shown in Fig 18. It should show the status OK.

Install RoundCube On Ubuntu 20.04 LTS - Send Email

Fig 18

Also, try to log in. It should show the OK message as shown in Fig 19.

Install RoundCube On Ubuntu 20.04 LTS - Test Login

Fig 19

In case you have enabled the password plugin, it can be configured as shown below.

# Mode to Password plugin
cd /var/www/webmail.example.com/site/plugins/password

# Copy config
sudo cp config.inc.php.dist config.inc.php

# Update Config
sudo nano config.inc.php

# Update
-----
$config['password_minimum_length'] = 8;
-----
$config['password_minimum_score'] = 3;
-----
$config['password_force_save'] = true;
-----
// Refer - $config['db_dsnw'] - /var/www/webmail.example.com/html/config/config.inc.php
$config['password_db_dsn'] = 'dsn from main config';
-----
$config['password_query'] = "UPDATE `mailbox` SET `password`= %c, modified=now() WHERE `username` = %u LIMIT 1";
-----

After configuring the password plugin, the virtual users can change their password from RoundCube. Now remove the installer folder from the RoundCube root, else disable installer by updating the main config file as shown below.

# Update Config
sudo nano /var/www/webmail.example.com/html/config/config.inc.php

# Add configuration
$config['enable_installer' ] = false;

# Save and exit the editor

Now login to RoundCube using the URL - https://webmail.example.com. It should log in and shows the dashboard as shown in Fig 20.

Install RoundCube On Ubuntu 20.04 LTS - Dashboard

Fig 20

This completes the installation of RoundCube on Ubuntu 20.04 LTS.

Summary

This tutorial provided all the steps and configurations required to set up and secure an email or mail server on Ubuntu 20.04 LTS using Postfix and Dovecot. It also provided the steps to install and configure the additional software, tools, and utilities including Clam as Antivirus, Amavis for Content Filter, SpamAssassin to detect spam emails, OpenDKIM, Postfix Admin, and Roundcube. It also provided the steps to configure Fail2ban and Monit for Postfix and Roundcube.

Write a Comment

Click on the captcha image to get new code.
Discussion Forum by DISQUS