Browse Source

[Project] Preliminary SPF plugin in Lua

tags/2.3
Vsevolod Stakhov 4 years ago
parent
commit
2a0c89f256
1 changed files with 147 additions and 0 deletions
  1. 147
    0
      src/plugins/lua/spf.lua

+ 147
- 0
src/plugins/lua/spf.lua View File

@@ -0,0 +1,147 @@
--[[
Copyright (c) 2019, Vsevolod Stakhov <vsevolod@highsecure.ru>

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
]]--

local N = "spf"
local lua_util = require "lua_util"
local rspamd_spf = require "rspamd_spf"
local bit = require "bit"

if confighelp then
rspamd_config:add_example(nil, N,
'Performs SPF checks',
[[
spf {
# Enable module
enabled = true
# Number of elements in the cache of parsed SPF records
spf_cache_size = 2048;
# Default max expire for an element in this cache
spf_cache_expire = 1d;
# Whitelist IPs from checks
whitelist = "/path/to/some/file";
# Maximum number of recursive DNS subrequests (e.g. includes chanin length)
max_dns_nesting = 10;
# Maximum count of DNS requests per record
max_dns_requests = 30;
# Minimum TTL enforced for all elements in SPF records
min_cache_ttl = 5m;
# Disable all IPv6 lookups
disable_ipv6 = false;
}
]])
return
end

local symbols = {
fail = "R_SPF_FAIL",
softfail = "R_SPF_SOFTFAIL",
neutral = "R_SPF_NEUTRAL",
allow = "R_SPF_ALLOW",
dnsfail = "R_SPF_DNSFAIL",
permfail = "R_SPF_PERMFAIL",
na = "R_SPF_NA",
}

local default_config = {
spf_cache_size = 2048,
max_dns_nesting = 10,
max_dns_requests = 30,
whitelist = nil,
min_cache_ttl = 60 * 5,
disable_ipv6 = false,
symbols = symbols
}

local local_config = rspamd_config:get_all_opt('spf')

if local_config then
local_config = lua_util.override_defaults(default_config, local_config)
else
local_config = default_config
end

local function spf_check_callback(task)
local function flag_to_symbol(fl)
if bit.band(fl, rspamd_spf.flags.temp_fail) ~= 0 then
return local_config.symbols.temp_fail
elseif bit.band(fl, rspamd_spf.flags.perm_fail) ~= 0 then
return local_config.symbols.perm_fail
elseif bit.band(fl, rspamd_spf.flags.na) ~= 0 then
return local_config.symbols.na
end

return 'SPF_UNKNOWN'
end

local function policy_decode(res)
if res == rspamd_spf.policy.fail then
return local_config.symbols.fail,'-'
elseif res == rspamd_spf.policy.pass then
return local_config.symbols.allow,'+'
elseif res == rspamd_spf.policy.soft_fail then
return local_config.symbols.softfail,'~'
elseif res == rspamd_spf.policy.neutral then
return local_config.symbols.neutral,'?'
end

return 'SPF_UNKNOWN','?'
end

local function spf_resolved_cb(record, flags, err)
if record then
local result, flag_or_policy, error_or_addr = record:check_ip(task:get_from_ip())

if result then
local sym,code = policy_decode(flag_or_policy)
local opt = string.format('%s%s', code, error_or_addr.str or '???')
if bit.band(flags, rspamd_spf.flags.cached) ~= 0 then
opt = opt .. ':c'
end
task:insert_result(sym, 1.0, opt)
else
local sym = flag_to_symbol(flag_or_policy)
task:insert_result(sym, 1.0, error_or_addr)
end
else
local sym = flag_to_symbol(flags)
task:insert_result(sym, 1.0, err)
end
end

rspamd_spf.resolve(task, spf_resolved_cb)
end

-- Register all symbols and init rspamd_spf library
rspamd_spf.config(local_config)
local sym_id = rspamd_config:register_symbol{
name = 'SPF_CHECK',
type = 'callback',
flags = 'fine,empty',
groups = {'policies','spf'},
score = 0.0,
callback = spf_check_callback
}

for _,sym in pairs(local_config.symbols) do
rspamd_config:register_symbol{
name = sym,
type = 'virtual',
parent = sym_id,
groups = {'policies', 'spf'},
}
end



Loading…
Cancel
Save