HomeAboutGitlab

Taking (back) control of my email

Intro

Up until recently, my personal email (rolando.cl) was hosted as a google account (I was able to get the free app tier when Google was offering it). It works, but because I'm in a stage where I wanted to be able to take back control of different pieces of my digital life, I decided to give this a try.

The strategy was simple:

Be able to have a working email server that would allow me to receive email and have some basic spam filtering. For sending email, the server should relay to something that's more suited for that (Amazon SES or Sendgrid or something similar).

For accessing emails, it should support IMAP

The solution

My solution was to reuse my current backup server and add the capabilities I wanted. It also gave me the opportunity to update ZFS on that machine and try the new encryption, that way my email would be encrypted on rest.

In the end, this is the high level overview:

  • Postfix for sending/receiving emails
    • SMTPD (sending) with SASL authentication through dovecot
    • Relay to SES for sending emails
  • Dovecot for IMAP & auth
    • Sieve (Pigeonhole) for email filtering and fun stuff
  • Spamassasin for spam filtering
  • Email encrypted at rest using ZFS encryption
  • Encrypted backups on S3 and other ZFS machine

The auth database for Dovecot is a simple hash database. In the near future, I might add a small service that would manage the static files & sieve filters using a local sqlite database.

Anyway. Let's head into the details. The backup server is an Archlinux ARM machine (rpi3) as described in my post from 2016. It was super easy to install postfix, dovecot and spamassasin. In the next few sections, I'll highlight the details that might be relevant if you want to replicate the setup.

The last two points of my setup are out of the scope of this post, and you should make sure that if you build your own email server, you are able to keep it safe (at least do some backup).

Initial install of things and preparation

The first thing you need to do, is to install the packets. I'm going to assume you're in arch, but replace the commands with your own package manager from your distro:

sudo pacman -Syu dovecot postfix spamassassin pigeonhole

With that done, you can now head to the configuration. I mostly followed the arch wiki for postfix and dovecot. Below are the most important details that might not be exactly the same as in there.

I also used virtual users/domains and followed a very similar scheme as to the one described in the arch wiki for Virtual mail user system (I didn't install roundcube though, I didn't want a web ui on my machine).

Postfix

I decide to use virtual users, but unlike the wiki docs for arch, my database would be just a regular hash-file with the emails. This is my section to cover that:

virtual_mailbox_domains = /etc/postfix/vhosts.txt
virtual_mailbox_base = /home/vmail
virtual_mailbox_maps = hash:/etc/postfix/vmaps.txt
virtual_uid_maps = static:5000
virtual_gid_maps = static:5000
virtual_alias_maps = hash:/etc/postfix/valias.txt
virtual_transport = dovecot

You will need to create the vhosts.txt, vmaps.txt and valias.txt as specified in the wiki.

I'm using static uid & gid because all email will live under the user & group vmail, that has an id 5000 (created previously). Its home is under an encrypted zfs dataset, and this is where all the mail is created. The last line in that section tells postfix to use dovecot to finally deliver the email. In my case, I'm using LMTP.

In the master.cf file, the only thing I changed from the default, was the addition of the dovecot entry, including spamassassin:

dovecot   unix  -       n       n       -       -       pipe
  flags=DRhu user=vmail:vmail argv=/usr/bin/vendor_perl/spamc -u spamd -e /usr/lib/dovecot/dovecot-lda -f ${sender} -d ${recipient}

Dovecot

I pretty much followed the instructions in the Arch wiki, however when generating the dhparam, I used openssl with the -dsparam option to avoid waiting forever on my rpi3 (more info about this in this question on SO), like this:

opennssl dhparam -dsaparam -out /etc/dovecot/dh.pem 4096

To create a user password, you would use something like this:

doveadm pw -s crypt -u user@domain.com

And add the result to /etc/dovecot/users in the right format:

user@domain.com:<password-hash>

In order to activate auth-passwd for dovecot, you need to un-comment the right section in 10-auth.conf and then edit auth-passwdfile.conf.ext. Mine looks like this:

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

userdb {
  driver = static
  args = uid=vmail gid=vmail home=/home/vmail/%d/%n
}

This file also tells dovecot to use the /etc/dovecot/users file for password authentication, but the user config is "static", in this case all users will have the same uid and gid, as well as home location (you can use Variables in there as you can tell from my config).

Testing everything

At this point, you should be able to reconfig your mx dns settings and point them to your new server. If you send a test email from another server, you should be able to receive it, and it should arrive in /home/vmail/domain.com/user. Dovecot should create the root Maildir in the users' home and you should also be able to connect to your IMAP server using a regular Email client: I tried iOS Mail.app and Wanderlust, but pretty much any client should work.

Securing your SMTP

Something you should do, is to make really sure your SMTP server (postfix) is not an open relay, otherwise it will get abused by spammers. This should be simple: just restrict relay from everyone, except authorized (logged in) users. To achieve this, this is my config (in main.cf):

# only trust our local machine for relay
mynetworks_style = host
myorigin = $mydomain

# SASL through dovecot
smtpd_sasl_auth_enable = yes
smtpd_sasl_type = dovecot
smtpd_sasl_path = private/auth

# relay settings
smtpd_sender_restrictions = permit_sasl_authenticated,reject_unauth_destination

With those settings, you should be able to auth to the SMTP server, it should use dovecot (as noted by the smtpd_sasl_type), but there's one thing you need to change in the dovecot config to reflect that, you need to tell the dovecot's auth server to use a local unix socket, in 10-master.conf:

service auth {
  unix_listener auth-userdb {
  }

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

If you restart dovecot and postfix at this point, they should be able to talk to each other. One hint in debugging things, if you're using systemd to orchestrate everything (and you should), you can open the logs for both dovecot and postfix in two separate terminals while debugging your server:

# open logs (and follow) for dovecot
journalctl -f -u dovecot.service
# open logs (and follow) for postfix
journalctl -f -u postfix.service

Having them open and checking while I was trying to login and send test emails helped me a lot.

Relay (send email)

While you should be able to receive email, with the setup so far, you will not be able to send anything: remember that relay was closed. To fix this, and to make sure that whoever is sending email is a trusted service, I recommend using a third party server that knows how to handle outgoing mail: you can select from many providers, including Gmail if you want. In my case, I opted for AWS Simple Email Service (SES), because I was already using other AWS services from them. The setup should be similar, given that the provider allows sending email through an authenticated smtp server.

In your postfix's main.cf, you should add something like this:

relayhost = [email-smtp.us-east-1.amazonaws.com]:587
smtp_sasl_auth_enable = yes
smtp_tls_security_level = encrypt
smtp_sasl_password_maps = hash:/etc/postfix/sasl_passwd
smtp_use_tls = yes
smtp_sasl_security_options = noanonymous
smtp_sasl_tls_security_options = $smtp_sasl_security_options

What this is doing, is configuring postfix to use SES for relaying, turning on SASL on the smtp protocol, making sure we use TLS, letting postfix know where to find the password for sasl auth when connecting to a smtp server that requests authentication.

The /etc/postfix/sasl_passwd file looks something like this:

[email-smtp.us-east-1.amazonaws.com]:587 username:password

Both username and password are in plain text, so make sure the file is owned by root and chmod 0600. I tried to use dovecot's auth for that as well but I was not successful. Maybe in the future ^_^.

Sieve for mail filtering

Up to now, you can receive email, it will be scanned by spamassassin (you should check the headers of your email, they should contain something like this: X-Spam-Checker-Version: SpamAssassin 3.4.1 (2015-04-28) on alarmpi). But everything will still arrive in your Inbox. In order to take action on that, you can use something like Sieve.

Dovecot supports Sieve through pigeonhole, you should have this already installed from the first part of this post.

You can go super fancy with filters: having global filters that act on all of your users' emails, vacation responders, whitelists, etc. You can also have a per-account filter (just a single file in your virtual user home) and that will be triggered every time you receive email. That's the approach I went for simplicity (I only have one account, my own personal one) and it makes it easy for me to add/remove things as I want.

To enable Sieve support in dovecot, you need to modify the corresponding config, in my case it was 90-sieve.conf:

plugin {
  sieve = file:~/current.sieve
}

This is telling Dovecot that if there is a file named current.sieve in the user's home (virtual user home, in my case /home/vmail/domain.com/user), try to use that as the active sieve filtering script.

My script is basically filtering for spam, and also some sort of white-list: only specific users go directly to Inbox, the rest (if they're not spam), go to Inbox.Other:

require ["fileinto", "reject", "mailbox", "imap4flags"];

if header :contains "X-Spam-Flag" "YES" {
  addflag "\\Seen";
  fileinto :create "Spam";
} elsif not address :is "from" ["m@rolando.cl", "user@domain.com", "user2@domain.com"] {
  fileinto :create "INBOX.Other";
}

Writing Sieve filters can be tricky at first, but they are super helpful. You can find a bunch of examples in the Dovecot documentation.

Final remarks

If you followed everything, you should have a working email server that will receive all your email (make sure to have backups and/or encrypt /home/vmail) and securely send your email through a trusted provider. I hope you enjoyed and that this tutorial/post helped you learn more about Email serving. It's not an easy task but for me personally, it's something I wanted to do for a long time.

Copyright © 2012-2018 Rolando Abarca - Powered by orgmode

Creative Commons License

This work is licensed under a Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International License unless otherwise noted.