diff --git a/lib/rack/attack.rb b/lib/rack/attack.rb index b741478..7ff8c79 100644 --- a/lib/rack/attack.rb +++ b/lib/rack/attack.rb @@ -7,6 +7,7 @@ module Rack::Attack autoload :Blacklist, 'rack/attack/blacklist' autoload :Track, 'rack/attack/track' autoload :StoreProxy,'rack/attack/store_proxy' + autoload :Fail2Ban, 'rack/attack/fail2ban' class << self diff --git a/lib/rack/attack/fail2ban.rb b/lib/rack/attack/fail2ban.rb new file mode 100644 index 0000000..c6c2ddf --- /dev/null +++ b/lib/rack/attack/fail2ban.rb @@ -0,0 +1,42 @@ +module Rack + module Attack + class Fail2Ban + class << self + def filter(name, discriminator, options) + bantime = options[:bantime] or raise ArgumentError, "Must pass bantime option" + findtime = options[:findtime] or raise ArgumentError, "Must pass findtime option" + maxretry = options[:maxretry] or raise ArgumentError, "Must pass maxretry option" + + if yield + fail!(name, discriminator, bantime, findtime, maxretry) + else + banned?(discriminator) + end + end + + private + def fail!(name, discriminator, bantime, findtime, maxretry) + count = cache.count("#{name}:#{discriminator}", findtime) + if count >= maxretry + ban!(discriminator, bantime) + end + + # Return true for blacklist + true + end + + def ban!(discriminator, bantime) + cache.write("fail2ban:#{discriminator}", 1, bantime) + end + + def banned?(discriminator) + cache.read("fail2ban:#{discriminator}") + end + + def cache + Rack::Attack.cache + end + end + end + end +end