[2019-11-22]

Optional: Server-based mailbox encryption

Guest column by Eelke Smit 🙂

Warning

This chapter is for people who know what they are doing and are aware of the fact that improperly handling can cause email messages being lost.
Also, the features used in this chapter are experimental as stated in the Dovecot documentation.

Introduction

Now we have come so far, that we have a new and shiny fully functional email server. With security settings that fits our needs and are up to date for the years to come. We can now securely send and receive email mostly (preferred) via a TLS connection. So although the email messages are in plain text, they are send over the wire in an encrypted form.

But when hosting your own mail server needs that little extra finishing touch security wise, we can go a step further. We can in fact, store our email at rest in a encrypted form. So when the server is compromised, all email messages are unreadable for the attacker.

Here’s where a plugin comes into play called “mail-crypt“. Its a plugin for Dovecot. The mail-crypt plugin is used to secure email messages stored (at rest) on the disc. Email messages are encrypted before written to disc. This is completely transparent to the user using the mail server.

The plugin has several options for encrypting messages:

  • There can be a single encryption key for the whole system (less secure)

    We are not going to take this route, as the keys are stored on the server. When the server get compromised, the keys can be used to decrypt the email messages.
  • Each user has a key of his/her own. The used cryptographical methods are widely used standards and keys are stored in portable formats, when possible.

    We are going to use this route, although this is an experimental option as stated in the dovecote documentation.

Functional Overview

The use of the mail-crypt plugin depends on a user having: a keypair (a private and a public key for asymmetric cryptography). These keys are provisioned in a variable via the user database or directly from Dovecot configuration files.

The public half of the provisioned keypairs are used to generate and encrypt keys for symmetric encryption. The symmetric keys are used to encrypt and decrypt individual files. Symmetric encryption is faster and more suitable for block mode storage encryption.

The symmetric key used to encrypt a file is stored, after being encrypted with the public asymmetric key, together with the file.

Encryption Technologies

The mail-crypt plugin provides encryption at rest for emails. Encryption of the messages is performed using the symmetric Advanced Encryption Standard (AES) algorithm in Galois/Counter Mode (GCM) with 256 bit keys. Integrity of the data is ensured using Authenticated Encryption with Associated Data (AEAD) with SHA256 hashing.

The encryption keys for the symmetric encryption are randomly generated. These keys in turn are encrypted using a key derived with from the provisioned private key. Provisioned private keys can be Elliptic Curve (EC) keys or RSA Encryption is done using the Integrated Encryption Scheme (IES). This algorithm is usable both with EC and RSA keys.

Lets go

We first need to tell dovecot that we are going to use mail-crypt, so take the following step. To make Dovecot use mail-crypt plugin we need to create a ‘conf’ file (configuration file).

Start by creating a file called /etc/dovecot/conf.d/10-mailcrypt.conf that contains:

mail_attribute_dict = file:%h/Maildir/dovecot-attributes
mail_plugins = $mail_plugins mail_crypt plugin
{
  mail_crypt_curve = SHA256-CRYPT
  mail_crypt_save_version = 2
  mail_crypt_require_encrypted_user_key = yes
}

This file is telling dovecot to use mail-crypt, and is also setting the used encryption curve, the version of the mail-crypt plugin used and the setting to set encryption per user (folder)

For the setting “mail_crypt_save_version = ” We have the option to use “0”, which means: encryption is off for new emails that arrive at the system.

Existing encrypted emails can still be read by the system.
We also have the option “1” but that’s is used as an old version, and should not be used anymore.

So we go for version “2

mail_attribute_dict has to be set since it is used to store the keys.

Advice

Any valid curve supported by cryptographic library is supported. If you wish to have EC (elliptic curve) keys, you may find curves using this openssl command:

openssl ecparam -list_curves

Important

it has to be the same crypto used for the password scheme in the file: /etc/dovecot/dovecot-sql.conf.ext

Later on you’ll see that we will actually set the “mail_crypt_save_version” via the database as a “per user” setting. That way we can controll encryption on a per user base.

Next in line is the following file:

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

Remember, this is a file you created earlier during the guide, but now we are modifying the file for the use of the mail-crypt plugin

Original:
It tells Dovecot how to access the MySQL database and where to find the information about email users/accounts. You will find it well documented although all configuration directives are commented out.

Add these lines at the bottom of the file:

driver = mysql
connect = host=127.0.0.1
dbname=mailserver
user=mailuser
password=ChangeMe  <- use your database access password instead
default_pass_scheme = SHA256-CRYPT
password_query = SELECT email as user, password FROM virtual_users WHERE email='%u';

New:

driver = mysql
connect = host=127.0.0.1
dbname=mailserver
user=mailuser
password=ChangeMe  <- use your database access password instead
default_pass_scheme = SHA256-CRYPT
password_query = SELECT email as user, password, crypt as userdb_mail_crypt_save_version, '%w' AS userdb_mail_crypt_private_password FROM virtual_users WHERE email='%u';

What these lines mean:

  • driver: the kind of database
  • connect: where to find the MySQL database and how to use it (username, password)
  • default_pass_scheme: the format in which the passwords are stored (we use strong salted SHA256 hashes)
  • password_query: an SQL statement that returns the user (=the email address) and the password (SHA256 hash) from the database where “%u” is the login user name (=we use the email address as the user name of an email account).

Whenever Dovecot needs to check an email user’s password if will run the above query and verify the password against the hash in the database. It will now also check if the user has encryption enabled via the following part of the statement:

crypt as userdb_mail_crypt_save_version, ‘%w’ AS userdb_mail_crypt_private_password

If its version “0” encryption is disabled for new arriving emails, if it is version “2” encryption is enabled for new arriving emails.

The important part here is, that we also use the users password for the encryption part. and because we do that, only the user who knows the password, can decrypt the email messages.
Therefor, if the user lost his/her password, all the messages are left encrypted and cannot be restored.

Database Preparation

As we are going to use encryption on a per user base, we need to tell our server which users are enabled with encryption.
We do this by altering the database.

Previously in the guide we have setup this as a database scheme:

CREATE TABLE IF NOT EXISTS `virtual_users` (
  `id` int(11) NOT NULL auto_increment,
  `domain_id` int(11) NOT NULL,
  `email` varchar(100) NOT NULL,
  `password` varchar(150) NOT NULL,
  PRIMARY KEY (`id`),
  UNIQUE KEY `email` (`email`),
  FOREIGN KEY (domain_id) REFERENCES virtual_domains(id) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

We change that to:

CREATE TABLE IF NOT EXISTS `virtual_users` (
  `id` int(11) NOT NULL auto_increment,
  `domain_id` int(11) NOT NULL,
  `password` varchar(150) NOT NULL,
  `email` varchar(100) NOT NULL,
  `crypt` int(11) NOT NULL,
  PRIMARY KEY (`id`),
  UNIQUE KEY `email` (`email`),
  FOREIGN KEY (domain_id) REFERENCES virtual_domains(id) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

As you can see we added one line:

`crypt` int(11) NOT NULL

We use that column to define whether a user has encryption enabled or disabled the used value’s are:

  • 0 – Disabled Encryption
  • 2 – Enabled Encryption

We use this column as a value in the file we created earlier: /etc/dovecot/dovecot-sql.conf.ext

password_query = SELECT email as user, password, crypt as userdb_mail_crypt_save_version, ‘%w’ AS userdb_mail_crypt_private_password FROM virtual_users WHERE email=’%u’;

Now to fill that column, you can use the same test data as previously used, but now with that extra value “crypt”

REPLACE INTO mailserver.virtual_users (
       id ,
       domain_id ,
       password ,
       email,
       crypt 
)
VALUES (
       '1', '1', '{SHA256-CRYPT}$5$M/GWzmtjsLroRWIf$0qDt7IgXCoCQyvQZO7v.NC46rXzFVBSkZcilijlwgL7', 'john@example.org', '2' 
); 

Notice the value “2” at the end of the line which enables encryption.

User Key Creation

Now we have set up mail-crypt, we need to generate the user keys.
There are two situations in which you can do this, both slightly different.

New user without existing mailbox

Use this command when the mailbox has not been created yet. (e.g. there has not been send an email to the new user, but the user does exists in the user db)

doveadm -o plugin/mail_crypt_private_password=password mailbox cryptokey generate -u john@example.org -U

What these options mean:

  • -o – Dovecot option, needed if you use password protected keys
  • -u – Username or mask to operate on
  • -U – Operate on user keypair only

You can also set a new password when the user needs a new password for their email account with:

doveadm mailbox cryptokey password -u john@example.org -n newpass -o password

Existing user with existing mailbox

Use this command when the mailbox HAS been created. (e.g. there HAS been send an email to the new user)

So, if you did not use this:

doveadm -o plugin/mail_crypt_private_password=password mailbox cryptokey generate -u john@example.org -U

Then use this:

doveadm mailbox cryptokey password -u john2@example.org -n newpass -o "yes"

What these options mean:

  • -u – Username or mask to operate on
  • -n – New password
  • -o – Old password

Information

Here is a possibility that the default password was set to “yes”, That’s why we use that value as the “old” password.
otherwise you can leave out the -o option

For more information on this, check out the Dovecot documentation.

Restart Services

Now to make all the changes active, we need to restart the dovecot service.

systemctl restart dovecot

Testing encrypted email

Now when new email comes in, it should be stored encrypted on the disc. Let’s check if that is indeed the cause.

Send yourself an email and do the following test:

cd /var/vmail/example.org/john/Maildir/new/

There should be a file that represents the new email.
Now cat the new email file in this folder:

cat "file-name"

The output should be some unreadable characters.
While normally the email is just plain text and readable.

Success is in the air.

Handle user creation and password changes

As you might understand, email encryption is very nice. And because we use the user’s password for the encryption key’s. The only person who can decrypt and read the messages, is the one with the password.

That said. If the user’s password is lost, ALL the user’s messages are left unreadable, and cannot be recovered.

I repeat: THEY CANNOT be recovered.

The encryption solution is for people who like to be more in control, and have a need for stored encrypted mail. Like for example, hosting a mail server in the cloud without actual ownership of the server.

So password management is a big thing with encryption enabled. A user cannot simple do a password change on their email account like they used to do. (via webmail, or via a web admin tool, e.t.c.)

This is because when the user changes the password of the account, the encryption key password does not change with it. This wil result in unreadable email messages in the mailbox because the key’s password does not match.

As an admin you’ll have to think of a way to deliver a procedure to change a users password. Because there are many way’s to solve this, i’ll only decribe the command to change the encryption key’s password, and let you decide on how to come up with some form of password change-solution

doveadm mailbox cryptokey password -u john2@example.org -n newpass -o "oldpass"

You could ofcourse create a script with some web frontend that does this for the user when they change their password.

And that’s it. You now have saved new email encrypted.

2 thoughts on “Optional: Server-based mailbox encryption

  • 2020-01-19 at 14:38
    Permalink

    Are the sql queries in “/etc/dovecot/conf.d/auth-sql.conf.ext” still valid?
    The “original”:
    driver = mysql
    connect = host=127.0.0.1
    dbname=mailserver
    user=mailuser
    password=ChangeMe <- use your database access password instead
    default_pass_scheme = SHA256-CRYPT
    password_query = SELECT email as user, password FROM virtual_users WHERE email='%u';

    Is much different from the one created at earlier (https://workaround.org/ispmail/buster/setting-up-dovecot/). And we are using BLF-CRYPT instead of SHA256-CRYPT right?

    Thanks

    Reply
    • 2020-01-23 at 13:56
      Permalink

      you are correct, they should both be the same

      Reply

Leave a Reply

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