Filtering

By default, the server will deny any SMTP transaction. We have to define filtering rules to accept connections and filter messages.

In this chapter, we will get a glimpse of vSMTP’s filtering system. To create filtering rules, we recommend checking out the vSL reference, focussing on the following chapters:

For this example, we will configure the following rules:

  • Messages from blacklisted domain will be rejected.
  • As Jenny is 11 years old, Jane wants her address to be added as a blind carbon copy of messages destined to her daughter.
  • Messages sent to the family must be delivered in Mailbox format.

Configuration

Let’s first add our filters in the /etc/vsmtp/conf.d/config.vsl script.

  fn on_config(config) {
    // Name of the server.
    config.server.name = "doe-family.com";

    // addresses that the server will listen to.
    // (change `192.168.1.254` for the desired address)
    config.server.interfaces = #{
      addr: ["192.168.1.254:25"],
      addr_submission: ["192.168.1.254:587"],
      addr_submissions: ["192.168.1.254:465"],
    };

+  // Root filter.
+  config.app.vsl.filter_path = "/etc/vsmtp/filter.vsl";
+  // Domain specific filters.
+  config.app.vsl.domain_dir = "/etc/vsmtp/domain-enabled";

    config
  }

Root Filter

Let’s define the root filter for incoming emails.

/etc/vsmtp/
 ┣ vsmtp.vsl
+┣ filter.vsl
 ┣ conf.d/
 ┃      ┣ config.vsl
 ┃      β”— *.vsl
 β”— objects/
        β”— family.vsl

Adding the root filtering script

The filter.vsl script is responsible for handling clients that just connected to vSMTP.

Add anti-relaying

Let’s setup anti-relaying by adding the following rule. (See the Root Filter section in the Transaction Context chapter for more details)

#{
  rcpt: [
    rule "anti relaying" || state::deny(),
  ]
}

/etc/vsmtp/filter.vsl

Use the blacklist

We can add the blacklist we defined in the Blacklist section to filter out sender domains that we do not trust.

// Importing objects that we defined in the last chapter.
+import "objects/family" as family;

#{
+ mail: [
+   rule "do not deliver untrusted domains" || {
+       if ctx::mail_from().domain in family::blacklist {
+           state::quarantine(family::untrusted_queue)
+       } else {
+           state::next()
+       }
+   },
+ ],

  rcpt: [
    rule "anti relaying" || state::deny(),
  ]
}

/etc/vsmtp/filter.vsl

The do not deliver untrusted domains rule will save any email from senders found in the blacklist in a quarantine folder named β€œuntrusted” and will not deliver the email.

Filtering for doe-family.com

Let’s create filtering rules for the doe-family.com domain.

/etc/vsmtp
  ┣ vsmtp.vsl
  ┣ filter.vsl
  ┣ conf.d/
  ┃      ┣ config.vsl
  ┃      β”— *.vsl
+ ┣ domain-available/
+ ┃      β”— doe-family.com/
+ ┃         ┣ incoming.vsl
+ ┃         ┣ outgoing.vsl
+ ┃         β”— internal.vsl
+ ┣ domain-enabled/
  β”— objects/
       β”— family.vsl

adding filtering scripts for the `doe-family.com` domain

Since we specified in the configuration that the domain-enabled directory was our domain filtering directory, we need to create a symbolic link to domain-available/doe-family.com to enable filtering for doe-family.com.

/etc/vsmtp
  ┣ vsmtp.vsl
  ┣ filter.vsl
  ┣ conf.d/
  ┃      ┣ config.vsl
  ┃      β”— *.vsl
  ┣ domain-available/
  ┃      β”— doe-family.com/
  ┃         ┣ incoming.vsl
  ┃         ┣ outgoing.vsl
  ┃         β”— internal.vsl
  ┣ domain-enabled/
+ ┃     β”— example.com -> /etc/vsmtp/domain-available/doe-family.com
  β”— objects/
       β”— family.vsl

Enabling the `example.com` domain filtering

vSMTP will pickup incoming.vsl, outgoing.vsl and internal.vsl scripts under a folder with a fully qualified domain name. Those rules will be run following vSMTP’s transaction logic. Let’s define rules for each cases.