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.

rspamd_update.lua 4.1KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161
  1. --[[
  2. Copyright (c) 2022, Vsevolod Stakhov <vsevolod@rspamd.com>
  3. Licensed under the Apache License, Version 2.0 (the "License");
  4. you may not use this file except in compliance with the License.
  5. You may obtain a copy of the License at
  6. http://www.apache.org/licenses/LICENSE-2.0
  7. Unless required by applicable law or agreed to in writing, software
  8. distributed under the License is distributed on an "AS IS" BASIS,
  9. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  10. See the License for the specific language governing permissions and
  11. limitations under the License.
  12. ]]--
  13. if confighelp then
  14. return
  15. end
  16. -- This plugin implements dynamic updates for rspamd
  17. local ucl = require "ucl"
  18. local fun = require "fun"
  19. local rspamd_logger = require "rspamd_logger"
  20. local rspamd_config = rspamd_config
  21. local hash = require "rspamd_cryptobox_hash"
  22. local lua_util = require "lua_util"
  23. local N = "rspamd_update"
  24. local rspamd_version = rspamd_version
  25. local maps = {}
  26. local allow_rules = false -- Deny for now
  27. local global_priority = 1 -- Default for local rules
  28. local function process_symbols(obj, priority)
  29. fun.each(function(sym, score)
  30. rspamd_config:set_metric_symbol({
  31. name = sym,
  32. score = score,
  33. priority = priority
  34. })
  35. end, obj)
  36. end
  37. local function process_actions(obj, priority)
  38. fun.each(function(act, score)
  39. rspamd_config:set_metric_action({
  40. action = act,
  41. score = score,
  42. priority = priority
  43. })
  44. end, obj)
  45. end
  46. local function process_rules(obj)
  47. fun.each(function(key, code)
  48. local f = load(code)
  49. if f then
  50. f()
  51. else
  52. rspamd_logger(rspamd_config, 'cannot load rules for %s', key)
  53. end
  54. end, obj)
  55. end
  56. local function check_version(obj)
  57. local ret = true
  58. if not obj then
  59. return false
  60. end
  61. if obj['min_version'] then
  62. if rspamd_version('cmp', obj['min_version']) > 0 then
  63. ret = false
  64. rspamd_logger.errx(rspamd_config, 'updates require at least %s version of rspamd',
  65. obj['min_version'])
  66. end
  67. end
  68. if obj['max_version'] then
  69. if rspamd_version('cmp', obj['max_version']) < 0 then
  70. ret = false
  71. rspamd_logger.errx(rspamd_config, 'updates require maximum %s version of rspamd',
  72. obj['max_version'])
  73. end
  74. end
  75. return ret
  76. end
  77. local function gen_callback()
  78. return function(data)
  79. local parser = ucl.parser()
  80. local res, err = parser:parse_string(data)
  81. if not res then
  82. rspamd_logger.warnx(rspamd_config, 'cannot parse updates map: ' .. err)
  83. else
  84. local h = hash.create()
  85. h:update(data)
  86. local obj = parser:get_object()
  87. if check_version(obj) then
  88. if obj['symbols'] then
  89. process_symbols(obj['symbols'], global_priority)
  90. end
  91. if obj['actions'] then
  92. process_actions(obj['actions'], global_priority)
  93. end
  94. if allow_rules and obj['rules'] then
  95. process_rules(obj['rules'])
  96. end
  97. rspamd_logger.infox(rspamd_config, 'loaded new rules with hash "%s"',
  98. h:hex())
  99. end
  100. end
  101. return res
  102. end
  103. end
  104. -- Configuration part
  105. local section = rspamd_config:get_all_opt("rspamd_update")
  106. if section and section.rules then
  107. local trusted_key
  108. if section.key then
  109. trusted_key = section.key
  110. end
  111. if type(section.rules) ~= 'table' then
  112. section.rules = { section.rules }
  113. end
  114. fun.each(function(elt)
  115. local map = rspamd_config:add_map(elt, "rspamd updates map", nil, "callback")
  116. if not map then
  117. rspamd_logger.errx(rspamd_config, 'cannot load updates from %1', elt)
  118. else
  119. map:set_callback(gen_callback(map))
  120. maps['elt'] = map
  121. end
  122. end, section.rules)
  123. fun.each(function(k, map)
  124. -- Check sanity for maps
  125. local proto = map:get_proto()
  126. if (proto == 'http' or proto == 'https') and not map:get_sign_key() then
  127. if trusted_key then
  128. map:set_sign_key(trusted_key)
  129. else
  130. rspamd_logger.warnx(rspamd_config, 'Map %s is loaded by HTTP and it is not signed', k)
  131. end
  132. end
  133. end, maps)
  134. else
  135. rspamd_logger.infox(rspamd_config, 'Module is unconfigured')
  136. lua_util.disable_module(N, "config")
  137. end