commit 3e4b62c: [Feature] Settings: add ip_map check and rework structure slightly
Vsevolod Stakhov
vsevolod at highsecure.ru
Tue Jan 12 14:00:09 UTC 2021
Author: Vsevolod Stakhov
Date: 2021-01-12 13:54:58 +0000
URL: https://github.com/rspamd/rspamd/commit/3e4b62c067c6bfeb8d9b43f2ac42b799b3df6853 (HEAD -> master)
[Feature] Settings: add ip_map check and rework structure slightly
---
src/plugins/lua/settings.lua | 410 ++++++++++++++++++++++++-------------------
1 file changed, 232 insertions(+), 178 deletions(-)
diff --git a/src/plugins/lua/settings.lua b/src/plugins/lua/settings.lua
index 56a9f5079..bb0ec6f76 100644
--- a/src/plugins/lua/settings.lua
+++ b/src/plugins/lua/settings.lua
@@ -23,7 +23,7 @@ end
-- https://rspamd.com/doc/configuration/settings.html
local rspamd_logger = require "rspamd_logger"
-local rspamd_maps = require "lua_maps"
+local lua_maps = require "lua_maps"
local lua_util = require "lua_util"
local rspamd_ip = require "rspamd_ip"
local rspamd_regexp = require "rspamd_regexp"
@@ -189,17 +189,17 @@ end
local function check_addr_setting(expected, addr)
local function check_specific_addr(elt)
if expected.name then
- if rspamd_maps.rspamd_maybe_check_map(expected.name, elt.addr) then
+ if lua_maps.rspamd_maybe_check_map(expected.name, elt.addr) then
return true
end
end
if expected.user then
- if rspamd_maps.rspamd_maybe_check_map(expected.user, elt.user) then
+ if lua_maps.rspamd_maybe_check_map(expected.user, elt.user) then
return true
end
end
if expected.domain and elt.domain then
- if rspamd_maps.rspamd_maybe_check_map(expected.domain, elt.domain) then
+ if lua_maps.rspamd_maybe_check_map(expected.domain, elt.domain) then
return true
end
end
@@ -226,7 +226,7 @@ local function check_string_setting(expected, str)
return true
end
elseif expected.check then
- if rspamd_maps.rspamd_maybe_check_map(expected.check, str) then
+ if lua_maps.rspamd_maybe_check_map(expected.check, str) then
return true
end
end
@@ -235,7 +235,7 @@ end
local function check_ip_setting(expected, ip)
if not expected[2] then
- if rspamd_maps.rspamd_maybe_check_map(expected[1], ip:to_string()) then
+ if lua_maps.rspamd_maybe_check_map(expected[1], ip:to_string()) then
return true
end
else
@@ -252,6 +252,10 @@ local function check_ip_setting(expected, ip)
return false
end
+local function check_map_setting(map, input)
+ return map:get_key(input)
+end
+
local function priority_to_string(pri)
if pri then
if pri >= 3 then
@@ -393,199 +397,206 @@ local function check_settings(task)
end
--- Process settings based on their priority
-local function process_settings_table(tbl, allow_ids, is_static)
- local get_priority = function(elt)
- local pri_tonum = function(p)
- if p then
- if type(p) == "number" then
- return tonumber(p)
- elseif type(p) == "string" then
- if p == "high" then
- return 3
- elseif p == "medium" then
- return 2
- end
-
- end
-
- end
-
- return 1
- end
-
- return pri_tonum(elt['priority'])
+local function convert_to_table(chk_elt, out)
+ if type(chk_elt) == 'string' then
+ return {out}
end
- -- Check the setting element internal data
- local process_setting_elt = function(name, elt)
+ return out
+end
- lua_util.debugm(N, rspamd_config, 'process settings "%s"', name)
- -- Process IP address: converted to a table {ip, mask}
- local function process_ip_condition(ip)
- local out = {}
+-- Process IP address: converted to a table {ip, mask}
+local function process_ip_condition(ip)
+ local out = {}
- if type(ip) == "table" then
- for _,v in ipairs(ip) do
- table.insert(out, process_ip_condition(v))
- end
- elseif type(ip) == "string" then
- local slash = string.find(ip, '/')
+ if type(ip) == "table" then
+ for _,v in ipairs(ip) do
+ table.insert(out, process_ip_condition(v))
+ end
+ elseif type(ip) == "string" then
+ local slash = string.find(ip, '/')
- if not slash then
- -- Just a plain IP address
- local res = rspamd_ip.from_string(ip)
+ if not slash then
+ -- Just a plain IP address
+ local res = rspamd_ip.from_string(ip)
- if res:is_valid() then
- out[1] = res
- out[2] = 0
- else
- -- It can still be a map
- out[1] = res
- end
- else
- local res = rspamd_ip.from_string(string.sub(ip, 1, slash - 1))
- local mask = tonumber(string.sub(ip, slash + 1))
+ if res:is_valid() then
+ out[1] = res
+ out[2] = 0
+ else
+ -- It can still be a map
+ out[1] = res
+ end
+ else
+ local res = rspamd_ip.from_string(string.sub(ip, 1, slash - 1))
+ local mask = tonumber(string.sub(ip, slash + 1))
- if res:is_valid() then
- out[1] = res
- out[2] = mask
- else
- rspamd_logger.errx(rspamd_config, "bad IP address: " .. ip)
- return nil
- end
- end
+ if res:is_valid() then
+ out[1] = res
+ out[2] = mask
else
+ rspamd_logger.errx(rspamd_config, "bad IP address: " .. ip)
return nil
end
-
- return out
end
+ else
+ return nil
+ end
- -- Process email like condition, converted to a table with fields:
- -- name - full email (surprise!)
- -- user - user part
- -- domain - domain part
- -- regexp - full email regexp (yes, it sucks)
- local function process_email_condition(addr)
- local out = {}
- if type(addr) == "table" then
- for _,v in ipairs(addr) do
- table.insert(out, process_email_condition(v))
+ return out
+end
+
+-- Process email like condition, converted to a table with fields:
+-- name - full email (surprise!)
+-- user - user part
+-- domain - domain part
+-- regexp - full email regexp (yes, it sucks)
+local function process_email_condition(addr)
+ local out = {}
+ if type(addr) == "table" then
+ for _,v in ipairs(addr) do
+ table.insert(out, process_email_condition(v))
+ end
+ elseif type(addr) == "string" then
+ if string.sub(addr, 1, 4) == "map:" then
+ -- It is map, don't apply any extra logic
+ out['name'] = addr
+ else
+ local start = string.sub(addr, 1, 1)
+ if start == '/' then
+ -- It is a regexp
+ local re = rspamd_regexp.create(addr)
+ if re then
+ out['regexp'] = re
+ else
+ rspamd_logger.errx(rspamd_config, "bad regexp: " .. addr)
+ return nil
end
- elseif type(addr) == "string" then
- if string.sub(addr, 1, 4) == "map:" then
- -- It is map, don't apply any extra logic
+
+ elseif start == '@' then
+ -- It is a domain if form @domain
+ out['domain'] = string.sub(addr, 2)
+ else
+ -- Check user at domain parts
+ local at = string.find(addr, '@')
+ if at then
+ -- It is full address
out['name'] = addr
else
- local start = string.sub(addr, 1, 1)
- if start == '/' then
- -- It is a regexp
- local re = rspamd_regexp.create(addr)
- if re then
- out['regexp'] = re
- else
- rspamd_logger.errx(rspamd_config, "bad regexp: " .. addr)
- return nil
- end
-
- elseif start == '@' then
- -- It is a domain if form @domain
- out['domain'] = string.sub(addr, 2)
- else
- -- Check user at domain parts
- local at = string.find(addr, '@')
- if at then
- -- It is full address
- out['name'] = addr
- else
- -- It is a user
- out['user'] = addr
- end
- end
+ -- It is a user
+ out['user'] = addr
end
- else
- return nil
end
-
- return out
end
+ else
+ return nil
+ end
- -- Convert a plain string condition to a table:
- -- check - string to match
- -- regexp - regexp to match
- local function process_string_condition(addr)
- local out = {}
- if type(addr) == "table" then
- for _,v in ipairs(addr) do
- table.insert(out, process_string_condition(v))
- end
- elseif type(addr) == "string" then
- if string.sub(addr, 1, 4) == "map:" then
- -- It is map, don't apply any extra logic
- out['check'] = addr
- else
- local start = string.sub(addr, 1, 1)
- if start == '/' then
- -- It is a regexp
- local re = rspamd_regexp.create(addr)
- if re then
- out['regexp'] = re
- else
- rspamd_logger.errx(rspamd_config, "bad regexp: " .. addr)
- return nil
- end
+ return out
+end
- else
- out['check'] = addr
- end
+-- Convert a plain string condition to a table:
+-- check - string to match
+-- regexp - regexp to match
+local function process_string_condition(addr)
+ local out = {}
+ if type(addr) == "table" then
+ for _,v in ipairs(addr) do
+ table.insert(out, process_string_condition(v))
+ end
+ elseif type(addr) == "string" then
+ if string.sub(addr, 1, 4) == "map:" then
+ -- It is map, don't apply any extra logic
+ out['check'] = addr
+ else
+ local start = string.sub(addr, 1, 1)
+ if start == '/' then
+ -- It is a regexp
+ local re = rspamd_regexp.create(addr)
+ if re then
+ out['regexp'] = re
+ else
+ rspamd_logger.errx(rspamd_config, "bad regexp: " .. addr)
+ return nil
end
+
else
- return nil
+ out['check'] = addr
end
-
- return out
end
+ else
+ return nil
+ end
+
+ return out
+end
+
+local function get_priority (elt)
+ local pri_tonum = function(p)
+ if p then
+ if type(p) == "number" then
+ return tonumber(p)
+ elseif type(p) == "string" then
+ if p == "high" then
+ return 3
+ elseif p == "medium" then
+ return 2
+ end
- local convert_to_table = function(chk_elt, out)
- if type(chk_elt) == 'string' then
- return {out}
end
- return out
end
- -- Used to create a checking closure: if value matches expected somehow, return true
- local function gen_check_closure(expected, check_func)
- return function(value)
- if not value then return false end
+ return 1
+ end
- if type(value) == 'function' then
- value = value()
- end
+ return pri_tonum(elt['priority'])
+end
- if value then
+-- Used to create a checking closure: if value matches expected somehow, return true
+local function gen_check_closure(expected, check_func)
+ return function(value)
+ if not value then return false end
- if not check_func then
- check_func = function(a, b) return a == b end
- end
+ if type(value) == 'function' then
+ value = value()
+ end
- local ret = fun.any(function(d)
- return check_func(d, value)
- end, expected)
- if ret then
- return true
- end
- end
+ if value then
+
+ if not check_func then
+ check_func = function(a, b) return a == b end
+ end
- return false
+ local ret
+ if type(expected) == 'table' then
+ ret = fun.any(function(d)
+ return check_func(d, value)
+ end, expected)
+ else
+ ret = check_func(expected, value)
+ end
+ if ret then
+ return true
end
end
+ return false
+ end
+end
+
+-- Process settings based on their priority
+local function process_settings_table(tbl, allow_ids, is_static)
+
+ -- Check the setting element internal data
+ local process_setting_elt = function(name, elt)
+
+ lua_util.debugm(N, rspamd_config, 'process settings "%s"', name)
+
local out = {}
local checks = {}
- if elt['ip'] then
+ if elt.ip then
local ips_table = process_ip_condition(elt['ip'])
if ips_table then
@@ -601,8 +612,26 @@ local function process_settings_table(tbl, allow_ids, is_static)
}
end
end
- if elt['client_ip'] then
- local client_ips_table = process_ip_condition(elt['client_ip'])
+ if elt.ip_map then
+ local ips_map = lua_maps.map_add_from_ucl(elt.ip_map, 'radix',
+ 'settings ip map for ' .. name)
+
+ if ips_map then
+ lua_util.debugm(N, rspamd_config, 'added ip_map condition to "%s"',
+ name)
+ checks.ip_map = {
+ check = gen_check_closure(ips_map, check_map_setting),
+ extract = function(task)
+ local ip = task:get_from_ip()
+ if ip and ip:is_valid() then return ip end
+ return nil
+ end,
+ }
+ end
+ end
+
+ if elt.client_ip then
+ local client_ips_table = process_ip_condition(elt.client_ip)
if client_ips_table then
lua_util.debugm(N, rspamd_config, 'added client_ip condition to "%s": %s',
@@ -618,8 +647,26 @@ local function process_settings_table(tbl, allow_ids, is_static)
}
end
end
- if elt['from'] then
- local from_condition = process_email_condition(elt['from'])
+ if elt.client_ip_map then
+ local ips_map = lua_maps.map_add_from_ucl(elt.ip_map, 'radix',
+ 'settings client ip map for ' .. name)
+
+ if ips_map then
+ lua_util.debugm(N, rspamd_config, 'added client ip_map condition to "%s"',
+ name)
+ checks.client_ip_map = {
+ check = gen_check_closure(ips_map, check_map_setting),
+ extract = function(task)
+ local ip = task:get_client_ip()
+ if ip and ip:is_valid() then return ip end
+ return nil
+ end,
+ }
+ end
+ end
+
+ if elt.from then
+ local from_condition = process_email_condition(elt.from)
if from_condition then
lua_util.debugm(N, rspamd_config, 'added from condition to "%s": %s',
@@ -633,8 +680,9 @@ local function process_settings_table(tbl, allow_ids, is_static)
}
end
end
- if elt['rcpt'] then
- local rcpt_condition = process_email_condition(elt['rcpt'])
+
+ if elt.rcpt then
+ local rcpt_condition = process_email_condition(elt.rcpt)
if rcpt_condition then
lua_util.debugm(N, rspamd_config, 'added rcpt condition to "%s": %s',
name, rcpt_condition)
@@ -647,8 +695,9 @@ local function process_settings_table(tbl, allow_ids, is_static)
}
end
end
- if elt['from_mime'] then
- local from_mime_condition = process_email_condition(elt['from_mime'])
+
+ if elt.from_mime then
+ local from_mime_condition = process_email_condition(elt.from_mime)
if from_mime_condition then
lua_util.debugm(N, rspamd_config, 'added from_mime condition to "%s": %s',
@@ -662,8 +711,9 @@ local function process_settings_table(tbl, allow_ids, is_static)
}
end
end
- if elt['rcpt_mime'] then
- local rcpt_mime_condition = process_email_condition(elt['rcpt'])
+
+ if elt.rcpt_mime then
+ local rcpt_mime_condition = process_email_condition(elt.rcpt_mime)
if rcpt_mime_condition then
lua_util.debugm(N, rspamd_config, 'added rcpt mime condition to "%s": %s',
name, rcpt_mime_condition)
@@ -676,8 +726,9 @@ local function process_settings_table(tbl, allow_ids, is_static)
}
end
end
- if elt['user'] then
- local user_condition = process_email_condition(elt['user'])
+
+ if elt.user then
+ local user_condition = process_email_condition(elt.user)
if user_condition then
lua_util.debugm(N, rspamd_config, 'added user condition to "%s": %s',
name, user_condition)
@@ -707,8 +758,9 @@ local function process_settings_table(tbl, allow_ids, is_static)
}
end
end
- if elt['hostname'] then
- local hostname_condition = process_string_condition(elt['hostname'])
+
+ if elt.hostname then
+ local hostname_condition = process_string_condition(elt.hostname)
if hostname_condition then
lua_util.debugm(N, rspamd_config, 'added hostname condition to "%s": %s',
name, hostname_condition)
@@ -721,7 +773,8 @@ local function process_settings_table(tbl, allow_ids, is_static)
}
end
end
- if elt['authenticated'] then
+
+ if elt.authenticated then
lua_util.debugm(N, rspamd_config, 'added authenticated condition to "%s"',
name)
checks.authenticated = {
@@ -731,6 +784,7 @@ local function process_settings_table(tbl, allow_ids, is_static)
end
}
end
+
if elt['local'] then
lua_util.debugm(N, rspamd_config, 'added local condition to "%s"',
name)
@@ -785,7 +839,7 @@ local function process_settings_table(tbl, allow_ids, is_static)
end
return safe_key
- end
+ end
-- Headers are tricky:
-- We create an closure with extraction function depending on header name
-- We also inserts it into `checks` table as an atom in form header:<hname>
@@ -846,7 +900,7 @@ local function process_settings_table(tbl, allow_ids, is_static)
end
end)
- if elt['selector'] then
+ if elt.selector then
local sel = lua_selectors.create_selector_closure(rspamd_config, elt.selector,
elt.delimiter or "")
@@ -868,13 +922,13 @@ local function process_settings_table(tbl, allow_ids, is_static)
-- Special, special case!
local inverse = false
- if elt['inverse'] then
+ if elt.inverse then
lua_util.debugm(N, rspamd_config, 'added inverse condition to "%s"',
name)
inverse = true
end
- -- Killmeplease
+ -- Count checks and create Rspamd expression from a set of rules
local nchecks = 0
for _,_ in pairs(checks) do nchecks = nchecks + 1 end
More information about the Commits
mailing list