-
Notifications
You must be signed in to change notification settings - Fork 1.1k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: support for using rspamd for spam filtering
- Loading branch information
Showing
4 changed files
with
86 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,74 @@ | ||
require 'net/http' | ||
|
||
module Postal | ||
module MessageInspectors | ||
class Rspamd < MessageInspector | ||
|
||
class Error < StandardError | ||
end | ||
|
||
def inspect_message(inspection) | ||
response = request(inspection.message, inspection.scope) | ||
response = JSON.parse(response.body) | ||
return unless response['symbols'].is_a?(Hash) | ||
|
||
response['symbols'].values.each do |symbol| | ||
next if symbol['description'].blank? | ||
|
||
inspection.spam_checks << SpamCheck.new(symbol['name'], symbol['score'], symbol['description']) | ||
end | ||
rescue Error => e | ||
inspection.spam_checks << SpamCheck.new("ERROR", 0, e.message) | ||
end | ||
|
||
private | ||
|
||
def request(message, scope) | ||
http = Net::HTTP.new(@config.host, @config.port) | ||
http.use_ssl = true if @config.ssl | ||
http.read_timeout = 10 | ||
http.open_timeout = 10 | ||
|
||
raw_message = message.raw_message | ||
|
||
request = Net::HTTP::Post.new('/checkv2') | ||
request.body = raw_message | ||
request['Content-Length'] = raw_message.bytesize.to_s | ||
request['Password'] = @config.password if @config.password | ||
request['Flags'] = @config.flags if @config.flags | ||
request['User-Agent'] = 'Postal' | ||
request['Deliver-To'] = message.rcpt_to | ||
request['From'] = message.mail_from | ||
request['Rcpt'] = message.rcpt_to | ||
request['Queue-Id'] = message.token | ||
|
||
if scope == :outgoing | ||
request['User'] = '' | ||
# We don't actually know the IP but an empty input here will | ||
# still trigger rspamd to treat this as an outbound email | ||
# and disable certain checks. | ||
# https://rspamd.com/doc/tutorials/scanning_outbound.html | ||
request['Ip'] = '' | ||
end | ||
|
||
response = nil | ||
begin | ||
response = http.request(request) | ||
rescue Exception => e | ||
logger.error "Error talking to rspamd: #{e.class} (#{e.message})" | ||
logger.error e.backtrace[0,5] | ||
|
||
raise Error, "Error when scanning with rspamd (#{e.class})" | ||
end | ||
|
||
unless response.is_a?(Net::HTTPOK) | ||
logger.info "Got #{response.code} status from rspamd, wanted 200" | ||
raise Error, "Error when scanning with rspamd (got #{response.code})" | ||
end | ||
|
||
response | ||
end | ||
|
||
end | ||
end | ||
end |
a1277ba
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is here soft rejects are supported? It could be in 2 cases:
a1277ba
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It will simply provide the score back to Postal in the same way that happens with SpamAssassin. If rspamd is unavailable, we don't make any decisions about a message and it will be assumed to not be spam.
Mail is not routed through rspamd, it is simply used to inspect messages therefore graylisting is still handled by Postal.
a1277ba
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I mean rspamd provide actions, not only score. hard fail and reply "X", soft fail and reply "Y", accept.