require 'rpatricia' module Paraxial module Checker @allows = { 'v4' => Patricia.new, 'v6' => Patricia.new(:AF_INET6) } @bans = { 'v4' => Patricia.new, 'v6' => Patricia.new(:AF_INET6) } @buffer = Queue.new @mutex = Mutex.new @headers = { 'Content-Type': 'application/json' } if Paraxial::Helpers.get_api_key @thread = Thread.new do loop do get_abr sleep(10) flush_buffer end end end def self.req_to_buff(req_hash) @buffer << req_hash end def self.flush_buffer @mutex.synchronize do requests = [] until @buffer.empty? requests << @buffer.pop(true) rescue nil end send_async_request(requests) unless requests.empty? end end def self.send_async_request(requests) body = { http_requests: requests, private_api_key: Paraxial::Helpers.get_api_key } # Do not send HTTP events when free tier is set to true ft = Paraxial::FreeTier.is_free_tier if ft puts "[Paraxial] HTTP ingest not supported on free tier" else Thread.new do uri = URI.parse(Paraxial::Helpers.get_ingest_url) Net::HTTP.post(uri, body.to_json, @headers) end end end def self.get_abr uri = URI.parse(Paraxial::Helpers.get_abr_url) body = { api_key: Paraxial::Helpers.get_api_key } begin r = Net::HTTP.post(uri, body.to_json, @headers) if r.code == '200' put_abr(JSON.parse(r.body)) else 'ab_failed' end rescue StandardError => e puts '[Paraxial] HTTP connection to backend failed, check configuration' 'ab_failed' end end def self.put_abr(abr) # Expected input: a hash # {"allows"=>[{"address"=>[96, 56, 162, 210], "netmask"=>32}], # "bans"=> # [{"address"=>[8193, 3512, 34211, 0, 0, 35374, 880, 29492], "netmask"=>128}, # {"address"=>[111, 56, 162, 210], "netmask"=>32}], # "rules"=>[]} ipv4_a = [] ipv4_b = [] ipv6_a = [] ipv6_b = [] abr.each do |key, value| next if key == 'rules' # skip rules for now value.each do |ip| address = ip['address'] if address.length == 4 if key == 'allows' ipv4_a << address.join('.') elsif key == 'bans' ipv4_b << address.join('.') end elsif key == 'allows' ipv6_a << address.map { |n| n.to_s(16).rjust(4, '0') }.join(':') elsif key == 'bans' ipv6_b << address.map { |n| n.to_s(16).rjust(4, '0') }.join(':') end end end ab = { 'allows' => { 'v4' => ipv4_a, 'v6' => ipv6_a }, 'bans' => { 'v4' => ipv4_b, 'v6' => ipv6_b } } Paraxial::Checker.put(ab) end def self.put(allow_ban) allows = allow_ban['allows'] bans = allow_ban['bans'] @allows = { 'v4' => create_patricia(allows['v4'], 'v4'), 'v6' => create_patricia(allows['v6'], 'v6') } @bans = { 'v4' => create_patricia(bans['v4'], 'v4'), 'v6' => create_patricia(bans['v6'], 'v6') } :ok end def self.create_patricia(list, type) if type == 'v4' p = Patricia.new list.each do |ip| p.add(ip) end p elsif type == 'v6' p = Patricia.new(:AF_INET6) list.each do |ip| p.add(ip) end p else raise 'Wrong type in Paraxial::Checker.create_patricia' end end def self.ban_ip_msg(ip, length, msg) if allow_ip?(ip) == true local_ban(ip) uri = URI.parse(Paraxial::Helpers.get_ruby_ban_url) body = { bad_ip: ip, ban_length: length, msg: msg, api_key: Paraxial::Helpers.get_api_key } r = Net::HTTP.post(uri, body.to_json, @headers) if r.code == '200' :ok else :error end else :already_banned end end def self.honeypot_ban(ip, length) local_ban(ip) uri = URI.parse(Paraxial::Helpers.get_honeypot_url) body = { api_key: Paraxial::Helpers.get_api_key, bad_ip: ip, ban_length: length } r = Net::HTTP.post(uri, body.to_json, @headers) if r.code == '200' :ok else :error end end def self.ban_ip(ip) local_ban(ip) uri = URI.parse(Paraxial::Helpers.get_ban_url) body = { api_key: Paraxial::Helpers.get_api_key, ip_address: ip } r = Net::HTTP.post(uri, body.to_json, @headers) if r.code == '200' :ok else :error end end def self.local_ban(ip) if ip.include?('.') # IPv4 current_t = @bans['v4'] current_t.add(ip) @bans['v4'] = current_t else # IPv6 current_t = @bans['v6'] current_t.add(ip) @bans['v6'] = current_t end end def self.allow_ip?(ip) if ip.include?('.') if !@allows['v4'].search_best(ip).nil? # v4 on allow list true elsif !@bans['v4'].search_best(ip).nil? # v4 on ban list false else # v4 on no list true end elsif !@allows['v6'].search_best(ip).nil? # v6 on allow list true elsif !@bans['v6'].search_best(ip).nil? # v6 on ban list false else # v6 on no list true end end end end