Adding an antivirus

Malware remains a scourge. As John is aware of security issues, he decides to add a layer of antivirus directly on the MTA. He installed ClamAV which comes with the clamsmtpd antivirus daemon.

vSMTP support security delegation via the SMTP protocol using .vsl scripts. In the following tutorial, we are going to setup a delegation workflow that looks like the following.

{ Incoming msg }  ---> vSMTP server ---> { Delivered msg }
                        |       ^
                        |       |
      clamsmtpd socket  |       |   vSMTP socket
         (delegator)    |       |    (receiver)  |       |
                        |       |
                        v       |
                  { clamsmtpd daemon } <-> { ClamAV }

Delegating emails to clamav and getting back the results

ClamAV setup

The following example assumes that the clamsmtpd service is loaded and started with the following configuration:

# The address to send scanned mail to.
# This option is required unless TransparentProxy is enabled
OutAddress: 10025

# Address to listen on (defaults to all local addresses on port 10025)

# Tells clamav to forward the email to vsmtp
# event thought it found a virus. (it drops the email by default)
Action: pass

clamav configuration at `/etc/clamsmtpd.conf`

sudo systemctl start clamsmtp
sudo systemctl start clamav-daemon

Starting clamav

SMTP Service

Let’s create a smtp service in the /etc/vsmtp/services/smtp.vsl script to send incoming emails to clamsmtpd and receive them back on a specific address.

export const clamsmtpd = smtp::connect(#{
  delegator: #{
    address: "",
    timeout: "60s",
  receiver: "",

Declaring a SMTP service

The receiver’s socket must be enabled in the root config.

fn on_config(config) {
  config.server.interfaces = #{
    addr: [
      // Receiver for clients.
      // Receiver for delegation results from clamav.


Update the root configuration with a receiver for clamav


Create the antivirus passthrough using the delegate keyword in the /etc/vsmtp/domain-available/ script.

import "objects/family" as family;
import "services/smtp" as srv;

  postq: [
    delegate srv::clamsmtpd "check email for virus" || {
      // this is executed once the delegation result are received.
      log("debug", "email analyzed by clamsmtpd.");

      // ClamAV inserts the "X-Virus-Infected" header if it found a virus.
      if msg::has_header("X-Virus-Infected") {
      } else {

Moving infected emails in the `virus_queue` quarantine queue.

Compromised emails are quarantined in the virus_queue folder.

Check out the Delegation chapter for more details.