Class: Rack::Deflect
- Inherits:
-
Object
- Object
- Rack::Deflect
- Defined in:
- lib/rack/contrib/deflect.rb
Overview
Rack middleware for protecting against Denial-of-service attacks en.wikipedia.org/wiki/Denial-of-service_attack.
This middleware is designed for small deployments, which most likely are not utilizing load balancing from other software or hardware. Deflect current supports the following functionality:
-
Saturation prevention (small DoS attacks, or request abuse)
-
Blacklisting of remote addresses
-
Whitelisting of remote addresses
-
Logging
Options:
:log When false logging will be bypassed, otherwise pass an object responding to #puts
:log_format Alter the logging format
:log_date_format Alter the logging date format
:request_threshold Number of requests allowed within the set :interval. Defaults to 100
:interval Duration in seconds until the request counter is reset. Defaults to 5
:block_duration Duration in seconds that a remote address will be blocked. Defaults to 900 (15 minutes)
:whitelist Array of remote addresses which bypass Deflect. NOTE: this does not block others
:blacklist Array of remote addresses immediately considered malicious
Examples:
use Rack::Deflect, :log => $stdout, :request_threshold => 20, :interval => 2, :block_duration => 60
CREDIT: TJ Holowaychuk <[email protected]>
Instance Attribute Summary collapse
-
#options ⇒ Object
readonly
Returns the value of attribute options.
Instance Method Summary collapse
- #block!(remote_addr) ⇒ Object
- #block_expired?(remote_addr) ⇒ Boolean
- #blocked?(remote_addr) ⇒ Boolean
- #call(env) ⇒ Object
- #clear!(remote_addr) ⇒ Object
- #deflect! ⇒ Object
- #deflect?(env) ⇒ Boolean
- #exceeded_request_threshold?(remote_addr) ⇒ Boolean
- #increment_requests(remote_addr) ⇒ Object
-
#initialize(app, options = {}) ⇒ Deflect
constructor
A new instance of Deflect.
- #log(message) ⇒ Object
- #map(remote_addr) ⇒ Object
- #sync(&block) ⇒ Object
- #watch(remote_addr) ⇒ Object
- #watch_expired?(remote_addr) ⇒ Boolean
- #watching?(remote_addr) ⇒ Boolean
Constructor Details
#initialize(app, options = {}) ⇒ Deflect
Returns a new instance of Deflect.
46 47 48 49 50 51 52 53 54 55 56 57 58 59 |
# File 'lib/rack/contrib/deflect.rb', line 46 def initialize app, = {} @mutex = Mutex.new @remote_addr_map = {} @app, @options = app, { :log => false, :log_format => 'deflect(%s): %s', :log_date_format => '%m/%d/%Y', :request_threshold => 100, :interval => 5, :block_duration => 900, :whitelist => [], :blacklist => [] }.merge() end |
Instance Attribute Details
#options ⇒ Object (readonly)
Returns the value of attribute options.
44 45 46 |
# File 'lib/rack/contrib/deflect.rb', line 44 def @options end |
Instance Method Details
#block!(remote_addr) ⇒ Object
102 103 104 105 106 |
# File 'lib/rack/contrib/deflect.rb', line 102 def block!(remote_addr) return if blocked?(remote_addr) log "blocked #{remote_addr}" map(remote_addr)[:block_expires] = Time.now + [:block_duration] end |
#block_expired?(remote_addr) ⇒ Boolean
112 113 114 |
# File 'lib/rack/contrib/deflect.rb', line 112 def block_expired?(remote_addr) map(remote_addr)[:block_expires] < Time.now rescue false end |
#blocked?(remote_addr) ⇒ Boolean
108 109 110 |
# File 'lib/rack/contrib/deflect.rb', line 108 def blocked?(remote_addr) map(remote_addr).has_key? :block_expires end |
#call(env) ⇒ Object
61 62 63 64 65 |
# File 'lib/rack/contrib/deflect.rb', line 61 def call env return deflect! if deflect? env status, headers, body = @app.call env [status, headers, body] end |
#clear!(remote_addr) ⇒ Object
120 121 122 123 124 |
# File 'lib/rack/contrib/deflect.rb', line 120 def clear!(remote_addr) return unless watching?(remote_addr) log "released #{remote_addr}" if blocked?(remote_addr) @remote_addr_map.delete remote_addr end |
#deflect! ⇒ Object
67 68 69 |
# File 'lib/rack/contrib/deflect.rb', line 67 def deflect! [403, { 'content-type' => 'text/html', 'content-length' => '0' }, []] end |
#deflect?(env) ⇒ Boolean
71 72 73 74 75 76 |
# File 'lib/rack/contrib/deflect.rb', line 71 def deflect? env remote_addr = env['REMOTE_ADDR'] return false if [:whitelist].include? remote_addr return true if [:blacklist].include? remote_addr sync { watch(remote_addr) } end |
#exceeded_request_threshold?(remote_addr) ⇒ Boolean
130 131 132 |
# File 'lib/rack/contrib/deflect.rb', line 130 def exceeded_request_threshold?(remote_addr) map(remote_addr)[:requests] > [:request_threshold] end |
#increment_requests(remote_addr) ⇒ Object
126 127 128 |
# File 'lib/rack/contrib/deflect.rb', line 126 def increment_requests(remote_addr) map(remote_addr)[:requests] += 1 end |
#log(message) ⇒ Object
78 79 80 81 |
# File 'lib/rack/contrib/deflect.rb', line 78 def log return unless [:log] [:log].puts([:log_format] % [Time.now.strftime([:log_date_format]), ]) end |
#map(remote_addr) ⇒ Object
87 88 89 90 91 92 |
# File 'lib/rack/contrib/deflect.rb', line 87 def map(remote_addr) @remote_addr_map[remote_addr] ||= { :expires => Time.now + [:interval], :requests => 0 } end |
#sync(&block) ⇒ Object
83 84 85 |
# File 'lib/rack/contrib/deflect.rb', line 83 def sync &block @mutex.synchronize(&block) end |
#watch(remote_addr) ⇒ Object
94 95 96 97 98 99 100 |
# File 'lib/rack/contrib/deflect.rb', line 94 def watch(remote_addr) increment_requests(remote_addr) clear!(remote_addr) if watch_expired?(remote_addr) and not blocked?(remote_addr) clear!(remote_addr) if blocked?(remote_addr) and block_expired?(remote_addr) block!(remote_addr) if watching?(remote_addr) and exceeded_request_threshold?(remote_addr) blocked?(remote_addr) end |
#watch_expired?(remote_addr) ⇒ Boolean
134 135 136 |
# File 'lib/rack/contrib/deflect.rb', line 134 def watch_expired?(remote_addr) map(remote_addr)[:expires] <= Time.now end |
#watching?(remote_addr) ⇒ Boolean
116 117 118 |
# File 'lib/rack/contrib/deflect.rb', line 116 def watching?(remote_addr) @remote_addr_map.has_key? remote_addr end |