class Rack::Deflect

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:

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 <tj@vision-media.ca>

Attributes

options[R]

Public Instance Methods

block!() click to toggle source
# File lib/rack/contrib/deflect.rb, line 100
def block!
  return if blocked?
  log "blocked #{@remote_addr}"
  map[:block_expires] = Time.now + options[:block_duration]
end
block_expired?() click to toggle source
# File lib/rack/contrib/deflect.rb, line 110
def block_expired?
  map[:block_expires] < Time.now rescue false
end
blocked?() click to toggle source
# File lib/rack/contrib/deflect.rb, line 106
def blocked?
  map.has_key? :block_expires
end
call(env) click to toggle source
# File lib/rack/contrib/deflect.rb, line 59
def call env
  return deflect! if deflect? env
  status, headers, body = @app.call env
  [status, headers, body]
end
clear!() click to toggle source
# File lib/rack/contrib/deflect.rb, line 118
def clear!
  return unless watching?
  log "released #{@remote_addr}" if blocked?
  @remote_addr_map.delete @remote_addr
end
deflect!() click to toggle source
# File lib/rack/contrib/deflect.rb, line 65
def deflect!
  [403, { 'Content-Type' => 'text/html', 'Content-Length' => '0' }, []]
end
deflect?(env) click to toggle source
# File lib/rack/contrib/deflect.rb, line 69
def deflect? env
  @remote_addr = env['REMOTE_ADDR']
  return false if options[:whitelist].include? @remote_addr
  return true  if options[:blacklist].include? @remote_addr
  sync { watch }
end
exceeded_request_threshold?() click to toggle source
# File lib/rack/contrib/deflect.rb, line 128
def exceeded_request_threshold?
  map[:requests] > options[:request_threshold]
end
increment_requests() click to toggle source
# File lib/rack/contrib/deflect.rb, line 124
def increment_requests
  map[:requests] += 1
end
log(message) click to toggle source
# File lib/rack/contrib/deflect.rb, line 76
def log message
  return unless options[:log]
  options[:log].puts(options[:log_format] % [Time.now.strftime(options[:log_date_format]), message])
end
map() click to toggle source
# File lib/rack/contrib/deflect.rb, line 85
def map
  @remote_addr_map[@remote_addr] ||= {
    :expires => Time.now + options[:interval],
    :requests => 0
  }
end
sync(&block) click to toggle source
# File lib/rack/contrib/deflect.rb, line 81
def sync &block
  @mutex.synchronize(&block)
end
watch() click to toggle source
# File lib/rack/contrib/deflect.rb, line 92
def watch
  increment_requests
  clear! if watch_expired? and not blocked?
  clear! if blocked? and block_expired?
  block! if watching? and exceeded_request_threshold?
  blocked?
end
watch_expired?() click to toggle source
# File lib/rack/contrib/deflect.rb, line 132
def watch_expired?
  map[:expires] <= Time.now
end
watching?() click to toggle source
# File lib/rack/contrib/deflect.rb, line 114
def watching?
  @remote_addr_map.has_key? @remote_addr
end

Public Class Methods

new(app, options = {}) click to toggle source
# File lib/rack/contrib/deflect.rb, line 44
def initialize app, options = {}
  @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(options)
end