mirror of
https://github.com/KumoCorp/kumomta.git
synced 2026-08-18 16:01:07 +00:00
403f80ac26
These files are used by me (wez!) while hacking things together locally as a non-privileged user (myself). They should not contribute to the overall message accounting database. Let's give each instantiation a separate temporary path.
292 lines
8.6 KiB
Lua
292 lines
8.6 KiB
Lua
-- THIS IS NOT THE FILE YOU ARE LOOKING FOR!
|
|
-- This file is wez's local hacking/testing config.
|
|
-- You do not want to use this. It is not appropriate
|
|
-- for your production needs.
|
|
local kumo = require 'kumo'
|
|
package.path = 'assets/?.lua;' .. package.path
|
|
local shaping = require 'policy-extras.shaping'
|
|
|
|
-- Called on startup to initialize the system
|
|
kumo.on('init', function()
|
|
kumo.configure_accounting_db_path(os.tmpname())
|
|
|
|
-- Define a listener.
|
|
-- Can be used multiple times with different parameters to
|
|
-- define multiple listeners!
|
|
kumo.start_esmtp_listener {
|
|
listen = '0.0.0.0:2025',
|
|
-- Override the hostname reported in the banner and other
|
|
-- SMTP responses:
|
|
-- hostname="mail.example.com",
|
|
|
|
-- override the default set of relay hosts
|
|
relay_hosts = { '127.0.0.1', '192.168.1.0/24' },
|
|
|
|
-- Customize the banner.
|
|
-- The configured hostname will be automatically
|
|
-- prepended to this text.
|
|
banner = 'Welcome to KumoMTA!',
|
|
|
|
-- Unsafe! When set to true, don't save to spool
|
|
-- at reception time.
|
|
-- Saves IO but may cause you to lose messages
|
|
-- if something happens to this server before
|
|
-- the message is spooled.
|
|
deferred_spool = false,
|
|
|
|
-- max_recipients_per_message = 1024
|
|
-- max_messages_per_connection = 10000,
|
|
}
|
|
|
|
kumo.configure_local_logs {
|
|
log_dir = '/var/tmp/kumo-logs',
|
|
}
|
|
|
|
kumo.start_http_listener {
|
|
listen = '0.0.0.0:8000',
|
|
-- allowed to access any http endpoint without additional auth
|
|
trusted_hosts = { '127.0.0.1', '::1', '192.168.1.0/24' },
|
|
}
|
|
kumo.start_http_listener {
|
|
use_tls = true,
|
|
listen = '0.0.0.0:8001',
|
|
-- allowed to access any http endpoint without additional auth
|
|
trusted_hosts = { '127.0.0.1', '::1' },
|
|
}
|
|
|
|
-- Define the default "data" spool location; this is where
|
|
-- message bodies will be stored
|
|
--
|
|
-- 'flush' can be set to true to cause fdatasync to be
|
|
-- triggered after each store to the spool.
|
|
-- The increased durability comes at the cost of throughput.
|
|
--
|
|
-- kind can be 'LocalDisk' (currently the default) or 'RocksDB'.
|
|
--
|
|
-- LocalDisk stores one file per message in a filesystem hierarchy.
|
|
-- RocksDB is a key-value datastore.
|
|
--
|
|
-- RocksDB has >4x the throughput of LocalDisk, and enabling
|
|
-- flush has a marginal (<10%) impact in early testing.
|
|
kumo.define_spool {
|
|
name = 'data',
|
|
path = '/var/tmp/kumo-spool/data',
|
|
flush = false,
|
|
kind = 'RocksDB',
|
|
}
|
|
|
|
-- Define the default "meta" spool location; this is where
|
|
-- message envelope and metadata will be stored
|
|
kumo.define_spool {
|
|
name = 'meta',
|
|
path = '/var/tmp/kumo-spool/meta',
|
|
flush = false,
|
|
kind = 'RocksDB',
|
|
}
|
|
|
|
-- Use shared throttles rather than in-process throttles
|
|
-- kumo.configure_redis_throttles { node = 'redis://127.0.0.1/' }
|
|
end)
|
|
|
|
kumo.on('get_egress_pool', function(pool_name)
|
|
if pool_name == 'pool0' then
|
|
local entries = {}
|
|
for i = 1, 3 do
|
|
local source_name = 'source' .. tostring(i)
|
|
table.insert(entries, { name = source_name, weight = i * 10 })
|
|
end
|
|
return kumo.make_egress_pool {
|
|
name = 'pool0',
|
|
entries = entries,
|
|
}
|
|
end
|
|
|
|
error("I don't know how to configure pool " .. pool_name)
|
|
end)
|
|
|
|
kumo.on('get_egress_source', function(source_name)
|
|
return kumo.make_egress_source {
|
|
name = source_name,
|
|
}
|
|
end)
|
|
|
|
--[[
|
|
|
|
-- Called to validate the helo and/or ehlo domain
|
|
kumo.on('smtp_server_ehlo', function(domain)
|
|
-- print('ehlo domain is', domain)
|
|
-- Use kumo.reject to return an error to the EHLO command
|
|
-- kumo.reject(420, 'wooooo!')
|
|
end)
|
|
|
|
-- Called to validate the sender
|
|
kumo.on('smtp_server_mail_from', function(sender)
|
|
-- print('sender', tostring(sender))
|
|
-- kumo.reject(420, 'wooooo!')
|
|
end)
|
|
|
|
-- Called to validate a recipient
|
|
kumo.on('smtp_server_rcpt_to', function(rcpt)
|
|
-- print('rcpt', tostring(rcpt))
|
|
end)
|
|
|
|
]]
|
|
|
|
-- Called once the body has been received.
|
|
-- For multi-recipient mail, this is called for each recipient.
|
|
kumo.on('smtp_server_message_received', function(msg)
|
|
local from_header = msg:from_header()
|
|
if not from_header then
|
|
kumo.reject(
|
|
552,
|
|
'5.6.0 DKIM signing requires a From header, but it is missing from this message'
|
|
)
|
|
end
|
|
|
|
-- local verify = msg:dkim_verify()
|
|
-- print('dkim', kumo.json_encode_pretty(verify))
|
|
-- msg:add_authentication_results(msg:get_meta 'hostname', verify)
|
|
-- print(msg:get_first_named_header_value 'Authentication-Results')
|
|
-- print(msg:get_data())
|
|
|
|
--[[
|
|
local failed = msg:check_fix_conformance(
|
|
-- check for and reject messages with these issues:
|
|
'MISSING_COLON_VALUE',
|
|
-- fix messages with these issues:
|
|
'LINE_TOO_LONG|NAME_ENDS_WITH_SPACE|NEEDS_TRANSFER_ENCODING|NON_CANONICAL_LINE_ENDINGS|MISSING_DATE_HEADER|MISSING_MESSAGE_ID_HEADER|MISSING_MIME_VERSION'
|
|
)
|
|
if failed then
|
|
kumo.reject(552, string.format('5.6.0 %s', failed))
|
|
end
|
|
]]
|
|
|
|
-- print('id', msg:id(), 'sender', tostring(msg:sender()))
|
|
-- print(msg:get_meta 'authn_id')
|
|
-- msg:set_meta('routing_domain', 'outlook.com')
|
|
|
|
-- Import scheduling information from X-Schedule and
|
|
-- then remove that header from the message
|
|
msg:import_scheduling_header('X-Schedule', true)
|
|
|
|
local signer = kumo.dkim.rsa_sha256_signer {
|
|
domain = msg:from_header().domain,
|
|
selector = 'default',
|
|
headers = { 'From', 'To', 'Subject' },
|
|
-- Using a file:
|
|
key = 'example-private-dkim-key.pem',
|
|
-- Using HashiCorp Vault:
|
|
--[[
|
|
key = {
|
|
vault_mount = "secret",
|
|
vault_path = "dkim/" .. msg:sender().domain
|
|
}
|
|
]]
|
|
}
|
|
msg:dkim_sign(signer)
|
|
|
|
-- msg:set_meta('queue', 'null')
|
|
|
|
-- set/get metadata fields
|
|
-- msg:set_meta('X-TestMSG', 'true')
|
|
-- print('meta X-TestMSG is', msg:get_meta 'X-TestMSG')
|
|
end)
|
|
|
|
-- Not the final form of this API, but this is currently how
|
|
-- we retrieve configuration used when making outbound
|
|
-- connections
|
|
kumo.on(
|
|
'get_egress_path_config',
|
|
function(routing_domain, egress_source, site_name)
|
|
-- print('get_egress_path_config', routing_domain, egress_source, site_name)
|
|
return kumo.make_egress_path {
|
|
-- enable_tls = 'OpportunisticInsecure',
|
|
enable_tls = 'Disabled',
|
|
-- max_message_rate = '5/min',
|
|
idle_timeout = '25s',
|
|
data_timeout = '20s',
|
|
data_dot_timeout = '25s',
|
|
connection_limit = 32,
|
|
-- max_connection_rate = '1/s',
|
|
-- max_ready = 16,
|
|
smtp_port = 2026,
|
|
-- max_deliveries_per_connection = 5,
|
|
|
|
-- hosts that we should consider to be poison because
|
|
-- they are a mail loop. The default for this is
|
|
-- { "127.0.0.0/8", "::1" }, but it is emptied out
|
|
-- in this config because we're using this to test
|
|
-- with fake domains that explicitly return loopback
|
|
-- addresses!
|
|
prohibited_hosts = {},
|
|
}
|
|
end
|
|
)
|
|
|
|
--[[
|
|
-- Not the final form of this API, but this is currently how
|
|
-- we retrieve configuration used for managing a queue.
|
|
kumo.on('get_queue_config', function(domain, tenant, campaign)
|
|
if domain == 'maildir.example.com' then
|
|
-- Store this domain into a maildir, rather than attempting
|
|
-- to deliver via SMTP
|
|
return kumo.make_queue_config {
|
|
protocol = {
|
|
maildir_path = '/var/tmp/kumo-maildir',
|
|
},
|
|
}
|
|
end
|
|
end)
|
|
]]
|
|
|
|
kumo.on('get_queue_config', function(domain, tenant, campaign)
|
|
return kumo.make_queue_config {
|
|
-- Age out messages after being in the queue for 2 minutes
|
|
-- max_age = "2 minutes"
|
|
-- retry_interval = "2 seconds",
|
|
-- max_retry_interval = "8 seconds",
|
|
egress_pool = 'pool0',
|
|
}
|
|
end)
|
|
|
|
-- A really simple inline auth "database" for very basic HTTP authentication
|
|
function simple_auth_check(user, password)
|
|
local password_database = {
|
|
['scott'] = 'tiger',
|
|
}
|
|
if password == '' then
|
|
return false
|
|
end
|
|
return password_database[user] == password
|
|
end
|
|
|
|
-- Consult a hypothetical sqlite database that has an auth table
|
|
-- with user and pass fields
|
|
function sqlite_auth_check(user, password)
|
|
local sqlite = require 'sqlite'
|
|
local db = sqlite.open '/tmp/auth.db'
|
|
local result = db:execute(
|
|
'select user from auth where user=? and pass=?',
|
|
user,
|
|
password
|
|
)
|
|
return result[1] == user
|
|
end
|
|
|
|
-- Use this to lookup and confirm a user/password credential
|
|
-- used with the http endpoint
|
|
kumo.on('http_server_validate_auth_basic', function(user, password)
|
|
return simple_auth_check(user, password)
|
|
|
|
-- or use sqlite
|
|
-- return sqlite_auth_check(user, password)
|
|
end)
|
|
|
|
-- Use this to lookup and confirm a user/password credential
|
|
-- when the client attempts SMTP AUTH PLAIN
|
|
kumo.on('smtp_server_auth_plain', function(authz, authc, password)
|
|
return simple_auth_check(authc, password)
|
|
-- or use sqlite
|
|
-- return sqlite_auth_check(authc, password)
|
|
end)
|