FreeBSD mail server: rspamd & DKIM

published on in category , Tags: freebsd mail rspamd dkim

Now that we have our basic mail infrastructure working, we will add support for rspamd and enable DKIM signing of our messages.

In this series we will set up a fully-featured mail server in a FreeBSD jail using OpenSMTPd, Dovecot and rspamd. In contrast to many other guides, this one is split into multiple posts that can either be read and followed individually, or as a whole. After each post, you end up with a fully working system (that might lack some features ;)).

Install rspamd and redis

rspamd uses redis for storing information, so we need to install both and enable the services. OpenSMTPd also has a filter module for rspamd, which makes the integration a breeze:

pkg install redis rspamd opensmtpd-filter-rspamd
sysrc redis_enable=YES
sysrc rspamd_enable=YES
service redis start
service rspamd start 

Create DKIM keys

For now we will leave the sane defaults for filtering provided by rspamd and go straight to DKIM signing. First, we’ll create the necessary keys:

cd /etc/mail
mkdir dkim
openssl genrsa -out /etc/mail/dkim/example.com.key 1024
openssl rsa -in /etc/mail/dkim/example.com.key -pubout -out /etc/mail/dkim/example.com.pub

Create DKIM DNS record

/etc/mail/dkim/example.com.pub now stores the public key, which you need to add to your domain’s DNS records. So get the contents of that file between the -----BEGIN PUBLIC KEY----- and -----END PUBLIC KEY----- markers and create a record like follows:

mail._domainkey   TXT   "v=DKIM1;k=rsa;p=your-key-goes-here"

The key stored in the file has line breaks in it, but you can just remove them and append them to make one big string which goes into your DNS record.

Configure rspamd for DKIM signing

Next we need to tell rspamd to sign outgoing mail with the key that we just created. Create the file /usr/local/etc/rspamd/local.d/dkim_signing.conf (you might need to create the local.d folder first) and add the following:

domain {
  example.com {
    path = "/etc/mail/dkim/example.com.key";
    selector = "mail";
  }
}

The selector basically needs to match the left-hand part of the DNS record key you just created and the path - obviously - the path of the file. Assuming that we created the DKIM key from the jail’s root user, we need to change the ownership of the /etc/mail/dkim folder to rspamd and then restart it:

chown -R rspamd:rspamd /etc/mail/dkim
service rspamd restart

Integrate rspamd with OpenSMTPd

For the integration between rspamd and OpenSMTPd to work, we will utilize opensmtpd-filter-rspamd. So the only thing we need to do on OpenSMTPd side is to set up a filter and to use it for incoming and outgoing mail. Open your /usr/local/etc/mail/smtpd.conf file and add the filter (I put it below my pki setup):

filter rspamd proc-exec "/usr/local/libexec/opensmtpd/opensmtpd-filter-rspamd"

Now, we simply need to use it in all listen directives, mine look like that (note the filter rspamd at the end):

# Listen directives: Sockets and their configuration. lo1 is the cloned interface for our jail,
# if this would be standalone, you would need to use the public network interface
#
# We listen for incoming mail. We will make sure that you cannot send outgoing mail without
# authentication in a second
listen on lo1 tls pki mail.example.com auth-optional filter rspamd

# We listen on smtps (= port 465) for outgoing mail, only for authenticated users
listen on lo1 smtps pki mail.example.com auth <passwd> filter rspamd

# We listen on submission (= port 587)
listen on lo1 port submission tls-require pki mail.example.com auth <passwd> filter rspamd

Restart smtpd and we are good to go:

service smtpd restart

Fix rspamd/redis communication in a jail

When you have a look at /var/log/rspamd/rspamd.log and scroll back to the moment when rspamd booted, you probably get this error:

2022-06-02 05:14:13 #7518(normal) <tbf5py>; cfg; rspamd_redis_init: cannot init redis backend for BAYES_SPAM

This is because rspamd tries to connect to redis on 127.0.0.1. As you probably recognized already, that does not work within jails since it binds to the host system. To make it work, I rebound redis to my jail’s private IP (192.168.0.2 in my case) and let rspamd connect to this address instead.

In /usr/local/etc/redis.conf, make sure to have bind 192.168.0.2 instead of bind 127.0.0.1 -::1 and restart redis. Next, we need to instruct rspamd to connect by that IP. Instead of changing it in /usr/local/etc/rspamd/modules/redis.conf, we’ll keep it update-safe and create our own file /usr/local/etc/rspamd/local.d/redis.conf (you have to create local.d first). This file only contains:

servers = "192.168.0.2";

After restarting rspamd, the communication should work and the log message should be gone.

Test DKIM integration

Try sending a mail from your mail server to any host and watch /var/log/maillog and /var/log/rspamd/rspamd.log to see if it signs the email or if any errors occur. On the receiving end, check the mail headers for the DKIM signature.

I also verified my DKIM setup on those sites:

Conclusion

Setting up DKIM signing with rspamd is easy and straight forward. Additionally, you will get spam filtering without any extra configuration. Of course, over the time you may want to tweak the rspamd settings for better classification, but it should be okay with the defaults. The problem only is, that you cannot tell rspamd yet how to learn spam or ham (= good mail). We will take care for that in one of our upcoming posts.