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.

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742
  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)
  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. return true, {
  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. return false, {}
  410. end
  411. else
  412. -- TODO: try every domain in the vault
  413. return true, {
  414. domain = dkim_domain,
  415. vault = true,
  416. }
  417. end
  418. end
  419. if settings.domain[dkim_domain] then
  420. -- support old style selector/paths
  421. if settings.domain[dkim_domain].selector or
  422. settings.domain[dkim_domain].path then
  423. local k = {}
  424. k.selector = settings.domain[dkim_domain].selector
  425. k.key = settings.domain[dkim_domain].path
  426. table.insert(p, k)
  427. end
  428. for _, s in ipairs((settings.domain[dkim_domain].selectors or {})) do
  429. lua_util.debugm(N, task, 'adding selector: %1', s)
  430. local k = {}
  431. k.selector = s.selector
  432. k.key = s.path
  433. table.insert(p, k)
  434. end
  435. end
  436. if #p == 0 then
  437. local ret, k = get_mempool_selectors(N, task)
  438. if ret then
  439. table.insert(p, k)
  440. lua_util.debugm(N, task, 'using mempool selector %s with key %s',
  441. k.selector, k.key)
  442. end
  443. end
  444. if settings.selector_map then
  445. local data = settings.selector_map:get_key(dkim_domain)
  446. if data then
  447. insert_or_update_prop(N, task, p, 'selector', 'selector_map', data)
  448. else
  449. lua_util.debugm(N, task, 'no selector in map for %s', dkim_domain)
  450. end
  451. end
  452. if settings.path_map then
  453. local data = settings.path_map:get_key(dkim_domain)
  454. if data then
  455. insert_or_update_prop(N, task, p, 'key', 'path_map', data)
  456. else
  457. lua_util.debugm(N, task, 'no key in map for %s', dkim_domain)
  458. end
  459. end
  460. if #p == 0 and not settings.try_fallback then
  461. lua_util.debugm(N, task, 'dkim unconfigured and fallback disabled')
  462. return false, {}
  463. end
  464. if not settings.use_redis then
  465. insert_or_update_prop(N, task, p, 'key',
  466. 'default path', settings.path)
  467. end
  468. insert_or_update_prop(N, task, p, 'selector',
  469. 'default selector', settings.selector)
  470. if settings.check_violation then
  471. if not check_violation(N, task, p.domain) then
  472. return false, {}
  473. end
  474. end
  475. insert_or_update_prop(N, task, p, 'domain', 'dkim_domain',
  476. dkim_domain)
  477. return true, p
  478. end
  479. exports.prepare_dkim_signing = prepare_dkim_signing
  480. exports.sign_using_redis = function(N, task, settings, selectors, sign_func, err_func)
  481. local lua_redis = require "lua_redis"
  482. local function try_redis_key(selector, p)
  483. p.key = nil
  484. p.selector = selector
  485. local rk = string.format('%s.%s', p.selector, p.domain)
  486. local function redis_key_cb(err, data)
  487. if err then
  488. err_func(string.format("cannot make request to load DKIM key for %s: %s",
  489. rk, err))
  490. elseif type(data) ~= 'string' then
  491. lua_util.debugm(N, task, "missing DKIM key for %s", rk)
  492. else
  493. p.rawkey = data
  494. lua_util.debugm(N, task, 'found and parsed key for %s:%s in Redis',
  495. p.domain, p.selector)
  496. sign_func(task, p)
  497. end
  498. end
  499. local rret = lua_redis.redis_make_request(task,
  500. settings.redis_params, -- connect params
  501. rk, -- hash key
  502. false, -- is write
  503. redis_key_cb, --callback
  504. 'HGET', -- command
  505. { settings.key_prefix, rk } -- arguments
  506. )
  507. if not rret then
  508. err_func(task,
  509. string.format("cannot make request to load DKIM key for %s", rk))
  510. end
  511. end
  512. for _, p in ipairs(selectors) do
  513. if settings.selector_prefix then
  514. logger.infox(task, "using selector prefix '%s' for domain '%s'",
  515. settings.selector_prefix, p.domain);
  516. local function redis_selector_cb(err, data)
  517. if err or type(data) ~= 'string' then
  518. err_func(task, string.format("cannot make request to load DKIM selector for domain %s: %s",
  519. p.domain, err))
  520. else
  521. try_redis_key(data, p)
  522. end
  523. end
  524. local rret = lua_redis.redis_make_request(task,
  525. settings.redis_params, -- connect params
  526. p.domain, -- hash key
  527. false, -- is write
  528. redis_selector_cb, --callback
  529. 'HGET', -- command
  530. { settings.selector_prefix, p.domain } -- arguments
  531. )
  532. if not rret then
  533. err_func(task, string.format("cannot make Redis request to load DKIM selector for domain %s",
  534. p.domain))
  535. end
  536. else
  537. try_redis_key(p.selector, p)
  538. end
  539. end
  540. end
  541. exports.sign_using_vault = function(N, task, settings, selectors, sign_func, err_func)
  542. local http = require "rspamd_http"
  543. local ucl = require "ucl"
  544. local full_url = string.format('%s/v1/%s/%s',
  545. settings.vault_url, settings.vault_path or 'dkim', selectors.domain)
  546. local upstream_list = lua_util.http_upstreams_by_url(rspamd_config:get_mempool(), settings.vault_url)
  547. local function vault_callback(err, code, body, _)
  548. if code ~= 200 then
  549. err_func(task, string.format('cannot request data from the vault url: %s; %s (%s)',
  550. full_url, err, body))
  551. else
  552. local parser = ucl.parser()
  553. local res, parser_err = parser:parse_string(body)
  554. if not res then
  555. err_func(task, string.format('vault reply for %s (data=%s) cannot be parsed: %s',
  556. full_url, body, parser_err))
  557. else
  558. local obj = parser:get_object()
  559. if not obj or not obj.data then
  560. err_func(task, string.format('vault reply for %s (data=%s) is invalid, no data',
  561. full_url, body))
  562. else
  563. local elts = obj.data.selectors or {}
  564. -- Filter selectors by time/sanity
  565. local function is_selector_valid(p)
  566. if not p.key or not p.selector then
  567. return false
  568. end
  569. if p.valid_start then
  570. -- Check start time
  571. if rspamd_util.get_time() < tonumber(p.valid_start) then
  572. return false
  573. end
  574. end
  575. if p.valid_end then
  576. if rspamd_util.get_time() >= tonumber(p.valid_end) then
  577. return false
  578. end
  579. end
  580. return true
  581. end
  582. fun.each(function(p)
  583. local dkim_sign_data = {
  584. rawkey = p.key,
  585. selector = p.selector,
  586. domain = p.domain or selectors.domain,
  587. alg = p.alg,
  588. }
  589. lua_util.debugm(N, task, 'found and parsed key for %s:%s in Vault',
  590. dkim_sign_data.domain, dkim_sign_data.selector)
  591. sign_func(task, dkim_sign_data)
  592. end, fun.filter(is_selector_valid, elts))
  593. end
  594. end
  595. end
  596. end
  597. local ret = http.request {
  598. task = task,
  599. url = full_url,
  600. callback = vault_callback,
  601. timeout = settings.http_timeout or 5.0,
  602. no_ssl_verify = settings.no_ssl_verify,
  603. keepalive = true,
  604. upstream = upstream_list and upstream_list:get_upstream_round_robin() or nil,
  605. headers = {
  606. ['X-Vault-Token'] = settings.vault_token,
  607. },
  608. }
  609. if not ret then
  610. err_func(task, string.format("cannot make HTTP request to load DKIM data domain %s",
  611. selectors.domain))
  612. end
  613. end
  614. exports.validate_signing_settings = function(settings)
  615. return settings.use_redis or
  616. settings.path or
  617. settings.domain or
  618. settings.path_map or
  619. settings.selector_map or
  620. settings.use_http_headers or
  621. (settings.signing_table and settings.key_table) or
  622. (settings.use_vault and settings.vault_url and settings.vault_token) or
  623. settings.sign_condition
  624. end
  625. exports.process_signing_settings = function(N, settings, opts)
  626. local lua_maps = require "lua_maps"
  627. -- Used to convert plain options to the maps
  628. local maps_opts = {
  629. sign_networks = { 'radix', 'DKIM signing networks' },
  630. path_map = { 'map', 'Paths to DKIM signing keys' },
  631. selector_map = { 'map', 'DKIM selectors' },
  632. signing_table = { 'glob', 'DKIM signing table' },
  633. key_table = { 'glob', 'DKIM keys table' },
  634. vault_domains = { 'glob', 'DKIM signing domains in vault' },
  635. whitelisted_signers_map = { 'set', 'ARC trusted signers domains' }
  636. }
  637. for k, v in pairs(opts) do
  638. local maybe_map = maps_opts[k]
  639. if maybe_map then
  640. settings[k] = lua_maps.map_add_from_ucl(v, maybe_map[1], maybe_map[2])
  641. elseif k == 'sign_condition' then
  642. local ret, f = lua_util.callback_from_string(v)
  643. if ret then
  644. settings[k] = f
  645. else
  646. logger.errx(rspamd_config, 'cannot load sign condition %s: %s', v, f)
  647. end
  648. else
  649. settings[k] = v
  650. end
  651. end
  652. end
  653. return exports