kaashif's blog

Programming, with some mathematics on the side

Moving to my own email server


There I was, a loyal user of http://mail.zoho.com, when I decided to download all of my emails. For archival purposes, you know.

So I fired up mbsync, set everything up, let 'er rip, but after only about 10,000 emails downloaded, I got this error:

IMAP command 'AUTHENTICATE PLAIN <authdata>' returned an error: NO [ALERT] Your account is currently not accessible via IMAP due to excessive usage. Kindly try after some time.
*** IMAP ALERT *** Your account is currently not accessible via IMAP due to excessive usage. Kindly try after some time.

What sort of...anyway, this was unacceptable, so I decided to set up my web server as a mail server.


Everything you need to run a mail server is already installed on OpenBSD. Of course, if you want to use DKIM (and you really do, or your mail will be sent to spam or just not received by anyone), you need to install dkimproxy.

$ doas pkg_add dkimproxy

That's basically it.

Setting up OpenSMTPD

OpenSMTPD has a pf-inspired config file. That is to say, it's very easy to grok.

Step 1: Set up an SSL certificate

This is very easy with acme-client, which comes with OpenBSD.

Your /etc/acme-client.conf should already have the letsencrypt authorities in it, so you just need to add your domain to the bottom.

It's so easy, look at man acme-client.conf. For reference, here is my domain entry:

domain earendil.kaashif.co.uk {
    alternative names { kaashif.co.uk git.kaashif.co.uk
    www.kaashif.co.uk mail.kaashif.co.uk }
    domain key "/etc/ssl/private/earendil.kaashif.co.uk.key"
    domain certificate "/etc/ssl/earendil.kaashif.co.uk.crt"
    domain full chain certificate "/etc/ssl/earendil.kaashif.co.uk.fullchain.pem"
    sign with letsencrypt

Then you run acme-client -vFAD earendil.kaashif.co.uk as the man page suggests and everything will Just Work, with your new certificates ending up where you specified.

Put that command in /etc/monthly.local and it will run every month, keeping everything valid and non-expired.

Step 2: Set up your virtual users

My mail server will be single user, so I just slapped this into /etc/mail/virtuals:

kaashif@mail.kaashif.co.uk kaashif

I don't even think you really need it, but in the future you may want to add a webmaster email, or mailing lists for specific projects, etc, so its good to have it.

Although you didn't add anything to /etc/mail/aliases, you should still run newaliases just to generate the aliases.db that OpenSMTPD uses.

Step 3: /etc/mail/smtpd.conf

This is the big one, where the magic happens.

Actually, this file is so simple, I can just dump it here and explain it:

pki mail.kaashif.co.uk certificate "/etc/ssl/earendil.kaashif.co.uk.crt"
pki mail.kaashif.co.uk key "/etc/ssl/private/earendil.kaashif.co.uk.key"

These are the letsencrypt certs you made earlier that smtpd will use.

table aliases file:/etc/mail/aliases
table virtuals file:/etc/mail/virtuals

This declares the tables so you can use them later on in the file as <aliases> and <virtuals>.

listen on all
listen on all port 10028 tag DKIM

This requires some explanation. We listen on all interfaces on the usual smtp ports for mail (this is where mailservers will send us mail). But on port 10028, we listen for mail tagged DKIM. This is where dkimproxy comes in.

When we send mail from this server, we want it signed with DKIM. So the dkimproxy_out daemon listens on port 10027, we send it mail and it sends it back (signed) on port 10028 and the session has a DKIM tag.

accept from any for domain "mail.kaashif.co.uk" virtual <virtuals> deliver to mda "/usr/local/bin/procmail"

Procmail is a well-known mail filtering agent. Explaining how to configure it is out of the scope of this post, but basically you just make a ~/.procmailrc in the users' home and put some rules in there. Where the mail ends up (mailbox, maildir, somewhere else) is entirely up to Procmail, smtpd forgets about it after that.

accept from local for local alias <aliases> deliver to mbox

We don't want to mix local and non-local mail, so all that local-only daily output and insecurity output will end up in our normal mbox in /var/mail. But of course, there's nothing stopping you from configuring Procmail to put some non-local mail in /var/mail too.

accept tagged DKIM for any relay
accept from local for any relay via smtp://

The only sessions that will ever be tagged DKIM are ones made by dkimproxy. So it is safe to just relay them all. Any mail sent from here is sent to dkimproxy on port 10027, to be signed and returned later.

Setting up DKIM and SPF

Step 1: Generating a key

DKIM works by signing messages with a private key stored locally. Then untrusting mail servers get the public key from a DNS TXT record and check the signature. We don't have any keys yet.

$ mkdir -p /etc/mail/dkim
$ cd /etc/mail/dkim
$ openssl genrsa -out private.key 1024
$ openssl rsa -in private.key -pubout -out public.key

Why only 1024 bits? Because that's all that fits in a single TXT record, it's a bit incovenient otherwise.

Now, make those readable by dkimproxy and no-one else:

$ chmod 0600 *.key
$ chown _dkimproxy:_dkimproxy *.key

Now edit /etc/dkimproxy_out.conf's keyfile line to point to the private key you just made:

keyfile   /etc/mail/dkim/private.key

Step 2: Putting everything into DNS

This is important since people will have no idea how to verify your sigs if they don't know your public key.

Make a record on selector1._domainkey.your.domain.tld, type TXT, with the following content:

v=DKIM1; k=rsa; p=<your public key>

When I say your public key, I mean go to your public key file, ignore the header and footer lines, concatenate each line then paste it there.

Also take the opportunity to set up SPF. Add a TXT record to your.domain.tld, with:

v=spf1 a mx ip4:<your ip address> ~all

Where your IP address is your static IP. If you don't have a static IP address, you'll probably forever be on every spam list ever. Contact your ISP to get a static IP, or get a VPS.

Final touches

Now just start everything:

$ rcctl enable dkimproxy_out
$ rcctl restart dkimproxy_out smtpd

And everything should work!

But now you wonder how I read mail? Easy, I SSH in and use mutt. Couldn't be any simpler.

But how do I check it on my phone? Dude, I just told you! SSH and mutt. I can use mail(1) if I really can't deal with a complicated TUI.

To my surprise, everything worked. http://www.mail-tester.com gave me a clean bill of health and I found my mails were able to reach all my friends and family.

And no arbitrary usage limits! I can download my many gigabytes of mail as many times as I want!

But of course, I do have to still download them from Zoho to upload them to my new server...