Commit Graph
205 Commits
Author SHA1 Message Date
Wez Furlong 907e85053a kumod: improve shutdown diagnostics
Log something every 15 seconds to demonstrate that we didn't get
hung up
2023-06-15 07:47:26 -07:00
Wez Furlong 38ae7cf20e smtp_server: improve debug/trace output
Show the buffer contents as a quoted string rather than an array
of the decimal bytes
2023-06-15 07:32:50 -07:00
Wez Furlong 0c70ab2c52 improve error message when converting lua -> rust types
refs: https://github.com/KumoCorp/kumomta/issues/56
2023-06-14 20:14:14 -07:00
Wez Furlong 2992e42138 add received_from and received_via meta values
We don't set received_via for http because axum/hyper make it a
bit weird to access that information and it will require some
effort to figure out how to get that without breaking the
existing proxy-aware client IP determination.
2023-06-13 15:18:06 -07:00
Wez Furlong 9c0ca973c2 replace ouroborous crate with self_cell
I picked ouroborous originally because the API was a bit nicer than that
in self_cell (we get to pick our own field names with ouroborous, but
don't in self_cell). However, ouroboros is now unmaintained and has a
soundness issue. The recommendation is to migrate, so that's what this
commit is.

refs: https://rustsec.org/advisories/RUSTSEC-2023-0042.html
refs: https://github.com/joshua-maros/ouroboros/issues/88

closes: https://github.com/KumoCorp/kumomta/security/dependabot/8
2023-06-12 14:52:16 -07:00
Wez Furlong 0249bd0d4d kcli: you may now list and cancel admin bounce entries
Entries now have a uuid that identifies them within a given instance
(NOT within a cluster).  The id is returned when the entry is
established:

```console
$ kcli --endpoint http://127.0.0.1:8000 bounce --everything --reason boop
{
  "id": "9a946198-9351-4e0e-b460-fc84e066878a",
  "bounced": {},
  "total_bounced": 0
}
```

You can list entries with their remaining duration:

```console
$ kcli --endpoint http://127.0.0.1:8000 bounce-list
[
  {
    "id": "9a946198-9351-4e0e-b460-fc84e066878a",
    "campaign": null,
    "tenant": null,
    "domain": null,
    "reason": "boop",
    "duration": "4m 57s 313ms 891us 882ns"
  }
]
```

and cancel them:

```console
$ kcli --endpoint http://127.0.0.1:8000 bounce-cancel --id 9a946198-9351-4e0e-b460-fc84e066878a
removed 9a946198-9351-4e0e-b460-fc84e066878a
```

and the error case if you try it again:

```console
$ kcli --endpoint http://127.0.0.1:8000 bounce-cancel --id 9a946198-9351-4e0e-b460-fc84e066878a
Error: bounce entry 9a946198-9351-4e0e-b460-fc84e066878a not found
```
2023-06-12 10:11:53 -07:00
Wez Furlong d6d2064564 admin: bounce: add id field to internals and to response
This doesn't do anything in the current milestone, but I wanted
to get it in there ready for work for the next milestone,
where I plan to make it possible to list and remove/cancel
a bounce, as well as persist them across a restart.
2023-06-12 07:11:43 -07:00
Wez Furlong 22451330f4 CLI: add kcli command for interacting with admin http endpoints
```console
$ kcli --help
KumoMTA CLI.

Full docs available at: <https://docs.kumomta.com>

Usage: kcli --endpoint <ENDPOINT> <COMMAND>

Commands:
  bounce
          Administratively bounce messages in matching queues
  set-log-filter
          Changes the diagnostic log filter
  help
          Print this message or the help of the given subcommand(s)

Options:
      --endpoint <ENDPOINT>
          URL to reach the KumoMTA HTTP API

  -h, --help
          Print help (see a summary with '-h')

  -V, --version
          Print version
```

```console
$ kcli bounce --help
Administratively bounce messages in matching queues.

Each individual message that is bounced will generate a log record capturing the event and then be removed from the spool.

Make sure that you mean it, as there is no going back!

The bounce will be applied immediately to queued messages, and the directive will remain in effect for the duration specified, causing newly received messages or messages that were in a transient state at the time the directive was received, to also be bounced as they are placed back into the matching queue(s).

Usage: kcli --endpoint <ENDPOINT> bounce [OPTIONS] --reason <REASON>

Options:
      --domain <DOMAIN>
          The domain name to match. If omitted, any domains will match!

      --campaign <CAMPAIGN>
          The campaign name to match. If omitted, any campaigns will match!

      --tenant <TENANT>
          The tenant name to match. If omitted, any tenant will match!

      --reason <REASON>
          The reason to log in the delivery logs

      --everything
          Purge all queues

      --duration <DURATION>
          The duration over which matching messages will continue to bounce. The default is '5m'

  -h, --help
          Print help (see a summary with '-h')
```

```console
$ kcli set-log-filter --help
Changes the diagnostic log filter

See <https://docs.kumomta.com/reference/kumo/set_diagnostic_log_filter/> for more information about the log filter syntax.

Usage: kcli --endpoint <ENDPOINT> set-log-filter <FILTER>

Arguments:
  <FILTER>

Options:
  -h, --help  Print help
```

refs: https://github.com/KumoCorp/kumomta/issues/55
2023-06-07 12:09:07 -07:00
Wez Furlong ec85c2da6f gracefully shutdown for SIGHUP, SIGTERM as well as SIGINT
Systemd doesn't terminate processes using SIGINT, so we didn't
get a chance to flush logs or messages to spool.

This should address that.

refs: https://github.com/KumoCorp/kumomta/issues/46
2023-05-31 12:45:36 -07:00
Wez Furlong ce7dece927 move domains out of listener, replace with get_listener_domain event
This commit also includes a policy helper
`policy_extras.listener_domains` to make it convenient to define
listener domains in toml and/or json files.

To facilitate this, the DomainMap rust structure has been exposed
to lua code via the new `kumo.domain_map.new` function.
2023-05-31 12:29:44 -07:00
Wez Furlong b7c557c31a define_egress_(pool|source) -> make_egress_(pool|source)
Switch the configuration plumbing for pools and sources to be pull-based
rather than push based.

In other words, rather than defining them in the `init` event,
you now need to supply them to the new `get_egress_pool` and
`get_egress_source` events.

Data is cached by default for 1 minute. This allows for new sources
to come into being on-demand, and for data to age out and change
over time, without requiring that the server be restarted.

This commit updates the reference section, but there is some content
in the user guide that refers to the old style of configuration that
will need to be updated.

refs: https://github.com/KumoCorp/kumomta/issues/13
2023-05-30 14:08:07 -07:00
Wez Furlong 81a4eba5a2 logging: Explicitly clear and flush the file map before completing
Otherwise we can race on shutdown and the process may exit without
fully flushing the logs!

refs: #46
2023-05-30 11:01:14 -07:00
Wez Furlong 9b1d82275c add spool_message_enumerated event
closes: #42
2023-05-30 10:14:37 -07:00
Wez Furlong 536827db99 make it an error to redefine an event handler
Produces an error like:

```
; KUMOD_LOG=kumod=info ./target/debug/kumod --policy simple_policy.lua
2023-05-16T18:16:02.112017Z  INFO main kumod::memory: using limits: soft=Some("101.22 GB"), hard=Some("134.96 GB")
Error: callback error
stack traceback:
        [C]: in function 'kumo.on'
        [string "simple_policy.lua"]:232: in main chunk
caused by: smtp_server_auth_plain event already has a handler defined at simple_policy.lua:226
```
2023-05-16 11:16:37 -07:00
Wez Furlong f21b7c53aa Add socks5 client support
Enable it via:

```lua
kumo.define_egress_source {
  name = "my source",
  socks5_proxy_server = '127.0.0.1:1080',
  socks5_proxy_source_address = '192.168.1.92',
}
```

couple it with:

```console
; RUST_LOG=proxy_server=trace cargo run -p proxy-server -- --listen 127.0.0.1:1080
   Compiling proxy-server v0.1.0 (/home/wez/wez-personal/kumomta/crates/proxy-server)
    Finished dev [unoptimized + debuginfo] target(s) in 1.30s
     Running `target/debug/proxy-server --listen '127.0.0.1:1080'`
[2023-05-11T23:54:56Z INFO  proxy_server] proxy listener on 127.0.0.1:1080
[2023-05-11T23:57:53Z TRACE proxy_server::proxy_handler] peer=127.0.0.1:49824 request: SocksV5Request { command: Bind, host: Ipv4([192, 168, 1, 92]), port: 0 }
[2023-05-11T23:57:53Z TRACE proxy_server::proxy_handler] peer=127.0.0.1:49824: status -> RequestStatus { status: Success, host: Ipv4([0, 0, 0, 0]), port: 0 }
[2023-05-11T23:57:53Z TRACE proxy_server::proxy_handler] peer=127.0.0.1:49824 request: SocksV5Request { command: Connect, host: Ipv4([103, 168, 172, 220]), port: 25 }
[2023-05-11T23:57:53Z TRACE proxy_server::proxy_handler] peer=127.0.0.1:49824: status -> RequestStatus { status: Success, host: Ipv4([192, 168, 1, 92]), port: 54052 }
[2023-05-11T23:57:53Z TRACE proxy_server::proxy_handler] peer=127.0.0.1:49824 -> going to passthru mode
```

refs: https://github.com/KumoCorp/kumomta/issues/45
2023-05-15 17:38:17 -07:00
Wez Furlong 9d3411431f Add outbound SMTP auth support
refs: https://github.com/KumoCorp/kumomta/issues/10
2023-05-15 17:38:17 -07:00
Wez Furlong db0c042cef Add amqp client
refs: https://github.com/KumoCorp/kumomta/issues/31
2023-05-15 17:24:15 -07:00
Wez Furlong 75f72aef7d tone down the debug
refs: #19
2023-05-10 09:53:09 -07:00
Wez Furlong 6fdee41600 Add HA Proxy support
I used a config like this for testing:

```
global
    log stdout  format raw  local0  debug

defaults
    timeout connect 10s
    timeout client 30s
    timeout server 30s
    log global

listen outboundsmtp
    log global
    bind 0:2526 accept-proxy
    mode tcp
    use-server v4 if { src 0.0.0.0/0 }
    use-server v6 if { src ::/0 }
    server v4 0.0.0.0 source 0.0.0.0 usesrc clientip
    server v6 ::: source ::: usesrc clientip
```

Launched via:

```console
$ sudo haproxy -f assets/haproxy.conf -V
```

with the source:

```lua
  kumo.define_egress_source {
    name = 'w00t',
    ha_proxy_server = '127.0.0.1:2526',
    ha_proxy_source_address = '192.168.1.92',
  }
```

refs: #19
2023-05-10 09:50:06 -07:00
Wez Furlong de325af0ac surface lua errors raised by get_egress_path_config to diagnostic log
Previously, they would get logged only as a transient failure now
we log to both places.
2023-05-09 10:01:13 -07:00
Wez Furlong 927721fc0c Add toml parsing/encoding functions to lua
refs: https://github.com/KumoCorp/kumomta/issues/43
2023-05-09 08:40:37 -07:00
Wez Furlong 992cae1409 Allow loading a key source directly from lua
This makes it easier to compose secret management with a variety
of lua functions without having to build that directly into each
of them.

In particular: one can now use vaults or other secret stores that
we add in the future to manage credentials for HTTP clients.
2023-05-09 08:25:54 -07:00
Wez Furlong 3a168687a8 rustfmt 2023-05-08 09:07:20 -07:00
Wez Furlong 65c7a9e5a6 Add reception and delivery protocol to log record
closes: https://github.com/KumoCorp/kumomta/issues/38
2023-05-08 08:39:40 -07:00
Wez Furlong 5dfa6e5865 add http client module
```lua
  local response = kumo.http
    .build_client({})
    :get('https://example.com/')
    :send()
  print(response:status_code(), response:status_reason())
  for k, v in pairs(response:headers()) do
    print("Header", k, v)
  end
  print(response:text())
```
2023-05-05 14:26:09 -07:00
Wez Furlong d6203f6eff Allow logging via a lua hook
Still need to write up some proper docs, but the gist is that
the example below will enqueue a message holding the content
of the JsonRecord for each log event, and that message will be
dispatched via a custom lua delivery mechanism.

```lua
kumo.on('init', function()
  kumo.configure_log_hook {}
end)

kumo.on('make.lua-sender', function(domain, tenant, campaign)
  print 'making lua sender'
  local sender = {}
  function sender:send(message)
    print('Sending a message!', message:get_data())
    return 'Super!'
  end
  return sender
end)

kumo.on('should_enqueue_log_record', function(msg)
  local log_record = msg:get_meta 'log_record'
  print('should_enqueue_log_record', log_record)
  -- avoid an infinite loop caused by logging that we logged
  if log_record.queue ~= 'lua-stuff' then
    msg:set_meta('queue', 'lua-stuff')
    return true
  end
  return false
end)

kumo.on('get_queue_config', function(domain, tenant, campaign)
  print('get_queue_config', domain, tenant, campaign)
  if domain == 'lua-stuff' then
    return kumo.make_queue_config {
      protocol = {
        custom_lua = {
          constructor = 'make.lua-sender',
        },
      },
    }
  end

  return kumo.make_queue_config {}
end)
```
2023-05-04 16:20:29 -07:00
Wez Furlong 2bdcd9cb4e Allow configuring multiple instances of local logging 2023-05-04 15:22:43 -07:00
Wez Furlong ac4d645670 Basic plumbing for handling deliveries in lua
```lua
-- Constructs a connection object in lua
kumo.on('make.lua-sender', function(domain, tenant, campaign)
  print 'making lua sender'
  local sender = {}
  function sender:send(message)
    print('Sending a message!', message)

    -- To log a transient failure:
    -- kumo.reject(420, 'boo!')

    -- To log a permanent failure:
    -- kumo.reject(500, "perm fail")

    -- To log success, return a string with additional data
    -- to be logged
    return 'Super!'
  end
  return sender
end)

-- Make everything use the lua "connection":
kumo.on('get_queue_config', function(domain, tenant, campaign)
  return kumo.make_queue_config {
    protocol = {
      custom_lua = {
        constructor = 'make.lua-sender',
      }
    },
  }
end)
```
2023-05-04 13:51:37 -07:00
Wez Furlong cdaae5adb1 move ideal_connection_count to be closer to caller 2023-05-04 13:51:37 -07:00
Wez Furlong 4cf2dd5e28 refactor: move DeliveryMetrics to its own file 2023-05-04 13:51:37 -07:00
Wez Furlong 28c3383169 queues: split ready queue in two halves
In preparation for web hook and other protocol delivery,
split the egress path / ready queue stuff into two pieces:

* The ready queue management
* A trait for dispatching ready queues

Implement smtp dispatching in terms of that new trait.

Add a stub trait for performing dispatching via lua; that will
be fleshed out in a follow up commit.

refs: https://github.com/KumoCorp/kumomta/issues/18
refs: https://github.com/KumoCorp/kumomta/issues/31
refs: https://github.com/KumoCorp/kumomta/issues/32
refs: https://github.com/KumoCorp/kumomta/issues/36
2023-05-04 13:51:37 -07:00
Wez Furlong ef2abc7031 allow comments in json parsed by kumo.json_load, kumo.json_parse 2023-04-26 15:08:29 -07:00
Wez Furlong cb8e75c37e queue: fix missing async dispatch of get_queue_config event 2023-04-26 06:38:04 -07:00
Wez Furlong 183621c396 fix get_egress_path_config site-name param, fix async call
Ensure that we trigger an async compatible callback so that
we can use eg: DNS resolution and memoization when processing it.

Fix the site name so that it doesn't include "source->site" and is
just the pure site name.
2023-04-25 15:58:17 -07:00
Wez Furlong ea8a8f7bb2 load meta/payload if needed during logging
In some cases (eg: initial spool -> expiration) we don't have
any metadata loaded, causing the logs to say that the metadata
wasn't loaded.

This commit loads it as needed.
2023-04-21 12:51:16 -07:00
Wez Furlong 217a9ba09f log transient failure when all connection attempts fail
We log all of the connection failure results in the log message
to aid in diagnostics.
2023-04-21 12:33:53 -07:00
Wez Furlong 07b75dd117 remove oldtime feature from chrono crate
it pulls in an old version of the time crate that flags many
rust projects as being vulnerable to an old CVE.

We still have other references to time that I'll remove in a follow up.
2023-04-18 15:02:59 -07:00
Wez Furlong 385788765e rename delayed_count -> scheduled_count in metrics 2023-04-12 12:07:58 -07:00
Wez Furlong 9225644ce7 export ready queue size
```
  "ready_count": {
    "help": "number of messages in the ready queue",
    "type": "gauge",
    "value": {
      "service": {
        "smtp_client:source1->loopback.dummy-mx.example.com": 46.0,
        "smtp_client:source2->loopback.dummy-mx.example.com": 152.0,
      }
    }
  },
```

closes: #30
2023-04-03 10:14:16 -07:00
Wez Furlong d3a5626180 Add log tailer utility
This commit adjusts the logger:

* When a log segment is finised, the `w` permission bits are removed.
  This is used to signal to the tailer that there will be no additional
  data written to that file.

* On startup, kumod will remove the `w` bits from all files in the
  configured log directories so that any that were left on by a prior
  incarnation will be considered closed when the new generation is
  started up.

* Add a `tailer` utility that will essentially `cat` all of the log
  files in a given directory in the appropriate order, then go into
  log tailing mode, where it will wait for new data to show up.

* When reading data, if it hits EOF, the tailer will wait around until
  more data shows up, or until the `w` bit is removed from the file
  that it is reading.

The idea is that, in a later commit, the tailer will be able to persist
a checkpoint file to record where it got to, and be able to dispatch
chunks of data to eg: a webhook push utility, advancing the checkpoint
only when the utility indicates success.

For now, running `cargo run -p tailer -- --tail /var/tmp/kumo-logs` is
a convenient way to tail the logs as they are written.
2023-03-30 14:01:46 -07:00
Wez Furlong 4b9c2f5238 fix default policy and include path to match docs 2023-03-27 09:41:42 -07:00
Wez Furlong e87a5b18bb Improve performance of finding CRLF.CRLF for large messages by 20x
Using a better tuned algorithm for finding a substring match removes
a CPU bottleneck and improves message throughput by 20x at 100Kb
message sizes.
2023-03-27 09:39:51 -07:00
Wez Furlong ece1b89b00 add admin HTTP API for changing the log level 2023-03-24 10:04:00 -07:00
Wez Furlong 6740a68fad add kumo.set_diagnostic_log_filter for adjusting log levels via config 2023-03-24 09:02:10 -07:00
Wez Furlong fbf50bc28f adjust debug log levels for the logging thread 2023-03-24 08:00:57 -07:00
Wez Furlong a81d9d19f9 adjust default logging to include INFO logs 2023-03-24 07:59:46 -07:00
Wez Furlong b57126f724 allow STARTTLS to fail without wiping out the connection
This should improve diagnostics around handshake errors
2023-03-23 16:01:47 -07:00
Wez Furlong 80c6f21b22 marginally improve logging of failed TLS handshake from server side 2023-03-23 15:16:44 -07:00
Wez Furlong 3a26c60438 also export counters as json
expand the docs to show what the data looks like
2023-03-23 10:47:17 -07:00
Wez Furlong 0167041ed0 add kumo.memoize as a way to add caching to any lua function 2023-03-22 17:01:07 -07:00