Sovite.Core.SMTPHandler (sovite v0.6.0)

Copy Markdown View Source

The MTA's Sovite.SMTP.Server.Handler: relay control, restrictions, authentication, recipient checks and expansion, and durable spooling.

Recipients are checked at RCPT time:

  1. <Postmaster>, and postmaster@ and abuse@ any hosted domain, are always accepted (RFC 5321 §4.5.1, RFC 2142). A bare <Postmaster> is queued as postmaster@<server.hostname>.
  2. Domains this server handles (local, aliased, hosted, and relay; see Sovite.Core.Routing): the recipient is expanded (Sovite.Core.Recipients.expand/2); one that no alias matched must be a known user (Sovite.Core.Recipients.check/2), otherwise 550 5.1.1.
  3. Any other destination, including address literals: accepted only from smtp.trusted_networks or after AUTH, otherwise 554 5.7.1 Relay access denied. The default config trusts no one, so it is never an open relay.

Domains are compared case-insensitively. Address tricks such as user%remote@local or source routes do not relay: only the domain of the parsed mailbox counts.

The anti-abuse checks (Sovite.Core.Screen: the postscreen-like client score, rate limits, greylisting, and suspended users) run first at each stage.

The restriction chains (Sovite.Core.Restrictions) run at connect, EHLO, MAIL, RCPT, DATA, and at the end of the data, after the built-in checks of each stage; they can reject, but never permit what relay control or recipient validation refuses. Policy servers in them (Sovite.Core.PolicyService) may also add header fields, redirect or copy the message, or send it to a content filter.

The listener's milters (Sovite.Core.Milters) see each stage last, and change the message at the end of the data.

Rewriting

The sender is rewritten at MAIL (Sovite.Core.Rewrite.sender/2) and each recipient at RCPT, before expansion. routing.always_bcc and the BCC maps add recipients at DATA. Mail from trusted networks and authenticated clients also gets its header addresses rewritten.

Authentication

AUTH runs against the configured backend (auth.backend), see Sovite.SASL.Server and Sovite.SASL.Dovecot. Failed logins are counted per client address (per /64 for IPv6) in a Sovite.Abuse.Penalty; a banned address gets 454 4.7.0 to AUTH, and is turned away at connect on listeners that require authentication. Each failure is answered after auth.failure_delay, to slow down guessing.

An authenticated client may only use the sender addresses its login maps to, see Sovite.Core.SenderCheck.

Submission fixes

Messages from authenticated clients are fixed up as RFC 6409 §8 allows: a missing Date: or Message-ID: is added, and the header fields in submission.strip_headers are removed.

Email authentication

Mail from outside is checked with SPF, DKIM, ARC, and DMARC, mail from users is DKIM signed, and forwarded mail is ARC sealed, see Sovite.Core.MailAuth.

With srs.enabled, mail from outside that an alias forwards to another domain gets an SRS sender address (Sovite.SRS) for that delivery, so SPF passes at the destination. Bounces to those addresses are accepted for the original sender at RCPT, and invalid or expired ones are refused with 550 5.1.1.

REQUIRETLS

With smtp.requiretls (the default), REQUIRETLS (RFC 8689) is offered over TLS. A message sent with it is queued with requiretls, so Sovite.Core.Delivery relays it only over verified TLS.

DSN

The DSN parameters (RFC 3461) are kept in the queued message, for each recipient after expansion: a recipient that an alias forwards to one address keeps them, and gets the address the client gave as its ORCPT when it had none. An alias that expands to several addresses passes them on without SUCCESS in NOTIFY, and when the client asked for SUCCESS, the alias itself gets an "expanded" notification (RFC 3461 §5.2.7.3, option c).

Priorities, deadlines, and RRVS

The MT-PRIORITY (RFC 6710) of a message is kept, except that clients neither trusted nor authenticated cannot raise it above 0. Only authenticated clients may hold messages for later (FUTURERELEASE, RFC 4865), and a DELIVERBY time (RFC 2852) becomes a deadline from the time the message arrived.

A recipient with RRVS (RFC 7293) that is a mailbox here (in the database) is refused with 550 5.7.17 if the mailbox was added after the given time. Other local recipients pass, as do role accounts (RFC 2142); one that is relayed keeps its RRVS for the next hop, unless an alias changed it.

ETRN

ETRN (RFC 1985) starts the delivery of deferred mail for a relay domain (Sovite.Core.Routing), or with @, for it and its subdomains, as Postfix's fast_flush_domains does by default. Other domains and named queues get 459.

Loops

A message with more than smtp.max_hops Received: fields is refused at the end of the data with 554 5.4.6 (RFC 5321 §6.3): it is most likely going round in circles.

Proxies and content filters

A client that used XCLIENT (from smtp.xclient_networks) is treated as the client it named: its LOGIN counts as authenticated, and its NAME as its verified reverse DNS name.

On a listener with a content filter, messages are queued for the filter (Sovite.Core.QueueManager). A reinjection listener is where filters send them back: email authentication is skipped there, as it ran when the message first arrived. XFORWARD attributes from the filter (smtp.xforward_networks) are kept as the message's original client.

Queueing

The message is accepted with 250 only after Sovite.Queue.Spool has made it durable. A Received: header is added at the top. The queue manager given as :queue_manager is then told about it. A message a restriction put on hold goes to the hold queue instead; one it discarded is accepted and dropped.

Telemetry

  • [:sovite, :smtp, :message, :discarded] and [:sovite, :smtp, :message, :held] - %{}, %{session_id, queue_id, reason}
  • [:sovite, :routing, :expansion_error] - %{}, %{session_id, recipient, reason}

Summary

Functions

Handler options from the running configuration.

Functions

opts(config, queue_manager \\ nil, runtime \\ [])

@spec opts(Sovite.Core.Config.t(), GenServer.server() | nil, keyword()) :: map()

Handler options from the running configuration.

  • queue_manager - the Sovite.Core.QueueManager to notify about new messages, if any.
  • runtime - :repo (a Sovite.Core.Repo reference), :milters (the names of the listener's milters), :penalty (the name of the Sovite.Abuse.Penalty for failed logins), :require_auth (the listener requires authentication), :resolver (for restrictions that look up domains), and the Sovite.Core.Screen options :screen, :screen_cache, :rate_limit, and :outbound, and the listener's :content_filter and :reinjection.