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 24KB

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