Skip to content

Docker image providing Rspamd anti-spam and anti-virus mail filters

License

Notifications You must be signed in to change notification settings

mlan/docker-rspamd

Repository files navigation

The mlan/rspamd repository

github action ci docker version image size docker pulls docker stars github stars

This (non official) repository provides dockerized mail filter anti-spam and anti-virus filter using Rspamd, and ClamAV, which also provides sender authentication using SPF and DKIM.

It uses a robust scoring framework and plug-ins to integrate a wide range of advanced heuristic and statistical analysis tests on email headers and body text including text analysis, Bayesian filtering, DNS block-lists, and collaborative filtering databases. Clam AntiVirus is an anti-virus toolkit, designed especially for e-mail scanning on mail gateways.

Features

Tags

The MAJOR.MINOR.PATCH SemVer is used. In addition to the three number version number you can use two or one number versions numbers, which refers to the latest version of the sub series. The tag latest references the build based on the latest commit to the repository.

To exemplify the usage of version tags, lets assume that the latest version is 1.0.0. In this case latest, 1.0.0, 1.0 and 1 all identify the same image.

Usage

Often you want to configure Rspamd and its components. There are different methods available to achieve this. Many aspects can be configured using environment variables described below. These environment variables can be explicitly given on the command line when creating the container. They can also be given in an docker-compose.yml file, see the docker compose example below. Moreover docker volumes or host directories with desired configuration files can be mounted in the container. And finally you can docker exec into a running container and modify configuration files directly.

You can start a mlan/rspamd container using the destination domain example.com and table mail boxes for info@example.com and abuse@example.com by issuing the shell command below.

docker run -d --name flt -e 'WORKER_CONTROLLER=password="secret";' -p 127.0.0.1:11334:11334 mlan/rspamd

One convenient way to test the image is to clone the github repository and run the demo therein, see below.

Docker compose example

An example of how to configure an mail filter using docker compose is given below. By it self the mail filter is perhaps not so exiting. A more complete configuration is shown in the demo.

name: demo

services:
  flt:
    image: mlan/rspamd
    ports:
      - "127.0.0.1:11334:11334" # HTML Rspamd WebGui
    environment: # Virgin config, ignored on restarts unless FORCE_CONFIG given.
      - WORKER_CONTROLLER=password="${FLT_PASSWD-secret}";
      - METRICS=${FLT_METRIC}
      - SYSLOG_LEVEL=${SYSLOG_LEVEL-}
      - LOGGING=level="${FLT_LOGGING-error}";
    volumes:
      - flt:/srv
      - /etc/localtime:/etc/localtime:ro        # Use host timezone
    cap_add: # helps debugging by allowing strace
      - sys_ptrace

volumes:
  flt:

Demo

This repository contains a demo where 5 services are defined. It is comprised of app, mta, flt, db and auth, which are the web mail server, the mail transfer agent, the mail filter, the SQL database and LDAP authentication respectively.

You find the demoin the demo directory, which hold the docker-compose.yml file as well as a Makefile which might come handy. Start with cloning the github repository.

git clone https://github.com/mlan/docker-rspamd.git

From within the demo directory you can start the containers by typing:

make init

Then you can assess WebApp on the URL http://localhost:8008 and log in with the user name demo and password demo .

make web

You can send yourself a test email by typing:

make test

When you are done testing you can destroy the test containers by typing

make destroy

Rspamd web interface

Rspamd comes with a simple web-based control interface for Rspamd spam filtering system. It provides basic functions for setting metric actions, scores, viewing statistic and learning.

Rspamd web interface

From the demo you can assess the Rspamd WebUI on the URL http://localhost:11334 and log in with the password demo .

make flt-web

Persistent storage

By default, docker will store the configuration and run data within the container. This has the drawback that the configurations and queued and quarantined mail are lost together with the container should it be deleted. It can therefore be a good idea to use docker volumes and mount the run directories and/or the configuration directories there so that the data will survive a container deletion.

To facilitate such approach, to achieve persistent storage, the configuration and run directories of the services has been consolidated to /srv/etc and /srv/var respectively. So if you to have chosen to use both persistent configuration and run data you can run the container like this:

docker run -d --name flt -v flt:/srv -p 127.0.0.1:11334:11334 mlan/rspamd

When you start a container which creates a new volume, as above, and the container has files or directories in the directory to be mounted (such as /srv/ above), the directory’s contents are copied into the volume. The container then mounts and uses the volume, and other containers which use the volume also have access to the pre-populated content. More details here.

Configuration / seeding procedure

The mlan/rspamd image contains an elaborate configuration / seeding procedure. The configuration is controlled by environment variables, described below.

The seeding procedure will leave any existing configuration untouched. This is achieved by the using an unlock file: DOCKER_UNLOCK_FILE=/srv/etc/.docker.unlock. During the image build this file is created. When the the container is started the configuration / seeding procedure will be executed if the DOCKER_UNLOCK_FILE can be found. Once the procedure completes the unlock file is deleted preventing the configuration / seeding procedure to run when the container is restarted.

The unlock file approach was selected since it is difficult to accidentally create a file.

In the rare event that want to modify the configuration of an existing container you can override the default behavior by setting FORCE_CONFIG=OVERWRITE to a no-empty string.

Environment variables

When you create the mlan/rspamd container, you can configure the services by passing one or more environment variables or arguments on the docker run command line. Once the services has been configured a lock file is created, to avoid repeating the configuration procedure when the container is restated.

Environment variables has a key and a value: ENVKEY='envvalue with spaces'. Here the ENVKEY determines in which section the rspamd configuration the envvalue which contains key-value pairs will be inserted. Practical this done ny generating configuration files, within the rspamd configuration directory, with file names based on the ENVKEY name. Some configuration files have underscore _ or dash - in their names. Both are represented by a underscore in the ENVKEY name, e.g. ANTIVIRUS=log_clean=false; will put log_clean=false; in /etc/rspamd/local.d/antivirus.conf and CLASSIFIER_BAYES='autolearn=[0,6]; min_learns=10; per_user=true; users_enabled=true; allow_learn=true;' It will put 'autolearn=[0,6]; min_learns=10; per_user=true; users_enabled=true; allow_learn=true;' in /etc/rspamd/local.d/classifier-bayes.conf.

MTA integration with mlan/postfix

The rspamd proxy worker in Milter mode, which is enabled by default, interact with Postfix. Use the Postfix configuration to have Postfix scan messages on Rspamd via the milter protocol. The rspamd proxy worker listens to port 11334 by default.

Actions

Unlike SpamAssassin, Rspamd suggests the desired action for a specific message scanned. This could be treated as a recommendation to MTA what it should do with this message. So Rspamd does not keep any quarantined emails.

METRIC

Use METRIC to set the action thresholds like this for example; METRIC='actions {greylist=4;add_header=6;rewrite_subject=8;reject=15;}'

Antivirus

The Antivirus module provides integration with virus scanners. The mlan/rspamd image have ClamAV configured and installed.

ClamAV virus signatures

ClamAV (clamd) requires a virus signature database to run. The database is kept up to date with official signatures using freshclam, which also runs in the mlan/rspamd image.

ClamAV memory usage

ClamAV holds search strings and regular expression in memory. The algorithms used are from the 1970s and are very memory efficient. The problem is the huge number of virus signatures. This leads to the algorithms' data-structures growing quite large. Consequently, The minimum recommended system requirements are for using ClamAV is 1GiB.

SPF

The SPF module performs checks of the sender’s SPF policy.

Sender Policy Framework (SPF) is an email authentication method designed to detect forged sender addresses in emails. SPF allows the receiver to check that an email claiming to come from a specific domain comes from an IP address authorized by that domain's administrators. The list of authorized sending hosts and IP addresses for a domain is published in the DNS records for that domain.

DKIM

Rspamd is configured to check the digital signature of incoming email as well as add digital signatures to outgoing email.

DKIM signing

Domain-Keys Identified Mail (DKIM) is an email authentication method designed to detect forged sender addresses in emails. DKIM allows the receiver to check that an email claimed to have come from a specific domain was indeed authorized by the owner of that domain. It achieves this by affixing a digital signature, linked to a domain name, DKIM_DOMAIN, to each outgoing email message, which the receiver can verify by using the DKIM key published in the DNS records for that domain.

DKIM_DOMAIN and DKIM_SELECTOR

The public key DNS record should appear as a TXT resource record at: DKIM_SELECTOR._domainkey.DKIM_DOMAIN. The TXT record to be used with the private key generated at container creation is written here: /var/lib/rspamd/dkim/DKIM_DOMAIN.DKIM_SELECTOR._domainkey.txt. Default: DKIM_SELECTOR=default

A DKIM signing key is generated if both DKIM_DOMAIN and DKIM_SELECTOR are defined.

DKIM_KEYBITS

The bit length used when creating new keys. Default: DKIM_KEYBITS=2048

DKIM_PRIVATEKEY

DKIM uses a private and public key pair used for signing and verifying email. A private key is created when the container is created. If you already have a private key you can pass it to the container by using the environment variable DKIM_PRIVATEKEY. For convenience the strings -----BEGIN RSA PRIVATE KEY----- and -----END RSA PRIVATE KEY----- can be omitted form the key string. For example DKIM_PRIVATEKEY="MIIEpAIBAAKCAQEA04up8hoqzS...1+APIB0RhjXyObwHQnOzhAk"

The private key is stored here /var/lib/rspamd/dkim/DKIM_DOMAIN.DKIM_SELECTOR.key, so alternatively you can copy the private key into the container:

docker cp $DKIM_DOMAIN.$DKIM_SELECTOR.key <container_name>:var/lib/rspamd/dkim

If you wish to create a new private key you can run:

docker exec -it <container_name> rspamadm dkim_keygen -s $DKIM_SELECTOR -b $DKIM_KEYBITS -d $DKIM_DOMAIN -k /var/lib/rspamd/dkim/$DKIM_DOMAIN.$DKIM_SELECTOR.key

Kopano-spamd integration with mlan/kopano

Kopano-spamd allow users to drag messages into the Junk folder triggering the anti-spam filter to learn it as spam. If the user moves the message back to the inbox, the anti-spam filter will unlearn it.

To allow kopano-spamd integration the kopano and rspamd containers need to share the KOPANO_SPAMD_LIB=/var/lib/kopano/spamd folder. If this directory exists within the rspamd container, the spamd-spam and spamd-ham service will be started.

They will run rspamc learn_spam or rspamc learn_ham, respectively when a message is placed in either var/lib/kopano/spamd/spam or var/lib/kopano/spamd/ham.

Logging SYSLOG_LEVEL

The level of output for logging is in the range from 0 to 7. The default is: SYSLOG_LEVEL=5

emerg alert crit err warning notice info debug
0 1 2 3 4 5 6 7

Knowledge base

Here some topics relevant for arranging a mail server are presented.

DNS records

The Domain Name System (DNS) is a hierarchical and decentralized naming system for computers, services, or other resources connected to the Internet or a private network.

SPF record

An SPF record is a TXT record that is part of a domain's DNS zone file. The TXT record specifies a list of authorized host names/IP addresses that mail can originate from for a given domain name. An example of such TXT record is give below

"v=spf1 ip4:192.0.2.0/24 mx include:example.com a -all"

DKIM record

The public key DNS record should appear as a TXT resource record at: DKIM_SELECTOR._domainkey

The data returned from the query of this record is also a list of tag-value pairs. It includes the domain's public key, along with other key usage tokens and flags as in this example:

"k=rsa; t=s; p=MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDDmzRmJRQxLEuyYiyMg4suA2Sy
MwR5MGHpP9diNT1hRiwUd/mZp1ro7kIDTKS8ttkI6z6eTRW9e9dDOxzSxNuXmume60Cjbu08gOyhPG3
GfWdg7QkdN6kR4V75MFlw624VY35DaXBvnlTJTgRg/EW72O1DiYVThkyCgpSYS8nmEQIDAQAB"

The receiver can use the public key (value of the p tag) to then decrypt the hash value in the header field, and at the same time recalculate the hash value for the mail message (headers and body) that was received.

Implementation

Here some implementation details are presented.

Container init scheme

The container use runit, providing an init scheme and service supervision, allowing multiple services to be started. There is a Gentoo Linux runit wiki.

When the container is started, execution is handed over to the script docker-entrypoint.sh. It has 4 stages; 0) register the SIGTERM signal (IPC) handler, which is programmed to run all exit scripts in /etc/docker/exit.d/ and terminate all services, 1) run all entry scripts in /etc/docker/entry.d/, 2) start services registered in SVDIR=/etc/service/, 3) wait forever, allowing the signal handler to catch the SIGTERM and run the exit scripts and terminate all services.

The entry scripts are responsible for tasks like, seeding configurations, register services and reading state files. These scripts are run before the services are started.

There is also exit script that take care of tasks like, writing state files. These scripts are run when docker sends the SIGTERM signal to the main process in the container. Both docker stop and docker kill --signal=TERM sends SIGTERM.

Build assembly

The entry and exit scripts, discussed above, as well as other utility scrips are copied to the image during the build phase. The source file tree was designed to facilitate simple scanning, using wild-card matching, of source-module directories for files that should be copied to image. Directory names indicate its file types so they can be copied to the correct locations. The code snippet in the Dockerfile which achieves this is show below.

COPY	src/*/bin $DOCKER_BIN_DIR/
COPY	src/*/entry.d $DOCKER_ENTRY_DIR/

There is also a mechanism for excluding files from being copied to the image from some source-module directories. Source-module directories to be excluded are listed in the file .dockerignore. Since we don't want files from the module notused we list it in the .dockerignore file:

src/notused