DIY mail server with OpenSMTPd and Rspamd
Setting up a mail server is surprisingly easy
A couple of months ago I came across a blog post on how to set up your own mail server using OpenSMTPd. The posts’ author – Gilles Chehade – is one of the main developers of OpenSMTPd, OpenBSDs default SMTP daemon. As I was busy with my final thesis at the time I put off trying it for later.
After finishing my final thesis, I followed his guide on how to set up the server and must say it was indeed not that difficult to set up, provided you’re a little bit familiar with general *nix system administration and adding some DNS records.
After succeeding, I immediately mailed a friend who is also running their own mail server to let them know that I finally managed to set one up as well. They confirmed that the config looked good, however they suggested that I remove some information from the mail headers.
Removing mail headers
The first piece of information to remove is the from
field in the Received
header. In my case the from
field contained both the RFC1918 (private) address
of my computer as well as the publicly routable IP address and host name
of my router’s WAN interface provided by the ISP.
The Received
header looked something like this (I have removed the sensitive information):
Received: from [192.168.69.20] (host name redacted [IP redacted])
by mail.example.com (OpenSMTPD) with ESMTPSA id 0c5e35d7 (TLSv1.3:AEAD-AES256-GCM-SHA384:256:NO)
for <recipient redacted>;
Mon, 8 Mar 2021 13:09:41 +0100 (CET)
A quick check using man smtpd.conf
reveals that OpenSMTPd can omit that field when adding the Received
header
using the mask-src
option in a listen
directive like so:
listen on all smtps pki "mail.example.com" auth mask-src filter rspamd
This shows another change I made to the configuration compared to the blog post.
Instead of using SMTP with STARTTLS on port 143 I only allow SMTP over explicit
TLS on port 465 for mail submission, which is what the smtps
option does.
Another header that my friend suggested I remove is the User-Agent
header, which in my case looked like this:
User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:78.0) Gecko/20100101 Thunderbird/78.8.0
Because I’m using Rspamd to perform DKIM signing on outgoing
mail anyway, I can also use it to remove the User-Agent
header.
For this I am using the built-in Milter headers module.
Simply add this to /etc/rspamd/local.d/milter_headers.conf
:
skip_local = false;
skip_authenticated = false;
use = ["remove-headers"];
routines {
remove-headers {
headers {
"User-Agent" = 0;
}
}
}
This configuration will remove all User-Agent
headers from all mail that
gets scanned by Rspamd. The options skip_local
and skip_authenticated
are
important and must be set to false
, otherwise mail arriving from authenticated
users via SMTP won’t have that filter applied, which is the entire point of this
exercise.
It took me quite a while to find this easy solution in a GitHub issue, even the helpful people in the official Rspamd IRC seemed to have forgotten about it. All in all Rspamd is easy to get going when you’re just using the default configuration. The lack of documentation however makes it quite difficult to figure out the correct configuration without other people’s blog posts and GitHub issues to help out.
Thunderbird autoconfiguration
Thunderbird and some other mail clients can automatically retrieve the mail
server settings from a configuration server when setting up a new account.
The configuration server is simply a HTTP server that serves an XML file with
the account setup information.
Thunderbird tries to download the XML file from the location
https://autoconfig.example.com/mail/[email protected]
or
https://example.com/.well-known/autoconfig/mail/config-v1.1.xml
, first using
HTTPS and if that doesn’t work using HTTP.
The configuration file format is described in detail here.
I’m using OpenBSD’s httpd as my HTTP server because it is part of the base
system and very easy to configure.
I placed my XML file at /var/www/autoconfig/config-v1.1.xml
and use the
following configuration for httpd:
server "autoconfig.example.com" {
listen on egress port http
location "/mail/*" {
root "/autoconfig"
request strip 1
}
location "/.well-known/acme-challenge/*" {
root "/acme"
request strip 2
}
location * {
block return 302 "https://$HTTP_HOST$REQUEST_URI"
}
}
server "autoconfig.example.com" {
listen on egress tls port https
tls {
certificate "/etc/ssl/autoconfig.example.com.fullchain.pem"
key "/etc/ssl/private/autoconfig.example.com.key"
}
location "/mail/*" {
root "/autoconfig"
request strip 1
}
location "/.well-known/acme-challenge/*" {
root "/acme"
request strip 2
}
}
types {
include "/usr/share/misc/mime.types"
}
The configuration serves both the XML file and handles the requests required
for the acme-client
TLS certificate renewal process.
One important thing to note is that the types
clause must be included,
otherwise httpd will set the MIME-type of the requested XML file to
application/octet-stream
instead of text/xml
which Thunderbird will refuse
to use for autoconfiguration.