There are only a handful of email MTA engines capable of operating at the speeds KumoMTA does. One that was popular in the past with large-scale Email Service Providers was the Momentum MTA. In this post, we will cover some important considerations when making the move from the Momentum MTA to KumoMTA.
Moving your platform to KumoMTA is a good way to keep your existing functionality and processing speed in a package that is still supported and in active development. Moreover, you can deploy on a modern Linux OS and have a voice in the future of the product. The conversion process can be painless if you remember a few essential concepts.
1) A binding is roughly equivalent to "Egress Source", and a binding group is roughly equivalent to an "Egress Pool". But KumoMTA took the concept much further and introduced the idea of a "Tenant". You can control delivery behaviour based on IP, pool, or a Tenant, giving you far greater control over multi-tenant deployments.
2) The Lua scripts you used will often translate closely into KumoMTA. However, it is important to understand that momentum used Lua for extra processing outside the config and with KumoMTA, Lua *IS* the config. Scripts you may have called out to before are now just written into the configuration.
3) Many settings were added as mitigation to common situations. Things like thread concurrency and drain rate do not require setting in KumoMTA because we manage those in the main code.
4) Many of those older configurations were written a decade ago and have only been modified slightly since. The world of email has changed, and so has the deployment environment. Be prepared to ask yourself, "Why did we do it this way?". KumoMTA may offer a better, more effective solution.
A word about Lua
Let's talk about Lua for a minute. This is one of the easiest languages to learn and is well suited to the purpose here as a powerful configuration tool. Cisco thought so, and so did Adobe Lightroom, and it is also used in many other places "under the hood". We use Lua because it allows for extreme customization right in the configuration itself, allowing for in-line iterations and conditional branches. Consider the Lua snippet below:
if myCount == 50 then
print ("The variable myCount is now at 50")
end
Even if you have never used Lua in your life, you can probably figure out what that does. Now let's do something a little more practical:
if CurrentTenantHeader == "XYZCompany" then
if AuthCheck == false then
msg:set_meta('queue','null')
else
msg:set_meta('queue','XYZ-mail')
end
end
Again, it should be pretty straightforward to see that we test to see if there is a Tenant Header with the value "XYZCompany". If it exists, we test some other variable "AuthCheck" to see if it is true or false, then either assign the message to the queue "XYZ-mail", or drop it in the "null" bin.
This is a very simplified example for demonstration, but it should be easy to see how powerful this can be when you extend this thinking to authentication systems, database lookups, and routing scenarios.
First, throw most of it out.
In any of the conversions we have done, it is typical to throw much of it out - literally. Most of the basic settings that almost everyone in the industry uses have been set to the KumoMTA defaults, so there is no need to set things like ehlo timeout and remote port and dozens of other settings because we made them defaults. Go ahead and change them if you like, but you don't need to set them if you already use the defaults. Likewise for the spool, logging, auth and other environment settings.
In addition, KumoMTA automatically performs MX rollup for all destination domains and queues messages based on the rolled-up MX fingerprint, so all messages destined for the same MX group will be queued and throttled together, regardless of the destination domain. This makes it much easier to manage traffic to large mailbox providers without any manual configuration. Of course, you can override these if you want to make exceptions to one particular domain.
Further, dynamic throttling, Feed Back Loop handling, and DKIM signing are all managed as first-class functions in KumoMTA with documented "helpers", so external configurations are not required.
Cluster management is also handled in very different ways in KumoMTA.
- Cluster-wide outbound throttles are handled by the central TSA Agent using Redis, which works well in both cloud and onprem networks.
- Instead of HAProxy or durable IPs, you can use our SOCKS5 proxy support with cluster awareness. This provides high resiliency and the in-transit data that is missing from other solutions.
- Clustered logging is so 2010. With KumoMTA, you can fire webhooks to a central reporting agent and eliminate a layer of complication.
- Updating cluster-wide configs is easy with something like Puppet, Chef, or Git, so there is no need for proprietary systems.
What's left?
The important bits that are left include listeners, throttling and routes.
In KumoMTA, SMTP and HTTP listeners are handled in very similar ways and can leverage Lua to enable multiple listener ports with iteration:
for _, ip_and_port in ipairs{'0:25', '0:587'} do
kumo.start_esmtp_listener{
listen = ip_and_port,
tls_private_key = "/opt/kumomta/etc/tls/mydomain.com/privkey.pem",
tls_certificate = "/opt/kumomta/etc/tls/mydomain.com/fullchain.pem",
}
end
Or without iteration:
kumo.start_http_listener {
listen = '0.0.0.0:8000',
trusted_hosts = { '127.0.0.1', '::1' },
use_tls = true,
}
Throttling is done through native cluster-aware traffic shaping with automation, meaning that in proxied environments, the nodes can share throttles and respect limits imposed by mailbox providers without having to manually divide throttles by node count.
A typical KumoMTA config is compact and can be shared cluster-wide.
[["hotmail.com".automation]]
regex = "/too many/"
action = {SetConfig={name="max_message_rate", value="300/h"}}
duration = "1 hour"
Routing messages in a multi-tenant environment is easy if you use the provided helpers.
The egress sources helper makes it easy to align ehlo host domains with pools of IPs, and the queues helper makes is easy to assign those egress pools based on X-Headers.
The listener domain helper makes it easy to assign behaviours based on domain and the dkim signing helper makes it dead simple to ensure signing all your out bound messages.
Get a second opinion
Finally, since you are moving from one MTA to another this might be a great time to review all the functions and the reasons they are there. As mentioned above, times have changed and there may be a better way today.
The team at KumoMTA can provide paid consulting services to help you get to an effective and efficient deployment quickly and we can help you cut through all the investigation and testing. If you have the time to spend on doing the conversion yourself but want to get community feedback on your efforts, feel free to join the Discord here.