#!/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