Filtering

It is possible to filter emails using .vsl files for incoming emails and specific domains. vSL is the vSMTP scripting language, a superset of Rhai which is used to filter emails, modify their contents, send them to a specific target, etc.

The vSL chapter explains in detail what is possible to do with .vsl scripts.

Root Filter

The root filter.vsl script is used to filter incoming transaction at the connect, helo and authenticate stages of an SMTP transaction. (Check out the Transaction Context chapter for more details)

/etc/vsmtp
 ┣ vsmtp.vsl
+┣ filter.vsl
 ┣ conf.d/
 ┃    ┣ config.vsl
 ┃    ┣ interfaces.vsl
 ┃    β”— app.vsl
 ┣ domain-available/
 ┣ domain-enabled/
 ┣ objects/
 ┣ services/
 β”— plugins/

Adding the root filter script

fn on_config(config) {
  config.app.vsl.filter_path = "/etc/vsmtp/filter.vsl";
  return config;
}

Specifying the path to the filter in the configuration

If this script is not present, vSMTP will deny all incoming transactions by default.

Domains

It is also possible to filter emails per domain.

 ┣ vsmtp.vsl
 ┣ filter.vsl
 ┣ conf.d/
 ┃    ┣ config.vsl
 ┃    ┣ interfaces.vsl
 ┃    β”— app.vsl
 ┣ domain-available/
+┃     β”— example.com/
+┃          ┣ incoming.vsl
+┃          ┣ outgoing.vsl
+┃          β”— internal.vsl
 ┣ domain-enabled/
 ┣ objects/
 ┣ services/
 β”— plugins/

Adding filtering scripts for the `example.com` domain under the `domain-available` directory

The configuration in conf.d/config.vsl must be updated like so:

fn on_config(config) {
  config.app.vsl.domain_dir = "/etc/vsmtp/domain-enabled";

  return config;
}

Specifying filtering rules directory for domains in the configuration

In the above configuration, vSMTP has been setup to pickup filtering rules in the domain-enabled directory, not domain-available. Let’s use symbolic links to make our scripts available for vSMTP inside the domain-available/example.com directory.

/etc/vsmtp
 ┣ vsmtp.vsl
 ┣ filter.vsl
 ┣ conf.d/
 ┃    ┣ config.vsl
 ┃    ┣ interfaces.vsl
 ┃    β”— app.vsl
 ┣ domain-available/
 ┃    β”— example.com/
 ┃         ┣ incoming.vsl
 ┃         ┣ outgoing.vsl
 ┃         β”— internal.vsl
 ┣ domain-enabled/
+┃    β”— example.com -> /etc/vsmtp/domain-available/example.com
 ┣ objects/
 ┣ services/
 β”— plugins/

Using symlinks to enable filtering for the `example.com` domain

This directory structure is standard. The goal here is to disable / enable domain specific filtering by simply removing / adding symbolic links while keeping the configuration intact.

The server will pickup the scripts defined in the domain-enabled/example.com directory and run them following the conditions defined in the Transaction Context chapter.

Domain specific configuration

It is possible to add a specific configuration for each domain.

/etc/vsmtp
 ┣ vsmtp.vsl
 ┣ filter.vsl
 ┣ conf.d/
 ┃    ┣ config.vsl
 ┃    ┣ interfaces.vsl
 ┃    β”— app.vsl
 ┣ domain-available/
 ┃    β”— example.com
+┃        ┣ config.vsl
 ┃        ┣ incoming.vsl
 ┃        ┣ outgoing.vsl
 ┃        β”— internal.vsl
 ┣ domain-enabled/
 ┃    β”— example.com -> /etc/vsmtp/domain-available/example.com
 ┣ objects/
 ┣ services/
 β”— plugins/

Adding specific configuration for a domain

The config.vsl script under a domain must contain, at least, the following statement:

fn on_domain_config(config) {
  config
}

An empty domain specific configuration

Like the root config.vsl file, this script contains a function used to configure the domain, in this case called on_domain_config. It is possible to configure TLS, DKIM and DNS for each domain.

fn on_domain_config(config) {
  config.tls = #{
    protocol_version: ["TLSv1.2", "TLSv1.3"],
    certificate: "/etc/vsmtp/certs/fullchain.pem",
    private_key: "/etc/vsmtp/certs/privkey.pem",
  };

  config.dkim = #{
    private_key: "/etc/vsmtp/certs/example.dkim.key",
  };

  config.server.dns.type = "system";

  config
}

Changing TLS, DKIM and DNS parameters for a specific domain

If this script is not present in a domain directory, configuration from the root config.vsl script is used instead.