Stages

vSMTP interacts with the SMTP transaction at all stages defined in the SMTP protocol. At each step, vSL updates a context containing transaction and mail data that can be queried in rules and actions.

vSMTP stages

StageSMTP stateContext available
connectBefore HELO/EHLO commandConnection related information.
authenticateAfter AUTH commandConnection related information.
heloAfter HELO/EHLO commandHELO string.
mailAfter MAIL FROM commandSender address.
rcptAfter each RCPT TO commandList of recipients.
preqBefore queuing1The entire mail.
postqAfter queuing2The entire mail.
deliveryBefore deliveringThe entire mail.

Available stages in order of evaluation

1

Preq stage triggers after the end of receiving data from the client, just before the server answers back with a 250 code.

2

Postq stage triggers after the preq stage, when the connection closes and the SMTP code is sent to the client.

Syntax

Stages are declared in .vsl files using the following syntax:

#{
    connect: [
        // rules, actions, delegations ...
    ],

    mail: [
        // rules, actions, delegations ...
    ],

    postq: [
        // rules, actions, delegations ...
    ],

    // other stages ...
}

Declaring stages

Stages do not need to be declared in the previous given order, but it is a good practice as it makes rules easier to read.

Rules

Rules are combined with stages in .vsl files.

#{
    connect: [
        // This rule is executed once a new client connects to the server.
        rule "check client ip" || {
            if ctx::client_ip() == "192.168.1.254" {
                state::faccept()
            } else {
                state::next()
            }
        }
    ],

    mail: [
        // This action is executed once the server receive the "MAIL FROM" command.
        action "log incoming transaction" || {
            // Logging to /var/log/vsmtp.
            log("info", `new transaction from ${ctx::mail_from()} at ${ctx::client_ip()}`);
        }
    ],
}

Combining stages and rules

Using stages, rules can be run at specific SMTP state, enabling precise email filtering.

Stages that are not defined are omitted, but must appear only once if used.

#{
  connect: [],
  // invalid, 'connect' must only appear once!
  connect: [],
}

Trailing rules

For security purpose, a trailing rule should be added at the end of a stage.

#{
    connect: [
        // This rule is executed once a new client connects to the server.
        rule "check client ip" || {
            if ctx::client_ip() == "192.168.1.254" {
                state::accept()
            } else {
                state::next()
            }
        }

        // If the client ip is not known, the connection is denied.
        rule "trailing" || state::deny(),
    ],
}

Adding a trailing rule

In a stage, rules are executed from top to bottom. In the above example, if the client ip does not equal the 192.168.1.254 ip, the rule engine jumps to the “trailing” rule, denying the transaction instantly.

Like firewall rules, the best practice is to deny “everything” and only accept authorized and known clients (like the example above).

Before queueing vs. after queueing

TL;DR connect, authenticate, helo, mail, rcpt and preq stages rules are run before an email is enqueued. postq and delivery stages rules are run after an email is enqueued and the connection with the client is closed.

vSMTP can process emails before the incoming SMTP mail transfer completes and thus rejects inappropriate mails by sending an SMTP error code and closing the connection. This is possible by creating rules under the connect, authenticate, helo, mail, rcpt and preq stages.

The advantages of an early detection of unwanted mails are:

  • The responsibility is on the remote SMTP client side.
  • It consumes less CPU and disk resources.
  • The system is more reactive.

However, as the SMTP transfer must to be completed within a deadline, heavy workload may cause a system to fail to respond in time.

Therefore, it is possible to handle an email “offline” when specifying rules under the postq and delivery stages. Rules under those stages are run after the client received a 250 Ok code, after vSMTP received the complete email.

At this point, the rule engine is not able to send codes to the client even if the client sends multiple emails (each email is treated as a single entity by the rule engine), thus the rest of the rule engine behavior is considered “offline”.

Context variables

As described above, depending on the stage, vSL exposes data that can be queried in rules. Check out the Mail Context reference for more details.