On Running My Own Email Infrastructure

A few weekends ago, I set up my own email infrastructure. The stack consists of OpenBSD as the operating system, OpenSMTPD as the SMTP server, and Dovecot as the IMAP server. I mostly followed this guide to get everything up and running, and this guide when trying to get server-side email encryption working (which I later disabled, more on that later). The non-sensitive parts of my config are listed below.

There is a common refrain that you’ll hear from many people on the Internet that hosting your own email infrastructure just isn’t worth it. “You’ll have delivery problems”, “you’ll drop messages”, etc. While my infra has only been running for a few weeks, I’m happy to report that I have not had any issues with spam, deliverability, or dropped messages. It really wasn’t that hard to set up, though it did take a little while to understand a few concepts that I hadn’t fully internalized before.

Benefits of Self-Hosted Email

Speaking of concepts I didn’t understand, this project helped me learn a lot about a pretty ubiquitous part of the Internet. For example, I now understand the relationship between SMTP, IMAP, and LMTP. Likewise, I know more about how mailers determine if you are sending spam, and how to SMTP spoofing prevention works. Finally, I learned more about the security characteristics of email (pro tip: do not send sensitive information unencrypted over email. Almost certainly, at some point in the pipeline the content will be visible as plaintext, and you have very little recourse to prevent this outside of encrypting the contents of an email).

Outside of learning a bunch of cool stuff about a core piece of Internet architecture, I now have a great way to organize my email with aliases and separate accounts. I currently have an accounts for the following:

Finally, it feels good to own my data and be an active participant in the Internet ecosystem.

Deciding Against Server-Side Encryption

Relatedly, as far as server-side encryption of email contents goes, I decided to disable it. The threat model of an attacker getting access to my emails is something like this:

  1. My IMAP credentials get compromised
  2. My server credentials get compromised
  3. My backup drive gets physically compromised (stolen, etc)

Server-side encryption does not help against situation 1. While the emails are indeed encrypted on-disk, they are transparently decrypted on the way to the IMAP client. Avoiding this would likely require a different way of accessing my email (which I may look into at a later date).

Situation 2 is a little more nuanced. While theoretically server-side encryption would prevent the attacker from just copying my email folder and stealing my data, the reality is that the encryption keys for the email are stored on the same disk in the same server. There are ways around this certainly, but the juice is not worth the squeeze in my opinion. It also makes backing up my emails a bit more complicated.

Speaking of backups, I am encrypting them. Each email folder is copied, tar’d, and then encrypted before I fetch them from the server. This should hopefully prevent situation 3.

At the end of the day, if you are sending sensitive information over email, be sure to employ some form of encryption such as PGP.

Conclusion

I think more people should self-host their own email. If you already have some services self-hosted, email really isn’t that hard. Follow the guides I linked, or adapt my configurations below. After having done this, I feel like I’ve leveled up as an Internet citizen.

I will try to post a follow up after a longer period of time has passed with any additional thoughts I have about all of this.

One final tip

If you do decide to implement server-side encryption for your emails, ensure that you set OpenSMTPD to use LMTP to talk to Dovecot. OpenSMTPD doesn’t do encryption directly.

Configurations

Here are my configurations with any “sensitive” paths replaced with <path>.

/etc/mail/smtpd.conf

pki mail.idylls.net cert "<path>"
pki mail.idylls.net key "<path>"

filter check_dyndns phase connect match rdns regex { '.*\.dyn\..*', '.*\.dsl\..*' } \
    disconnect "550 no residential connections"

filter check_rdns phase connect match !rdns \
    disconnect "550 no rDNS is so 80s"

filter check_fcrdns phase connect match !fcrdns \
    junk

filter senderscore \
    proc-exec "filter-senderscore -blockBelow 10 -junkBelow 70 -slowFactor 5000"

filter rspamd proc-exec "filter-rspamd"

table aliases file:/etc/mail/aliases

listen on all tls pki mail.idylls.net \
    filter { check_dyndns, check_rdns, check_fcrdns, senderscore, rspamd }

listen on egress port submission tls-require pki mail.idylls.net auth filter rspamd

action "recv" lmtp "/var/dovecot/lmtp" alias <aliases>
action "outbound" relay helo mail.idylls.net

match from any for domain "idylls.net" action "recv"
match for local action "recv"

match from any auth for any action "outbound"
match for any action "outbound"

/etc/dovecot/local.conf

ssl = required
ssl_cert = <<path>
ssl_key = <<path>

disable_plaintext_auth = yes

mail_attribute_dict = file:<path>
mail_plugins = $mail_plugins

protocols = imap lmtp

service lmtp {
    unix_listener lmtp {
    }
}

protocol imap {
  mail_plugins = $mail_plugins imap_sieve
}

plugin {
  sieve_plugins = sieve_imapsieve sieve_extprograms
  sieve_global_extensions = +vnd.dovecot.pipe +vnd.dovecot.environment

  imapsieve_mailbox1_name = Junk
  imapsieve_mailbox1_causes = COPY APPEND
  imapsieve_mailbox1_before = file:/usr/local/lib/dovecot/sieve/report-spam.sieve

  imapsieve_mailbox2_name = *
  imapsieve_mailbox2_from = Junk
  imapsieve_mailbox2_causes = COPY
  imapsieve_mailbox2_before = file:/usr/local/lib/dovecot/sieve/report-ham.sieve

  imapsieve_mailbox3_name = Inbox
  imapsieve_mailbox3_causes = APPEND
  imapsieve_mailbox3_before = file:/usr/local/lib/dovecot/sieve/report-ham.sieve

  sieve_pipe_bin_dir = /usr/local/lib/dovecot/sieve
}
Published on 2021-09-30