3.2. Running configuration

The SMTP server (smtpd) handles all email going in and out, including the queue subsystem. As described in the SMTP server script manual it is primaily configured using hooks on the SMTP command phases; HELO, MAIL FROM, RCPT TO, etc. Similarily, the queue can be configured using pre- and post-delivery hooks as described in the queue script manual. Virtual queues are defined by the active queue policy (using concurrency and rate).

This is the reloadable part of the configuration, which contains the bulk of the configuration data. The default start configuration’s environment.appconf directive loads it from /etc/halon/smtpd-app.yaml. It is described by, and can be validated with, the smtpd-app.schema.json.

It can be softly reloaded and deployed in a blue-green fashion using per-connection conditions from Visual Studio Code, command line interface or control sockets.

3.2.1. Script directives

The script is normally edited as individual files using Visual Studio Code or simply a text editor. Those are then checked and “packed” into an actual configuration file by the halonconfig script. For example, two script files called src/hooks/rcptto/smtplookup.hsl and src/hooks/queue/predelivery.hsl will result in:

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

and the “smtplookup” RCPT TO script can then be hooked into a server with:

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

The default configuration in /opt/halon/share/examples contains the folder structure for all the different hook types.

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. There is however just one set of pre- and post-delivery scripts controlling the queue.

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.hooks.disconnect[]

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

scripting.hooks.predelivery

The pre-delivery script executed as the first step of loading an email into the active queue.

scripting.hooks.postdelivery

The post-delivery script executed as the last step after a delivery attempt.

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. As with the script hooks, this is normally populated by the halonconfig script. It is however also possible to load files from disk (on reload) if allowed by scripting.rootpath.

scripting:
  files:
    - id: blocklist.csv
      path: config/blocklist.csv

3.2.2. 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. Many of the properties only applies to servers of type smtp, therefore the properties which applies to http and plugin servers are explicitly mentioned.

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[].id

The id of the server. It must match the same type as configured in the startup configuration.

servers[].type

The type of the server. It must match the same type as configured in the startup configuration. The default is smtp.

servers[].concurrency.ip

The maximum total number of SMTP connections that the server accepts from a single IP address. The default is unlimited.

servers[].concurrency.total

Also applies to http servers

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. You must revise the value of environment.rlimit.nofile so that the server has enough file descriptor resources available. The default is 1000.

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

The number of reserved connection slots. This functionality is reserving slots within the total number of allowed connections (see servers[].concurrency.total).

servers[].concurrency.reserved.remoteips

An array of IP addresses which are allowed to use the reserved slots.

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.auth.apikeys

Only applies to http servers

List of allowed API keys for the HTTP server (sent in the X-API-Key header).

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.mailfrom.maxmessages

Maximum number of messages per connection (after which a 4xx error is returned). The default is to have no limit.

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.rcptto.maxrecipients

Maximum number of recipients per message (after which a 4xx error is returned). The default is 1000.

servers[].phases.data.maxsize

Also applies to http servers

How large DATA response to accept. The default is 10485760 bytes (10 MiB).

servers[].phases.data.maxhops

Number of Received headers allowed (loop detection). The default is 50.

servers[].phases.data.maxheadersize

The max message header size (in bytes). The default is 524288 bytes (512 KiB).

servers[].phases.data.receivedheader

Also applies to http servers

Add a Received header. This property may also be an object controling the format of the added Received header. The default is true.

servers[].phases.data.receivedheader.from

Include the from peer (...) information. The default is true.

servers[].phases.data.receivedheader.for

For messages with only one recipient add for <mail-address> information. The default is false.

servers[].phases.data.receivedheader.tls

For messages received with TLS add tls information (such as protocol version and ciphers). The default is false.

servers[].phases.data.fixheaders

Repair RFC822 messages with broken headers, by injecting a \r\n before the broken header, making it part of the message body. This is the default behvariour of most MTA’s. The default is false.

servers[].phases.data.multipart

Also applies to http and plugin servers

If set to false, disable MIME multipart parsing. The message will still contain the RFC822 headers, but no MIME parts will be parsed.

servers[].phases.data.multipart.maxparts

The maximum message mimeparts to allow (before rejecting the message as invalid). The default is 512.

servers[].phases.data.multipart.fixheaders

Repair MIME multipart messages with broken headers, by injecting a \r\n before the broken header, making it part of the message part body. The default is false.

servers[].phases.eod.hook

Also applies to http and plugin servers

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[].phases.disconnect.hook

Which scripting.hooks.disconnect[] to run on disconnect. The default is no script.

servers[].hostname

Also applies to http and plugin servers

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[].proxy.xforwardedfor

Only applies to http servers

Allow a HTTP proxy to change the notion of remoteip by sending the X-Forwarded-For header.

servers[].extensions.smtputf8

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

servers[].extensions.chunking

Enable support for the CHUNKING extension. The default is true.

servers[].extensions.pipelining

Enable support for the PIPELINING extension. The default is true. To enforce strict protocol synchronization rules use the compatibility.synchronization setting.

servers[].extensions.xclient

An array of IP addresses or networks to allow XCLIENT from, or true to allow from anyone. The default is false; not from anyone. The following session properties may be updated ADDR, PORT, NAME, HELO and LOGIN.

servers[].logging.protocol

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

servers[].logging.hook

Also applies to http and plugin servers

Enable script hooks logging (pre and post-amble). The default is true.

servers[].timeout.idle

Number of seconds of inactivity before disconnecting a client. The default is 300.

Note

Halon uses an asynchronous model, and can support a much larger number of connected clients compared to servers that use a process- or thread-based model. Therefore, there is usually no reason to lower this setting.

servers[].commands.maxidle

The max amount of idle commands (RSET, NOOP) before disconnect The default is 100.

servers[].commands.maxbad

The max amount of bad commands (syntax errors or bad commands) before disconnect The default is 10.

servers[].tls.certs.cert

Also applies to https servers

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. Available protocols are SSLv2, SSLv3, TLSv1, TLSv1.1, TLSv1.2, and TLSv1.3. Either specify a list of explicitly supported protocols or a list of ! unsupported protocol (meaning that all not excluded are supported, which may support future version of TLS). The default is !SSLv2,!SSLv3 (meaning that TLSv1 and later are supported).

servers[].tls.ciphers

Which TLS ciphers to support (<=TLSv1.2). The ciphers supported in each group may differ between different version of OpenSSL and SSL/TLS versions (see the openssl ciphers command). The default is aNULL:-aNULL:HIGH:MEDIUM:+RC4:@STRENGTH.

servers[].tls.ciphersuites

Which TLS cipher suites to support (TLSv1.3). The cipher suites supported in each group may differ between different version of OpenSSL and SSL/TLS versions (see the openssl ciphers command).

servers[].tls.clientcert

An array of IP addresses or networks 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 servers[].listeners[] 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, you need reference the listener on port 465 by its ID idof465:

smtpd.yaml
  - id: relay
    listeners:
      - port: 465
        id: idof465
      - port: 587
smtpd-app.yaml
servers:
  - id: relay
    tls:
      implicit:
        - idof465
servers[].productname

Also applies to http and plugin servers

The software name. The default is Halon. This is used in eg. Received headers.

servers[].transport

Also applies to http and plugin servers

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

3.2.3. Transport directives

In Halon, every email is queued with a text label called “transport”, which normally exists in the configuration as a definition for how the email should be delivered. The parameters defined by the configured transport can be overridden in the pre- and post-delivery script, as in the addresses[] example.

transportgroups[]

The transport groups are simply a logical grouping of transports in the configuration, and transports inherit parameters from the group. In addition to all the transport directives listed below, transport groups have an id, and an array of transports.

transportgroups[].transports[]

Each of the transports have an id in addition to all the transport directives. The example below has two “outbound” MX transports; one with and other without DANE encryption. Then there are two other transports called “smarthost1” and “inbound” in a group called “default”.

transportgroups:
- id: mxes
  retry:
    count: 30
    intervals:
      - interval: 60
      - interval: 900
      - interval: 3600
        notify: true
      - interval: 7200
      - interval: 10800
  dsn:
    transport: mx
  connection:
    sourceip:
      ipv4:
        - "out1"
        - "out2"
      ipv6:
        - "out1v6"
        - "out2v6"
  transports:
    - id: mx
      session:
          tls:
            mode: optional
    - id: mxdane
      session:
          tls:
            mode: dane
- id: default
  transports:
    - id: smarthost1
      connection:
        server: "smtp-out.example.org"
        port: 587
        sourceip:
          ipv6: false
      session:
        authentication:
          username: foo
          password: bar
    - id: inbound
      connection:
        server: "dovecot.example.com"
        port: 24
      session:
        protocol: lmtp

The transport directives listed below are valid for both transport groups and transports.

transportgroups[].connection.server

By default, an MX lookup is performed to determine the next hop destination. By setting the server directive, you can specify an IPv4, IPv6 or hostname as destination or alternativly an object containing transportgroups[].connection.server.host and transportgroups[].connection.server.mx. This is normally the case for “inbound” traffic to a mailbox server (maybe over LMTP) or “outbound” delivery via a so-called smarthost.

transportgroups[].connection.server.host

This configuration option is to determine the next hop destination. The default is the recipients domain.

transportgroups[].connection.server.mx

This configuration option is used to control if an MX or A/AAAA query is used for the transportgroups[].connection.server.host domain. The default is false.

transportgroups[].connection.port

The TCP port to use for the SMTP/LMTP connection. The default is 25.

transportgroups[].connection.proxyprotocol.server

The IP address of a outbound PROXY (haproxy) protocol server.

transportgroups[].connection.proxyprotocol.port

The TCP port to use for the outbound PROXY (haproxy) protocol server. The default is 25.

transportgroups[].connection.sourceip.ipv4

An array with one or more IPv4 addresses[] IDs to use as local IP(s), or false to disable IPv4.

transportgroups[].connection.sourceip.ipv6

An array with one or more IPv6 addresses[] IDs to use as local IP(s), or false to disable IPv6.

transportgroups[].connection.sourceip.random

Use a random source IP. If disabled it will use the first usable and non-suspended source IP from the ones configured. The default is true.

transportgroups[].session.protocol

Only needed if lmtp is to be used. The default is smtp.

transportgroups[].session.hostname

The HELO name to use. The default is to use the hostname of the addresses[] chosen as source IP, and to fall back to the system hostname.

transportgroups[].session.extensions.chunking

Enable support for the CHUNKING extension. The default is true.

transportgroups[].session.extensions.pipelining

Enable support for the PIPELINING extension. The default is true.

transportgroups[].session.tls.mode

How to handle TLS for the connection. The default is not to use TLS (plain text). To do custom TLS verification per destination, use the pre-delivery script’s tls_X parameters. The script library contains an MTA-STS implementation.

  • optional: Use opportunistic, unverified TLS (fall back to plain text).

  • require: Require TLS, but don’t verify the peer.

  • dane: Verifiy the peer using DANE (DNSSEC), it not supported fallback to optional TLS. The most secure option.

  • dane_fallback_require: Prefer DANE, it not supported fallback to require TLS. Only makes sense for specific destinations.

  • dane_require: Require DANE. Only makes sense for specific destinations.

transportgroups[].session.tls.implicit

Use implicit TLS (do not use STARTTLS). The default is false.

transportgroups[].session.authentication.username

The username to use for password-based authentication (SASL). Normally used for sending via smarthosts. The default is to not authenticate.

transportgroups[].session.authentication.password

See transportgroups[].session.authentication.username.

transportgroups[].retry.during

The time (?d?h?m?s formatted time, eg. 5d) during which an email will be re-delivered via the defer queue. Use transportgroups[].retry.intervals[] to specify each delivery attempt. This option is mutually exclusive with transportgroups[].retry.count.

Expired messages in the active queue will be bounced if the during period elapses while in the active queue.

Note

Be aware that this time is calculated against the actual received time of an email. If an email is held in the hold queue or suspended this time is still elapsing.

transportgroups[].retry.count

The number of times to attempt re-delivery of an email via the defer queue. Determines the time in queue together with the transportgroups[].retry.intervals[]. This option is mutually exclusive with transportgroups[].retry.during.

transportgroups[].retry.intervals[]

An array of intervals (in seconds or ?d?h?m?s formatted time, eg. 1h30m) for how long the email should stay in the defer queue before retry. If there are fewer intervals than transportgroups[].retry.count, the last interval will be used for consecutive defers. As with most other transport parameters, this behvariour can be overridden via pre- and post-delivery script. By setting notify to true on an interval, a delayed notification will be delivered to the sender at this time.

transportgroups:
- id: mxes
  retry:
    count: 30
    intervals:
      - interval: 1m
      - interval: 15m
      - interval: 1h
        notify: true
      - interval: 2h
      - interval: 3h
transportgroups[].dsn.transport

Enable bounces on delivery failures via the specified transport ID.

3.2.4. Queue directives

queues.concurrency.total

The maximum total number of outbound SMTP connections that the queue subsystem will make. This should be the basis for the virtual queues you define. Halon uses an asynchronous model, and can therefore support a much larger number compared to servers that use a process- or thread-based model. You must revise the value of environment.rlimit.nofile so that the server has enough file descriptor resources available. The default is 10000.

smtpd-app.yaml
queues:
  concurrency:
    total: 20000
smtpd.yaml
environment:
  rlimit:
    nofile: 70000
queues.maxmessages

The maximum number of messages in the queue subsystem (all states). When this limit is exceeded no more messages will be accepted over SMTP or HTTP. However internal messages will still be created. For more granual control use quotas. The default is unlimited.

queues.pooling.size

The maximum number of idle connections that the queue subsystem will hold in its pooling cache for connection reuse before eviction takes place. You must revise the value of environment.rlimit.nofile so that the server has enough file descriptor resources available. Unlimited is 0 (not recommended). The default is 1000.

queues.pooling.policy

The connection reuse policy. This can either be strict “remote” (based on both remoteip and remotemx matching), “remotemx” (based on only the remotemx matches, if available, otherwise remoteip) and “grouping” (based on only the grouping matches).

queues.grouping

Queue groupings of either the recipientdomain or remotemx into a specific queue tree field called grouping. If not configured, the grouping field will be empty. Groupings are matched in order.

queues.grouping.default

The grouping value if not queues.grouping.groupings[] match. This can either be “recipientdomain” or “remotemx”. This property is required.

queues.grouping.groupings[]

List of groupings.

queues.grouping.groupings[].id

The grouping ID. In all API’s the grouping id should be prefix with %. This property is required.

queues.grouping.groupings[].unique

If the grouping should be unique per remoteip. The only valid option (enum) is remoteip. This will append the %grouping-name;<remoteip> to the grouping name.

queues.grouping.groupings[].remoteip[]

The values in the list can be IP addresses or subnets.

queues.grouping.groupings[].remotemx[]

The values in the list can be domains, wild-card domains (*.example.com) or /regex/ patterns.

queues.grouping.groupings[].recipientdomain[]

The values in the list can be domains, wild-card domains (*.example.com) or /regex/ patterns.

3.2.5. General directives

postmaster.name

The name used in the From header of system generated email such as delivery reports.

postmaster.address.localpart

The localpart of the email address used in the From header of system generated email such as delivery reports. The default is postmaster.

postmaster.address.domain

The domain of the email address used in the From header of system generated email such as delivery reports. The default is the reporting MTA.

addresses[]

This directive is used to define “named” IP addresses so that they can be referenced by their ID from configuration and 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.

addresses:
  - id: "out1"
    address: 198.51.100.5
    hostname: smtp-out1.example.com
  - id: "out1v6"
    address: 2001:db8:85a3::8a2e:370:7334
    hostname: smtp-out1.example.com
  - id: "out2"
    address: 198.51.100.6
    hostname: smtp-out1.example.com
  - id: "out2v6"
    address: 2001:db8:85a3::8a2e:370:7335
    hostname: smtp-out1.example.com
  - id: "bulk"
    address: 203.0.113.67
    hostname: smtp-out2.example.com

They can then be referenced from for example script, like this pre-delivery snippet:

$sourceip = ["out", "out2", "outv6", "out2v6"];
if (GetMetaData()["spam"] == "yes")
  $sourceip = ["bulk"];
Try(["sourceip" => $sourceip]);
addresses[].id

The ID of the IP address.

addresses[].address

The IP address.

addresses[].hostname

Will be used as HELO name if defined.

addresses[].proxyprotocol.server

The IP address of a outbound PROXY (haproxy) protocol server.

addresses[].proxyprotocol.port

The TCP port to use for the outbound PROXY (haproxy) protocol server. The default is 25.

3.2.6. List directives

These directives are used to define “named” lists which can then be referenced by prefixing their ID with “@” from the queue policy, queue suspend and queue delivery configurations. Lists may also be created using the C API.

lists.jobid.{id}[]

The values in the list can either be job IDs or /regex/ patterns matching job IDs.

lists.grouping.{id}[]

The values in the list can either be groupings or /regex/ patterns matching groupings.

lists.localip.{id}[]

The values in the list can either be IP addresses or IP subnets (192.168.0.0/24).

lists.remoteip.{id}[]

The values in the list can either be IP addresses or IP subnets (192.168.0.0/24).

lists.transportid.{id}[]

The values in the list can either be transport IDs or /regex/ patterns.

lists.recipientdomain.{id}[]

The values in the list can be domains, wild-card domains (*.example.com) or /regex/ patterns.

lists.remotemx.{id}[]

The values in the list can be domains, wild-card domains (*.example.com) or /regex/ patterns.

3.2.7. Resolver 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.preference

The IP address family preference (A or AAAA), either ipv4 or ipv6. The default is ipv6.

resolver.exclude[]

A list of types ipv4, ipv6 (no IPv6 is available) or tlsa (if DANE is not used) to exclude as an performance optimization. The default is not to skip any RR types.

resolver.domain.cache.size

The number of domains to store in the in-memory LRU cache. The default is to cache 10000 domains.

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.

resolver.mx.exclude.ip[]

A list of IP addresses or networks that will be excluded from MX lookup results. This may be useful to prevent looping traffic (eg. MX resolving to 127.0.0.1).

resolver.mx.exclude.mx[]

A list of domains or wild-card domains (*.example.com) that will be excluded from MX lookup results. This may be useful to prevent looping traffic (eg. MX resolving to localhost).

3.2.8. Other directives

plugins[]

Specify a reloadable configuration to a plugin based on it’s id.

plugins:
  - id: test
    config:
      myval: true
plugins[].id

The ID of the plugin.

plugins[].config

The reloadable configuration. This property may be either a string or an YAML object. If this property is given as a string it must point to a YAML file on disk.

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 privatekey can however be defined in the startup configuration for the same ID, in which case it is not required. 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 the private keys in the startup configuration instead of here, as it is read before the privilege drop.

compatibility.barelf

The SMTP RFC requires <CR><LF> as end-of-line and end-of-data delimiters. However the use of bare <LF> is sometimes used in legacy clients, printers or various tools.

  • Halon always allows bare <LF> as end-of-line delimiter for SMTP commands.

  • Halon always converts any bare <LF> in the message DATA into <CR><LF>.

However, there is a known issue when there is a mismatch of the end-of-data delimiter (<CR><LF>.<CR><LF>) between the client and server, causing potential message injection attacks if both do not agree on when to terminate the message (smuggling).

Halon auto-detect by default (if not configured) if it should allow the combination of bare <LF>.<LF> as end-of-data sequence based on the first header line terminator. This assumes that non-conforming client will use <LF> as line delimiter for the entire data stream. Known smuggling attacks are not practially possible with the default auto-detect setting.

This setting may be set to

  • true to always allow bare <LF> (no auto-detect)

  • false to never allow bare <LF>

  • array[] of IP-addresses/networks to only allow bare <LF> from those hosts

compatibility.synchronization

Enable this feature to enforce stricter protocol synchronization rules. This feature works in combination with servers[].extensions.pipelining.

  • without PIPELINING (rfc5321) all commands are synchronization points

  • with PIPELINING (rfc2920) only check synchronization points at certain commands

Due to the nature of TCP and network delays, it’s not always possible to detect protocol violations. The default is false.

3.2.9. Example

Below is an unrealistically simple example of a running configuration with a few transports. The actual scripting is omitted, since it is normally packed by the tools.

servers:
  - id: default
    transport: mailserver
    phases:
      mailfrom:
        hook: ldaplookup
      eod:
        hook: inbound
    concurrency:
      total: 10000
      ip: 10
  - id: relay
    transport: mx
    phases:
      mailfrom:
        hook: relaylist
      eod:
        hook: outbound
transportgroups:
  - id: default
    transports:
      - id: mx
        session:
          tls:
            mode: dane
        dsn:
          transport: mx
        retry:
          count: 30
          intervals:
            - interval: 60
            - interval: 300
            - interval: 3600
              notify: true
            - interval: 7200
      - id: mailserver
        connection:
          server: mail.example.com
          port: 25
        retry:
          count: 30
          intervals:
            - interval: 10
            - interval: 60
            - interval: 3600
queueus:
  concurrency:
    total: 10000
postmaster:
  address: [email protected]
  name: Postmaster

3.2.10. Additional configurations

There are several other running configuration files, listed below.

3.2.10.1. Queue policy

Specified by the environment.policyconf directive and documented in the queue section.

3.2.10.2. Queue suspend

Specified by the environment.suspendconf directive and documented in the queue section.

3.2.10.3. Queue delivery

Specified by the environment.deliveryconf directive and documented in the queue section.

3.2.10.4. Rate subsystem

Configuration for the rated program that is used for the rate() script function. Specified by rated.yaml’s environment.appconf directive. It is described by, and can be validated with, the rated-app.schema.json JSON schema (included in our Visual Studio Code integration).

3.2.10.5. Content inspection

Configuration for the dlpd program that is used for the DLP script function. Specified by dlpd.yaml’s environment.appconf directive. It is described by, and can be validated with, the dlpd-app.schema.json JSON schema (included in our Visual Studio Code integration).