Virtual Domain Aware Mail Filtering

Many people who have personal servers handle email for a number of domains, and struggle to deal with the ever-increasing volume of spam, junk, and virus-laden email.

There are solutions for dealing with this problem, ranging from hosted services, to software you must run yourself.

This page documents a consistent and extensible solution built around open source software, which allows you to handle mail filtering for multiple domains without complex configuration.

This Distribution

The software presented here consists of a co-ordinated collection of plugins that can be used with the qpsmtpd SMTP server to offer a system that will filter email for a number of domains, and allow you to specify different filtering for different domains.

It is helpful if you're familiar with qpsmtpd so that you can install it and and understand how it works, but that isn't entirely necesary.

Once installed this collection of plugins will give you:

  • A simple system for handling mail for a number of domains on a single host.
    • Different domains can have independant settings.
    • A simple browsable archive of rejected mails.
  • A consistant experience where mail is either:
    • Rejected at SMTP-time and archived to a local quarantine.
    • Or accepted for delivery and passed to a local installation of exim4 to actually be delivered.

To get started please see the code on github:

git clone

Initial Setup

To get started you'll need to install qpsmtpd and configure it to use the ms-lite plugins.

Actually installing qpsmtpd, and configuring it to listen upon your public IP on port 25 is outside the scope of our documentation.

Once you've configured qpsmtpd to load the plugins there are three things you must do to get up and running:

  • Create the quarantine/archive directory.
  • Specify users & the filtering options for your domains.
Configuring The Rejection Quarantine

Configuring the quarantine directory is as simple making a directory and then configuring the permissions on it:

root@host:~# mkdir /spam
root@host:~# chown qpsmtpd.qpsmtpd /spam
root@host:~# chmod 750 /spam

This will give you a local directory to which the qpsmtpd user can both read and write.

Configuring The Domains

Configuring the domains is a simple process of creating files and directories, but we'll come back to that later.

Domains & Domain User Setup

To configure which domains and users will have their mail accepted we need to create a couple of directories for each domain beneath /srv. (This prefix /srv is fixed, just as the quarantine directory must be located at /spam.)

For each domain you specify users by creating entries in the directories:

  • /srv/$domain/users/valid
  • /srv/$domain/users/invalid

To demonstrate how this works we'll show how you'd configure the two domains "" and "". We'll configure these domains to work like this:

This will accept mail for only a few local users.

This domain will have wildcard handling so any account will accept mail, with the one exception that mail addressed to will be rejected.

To get started we configure

mkdir -p /srv/
touch    /srv/
touch    /srv/
touch    /srv/
touch    /srv/

Here we've configured the domain to accept mail addressed to,,, and - mail to all other accounts will be rejected (and stored in the quarantine)

For the setup is almost the same. We use the same "/users/valid" path, but instead of creating accounts we create a file called *:

# wildcard users - except the user "spam" is invalid
mkdir -p /srv/
touch    /srv/\*
mkdir -p /srv/
touch    /srv/

As you can see we've configured this domain to accept mail to all users except the user

Now that you've configured the users and the domain names mail will start to be accepted be delivered as expected - however we've not yet configured any filtering.

The configuration of filters follows the same pattern as the user setup, it merely involves the creation of a couple more files and directories.

Configuring The Filtering

We've already seen that to get a new domain going you need to create entries in the special directories:

  • /srv/$domain/users/valid
  • /srv/$domain/users/invalid

Configuring the various filters is achieved in the same fashion. You must merely touch files in the directory /srv/$domain/checks.

There are several filters included in the distributed list and we can enable all of them for our two example domains by running:

mkdir -p /srv/
touch    /srv/

mkdir -p /srv/
touch    /srv/

However you might prefer to enable different checks on a per-domain basis. That is also possible - rather than creating /checks/all create one file for each test you wish to be enabled on this domain.

For example to only enable the helo and date checks for the domain you'd run this:

rm -rf    /srv/
mkdir -p /srv/

touch    /srv/
touch    /srv/

How do you know what filenames to use? That is simple. The filename you create matches the name of the plugin in the source - this is also documented in the example plugins file.

Whitelisting & Blacklisting

Any anti-spam system will make mistakes from time to time, and the simplest way of avoiding repeats is to make use of whitelisting.

We allow messages to be whitelisted based upon either the email address that sent the message, the account it is addressed to, or the host that delivered the mail to us.

Like the prior setup this is again configured by creating files in a particular set of directories:

Whitelisting Senders

Create files in /srv/domain/whitelisted/senders/. For example if you wish to always accept email from the user you'd run this:

mkdir -p /srv/
touch    /srv/
Whitelisting Recipients

Create the files in the directory /srv/$domain/whitelisted/recipients/. Note the domain name is implied by the directory name, so you don't need to repeat it.

For example if you run a helpdesk and people forward spam complaints to the address you'd always want to accept them even if they looked like spam so you'd run this:

mkdir -p /srv/
touch    /srv/
Whitelisting Hosts

Finally to whitelist the hosts that are sending you email you need to create files beneath /srv/domain/whitelisted/ips/. As an example to always accept mail addressed from the IP address you'd run:

mkdir -p /srv/
touch    /srv/

These whitelisting settings are applied to all mails, and you don't need to restart the service for changes to take effect.

Blacklisting is achieved in a virtually identical fashion, the only change is that you use /srv/$domian/blacklisted as the prefix instead. So you might blacklist a hostname, an IP address, or even a sending address or domain via commands like:

# Blacklist the IP address from sending you mail.
touch /srv/

# Block all mail from
touch /srv/

# Blacklist mail from
touch /srv/

For each of the blacklist and whitelist options you can choose to apply these overrides or blacklists on a per-domain basis, as we have done above, or globally via the magic prefix of /srv/_global_/.

For example if you never wished to receive mail from AOL users you could run this:

mkdir -p /srv/_global_/blacklisted/domains/
touch /srv/_global_/blacklisted/domains/