init
This commit is contained in:
118
files/discourse-smtp-fast-rejection
Normal file
118
files/discourse-smtp-fast-rejection
Normal file
@ -0,0 +1,118 @@
|
||||
#!/usr/bin/env ruby
|
||||
|
||||
require 'syslog'
|
||||
require 'json'
|
||||
require 'uri'
|
||||
require 'cgi'
|
||||
require 'net/http'
|
||||
|
||||
ENV_FILE = "/etc/postfix/mail-receiver-environment.json"
|
||||
|
||||
def logger
|
||||
@logger ||= Syslog.open("smtp-reject", Syslog::LOG_PID, Syslog::LOG_MAIL)
|
||||
end
|
||||
|
||||
def fatal(*args)
|
||||
logger.crit *args
|
||||
exit 1
|
||||
end
|
||||
|
||||
def main
|
||||
unless File.exists?(ENV_FILE)
|
||||
fatal "Config file %s does not exist. Aborting.", ENV_FILE
|
||||
end
|
||||
|
||||
real_env = JSON.parse(File.read(ENV_FILE))
|
||||
|
||||
%w{DISCOURSE_BASE_URL DISCOURSE_API_KEY DISCOURSE_API_USERNAME}.each do |kw|
|
||||
fatal "env var %s is required", kw unless real_env[kw]
|
||||
end
|
||||
|
||||
process_requests(real_env)
|
||||
end
|
||||
|
||||
def process_requests(env)
|
||||
$stdout.sync = true # unbuffered output
|
||||
|
||||
args = {}
|
||||
while line = gets
|
||||
# Fill up args with the request details.
|
||||
# logger.err "KDDEBUG line %s", line
|
||||
line = line.chomp
|
||||
if line.empty?
|
||||
process_single_request(args, env)
|
||||
args = {} # reset for next request.
|
||||
else
|
||||
k,v = line.chomp.split('=', 2)
|
||||
args[k] = v
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def process_single_request(args, env)
|
||||
# logger.err "KDDEBUG args %s", args
|
||||
action = 'dunno'
|
||||
if args['request'] != 'smtpd_access_policy'
|
||||
action = 'defer_if_permit Internal error, Request type invalid'
|
||||
elsif args['protocol_state'] != 'RCPT'
|
||||
action = 'dunno'
|
||||
elsif args['sender'].nil?
|
||||
action = 'defer_if_permit No sender specified'
|
||||
elsif args['recipient'].nil?
|
||||
action = 'defer_if_permit No recipient specified'
|
||||
else
|
||||
action = maybe_reject_email(args['sender'], args['recipient'], env)
|
||||
end
|
||||
|
||||
puts "action=#{action}"
|
||||
puts ''
|
||||
end
|
||||
|
||||
def maybe_reject_email(from, to, env)
|
||||
endpoint = "#{env['DISCOURSE_BASE_URL']}/admin/email/smtp_should_reject.json"
|
||||
key = env["DISCOURSE_API_KEY"]
|
||||
username = env["DISCOURSE_API_USERNAME"]
|
||||
# just maker sure we have something in the from field
|
||||
# so we can test for addresses remotely
|
||||
if from == ''
|
||||
from = 'test@example.org'
|
||||
end
|
||||
uri = URI.parse(endpoint)
|
||||
fromarg = CGI::escape(from)
|
||||
toarg = CGI::escape(to)
|
||||
|
||||
api_qs = "from=#{fromarg}&to=#{toarg}"
|
||||
if uri.query and !uri.query.empty?
|
||||
uri.query += "&#{api_qs}"
|
||||
else
|
||||
uri.query = api_qs
|
||||
end
|
||||
|
||||
begin
|
||||
http = Net::HTTP.new(uri.host, uri.port)
|
||||
http.use_ssl = uri.scheme == "https"
|
||||
# logger.err "KDDEBUG request_uri %s", uri.request_uri
|
||||
get = Net::HTTP::Get.new(uri.request_uri, "api-key" => "#{key}", "api-username" => "#{username}")
|
||||
response = http.request(get)
|
||||
rescue StandardError => ex
|
||||
logger.err "Failed to GET smtp_should_reject answer from %s: %s (%s)", endpoint, ex.message, ex.class
|
||||
logger.err ex.backtrace.map { |l| " #{l}" }.join("\n")
|
||||
return "defer_if_permit Internal error, API request preparation failed"
|
||||
ensure
|
||||
http.finish if http && http.started?
|
||||
end
|
||||
|
||||
if Net::HTTPSuccess === response
|
||||
reply = JSON.parse(response.body)
|
||||
if reply['reject']
|
||||
return "reject #{reply['reason']}"
|
||||
end
|
||||
else
|
||||
logger.err "Failed to GET smtp_should_reject answer from %s: %s", endpoint, response.code
|
||||
return "defer_if_permit Internal error, API request failed"
|
||||
end
|
||||
|
||||
return "dunno" # let future tests also be allowed to reject this one.
|
||||
end
|
||||
|
||||
main if __FILE__ == $0
|
75
files/receive-mail
Normal file
75
files/receive-mail
Normal file
@ -0,0 +1,75 @@
|
||||
#!/usr/bin/env ruby
|
||||
|
||||
ENV_FILE = "/etc/postfix/mail-receiver-environment.json"
|
||||
EX_TEMPFAIL = 75
|
||||
EX_SUCCESS = 0
|
||||
|
||||
require 'syslog'
|
||||
require 'json'
|
||||
require "uri"
|
||||
require "net/http"
|
||||
|
||||
def logger
|
||||
@logger ||= Syslog.open("receive-mail", Syslog::LOG_PID, Syslog::LOG_MAIL)
|
||||
end
|
||||
|
||||
def fatal(*args)
|
||||
logger.crit *args
|
||||
exit EX_TEMPFAIL
|
||||
end
|
||||
|
||||
def main
|
||||
unless File.exists?(ENV_FILE)
|
||||
fatal "Config file %s does not exist. Aborting.", ENV_FILE
|
||||
end
|
||||
|
||||
real_env = JSON.parse(File.read(ENV_FILE))
|
||||
|
||||
%w{DISCOURSE_BASE_URL DISCOURSE_API_KEY DISCOURSE_API_USERNAME}.each do |kw|
|
||||
fatal "env var %s is required", kw unless real_env[kw]
|
||||
end
|
||||
|
||||
recipient = ARGV.first
|
||||
mail = $stdin.read
|
||||
|
||||
logger.debug "Recipient: #{recipient}"
|
||||
fatal "No recipient passed on command line." unless recipient
|
||||
fatal "No message passed on stdin." if mail.nil? || mail.empty?
|
||||
|
||||
post_email(recipient, mail, real_env)
|
||||
rescue StandardError => ex
|
||||
logger.err "Unexpected error while invoking mail processor: %s (%s)", ex.message, ex.class
|
||||
logger.err ex.backtrace.map { |l| " #{l}" }.join("\n")
|
||||
|
||||
exit EX_TEMPFAIL
|
||||
end
|
||||
|
||||
def post_email(_recipient, mail, env)
|
||||
endpoint = "#{env['DISCOURSE_BASE_URL']}/admin/email/handle_mail"
|
||||
key = env["DISCOURSE_API_KEY"]
|
||||
username = env["DISCOURSE_API_USERNAME"]
|
||||
|
||||
uri = URI.parse(endpoint)
|
||||
|
||||
begin
|
||||
http = Net::HTTP.new(uri.host, uri.port)
|
||||
http.use_ssl = uri.scheme == "https"
|
||||
post = Net::HTTP::Post.new(uri.request_uri,"api-key" => "#{key}", "api-username" => "#{username}")
|
||||
post.set_form_data(email: mail)
|
||||
|
||||
response = http.request(post)
|
||||
rescue StandardError => ex
|
||||
logger.err "Failed to POST the e-mail to %s: %s (%s)", endpoint, ex.message, ex.class
|
||||
logger.err ex.backtrace.map { |l| " #{l}" }.join("\n")
|
||||
exit EX_TEMPFAIL
|
||||
ensure
|
||||
http.finish if http && http.started?
|
||||
end
|
||||
|
||||
exit EX_SUCCESS if Net::HTTPSuccess === response
|
||||
|
||||
logger.err "Failed to POST the e-mail to %s: %s", endpoint, response.code
|
||||
exit EX_TEMPFAIL
|
||||
end
|
||||
|
||||
main if __FILE__ == $0
|
Reference in New Issue
Block a user