This is just your run-of-the-mill SMTP, IMAP, and CalDAV server. If you'd like to setup something similar, check out this article from poolp.org for a detailed overview, or keep reading for a quick run-down :)
Because I can. Being able to run a mail server is a handy skill for any sysadmin, and it ain't like it's terribly expensive to do so in this day and age of virtual servers and cloud computing. Also, the more people and organizations running their own mail servers, the less of a stranglehold centralized providers like Google and Microsoft get to have on email.
I mostly followed the setup steps from Gilles Chehade's excellent article on the subject when setting up this server. Considering he's one of the main OpenSMTPD developers, I'd say his site's an excellent resource in general on this sort of thing. I did deviate from his guide in a couple places, so the rest of this page documents how I've got everything setup, and how you might go about setting up something equivalent.
Some conventions (if you see these below, replace 'em with your own values):
[[DOMAIN]]
is your domain name
(mine: yellowapple.us
)
[[DKIM_PUBKEY]]
is your DKIM public key (see the
"DKIM Key" section below for instructions on how to create it)
(mine: MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDBKoo/ybHvozcsmfq7UqqhZK8aiHffceJT+j23nAtRGLI7KdihgIVKIUu3iGpZrbvu3e26MZCa+XlpoF8xuMkad12/Fj2WK7KImaKoufUN9Cn0zW558s69+4RBzDod4dhRD3/iRnILIXytHFIg+/vTU6pVJ1ti7gqxUxfkUNva3QIDAQAB
)
[[DKIM_SELECTOR]]
is your DKIM selector, which can
be anything (mine: yellowapple
)
[[USER]]
is a non-root local user of your
choosing (e.g. the one you created when installing OpenBSD)
Hosted as a VPS from 1984, which is based out of Iceland. I started using 'em years ago specifically because they support OpenBSD as an install option, and I've been absolutely happy with that decision.
Really, any hosting provider (be it VPS or dedicated) will do, provided it runs OpenBSD (if you're following these instructions), you've got an IP address that doesn't suck (i.e. not blacklisted by the rest of the email-sending world, whether individually or as part of a blacklisted subnet), and you can set rDNS for that IP address. 1984 passes all these criteria.
This server has 4 vCPUs, 8GB of RAM, 160GB of SSD space, and 5TB of monthly data transfer; all of these things are almost certainly overkill for a personal mailserver, but I also use it for other domains, and it's obviously hosting these instructions, so better safe than sorry. 1984's cheap enough that it ain't all that big of a deal.
A bog-standard
OpenBSD 6.8 6.9 7.4 install.
Some notes:
sysupgrade
.
rsync
, which I'll leave as an
exercise to the reader (but if you're reading this as an
actual guide to setup your own mail server from scratch, you
probably won't need to do that anyway).
Run pkg_add redis rspamd opensmtpd-filter-rspamd
to
get everything installed. We'll then need
an /etc/rspamd/local.d/dkim_signing.conf
allow_username_mismatch = true; domain { [[DOMAIN]] { path = "/etc/mail/dkim/mail.[[DOMAIN]].key"; selector = "[[DKIM_SELECTOR]]"; } }
Next up, for greylisting to work we
need /etc/rspamd/local.d/redis.conf
...
servers = "127.0.0.1";
...and /etc/rspamd/local.d/greylist.conf
:
servers = "127.0.0.1:6379";
Last but not least, we need to tell Rspamd about different
settings to use for mail we send. Once upon a time (i.e. at
some point between when I originally wrote this document and in
2024 when I'm now updating it) this didn't seem to be necessary,
but nowadays you need to tell Rspamd to not run your outbound
mails through the full anti-spam pipeline. I failed to do this,
and ended up wondering why Rspamd was greylisting everything I
tried to send (and also was failing to add DKIM signatures
despite the above), but adding a
custom /etc/rspamd/local.d/settings.conf
(and
telling OpenSMTPD to use it, which you'll see later) fixed it:
outbound { id = "outbound"; apply { groups_enabled = ["dkim"]; actions { reject = 100.0; greylist = 100.0; "add header" = 100.0; } } }
Now, let's generate our DKIM key, get the permissions dialed in, and fire up Rspamd:
openssl genrsa \ -out /etc/mail/dkim/mail.[[DOMAIN]].key 1024 openssl rsa \ -in /etc/mail/dkim/mail.[[DOMAIN]].key \ -pubout -out /etc/mail/dkim/mail.[[DOMAIN]].pub chown -R :_rspamd /etc/mail/dkim chmod 750 /etc/mail/dkim chmod 640 /etc/mail/dkim/* rcctl enable redis rcctl enable rspamd rcctl start redis rcctl start rspamd
Take a note of the contents of
the mail.[[DOMAIN]].pub
we created above; the
contents of that file (without the -----BEGIN PUBLIC
KEY-----
/ -----END PUBLIC KEY-----
and any
newlines) is what you'll need to use
for [[DKIM_PUBKEY]]
below.
My domain's hosted through Free DNS, and has been for years. Pretty much any other DNS host will work fine for this, too. You'll need a couple DNS records to reliably serve up mail:
mail.[[DOMAIN]]
to your
server's IP address
10:mail.[[DOMAIN]]
[[DOMAIN]]
for SPF,
like "v=spf1 +a +mx -all"
, (translation: our A
records and our MX records will send our mail; reject anything
else)
[[DKIM_SELECTOR]]._domainkey.[[DOMAIN]]
for DKIM,
like "v=DKIM1; p=[[DKIM_PUBKEY]]; t=s"
_dmarc.[[DOMAIN]]
for DMARC,
like "v=DMARC1;p=none;pct=100;rua=mailto:postmaster@[[DOMAIN]];"
We'll need an /etc/httpd.conf
:
server "mail.[[DOMAIN]]" { listen on * port 80 location "/.well-known/acme-challenge/*" { root "/acme" request strip 2 } location * { block return 302 "https://$HTTP_HOST$REQUEST_URI" } } # The following is optional, and might actually fail to work if you don't # already have SSL certs present; I haven't tested that. If you include this, # you'll probably want to "mkdir -p /var/www/htdocs/mail.[[DOMAIN]]" and include # at least an index.html in that folder. server "mail.[[DOMAIN]]" { listen on * tls port 443 tls { certificate "/etc/ssl/mail.[[DOMAIN]]/fullchain.pem" key "/etc/ssl/private/mail.[[DOMAIN]]/privkey.pem" } location "/.well-known/acme-challenge/*" { root "/acme" request strip 2 } location * { root "/htdocs/mail.[[DOMAIN]]" directory auto index } }
Then run rcctl enable httpd && rcctl start httpd
.
We're using Let's Encrypt, via OpenBSD's
built-in acme-client
. We'll need
an /etc/acme-client.conf
:
authority letsencrypt { api url "https://acme-v02.api.letsencrypt.org/directory" account key "/etc/acme/letsencrypt-privkey.pem" } domain mail.[[DOMAIN]] { domain key "/etc/ssl/private/mail.[[DOMAIN]]/privkey.pem" domain full chain certificate "/etc/ssl/mail.[[DOMAIN]]/fullchain.pem" sign with letsencrypt }
And now it's just a matter of running
acme-client -v mail.[[DOMAIN]] && rcctl reload
httpd
to get it going. To automatically renew, we can
add it to root
's crontab
, but it's
even easier to just make it part of the built-in weekly
maintenance by creating a /etc/weekly.local
like
so:
# certs next_part "Renewing TLS certificate:" acme-client mail.[[DOMAIN]] case $? in 0) echo "TLS certificate renwed; reloading relevant daemons..." rcctl reload httpd >/dev/null 2>&1 case $? in 0) echo "httpd reloaded" ;; *) echo "httpd failed to reload" ;; esac rcctl restart smtpd >/dev/null 2>&1 case $? in 0) echo "smtpd restarted (reload not supported)" ;; *) echo "smtpd failed to reload" ;; esac rcctl reload dovecot >/dev/null 2>&1 case $? in 0) echo "dovecot reloaded" ;; *) echo "dovecot failed to reload" ;; esac ;; 2) echo "TLS certificate ain't expiring anytime soon; skipping" ;; *) echo "TLS certificate failed to renew" ;; esac
Let's start with our /etc/mail/smtpd.conf
(you
might want to move the existing version out of the way first):
pki mail.[[DOMAIN]] cert \ "/etc/ssl/mail.[[DOMAIN]]/fullchain.pem" pki mail.[[DOMAIN]] key \ "/etc/ssl/private/mail.[[DOMAIN]]/privkey.pem" table aliases file:/etc/mail/aliases table vusers file:/etc/mail/vusers table vdomains file:/etc/mail/vdomains filter check_dyndns phase connect \ match rdns regex { '.*\.dyn\..*', '.*\.dsl\..*' } \ junk filter check_rdns phase connect \ match !rdns \ junk filter check_fcrdns phase connect \ match !fcrdns \ junk filter senderscore \ proc-exec "filter-senderscore -junkBelow 70 -slowFactor 5000" filter rspamd \ proc-exec "filter-rspamd" filter rspamd-outbound \ proc-exec "filter-rspamd -settings-id outbound" filter inbound-filters \ chain { check_dyndns, check_rdns, check_fcrdns, senderscore, rspamd } filter outbound-filters \ chain { rspamd-outbound } listen on all tls pki mail.[[DOMAIN]] filter inbound-filters listen on all port submission tls-require pki mail.[[DOMAIN]] \ auth filter outbound-filters action "inbound" maildir junk virtualaction "outbound" relay match for local action "inbound" match from any for domain action "inbound" match from any auth for any action "outbound" match for any action "outbound"
We'll then need to setup our /etc/mail/vdomains
with at least two entries — one for the domain and one for the
server itself (if this server is handling mail for multiple
domains, you'd include those here, too):
[[DOMAIN]] mail.[[DOMAIN]]
Next, our /etc/mail/vusers
(if this server is
handling mail for multiple domains, you'll probably want to
duplicate "Standard destinations" for each one):
# Yourself and other local users [[USER]]@[[DOMAIN]] [[USER]] # System stuff root@[[DOMAIN]] [[USER]] root@mail.[[DOMAIN]] [[USER]] # Standard destinations daemon@[[DOMAIN]] [[USER]] ftp-bugs@[[DOMAIN]] [[USER]] operator@[[DOMAIN]] [[USER]] www@[[DOMAIN]] [[USER]] postmaster@[[DOMAIN]] [[USER]] MAILER-DAEMON@[[DOMAIN]] [[USER]] webmaster@[[DOMAIN]] [[USER]] abuse@[[DOMAIN]] [[USER]]
Then just run rcctl restart smtpd
.
Run pkg_add dovecot
, and then add
a dovecot
class in /etc/login.conf
to
give it a needed resource bump:
dovecot:\ :openfiles-cur=1024:\ :openfiles-max=2048:\ :tc=daemon:
We'll then need to tweak the following
in /etc/dovecot/conf.d/10-ssl.conf
:
ssl_cert = </etc/ssl/mail.[[DOMAIN]]/fullchain.pem ssl_key = </etc/ssl/private/mail.[[DOMAIN]]/privkey.pem
And then we'll need to tweak the following
in /etc/dovecot/conf.d/10-mail.conf
:
mail_location = maildir:~/Maildir
And finally, a quick rcctl enable dovecot && rcctl start
dovecot
to get IMAP up and running.
Ain't strictly necessary, but it'd sure be nice to have calendar syncing, right? First, as root (where [[USER]] is some username):
pkg_add kcaldav rcctl enable slowcgi rcctl start slowcgi kcaldav.passwd -Cu [[USER]] # repeat for each user chown www /var/www/caldav/kcaldav.db
Next, we need to update our httpd.conf
:
server "mail.[[DOMAIN]]" { listen on * port 80 location "/.well-known/acme-challenge/*" { root "/acme" request strip 2 } location * { block return 302 "https://$HTTP_HOST$REQUEST_URI" } } server "mail.[[DOMAIN]]" { listen on * tls port 443 tls { certificate "/etc/ssl/mail.[[DOMAIN]]/fullchain.pem" key "/etc/ssl/private/mail.[[DOMAIN]]/privkey.pem" } location "/.well-known/acme-challenge/*" { root "/acme" request strip 2 } location "/cgi-bin/*" { root "/" fastcgi } location "/.well-known/caldav" { block return 301 "https://mail.[[DOMAIN]]/cgi-bin/kcaldav" } location "/kcaldav/*" { root "/htdocs/kcaldav/" request strip 1 } location * { root "/htdocs/mail.[[DOMAIN]]" directory auto index } }
And finally, a quick rcctl reload httpd
as root. Your
mail server should now work as a calendar host, too (and should even
provide a slick web interface via /kcaldav/home.html
to let you manage your calendars).
I'm Ryan S. Northrup, a.k.a. "RyNo". I'm a programmer based out of Reno, NV, USA.