You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

lua_dkim_tools.lua 23KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738
  1. --[[
  2. Copyright (c) 2016, Andrew Lewis <nerf@judo.za.org>
  3. Copyright (c) 2022, Vsevolod Stakhov <vsevolod@rspamd.com>
  4. Licensed under the Apache License, Version 2.0 (the "License");
  5. you may not use this file except in compliance with the License.
  6. You may obtain a copy of the License at
  7. http://www.apache.org/licenses/LICENSE-2.0
  8. Unless required by applicable law or agreed to in writing, software
  9. distributed under the License is distributed on an "AS IS" BASIS,
  10. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  11. See the License for the specific language governing permissions and
  12. limitations under the License.
  13. ]]--
  14. local exports = {}
  15. local E = {}
  16. local lua_util = require "lua_util"
  17. local rspamd_util = require "rspamd_util"
  18. local logger = require "rspamd_logger"
  19. local fun = require "fun"
  20. local function check_violation(N, task, domain)
  21. -- Check for DKIM_REJECT
  22. local sym_check = 'R_DKIM_REJECT'
  23. if N == 'arc' then sym_check = 'ARC_REJECT' end
  24. if task:has_symbol(sym_check) then
  25. local sym = task:get_symbol(sym_check)[1]
  26. logger.infox(task, 'skip signing for %s: violation %s found: %s',
  27. domain, sym_check, sym.options)
  28. return false
  29. end
  30. return true
  31. end
  32. local function insert_or_update_prop(N, task, p, prop, origin, data)
  33. if #p == 0 then
  34. local k = {}
  35. k[prop] = data
  36. table.insert(p, k)
  37. lua_util.debugm(N, task, 'add %s "%s" using %s', prop, data, origin)
  38. else
  39. for _, k in ipairs(p) do
  40. if not k[prop] then
  41. k[prop] = data
  42. lua_util.debugm(N, task, 'set %s to "%s" using %s', prop, data, origin)
  43. end
  44. end
  45. end
  46. end
  47. local function get_mempool_selectors(N, task)
  48. local p = {}
  49. local key_var = "dkim_key"
  50. local selector_var = "dkim_selector"
  51. if N == "arc" then
  52. key_var = "arc_key"
  53. selector_var = "arc_selector"
  54. end
  55. p.key = task:get_mempool():get_variable(key_var)
  56. p.selector = task:get_mempool():get_variable(selector_var)
  57. if (not p.key or not p.selector) then
  58. return false, {}
  59. end
  60. lua_util.debugm(N, task, 'override selector and key to %s:%s', p.key, p.selector)
  61. return true, p
  62. end
  63. local function parse_dkim_http_headers(N, task, settings)
  64. -- Configure headers
  65. local headers = {
  66. sign_header = settings.http_sign_header or "PerformDkimSign",
  67. sign_on_reject_header = settings.http_sign_on_reject_header_header or 'SignOnAuthFailed',
  68. domain_header = settings.http_domain_header or 'DkimDomain',
  69. selector_header = settings.http_selector_header or 'DkimSelector',
  70. key_header = settings.http_key_header or 'DkimPrivateKey'
  71. }
  72. if task:get_request_header(headers.sign_header) then
  73. local domain = task:get_request_header(headers.domain_header)
  74. local selector = task:get_request_header(headers.selector_header)
  75. local key = task:get_request_header(headers.key_header)
  76. if not (domain and selector and key) then
  77. logger.errx(task, 'missing required headers to sign email')
  78. return false,{}
  79. end
  80. -- Now check if we need to check the existing auth
  81. local hdr = task:get_request_header(headers.sign_on_reject_header)
  82. if not hdr or tostring(hdr) == '0' or tostring(hdr) == 'false' then
  83. if not check_violation(N, task, domain, selector) then
  84. return false, {}
  85. end
  86. end
  87. local p = {}
  88. local k = {
  89. domain = tostring(domain),
  90. rawkey = tostring(key),
  91. selector = tostring(selector),
  92. }
  93. table.insert(p, k)
  94. return true, p
  95. end
  96. lua_util.debugm(N, task, 'no sign header %s', headers.sign_header)
  97. return false,{}
  98. end
  99. local function prepare_dkim_signing(N, task, settings)
  100. local is_local, is_sign_networks, is_authed
  101. if settings.use_http_headers then
  102. local res,tbl = parse_dkim_http_headers(N, task, settings)
  103. if not res then
  104. if not settings.allow_headers_fallback then
  105. return res,{}
  106. else
  107. lua_util.debugm(N, task, 'failed to read http headers, fallback to normal schema')
  108. end
  109. else
  110. return res,tbl
  111. end
  112. end
  113. if settings.sign_condition and type(settings.sign_condition) == 'function' then
  114. -- Use sign condition only
  115. local ret = settings.sign_condition(task)
  116. if not ret then
  117. return false,{}
  118. end
  119. if ret[1] then
  120. return true,ret
  121. else
  122. return true,{ret}
  123. end
  124. end
  125. local auser = task:get_user()
  126. local ip = task:get_from_ip()
  127. if ip and ip:is_local() then
  128. is_local = true
  129. end
  130. local has_pre_result = task:has_pre_result()
  131. if has_pre_result then
  132. local metric_action = task:get_metric_action()
  133. if metric_action == 'reject' or metric_action == 'drop' then
  134. -- No need to sign what we are already rejecting/dropping
  135. lua_util.debugm(N, task, 'task result is already %s, no need to sign', metric_action)
  136. return false,{}
  137. end
  138. if metric_action == 'soft reject' then
  139. -- Same here, we are going to delay an email, signing is just a waste of time
  140. lua_util.debugm(N, task, 'task result is %s, skip signing', metric_action)
  141. return false,{}
  142. end
  143. -- For spam actions, there is no clear distinction
  144. if metric_action ~= 'no action' and type(settings.skip_spam_sign) == 'boolean' and settings.skip_spam_sign then
  145. lua_util.debugm(N, task, 'task result is %s, no need to sign', metric_action)
  146. return false,{}
  147. end
  148. end
  149. if settings.sign_authenticated and auser then
  150. lua_util.debugm(N, task, 'user is authenticated')
  151. is_authed = true
  152. elseif (settings.sign_networks and settings.sign_networks:get_key(ip)) then
  153. is_sign_networks = true
  154. lua_util.debugm(N, task, 'mail is from address in sign_networks')
  155. elseif settings.sign_local and is_local then
  156. lua_util.debugm(N, task, 'mail is from local address')
  157. elseif settings.sign_inbound and not is_local and not auser then
  158. lua_util.debugm(N, task, 'mail was sent to us')
  159. else
  160. lua_util.debugm(N, task, 'mail is ineligible for signing')
  161. return false,{}
  162. end
  163. local efrom = task:get_from('smtp')
  164. local empty_envelope = false
  165. if #(((efrom or E)[1] or E).addr or '') == 0 then
  166. if not settings.allow_envfrom_empty then
  167. lua_util.debugm(N, task, 'empty envelope from not allowed')
  168. return false,{}
  169. else
  170. empty_envelope = true
  171. end
  172. end
  173. local hfrom = task:get_from('mime')
  174. if not settings.allow_hdrfrom_multiple and (hfrom or E)[2] then
  175. lua_util.debugm(N, task, 'multiple header from not allowed')
  176. return false,{}
  177. end
  178. local eto = task:get_recipients(0)
  179. local dkim_domain
  180. local hdom = ((hfrom or E)[1] or E).domain
  181. local edom = ((efrom or E)[1] or E).domain
  182. local tdom = ((eto or E)[1] or E).domain
  183. local udom = string.match(auser or '', '.*@(.*)')
  184. local function get_dkim_domain(dtype)
  185. if settings[dtype] == 'header' then
  186. return hdom
  187. elseif settings[dtype] == 'envelope' then
  188. return edom
  189. elseif settings[dtype] == 'auth' then
  190. return udom
  191. elseif settings[dtype] == 'recipient' then
  192. return tdom
  193. else
  194. return settings[dtype]:lower()
  195. end
  196. end
  197. local function is_skip_sign()
  198. return not (settings.sign_networks and is_sign_networks) and
  199. not (settings.sign_authenticated and is_authed) and
  200. not (settings.sign_local and is_local)
  201. end
  202. if hdom then
  203. hdom = hdom:lower()
  204. end
  205. if edom then
  206. edom = edom:lower()
  207. end
  208. if udom then
  209. udom = udom:lower()
  210. end
  211. if tdom then
  212. tdom = tdom:lower()
  213. end
  214. if settings.signing_table and (settings.key_table or settings.use_vault) then
  215. -- OpenDKIM style
  216. if is_skip_sign() then
  217. lua_util.debugm(N, task,
  218. 'skip signing: is_sign_network: %s, is_authed: %s, is_local: %s',
  219. is_sign_networks, is_authed, is_local)
  220. return false,{}
  221. end
  222. if not hfrom or not hfrom[1] or not hfrom[1].addr then
  223. lua_util.debugm(N, task,
  224. 'signing_table: cannot get data when no header from is presented')
  225. return false,{}
  226. end
  227. local sign_entry = settings.signing_table:get_key(hfrom[1].addr)
  228. if sign_entry then
  229. -- Check opendkim style entries
  230. lua_util.debugm(N, task,
  231. 'signing_table: found entry for %s: %s', hfrom[1].addr, sign_entry)
  232. if sign_entry == '%' then
  233. sign_entry = hdom
  234. end
  235. if settings.key_table then
  236. -- Now search in key table
  237. local key_entry = settings.key_table:get_key(sign_entry)
  238. if key_entry then
  239. local parts = lua_util.str_split(key_entry, ':')
  240. if #parts == 2 then
  241. -- domain + key
  242. local selector = settings.selector
  243. if not selector then
  244. logger.errx(task, 'no selector defined for sign_entry %s, key_entry %s',
  245. sign_entry, key_entry)
  246. return false,{}
  247. end
  248. local res = {
  249. selector = selector,
  250. domain = parts[1]:gsub('%%', hdom)
  251. }
  252. local st = parts[2]:sub(1, 2)
  253. if st:sub(1, 1) == '/' or st == './' or st == '..' then
  254. res.key = parts[2]:gsub('%%', hdom)
  255. lua_util.debugm(N, task, 'perform dkim signing for %s, selector=%s, domain=%s, key file=%s',
  256. hdom, selector, res.domain, res.key)
  257. else
  258. res.rawkey = parts[2] -- No sanity check here
  259. lua_util.debugm(N, task, 'perform dkim signing for %s, selector=%s, domain=%s, raw key used',
  260. hdom, selector, res.domain)
  261. end
  262. return true,{res}
  263. elseif #parts == 3 then
  264. -- domain, selector, key
  265. local selector = parts[2]
  266. local res = {
  267. selector = selector,
  268. domain = parts[1]:gsub('%%', hdom)
  269. }
  270. local st = parts[3]:sub(1, 2)
  271. if st:sub(1, 1) == '/' or st == './' or st == '..' then
  272. res.key = parts[3]:gsub('%%', hdom)
  273. lua_util.debugm(N, task, 'perform dkim signing for %s, selector=%s, domain=%s, key file=%s',
  274. hdom, selector, res.domain, res.key)
  275. else
  276. res.rawkey = parts[3] -- No sanity check here
  277. lua_util.debugm(N, task, 'perform dkim signing for %s, selector=%s, domain=%s, raw key used',
  278. hdom, selector, res.domain)
  279. end
  280. return true,{res}
  281. else
  282. logger.errx(task, 'invalid key entry for sign entry %s: %s; when signing %s domain',
  283. sign_entry, key_entry, hdom)
  284. return false,{}
  285. end
  286. elseif settings.use_vault then
  287. -- Sign table is presented, the rest is covered by vault
  288. lua_util.debugm(N, task, 'check vault for %s, by sign entry %s, key entry is missing',
  289. hdom, sign_entry)
  290. return true, {
  291. domain = sign_entry,
  292. vault = true
  293. }
  294. else
  295. logger.errx(task, 'missing key entry for sign entry %s; when signing %s domain',
  296. sign_entry, hdom)
  297. return false,{}
  298. end
  299. else
  300. logger.errx(task, 'cannot get key entry for signing entry %s, when signing %s domain',
  301. sign_entry, hdom)
  302. return false,{}
  303. end
  304. else
  305. lua_util.debugm(N, task,
  306. 'signing_table: no entry for %s', hfrom[1].addr)
  307. return false,{}
  308. end
  309. else
  310. if settings.use_domain_sign_networks and is_sign_networks then
  311. dkim_domain = get_dkim_domain('use_domain_sign_networks')
  312. lua_util.debugm(N, task,
  313. 'sign_networks: use domain(%s) for signature: %s',
  314. settings.use_domain_sign_networks, dkim_domain)
  315. elseif settings.use_domain_sign_local and is_local then
  316. dkim_domain = get_dkim_domain('use_domain_sign_local')
  317. lua_util.debugm(N, task, 'local: use domain(%s) for signature: %s',
  318. settings.use_domain_sign_local, dkim_domain)
  319. elseif settings.use_domain_sign_inbound and not is_local and not auser then
  320. dkim_domain = get_dkim_domain('use_domain_sign_inbound')
  321. lua_util.debugm(N, task, 'inbound: use domain(%s) for signature: %s',
  322. settings.use_domain_sign_inbound, dkim_domain)
  323. elseif settings.use_domain_custom then
  324. if type(settings.use_domain_custom) == 'string' then
  325. -- Load custom function
  326. local loadstring = loadstring or load
  327. local ret, res_or_err = pcall(loadstring(settings.use_domain_custom))
  328. if ret then
  329. if type(res_or_err) == 'function' then
  330. settings.use_domain_custom = res_or_err
  331. dkim_domain = settings.use_domain_custom(task)
  332. lua_util.debugm(N, task, 'use custom domain for signing: %s',
  333. dkim_domain)
  334. else
  335. logger.errx(task, 'cannot load dkim domain custom script: invalid type: %s, expected function',
  336. type(res_or_err))
  337. settings.use_domain_custom = nil
  338. end
  339. else
  340. logger.errx(task, 'cannot load dkim domain custom script: %s', res_or_err)
  341. settings.use_domain_custom = nil
  342. end
  343. else
  344. dkim_domain = settings.use_domain_custom(task)
  345. lua_util.debugm(N, task, 'use custom domain for signing: %s',
  346. dkim_domain)
  347. end
  348. else
  349. dkim_domain = get_dkim_domain('use_domain')
  350. lua_util.debugm(N, task, 'use domain(%s) for signature: %s',
  351. settings.use_domain, dkim_domain)
  352. end
  353. end
  354. if not dkim_domain then
  355. lua_util.debugm(N, task, 'could not extract dkim domain')
  356. return false,{}
  357. end
  358. if settings.use_esld then
  359. dkim_domain = rspamd_util.get_tld(dkim_domain)
  360. if hdom then
  361. hdom = rspamd_util.get_tld(hdom)
  362. end
  363. if edom then
  364. edom = rspamd_util.get_tld(edom)
  365. end
  366. end
  367. lua_util.debugm(N, task, 'final DKIM domain: %s', dkim_domain)
  368. -- Sanity checks
  369. if edom and hdom and not settings.allow_hdrfrom_mismatch and hdom ~= edom then
  370. if settings.allow_hdrfrom_mismatch_local and is_local then
  371. lua_util.debugm(N, task, 'domain mismatch allowed for local IP: %1 != %2', hdom, edom)
  372. elseif settings.allow_hdrfrom_mismatch_sign_networks and is_sign_networks then
  373. lua_util.debugm(N, task, 'domain mismatch allowed for sign_networks: %1 != %2', hdom, edom)
  374. else
  375. if empty_envelope and hdom then
  376. lua_util.debugm(N, task, 'domain mismatch allowed for empty envelope: %1 != %2', hdom, edom)
  377. else
  378. lua_util.debugm(N, task, 'domain mismatch not allowed: %1 != %2', hdom, edom)
  379. return false,{}
  380. end
  381. end
  382. end
  383. if auser and not settings.allow_username_mismatch then
  384. if not udom then
  385. lua_util.debugm(N, task, 'couldnt find domain in username')
  386. return false,{}
  387. end
  388. if settings.use_esld then
  389. udom = rspamd_util.get_tld(udom)
  390. end
  391. if udom ~= dkim_domain then
  392. lua_util.debugm(N, task, 'user domain mismatch')
  393. return false,{}
  394. end
  395. end
  396. local p = {}
  397. if settings.use_vault then
  398. if settings.vault_domains then
  399. if settings.vault_domains:get_key(dkim_domain) then
  400. return true, {
  401. domain = dkim_domain,
  402. vault = true,
  403. }
  404. else
  405. lua_util.debugm(N, task, 'domain %s is not designated for vault',
  406. dkim_domain)
  407. return false,{}
  408. end
  409. else
  410. -- TODO: try every domain in the vault
  411. return true, {
  412. domain = dkim_domain,
  413. vault = true,
  414. }
  415. end
  416. end
  417. if settings.domain[dkim_domain] then
  418. -- support old style selector/paths
  419. if settings.domain[dkim_domain].selector or
  420. settings.domain[dkim_domain].path then
  421. local k = {}
  422. k.selector = settings.domain[dkim_domain].selector
  423. k.key = settings.domain[dkim_domain].path
  424. table.insert(p, k)
  425. end
  426. for _, s in ipairs((settings.domain[dkim_domain].selectors or {})) do
  427. lua_util.debugm(N, task, 'adding selector: %1', s)
  428. local k = {}
  429. k.selector = s.selector
  430. k.key = s.path
  431. table.insert(p, k)
  432. end
  433. end
  434. if #p == 0 then
  435. local ret, k = get_mempool_selectors(N, task)
  436. if ret then
  437. table.insert(p, k)
  438. lua_util.debugm(N, task, 'using mempool selector %s with key %s',
  439. k.selector, k.key)
  440. end
  441. end
  442. if settings.selector_map then
  443. local data = settings.selector_map:get_key(dkim_domain)
  444. if data then
  445. insert_or_update_prop(N, task, p, 'selector', 'selector_map', data)
  446. else
  447. lua_util.debugm(N, task, 'no selector in map for %s', dkim_domain)
  448. end
  449. end
  450. if settings.path_map then
  451. local data = settings.path_map:get_key(dkim_domain)
  452. if data then
  453. insert_or_update_prop(N, task, p, 'key', 'path_map', data)
  454. else
  455. lua_util.debugm(N, task, 'no key in map for %s', dkim_domain)
  456. end
  457. end
  458. if #p == 0 and not settings.try_fallback then
  459. lua_util.debugm(N, task, 'dkim unconfigured and fallback disabled')
  460. return false,{}
  461. end
  462. if not settings.use_redis then
  463. insert_or_update_prop(N, task, p, 'key',
  464. 'default path', settings.path)
  465. end
  466. insert_or_update_prop(N, task, p, 'selector',
  467. 'default selector', settings.selector)
  468. if settings.check_violation then
  469. if not check_violation(N, task, p.domain) then
  470. return false,{}
  471. end
  472. end
  473. insert_or_update_prop(N, task, p, 'domain', 'dkim_domain',
  474. dkim_domain)
  475. return true,p
  476. end
  477. exports.prepare_dkim_signing = prepare_dkim_signing
  478. exports.sign_using_redis = function(N, task, settings, selectors, sign_func, err_func)
  479. local lua_redis = require "lua_redis"
  480. local function try_redis_key(selector, p)
  481. p.key = nil
  482. p.selector = selector
  483. local rk = string.format('%s.%s', p.selector, p.domain)
  484. local function redis_key_cb(err, data)
  485. if err then
  486. err_func(string.format("cannot make request to load DKIM key for %s: %s",
  487. rk, err))
  488. elseif type(data) ~= 'string' then
  489. lua_util.debugm(N, task, "missing DKIM key for %s", rk)
  490. else
  491. p.rawkey = data
  492. lua_util.debugm(N, task, 'found and parsed key for %s:%s in Redis',
  493. p.domain, p.selector)
  494. sign_func(task, p)
  495. end
  496. end
  497. local rret = lua_redis.redis_make_request(task,
  498. settings.redis_params, -- connect params
  499. rk, -- hash key
  500. false, -- is write
  501. redis_key_cb, --callback
  502. 'HGET', -- command
  503. {settings.key_prefix, rk} -- arguments
  504. )
  505. if not rret then
  506. err_func(task,
  507. string.format( "cannot make request to load DKIM key for %s", rk))
  508. end
  509. end
  510. for _, p in ipairs(selectors) do
  511. if settings.selector_prefix then
  512. logger.infox(task, "using selector prefix '%s' for domain '%s'",
  513. settings.selector_prefix, p.domain);
  514. local function redis_selector_cb(err, data)
  515. if err or type(data) ~= 'string' then
  516. err_func(task, string.format("cannot make request to load DKIM selector for domain %s: %s",
  517. p.domain, err))
  518. else
  519. try_redis_key(data, p)
  520. end
  521. end
  522. local rret = lua_redis.redis_make_request(task,
  523. settings.redis_params, -- connect params
  524. p.domain, -- hash key
  525. false, -- is write
  526. redis_selector_cb, --callback
  527. 'HGET', -- command
  528. {settings.selector_prefix, p.domain} -- arguments
  529. )
  530. if not rret then
  531. err_func(task, string.format("cannot make Redis request to load DKIM selector for domain %s",
  532. p.domain))
  533. end
  534. else
  535. try_redis_key(p.selector, p)
  536. end
  537. end
  538. end
  539. exports.sign_using_vault = function(N, task, settings, selectors, sign_func, err_func)
  540. local http = require "rspamd_http"
  541. local ucl = require "ucl"
  542. local full_url = string.format('%s/v1/%s/%s',
  543. settings.vault_url, settings.vault_path or 'dkim', selectors.domain)
  544. local function vault_callback(err, code, body, _)
  545. if code ~= 200 then
  546. err_func(task, string.format('cannot request data from the vault url: %s; %s (%s)',
  547. full_url, err, body))
  548. else
  549. local parser = ucl.parser()
  550. local res,parser_err = parser:parse_string(body)
  551. if not res then
  552. err_func(task, string.format('vault reply for %s (data=%s) cannot be parsed: %s',
  553. full_url, body, parser_err))
  554. else
  555. local obj = parser:get_object()
  556. if not obj or not obj.data then
  557. err_func(task, string.format('vault reply for %s (data=%s) is invalid, no data',
  558. full_url, body))
  559. else
  560. local elts = obj.data.selectors or {}
  561. -- Filter selectors by time/sanity
  562. local function is_selector_valid(p)
  563. if not p.key or not p.selector then
  564. return false
  565. end
  566. if p.valid_start then
  567. -- Check start time
  568. if rspamd_util.get_time() < tonumber(p.valid_start) then
  569. return false
  570. end
  571. end
  572. if p.valid_end then
  573. if rspamd_util.get_time() >= tonumber(p.valid_end) then
  574. return false
  575. end
  576. end
  577. return true
  578. end
  579. fun.each(function(p)
  580. local dkim_sign_data = {
  581. rawkey = p.key,
  582. selector = p.selector,
  583. domain = p.domain or selectors.domain,
  584. alg = p.alg,
  585. }
  586. lua_util.debugm(N, task, 'found and parsed key for %s:%s in Vault',
  587. dkim_sign_data.domain, dkim_sign_data.selector)
  588. sign_func(task, dkim_sign_data)
  589. end, fun.filter(is_selector_valid, elts))
  590. end
  591. end
  592. end
  593. end
  594. local ret = http.request{
  595. task = task,
  596. url = full_url,
  597. callback = vault_callback,
  598. timeout = settings.http_timeout or 5.0,
  599. no_ssl_verify = settings.no_ssl_verify,
  600. keepalive = true,
  601. headers = {
  602. ['X-Vault-Token'] = settings.vault_token,
  603. },
  604. }
  605. if not ret then
  606. err_func(task, string.format("cannot make HTTP request to load DKIM data domain %s",
  607. selectors.domain))
  608. end
  609. end
  610. exports.validate_signing_settings = function(settings)
  611. return settings.use_redis or
  612. settings.path or
  613. settings.domain or
  614. settings.path_map or
  615. settings.selector_map or
  616. settings.use_http_headers or
  617. (settings.signing_table and settings.key_table) or
  618. (settings.use_vault and settings.vault_url and settings.vault_token) or
  619. settings.sign_condition
  620. end
  621. exports.process_signing_settings = function(N, settings, opts)
  622. local lua_maps = require "lua_maps"
  623. -- Used to convert plain options to the maps
  624. local maps_opts = {
  625. sign_networks = {'radix', 'DKIM signing networks'},
  626. path_map = {'map', 'Paths to DKIM signing keys'},
  627. selector_map = {'map', 'DKIM selectors'},
  628. signing_table = {'glob', 'DKIM signing table'},
  629. key_table = {'glob', 'DKIM keys table'},
  630. vault_domains = {'glob', 'DKIM signing domains in vault'},
  631. whitelisted_signers_map = {'set', 'ARC trusted signers domains'}
  632. }
  633. for k,v in pairs(opts) do
  634. local maybe_map = maps_opts[k]
  635. if maybe_map then
  636. settings[k] = lua_maps.map_add_from_ucl(v, maybe_map[1], maybe_map[2])
  637. elseif k == 'sign_condition' then
  638. local ret,f = lua_util.callback_from_string(v)
  639. if ret then
  640. settings[k] = f
  641. else
  642. logger.errx(rspamd_config, 'cannot load sign condition %s: %s', v, f)
  643. end
  644. else
  645. settings[k] = v
  646. end
  647. end
  648. end
  649. return exports