Let’s do Dovecot slowly and properly: Part 2 – Proper authentication

Encrypting communications with dovecot and hashing passwords for safe storage

In part 1 of this Dovecot series (which itself is an offshot of my Postfix series) we set up the very most basic dovecot install we could get away with. Now we will try to redeem it a bit by improving the security of the the authentication mechanism and the storage of passwords on the server.

In this post we will will make it much harder to snoop on our communications with the IMAP server and decrease the overall likelihood of somebody learning our password, including anybody with access – legitimate or otherwise – to our server.

We should start off by clarifying a few terms first. We touched briefly on the idea of an authentication mechanism last time and even showcased the plain mechanism’s workings by authenticating via telnet. It should therefore be apparent that an authentication mechanism is the protocol by which we present credentials to dovecot and it checks them.

It is, however, distinct from the password database and the password scheme. To use a terrible metaphor, let’s say that these three things are the socks, pants and shirt of your security setup. You can mix and match as you please (well, not quite but for the time being…) but some things work better in general and in combination.

The password scheme is the hashing function you use to treat your password before it is stored on the server. To check if a login attempt uses the correct password, the password is hashed using the same function and if the resulting string of garbled characters matches the string of garbled characters on file, dovecot knows that the password entered was correct. Wired has a pretty good explanation of password hashing in their ‘hacker lexicon’. If you don’t hash the password but keep it lying around in plaintext as we did after last post, any user with read rights can read it, memorize it and use it without anybody being any the wiser.

The password database on the other hand is the means by which we store and access the password, however it is hashed. Do we put it in a text file, do we use mysql, is it saved somewhere in the bowels of the system and we have to ask some obscure unix gatekeeper to check it for us, etc. Though there are likely to be caveats to this rule, in general, you can use any database with any scheme. Just like pants and shirts, right?

Finally, the authentication mechanism. The alternatives to plain text authentication mostly revolve around clever ways to avoid sending the password in plain text. So the password is treated in some way before it’s sent by an unencrypted connection. This then places certain restrictions on the password scheme because either dovecot has to have stored the password in a manner similar to the treatment the proffered password has received, or it has to have access to a plaintext version (that can then be treated similarly to the password before it was sent). In other words, plaintext authentication mechanism is like black socks, goes with everything.

To belabour this metaphor further: We cannot, however, walk around in black socks, because well, everybody can see them, smell our feet and read our passwords. Solution: Put on some goddammn shoes already, aka encryption.

Unlike the specialised authentication mechanism designed for password transfer, this is the same general purpose encryption we use when we present a website via https. Just like with your website it requires an issued certificate and can make all your communications with your server private, not just the password. The raisons d’être for most of the alternatives are no longer very valid in this day and age but for the record: They require less computing power and they do not require a certificate. With the Let’s Encrypt project, certificates are free and plentiful and while computing power isn’t free, there’s a lot more of it these days than there used to be.

So basically in this post we will discuss different password databases and schemes but as for authentication mechanisms, we’re going to take encrypted plaintext as a given.

TLS

Let’s Encrypt’s software, certbot, is designed to interact with your web server to make getting and renewing certificates as painless and automatic as possible. AFAIK it says nothing about your mail server. Anyway, there isn’t much to integrating your Let’s Encrypt certificates into your setup assuming a) that you have them, b) you know where they are and c) they cover the right domain.

If you don’t already have some on hand from setting up a web site, I would suggest heading to Digital Ocean’s tutorial for certbot in standalone mode. Just googling around for certbot instructions will most likely get all kinds of Apache specific stuff that isn’t relevant to our purpose here. Standalone mode gets you the certificates without any attempts at being clever. If you’re already in possession of Let’s Encrypt certificates, you will find them in the directory

/etc/letsencrypt/archive/[domain name]

When you renew your certificates, certbot keeps your old certificates around and just stick a version number on the end of the file name, so you may well see a list of cert.pem, cert1.pem, cert2.prm, etc. To always get the current certificate, stick to referring to the maintained list of symbolic links in

/etc/letsencrypt/live/[domain name]

This directory also has a handy README file which basically tells you what you need to know about the various .pem files in the directories:

This directory contains your keys and certificates.

`privkey.pem`  : the private key for your certificate.
`fullchain.pem`: the certificate file used in most server software.

I’ve never had to reference any other files than these two. The fullchain file contains not only your certificate but also that of the various links in the chain of trust all the way up to the root certificate. I’ve mentioned this before but when it comes to domains, stick to your 2nd level domain name (e.g. mydomain.net). Don’t get cute and try to put mail on mail.mydomain.net. It just makes life more difficult and at this scale, there’s no way that’s necessary.

Enough about the certificates, let’s start using them. First we will sabotage our config from last time. Edit the conf.d file 10-auth.conf and find the line

disable_plaintext_auth = no

We set this explicitly to no last time in order to be allowed to use the plain authentication mechanism without encryption. Now, we want to disallow it again, so change the no to a yes. We are still using plain as our authentication mechanism as witnessed by it being included in the auth_mechanisms = plain setting in 10-auth.conf, only we’re now only allowing it when it comes through an encrypted tunnel. Restart dovecot and you should get denied when trying to access your account using a client set up to no encryption access on port 143.

Dovecot needs to be told that it should run an encrypted access service as a process at a specific port. This we do in 10-master.conf where we previously disabled said service

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

To enable the encrypted access, simply uncomment the port and ssl options on the listener. I did try to disable the imap listener on port 143, figuring that it was no longer necessary. However, that meant that I was also unable to properly use the encrypted access on port 993. Maybe some internal logic redirects traffic back to 143 after decryption? [Test if 143 needs to be open to the public?] So leave both listeners up.

Finally we need to set up dovecot to use our certificates. This is handled in the 10-ssl.conf file in conf.d. At the top you should set

ssl = yes

And just below that set the paths to your certificate and your private key:

ssl_cert = </etc/letsencrypt/live/[domain name]/fullchain.pem
ssl_key = </etc/letsencrypt/live/[domain name]/privkey.pem

Note that the pointy brackets precede the paths. This indicates that it is the contents of the file that are to be assigned to the variable rather than the path itself. So ssl_cert is set to

-----BEGIN CERTIFICATE-----
MIIEkjCCA3qgAwIBAgIQCgFBQgAAAVOFc2oLheynCDANBgkqhkiG9w0BAQsFADA/
MSQwIgYDVQQKExtEaWdpdGFsIFNpZ25hdHVyZSBUcnVzdCBDby4xFzAVBgNVBAMT
DkRTVCBSb290IENBIFgzMB4XDTE2MDMxNzE2NDA0NloXDTIxMDMxNzE2NDA0Nlow
SjELMAkGA1UEBhMCVVMxFjAUBgNVBAoTDUxldCd... etc.

and not “/etc/letsencrypt/live/[domain name]/fullchain.pem”.

With these corrections in place you should be able to restart dovecot and access your mail via an encrypted channel on port 993.

Password databases and schemes

With the authentication mechanism taken care of, we now turn to the way to store and encrypt passwords on the server.

While Dovecot supports both SQL databases and LDAP as a means to check user rights and passwords, I see very little reason to consider such heavyweight solutions for a hobbyist setup. Instead I’m going to go with the simplest of solutions (apart from static) which is based on text files. For a single or a handful of users this more than enough.

Essentially, the passwd-file option simply requires specifying a specific text file containing the username and encrypted password, formatted similarly to the classic /etc/passwd file you know from linux system user management. To keep things straight, though, remember that the users we’re registering in our passwd-file file are of the virtual kind, like alice@brokkr.net and not system users.

First we disable the static passdb by commenting out the passdb section in conf.d/auth-static.conf.ext. Then we enable the passdb section in the conf.d/auth-passwdfile.conf.ext as its substitute:

passdb {
  driver = passwd-file
  args = scheme=SHA512-CRYPT username_format=%u /etc/dovecot/users
}

The three settings or arguments are the password scheme which we will talk about shortly, the username format to be found in the password file (set to the variable %u or the full username) and the path to the passwd-file.

The passwd-file itself is simplicity itself: One line per user containing the full username and the hashed password separated by a colon. I suspect having defined the password scheme in auth-passwdfile.conf.ext should suffice but for good measure we can make it explicit here as well by writing it in brackets before the password proper:

username:{SHA512-CRYPT}$6$k0RHGDl03wYr3Pf3$NKqfje46OwwiN2ABPUYMgRlWp5FvFiEXCbnRnKtRA.SPA3eYvrc0oVxELzPUe7yC6BaymknXF4VqfFI.hxGhn.

(In case you’re wondering: Yes, that is the hash for the literal password ‘password’)

As you can see I have chosen the password scheme SHA512-CRYPT. This is based simply on a) the fact that the Dovecot wiki calls it ‘a strong scheme’ and b) the BLF-CRYPT (or Blowfish Crypt) is usually not provided on linux for various reasons. It is provided by Dovecot itself from version 2.3 but my current os (and docker image) of choice, Ubuntu 18.04, ships with v. 2.2.33.

To get the hashed password to put into your users file you need to run

doveadm pw -s sha512-crypt

enter the password as requested and copy-paste the result. Substitute your chosen scheme for sha512-crypt. It should now be apparent that using a text file is a brilliant solution for a single user/small group scenario because you, the sysadmin, can easily do this manually whenever passwords need changing. It should also be apparent that it will quickly become a nightmare to manage once the number of users grow beyond the fingers on your hands.

Seeing as I’m going to stick to all email being owned by a single system user, there is no reason to switch the userdb over to passwd-file, though, so we will just stick with the static solution already in place. In order for dovecot to pick up both our static userdb setting and our passwd-file passdb setting we need to instruct it to source both of the .ext files containing the setup. To do this check the tail end of your conf.d/10-auth.conf:

#!include auth-deny.conf.ext
#!include auth-master.conf.ext

#!include auth-system.conf.ext
#!include auth-sql.conf.ext
#!include auth-ldap.conf.ext
!include auth-passwdfile.conf.ext
#!include auth-checkpassword.conf.ext
#!include auth-vpopmail.conf.ext
!include auth-static.conf.ext

Conclusion

After restarting Dovecot with these new settings and port 993 forwarded you should be able to start accesing your mail with a reasonable expectation of confidentiality. In other words, this is now a fully functional and useable email system. Only, I hope you like spam because you’re going to get a lot it. We will get back to that issue but for now you should use your email client to test things out. I recommend deleting the old port 143 based account (in your client) and starting over with security set to ‘SSL/TLS’, port to 993 and indicate that the password should be sent plaintext (language will probably differ here; K9 for instance calls it ‘Normal password’ under the heading of ‘Authentication’).

The next steps are both about tying Dovecot and Postfix closer together. The first is about Postfix piggybacking on the authentication setup I just added (SASL). The other is about shifting some tasks from Postfix over to Dovecot (LMTP). If you’re only interested in Dovecot and not Postfix, you can check out the remaining posts in the Dovecot series.

Dovecot © Dave Haygarth, CC-BY 2.0

Leave a Reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.