]> git.ipfire.org Git - thirdparty/rspamd.git/commitdiff
[Feature] Add confighelp documentation for RBL module
authorVsevolod Stakhov <vsevolod@rspamd.com>
Thu, 25 Dec 2025 15:26:07 +0000 (15:26 +0000)
committerVsevolod Stakhov <vsevolod@rspamd.com>
Thu, 25 Dec 2025 15:26:07 +0000 (15:26 +0000)
Add schema documentation for all RBL rule and plugin options.
Register schemas with plugin registry for rspamadm confighelp.

lualib/plugins/rbl.lua

index f0eb48503c9996b78cfee459476854a8501e2a02..277f192f74f98d32a353971a68cac627f2112144 100644 (file)
@@ -17,6 +17,7 @@ limitations under the License.
 local T = require "lua_shape.core"
 local lua_maps = require "lua_maps"
 local lua_util = require "lua_util"
+local PluginSchema = require "lua_shape.plugin_schema"
 
 -- Common RBL plugin definitions
 
@@ -90,76 +91,136 @@ local return_bits_schema = T.table({}, {
 }):doc({ summary = "Map of symbol names to bit numbers" })
 
 local rule_schema_tbl = {
-  content_urls = T.boolean():optional(),
-  disable_monitoring = T.boolean():optional(),
-  disabled = T.boolean():optional(),
-  dkim = T.boolean():optional(),
-  dkim_domainonly = T.boolean():optional(),
-  dkim_match_from = T.boolean():optional(),
-  emails = T.boolean():optional(),
-  emails_delimiter = T.string():optional(),
-  emails_domainonly = T.boolean():optional(),
-  enabled = T.boolean():optional(),
-  exclude_local = T.boolean():optional(),
-  exclude_users = T.boolean():optional(),
-  from = T.boolean():optional(),
-  hash = T.enum({ "sha1", "sha256", "sha384", "sha512", "md5", "blake2" }):optional(),
-  hash_format = T.enum({ "hex", "base32", "base64" }):optional(),
-  hash_len = T.one_of({ T.integer(), T.transform(T.string(), tonumber) }):optional(),
-  helo = T.boolean():optional(),
-  ignore_default = T.boolean():optional(),
-  ignore_defaults = T.boolean():optional(),
-  ignore_url_whitelist = T.boolean():optional(),
-  ignore_whitelist = T.boolean():optional(),
-  ignore_whitelists = T.boolean():optional(),
-  images = T.boolean():optional(),
-  ipv4 = T.boolean():optional(),
-  ipv6 = T.boolean():optional(),
-  is_whitelist = T.boolean():optional(),
-  local_exclude_ip_map = T.string():optional(),
-  monitored_address = T.string():optional(),
-  no_ip = T.boolean():optional(),
-  process_script = T.string():optional(),
-  random_monitored = T.boolean():optional(),
-  rbl = T.string(),
-  rdns = T.boolean():optional(),
-  received = T.boolean():optional(),
-  received_flags = T.array(T.string()):optional(),
-  received_max_pos = T.number():optional(),
-  received_min_pos = T.number():optional(),
-  received_nflags = T.array(T.string()):optional(),
-  replyto = T.boolean():optional(),
-  requests_limit = T.one_of({ T.integer(), T.transform(T.string(), tonumber) }):optional(),
+  content_urls = T.boolean():optional()
+      :doc({ summary = "Check URLs found in message body content" }),
+  disable_monitoring = T.boolean():optional()
+      :doc({ summary = "Disable automatic monitoring/health checks for this RBL" }),
+  disabled = T.boolean():optional()
+      :doc({ summary = "Completely disable this RBL rule" }),
+  dkim = T.boolean():optional()
+      :doc({ summary = "Check DKIM signature domains against this RBL" }),
+  dkim_domainonly = T.boolean():optional()
+      :doc({ summary = "Use only the domain part of DKIM signatures for lookups" }),
+  dkim_match_from = T.boolean():optional()
+      :doc({ summary = "Only check DKIM domains that match the From header domain" }),
+  emails = T.boolean():optional()
+      :doc({ summary = "Check email addresses found in message against this RBL" }),
+  emails_delimiter = T.string():optional()
+      :doc({ summary = "Delimiter to use when constructing email-based RBL queries" }),
+  emails_domainonly = T.boolean():optional()
+      :doc({ summary = "Use only the domain part of email addresses for lookups" }),
+  enabled = T.boolean():optional()
+      :doc({ summary = "Enable this RBL rule" }),
+  exclude_local = T.boolean():optional()
+      :doc({ summary = "Skip RBL checks for locally originated messages" }),
+  exclude_users = T.boolean():optional()
+      :doc({ summary = "Skip RBL checks for authenticated users" }),
+  from = T.boolean():optional()
+      :doc({ summary = "Check the sending IP address (from SMTP envelope) against this RBL" }),
+  hash = T.enum({ "sha1", "sha256", "sha384", "sha512", "md5", "blake2" }):optional()
+      :doc({ summary = "Hash algorithm to use for RBL queries (for hash-based RBLs)" }),
+  hash_format = T.enum({ "hex", "base32", "base64" }):optional()
+      :doc({ summary = "Encoding format for hashed RBL queries" }),
+  hash_len = T.one_of({ T.integer(), T.transform(T.string(), tonumber) }):optional()
+      :doc({ summary = "Truncate hash to this many characters" }),
+  helo = T.boolean():optional()
+      :doc({ summary = "Check the HELO/EHLO hostname against this RBL" }),
+  ignore_default = T.boolean():optional()
+      :doc({ summary = "Ignore default settings for this rule (alias for ignore_defaults)" }),
+  ignore_defaults = T.boolean():optional()
+      :doc({ summary = "Ignore default settings for this rule" }),
+  ignore_url_whitelist = T.boolean():optional()
+      :doc({ summary = "Do not apply URL whitelist to this RBL" }),
+  ignore_whitelist = T.boolean():optional()
+      :doc({ summary = "Do not apply global whitelist to this RBL" }),
+  ignore_whitelists = T.boolean():optional()
+      :doc({ summary = "Do not apply any whitelists to this RBL" }),
+  images = T.boolean():optional()
+      :doc({ summary = "Check URLs of embedded images against this RBL" }),
+  ipv4 = T.boolean():optional()
+      :doc({ summary = "Enable lookups for IPv4 addresses" }),
+  ipv6 = T.boolean():optional()
+      :doc({ summary = "Enable lookups for IPv6 addresses" }),
+  is_whitelist = T.boolean():optional()
+      :doc({ summary = "Treat this RBL as a whitelist (positive result means whitelisted)" }),
+  local_exclude_ip_map = T.string():optional()
+      :doc({ summary = "Path to map file containing IPs to exclude from this RBL check" }),
+  monitored_address = T.string():optional()
+      :doc({ summary = "Specific address to use for RBL health monitoring queries" }),
+  no_ip = T.boolean():optional()
+      :doc({ summary = "Disable IP-based lookups for this RBL" }),
+  process_script = T.string():optional()
+      :doc({ summary = "Lua script to process/transform RBL query results" }),
+  random_monitored = T.boolean():optional()
+      :doc({ summary = "Use random addresses for RBL health monitoring" }),
+  rbl = T.string()
+      :doc({ summary = "The RBL zone/domain to query (required)" }),
+  rdns = T.boolean():optional()
+      :doc({ summary = "Check reverse DNS (PTR) hostname of sender IP against this RBL" }),
+  received = T.boolean():optional()
+      :doc({ summary = "Check IP addresses from Received headers against this RBL" }),
+  received_flags = T.array(T.string()):optional()
+      :doc({ summary = "Only check Received headers with these flags set" }),
+  received_max_pos = T.number():optional()
+      :doc({ summary = "Maximum position in Received header chain to check (1 = first hop)" }),
+  received_min_pos = T.number():optional()
+      :doc({ summary = "Minimum position in Received header chain to check" }),
+  received_nflags = T.array(T.string()):optional()
+      :doc({ summary = "Only check Received headers without these flags set" }),
+  replyto = T.boolean():optional()
+      :doc({ summary = "Check Reply-To header domain against this RBL" }),
+  requests_limit = T.one_of({ T.integer(), T.transform(T.string(), tonumber) }):optional()
+      :doc({ summary = "Maximum number of RBL requests per message for this rule" }),
   require_symbols = T.one_of({
     T.array(T.string()),
     T.transform(T.string(), function(s)
       return { s }
     end)
-  }):optional(),
-  resolve_ip = T.boolean():optional(),
-  return_bits = return_bits_schema:optional(),
-  return_codes = return_codes_schema:optional(),
-  returnbits = return_bits_schema:optional(),
-  returncodes = return_codes_schema:optional(),
-  returncodes_matcher = T.enum({ "equality", "glob", "luapattern", "radix", "regexp" }):optional(),
-  selector = T.one_of({ { name = "string", schema = T.string() }, { name = "table", schema = T.table({}, { open = true }) } }):optional(),
-  selector_flatten = T.boolean():optional(),
-  symbol = T.string():optional(),
-  symbols_prefixes = T.table({}, { open = true, extra = T.string() }):optional(),
-  unknown = T.boolean():optional(),
-  url_compose_map = lua_maps.map_schema:optional(),
-  url_full_hostname = T.boolean():optional(),
-  url_whitelist = lua_maps.map_schema:optional(),
-  urls = T.boolean():optional(),
-  whitelist = lua_maps.map_schema:optional(),
+  }):optional()
+      :doc({ summary = "Only perform RBL check if these symbols are present" }),
+  resolve_ip = T.boolean():optional()
+      :doc({ summary = "Resolve hostnames to IPs before RBL lookup" }),
+  return_bits = return_bits_schema:optional()
+      :doc({ summary = "Map symbol names to bit positions in RBL response" }),
+  return_codes = return_codes_schema:optional()
+      :doc({ summary = "Map symbol names to specific RBL return codes/IPs" }),
+  returnbits = return_bits_schema:optional()
+      :doc({ summary = "Alias for return_bits" }),
+  returncodes = return_codes_schema:optional()
+      :doc({ summary = "Alias for return_codes" }),
+  returncodes_matcher = T.enum({ "equality", "glob", "luapattern", "radix", "regexp" }):optional()
+      :doc({ summary = "Method to match return codes: equality, glob, luapattern, radix, or regexp" }),
+  selector = T.one_of({ { name = "string", schema = T.string() }, { name = "table", schema = T.table({}, { open = true }) } }):optional()
+      :doc({ summary = "Selector expression to extract custom data for RBL lookup" }),
+  selector_flatten = T.boolean():optional()
+      :doc({ summary = "Flatten selector results into individual lookups" }),
+  symbol = T.string():optional()
+      :doc({ summary = "Symbol name to register for this RBL rule" }),
+  symbols_prefixes = T.table({}, { open = true, extra = T.string() }):optional()
+      :doc({ summary = "Prefix mappings for generated symbol names" }),
+  unknown = T.boolean():optional()
+      :doc({ summary = "Check IPs with unknown/missing PTR records" }),
+  url_compose_map = lua_maps.map_schema:optional()
+      :doc({ summary = "Map to compose/rewrite URLs before RBL lookup" }),
+  url_full_hostname = T.boolean():optional()
+      :doc({ summary = "Use full hostname (not just registered domain) for URL lookups" }),
+  url_whitelist = lua_maps.map_schema:optional()
+      :doc({ summary = "Map of URLs to exclude from this RBL check" }),
+  urls = T.boolean():optional()
+      :doc({ summary = "Check URLs found in message against this RBL" }),
+  whitelist = lua_maps.map_schema:optional()
+      :doc({ summary = "Map of IPs/domains to exclude from this RBL check" }),
   whitelist_exception = T.one_of({
     T.array(T.string()),
     T.transform(T.string(), function(s)
       return { s }
     end)
-  }):optional(),
-  checks = T.array(T.enum(lua_util.keys(check_types))):optional(),
-  exclude_checks = T.array(T.enum(lua_util.keys(check_types))):optional(),
+  }):optional()
+      :doc({ summary = "Symbols that bypass the whitelist" }),
+  checks = T.array(T.enum(lua_util.keys(check_types))):optional()
+      :doc({ summary = "List of check types to enable: from, received, helo, urls, emails, dkim, rdns, etc." }),
+  exclude_checks = T.array(T.enum(lua_util.keys(check_types))):optional()
+      :doc({ summary = "List of check types to explicitly disable" }),
 }
 
 local function convert_checks(rule, name)
@@ -237,9 +298,53 @@ for def_k, _ in pairs(default_options) do
   rule_schema_tbl[def_k:sub(#('default_') + 1)] = T.boolean():optional()
 end
 
+local rule_schema = T.table(rule_schema_tbl):doc({ summary = "RBL rule configuration schema" })
+
+local plugin_schema = T.table({
+  enabled = T.boolean():optional()
+      :doc({ summary = "Enable or disable the RBL module" }),
+  disable_monitoring = T.boolean():optional()
+      :doc({ summary = "Disable health monitoring for all RBLs" }),
+  local_exclude_ip_map = lua_maps.map_schema:optional()
+      :doc({ summary = "Global map of IPs to exclude from all RBL checks" }),
+  default_enabled = T.boolean():optional()
+      :doc({ summary = "Default value for enabled option in rules" }),
+  default_ipv4 = T.boolean():optional()
+      :doc({ summary = "Default value for ipv4 option in rules" }),
+  default_ipv6 = T.boolean():optional()
+      :doc({ summary = "Default value for ipv6 option in rules" }),
+  default_unknown = T.boolean():optional()
+      :doc({ summary = "Default value for unknown option in rules" }),
+  default_dkim_domainonly = T.boolean():optional()
+      :doc({ summary = "Default value for dkim_domainonly option in rules" }),
+  default_emails_domainonly = T.boolean():optional()
+      :doc({ summary = "Default value for emails_domainonly option in rules" }),
+  default_exclude_users = T.boolean():optional()
+      :doc({ summary = "Default value for exclude_users option in rules" }),
+  default_exclude_local = T.boolean():optional()
+      :doc({ summary = "Default value for exclude_local option in rules" }),
+  default_no_ip = T.boolean():optional()
+      :doc({ summary = "Default value for no_ip option in rules" }),
+  default_dkim_match_from = T.boolean():optional()
+      :doc({ summary = "Default value for dkim_match_from option in rules" }),
+  default_selector_flatten = T.boolean():optional()
+      :doc({ summary = "Default value for selector_flatten option in rules" }),
+  default_received_flags = T.array(T.string()):optional()
+      :doc({ summary = "Default received flags for all rules" }),
+  default_received_nflags = T.array(T.string()):optional()
+      :doc({ summary = "Default received negative flags for all rules" }),
+}, {
+  open = true,
+  extra = rule_schema:optional()
+      :doc({ summary = "RBL rule definition keyed by rule name" }),
+}):doc({ summary = "RBL module configuration" })
+
+PluginSchema.register("plugins.rbl.rule", rule_schema)
+PluginSchema.register("plugins.rbl", plugin_schema)
+
 return {
   check_types = check_types,
-  rule_schema = T.table(rule_schema_tbl):doc({ summary = "RBL rule configuration schema" }),
+  rule_schema = rule_schema,
   default_options = default_options,
   convert_checks = convert_checks,
 }