Blog post
Running you own mail server IV: Rspamd
rspamd
By Lucia di Rago
On Running you own mail server I: OpenSMTPD we learned how to up OpenSMTPD as a mail transfer agent (MTA), we moved on to installing Cyrus IMAP as a mail delivery agent (MDA) on Running you own mail server II: Cyrus IMAP and to configure DMARC, DKIM and SPF rules on Running your own mail server III: DNS records. This fourth and last part of a no-loose-ends guide on running your own mail server covers what's missing: incoming mail verification, outgoing DKIM signing — using your previously created key — and spam filtering. That may sound like a lot of work and a lot of tools. Lucky for us, there is one piece of software which takes care of it all. Meet Rspamd.
Meet Rspamd
Released in 2008, Rspamd started out as an open-source spam-filtering tool. Today it achieves much more than that. This high-performance system analyzes the content of your mail, checks for authentication protocols and compares domains against those of black- and whitelists. By collecting positive and negative points from various attributes, Rspamd assigns a final score to a message which, depending on its value, gets ranked as "spam" (phishy, unwanted messages) or "ham" (legitimate mail). Rspamd can be used with its default settings or it can be completely customized — in any case, you decide under what criteria and in what way an E-mail should get filtered. Let's walk through its integration into an OpenSMTPD Mail Transfer Agent.
Rspamd on your own mail server
Rspamd can be installed through packages on various systems. On FreeBSD, just run pkg install rspamd. For other OSes, refer to the official Rspamd's documentation .
We now need to integrate this software to our Mail Transfer Agent (MTA). While most MTAs require plenty of manual configuration, integrating Rspamd to OpenSMTPD is extremely simple. Just one filter is needed — on FreeBSD, run pkg install opensmtpd-filter-rspamd.
To set it up, add the line filter rspamd proc-exec "/usr/local/libexec/opensmtpd/opensmtpd-filter-rspamd" to your smtpd.conf file replacing your local path to the filter and attach Rspamd to your listeners, using your actual interface name instead of the placeholder if0:
| listen on if0 tls pki circle-networks.com auth-optional filter rspamd | |
| listen on if0 port submission tls-require pki circle-networks.com auth <users> filter rspamd |
Edit /etc/rc.conf and add the line rspamd_enable="YES" if you want the daemon to start automatically at boot.
Note on Redis
Rspamd can also work with a data store called Redis, which enables statistical learning and caching of message processing history. Since we find the static rules — SPF, DKIM and DMARC verification — more than sufficient, we'll skip Redis' installation steps. Nevertheless, if you are interested, you will need to get the Redis package and set it up following Rspamd's documentation.
Before moving on — DNS Resolver
There's a key aspect to Rspamd (and to any other spam-filtering tool) which gets overlooked on most blogposts about its installation: to be able to run it, you must use a local recursive DNS resolver.
Spam detection relies heavily on constant RBL queries and DNS lookups, which mostly get rejected when using a public DNS resolver such as Google (8.8.8.8), Quad9 (9.9.9.9), Cloudflare (1.1.1.1), amongst others. I've encountered this problem myself, where my Rspamd logfiles were filled with failed-query entries or messages like "DNS reply returned 'no error' for (...) 'no records with this name' was expected when querying for (...) ".
For a comprehensive guide on installing Rspamd and to avoid this issue altogether, let's set up Unbound as a local recursive DNS resolver.
Installing Unbound
Unbound is a free, open-source, recursive DNS resolver which runs on all BSD and Linux distributions. To install it, run the following on the command line
| # | pkg install unbound |
| # | mkdir /usr/local/etc/unbound |
| # | nano /usr/local/etc/unbound/unbound.conf |
Create the file unbound.conf and add the following configuration.
| /usr/local/etc/unbound/unbound.conf | |
|---|---|
| 1 | server: |
| 2 | access-control: "127.0.0.1" allow |
| 3 | aggressive-nsec: yes |
| 4 | |
| 5 | cache-max-ttl: 86400 |
| 6 | cache-min-ttl: 0 |
| 7 | |
| 8 | chroot: /usr/local/etc/unbound |
| 9 | directory: /usr/local/etc/unbound |
| 10 | logfile: /unbound.log |
| 11 | root-hints: /root.hints |
| 12 | auto-trust-anchor-file: /root.key |
| 13 | |
| 14 | do-ip4: yes |
| 15 | do-ip6: no |
| 16 | do-tcp: yes |
| 17 | hide-identity: yes |
| 18 | hide-version: yes |
| 19 | interface: 127.0.0.1 |
| 20 | port: 53 |
| 21 | prefetch: yes |
| 22 | rrset-roundrobin: yes |
| 23 | so-reuseport: yes |
| 24 | so-sndbuf:0 |
| 25 | tls-cert-bundle: "/usr/local/share/certs/ca-root-nss.crt" |
| 26 | username: unbound |
Instead of asking a public DNS server to resolve names for you, with a local recursive server such as Unbound, the first step to track down an IP address consists in querying multiple authoritative servers. To start the resolving chain, we need this root addresses to already be stored locally. The /root.hints file contains the names and IP addresses of the authorative DNS root name servers, the starting point for queries which haven't yet been cached. To create this file, run fetch -o /usr/local/etc/unbound/root.hints https://www.internic.net/domain/named.root/.
To enable DNSSEC and therefore verify the responses from authorative name servers, our DNS server needs a trust anchor. The /root.key file contains the cryptographic keys needed to start the verifying chain. To create this file, run unbound-anchor. The default storage path is /usr/local/etc/unbound/root.key.
Out of this two commands we will create cron-jobs. These will update both /root.hints and /root.key automatically each week, therefore keeping your DNS resolving secure. Run crontab -e on the command line and add the following lines
| 1 | 00 12 * 1 1 fetch -o /usr/local/etc/unbound/root.hints https://www.internic.net/domain/named.root/ |
| 2 | 00 12 * 1 1 unbound-anchor |
Now edit /etc/resolv.conf. Delete or comment out any former entries and type in
| 1 | #Unbound |
| 2 | nameserver 127.0.0.1 |
Add this lines to /etc/rc.conf to allow Unbound to start at boot.
| 1 | unbound_enable="YES" |
| 2 | unbound_flags="-c /usr/local/etc/unbound/unbound.conf" |
Finally, run service unbound start and drill — or dig on most other Linux or BSD systems — your favourite website to confirm the name resolves.
Last step
With Unbound set up as your local resolver, you are only three short config files away from completing your mail server: actions.conf, dkim_signing.conf and options.inc. While most, if not all, of Rspamd's parameters are customizable, we will just focus on the essential files. To adjust other settings to your liking, refer to Rspamd's documentation.
Rspamd's configuration files can be found under /etc/rspamd/ or /usr/local/etc/rspamd/. It is important that you never edit those files directly, in order to prevent them from being reverted when Rspamd is updated. Instead, new custom configuration files should be stored under subdirectories /local.d or /override.d.
actions.conf
We've mentioned at the beginning of this post how Rspamd filters mail through a scoring system. This is an example Rspamd analysis, you will find such entries under /var/log/rspamd/rspamd.log.
| 2026-06-26 11:15:30 #28435(normal) <619a4b>; scan; connection: 192.0.2.55:44321; queueid: 4WlgHz0Xz1z1H; message-id: <user@example.com>; action: add header; score: 6.45 / 15.0; symbols: [BAYES_SPAM(2.5), R_DKIM_REJECT(1.5), SPF_FAIL(1.05), DMARC_POLICY_QUARANTINE(0.5), SUBJ_ALL_CAPS(0.9)] |
Depending on what your settings are, a resulting action will be taken for a range of final scores. Here the resulting action is add header for an overall score of 6.45. To define this thresholds, create the file /usr/local/etc/rspamd/local.d/actions.conf with the values you find accurate.
| 1 | reject = 7; # reject obvious spam |
| 2 | add_header = 5; # add spam headers |
| 3 | greylist = 3; # temporary delay suspicious mail |
dkim_signing.conf
Rspamd checks on incoming emails for DMARC, SPF records and DKIM validation, but it also takes care of actively signing outgoing mail ensuring authenticity.
We've created our DKIM key before on Running your own mail server III, we now need to integrate it to Rspamd. Create the file /usr/local/etc/rspamd/local.d/dkim_signing.conf. Replace your domain and add your key as well as your previously chosen selector.
| 1 | domain { |
| 2 | <YourDomain.com> { |
| 3 | path = "/usr/local/etc/mail/dkim/dkim.<YourDomain>.key" |
| 4 | selector = "<YourSelector>" |
| 5 | } |
| 6 | } |
options.inc
Specify the address of your DNS server, in this case localhost, for Rspamd to correctly query white- and blacklists. Create the file /usr/local/etc/rspamd/local.d/options.inc and copy the following.
| 1 | dns { |
| 2 | nameserver = ["127.0.0.1"]; |
| 3 | } |
Because of this last configuration changes, OpenSMTPD and Rspamd need a restart. Run service smtpd restart and service rspamd restart.
Keep in mind
If you followed this whole post, you will have installed three services. Remember to confirm they are running, to set them up to start at boot — if you want them to — and to restart them each time you change something in their configuration files.
This was the last part of a four-step-instruction — you should now be able to send, recieve and control mail on your chosen domain! Enjoy the independency of running your own mail server and, above all, the satisfaction of crafting something from scratch.