Commit Graph
393 Commits
Author SHA1 Message Date
Wez Furlong 4055c50522 authz: introduce an ACL facility
This commit is a step towards some general improvements around
our handling of authentication and authorization.

This commit is focused primarily on authorization, but there are
some adjustments to how we track authentication as part of enabling
that.

We now have a separate AuthInfo type that holds the overall
authentication information/context associated with an inbound
SMTP or HTTP session.

It is populated with the peer_address as a fact rather than a statement
of trust.

If authentication via the appropriate lua auth callback is successful,
then the AuthInfo has additional identities added.

There are some types and events for loading access control lists and
matching their rules against an AuthInfo.

There is now a system default ACL that is equivalent to the prior
hard-coded access policy that was encoded into each HTTP endpoint.

This change makes it possible to replace the ACL with a
user-defined ACL.

Later will be some work on authentication to allow more options
for HTTP auth.
2025-12-18 06:30:06 +00:00
Wez Furlong d68a4eca30 fix: log errors raised during should_enqueue_log_record
Previously, errors raised there would be silently discarded.
This commit logs them to the diagnostic log.
2025-12-18 06:23:43 +00:00
Wez Furlong fead711c80 mailparsing: improve charset detection/conformance fixing
We found an interesting issue where a MIME part was labelled as `text/`
with no explicit charset (and was thus implicitly "us-ascii", according
to the specs).  The content was actually UTF-8 encoded farsi text.

Since encoding_rs treats us-ascii as being an alias for windows-1252,
that part was re-encoded as bogus data, rather than being recognized as
UTF-8.

This commit improves the outcome in this situation:

1. Remove encoding_rs.  Replace its Charset type with the Encoding type
   that I added to the charset-normalizer crate in
   https://github.com/nickspring/charset-normalizer-rs/pull/45
   which allows us to actually differentiate between 7-bit ascii
   and latin-1.
2. Improve the handling of body part extraction; there were a couple
   of cases where we didn't consider the charset for text parts(!)
   and now we do, with some additional smarts for when the charset
   fails to decode.
3. Improve the conformance checking.  We do the majority of this
   during parsing, but we don't check for deeper issues such as
   the charset not matching the transfer-decoded payload because
   that is too costly to do for the majoriy of parses.  This commit
   introduces a deep conformance check that does validate that we
   can extract the part, flagging any failures as
   NEEDS_TRANSFER_ENCODING.
4. The charset detection options are threaded through the conformance
   checks and into the logic that extracts the parts during a rebuild,
   so that we can fix up those parts during rebuild.

The bulk of the conformance checking/fixing has been moved into the
mailparsing crate from the message crate, which makes the above a bit
easier and centralizes that logic better--fewer places to look to figure
things out.
2025-12-17 10:31:59 +00:00
Wez Furlong 6119250ebf templates: enable recursive lookup for Handlebars 2025-12-17 06:31:14 +00:00
Wez Furlong 716f880d41 spf: fix a potential panic if spf record contains U+200B
Adopting the get() method to handle the case where a string index is not
valid allows trapping and dealing appropriately with this abberant
input.
2025-12-16 13:53:27 +00:00
Wez Furlong b551d6b4c7 docs: update for release 2025.12.02-67ee9e96 2025-12-02 11:11:05 +00:00
Wez Furlong f0c2b490f4 metrics: fix prefix not being applied to metrics with multiple labels
we were producing output like this when `prefix=kumomta_`:

```
 # HELP kumomta_total_readyq_runs total number of times a readyq maintainer was run
 # TYPE kumomta_total_readyq_runs counter
 kumomta_total_readyq_runs 17
 # HELP kumomta_connection_count number of active connections
 # TYPE kumomta_connection_count gauge
 connection_count{service="smtp_client"} 0
 connection_count{service="esmtp_listener"} 0
 connection_count{service="lua"} 0
```

Those `connection_count` lines should be `kumomta_connection_count`.
2025-11-27 05:00:13 +00:00
Wez Furlong c5266e260e docs: changelog for #457 2025-11-25 13:17:46 +00:00
Wez Furlong 3d7802db37 docs: changelog for keysource event callback 2025-11-20 15:12:35 +00:00
Wez Furlong 65fde5c03b docs: fixup whitespace in changelog 2025-11-20 15:09:16 +00:00
crxandWez Furlong 39fbf150a9 traffic-gen header in changelog (#455)
* Changelog update

Co-authored-by: Wez Furlong <wez@wezfurlong.org>
2025-11-20 08:08:15 -07:00
Wez Furlong 2023aba6af docs: mention template dialect in injection and eval_template APIs 2025-11-20 14:50:15 +00:00
Wez Furlong 4c89acae96 docs and changelog for require_proxy_protocol #440 2025-11-13 15:27:39 +00:00
Wez Furlong f2f792e74f kumo.digest: add various HMAC_SHAXXX functions 2025-11-13 12:06:32 +00:00
Wez Furlong fdd2276dfb mod-digest: switch from ring::digest to aws-lc-rs::digest
Switch away from ring to aws-lc-rs which API compatible and has support
for more digest algorithms.

Add support for all of the current algorithms.
2025-11-13 11:10:13 +00:00
Wez Furlong 26a0c86c49 Expose version number to lua via kumo.version
I'm surprised that we didn't already do this!
It allows phrasing the policy to accommodate different versions
of kumo.
2025-11-13 06:30:35 +00:00
Wez Furlong 1c3b6dc14a smtp_dispatcher: fix reverse order for mx_list
When using an explicit mx_list, rather than consuming the DNS-provided
MX records, we were populating the connection plan LIFO with the mx_list
which is specified as a FIFO.

The result was that we would connect to the addresses in the reverse of
the intended order.

Most users only populate a single entry for mx_list, if they use this at
all, so it has flown under the radar.  This issue was introduced
together with the mx_list feature in bcd2946c53.

This commit corrects that issue so that the behavior conforms to the
intended and documented ordering, and tweaks the tests to accommodate
it; the test changes required were:

1. disconnect_in_data was actually sensitive to the order, and encoded
   the reverse ordering in its expectations. :-/
   It now correctly encodes the correct order, which should allow
   detection of regression in the future.

2. disconnect_terminate_ok is sensitive to extraneous TransientFailures.
   It was implicitly dependent on the ordering of the mx_list in the
   sense that it relied on the FIFO list returning as [127.0.0.1, ::1],
   which resulted in trying ::1 first (because that list is consumed as
   LIFO), failing due to ::1 not actually being bound in the test
   environment, and proceeding to test with 127.0.0.1 being the last
   working address.

   With the ordering fixed that test started to fail because the second
   host in the correct connection plan was ::1 and that was not
   reachable, triggering a TransientFailure against the expectations
   of the test.

   The goal of the test was to verify the behavior in the
   exhausted-connection-plan case, rather than the
   unreachable-final-mx-case, so the resolution here is to add v6
   addresses to the skip_hosts config.  Will that come back to haunt us?
   Time will tell.
2025-11-13 06:12:38 +00:00
Wez Furlong 4917ee6245 prohibited_hosts: add v4 and v6 any address
Saw an MX record that expands to 0.0.0.0 in the wild.

As well as being nonsense, that causes a mail loop, so we should add it
to prohibited_hosts.

I noticed that we also did not document its default value, so the
doc update here shows the before and after for this.
2025-11-10 15:09:11 +00:00
Wez Furlong c06b0bc0c0 docs: changelog for normalize_smtp_response 2025-11-09 08:13:39 +00:00
Wez Furlong b5f2c46f08 docs: add breaking change note about RCPT TO 552 status
in 18e55a6e91 I noticed that we should be
handling 552 as a transient failure, but I neglected to mention this
change in the changelog because it seemed obscure and "couldn't" hurt
anyone.

I overlooked that we have client response rewriting and of course this
change did impact one of our sponsors that rewrites certain transient
452 responses to 552 responses.

So, let's add a note explaining this to see if we can prevent others
from hitting it in the future.
2025-11-04 17:11:34 +00:00
Wez Furlong 4c6d7ca8ea add policy-extras.mail_auth module
This aggregates the various authentication-results producing auth checks
into a more convenient function.

Part of this change improves some plumbing in the dkim checking (we no
longer raise an error for a missing From, but instead indicate a failed
dkim result), and we now support passing down a resolver name to the
various checking functions, to facilitate testing and other more
advanced use cases.

refs: https://github.com/KumoCorp/kumomta/issues/16
refs: https://github.com/KumoCorp/kumomta/issues/84
2025-11-02 11:56:57 +00:00
Wez Furlong c085e368b2 dkim_(key|signer)_cache: enable thundering herd protection
Use the managed accessor for thundering herd protection.

May help with a situation as discussed in
https://github.com/KumoCorp/kumomta/pull/438
2025-10-31 18:04:17 +00:00
Wez Furlong 5ad7df0203 arc: expose arc_seal and arc_verify methods to lua
refs: https://github.com/KumoCorp/kumomta/issues/16
2025-10-31 12:16:32 +00:00
Wez Furlong e9deb2500d dkim: fix subdomain checking issue 2025-10-31 12:16:30 +00:00
Wez Furlong 6adb43221f Expose text wrapping function as kumo.string.wrap
Extracts the wrap function to its own little crate and expose that to
lua.
2025-10-29 12:06:06 +00:00
Wez Furlong 67bf14ca20 improve header wrapping functions
Augment the docs to clarify that msg:append_header and
msg:prepend_header do no modification of the value by default.

Adjust the wrapping used by Header:new_unstructured so that it
hard wraps before the SMTP line length limit.

Add optional encode parameter to msg:append_header and
msg:prepend_header so that those methods can opt-in to the
Header:new_unstructured formatting.

Add HeaderMap:append method to mirror the existing HeaderMap:prepend
method.

Add unit tests that demonstrate the effect of wrapping/encoding
for both the "dumb" msg:append_header and msg:prepend_header functions,
and the "smarter" HeaderMap:prepend and HeaderMap:append methods that
always encode their parameters.

Update the docs for the headermap flavor of these methods to indicate
that they will always encode the value.
2025-10-29 11:35:42 +00:00
Wez Furlong 1d70c2ffbb new smtp_server_rewrite_response event
This allows you to customize or otherwise adjust the responses
made by the smtp server.
2025-10-29 08:54:10 +00:00
Wez Furlong 47aac10ea6 data-loader: allow callback/event based loading of secrets
This allows a (more or less) arbitrary lua function to be used to load
data.

The primary advantage of this is that the size of the resulting
KeySource struct is smaller than it would be to hold the actual data
inline.  It also enables the surrounding code to be slightly better
factored.
2025-10-24 13:41:27 +01:00
Wez Furlong 6f31d5514c new Time and TimeDelta objects for lua
Motivated by https://github.com/KumoCorp/kumomta/pull/403,
this commit introduces reasonably full featured Time and TimeDelta
types built atop the chrono crate.

closes: https://github.com/KumoCorp/kumomta/pull/403
2025-10-22 17:26:35 +01:00
Dario MaiocchiandWez Furlong 847cc21e9b implement aes-cbc block with examples
Add CBC and ecb Block mode aes encryption/decryption.

To facilitate testing this better, and just to be nicer overall, adjust
data-loader's KeySource::key_data variant to support loading raw binary
bytes.

Co-authored-by: Wez Furlong <wez@wezfurlong.org>

Closes: https://github.com/KumoCorp/kumomta/pull/395
2025-10-22 11:55:16 +01:00
Wez Furlong 6fc0d782ff spf: populate smtp.mailfrom in Auth-Results, add check_msg func
Add a convenience function that is intended to be used around the
time that you might be building up the overall set of
Authentication-Results.

Populate missing smtp.mailfrom in SPF authentication result.
2025-10-21 21:25:21 +01:00
Wez Furlong 0a117e9407 fix incorrectly treating 552 as TransientFailure
We should do this ONLY when the response is to a RCPT TO command,
otherwise it is a permanent failure.

refs: https://github.com/KumoCorp/kumomta/issues/431
2025-10-16 16:03:12 +01:00
Wez Furlong 8ba93b5454 add smtp_server_rejections counter to metrics
This is incremented each time we log a Rejection record from the smtp
server.

The service label identifies the listener address, and there is a
special `total` that is incremented as a rollup across all listeners.

```
 # HELP smtp_server_rejections number of Rejection records logged by the smtp server
 # TYPE smtp_server_rejections counter
 smtp_server_rejections{service="127.0.0.1:2025"} 1
 smtp_server_rejections{service="total"} 1
```

closes: https://github.com/KumoCorp/kumomta/issues/429
2025-10-15 06:59:56 +01:00
Wez Furlong 7eeb755f30 mailparsing: fix quoting issue with Mailbox display name
Let's base our quoting decisions based on whether the string is fully
atext compatible, and once quoting, on whether the character requires
quoting within the quoted string.

refs: https://github.com/KumoCorp/kumomta/issues/428
2025-10-14 14:59:04 +01:00
Wez Furlong 43e45e12c0 smtp_server: fix max_line_length issue
Partially reverts aac842439f and fixes the
underlying issue that led me to throw that together: the issue was that
the check_line_lengths function needed to add 2 to the index otherwise
iterations after the first would over-estimate the length of the
currently found line by 2, because the prior index included the CRLF
that it found in the input.

While poking at this, I found that there was a fairly harmless edge case
around non-DATA line handling: we used a 1024 stack buffer to read the
command in, and would accept anything that fit into that as valid, even
though the limit is set to a slightly smaller value.
2025-10-14 08:15:52 +01:00
Wez Furlong aac842439f smtp_server: increase line_length_hard_limit default to 1000
We were including the CRLF in the measurement that we were making,
while the default value was the value from the spec excluding the CRLF.
Let's increase the default value to 1000 to make things match up.
2025-10-13 09:53:03 +01:00
Wez Furlong adfdcef1f8 spf: improve test coverage, couple of fixes
* Add exp= test based on an example in the rfc
 * Discovered that we were a bit too strict and didn't allow spaces
   as shown in that example
 * Add integration test for the ehlo_domain string quoting issue
   resolved in 236afa417f
 * lua spf check: Assume ehlo_domain = domain when sender is none
2025-10-13 08:22:36 +01:00
Wez Furlong b73d81b0fc dns: utilities and plumbing for querying DNSBLs
This commit does some plumbing work to allow defining alternative
resolvers with different names that can be used to query different
upstream DNS servers.

This is most useful when implement DNSBLs where you might have a custom
DNS server configured with one or more RBL zones that is reserved purely
for RBL lookups.

The plumbing introduces a more regular syntax for defining one of the
various implementations of the Resolver trait, as well as a new
Aggregate resolver impl that can query across multiple Resolvers until a
query is satisfied.

This allows some interesting and powerful configurations, such as
loading a static zone file into memory to query it directly, and/or
blending that together with querying either the system or some other
upstream DNS server as a fallback.

The various lookup functions (except for MX!) have been updated to
accept an optional alternate resolver name, so that they work together
with the above.

A new rbl_lookup function is also provided as a convenience for querying
the most common form of RBLS.

ptr_host and reverse_ip are two string utility functions that are likely
not going to be widely used, but are very convenient to have when you
do have a usecase that requires it!

closes: https://github.com/KumoCorp/kumomta/issues/269
2025-10-10 18:45:52 +01:00
Wez Furlong 67061e3a24 spf: fixup handling of NoRecordsFound during exists: check
Similar to the general purpose resolve function, we should map
NoRecordsFound to empty list(s) of results in the specialized
ip, mx and ptr lookup functions, otherwise we can cause rule
evaluation to terminate too soon.
2025-10-09 08:47:09 +01:00
Wez Furlong 5b914a597a deprecating the lapin-based amqp client
This never worked as well as the other amqp option that we support,
and upgrading it to the current version is a bit of a chore.

I'd rather just drop this dep and keep things more trim.

Since none of our sponsors are using the lapin implementation, let's
mark it as deprecated and we can take it out after we cut the next
stable release.
2025-10-08 11:17:55 +01:00
Wez Furlong 8fc11713d8 deps: update axum and utoipa ecosystem crates
A "casualty" of this is that the client-ip crate, which we were using
for its InsecureClientIp type for informational purposes, has retired
the InsecureClientIp type because it wasn't trustworthy overall
(https://github.com/imbolc/axum-client-ip/issues/32).

What we do for the time being is configure that crate to use the direct
peer IP.  There isn't a way to automagically select the "best"
information available without building in some kind of middleware, and
we probably should make that configurable in order to be fully
trustworthy.

So for now, we're punting on that until someone shows up with some
requirements and sponsorship and we'll make it happen then.
2025-10-08 10:24:57 +01:00
Wez Furlong 5c98511c06 add new kumo.file_type module
This is useful for example when processing attachments as part of a
content scanning policy.
2025-10-07 12:12:46 +01:00
Wez Furlong 7f4a929ca2 message transfer (xfer) support
This commit implements a kumomta-specific message transfer
protocol that is intended to be used to migrate messages
from one kumomta node to another.

The transfer is carried out using an HTTP POST request
to the destination node's http listener.

The request includes the full message metadata and body,
in a compressed form.

An xfer request can be made via `kcli xfer` (and thus also via an HTTP API
endpoint).  It works similarly to a rebind operation; you specify the
criteria to be used to match scheduled queues, along with the target
node for the xfer, and kumomta will find matching queues, drain out the
messages, make an adjustment to the metadata to capture current
scheduling information, and then place the messages into an xfer queue.

The xfer queue has hard-coded scheduling queue configuration of its own,
with the base retry interval set to 10 seconds, which should be suitably
aggressive for the intended use case.

You may apply shaping to affect the number of concurrent requests in a
similar way to how TSA shaping is configured.

On the receiving side, the incoming xfer sanity check to prohibit
trying to xfer to itself.

The spool id of the Message is not suitable to be reused verbatim on
another node (spool ids include the local mac address and creation
timestamp information, as well as a random component), so the receiving
side will derive an id that should be suitable for use on that node.
The originating node id and spool id will be preserved in metadata to
aid in tracing.

It is possible for an xfer request to target an existing xfer queue, so
that you can correct/update the target in various circumstances. In that
situation the messages will be "simply" moved from the source queue to
the destination queue.

It is possible to cancel an xfer request via `kcli xfer-cancel` (and
thus also via an HTTP API endpoint).  You specify the target queue,
which must be an xfer queue, and it will have its messages drained and
the metadata changes that were applied when the xfer was initiated will
be reversed, allowing the messages to then be reinserted into their
originating queue.

refs: https://github.com/KumoCorp/kumomta/issues/311
2025-10-07 10:58:07 +01:00
Wez Furlong de6e3bd0de allow admin bounce and suspend to specify exact queue name 2025-10-07 08:43:16 +01:00
Wez Furlong 407d2bc332 docs: update for multi-recipient changes 2025-10-07 08:35:59 +01:00
Wez Furlong b157e2f617 add smtp_server_data event for per-txn message validation
Great for doing content scanning and recipient list modification!
2025-10-07 07:53:13 +01:00
Wez Furlong 18e55a6e91 smtp: handle "too many recipients" status more appropriately
This commit adds a test for exceeding the number of recipients
allowed in a transaction and fleshes out our handling of that
status so that we will immediately try the remaining recipients
of the batch, without delaying the message by eg: 20 minutes
in the default retry parameters.

As part of this, I noticed that we were returning the wrong
smtp status code in our server implementation, so this
commit fixes that up.
2025-10-07 07:52:58 +01:00
Wez Furlong 206dc7c7fa optionally detect legacy 8-bit during check-fix 2025-10-07 07:50:46 +01:00
Wez Furlong 981de67f1c docs: update for 2025.10.06-5ec871ab release 2025-10-07 07:38:10 +01:00
Wez Furlong 307d0b3093 docs: changelog for #423
closes: https://github.com/KumoCorp/kumomta/issues/423
2025-10-06 19:41:53 +01:00