Restriction chains ([restrictions]): lists of checks run at each
stage of an SMTP session.
Each stage's list runs in order until a check decides: permit ends
the list, a rejection ends the session's request. Restrictions only add
checks: relay control and recipient validation always apply, so no
restriction can make Sovite an open relay.
Checks
| Check | Stages | Effect |
|---|---|---|
permit, reject, defer | all | Accept, 554 5.7.1, or 450 4.7.1. |
permit_trusted | all | Accept clients in smtp.trusted_networks. |
permit_authenticated | all | Accept clients that logged in. |
client_access | all | The access rules for the client IP address. |
helo_access | from helo | The access rules for the EHLO name. |
sender_access | from mail | The access rules for the sender address. |
recipient_access | rcpt | The access rules for the recipient address. |
require_fqdn_helo | from helo | 504 unless the name has a dot or is an address literal. |
require_fqdn_sender / require_fqdn_recipient | from mail / rcpt | 504 unless the domain has a dot. |
require_known_sender_domain / require_known_recipient_domain | from mail / rcpt | 550 if the domain has no MX or address records, or a Null MX; 450 when DNS fails. |
Checks whose information is not known yet are skipped: a helo_access
in the mail stage of a client that sent no EHLO does nothing.
Access rules
Access rules live in the database (sovitectl access). A rule matches
a pattern and has an action:
ACCEPT- accept, ending the list.CONTINUE- as if no rule matched: go on with the next check.REJECT [text]-554 5.7.1.DEFER [text]-450 4.7.1.4NN [x.y.z] text/5NN [x.y.z] text- that reply.DISCARD [text]- accept, then silently drop the message. Ends the list.HOLD [text]- accept, and put the message in the hold queue.WARN text- log, and go on.
Patterns tried, in order:
- client: the IP address, then for IPv4 the networks
192.0.2,192.0,192. EHLOnames and domains: the name, then each parent domain as.example.com(subdomains only) andexample.com(the domain and its subdomains).- addresses: the address, the address without its extension
(
routing.extension_delimiter), the domain patterns as above, thenuser@(any domain). The null sender is<>.
Summary
Functions
Whether check name can run at stage.
Checks a restriction name. Returns it, or an error message.
Runs checks for stage. WARN results are reported with telemetry
[:sovite, :restrictions, :warn] (%{stage, check, text}).
The stages, in session order.
Types
@type context() :: map()
What a chain looks at:
:client_ip,:helo,:sender,:recipient-nilwhen not known yet.:trusted,:authenticated- booleans.:access- the access rule tables (Sovite.Core.Lookup.tables()) by kind::client,:helo,:sender,:recipient.:resolver- for the known-domain checks.:delimiter- the extension delimiter characters.
@type verdict() :: :ok | {:reject, Sovite.SMTP.Reply.t()} | {:discard, String.t()} | {:hold, String.t()}
The verdict of a chain.
Functions
Whether check name can run at stage.
Checks a restriction name. Returns it, or an error message.
Runs checks for stage. WARN results are reported with telemetry
[:sovite, :restrictions, :warn] (%{stage, check, text}).
@spec stages() :: [atom()]
The stages, in session order.