Commit Graph
1015 Commits
Author SHA1 Message Date
Wez Furlong 2dca79ad9b dns-resolver: add metrics around MailExchanger::resolve 2024-12-04 09:19:22 -07:00
Wez Furlong 20ad646514 smtp_server: move listener domain cache to be scoped per connection
This fixes an issue where the cache being scoped globally could allow
the same IP/domain combination to appear to be satisfied by an earlier
authenticated session with the same IP/domain combination, for a period
of 60 seconds (the default TTL that we used for that cache).

This commit moves the cache to be smaller and more focused in scope;
now each session remembers the last few domains (bounded, to avoid
a trivial DoS by a malicious client) made on it.

closes: https://github.com/KumoCorp/kumomta/issues/320
2024-12-04 08:23:46 -07:00
Wez Furlong 38fd692e94 docs: show how to skip IPv6
I was halfway through adding special purpose options for this,
but I realized that skip_hosts already exists for this function
and is much more flexible.

Add an example to the docs to show how it can be done.
The same technique can be used to skip using IPv4 if that is
desired (despite being impractical with the current state
of SMTP on ipv6), but using `0.0.0.0/0` as an entry in the
skip_hosts list.

closes: https://github.com/KumoCorp/kumomta/issues/317
2024-12-04 07:33:50 -07:00
Wez Furlong e5127cb24b queue: route most ready-queue-insertion errors via requeue_message event
Previously, we would only trigger the requeue_message event in
situations where we were actively working on talking to the destination.
That left issues such as persistently NXDOMAIN destinations as being
unable to be caught and handled by the requeue_message event, which is
an issue for sites that want to fail out messages from the queue that
have bogus domains before they reach max_age.

This commit replaces all but one of the force_into_delayed calls with
requeue_message, and makes the call out to the event unconditional
(rather than dependent upon whether we were incrementing retries or
not).

The only case now that doesn't cause requeue_message to fire is when the
ready queue is full. The rationale is that that is a transient local
resource issue (rather than some external factor to which we need to
react), and that is likely to be a hot event when it triggers, so we
don't want to add CPU pressure with calling out to the requeue event for
them.

refs: https://github.com/KumoCorp/kumomta/issues/319
2024-12-03 13:01:42 -07:00
Wez Furlong 4a0a4d6a1e queue.rs: fix occasional accidental delay
The timerwheel achieves its cheap insertion and removal by
bucketing events with a slight loss in precision.

It is possible for messages to be popped because they are due "now", but
the precise now value for any given message might still be a small
number (tens) of milliseconds in the future.

Separately from this, there is logic that checks to see if the various
throttling related events have delayed any messages and will reinsert
those messages into the scheduled queue.

That logic can be falsely triggered by the slight imprecision and
cause a message to miss its true scheduling window.  I've observed
this case manifest in the retry_schedule test case.

This commit deals with this case by ensuring that we wait until all
of the due messages are really due; in practice this is either 0ns
or ~20ms.
2024-12-03 12:54:24 -07:00
Wez Furlong 1da8c390da validate-shaping: ensure that we have at least one file to validate 2024-11-27 18:30:56 -07:00
Wez Furlong a2f8a1007b remove stale comment 2024-11-22 09:42:10 -07:00
Wez Furlong 17fafff1c8 adopt more compact representation of ResolvedAddress in logs/diagnostics
Previously we'd use the Debug impl of ResolvedAddress, which rendered
like `ResolvedAddress { name: "some.host.", addr: "10.0.0.1" }`, which
is a bit heavyweight when it shows up in a log where we're indicating
that none of the hosts could be connected.

This commit adds a Display impl that renders that same struct in a
more compact form: `some.host./10.0.0.1`.
2024-11-22 08:54:12 -07:00
Wez Furlong b1484c405e queue: check for expiration even if we're not incrementing retries
This should help to age out messages in pathological cases where
the bulk of the messages are not actually being attempted, but
rather being requeued due to excessive connection failures or
other bulk queue operations.
2024-11-20 16:17:56 -07:00
Wez Furlong e85f2c245a update unbound, fixup DNS port number propagation
closes: https://github.com/KumoCorp/kumomta/issues/314
2024-11-19 10:57:49 -07:00
Wez Furlong 6c8a1882e5 docs: changelog for #316
closes: #316
2024-11-19 08:16:16 -07:00
Wez Furlong 70583302ee add session_id to LogRecord and thread through
refs: https://github.com/KumoCorp/kumomta/issues/316
2024-11-19 08:12:13 -07:00
Wez Furlong f75e0edc0f ignore remember_broken_tls when tls policy is required
This makes it easier to set a default for remember_broken_tls
without it causing issues for sites that have a transient blip
with TLS, when TLS is set to required for those sites.
2024-11-15 15:10:14 -07:00
Mike Hillyer 25bace1f30 Update custom.html
Bye Bot
2024-11-13 13:15:31 -05:00
Mike Hillyer 6ea55a8709 Add pattern matching rollups to the changelog. 2024-11-13 10:42:48 -05:00
Wez Furlong 971612a7d2 docs: add section about memory management 2024-11-12 13:30:10 -07:00
Wez Furlong e06a4a9049 docs: rotate changelog for 2024.11.08-d383b033 2024-11-12 12:28:40 -07:00
Wez Furlong cf911c4067 docs: update dev -> 2024.11.08-d383b033 2024-11-12 12:25:53 -07:00
Wez Furlong d383b033cf docs: add a refman section for template syntax 2024-11-08 10:37:08 -07:00
Mike Hillyer 00ccd5d69c Update the add_authentication_results page to show SPF as well as DKIM. 2024-11-08 11:11:28 -05:00
Wez Furlong d3e399cfda docs: describe the configuration monitoring and epoch system 2024-11-08 07:14:03 -07:00
Wez Furlong d33aa15c40 docs: add note about the null queue not actually being a queue 2024-11-08 05:32:37 -07:00
Wez Furlong f74f0a7556 add ehlo_domain to the set of predefined connection metadata values 2024-11-08 05:27:02 -07:00
Wez Furlong ea53531bc6 rocks: purge memtables when memory is low, export metrics
Export the memory statistics for each spool database to prometheus for
charting and tracking.

Allow the hosting application to request a cache purge and set up a
monitor task to do that when memory usage is too high.  That step will
print the memory that it reclaimed when it kicks in.
2024-11-07 16:29:11 -07:00
Mike Hillyer 6f154f6f3c Add information on trace-smtp-client to the kcli page of the User Guide. 2024-11-07 16:29:16 -05:00
Wez Furlong d12bda6ee6 docs: changelog for SPF
closes: #83
2024-11-07 08:27:13 -07:00
Dirkjan Ochtman 55dcf3e6f9 kumod: add basic SPF Lua API 2024-11-07 07:52:37 -07:00
Wez Furlong 7a9a33f836 apply cache-purge-on-low-mem to all LruCaches 2024-11-06 20:50:30 -07:00
Wez Furlong 1241846bf9 purge mod-memoize caches when memory is low
I realized that we hadn't connected this up to the memory monitor,
so this commit does that.

I'd like to hook this up for every LruCacheWithTtl, but that
requires some more extensive changes.

I'll look that in a follow up commit.
2024-11-06 17:31:29 -07:00
Mike Hillyer 8bdfa734e9 Should not have made a page in the refman. 2024-11-05 17:39:16 -05:00
Mike Hillyer 1c8e3e413e Add a page for overall_max_message_rate to the reference manual. 2024-11-05 17:26:48 -05:00
Mike Hillyer 77429c9e32 Add queue throttling to the queues page. 2024-11-05 17:21:35 -05:00
Wez Furlong fbed08567f add remember_broken_tls option
This provides a simpler and easier to use option for dealing with broken
TLS.

The back story here is that sites with broken TLS ideally should result
in a TLS failure and return us to a clear text SMTP session that we
could use for the remainder if we are in opportunistic mode.

In practice, it's not that simple:

* The rustls implementation provides into_fallible() to enable this mode
  of operation, but certain handshake errors result in the session being
  unusable once the failed handshake has been communicated
* Openssl is perfectly capable of operating this way, but the Rust
  ecosystem bindings do not expose the handshake failure in a way that
  allows the clear text session to continue

If we wanted to allow the session to communicate with the failed host in
clear text we will need to make a new separate connection for this case.

There is a concern that this will bump a connection counter in a
reputation system implemented on the destination host, but it is
reasonable to wonder if a site that has broken TLS would be sufficiently
sophisticated to employ such a system.

If we did want to allow making a new connection to the same host, it is
a bit awkward to fit into our current mx host connection plan system.

So, with all that in mind, this commit tries a slightly different
approach:

* A new `remember_broken_tls = "3 days"` field in the egress path config
  will enable an in-process cache to keep track of the site_names that
  advertise STARTTLS and for which we failed to handshake, or failed to
  re-EHLO after a handshake.

* That will cause the connection plan to move on to subsequent hosts, if
  any.  It won't cause us to re-attempt the current host.

* Subsequent connection attempts (which may be immediate if there are
  additional hosts in the current session plan) will consult the
  broken tls cache for the site, and use that to decide on the
  availability of TLS

* If we previously encountered broken TLS, we will effectively treat
  the site as not advertising TLS in EHLO, but will generate an
  appropriate error message if TLS is Required.

Since we track the breakage in a cache with per-item TTLs, you
can set the duration as appropriate to your situation.

The cache will be cleared when the process is restarted.

There is no sharing of the cache between nodes.

This is a much lower complexity solution than the existing workaround
using TSA automation rules to override the site with Disabled TLS, and
will help to minimize the number of rules for that purpose in the TSA
daemon.

Note that this commit doesn't change any behavior; we can do a followup
commit to replace those TSA rules with this mechanism.
2024-10-31 13:11:49 -07:00
Mike Hillyer eab7955b7a Whoops, all validate mentions. 2024-10-31 12:58:13 -04:00
Wez Furlong e997cb4856 Improve handling of errors in get_queue_config
If you have a get_queue_config event handler that raises an error, it
will propagate back to the injector as a transient error, which is the
desired behavior.

However, we didn't ensure that the message was removed from the spool in
that situation, which could lead to duplicate sends of the same logical
message.

This commit makes things more robust in the context of this kind of
local configuration issue:

* QueueManager::insert_or_unwind is now the preferred way to handle
  insertion of the message into the queue during reception at the point
  of ingress.  It will take care to remove the message from the spool
  and to adjust the accounting by logging a Bounce record to complement
  the Reception that it assumes that its caller has just logged for the
  message.

* Both SMTP and HTTP listeners have been updated to use
  insert_or_unwind.

* Spool enumeration has been updated to count failed inserts and to
  summarize that the failure is serious and that the messages are not
  going to go anywhere until the configuration issue is addressed and
  the server is restarted.  Note that we cannot recover from this while
  the server is online because we have to perform a full spool
  enumeration in order to re-discover those messages.  (We could
  potentially build a thing to remember the complete list of messages,
  but I'd rather not try to address what is essentially a configuration
  deployment validation problem with engineering on this end of things.)
2024-10-31 09:28:38 -07:00
Wez Furlong 73d811b8ef tsa: add SetDomainConfig action
The back story here is that we were investigating a report of
get_egress_path_config sometimes taking a long time (30-60 seconds!) to
complete.

The issue correlates with a large number of TSA entries that
disable the use of TLS for broken sites (>30,000 entries),
and some kind of DNS resolution.

Running the sample of the TSA output through validate-shaping on my
local system can take 30+ seconds to resolve, with a handful of the
entries timing out after 15 seconds (which is 5 seconds timeout on a
query, plus 2 retries of 5 seconds each).  In this sort of situation
with a persistently slow resolve on specific domains, the best case
result is therefore 15 seconds, but it could be longer depending on the
ordering of the results and prevailing winds on the upstream dns server.

What's tricky in this situation is that we have to resolve the site
name for each of the entries that have mx_rollup=true enabled in
order to correctly match any given domain when satisfying the
get_egress_path_config event callout.  Being lazy with resolution
doesn't help at load time because we're only loaded immediately
prior to invoking this event.

The next logical question to ask is: why do we need to have 30,000+
entries to disable TLS for these broken sites--can't we just retry in
clear text? Well, it's a bit tricky: we're not guaranteed to be able
to continue in clear text after STARTLS fails. If we were to disconnect
and reconnect to the same host immediately after, that's technically
2 connections being made in quick succession to the remote site, and
that might have reputation influencing consequences. I'd like to avoid
adding that sort of reconnection logic without providing a way to
control that behavior, and that is something for a separate change
from this one.

So, looking at the nature of the problematic rule, it doesn't
actually need to be keyed by the site name because there are
not shared limits or constraints that it might make sense to
apply, and in the context of this issue, it's not necessary
to use rollup.

This commit introduces a `SetDomainConfig` action that will
emit a config entry that explicitly sets `mx_rollup=false`
regardless of the value of `mx_rollup` for the defining rule
condition.

When mx_rollup=false, we do not need to resolve the MX
record for the entry when we load the shaping rules,
which eliminates the DNS latency for the aggregate
set of domains with broken TLS.

It would be great if we could automagically fixup the
TSA config database when updating to this version, but
since the rule is an optional part of the suggested
shaping configuration, which may not even be loaded
by a given overall configuration, it's not appropriate
to assume that we should do any fixup.

There's likely a one liner that can be run in sqlite to remove or force
the expiration of the old rules.
2024-10-31 09:28:38 -07:00
Mike Hillyer c22069cf1e Add mention to validate policy to the troubleshooting guide. 2024-10-31 12:06:13 -04:00
Wez Furlong 207bf44f18 docs: format enable_dane.md 2024-10-28 20:46:03 -07:00
Wez Furlong d7eb2dd6b2 docs: remove dead file
This toml file was from the mdbook version of the docs
2024-10-28 20:45:14 -07:00
Mike Hillyer 39df663cd4 Add links, fix linter complaints. 2024-10-25 13:06:49 -04:00
Wez Furlong 18cbdeae1a fixup enable_dane page
it was still formatted as if it were part of its former home
in the big page for make_egress_path.

Tidy it up.
2024-10-25 09:09:11 -07:00
Wez Furlong 1166fd158d add a security considerations section to the docs 2024-10-25 09:07:47 -07:00
Mike Hillyer 3dd3243635 Fix linter complaints. 2024-10-25 11:03:32 -04:00
Mike Hillyer b291ccb39b Add FAQ entry for shared throttles in a cluster. 2024-10-24 15:38:36 -04:00
Laurent Marchaud 6142808730 fix a few typos in the docs
Signed-off-by: Laurent Marchaud <laurent@marchaud.com>
2024-10-24 12:10:53 -04:00
Wez Furlong a7eb59f528 docs: update changelog for #305
closes: https://github.com/KumoCorp/kumomta/pull/305
2024-10-21 07:57:43 -07:00
Wez Furlong 6359820d3d startup: partially restore waiting for spool startup
We had a user report that launching kumod
post-7fbec8758720dad465d711458f6bf7923b778773 produced a panic about the
spool not being configured.

My supposition is that they were bringing the system up under load
(incoming traffic) and that a new message was being injected before we
had started the spool.

So what we do here is restore the spool_started check, but have it
be satisfied once the startup has been triggered, rather than
on completion of enumeration.

The result is that there is still a small window where we might turn
away a message during startup, but it should be very very small.
2024-10-16 16:35:13 -07:00
Mike Hillyer 46a1462371 Fix indentation on troubleshooting page. 2024-10-16 12:20:30 -04:00
Wez Furlong b0a37c709b mailparsing: fix replace_text_body / msg:append_text_XXX
We weren't stripping out an existing Content-Transfer-Encoding,
which could lead to duplicate and ambiguous results.
2024-10-15 20:08:25 -07:00
Wez Furlong 9cae055af9 fix a mime rfc2047 qp encoding issue for unstructured fields
This is a bit of a weird one, but the gist of it is that a sequence
of qp encoded words separated by spaces is, per the spec, re-assembled
and the spaces between the encoded words is ignored.

That means that any pair of adjacent encoded words should really
collapsed into a single encoded word that internally includes the
space in order for it to round trip.

To make things a little more complicated, the underlying quoted
printable crate that we're using implements the base qp space rather
than the modified version used by rfc2047 and it will strip trailing
whitespace from the words that are being encoded.

What we do here in this commit is an improvement, but may still
be subject to nuance in the future:

When rebuilding a header value, instead of splitting by space and
emitting a series of encoded words, we will now produce a single
encoded word for the entire header. That word may be split and
wrapped as needed to ensure that the maximum line length is
respected.
2024-10-15 17:48:19 -07:00