This is a question a lot of people with their own domain name or Unix experience might ask at some point, and you’ll probably get a lot of different answers.
This blog post by Lars Ingebrigtsen covers a lot of it, and is the source of inspiration for this blog post.
I have wanted to do this for a while, but it seemed like a hassle (if it seems like a hassle, then that’s probably not a good sign for me doing this…). I pay for tuffmail and haven’t had any problems with them.
Some enthusiast IMAP/email providers have limits on amounts of traffic, and I subscribe to some higher volume email mailing lists, so I thought it’d be worth looking at what it’d be like to host email myself. I basically got a “hello world” working with the approach below.
Working with a ‘smarthost’
What if you only needed to solve half of the hosting problem? One tricky part of hosting your own mail server is sending email without it ending up in everyone’s spam folder. What if you get a free or cheap provider for the handful of emails you send, and then just focus on hosting the receiving part? You can find a free-tier plan from SendGrid, or mailgun for sending? I’ll go into more details below.
Exim
The process starts with installing a MTA. I’m using Exim, since that was used in Lars’s original tutorial. I already have a server used to host this blog, and it already has Let’s Encrypt certificates installed on it, so I’m going to reuse it for email. Normally it isn’t a great idea to reuse a server like this, and given Exim’s history, it’s not something I’d do unless I was only hosting a blog backed by a public git repository…
Scripting
I modified Lars’s make-mta.sh
as a starting point. It sets up a
basic firewall, mta-sts, apt installs exim4-daemon-heavy clamav spamassassin clamav-daemon sasl2-bin bind9
, sets up the config, installs dovecot,
and prints some DNS settings. Unfortunately, it had some issues
parsing the $host
and $domain
variables, so I needed to go in and
clean things up. I might have lost spamassassin and clamav during the
config file regeneration.
SMTP MTA Strict Transport Security (MTA-STS)
MTA-STS is like HSTS for SMTP hosts. Since the SMTP RFC allows servers to deny STARTTLS, an attacker could intercept the connection to the SMTP server and keep the connection downgraded to intercept emails. MTA-STS is supposed to prevent this. Basically I have to host a file at https://mta-sts.mail.timmydouglas.com/.well-known/mta-sts.txt that contains something like:
version: STSv1
mode: enforce
mx: timmydouglas.com
max_age: 1209600
then create a DNS record:
_mta-sts.mail.timmydouglas.com. IN TXT "v=STSv1; id=20160831085700Z;"
The id there is basically for cache busting the well known text file.
I was able to configure this manually by creating
/etc/apache2/sites-enabled/mta-sts.conf
:
<VirtualHost *:443>
ServerName mta-sts.mail.timmydouglas.com
DocumentRoot /var/www/mta-sts
SSLCertificateFile /etc/letsencrypt/live/mail.timmydouglas.com/fullchain.pem
SSLCertificateKeyFile /etc/letsencrypt/live/mail.timmydouglas.com/privkey.pem
</VirtualHost>
Run certbot, tell it you want a cert for mta-sts.mail.timmydouglas.com, then it will modify the file for you to:
<VirtualHost *:443>
ServerName mta-sts.mail.timmydouglas.com
DocumentRoot /var/www/mta-sts
SSLCertificateFile /etc/letsencrypt/live/mta-sts.mail.timmydouglas.com/fullchain.pem
SSLCertificateKeyFile /etc/letsencrypt/live/mta-sts.mail.timmydouglas.com/privkey.pem
Include /etc/letsencrypt/options-ssl-apache.conf
</VirtualHost>
More info on Hardenize website. Has a verification tool
Exim
The debian package of exim4 is configured in a file
/etc/exim4/update-exim4.conf.conf
and it generates the exim config
file when you run /usr/sbin/update-exim4.conf
(weird?)
If you want an interactive configuration experience, you can run
dpkg-reconfigure exim4-config
My config looked like this:
dc_eximconfig_configtype='local'
dc_other_hostnames='timmydouglas.com;mail.timmydouglas.com'
dc_local_interfaces=''
dc_readhost='mail.timmydouglas.com'
dc_relay_domains=''
dc_minimaldns='false'
dc_relay_nets=''
dc_smarthost=''
CFILEMODE='644'
dc_use_split_config='true'
dc_hide_mailname='true'
dc_mailname_in_oh='true'
dc_localdelivery='maildir_home'
I’m using local because I want to accept incoming mail but not send email anywhere.
DNS
I have these set up to receive mail.
mail.timmydouglas.com. 3600 IN MX 10 timmydouglas.com.
mta-sts.mail.timmydouglas.com. 3600 IN CNAME timmydouglas.com.
_mta-sts.mail.timmydouglas.com. IN TXT "v=STSv1; id=20160831085700Z;"
timmydouglas.com. 900 IN A 52.156.153.159
So when an email is sent to timmy@mail.timmydouglas.com
it will
look up the MX record, follow the CNAME and connect to the IP, ideally
using the MTA-STS to ensure it goes to a secure port, SMTPS/465. Exim
will write it to ~timmy/Maildir, which dovecot reads when serving them
through IMAP.
Dovecot
apt install dovecot-imapd
then edit the ssl certs in
/etc/dovecot/conf.d/10-ssl.conf:
ssl = yes
ssl_cert = </etc/letsencrypt/live/timmydouglas.com/fullchain.pem
ssl_key = </etc/letsencrypt/live/timmydouglas.com/privkey.pem
Also set the mail_location in conf.d/10-mail.conf:
mail_location = maildir:~/Maildir
Offlineimap
To receive mail from Dovecot, I used this ~/.config/offlineimap/config
:
[general]
accounts = mailtimmydouglascom
pythonfile = ~/.config/offlineimap/helpers.py
[Account mailtimmydouglascom]
localrepository = mailtimmydouglascom-local
remoterepository = mailtimmydouglascom-remote
[Repository mailtimmydouglascom-local]
type = Maildir
localfolders = ~/mail/mailtimmydouglascom
[Repository mailtimmydouglascom-remote]
type = IMAP
remotehost = timmydouglas.com
remoteuser = timmy
remotepasseval = get_pass('mailtimmydouglascom')
sslcacertfile = /etc/ssl/certs/ca-certificates.crt
ssl = yes
Then run offlineimap -o -a mailtimmydouglascom -u basic
and the mail
should be put in ~/mail/mailtimmydouglascom
.
I want to try mbsync/isync someday because I hear it’s faster.
Sending with SendGrid
So, I’m not sure this is the ideal scenario–the service seems to be set up for marketing teams to send a bunch of marketing emails. I was able to create an account, add domain authentication by adding 3 CNAME records to my DNS, add then by adding a SMTP relay.
I added the relay to my authinfo.gpg:
machine smtp.sendgrid.net login apikey password your-password-here
Then editing my Emacs config:
(setq send-mail-function 'smtpmail-send-it
smtpmail-smtp-server "smtp.sendgrid.net"
smtpmail-stream-type 'ssl
smtpmail-smtp-service 465)
I was able to send (through SendGrid) and receive (through Exim/Dovecot) an email from Gmail successfully.
I probably need to think about this approach some more. I’m not sure if I would hit some gotchas by using different methods for sending and receiving for the same mail domain. Honestly, it might just be easier to send email using Exim than to have a separate smarthost. I didn’t even go into setting up the SPF, DKIM, and DMARC records (which probably aren’t that bad…). But for now, I’ll continue paying for email.