O Rspamd é um sistema avançado de filtragem de spam que suporta uma variedade de mecanismos de filtragem, incluindo expressões regulares, análises estatísticas e serviços personalizados, como listas negras de URL. Cada mensagem é analisada pelo Rspamd e recebe uma pontuação de spam.
De acordo com essa pontuação de spam e as configurações do usuário, o Rspamd recomenda uma ação para o MTA aplicar à mensagem, por exemplo, para transmitir, rejeitar ou adicionar um cabeçalho. O Rspamd é projetado para processar centenas de mensagens por segundo simultaneamente.
Mais sobre Rpamd pode ser lido aqui https://rspamd.com/about.html.
Antes de implementar a configuração SPFBL.net para o seu Rspamd, tenha certeza que seu serviço de e-mail não ultrapassará o limite de 10 consultas por segundo em nossos serviços. Normalmente isso não ocorre mas, se achar que isso for possível, considere uma doação mensal ao projeto e então poderemos aumentar o limite de acordo com o valor doado. No caso do seu serviço de e-mail ultrapassar o limite estabelecido, seja ele qual for, este serviço poderá sofrer banimento temporário em todos os nossos serviços públicos.
Você pode implementar todos os serviços SPFBL.net em seu Rspamd baixando o arquivo spfbl-rspamd-config-1.0.tar e seguindo esses passos:
1. Faça um backup do seu diretório /etc/rspamd
tar -czf /root/backup-rspamd.tar.gz /etc/rspamd
2. Descompacte o arquivo spfbl-rspamd-config-1.0.tar.gz
tar -C / -xzf /path/to/spfbl-rspamd-config-1.0.tar.gz
3. Reinicie o serviço rspamd.
Ou então, você pode também implementar manualmente todos os serviços SPFBL.net em seu Rspamd seguindo estes exemplos de códigos-fonte:
local logger = require "rspamd_logger" local util = require "rspamd_util" local lua_util = require "lua_util" local function spfbl_getkeys(tab) local keyset = {} for k,v in pairs(tab) do keyset[#keyset + 1] = k end return keyset end local function spfbl_validate_dns(lstr) if lstr:match('%.%.') then -- two dots in a row return false end for v in lstr:gmatch('[^%.]+') do if not v:match('^[%w-]+$') or v:len() > 63 or v:match('^-') or v:match('-$') then -- too long label or weird labels return false end end return true end local function spfbl_score_table(score) local result = 0 if score >= 0 and score <= 100 then if score > 79 then result = (score-79)*1/(21)*-1 elseif score < 50 then result = 2-(score-9)*2/(41) if result > 2 then result = 2 end end end return result end local function spfbl_check_score(task, query) local dns_cb = function(resolver, to_resolve, results, err) if err and (err ~= 'requested record is not found' and err ~= 'no records with this name') then rspamd_logger.infox(task, 'DNS query error: %1 = %2', to_resolve, err) return end if results then logger.infox(task, 'DNS query: %1 = %2', to_resolve, results) local found = false for _,result in ipairs(results) do local ipstr = result:to_string() for m in ipstr:gmatch("127.0.1.(%d+)") do if m and tonumber(m) <= 100 then local score = spfbl_score_table(tonumber(m)) score = math.floor(score * 10^2 + 0.5) / 10^2 local response = to_resolve .. ' : ' .. ipstr logger.infox(task, '%1 = %2', query.symbol, response) if not task:get_symbol(query.symbol) then task:insert_result(query.symbol, score, response) end found = true break end end if found then break end end else logger.infox(task, 'DNS query: %1 = no results', to_resolve) end end for _, v in ipairs(query.keys) do local to_resolve = v .. '.score.spfbl.net' if v ~= "localhost" and v ~= "1.0.0.127" and spfbl_validate_dns(to_resolve) then task:get_resolver():resolve_a({ task = task, name = to_resolve, callback = dns_cb, forced = true }) end end end local function spfbl_resolve_dns(task, query) local dns_cb = function(resolver, to_resolve, results, err) if err and (err ~= 'requested record is not found' and err ~= 'no records with this name') then rspamd_logger.infox(task, 'DNS query error: %1 = %2', to_resolve, err) return end if results then logger.infox(task, 'DNS query: %1 = %2', to_resolve, results) local found = false for _,result in ipairs(results) do local ipstr = result:to_string() for symbol,i in pairs(query.returncodes) do if i == ipstr then local response = to_resolve .. ' : ' .. ipstr logger.infox(task, '(%1) %2 = %3', query.symbol, symbol, response) if query.symbol == "SPFBL_RECEIVED" or not task:get_symbol(query.symbol) then task:insert_result(query.symbol, 0) task:insert_result(symbol, 1, response) end found = true break end end if found then break end end else logger.infox(task, 'DNS query: %1 = no results', to_resolve) end end for _, v in ipairs(query.keys) do local to_resolve = v .. '.' .. query.dbl if v ~= "localhost" and (spfbl_validate_dns(to_resolve) or v:match("^[%w.]+@%w+%.%w+$")) then task:get_resolver():resolve_a({ task = task, name = to_resolve, callback = dns_cb }) end end end -- All SPFBL symbols here local spfbl_symbols = { SPFBL_WHITELIST_DOMAIN = { SPFBL_WHITELIST_DOMAIN_GOOD_REPUTATION = "127.0.0.2", SPFBL_WHITELIST_DOMAIN_PUBLIC_SERVICE = "127.0.0.3", SPFBL_WHITELIST_DOMAIN_CORPORATE_SERVICE = "127.0.0.4", SPFBL_WHITELIST_DOMAIN_BULK_SENDER = "127.0.0.5" }, SPFBL_DOMAIN = { SPFBL_DOMAIN_BAD_REPUTATION = "127.0.0.2", SPFBL_DOMAIN_SUSPECTED_SOURCE = "127.0.0.3" }, SPFBL_SERVER = { SPFBL_SERVER_BAD_REPUTATION = "127.0.0.2", SPFBL_SERVER_SUSPECTED_SOURCE = "127.0.0.3", SPFBL_SERVER_END_USER = "127.0.0.4" }, SPFBL_WHITELIST_SERVER = { SPFBL_WHITELIST_SERVER_GOOD_REPUTATION = "127.0.0.2", SPFBL_WHITELIST_SERVER_PUBLIC_SERVICE = "127.0.0.3", SPFBL_WHITELIST_SERVER_CORPORATE_SERVICE = "127.0.0.4" }, SPFBL_RECEIVED = { SPFBL_RECEIVED_BAD_REPUTATION = "127.0.0.2", SPFBL_RECEIVED_SUSPECTED_SOURCE = "127.0.0.3", SPFBL_RECEIVED_END_USER = "127.0.0.4" }, SPFBL_EMAIL = { SPFBL_EMAIL_BAD_REPUTATION = "127.0.0.2", SPFBL_EMAIL_SUSPECTED_SOURCE = "127.0.0.3" }, SPFBL_SCORE = nil } local function spfbl_get_symbols(task, key) for k, v in pairs(spfbl_symbols) do if k == key then if task:get_symbol(k) then return true end for k2, v2 in pairs(v) do if task:get_symbol(k2) then return true end end end end return false end local function spfbl_register_symbols(id_symbol, group) for k, v in pairs(group) do rspamd_config:register_symbol({name = k, parent = id_symbol, type = 'virtual' }) end end local function spfbl_register_dependency(symbol,key) for k, v in pairs(spfbl_symbols) do if k == key then rspamd_config:register_dependency(symbol, key) for k2, v2 in pairs(v) do rspamd_config:register_dependency(symbol, k2) end end end return false end local spfbl_id_symbol = nil -- spfbl_id_symbol = rspamd_config:register_symbol{name = "SPFBL_RECEIVED", type = 'callback', flags = 'empty,no_squeeze', callback = function(task) logger.infox(task, 'started SPFBL_RECEIVED') if spfbl_get_symbols(task, 'SPFBL_SERVER') or spfbl_get_symbols(task, 'SPFBL_WHITELIST_SERVER') then return end local ip = task:get_from_ip() if ip and ip:is_valid() then local received_headers = task:get_received_headers() for k, v in pairs(received_headers) do if v['real_ip'] and v['real_ip']:is_valid() and not v['flags']['artificial'] and not v['real_ip']:is_local() and v['real_ip']:to_string() ~= ip:to_string() then local ip_query = {[1] = table.concat(v['real_ip']:inversed_str_octets(), '.') } spfbl_resolve_dns(task, { dbl = 'dnsbl.spfbl.net', keys = ip_query, symbol = "SPFBL_RECEIVED", returncodes = spfbl_symbols.SPFBL_RECEIVED }) end end end end} spfbl_register_symbols(spfbl_id_symbol, spfbl_symbols.SPFBL_RECEIVED) spfbl_register_dependency("SPFBL_RECEIVED", "SPFBL_SERVER") spfbl_register_dependency("SPFBL_RECEIVED", "SPFBL_WHITELIST_SERVER") -- spfbl_id_symbol = rspamd_config:register_symbol{name = "SPFBL_WHITELIST_DOMAIN", type = 'callback', flags = 'nice,empty,no_squeeze', callback = function(task) logger.infox(task, 'started SPFBL_WHITELIST_DOMAIN') if spfbl_get_symbols(task, 'SPFBL_WHITELIST_SERVER') then return end local search = {} if task:get_symbol('R_SPF_ALLOW') then local from = task:get_from(1) if (from and from[1]) then search[from[1].domain:lower()] = 1 end end if task:get_symbol('R_DKIM_ALLOW') or task:get_symbol('DMARC_POLICY_ALLOW') then local from = task:get_from(2) if (from and from[1]) then search[from[1].domain:lower()] = 1 end end local query = spfbl_getkeys(search) if #query > 0 then spfbl_resolve_dns(task, { dbl = 'dnswl.spfbl.net', keys = query, symbol = "SPFBL_WHITELIST_DOMAIN", returncodes = spfbl_symbols.SPFBL_WHITELIST_DOMAIN }) end end} spfbl_register_symbols(spfbl_id_symbol, spfbl_symbols.SPFBL_WHITELIST_DOMAIN) spfbl_register_dependency("SPFBL_WHITELIST_DOMAIN", "SPFBL_WHITELIST_SERVER") rspamd_config:register_dependency('SPFBL_WHITELIST_DOMAIN', 'R_SPF_ALLOW') rspamd_config:register_dependency('SPFBL_WHITELIST_DOMAIN', 'R_DKIM_ALLOW') rspamd_config:register_dependency('SPFBL_WHITELIST_DOMAIN', 'DMARC_POLICY_ALLOW') -- spfbl_id_symbol = rspamd_config:register_symbol{name = "SPFBL_DOMAIN", type = 'callback', flags = 'empty,no_squeeze', callback = function(task) logger.infox(task, 'started SPFBL_DOMAIN') if spfbl_get_symbols(task, 'SPFBL_WHITELIST_DOMAIN') then return end local search = {} local from = task:get_from(1) if (from and from[1]) then search[from[1].domain:lower()] = 1 end from = task:get_from(2) if (from and from[1]) then search[from[1].domain:lower()] = 1 end local replyto = task:get_header('Reply-To') if replyto then local rt = util.parse_mail_address(replyto, task:get_mempool()) if (rt and rt[1]) then search[rt[1].domain:lower()] = 1 end end local query = spfbl_getkeys(search) if #query > 0 then spfbl_resolve_dns(task, { dbl = 'dnsbl.spfbl.net', keys = query, symbol = "SPFBL_DOMAIN", returncodes = spfbl_symbols.SPFBL_DOMAIN }) end end} spfbl_register_symbols(spfbl_id_symbol, spfbl_symbols.SPFBL_DOMAIN) spfbl_register_dependency("SPFBL_DOMAIN", "SPFBL_WHITELIST_DOMAIN") --- spfbl_id_symbol = rspamd_config:register_symbol{name = "SPFBL_EMAIL", type = 'callback', flags = 'empty,no_squeeze', callback = function(task) logger.infox(task, 'started SPFBL_EMAIL') if spfbl_get_symbols(task, 'SPFBL_DOMAIN') or spfbl_get_symbols(task, 'SPFBL_WHITELIST_DOMAIN') then return end local search = {} if task:get_symbol('FREEMAIL_ENVFROM') then local from = task:get_from(1) if (from and from[1]) then search[from[1].addr] = 1 end end if task:get_symbol('FREEMAIL_FROM') then local from = task:get_from(2) if (from and from[1]) then search[from[1].addr] = 1 end end if task:get_symbol('FREEMAIL_REPLYTO') then local replyto = task:get_header('Reply-To') if replyto then local rt = util.parse_mail_address(replyto, task:get_mempool()) if (rt and rt[1]) then lua_util.remove_email_aliases(rt[1]) search[rt[1].addr] = 1 end end end local query = spfbl_getkeys(search) if #query > 0 then spfbl_resolve_dns(task, { dbl = 'dnsbl.spfbl.net', keys = query, symbol = "SPFBL_EMAIL", returncodes = spfbl_symbols.SPFBL_EMAIL }) end end} spfbl_register_symbols(spfbl_id_symbol, spfbl_symbols.SPFBL_EMAIL) spfbl_register_dependency("SPFBL_EMAIL", "SPFBL_WHITELIST_DOMAIN") spfbl_register_dependency("SPFBL_EMAIL", "SPFBL_DOMAIN") rspamd_config:register_dependency('SPFBL_EMAIL', 'FREEMAIL_ENVFROM') rspamd_config:register_dependency('SPFBL_EMAIL', 'FREEMAIL_FROM') rspamd_config:register_dependency('SPFBL_EMAIL', 'FREEMAIL_REPLYTO') --- spfbl_id_symbol = rspamd_config:register_symbol{name = "SPFBL_SCORE", type = 'callback', flags = 'nice,empty,no_squeeze', callback = function(task) logger.infox(task, 'started SPFBL_SCORE') -- check IP/RDNS score if not (spfbl_get_symbols(task, 'SPFBL_SERVER') or spfbl_get_symbols(task, 'SPFBL_WHITELIST_SERVER')) then local search = {} local ip = task:get_from_ip() if ip and ip:is_valid() and not ip:is_local() then search[table.concat(ip:inversed_str_octets(), '.')] = 1 end local rdns = task:get_hostname() if not (rdns == nil or rdns == '' or rdns == 'unknown' or rdns == 'localhost') then search[rdns:lower()] = 1 end local query = spfbl_getkeys(search) if #query > 0 then spfbl_check_score(task, { keys = query, symbol = "SPFBL_SCORE_SERVER" }) end end -- check domain score if not (spfbl_get_symbols(task, 'SPFBL_DOMAIN') or spfbl_get_symbols(task, 'SPFBL_WHITELIST_DOMAIN') or spfbl_get_symbols(task, 'SPFBL_EMAIL')) then local search = {} if task:get_symbol('R_SPF_ALLOW') then local from = task:get_from(1) if (from and from[1]) then search[from[1].domain:lower()] = 1 end end if task:get_symbol('R_DKIM_ALLOW') or task:get_symbol('DMARC_POLICY_ALLOW') then local from = task:get_from(2) if (from and from[1]) then search[from[1].domain:lower()] = 1 end end local replyto = task:get_header('Reply-To') if replyto then local rt = util.parse_mail_address(replyto, task:get_mempool()) if (rt and rt[1]) then lua_util.remove_email_aliases(rt[1]) search[rt[1].addr] = 1 end end local query = spfbl_getkeys(search) if #query > 0 then spfbl_check_score(task, { keys = query, symbol = "SPFBL_SCORE_DOMAIN" }) end end end} rspamd_config:register_symbol({name = "SPFBL_SCORE_SERVER", parent = spfbl_id_symbol, type = 'virtual' }) rspamd_config:register_symbol({name = "SPFBL_SCORE_DOMAIN", parent = spfbl_id_symbol, type = 'virtual' }) spfbl_register_dependency("SPFBL_SCORE", "SPFBL_WHITELIST_DOMAIN") spfbl_register_dependency("SPFBL_SCORE", "SPFBL_DOMAIN") spfbl_register_dependency("SPFBL_SCORE", "SPFBL_WHITELIST_SERVER") spfbl_register_dependency("SPFBL_SCORE", "SPFBL_SERVER") spfbl_register_dependency("SPFBL_SCORE", "SPFBL_EMAIL") rspamd_config:register_dependency('SPFBL_SCORE', 'R_SPF_ALLOW') rspamd_config:register_dependency('SPFBL_SCORE', 'R_DKIM_ALLOW') rspamd_config:register_dependency('SPFBL_SCORE', 'DMARC_POLICY_ALLOW') -- Adding conditions to all symbols for k, v in pairs(spfbl_symbols) do rspamd_config:add_condition(k, function(task) if task:get_user() then return false end local ip = task:get_from_ip() if ip and ip:is_local() then return false end return true end) end
SPFBL_UNAUTH { expression = "(SPFBL_RECEIVED_END_USER or SPFBL_SERVER_END_USER) and not SPFBL_WHITELIST_DOMAIN and not RCVD_VIA_SMTP_AUTH"; description = "Relayed through SPFBL.NET IP without sufficient authentication (possible indicating an open relay)" score = 0.5; policy = "leave"; } SPFBL_WHITELIST { expression = "SPFBL_WHITELIST_DOMAIN and not SPFBL_WHITELIST_DOMAIN_BULK_SENDER and (SPFBL_WHITELIST_SERVER_GOOD_REPUTATION or SPFBL_WHITELIST_SERVER_PUBLIC_SERVICE or SPFBL_WHITELIST_SERVER_CORPORATE_SERVICE or SPFBL_WHITELIST_SERVER_BULK_SENDER)"; description = "Whitelisted by SPFBL.NET"; score = -1.0; policy = "leave"; } SPFBL_BULK_SENDER_BAD_REPLYTO { expression = "SPFBL_WHITELIST_SERVER_BULK_SENDER and (FREEMAIL_REPLYTO or DISPOSABLE_REPLYTO)"; description = "Bulk/Good senders have no reason to use a free/disposable e-mail address in Reply-To"; score = 1.0; policy = "leave"; }
rules { SPFBL_URIBL_EMAIL { dnsbl = "uribl.spfbl.net"; domain_only = false; returncodes = { SPFBL_URIBL_EMAIL_PHISHING_SPAM = "127.0.0.2"; SPFBL_URIBL_EMAIL_SUSPECTED_MALWARE = "127.0.0.3"; } } }
group "spfbl" { symbols = { "SPFBL_DOMAIN" { weight = 0.0; description = "Domain blacklist at SPFBL.NET"; one_shot = true; } "SPFBL_DOMAIN_BAD_REPUTATION" { weight = 3.0; description = "Domain blacklisted due to bad reputation and confirmed by anonymous complaints"; } "SPFBL_DOMAIN_SUSPECTED_SOURCE" { weight = 1.0; description = "Domain flagged due to the difficulty of identifying the person responsible"; } "SPFBL_SCORE" { weight = 0.0; description = "Score from SPFBL.NET"; } "SPFBL_SCORE_SERVER" { weight = 1.0; description = "Server score from SPFBL.NET"; } "SPFBL_SCORE_DOMAIN" { weight = 2.0; description = "Server score from SPFBL.NET"; } "SPFBL_WHITELIST_DOMAIN" { weight = 0.0; description = "Domain whitelist at SPFBL.NET"; } "SPFBL_WHITELIST_DOMAIN_GOOD_REPUTATION" { weight = -3.0; description = "Domain listed by excellent reputation, confirmed by the community"; one_shot = true; } "SPFBL_WHITELIST_DOMAIN_PUBLIC_SERVICE" { weight = -1.5; description = "Domain listed as public service or indispensable for the proper functioning of society"; one_shot = true; } "SPFBL_WHITELIST_DOMAIN_CORPORATE_SERVICE" { weight = -1.0; description = "Domain listed as corporate message service, forbidden to use for marketing purposes"; one_shot = true; } "SPFBL_WHITELIST_DOMAIN_BULK_SENDER" { weight = 0.0; description = "Domain listed as bulk message sender with low spam or phishing complaints"; } "SPFBL_EMAIL_BAD_REPUTATION" { weight = 5.0; description = "E-mail address blacklisted due to bad reputation and confirmed by anonymous complaints"; } "SPFBL_EMAIL_SUSPECTED_SOURCE" { weight = 2.5; description = "E-mail address flagged due to the difficulty of identifying the person responsible"; } } }
rbls { spfbl_server { symbol = "SPFBL_SERVER"; rbl = "dnsbl.spfbl.net"; ipv6 = true; ipv4 = true; received = false; from = true; rdns = true; ignore_whitelists = false; returncodes { SPFBL_SERVER_BAD_REPUTATION = "127.0.0.2"; SPFBL_SERVER_SUSPECTED_SOURCE = "127.0.0.3"; SPFBL_SERVER_END_USER = "127.0.0.4"; } } spfbl_whitelist_server { symbol = "SPFBL_WHITELIST_SERVER"; rbl = "dnswl.spfbl.net"; ipv6 = true; ipv4 = true; is_whitelist = true; received = false; from = true; rdns = true; ignore_whitelists = true; whitelist_exception = "SPFBL_WHITELIST_SERVER"; whitelist_exception = "SPFBL_WHITELIST_SERVER_GOOD_REPUTATION"; whitelist_exception = "SPFBL_WHITELIST_SERVER_CORPORATE_SERVICE"; returncodes { SPFBL_WHITELIST_SERVER_GOOD_REPUTATION = "127.0.0.2"; SPFBL_WHITELIST_SERVER_PUBLIC_SERVICE = "127.0.0.3"; SPFBL_WHITELIST_SERVER_CORPORATE_SERVICE = "127.0.0.4"; SPFBL_WHITELIST_SERVER_BULK_SENDER = "127.0.0.5"; } } }
symbols = { "SPFBL_WHITELIST_SERVER" { weight = 0.0; one_shot = true; } "SPFBL_WHITELIST_SERVER_GOOD_REPUTATION" { weight = -2.0; description = "IP or Hostname listed by excellent reputation, confirmed by the community"; one_shot = true; } "SPFBL_WHITELIST_SERVER_PUBLIC_SERVICE" { weight = -1.0; description = "IP or Hostname listed as public service or indispensable for the proper functioning of society"; one_shot = true; } "SPFBL_WHITELIST_SERVER_CORPORATE_SERVICE" { weight = -1.0; description = "IP or Hostname listed as corporate message service, forbidden to use for marketing purposes"; one_shot = true; } "SPFBL_WHITELIST_SERVER_BULK_SENDER" { weight = 0.0; description = "IP or Hostname listed as bulk message sender with low spam or phishing complaints"; one_shot = true; } "SPFBL_SERVER" { weight = 0.0; one_shot = true; } "SPFBL_SERVER_BAD_REPUTATION" { weight = 2.0; description = "IP or Hostname blacklisted due to bad reputation and confirmed by anonymous complaints"; one_shot = true; } "SPFBL_SERVER_SUSPECTED_SOURCE" { weight = 1.0; description = "IP or Hostname flagged due to the difficulty of identifying the person responsible"; one_shot = true; } "SPFBL_SERVER_END_USER" { weight = 3.0; description = "IP or Hostname should not deliver unauthenticated SMTP email to any Internet mail server"; one_shot = true; } "SPFBL_RECEIVED_BAD_REPUTATION" { weight = 0.5; description = "Received address blacklisted due to bad reputation and confirmed by anonymous complaints"; } "SPFBL_RECEIVED_SUSPECTED_SOURCE" { weight = 0.1; description = "Received address flagged due to the difficulty of identifying the person responsible"; } "SPFBL_RECEIVED_END_USER" { weight = 0.0; description = "Received address should not deliver unauthenticated SMTP email to any Internet mail server"; } }
rules { spfbl_uribl { suffix = "uribl.spfbl.net"; noip = false; resolve_ip = true; ips { SPFBL_URIBL_PHISHING_SPAM = "127.0.0.2"; SPFBL_URIBL_SUSPECTED_MALWARE = "127.0.0.3"; } } }
symbols = { "SPFBL_URIBL_PHISHING_SPAM" { weight = 2.0; description = "Domain listed for inappropriate use of the URL, such as phishing or used by spammer"; } "SPFBL_URIBL_SUSPECTED_MALWARE" { weight = 1.0; description = "Executable file listed for suspected malware"; } "SPFBL_URIBL_EMAIL_PHISHING_SPAM" { weight = 4.0; description = "E-mail listed for inappropriate use, such as phishing or used by spammer"; } "SPFBL_URIBL_EMAIL_SUSPECTED_MALWARE" { weight = 1.0; description = "Executable file listed for suspected malware"; } }