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
Stage | SMTP state | Context available |
---|---|---|
connect | Before HELO/EHLO command | Connection related information. |
authenticate | After AUTH command | Connection related information. |
helo | After HELO/EHLO command | HELO string. |
After MAIL FROM command | Sender address. | |
rcpt | After each RCPT TO command | List of recipients. |
preq | Before queuing1 | The entire mail. |
postq | After queuing2 | The entire mail. |
delivery | Before delivering | The entire mail. |
Available stages in order of evaluation
Preq stage triggers after the end of receiving data from the client, just before the server answers back with a 250 code.
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
,rcpt
andpreq
stages rules are run before an email is enqueued.postq
anddelivery
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.