1.1. SMTP server configuration

The SMTP server handles all clients connecting to the MTA. As described in the script language manual the SMTP server is primaily configured using hooks on the SMTP command phases; HELO, MAIL FROM, RCPT TO, etc.

1.1.1. Startup configuration

This is the non-reloadable part of the configuration. By default, smtpd loads it from /etc/halon/smtpd.yaml. It is described by, and can be validated with, the smtpd.schema.json.

It most importantly contains the server listen sockets (bind addresses and ports), PROXY protocol and thread settings. Below is an example for adding a virtual server called “relay”, listening to any IP on port 587:

servers:
  - id: relay
    listeners:
    - port: 587

It contains the environment setting documented in the programs section. The example configuration contains reasonable defaults, but you might need to configure the number of open files resource limit.

1.1.2. Running configuration

This the the reloadable part of the configuration. The default startup configuration loads it from /etc/halon/smtpd-app.yaml. It is described by, and can be validated with, the smtpd-app.schema.json.

It contains the bulk of the configuration data; most importantly the SMTP server script.

It can be softly reloaded and deployed in a blue-green fashion using per-connection conditions from the integrated web admin, command line tools or control sockets.

The script is normally edited as separate files using the integrated package’s IDE (script editor) or the Visual Studio Code plugin. The actual YAML configuration is generated (or “packed”) by command line toolshct build command.

1.1.2.1. Script directives

Since the SMTP server can have multiple virtual servers[] (one inbound on port 25 and one outbound on port 587 for example), the script hooks are designed so that there can be multiple scripts of a given hook type which are then mapped to servers.

The script is normally edited as separate files using the integrated package’s IDE (script editor) or the Visual Studio Code plugin. The actual YAML configuration is generated (or “packed”) by command line toolshct build command.

For example, a script called src/hooks/rcptto/smtplookup.hsl will result in:

scripting:
  hooks:
    rcptto:
      - id: smtplookup
        data: |
          ...

which can then be hooked into a server with:

servers:
  - id: inbound
    phases:
      rcptto:
        hook: smtplookup

The hct init command will create a folder structure with all the different hook types.

scripting.hooks.connect[]

An array of connect scripts. Attached to a virtual server via servers[].phases.connect.hook.

scripting.hooks.helo[]

An array of HELO/EHLO scripts. Attached to a virtual server via servers[].phases.helo.hook.

scripting.hooks.auth[]

An array of AUTH scripts. Attached to a virtual server via servers[].phases.auth.hook.

scripting.hooks.mailfrom[]

An array of MAIL FROM scripts. Attached to a virtual server via servers[].phases.mailfrom.hook.

scripting.hooks.rcptto[]

An array of RCPT TO scripts. Attached to a virtual server via servers[].phases.rcptto.hook.

scripting.hooks.eod[]

An array of end-of-DATA scripts. Attached to a virtual server via servers[].phases.eod.hook.

scripting.hooks.proxy[]

An array of proxy scripts. Attached to a virtual server via servers[].phases.proxy.hook.

scripting.files[]

An array of virtual files used by the script hooks. It is most commonly used for imported script modules, include files, CSV lists and plain text files.

1.1.2.1.1. Server directives

The virtual servers first need to be defined in the startup configuration, as they specify which port(s) and address(es) to listen to (which cannot be softly reloaded). All other properties are then defined in this reloadable configuration.

servers[]

An array of virtual servers, which for example define which script hooks should be used. They are connected to the directives of the startup configuration via their IDs, as per the example below:

smtpd-app.yaml
servers:
  - id: default
    transport: mailserver
    phases:
      rcptto:
        hook: smtplookup
      eod:
        hook: inbound
    concurrency:
      total: 10000
      ip: 10
  - id: relay
    transport: mx
    phases:
      rcptto:
        hook: relaytrusted
      eod:
        hook: outbound
smtpd.yaml
servers:
  - id: default
    listeners:
      - port: 25
        backlog: 2048
  - id: relay
    listeners:
      - port: 587
        address: 192.168.0.100
servers[].concurrency.total

The maximum total number of SMTP connections that the server accepts. Halon uses an asynchronous model, and can therefore support a much larger number compared to servers that use a process- or thread-based model. Make sure that the number of files resource limit is a 2-3 times larger than this number. For 20000 connections, something like 50000 open files are needed in startup configuration. The default is 1000.

smtpd-app.yaml
servers:
  - id: default
    concurrency:
      total: 20000
smtpd.yaml
environment:
  rlimit:
    nofile: 50000
servers[].phases.connect.hook

Which scripting.hooks.connect[] to run on connect. The default is no script; to accept the connection.

servers[].phases.connect.remoteptr

Should a forward-confirmed reverse DNS lookup be performed on the connecting IP, for use in script and trace information (Received header). The default is false.

servers[].phases.helo.hook

Which scripting.hooks.helo[] to run on HELO/EHLO. The default is no script, to send a standard response.

servers[].phases.helo.required

Should the client be required to send HELO/EHLO before proceeding. The default is false.

servers[].phases.auth.hook

Which scripting.hooks.auth[] to run on AUTH. The default is no script, which disables AUTH.

servers[].phases.auth.mechanisms

Which SASL mechanisms to announce. The default is the built-in types LOGIN and PLAIN. If additional mechanisms are announced, they need to be implemented by the scripting.hooks.auth[] script.

servers[].phases.auth.tlsrequired

Should TLS be required for AUTH. The default is false.

servers[].phases.mailfrom.hook

Which scripting.hooks.mailfrom[] to run on MAIL FROM. The default is no script; to accept the sender.

servers[].phases.mailfrom.unqualified

If unqualified addresses (local part only) should be accepted. The default is false.

servers[].phases.rcptto.hook

Which scripting.hooks.rcptto[] to run on RCPT TO. The default is no script; to accept the recipient.

servers[].phases.rcptto.unqualified

If unqualified addresses (local part only) should be accepted. The default is false.

servers[].phases.data.maxsize

How large DATA response to accept. The default is 10 485 760 bytes.

servers[].phases.eod.hook

Which scripting.hooks.eod[] to run on end-of-DATA. The default is no script; to queue the email for all recipients.

servers[].phases.proxy.patterns[]

An array of SMTP command “patterns” before which to run the servers[].phases.proxy.hook script. The SMTP commands are matched up to the length of the pattern, case-insensitive. For example, specifying just one letter (such as “q”) will run the proxy script before all SMTP commands starting with that letter (such as QUIT). The default is to run the proxy script before all commands.

servers[].phases.proxy.hook

Which scripting.hooks.proxy[] to run on before commands matched by servers[].phases.proxy.patterns[]. The default is no script.

servers[].hostname

The hostname shown in the banner and HELO/EHLO. The default is to use the system hostname. This can be overridden by the scripting.hooks.connect[] and scripting.hooks.helo[] scripts.

servers[].extensions.smtputf8

Enable support for the SMTPUTF8 extension. The default is false.

servers[].extensions.xclient

An array of IP addresses to allow XCLIENT from, or true to allow from anyone. The default is false; not from anyone.

servers[].logging.protocol

Enable SMTP protocol logging (more verbose). The default is false.

servers[].tls.certs.cert

Which pki.private[] to use for TLS. The default is no TLS.

servers[].tls.certs.sni

An array with pki.private[] for use with SNI. The CN/SANs of the certificate will be used for matching. An optional list of subject names to use instead of the one(s) in the certificate can be provided. The default is no SNI.

servers:
  - id: default
    tls:
      certs:
        cert: defaultpki
        sni:
          - cert: otherpki
          - cert: yetanotherpki
            subjects:
              - "test2.example.org"
servers[].tls.protocols

Which TLS protocols to support. The default is !SSLv2,!SSLv3.

servers[].tls.ciphers

Which TLS protocols to support. The default is aNULL:-aNULL:HIGH:MEDIUM:+RC4:@STRENGTH.

servers[].tls.clientcert

An array of IP addresses to request client certificates from, or true to request from anyone. The default is false; not from anyone.

servers[].tls.implicit

An array of startup configuration listener[] IDs to enable implicit TLS for, or true enable implicit TLS for all listeners on this virtual server. The default is false; no implicit TLS.

To enable implcit TLS on port 465, but not 587, in the following startup configuration:

  - id: relay
    listeners:
      - port: 465
        id: idof465
      - port: 587

you need reference the listener on port 465 by its ID idof465:

servers:
  - id: relay
    tls:
      implicit:
        - idof465
servers[].transport

The default transport to queue the email on. This can be overridden by the scripting.hooks.eod[] script.

1.1.2.2. Other directives

resolver.concurrency

The maximum number of concurrent (pending) DNS queries. This should be set according to the DNS server used. The default is 100.

resolver.cache.size

The number of DNS queries to store in the in-memory LRU cache. The default is no caching.

resolver.cache.ttl.min

Normalize the time-to-live values in the cache to have at least this value.

resolver.cache.ttl.max

Normalize the time-to-live values in the cache to have at most this value.

addresses[]

Named addresses are normally not needed in the SMTP server configuration, as they are mainly used as source IPs when delivering email from the queue subsystem. Nevertheless, it is used to define “named” IP addresses so that they can be referenced by their ID from script. This is not only useful for giving IP addresses descriptive IDs, but also since you can share script between multiple MTAs in a cluster without having to reference specific IPs that only exists on one instance. If defined, the hostname directive will be used as HELO name by functions such as smtp_lookup_rcpt().

addresses:
  - id: "primary"
    address: 198.51.100.5
    hostname: mx1.example.com
pki.private[]

Array of private keys, possibly with X.509 certificates, for use with servers[] and script functions such as PKCS7, RSA, DKIM, client certificates, etc.

The id and privatekey properties are required, and certificate is optional. The private key and certificate should have either a path or data property.

pki:
  private:
    - id: selfsigned
      certificate:
        data: |-
          -----BEGIN CERTIFICATE-----
          ...
      privatekey:
        data: |-
          -----BEGIN PRIVATE KEY-----
          ...

Note

For privilege separation reasons, it’s normally recommended to define those in the startup configuration instead, as it is read before the privilege drop.

transportgroups[]

Transports are normally not needed in the SMTP server configuration, as they mainly define the behaviour of the queue subsystem. If you need to define a transport in this configuration for use by a script function, please see the queue configuration.