As far as spam filters go, SpamAssassin isn’t terrible – but it’s slow and chews through more CPU than I’d like. As I needed to set up a new mail server, I decided to look for alternatives – and after reading many positive reviews, I decided to have a go at running Rspamd instead of setting up SpamAssassin again. I was also able to do away with Policyd-SPF/OpenDKIM/OpenDMARC, as Rspamd handles all these functions as well!
Like pretty much every other antispam suite, Rspamd needs quite a bit of configuration – here are my notes for a minimally configured setup that seems to do a decent job. For larger servers with a higher email volume, further tuning will almost certainly be required – but this should get you going.
Assumptions
Here’s what you should already have:
- A Debian Stretch server with Internet access
(this guide will probably also work on Debian Jessie and versions of Ubuntu, but I haven’t tested it) - A functioning LAMP stack
(note that I’m using Apache rather than nginx) - Postfix and Dovecot already configured and delivering mail
(I recommend Christoph Hass’ ISPMail tutorials – just skip the Spamassassin instructions) - Ability to update DNS records for your domain(s)
- Knowledge of how and when to use
sudo
Overview
Here’s what you need to do:
- Install Rspamd and Redis
- Create config files for Rspamd and Redis
- Get Postfix talking to Rspamd
- Get Dovecot’s sieve and imapsieve plugins to train Rspamd
- Create SPF records for your domain
- Configure DKIM signing in Rspamd
- Configure Apache to reverse proxy Rspamd’s web interface
- Train Rspamd with existing spam/ham corpora
- Acquire spam for additional training
- Filter everyone’s spam into the Junk folder
Install Rspamd and Redis
Note: if you’re following the ISPMail guide (or other similar guide) for Postfix and Dovecot, don’t install spamassassin or spamass-milter!
The packages for Rspamd in Stretch are out of date and unsupported – add the official Rspamd repository before installing Rspamd and Redis.
First, create /etc/apt/sources.list.d/rspamd.list
and add the following line (you’ll need to change the release code name if you’re not using Stretch):
deb http://rspamd.com/apt-stable/ stretch main
Next, get the GPG key and install stuff:
wget https://rspamd.com/apt-stable/gpg.key apt-key add gpg.key apt-get update apt-get install rspamd redis-server
Create config files for Rspamd and Redis
First, create a secure password for the Rspamd controller using rspamadm
– you’ll need the output from this command in one of the config files:
rspamadm pw
Next, create a bunch of config files – these can be adjusted to taste, but the aim here is a minimal config to get things up and running – you can check the official documentation for additional options.
Set host and port for the main Rspamd worker:
/etc/rspamd/local.d/worker-normal.inc
bind_socket = "localhost:11333";
Configure the Rspamd proxy to talk to Postfix via a milter:
/etc/rspamd/local.d/worker-proxy.inc
bind_socket = "localhost:11332"; milter = yes; timeout = 120s; upstream "local" { default = yes; self_scan = yes; }
Configure a socket for the Rspamd controller (used for the web interface and for Dovecot sieve filters). This is where you use the password generated by rspamadm
earlier – you may want to use separate standard and “enable” (superuser) passwords:
/etc/rspamd/local.d/worker-controller.inc
password = "$2$s164jny....."; enable_password = "$2$s164jny....."; bind_socket = "localhost:11334";
Make the Rspamd bayes classifier use Redis:
/etc/rspamd/local.d/classifier-bayes.conf
servers = "127.0.0.1"; backend = "redis"; autolearn = true;
Choose which headers get added to emails. This is a matter of personal taste, but I like the following as it’s not too verbose:
/etc/rspamd/local.d/milter_headers.conf
use = ["authentication-results", "x-spam-status"]; authenticated_headers = ["authentication-results"];
If a user has replied to an email, don’t mark other emails in the same thread as spam:
/etc/rspamd/local.d/replies.conf
action = "no action";
Add URL redirect checks to the URL blacklist check – note that hitting some of these blacklist services more frequently might require purchasing a license:
/etc/rspamd/local.d/surbl.conf
redirector_hosts_map = "/etc/rspamd/redirectors.inc";
Assign dynamic reputation to certain TLDs:
/etc/rspamd/local.d/url_reputation.conf
enabled = true;
Cache URL tags in Redis:
/etc/rspamd/local.d/url_tags.conf
enabled = true;
Where to find Redis:
/etc/rspamd/local.d/redis.conf
servers = "127.0.0.1";
Now we know where to find Redis, adjust Redis’ configuration so it binds to the appropriate IP addresses and is a little less memory hungry – /etc/redis/redis.conf
already exists, you just need to adjust one line and add two others:
bind 127.0.0.1 ::1 maxmemory 500mb maxmemory-policy volatile-lru
A couple other configuration items to consider –
Configure a local DNS resolver – not really necessary for low volumes of mail, as Rspamd will use whatever is in /etc/resolv.conf
– however for higher volumes of mail, install something like Unbound and then:
/etc/rspamd/local.d/options.inc
dns { nameserver = ["127.0.0.1:53:1"]; }
Check for phishing emails. Phishing checks chew up an insane amount of memory (~800 MB for the few minutes I had it switched on), so you may not want this..:
/etc/rspamd/local.d/phishing.conf
openphish_enabled = true; phishtank_enabled = true;
Get Postfix talking to Rspamd
We’ve already set up Rspamd to talk to Postfix via the milter protocol – now we need to set up Postfix to join the conversation. Update /etc/postfix/main.cf
– if you already have a milter section, it will need to be adjusted:
milter_protocol = 6 milter_default_action = accept smtpd_milters = inet:localhost:11332 non_smtpd_milters = $smtpd_milters milter_mail_macros = i {mail_addr} {client_addr} {client_name} {auth_authen}
Now that Rspamd and Postfix are talking to each other, restart both services. New inbound email should have headers like “X-Spam-Status:” added by Rspamd.
systemctl restart postfix systemctl restart rspamd
Get Dovecot’s sieve and imapsieve plugins to train Rspamd
If Rspamd gets spam detection wrong for a given email, users can retrain Rspamd themselves by moving the email either out of the Junk folder or into the Junk folder as appropriate.
First, enable a couple sieve plugins within the protocol imap { } and protocol lmtp { } sections:
/etc/dovecot/conf.d/20-imap.conf
protocol imap { mail_plugins = $mail_plugins imap_sieve }
/etc/dovecot/conf.d/20-lmtp.conf
protocol lmtp { mail_plugins = $mail_plugins sieve }
Next, create a directory for the sieve filters:
mkdir /etc/dovecot/sieve
Tell Dovecot to pay attention to emails being shifted between folders and to trigger the sieve filters:
/etc/dovecot/conf.d/90-imapsieve.conf
plugin { sieve_plugins = sieve_imapsieve sieve_extprograms # From elsewhere to Junk folder imapsieve_mailbox1_name = Junk imapsieve_mailbox1_causes = COPY imapsieve_mailbox1_before = file:/etc/dovecot/sieve/report-spam.sieve # From Junk folder to elsewhere imapsieve_mailbox2_name = * imapsieve_mailbox2_from = Junk imapsieve_mailbox2_causes = COPY imapsieve_mailbox2_before = file:/etc/dovecot/sieve/report-ham.sieve sieve_pipe_bin_dir = /etc/dovecot/sieve sieve_global_extensions = +vnd.dovecot.pipe }
Create the relevant sieve filters:
/etc/dovecot/sieve/report-spam.sieve
require ["vnd.dovecot.pipe", "copy", "imapsieve", "environment", "variables"]; if environment :matches "imap.email" "*" { set "email" "${1}"; } pipe :copy "learn-spam.sh" [ "${email}" ];
/etc/dovecot/sieve/report-ham.sieve
require ["vnd.dovecot.pipe", "copy", "imapsieve", "environment", "variables"]; if environment :matches "imap.mailbox" "*" { set "mailbox" "${1}"; } if string "${mailbox}" "Trash" { stop; } if environment :matches "imap.user" "*" { set "username" "${1}"; } pipe :copy "learn-ham.sh" [ "${email}" ];
Restart Dovecot, then compile the two sieve filters:
systemctl restart dovecot sievec /etc/dovecot/sieve/report-spam.sieve sievec /etc/dovecot/sieve/report-ham.sieve
Finally, add the scripts referenced in the sieve filters that tell Rspamd to learn as spam or learn as ham:
/etc/dovecot/sieve/learn-spam.sh
exec /usr/bin/rspamc -h localhost:11334 learn_spam
/etc/dovecot/sieve/learn-ham.sh
exec /usr/bin/rspamc -h localhost:11334 learn_ham
Make the scripts executable, then restart Dovecot one more time:
chmod 755 /etc/dovecot/sieve/learn-spam.sh chmod 755 /etc/dovecot/sieve/learn-ham.sh systemctl restart dovecot
Create SPF records for your domain
Configuring outbound SPF involves working out what mail servers you could potentially send through and then crafting an appropriate DNS TXT record (note that DNS SPF records have been deprecated and shouldn’t be used).
There are plenty of decent wizards and tutorials out there, but I like the easySPF wizard. Because I run my own mail server and never, ever send through any other mail service, my record looks like this:
example.com. IN TXT "v=spf1 mx -all"
If I also wanted to use Gmail or Google Apps to send email from my domain, I’d need to change it to look like this:
example.com. IN TXT "v=spf1 mx include:_spf.google.com -all"
Configure DKIM signing in Rspamd
DKIM won’t improve spam detection rates, so this section can be skipped – but valid DKIM signatures are often used by antispam systems as a negative score, so it may improve delivery rates for your outbound email.
Thankfully, DKIM signing in Rspamd is even easier than setting up OpenDKIM! Create a directory for DKIM keys and then generate a key – I’ve used the domain name in the file name (this makes it easier to have different keys for different domains), and I’m using “dkim” as a selector (use something like the server name for multi-server configs) – adjust the file name in the command below to taste:
sudo mkdir /var/lib/rspamd/dkim sudo rspamadm dkim_keygen -k /var/lib/rspamd/dkim/example.com.dkim.key -b 2048 -s dkim -d example.com
The rspamadm dkim_keygen
command above will create the private key file and display the public key for the DNS TXT record on screen – the public key isn’t saved anywhere, so add the DNS TXT record immediately as follows:
- The name of the record should be the selector, followed by _domainkey, followed by the domain
(so dkim._domainkey.example.com for this example). - The value for the record needs to be copied carefully – the three lines should end up on a single line in the record, with spaces in between each:
dkim._domainkey.example.com. IN TXT "v=DKIM1; k=rsa;" "p=MIIBI.....HtByA" "504pO.....DAQAB"
Protect the private key:
chown -R _rspamd._rspamd /var/lib/rspamd/dkim chmod 640 /var/lib/rspamd/dkim/example.com.dkim.key
Enable DKIM signing and ARC signing in Rspamd with two new config files – both files have the same content, but they both need to exist. Note that both the domain and selector are called out – this is to allow for multiple domains and different keys. Additionally, allow username mismatches when signing:
/etc/rspamd/local.d/dkim_signing.conf
/etc/rspamd/local.d/arc.conf
path = "/var/lib/rspamd/dkim/$domain.$selector.key"; selector = "dkim"; allow_username_mismatch = true;
Restart Rspamd:
systemctl restart rspamd
It’s extremely important to test the SPF and DKIM setup at this point – don’t just assume everything is working. Incorrect SPF/DKIM configs will break email delivery.
First, wait for your DNS updates to propagate – then check the SPF record and the DKIM record to make sure they are valid.
If these checks pass, use DKIMValidator.com to ensure that outbound mail is being correctly signed.
Configure Apache to reverse proxy Rspamd’s web interface
If you’ve already got admin websites up and running, add access to the shiny Rspamd web interface by using an Apache reverse proxy – first, enable two Apache modules:
a2enmod proxy a2enmod proxy_http
Add the following to an existing virtual host – the Rspamd web interface will be available at https://example.com/rspamd/ (note the trailing slash):
RewriteEngine On RewriteRule ^/rspamd/(.*) http://127.0.0.1:11334/$1 [P,L] <Location /rspamd> Options FollowSymLinks Require all granted </Location>
Restart Apache:
systemctl restart apache2
Train Rspamd with existing ham/spam corpora
Rspamd works without training – but training will definitely improve accuracy! For my training, I used the contents of my Inbox as a source of ham and the contents of my Junk folder as a source of spam – modify the following to suit your environment:
rspamc learn_ham /var/vmail/example.com/user/cur/ rspamc learn_spam /var/vmail/example.com/user/.Junk/cur/
You will probably get a few errors during the training process – some of the more common ones include:
<MessageID> contains less tokens than required for bayes classifier
(the email was too short to classify)<MessageID> has been already learned as ham, ignore it
(means exactly what it says – the email is similar enough to one already in the database)
Acquire spam for additional training
If you’re like me, you have lots of ham for training purposes – but you’ve deleted most of your spam. It’s important to train both, so you really need some recent spam to tell Rspamd what to look for (old spam isn’t as useful, as spam changes over time).
Art Invoice make their spam corpora available to download – each day has a gzipped file filled with fresh spam ready to train with! Just one problem – it’s in mbox format, rather than Maildir format.
I found a python script to convert mbox files to Maildirs on GitHub – grab that, call it mb2md.py
, make it executable and then put it in the same folder as this shell script (which needs to be executable as well):
/path/to/getspam.sh
#!/bin/bash spamfile=spam--`date '+%Y-%m-%d'`.gz spamfile_unpacked=spam--`date '+%Y-%m-%d'` wget http://artinvoice.hu/spams/$spamfile gunzip $spamfile ./mb2md.py -i $spamfile_unpacked -o spam/ rspamc learn_spam spam/cur/ rm -r $spamfile_unpacked spam/ exit 0
Run it once (as root) to test, then add it to cron for automated daily spam training. Rspamd will already detect most of the messages as spam – you’ll see errors like this:
HTTP error: 410, <MessageID> is skipped for bayes classifier: already in class spam; probability 100.00%
..but there will be enough that get learned to make this a worthwhile exercise.
Filter everyone’s spam into the Junk folder
This step is definitely optional and I’d recommend holding off on this until you’re comfortable that Rspamd is doing a good job of detecting spam.
Out of the box, Rspamd has a few too many false positives for my liking – so I decided to raise the score at which the X-Spam: Yes
header gets added from 6 to 8. You can modify this setting with the Rspamd web interface, in the Configuration tab (it’s the setting called “Probably Spam”). I’d recommend adjusting this before creating the following global sieve filter..
First, edit /etc/dovecot/conf.d/90-sieve.conf
and add the following line:
sieve_after = /etc/dovecot/sieve/after.d/
Create the directory mentioned above:
mkdir /etc/dovecot/sieve/after.d/
Create the sieve filter – /etc/dovecot/sieve/after.d/junk.sieve
require ["fileinto","mailbox"]; if header :contains "X-Spam" "Yes" { fileinto :create "Junk"; stop; }
Finally, compile the sieve filter, update privileges and restart Dovecot:
sievec /etc/dovecot/sieve/after.d/junk.sieve chown -R vmail.vmail /etc/dovecot/sieve/ systemctl restart dovecot
References and notes
I couldn’t have written this guide without extensive Googling – among other sites, these were the most helpful:
- Rspamd and Rmilter quick start
- Own mail server based on Dovecot, Postfix, MySQL, Rspamd and Debian 9 Stretch
(yup, exact same software – however my version is intended to build on the excellent ISPMail config rather than starting from scratch!) - How To Run Your Own Mail Server
- Replacing antispam plugin with IMAPSieve
I will probably tweak this guide as I learn more about Rspamd – I will note any updates I make here.
- 2017-10-15: Added notes on how to acquire additional spam for training purposes
- 2017-10-17: Included instructions for enabling the Dovecot imap_sieve and sieve plugins
- 2017-10-17: Simplified worker controller configuration to use a single bind_socket
- 2018-01-28: Updated Apache proxy configuration to remove redundant config option
- 2018-06-05: Change privileges on the Dovecot sieve script directory
Any feedback is greatly appreciated – I hope you find this guide useful!
This has been fantastically useful – thank you.
The only thing I have found missing so far is that to get sieve working I had to uncomment and modify the following line in /etc/dovecot/conf.d/20-imap.conf
mail_plugins = $mail_plugins imap_sieve
And similarly the same to the following line in both /etc/dovecot/conf.d/15-lda.conf and /etc/dovecot/conf.d/20-lmtp.conf
mail_plugins = $mail_plugins sieve
I found setting
mail_debug = yes
In /etc/dovecot/conf.d# vi 10-logging.conf really handy for checking if things were working as expected
Thanks, Keith – that’s really valuable feedback! Turns out I already had the Sieve plugin configured in Dovecot, so I forgot to include it in the guide. Will fix that up now. 🙂
Ugh! copy+paste niggle. That last path should be /etc/dovecot/conf.d/10-logging.conf
Great tutorial — thank you very much.
I have one question, though. Would be very happy if you could help me out.
Since I have no intention of sending mail from my server, I have no need for postfix. Even if I wanted to, my ISP gave me a dynamic (more or less) IP address, which is not accepted by Spamhaus. I figured this out after having set a complete mailserver…..
So, I followed this howto, but did not install postfix.
Instead, I use getmail to get mail from my ISP, and I’m using rspamc to check this mail.
Is there a minimum number that rspamd needs before it classifies mail as spam ?
I sent myself some mails with the exact same content, only the subject was different. Upon reception, I placed them in the .INBOX.Junk/cur folder, and I ran the rspamc learnh_spam as described above.
However, when sending the same mails again (different subject), they are not recognized as spam by rspamc/d…
Any idea why ?
Hi Hans,
From what I’ve seen, learning something as spam only influences it a degree – Rspamd still makes up it’s own mind. I’d first check the rspamd log file (/etc/rspamd/rspamd.log) to make sure it learnt the message you moved to the Junk folder as spam (I’m going to assume that that worked) – but I’d also check the Rspamd Web Interface to see how Rspamd categorised those other messages.
What you’ll probably see is that it’s added a few points for the message you’ve learnt as spam, but there are other elements in the message that are changing the score. Another thing to consider is that if you’re sending the message yourself, it’s probably not malformed / from bad mail servers / in any of the blacklists that Rspamd checks – and so it’s not getting the spam score you’re after.
– zac.
Thanks a lot for this very useful article !
Just two questions regarding differences with Dovecot documentation that I don’t totally understand.
Firstly in the shell script to report ham/spam (https://wiki.dovecot.org/HowTo/AntispamWithSieve#For_rspamd), the parameter “-P ” is added to the rspamc command. Why don’t you use it here ? Isn’t it required and why or why not ?
Secondly, in the sieve scripts (https://wiki.dovecot.org/HowTo/AntispamWithSieve#Sieve_scripts), “imap.user” is passed to the shell script instead of “imap.email” in your article, what’s the difference ? Anyway, the parameter does not seem to be used in the shell script ?
The reason I don’t use ‘-P’ is because I’m not sending through a password – localhost is already trusted, so no password is required.
As for the second question, imap.user and imap.email may be the same depending on how email users are configured on your system. The example I borrowed from used imap.email, so I stuck with it. It does the job, so I didn’t question it! 🙂
– zac.