December 4, 2018

Setting Up Your Own Email Server With OpenBSD

Creating your email may be seen as equivalent to making your own cheese, or crafting your own beer: it is kinda of hipster thing. There are so many email services out there so why would you spend the time to do that? What I like about it is that it allows me to learn to work with OpenBSD through a fun, useful project, and it’s kinda of radical thing to do. Rather than trusting a giant company to handle and manage all your email, you will be sticking to the man (kind of at least).

Why OpenBSD?

So far I have really enjoyed working with OpenBSD. It is not bloated with features I will never use, it is intuitive, and the firewall is easy to configure. Moreover OpenBSD is perhaps best known as a very secure OS. OpenBSD also seems to be committed to the Unix Philosophy. This also makes the code easy to follow and understand when you are troubleshooting issues, as each application and utility is meant to do one thing and do it well.

Building an OpenBSD server

This is what you will need before starting:

  • An OpenBSD server. I use Vultr and highly recommend them. Note that this tutorial is specific to OpenBSD 6.4, as the syntax for the SMTP service configuration file is different than that of previous versions.
  • An domain, preferably with an awesome TLD.
  • Some patience.

Login to your OpenBSD server and setup a new user account. There are plenty of resources online on how to do that, so we will focus on the email setup. You will also need to make sure your user is a member of the wheel group. Once you’ve done that, you will need to install a few packages:

doas pck_add dkimproxy
doas pkg_add dovecot

We dont need anything else for now.

PF Setup

The first thing we need to do is set up our firewall rules to allow inbound and outbound email. In OpenBSD this is done using PF, a pretty simple but effective firewall that comes with OpenBSD. So use your favorite text editor and edit /etc/pf.conf.

7c0$ doas cat /etc/pf.conf
inbound_tcp = "{  https, submission, imaps, smtp, smtps, imap, 1977 }"

set skip on lo

block all

# Enable logging on egress interface
set loginterface egress

table <badhosts> persist

# ICMP
icmp_types = "{ 0, 8, 3, 4, 11, 30 }"

pass in quick inet proto icmp icmp-type $icmp_types
pass in quick inet6 proto icmp6

pass in on egress proto tcp to port $inbound_tcp
block on vio0 from <badhosts> to any
# allow all out
pass out

Pretty simple. We setup a variable that contains all the ports we will need and we allow incoming traffic on the inet and inet6 interfaces, which are the interfaces that your server uses to communicate with the world. You can make your configuration tighter and fancier, but this should suffice for our purposes. I also added a way to block certain IP addresses, as you may start seeing bots and scanners trying to login to your OpemSMTPd services. You can add hosts to badhosts by typing sudo pfctl -t badguys -T add <theIP> in your terminal. Once you are done making your edits, run the following to apply your rules:

pfctl -f /etc/pf.conf

If you get any errors, you may want to run pfctl -nf /etc/pf.conf to check whether your pf configuration is valid.

The last thing we want to do before getting started with OpenSMTPd is setup some certificates:

openssl genrsa -out /etc/ssl/private/mail.example.com.key 4096
openssl req -new -x509 -key /etc/ssl/private/mail.example.com.key -out /etc/ssl/mail.domain.com.crt -days 1440

Make sure your certs can only be accessed by root:

chmod 500 /etc/ssl/private/mail.example.com.key
chmod 500 /etc/ssl/mail.domain.com.crt

The above commands create our private and public certificates.

OpenSMTPd

This is where the fun happens: configuring OpenSMTPd. I am first going to show you what my configuration looks like:

7c0$ doas cat /etc/mail/smtpd.conf

# Tell OpenSMTPd where our certs are
pki mail.7c0.network key "/etc/ssl/private/mail.domain.com.key"
pki mail.7c0.network cert "/etc/ssl/mail.domain.com.crt"

# Listen on our local interface on port 10028 and tag email with DKIM_OUT (nothing fancy, just a string tag)
listen on lo0
listen on lo0 port 10028 tag DKIM_OUT

# Listen on SMTP, SMTPS, SUBMISSION in on egress interface and require authentication
listen on egress inet4 tls pki mail.domain.com auth-optional   # MTA
listen on egress inet4 smtps pki mail.domain.com auth
listen on egress inet4 port submission tls-require pki mail.domain.com auth   # MSA (auth required)

# This are our email aliases
table aliases db:/etc/mail/aliases.db

# Create some actions, kinda like IFTTT actions (yeap, that IFTTT)
action dovecot lmtp "/var/dovecot/lmtp" alias <aliases> # lmtp will move our messages to our inbox
action add_dkim relay host smtp://127.0.0.1:10027 
action "relay" relay
action dovecot_deliver maildir "~/mbox"

# use the above actions
match from any for domain "example.com" action dovecot
match tag DKIM_OUT for any action "relay"
match from local for any action add_dkim
match auth from any for any action add_dkim

The fist few lines tell SMTPD to listen on port 10028 with th loopback interface. Next we setup a number of actions that we then use in the last few lines. We also setup a table of aliases. Basically we do that so that email directed to root, or say postamaster@example.com is redirected to you. This is what will happen for incoming email:

  1. Incoming email not meant for our local server will be sent to dkim via smtp://127.0.0.1:10027
  2. dkim will grab the email on port 10027 and sign it with our certs (we will get to dkim in a moment)
  3. dkim will return the signed email to OpenSMTPd on port 10028, hence the line that tells OpenSMTPd to listen on that port.
  4. Mail tagged with DKIM_OUT will be relayed to the world, mind blowing.

Now let’s generate the aliases table. Edit the /etc/mail/aliases file and make sure to add a line like this:

root: yourusername

Now generate the table by typing newliases. That should create /etc/mail/aliases.db.

dkim

We are now ready to configure dkim. All outgoing email will be sent to dkim so it is signed before it gets sent to its destination. This is so that the email clients have a way to check that you are the one sending email from example.com and not some evil con artist. Go ahead and open dkim:

vim /etc/dkim

The configuration is pretty simple.

7c0$ doas cat /etc/dkimproxy_out.conf
# specify what address/port DKIMproxy should listen on
listen    127.0.0.1:10027

# specify what address/port DKIMproxy forwards mail to
relay     127.0.0.1:10028

# specify what domains DKIMproxy can sign for (comma-separated, no spaces)
domain    example.com

# specify what signatures to add
signature dkim(c=relaxed)
signature domainkeys(c=nofws)

# specify location of the private key
keyfile   /var/dkimproxy/dkim.key

# specify the selector (i.e. the name of the key record put in DNS)
selector  dkimselector

All we are doing is telling dkim to listen on port 10026 (this is where OpenSMTPd will send emails for signing), and to relay those emails to port 10028 once they are signed. We also specify our email box and the certificates that dkim will use for signing emails.

We need to configure another cert, this time for dkim:

openssl genrsa -out /etc/ssl/private/dkim.key 1024
openssl rsa -in /etc/ssl/private/dkim.key -pubout -out dkim_public.key

Before we forget, we also need to create the folder where email will be sent to (make sure to do is as the user who will get all the email):

mkdir ~/mbox

Great, almost there. Now we need to setup dovecot so that we can use fancy schmancy e-email clients like Thunderbird to send and receive emails.

dovecot

We just need to setup a 3 files. So sudo vim /etc/dovecot/dovecot.conf:

## Dovecot configuration file
# Protocols we want to be serving.
protocols = imap lmtp 

ssl_key = </etc/ssl/private/mail.exmaple.com.key
ssl_cert = </etc/ssl/mail.example.com.crt

listen = *, ::

Let’s tell dovecot where to deliver email to:

7c0$ doas vim /etc/dovecot/conf.d/10-mail.conf
# path given in the mail_location setting.
   mail_location = mbox:~/mail:INBOX=/var/mail/%u

and lastly some good old SSL stuff:

7c0$ doas cat /etc/dovecot/conf.d/10-ssl.conf

# SSL/TLS support: yes, no, required. <doc/wiki/SSL.txt>
ssl = required
ssl_key = </etc/ssl/private/mail.example.com.key
ssl_cert = </etc/ssl/mail.example.com.crt

Login resources

With OpenBSD you have to adjust the resources that different services use. We need to do that for dovecot. Go ahead and open /etc/login.conf and add this to the end of that file:

#
# DoveCot
#
dovecot:\
        :openfiles-cur=512:\
        :openfiles-max=1024:\
        :tc=daemon:

DNS records and config

There are a number of things you need to setup with your DNS and VPS provider:

  1. Go to your DNS provider and make sure to configure reverse DNS. It should match your mail server address, something like mail.example.com.

  2. Create a TXT record with your DNS provider like this, which sets the sender policy:

    example.com      IN   TXT    "v=spf1 mx mx:domain.tdl -all"
    
  3. Create A records for IPv4 and AAAA for IPv6 that point to your email server domain name (use your own ips below):

    @      IN  A      203.0.113.41
    @      IN  AAAA   2001:db8::2
    mail   IN  A      203.0.113.42
    mail   IN  AAAA   2001:db8::3
    
  4. Create an MX record:

    IN  MX  10 mail.example.com.
    

Now we are ready to get everything started. Let’s first enable all required services

Getting everything started

doas rcctl enable smtpd
doas rcctl enable dkimproxy
doas rcctl enable dovecot

And now lest start our rad services:

doas rcctl start smtpd
doas rcctl start dkimproxy
doas rcctl start dovecot

And that is it! Well, it can be. Do not get frustrated if your setup doesn’t work right away. As things usually go, finding problems and figuring out solutions to those problems (and reading blog posts and man pages along the way) tends to be a great way to learn.

Note that this setup packages the essentials but we also needs a way (at least eventually) to manage spam. That will come in a later post as I play with spamd configurations in my server.

Arrrgh! But it does not work matey!

Try the following

  • Make sure your VPS provider allows outbound email traffic. I spent more hours than I care to admit troubleshooting an issue where I could not send emails until I found an article from vultr stating that “….”

  • Read your mail log. You can see your mail log live using tail -f /var/log/mailog/.

  • If SMTPD fails to start run smtpd in your terminal (yeap, just like that). SMTPd will then check for configuration errors and tell you what you did wrong.

Great, now connect your email client to your server and start sending end receiving email. Not that this setup does not include a way to block spam. We will configure that in a future blog post.

© hex0punk 2023