a simple mail server on openbsd

setting up a simple mail server using opensmtpd, dovecot, and rspamd

published on 2021-10-11

this is mostly a guide for myself in-case my mail server breaks or i have to do this sort of thing again, but hope i do hope it maybe proves useful for anyone else on the internet?

(note; this was done on openbsd 6.9, in case you're reading this in tha future and shit's different!)

Port 25

port 25 is the standard SMTP port. before doing anything, you want to make sure its open!

an easy way to do this is just with nc(1). in the terminal on the host machine, type

# nc -l 25

(this listens to port 25 on the machine)

and on another machine, outside of the hosts local network, type

# nc -zv <host ip> 25

if you see something like

Connection to [ip] on port [tcp/smtp] succeeded!

you're good to go! if not, you may need to talk to your server provider!

DNS

you must create A, AAAA records pointing to the host, and an MX record pointing to the domain of the prior.

a quick example is the records of my own mail server,

mail.piapiac.org   A       144.202.127.8
mail.piapiac.org   AAAA    2001:19f0:6001:3d76:5400:03ff:fe99:fa79
piapiac.org.       MX 0    mail.piapiac.org.

rDNS

reverse DNS is necessary to make sure your emails will not get sent to spam!

the way you manage this will depend on your server provider, whether it be vultr, digitalocean, linode, openbsd.amsterdam, some other provider, or you're rolling your own (talk to your ISP!)

you can check this pretty easily using host(1).

# host 144.202.127.8
8.127.202.144.in-addr.arpa domain pointer mail.piapiac.org.

SPF

SPF is a mechanism which specifies the mail servers that are allowed to send email for your domain. if you don't use it, you probably will get flagged as spam!

you can define it in different ways by adding a TXT record (not an SPF record, which is deprecated!) to your domain.

i will set it up so only my mail server can send mail on behalf of piapiac.org,

piapiac.org.       TXT     "v=spf1 mx -all"

DKIM

DKIM is an authentication method that allows receivers to check that an email coming from a specific domain was indeed authorized by the owner of that domain.

along with rDNS and SPF, this is pretty much necessary to make sure you don't end up in someone's spam inbox!

DKIM provides the ability for senders to sign messages using a private key, and for receivers to verify signed messages using the public key.

you can use openssl(1) to generate the private/public keys. i will be generating 1024 bit keys, but you can do 2048 if you wish!

# openssl genrsa -out /etc/mail/private.key 1024
# openssl rsa -in /etc/mail/private.key -pubout -out /etc/mail/public.key

do not share your private key with anyone! it is called as such for a reason.

for your public key, create a TXT record,

; <selector>._domainkey.<domain>                 v=DKIM1;    p=<pubkey>
hydref102021._domainkey.piapiac.org.    TXT     "v=DKIM1;t=s;p=MIGf..."

the selector can be anything! the date, a name, office, whatever.

you can read more about the tags used in the TXT record on the DKIM specification.

we will set up the software to handle DKIM signing later!

DMARC

the final authentication mechanism on this list! a DMARC policy allows the sender to tell the receiver what to do if neither SPF or DKIM pass.

create another TXT record similar to DKIM,

;      <domain>.                                   ruf=<admin>
_dmarc.piapiac.org. TXT "v=DMARC1;p=reject;pct=100;ruf=mailto:_c@piapiac.org"

you can read the DMARC spec for which tags do what!

TLS Certificate

next step is to obtain a TLS certificate! fortunately, acme-client(1) is included in the openbsd base system, along with httpd(8), so this can be done fairly easily.

also included is an example httpd.conf and acme-client.conf!

so, let's configure both!

# cp /etc/examples/httpd.conf /etc/httpd.conf
# vi /etc/httpd.conf

make changes as needed! the configuration is extremely straight forward, and you can read the manpage if you need help.

yours should look something like this,

server "mail.piapiac.org" {
   listen on * port 80
   location "/.well-known/acme-challenge/*" {
       root "/acme"
       request strip 2
   }
}

check to make sure the configuration is okay, then start httpd!

# httpd -n
configuration OK
# rcctl -f start httpd
httpd(ok)

now for acme-client,

# cp /etc/examples/acme-client.conf /etc/acme-client.conf
# vi /etc/acme-client.conf

make changes as needed! again, the configuration is very straight forward, and you can read the manpage if you need help.

it should look something like this,

domain mail.piapiac.org {
   domain key "/etc/ssl/private/mail.piapiac.org.key"
   domain full chain certificate "/etc/ssl/mail.piapiac.org.fullchain.pem"
   sign with letsencrypt
   # OR sign with buypass
}

that's it! now run acme-client(1),

# acme-client -v mail.piapiac.org
acme-client: /etc/ssl/private/mail.piapiac.org.key: created
acme-client: /etc/ssl/mail.piapiac.org.fullchain.pem: created

you can use cron(8) to attempt to renew your certificate daily

# echo "acme-client -v mail.piapiac.org" >> /etc/daily.local

that's it! now you should have TLS certificates up and working.

OpenSMTP

luckily, smtpd(8) is also included in the openbsd base system, so all that needs to be done here is configuring it!

first, let's edit /etc/mail/aliases. we can delete the entire file and roll our own!

the format of the table file is pretty straightforward; we'll set some aliases, and some as vmail for dovecot to handle.

root@piapiac.org: cricket@piapiac.org
abuse@piapiac.org: cricket@piapiac.org
postmaster@piapiac.org: cricket@piapiac.org
cricket@piapiac.org: vmail
name@piapiac.org: vmail

then create a new file /etc/mail/passwd and fill it with,

cricket@piapiac.org:<hash>
name@piapiac.org:<hash>

you can generate the hashes using smtpctl(8),

# echo cricket@piapiac.org:$(smtpctl encrypt <password>) >> /etc/mail/passwd
# echo name@piapiac.org:$(smtpctl encrypt <password>) >> /etc/mail/passwd

now all that's left to configure for smtpd is smtpd.conf.

# vi /etc/mail/smtpd.conf

this file should already be there, but we are going to change it completely! i'll walk you through a minimal configuration;

first, let's tell opensmtpd about the certificates we generated earlier,

pki mail.piapiac.org cert "/etc/ssl/mail.piapiac.org.fullchain.pem"
pki mail.piapiac.org key "/etc/ssl/private/mail.piapiac.org.key"

let's include the tables we created earlier,

table aliases file:/etc/mail/aliases
table passwd file:/etc/mail/passwd

then, let's define some filters to kick away spam!

filter rdns_filter phase connect match !rdns disconnect "550 No rDNS"
filter fcrdns_filter phase connect match !fcrdns disconnect "550 No FCrDNS"
filter rspamd proc-exec "filter-rspamd" # TODO: set this up later!!

you can use junk instead of disconnect "550" to send spam emails to your junk folder rather than bouncing them.

tell smtpd to listen for incoming connections and apply appropriate filters,

listen on all tls pki mail.piapiac.org \
   filter { rdns_filter, fcrdns_filter, rspamd }

and for outbound connections from your email client. filtered through rspamd to do DKIM signing, which we will set up later!

# smtps
listen on all port 465 smtps pki mail.piapiac.org \
   filter rspamd auth <passwd>

# startls
listen on all port 587 tls-require pki mail.piapiac.org \
    filter rspamd auth <passwd>

then define an action for dovecot to deal with our virtual mail (we'll set it up later)

action lmtp_deliver lmtp "/var/dovecot/lmtp" rcpt-to virtual <aliases>

and one to relay mail and let the host know who we are,

action "outbound" relay helo main.piapiac.org

now for match directives,

match from any for domain "piapiac.org" action lmtp_deliver
match from local for local action lmtp_deliver

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

that should be all that's needed for OpenSMTP!

though you should probably validate your config:

# smptd -n
configuration OK

Dovecot

Dovecot is the IMAP server we will be using. it is not included in the base openbsd system, so we need to install it ourselves.

first, we need to lift the default limits on file descriptors as they're insufficient to run Dovecot. in the login.conf(5) file, let's append the following:

dovecot:\
   :openfiles-cur=1024:\
   :openfiles-max=2048:\
   :tc=daemon:

and rebuild the login.conf.db file if necessary,

# [ -f /etc/login/conf.db ] && cap_mkdb /etc/login.conf

then, let's create the vmail user using useradd(8),

# useradd -m -d /var/vmail vmail

now let's configure dovecot,

# pkg_add dovecot
...
# ls /etc/dovecot/conf.d
10-auth.conf
10-directory.conf
10-loggin.conf
10-master.conf
10-metrics.conf
10-ssl.conf
15-lda.conf
15-mailboxes.conf
20-imap.conf
20-lmtp.conf
...

holy cow! that's a /lot/ of configuration files. i'm sure we don't need that many. in fact, dovecot's own quick configuration manual says as such.

# rm -rf /etc/dovecot/*
# vi /etc/dovecot/dovecot.conf

cool, now lets edit this file and fill in some basic configuration. first, let's tell dovecot what protocols we intend to be serving and configure them,

protocols = imap lmtp

service lmtp {
   unix_listener lmtp {
       user = vmail
       group = vmail
   }
}

service imap-login {
   # STARTTLS
   inet_listener imap {
       port = 143
   }

   # IMAPS
   inet_listener imaps {
       port = 993
   }
}

we also intend to use SSL, and have it fail for non-SSL or non-plaintext authentication.

ssl = required
ssl_cert = </etc/ssl/mail.piapiac.org.fullchain.pem
ssl_key = </etc/ssl/private/mail.piapiac.org.key

now let's tell dovecot about our passwd file and create a static user database, where and what format to store our messages

passdb {
   driver = passwd-file
   args = scheme=CRYPT /etc/mail/passwd
}

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

mail_location = maildir:/var/vmail/%d/%n/Maildir

and finally, let's set-up some inbox folders,

namespace inbox {
   inbox = yes

   mailbox Drafts {
       auto = no
       special_use = \Drafts
   }
   mailbox Sent {
       auto = subscribe
       special_use = \Sent
   }
   mailbox Junk {
       auto = create
       special_use = \Junk
   }
   mailbox Trash {
       auto = no
       special_use = \Trash
   }
}

that should be all the configuration you need for dovecot! just check to make sure your configuration is okay,

# dovecot -n

if you don't get an error here, you're good to go! on to the final step!

rspamd

rspamd works as a spam filter, but more importantly, will be what we use for DKIM signing! pretty cool, huh?

it is not included in the OpenBSD base system, so we must install it! let's install rspamd, opensmtpd-filter-rspamd (which lets us use rspamd as a filter on opensmtpd), and redis, which is a database required to make rspamd work.

# pkg_add rspamd opensmtpd-filter-rspamd redis

cool! now let's configure rspamd. we don't really have to change any of the configuration defaults, aside from setting up dkim signing.

# cat << EOF > /etc/rspamd/local.d/dkim_signing.conf
allow_username_mismatch = true;

domain {
   piapiac.org {
       path = "/etc/mail/private.key";
       selector = "hydref102021";
   }
}

let the selector be whatever you set up as the DKIM selector earlier, and finally, let's make sure the rspamd config doesn't contain any errors,

# rspamadm configtest
syntax OK

if this command doesn't produce any errors, you're good to go!

up and running

now that everything's nice and configured, let's get it up and running!

# rcctl enable redis rspamd dovecot smtpd
# rcctl start redis rspamd dovecot smtpd
redis(ok)
rspamd(ok)
dovecot(ok)
smtpd(ok)

(if one is already running, it won't show name(ok)... you might have to rcctl restart instead!)

you should be good to go! using your favourite e-mail client, try to log-in! send out an e-mail, send yourself an message from another e-mail. just make sure everything is working :)

if something's not working, you can troubleshoot by looking in /var/log/maillog,

# tail -f /var/log/maillog

the messages here should give you an idea about what's going on!

closing notes

i recommend reading the docs of opensmtpd, dovecot, and rspamd to see what kind of stuff you can do with/change with them.

also; about OpenBSD, i've only been running my server on it since relatively recently, and i am very much in love with it!

the simplicity of the operating system and the tools that comes with it (seriously, httpd(8) is the best, simplest web server i've used, and acme-client(1) is the only sane acme client i've used) make it really easy to use.

and the manpages are really nice! they're informative but not cluttered (try man gcc on your machine for a counter-example)

this is the first e-mail server i've set-up where i didn't have loads of issues afterward that i had to spend hours troubleshooting! thanks opensmtpd :)c

if you haven't used OpenBSD before, i urge you to try it, whether it be on your server or desktop!