Mail Server on Ubuntu 16.04 Part 2

Welcome to Mail Server on Ubuntu 16.04 Part 2. This is the second part of a series of blog posts. The mailserver will use Postfix, Dovecot and Amavis. To see Mail Server on Ubuntu 16.04 Part1 follow the link.

In Mail Server on Ubuntu 16.04 Part 2 we will turn on the ability for any user to send and receive emails once they are know to the system.

Installing Some Software

Make sure everything is up to date with the latest patches and security updates.

sudo apt-get update
sudo apt-get dist-upgrade

To install the required software we need packages for MySQL and how MySQL talks to both Postfix and Dovecot and also the lmtp extension for Dovecot. This will also pull in loads of other dependent packages.

sudo apt-get install postfix-mysql dovecot-mysql dovecot-lmtpd mysql-server 

When the installation process starts you will be asked for a MySQL root password. This is the password for the root user of MySQL this is not the same as the root user on the Linux system. YOU need to remember what YOU type in here.

MySQL Password, just make one up and remember it! I will use MYSQLPasswd01!.

Configuring MySQL

We will begin building Mail Server on Ubuntu 16.04 Part 2 by making MySQL a little more secure and making our own access a little simpler for this test server.

Simple MySQL Security Measures

Run the script mysql_secure_installation to remove unnecessary users and the test database. It will also allow you to setup a password for the root MySQL user. But you already did that, didn’t you.

mysql_secure_installation

This will ask a number of questions that will help strengthen the security of your MySQL database.

  • Enter password for user root: MYSQLPasswd01!
  • VALIDATE PASSWORD PLUGIN: Yes

Choose the level of security you want on this server. Low is okay on your test server but always use high on production servers.

Reset password: No
You already set a password during the installation.

For the remaining questions you can enter ‘Y or y

  • Remove anonymous users?
  • Disallow root login remotely?
  • Remove test database and access to it?
  • Reload privilege tables now?

Simplify logging into MySQL

We can make logging into MySQL simpler by creating a .my.cnf file in our HOME directory.

cd
nano .my.cnf
[client]
host = localhost
user = root
password = MYSQLPasswd01!

The file contains a password so make sure you change the permissions of that file so only you can read and write to it.

chmod 600 .my.cnf

Now all you have to do in order to log into a MySQL prompt is type mysql on the command line and the contents of the .my.cnf file will be read in and used. This is not a good idea on production servers. But while we are learning how to setup our server in a test area it saves time.

Configuring MySQL

We will edit the system MySQL mysqld.cnf file to check on access and get some logging to happen.

sudo nano /etc/mysql/mysql.conf.d/mysqld.cnf

The following should already be set. We want to bind to 127.0.0.1 not localhost. This stops connections from remote machines, which means that we have a security advantage in that only users already logged in to mailserver can access the MySQL database. It should already be set.

bind-address = 127.0.0.1

I find it is helpful to see what the MySQL DB is doing especially in the early stages. Use these options to start the logging of any queries or requests that are made to MySQL. It is probably commented out.

general_log_file = /var/log/mysql/mysql.log
general_log = 1

When all is running correctly comment those out again, as the logging slows MySQL down. Remember to turn these off on your production server when it goes live.

Restart the MySQL daemon.

sudo service mysql restart

Monitor the log files

To monitor what is going on behind the scenes with Postfix and MySQL we can watch the tail of the log files and syslog file. Open a new terminal and use the following command to follow the end of the log files as they are written. I leave this running all the time, it helps with debugging. Try it out, you will see what I mean. You can also run a tail on each file in its own terminal.

sudo tail -f /var/log/syslog /var/log/mysql/mysql.log /var/log/mail.log

Creating the Database

To create a MySQL database is very simple from the command line. We will be calling our database ‘postfix‘ as it is for Postfix and I like to keep things simple. 🙂 We need to login as the user root. If you setup the .my.cnf there is no need to use the -u root and -p command line switches. Create the database with the following:

mysqladmin create postfix

If you look at the output in the log files you will see a line saying “create database `postfix`”.
You can also check by logging into mysql and running the show databases command. If all went well you will see a line with postfix.

mysql
mysql> show databases;
+--------------------+
| Database           |
+--------------------+
| information_schema |
| mysql              |
| performance_schema |
| postfix            | =-- This is the one we are looking for
+--------------------+
4 rows in set (0.00 sec)

Creating our MySQL User

We will be needing a user with restricted permissions to access the tables for lookups. This user will only need to SELECT data they will never be INSERTing or UPDATEing data.

mysql

Enter the following SQL command when you see the mysql> prompt. Change the password to something complex on the live server it will only be used internally meaning you will rarely have to type it in yourself. Do not have a space in the password it makes adding the password to scripts harder. This command tells MySQL to create a new user called mail. That user will have a password of ‘MYSQLPasswd01!’ and can only use the SELECT clause on the database postfix.

GRANT SELECT ON postfix.* TO mail@localhost IDENTIFIED BY 'MYSQLPasswd01!';

This will create a new mysql user called mail who has SELECT access only to our database. To get these new permissions noticed by MySQL you need to flush them. So before you exit the mysql prompt use the following command.

flush privileges;

We can check that the user was created with the following query.

SELECT Host, User, authentication_string FROM mysql.user where User = 'mail';
Query OK, 0 rows affected (0.00 sec)

Reading table information for completion of table and column names
You can turn off this feature to get a quicker startup with -A

+-----------+------+-------------------------------------------+
| Host      | User | authentication_string                     |
+-----------+------+-------------------------------------------+
| localhost | mail | *14567890ABCDEFGH451C7ADACE12345678901234 |
+-----------+------+-------------------------------------------+
1 row in set (0.00 sec)

Creating the tables

We will be using three tables to interface between Postfix and MySQL. The way I create them is to add the SQL commands to a text file and then load them in via the mysql command. This saves a lot of typing. It also means that when you are testing and trying out different options it is easy to delete all the data and start again.

USE postfix;

DROP TABLE IF EXISTS user;
DROP TABLE IF EXISTS alias;
DROP TABLE IF EXISTS domain;

CREATE TABLE domain (
   domain VARCHAR(50) NOT NULL,
   PRIMARY KEY (domain)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;


CREATE TABLE user (
   email VARCHAR(255) NOT NULL,
   domain VARCHAR(50) NOT NULL,
   password VARCHAR(110) NOT NULL,
   PRIMARY KEY (email),
   KEY (domain),
   FOREIGN KEY (domain) REFERENCES domain(domain)
           ON UPDATE NO ACTION ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8;


CREATE TABLE alias (
   id int(11) unsigned NOT NULL AUTO_INCREMENT,
   source varchar(255) NOT NULL,
   destination varchar(255) NOT NULL,
   PRIMARY KEY (id),
   KEY source_idx (source),
   KEY dest_idx (destination)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

The user table holds as you probably guessed from its name, data about the actual users of our email server. That is their email address and password.
The domain table is the simplest and is really just a list of domains our mailserver will process emails for.
The alias table is used to lookup an email address and forward them to the real user address.

Create a blank file called create_postfix_mysql_tables.sql, add the text above and then save it. You can then import create_postfix_mysql_tables.sql and issue the commands it contains with the following:

mysql postfix < create_postfix_mysql_tables.sql

You should see the commands displayed in the tail from the log file if you have that running. If not you can see check the tables have now been created, by using the following commands:

mysql postfix
mysql> show tables;
+-------------------+
| Tables_in_postfix |
+-------------------+
| alias             |
| domain            |
| user              |
+-------------------+
3 rows in set (0.00 sec)
This would be a good backup point!

Postfix

We can now make some changes to the Postfix configuration files ready for Postfix to start looking at our new domain, users and alias database.

Configuring Postfix

The configuration for postfix is controlled via two files main.cf and master.cf.

sudo nano /etc/postfix/main.cf

Make sure the following are not commented out.

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

Amend the mydestination variable to only include the generic and local host and domain values. It should look simpler to this

mydestination = localhost.dragon.lab, localhost

I am assuming for this change that you are currently only using IPV4. If you do not know then most likely you are not using IPV6. Making this change will stop your mailserver from trying to connect using IPV6 and adding loads of warnings in your mail log file.

inet_protocols = ipv4

There are some new lines you will want, just add them to the end of the main.cf file.

# Postfix/TLS does not use the OpenSSL default of 300s, but a longer time of 3600sec 
# (=1 hour). RFC 2246 recommends a maximum of 24 hours. 
smtpd_tls_session_cache_timeout = 3600s

# We will let the client end use STARTTLS if they want, i.e they _may_ use it.
smtpd_tls_security_level = may

Add a new line to enable Dovercot's LMTP

virtual_transport = lmtp:unix:private/dovecot-lmtp

Stop our mailserver from using the insecure SSLv2 and SSLv3 protocols, find the first line and remove it or comment it out. Then add the lines below.

#smtpd_tls_mandatory_protocols = SSLv3, TLSv1
smtpd_tls_mandatory_protocols = !SSLv2, !SSLv3
smtpd_tls_protocols = !SSLv2, !SSLv3
smtp_tls_mandatory_protocols = !SSLv2, !SSLv3
smtp_tls_protocols = !SSLv2, !SSLv3

Save the changes and exit.

We can now edit the master.cf file.

sudo nano /etc/postfix/master.cf

Look for the following lines, they should all be commented out.

#submission inet n       -       y       -       -       smtpd
#  -o syslog_name=postfix/submission
#  -o smtpd_tls_security_level=encrypt
#  -o smtpd_sasl_auth_enable=yes
#  -o smtpd_reject_unlisted_recipient=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

We want them to look like these lines

submission inet n       -       y       -       -       smtpd
  -o syslog_name=postfix/submission
  -o smtpd_tls_security_level=encrypt
  -o smtpd_sasl_auth_enable=yes
  -o smtpd_reject_unlisted_recipient=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=permit_mynetworks,permit_sasl_authenticated,reject
  -o smtpd_relay_restrictions=permit_sasl_authenticated,reject
  -o milter_macro_daemon_name=ORIGINATING

Save the changes and exit.

This would be a good backup point!

How Postfix talks to MySQL

We are going to create a bunch of map files that will tell Postfix how to get the information out of a MySQL database. They are fairly simple and I am sure you will work out how they do their mapping without explanation. In each file you will need to change MYSQLPasswd01! to be the password you use for your MySQL user called mail.

Create Postfix Map Files For MySQL

The Postfix map files are how we tell Postfix to talk to the MySQL database. Each file contains key = value pairs of data telling Postfix how it can connect and send a query to the database and therefore retrieve the data it requires. Once these map files are created we will add them to the Postfix configuration file /etc/postfix/main.cf using the command line tool postconf. The resulting change will be written to the file and the postfix configuration can then be reloaded.

Mapping valid Domains

This map file is how Postfix will find valid domains within our mailserver. The domain table holds one row for each domain that our mailserver will be accepting mail for and that it will deliver to the end user. With an editor create the domain lookup file with the following details. You will need to change the password from MYSQLPasswd01! to whatever you are using as the password for your MySQL user mail.

sudo nano /etc/postfix/mysql-virtual-mailbox-domains.cf
user = mail
hosts = 127.0.0.1
dbname = postfix
password = MYSQLPasswd01!
query = SELECT domain FROM domain WHERE domain = '%s'

To add the necessary line to the file /etc/postfix/main.cf from the command line:

sudo postconf -e virtual_mailbox_domains=mysql:/etc/postfix/mysql-virtual-mailbox-domains.cf

Mapping Users

As we are intending to use our mailserver to deliver emails to users who are registered on the MySQL database and not just those that have Linux accounts. For Postfix to deliver the emails, each user will be a directory on the file system. We certainly do not want to create a new Linux user account and home directory for all our email users as we did for the quick test in part 1 when we used the user localadmin. What we will do is define a top level directory /var/vmail where all the incoming the emails will be stored. The data held in the virtual tables for the user mappings will be used to construct the lower part of the directory tree, selecting the domain and email address. For each user we will have:

/var/vmail/vhosts//

The domain and user name are derived from their actual email address. For example using fred@dragon.lab we will see this directory structure.

/var/vmail/vhosts/dragon.lab/fred

Let's create the map file:

sudo nano /etc/postfix/mysql-virtual-mailbox-maps.cf
user = mail
hosts = 127.0.0.1
dbname = postfix
password = MYSQLPasswd01!
query = SELECT email FROM user WHERE email='%s'
sudo postconf -e virtual_mailbox_maps=mysql:/etc/postfix/mysql-virtual-mailbox-maps.cf

Mapping aliases to Users

This file maps email aliases or forwarders to real email addresses. This means for one user name or destination we can have many email addresses.

For example, in a company all the emails for support@dragon.lab and info@dragon.lab will be delivered to fred@dragon.lab. Harry is the IT administrator and therefore gets mail that is not addressed to anyone in particular. That is Harry will get mail that cannot be delivered anywhere else. Most of which will be spam so he will no doubt work out a better way to deal with it later as he gets more experience :-). This is called a catch all email alias.

Using email aliases, can also be used as an effective anti-spam system. Whenever you deal with a new company simply create a new email alias from_company@dragon.lab aliased to your own real email address. If you start getting spam on that address, you know they are spamming you or they have sold your address on or worse they have been hacked. In the same way that Play.com were hacked and then they failed to notify their customers in an effort to cover it up. Either way you know it's a company you no longer wish to deal with. You can remove the email listing and no more spam from them. If there is no catch all email setup then those spam emails get thrown away by your mail server.

sudo nano /etc/postfix/mysql-virtual-alias-maps.cf
user = mail
hosts = 127.0.0.1
dbname = postfix
password = MYSQLPasswd01!
query = SELECT destination FROM alias WHERE source = '%s'
sudo postconf -e virtual_alias_maps=mysql:/etc/postfix/mysql-virtual-alias-maps.cf

This final mapping table is used as part of the virtual_alias_maps mapping and saves you having to map every user back to themselves. Why would you need to do that? You ask. See below, it is to do with the way that catch all addresses work.

sudo nano /etc/postfix/mysql-virtual-alias-maps-self.cf
user = mail
hosts = 127.0.0.1
dbname = postfix
password = MYSQLPasswd01!
query = SELECT email FROM user WHERE email = '%s'

The following command line should be all on one line with no spaces after the '='.

sudo postconf -e virtual_alias_maps=mysql:/etc/postfix/mysql-virtual-alias-maps.cf,mysql:/etc/postfix/mysql-virtual-alias-maps-self.cf
As these lookup table files all contain passwords they should not be readale to the world. We can do that by restricting the group permissions and removing all world read and write permissions.
sudo chgrp postfix /etc/postfix/mysql-*.cf
sudo chmod 640 /etc/postfix/mysql-*.cf
-rw-r----- 1 root postfix 124 2013-11-12 16:46 /etc/postfix/mysql-virtual-alias-maps.cf
-rw-r----- 1 root postfix 116 2013-11-12 16:46 /etc/postfix/mysql-virtual-alias-maps-self.cf
-rw-r----- 1 root postfix 135 2013-11-12 16:45 /etc/postfix/mysql-virtual-mailbox-domains.cf
-rw-r----- 1 root postfix 114 2013-11-12 16:46 /etc/postfix/mysql-virtual-mailbox-maps.cf

Aliases are the mechanism that allows mail to be delivered in a very flexible way with a very simple setup. That is the users get their mail and you as the Administrator, don not have too much extra work. 😉

To take the changes into use, restart Post fix.

sudo service postfix restart

Creating Some Test Users

To test those MySQL tables and the interface between Postfix and MySQL we will add some test data to the database. We can then test this is working by using the postmap command to read it back.

If we put the SQL required to build up some test data into a file say insert_test_data.sql, we can load it again when necessary while testing is on going and also add more data if required.

USE postfix;

DELETE FROM domain;
DELETE FROM user;
DELETE FROM alias;

INSERT INTO domain (domain) VALUES ('dragon.lab');

INSERT INTO user (email, domain, password) VALUES
    ('fred@dragon.lab', 'dragon.lab',  ENCRYPT('MYSQLPasswd01!', CONCAT('\$6\$', SUBSTRING(SHA(RAND()), -16)))),
    ('harry@dragon.lab', 'dragon.lab', ENCRYPT('MYSQLPasswd01!', CONCAT('\$6\$', SUBSTRING(SHA(RAND()), -16))));

INSERT INTO alias (source, destination) VALUES
-- emails to bert@dragon.lab actually go to Fred
    ('bert@dragon.lab', 'fred@dragon.lab'),
-- and so do emails to support and info
    ('support@dragon.lab', 'fred@dragon.lab'),
    ('info@dragon.lab', 'fred@dragon.lab'),
-- Harry is the IT guy so gets all emails to root and postmaster 
-- along with the catch all email alias, @dragon.lab -> harry@dragon.lab
    ('root', 'harry@dragon.lab'),
    ('postmaster@dragon.lab', 'harry@dragon.lab'),
    ('@dragon.lab', 'harry@dragon.lab');

You can then rebuild your database with test data with the following command.

mysql postfix < insert_test_data.sql

You will see these being INSERTED in the tail of the log file. To check that the data was inserted correctly run the following queries.

mysql postfix
mysql> SELECT * FROM domain;
+-------------+
| domain      |
+-------------+
| dragon.lab |
+-------------+
1 rows in set (0.00 sec)

mysql> SELECT * FROM user;
+------------------+------------+------------------------------------------------------------------------------------------------------------+
| email            | domain     | password                                                                                                   |
+------------------+------------+------------------------------------------------------------------------------------------------------------+
| fred@dragon.lab  | dragon.lab | $6$8a024ade09ec4b2e$70PtLJbZ.S316xiyhSnqm2EzMcbsb68NzFiJPhNTpSZP7dRyDkdRMyZ9cMAw0JODNNTXFtZjP2Eu4.2HPlN8u. |
| harry@dragon.lab | dragon.lab | $6$67b5d001ed54ba85$9nTBNbwr3wMgC677PfrLmB7OtEHiyAaXLmYEGjtpI5DO/ZhcYsA520/tupLt.ij191gGlYmokGCQj6seRJtN7. |
+------------------+------------+------------------------------------------------------------------------------------------------------------+
2 rows in set (0.00 sec)

mysql> SELECT * FROM alias;
+----+----------------------+------------------+
| id | source               | destination      |
+----+----------------------+------------------+
| 1 | root                  | harry@dragon.lab |
| 2 | bert@dragon.lab       | fred@dragon.lab  |
| 3 | support@dragon.lab    | fred@dragon.lab  |
| 4 | info@dragon.lab       | fred@dragon.lab  |
| 5 | postmaster@dragon.lab | harry@dragon.lab |
| 6 | @dragon.lab           | harry@dragon.lab |
+----+----------------------+------------------+
6 rows in set (0.00 sec)

We can now do some very simple tests to check the Postfix-MySQL interface it working. The first test will lookup dragon.lab to see if it is valid domain for our system. If it is it will return dragon.lab. Also try this with a domain name that is not valid.

sudo postmap -q dragon.lab mysql:/etc/postfix/mysql-virtual-mailbox-domains.cf

We can lookup a user to see if they are a real use. This test should return fred@dragon.lab as he is valid and it should return nothing.

sudo postmap -q fred@dragon.lab mysql:/etc/postfix/mysql-virtual-mailbox-maps.cf

This final test checks that aliases are working bert@dragon.lab is an alias for fred@dragon.lab. So we should see Fred's email address returned as the answer.

sudo postmap -q bert@dragon.lab mysql:/etc/postfix/mysql-virtual-alias-maps.cf

If any of those tests fail you will need to go back and find your typo.

This would be a good backup point!

Dovecot

Installing Dovecot

To get POP3 and IMAP features from your mail server we will need Dovecot. It should have been installed above.

Creating a vmail user

We are using virtual users on this mailserver. Which means not all users will have a HOME directory in which we can store incoming emails. So we will store all mail in one directory structure as explained above. Look for a silver text box above.

The email and directory structure needs to be secure. We therefore, use a non privileged user vmail for that. Let's create that user and group now. I am using a user id (UID) and a group ID (GID) of 6004 it can be any unused UID and GID but you will need to remember it for later.

sudo groupadd -g 6004 vmail
sudo useradd -g vmail -u 6004 vmail -d /var/vmail -m -s /sbin/nologin

To check these have been created okay, we can look in the passwd and group files.

grep vmail /etc/passwd /etc/group
/etc/passwd:vmail:x:6004:6004::/var/vmail:/sbin/nologin
/etc/group:vmail:x:6004:

Create the top level directory where emails will be stored. The remaining structure will be created as required. We also need to change the owner and group.

sudo mkdir -p /var/vmail/vhosts/dragon.lab
sudo chown -R vmail:vmail /var/vmail

ls -dl /var/vmail/vhosts
drwxr-xr-x 3 vmail vmail 4096 Nov 30 09:52 /var/vmail/vhosts/

Configuring Dovecot

Make a copy of the files we are going to amend before we start. When a mistake is made you still have the original to compare against. 🙂

cd /etc/dovecot
sudo mkdir backup
sudo cp -a conf.d/10-auth.conf \
     conf.d/99-mail-stack-delivery.conf \
     dovecot-sql.conf.ext backup

Modify conf.d/10-auth.conf so that it will load auth-sql.conf.ext and not auth-system.conf.ext. The lines you are looking for are at the end of the file.

sudo nano /etc/dovecot/conf.d/10-auth.conf

Two lines to modify here, comment one and uncomment the other.

#!include auth-system.conf.ext
!include auth-sql.conf.ext

There are some changes which need to be made in the system file /etc/dovecot/dovecot-sql.conf.ext. Use search/find and amend the lines as necessary.

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

The number, 6004, in the user query must be the same number you used as the UID and GID for the vmail user and group from above see the silver box.

# /etc/dovecot/dovecot-sql.conf.ext
driver = mysql
# There is a password on this line update it to the one you are using
connect = host=127.0.0.1 dbname=postfix user=mail password=MYSQLPasswd01!
default_pass_scheme = SHA512-CRYPT
password_query = SELECT email as user, password FROM user WHERE email='%u'
user_query = SELECT email as user, 6004 AS uid, 6004 AS gid FROM user WHERE email = '%n@%d' AND domain = '%d'

Update the permissions as dovecot-sql.conf.ext contains a password in the open.

cd /etc/dovecot
sudo chown root:root /etc/dovecot/dovecot-sql.conf.ext
sudo chmod 600 /etc/dovecot/dovecot-sql.conf.ext

Dovecot reads the configuration files in order with the lowest being read first. Any settings found in a later file will override those already read in. Therefore, the settings in 99-mail-stack-delivery.conf override all other settings. We will put the majority of our changes in this file. It also means that when dovecot gets updated most of our changes will be untouched by new files.

sudo nano /etc/dovecot/conf.d/99-mail-stack-delivery.conf

For each line or group of lines find them with search and then amend them as necessary. If the line cannot be found then simply add it to the end of the file. The file name in comments shows the original file where the setting was found. The file in comments does not have to be changed.

# /etc/dovecot/dovecot.conf
protocols = imap pop3 lmtp

# /etc/dovecot/conf.d/10-master.conf
mail_location = maildir:/var/vmail/vhosts/%d/%n

# etc/dovecot/conf.d/10-ssl.conf
ssl_protocols = !SSLv2 !SSLv3

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

    unix_listener auth-userdb {
       mode = 0600
       user = vmail
       #group =
    }

    # Auth process is run as this user.
    user = dovecot
}

Some lines are new and can be added to the end of the file, 99-mail-stack-delivery.conf.

# conf.d/10-mail.conf                         
mail_privileged_group = vmail
disable_plaintext_auth = no

# conf.d/auth-sql.conf.ext
passdb {
   driver = sql
   args = /etc/dovecot/dovecot-sql.conf.ext
}

# conf.d/auth-sql.conf.ext
userdb {
   driver = static
   # The home value should be the same as the mail_location
   args = uid=vmail gid=vmail home=/var/vmail/vhosts/%d/%n
}

# conf.d/10-master.conf
service imap-login {
  inet_listener imap {
    port = 143
  }
  inet_listener imaps {
    port = 993
    ssl = yes
  }
}

# conf.d/10-master.conf
service pop3-login {
  inet_listener pop3 {
    port = 110
  }
  inet_listener pop3s {
    port = 995
    ssl = yes
  }
}

# conf.d/10-master.conf
service lmtp {
   unix_listener /var/spool/postfix/private/dovecot-lmtp {
     mode = 0600
     user = postfix
     group = postfix
   }
}

# conf.d/10-master.conf
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
}

# conf.d/15-lda.conf
postmaster_address = postmaster@dragon.lab

# These last few lines turn on logging while we are debugging, to turn 
# off the verbose logging comment these lines and restart dovecot

log_path = syslog
syslog_facility = mail
auth_verbose = yes
auth_debug = yes
auth_debug_passwords = yes
mail_debug = yes
verbose_ssl = yes

Save your changes and exit.

Restart dovecot to take the changes into use.

sudo service dovecot restart

In your log files you should see some output similar to this

Dec 16 14:32:03 mailserver systemd[1]: Stopped Dovecot IMAP/POP3 email server.
Dec 16 14:32:03 mailserver systemd[1]: Starting Dovecot IMAP/POP3 email server...
Dec 16 14:32:03 mailserver systemd[1]: dovecot.service: PID file /var/run/dovecot/master.pid not readable (yet?) after start: No such file or directory
Dec 16 14:32:03 mailserver dovecot: master: Dovecot v2.2.22 (fe789d2) starting up for imap, pop3, lmtp (core dumps disabled)
Dec 16 14:32:03 mailserver systemd[1]: Started Dovecot IMAP/POP3 email server.

The file mentioned in the log file as being not readable (yet?) should exist and be readable by root after dovecot is started.

Now would be a good time to make a backup of your new server.

Testing Emails Get Sent

For testing out the mailserver we can use openssl or telnet. Using these commands is a little more complex but means you do not need to install the mail utilities. You can also see the output in the log files as you enter the commands. It makes it simpler to debug. See these blog posts:

Pop over to those posts and send and read some emails. You should be able to send and read emails to Fred and Harry. As well as the aliases we set up for postmaster, root and bert. For now use the telnet option when opening a connection via POP3.

Setting Up A GUI Email Client

At this point we can set up Thunderbird or Evolution or any other email client. I'm sure you can work this out but here is a very quick and dirty description. You will only need one of the IMAP or POP3 configurations. Because we setup Postfix to say the user MAY use SSL, we can use an SSL value of None or STARTTLS. If you are using Thunderbird and no encryption you will see a big scary message saying your connections will not be encrypted. This is not a good idea for production servers providing emails to businesses.

  • Name: Fred
  • Email Address: fred@dragon.lab
  • Username: fred@dragon.lab
  • Password: MYSQLPasswd01!
  • POP3:
    • Server: mailserver.dragon.lab
    • Authentication: Normal Password
    • Port: 110
    • SSL: STARTTLS | None
  • IMAP:
    • Server: mailserver.dragon.lab
    • Port: 143
    • SSL: STARTTLS | None
    • Authentication: Normal Password
  • SMTP:
    • Server: mailserver.dragon.lab
    • Port: 587
    • SSL: STARTTLS | None
    • Authentication: Normal Password

We should now be able to send and receive emails for fred@dragon.lab and harry@dragon.lab YAY! Emails sent to bert@dragon.lab will be collected by Fred when he logs in and downloads his email. Any emails sent to unknown email accounts should go to Harry.

Dealing With Spam

When your own live mailserver is setup you can employ a simple email spam reduction process. Whenever you start dealing with a company say, Acme, create a unique email address that you use only for Acme to use and forward those emails to your login account. You can use something like from_acme@dragon.lab or fred_acme@dragon.lab. If you ever get any spam on that email you know that Acme was hacked and/or gave out your email or simply sold it on. You can stop dealing with them and stop the spam by deleting the forwarding email from_acme@dragon.lab. You can also tell the world that Acme was hacked and are trying to cover it up!

This type of email naming schema also has the side effect that it will help your email client sort email into folders when mail is delivered. Nice! That is the end of Mail Server on Ubuntu 16.04 Part 2.

If All is working. Now would be a good time to make a full backup of your new server

In Part 3

In Mail Server on Ubuntu 16.04 Part 3 of this series we will be adding anti-virus and anti-spam software and a tool to greylist emails to cut down on the work our server has to do. This will be added to postfix via amavis-new.

2 thoughts on “Mail Server on Ubuntu 16.04 Part 2

  1. Andre R. Gagne

    First off, just let me say that I love your guide! It works perfectly, and you walk a person through the process so carefully that it really is difficult to make a mistake!

    Just one thing I would like to know: In part one, when you sent an email via the commandline to localadmin using “mail”, that worked as describe for me. However, once I had my server set up and running exactly as expected (through part 5), I could no longer send an email to my local admin via the “mail” command. Is this an “expected” end result of all this, or did I mess something up somewhere? To re-iterate, when I send an email via the cli using the “mail” command to my local admin “robert”, I get no errors, nor do I receive any confirmations of success; but when I type “mail” at a terminal prompt afterwards, I do NOT have a mail message waiting in “robert”s (my) mailbox.

    Is this what one should expect, and, if so, can you explain WHY that no longer works?

    Thanks again for this great guide!

    Reply
    1. Richard Post author

      Thank you for the kind words.
      The command line testing should still work as it is the same process/handshaking your email client will do when sending and receiving emails. The test in the first part will only accept Linux users as recipients. Where as in part two this is turned off and the valid users or recipients must be setup in the Mysql database. You should see an error message “Recipient address rejected: User unknown in virtual mailbox table;”,in /var/log/mail.log if that is the case.
      If not, I can only suggest looking at the log files, with tail -f, while running through the cli tests. They may give you some idea why the test emails are not getting there. A silly thing to check would be you are sending it to the right server. Always a good start. 🙂 Check the email is not stuck in a queue with postqueue -p, take a look at the man page for more details.

      Hope that helps.

      Reply

Leave a Reply

Your email address will not be published. Required fields are marked *