Files
kumomta/examples/smart-sink-docker/policy/init.lua
T
2024-09-11 11:00:24 -07:00

113 lines
3.5 KiB
Lua

-- This config acts as a sink that will discard all received mail
local kumo = require 'kumo'
package.path = 'assets/?.lua;' .. package.path
local utils = require 'policy-extras.policy_utils'
local SINK_DATA_FILE = os.getenv 'SINK_DATA'
or '/opt/kumomta/etc/policy/responses.toml'
-- Resolve our hostname, then derive the docker network from that.
-- This assumes that the network is a /24 and that HOSTNAME is
-- injected by docker and resolves to the container IP on that network
local function resolve_docker_network()
local MY_IPS = kumo.dns.lookup_addr(os.getenv 'HOSTNAME' or 'localhost')
local DOCKER_NETWORK = string.match(MY_IPS[1], '^(.*)%.%d+$') .. '.0/24'
return DOCKER_NETWORK
end
kumo.on('init', function()
kumo.configure_accounting_db_path(os.tmpname())
kumo.set_config_monitor_globs { SINK_DATA_FILE }
local DOCKER_NETWORK = resolve_docker_network()
local SINK_PORT = os.getenv 'SINK_PORT' or '25'
kumo.start_esmtp_listener {
listen = '0:' .. SINK_PORT,
-- Explicitly an open relay, because we want to sink everything
relay_hosts = { '0.0.0.0/0' },
banner = 'This system will sink and discard all mail',
}
local SINK_HTTP = os.getenv 'SINK_HTTP' or '8000'
kumo.start_http_listener {
listen = '0.0.0.0:' .. SINK_HTTP,
trusted_hosts = { '127.0.0.1', '::1', DOCKER_NETWORK },
}
-- Define spool locations
-- This is unused by this config, but we are required to
-- define a default spool location.
local spool_dir = os.getenv 'SINK_SPOOL' or '/var/spool/kumomta'
for _, name in ipairs { 'data', 'meta' } do
kumo.define_spool {
name = name,
path = spool_dir .. '/' .. name,
}
end
-- No logs are configured: we don't need them
end)
-- Load and parse the responses.toml data and resolve the configuration
-- for a given domain
local function load_data_for_domain(domain)
local data = kumo.toml_load(SINK_DATA_FILE)
local config = data.domain[domain] or data.default
config.bounces = data.bounce[domain] or { { code = 550, msg = 'boing!' } }
config.defers = data.defer[domain] or { { code = 451, msg = 'later!' } }
return config
end
-- Cache the result of a load_data_for_domain call
local resolve_domain = kumo.memoize(load_data_for_domain, {
name = 'response-data-cache',
ttl = '1 hour',
capacity = 100,
})
kumo.on('smtp_server_message_received', function(msg)
local recipient = msg:recipient()
-- Do any special responses requested by the client
if string.find(recipient.user, 'tempfail') then
kumo.reject(400, 'tempfail requested')
end
if string.find(recipient.user, 'permfail') then
kumo.reject(500, 'permfail requested')
end
if utils.starts_with(recipient.user, '450-') then
kumo.reject(450, 'you said ' .. recipient.user)
end
if utils.starts_with(recipient.user, '250-') then
msg:set_meta('queue', 'null')
return
end
-- Now any general bounce responses based on the toml file
local domain = recipient.domain
local config = resolve_domain(domain)
local d100 = math.random(100)
local selection = nil
if d100 < config.bounce then
selection = config.bounces
elseif d100 < config.bounce + config.defer then
selection = config.defers
end
if selection then
local choice = selection[math.random(#selection)]
kumo.reject(choice.code, choice.msg)
end
-- Finally, accept and discard any messages that haven't
-- been rejected already
msg:set_meta('queue', 'null')
end)
kumo.on('http_message_generated', function(msg)
-- Accept and discard all messages
msg:set_meta('queue', 'null')
end)