mirror of
https://github.com/samsonjs/rack-attack.git
synced 2026-03-25 09:25:49 +00:00
Initial working version
This commit is contained in:
parent
e166e87fb9
commit
dc2e402812
9 changed files with 66 additions and 39 deletions
|
|
@ -1 +1,6 @@
|
|||
# Rack::Attack - middleware for throttling & blocking abusive clients
|
||||
|
||||
## Processing order
|
||||
* If any whitelist matches, the request is allowed
|
||||
* If any blacklist matches, the request is blocked (unless a whitelist matched)
|
||||
* If any throttle matches, the request is throttled (unless a whitelist or blacklist matched)
|
||||
|
|
|
|||
|
|
@ -15,17 +15,11 @@ Rack::Attack.throttle "logins/email", :limit => 2, :period => 60 do |req|
|
|||
req.params['email'] unless req.params['email'].blank?
|
||||
end
|
||||
|
||||
|
||||
# Block cloud IPs from accessing PATH regexp
|
||||
Rack::Attack.block "bad_ips from logging in" do |req|
|
||||
# Blacklist cloud IPs from accessing PATH regexp
|
||||
Rack::Attack.blacklist "bad_ips from logging in" do |req|
|
||||
req.path =~ /^login/ && bad_ips.include?(req.ip)
|
||||
end
|
||||
|
||||
# Throttle login attempts
|
||||
Rack::Attack.throttle "logins/ip", :limit => 2, :period => 1.second do | req|
|
||||
req.ip if req.post? && req.path_info =~ /^login/
|
||||
end
|
||||
|
||||
# Whitelist a User-Agent
|
||||
Rack::Attack.whitelist 'internal user agent' do |req|
|
||||
req.user_agent =~ 'InternalUserAgent'
|
||||
|
|
|
|||
|
|
@ -2,17 +2,19 @@ require 'rack'
|
|||
module Rack::Attack
|
||||
require 'rack/attack/cache'
|
||||
require 'rack/attack/throttle'
|
||||
require 'rack/attack/whitelist'
|
||||
require 'rack/attack/blacklist'
|
||||
|
||||
class << self
|
||||
|
||||
attr_reader :cache, :notifier
|
||||
|
||||
def whitelist(name, &block)
|
||||
(@whitelists ||= {})[name] = block
|
||||
(@whitelists ||= {})[name] = Whitelist.new(name, block)
|
||||
end
|
||||
|
||||
def block(name, &block)
|
||||
(@blocks ||= {})[name] = block
|
||||
def blacklist(name, &block)
|
||||
(@blacklists ||= {})[name] = Blacklist.new(name, block)
|
||||
end
|
||||
|
||||
def throttle(name, options, &block)
|
||||
|
|
@ -20,7 +22,7 @@ module Rack::Attack
|
|||
end
|
||||
|
||||
def whitelists; @whitelists ||= {}; end
|
||||
def blocks; @blocks ||= {}; end
|
||||
def blacklists; @blacklists ||= {}; end
|
||||
def throttles; @throttles ||= {}; end
|
||||
|
||||
def new(app)
|
||||
|
|
@ -38,8 +40,8 @@ module Rack::Attack
|
|||
return @app.call(env)
|
||||
end
|
||||
|
||||
if blocked?(req)
|
||||
blocked_response
|
||||
if blacklisted?(req)
|
||||
blacklisted_response
|
||||
elsif throttled?(req)
|
||||
throttled_response
|
||||
else
|
||||
|
|
@ -48,18 +50,14 @@ module Rack::Attack
|
|||
end
|
||||
|
||||
def whitelisted?(req)
|
||||
whitelists.any? do |name, block|
|
||||
block[req].tap{ |match|
|
||||
instrument(:type => :whitelist, :name => name, :request => req) if match
|
||||
}
|
||||
whitelists.any? do |name, whitelist|
|
||||
whitelist[req]
|
||||
end
|
||||
end
|
||||
|
||||
def blocked?(req)
|
||||
blocks.any? do |name, block|
|
||||
block[req].tap { |match|
|
||||
instrument(:type => :block, :name => name, :request => req) if match
|
||||
}
|
||||
def blacklisted?(req)
|
||||
blacklists.any? do |name, blacklist|
|
||||
blacklist[req]
|
||||
end
|
||||
end
|
||||
|
||||
|
|
@ -73,7 +71,7 @@ module Rack::Attack
|
|||
notifier.instrument('rack.attack', payload) if notifier
|
||||
end
|
||||
|
||||
def blocked_response
|
||||
def blacklisted_response
|
||||
[503, {}, ['Blocked']]
|
||||
end
|
||||
|
||||
|
|
@ -82,7 +80,7 @@ module Rack::Attack
|
|||
end
|
||||
|
||||
def clear!
|
||||
@whitelists, @blocks, @throttles = {}, {}, {}
|
||||
@whitelists, @blacklists, @throttles = {}, {}, {}
|
||||
end
|
||||
|
||||
end
|
||||
|
|
|
|||
13
lib/rack/attack/blacklist.rb
Normal file
13
lib/rack/attack/blacklist.rb
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
require_relative 'check'
|
||||
module Rack
|
||||
module Attack
|
||||
class Blacklist < Check
|
||||
def initialize(name, block)
|
||||
super
|
||||
@type = :blacklist
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
19
lib/rack/attack/check.rb
Normal file
19
lib/rack/attack/check.rb
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
module Rack
|
||||
module Attack
|
||||
class Check
|
||||
attr_reader :name, :block, :type
|
||||
def initialize(name, block)
|
||||
@name, @block = name, block
|
||||
@type = nil
|
||||
end
|
||||
|
||||
def [](req)
|
||||
block[req].tap {|match|
|
||||
Rack::Attack.instrument(:type => type, :name => name, :request => req) if match
|
||||
}
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
|
@ -16,7 +16,7 @@ module Rack
|
|||
end
|
||||
|
||||
def [](req)
|
||||
discriminator = @block[req]
|
||||
discriminator = block[req]
|
||||
return false unless discriminator
|
||||
|
||||
key = "#{name}:#{discriminator}"
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
module Rack
|
||||
module Attack
|
||||
VERSION = '0.0.2'
|
||||
VERSION = '0.0.3'
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -1,12 +1,10 @@
|
|||
require_relative 'check'
|
||||
module Rack
|
||||
module Attack
|
||||
class Whitelist
|
||||
attr_reader :name, :block
|
||||
def initialize(name, &block)
|
||||
@name, @block = name, block
|
||||
end
|
||||
|
||||
def [](req)
|
||||
class Whitelist < Check
|
||||
def initialize(name, block)
|
||||
super
|
||||
@type = :whitelist
|
||||
end
|
||||
|
||||
end
|
||||
|
|
|
|||
|
|
@ -22,15 +22,15 @@ describe 'Rack::Attack' do
|
|||
|
||||
allow_ok_requests
|
||||
|
||||
describe 'with a block' do
|
||||
describe 'with a blacklist' do
|
||||
before do
|
||||
@bad_ip = '1.2.3.4'
|
||||
Rack::Attack.block("ip #{@bad_ip}") {|req| req.ip == @bad_ip }
|
||||
Rack::Attack.blacklist("ip #{@bad_ip}") {|req| req.ip == @bad_ip }
|
||||
end
|
||||
|
||||
it('has a block') { Rack::Attack.blocks.key?("ip #{@bad_ip}") }
|
||||
it('has a blacklist') { Rack::Attack.blacklists.key?("ip #{@bad_ip}") }
|
||||
|
||||
it "should block bad requests" do
|
||||
it "should blacklist bad requests" do
|
||||
get '/', {}, 'REMOTE_ADDR' => @bad_ip
|
||||
last_response.status.must_equal 503
|
||||
end
|
||||
|
|
@ -44,7 +44,7 @@ describe 'Rack::Attack' do
|
|||
end
|
||||
|
||||
it('has a whitelist'){ Rack::Attack.whitelists.key?("good ua") }
|
||||
it "should allow whitelists before blocks" do
|
||||
it "should allow whitelists before blacklists" do
|
||||
get '/', {}, 'REMOTE_ADDR' => @bad_ip, 'HTTP_USER_AGENT' => @good_ua
|
||||
last_response.status.must_equal 200
|
||||
end
|
||||
|
|
|
|||
Loading…
Reference in a new issue