Code Monkey home page Code Monkey logo

rack-attack's People

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

rack-attack's Issues

Throttling Basic Auth

I'm wondering if there's a way for me to throttle basic auth requests to protect against brute force attacks.
I posted on SO, but haven't had an response.
http://stackoverflow.com/questions/22261081/using-rackattack-to-throttle-basic-auth

My basic auth code setup like so:

# config/environments/production.rb
config.middleware.insert_after(::Rack::Lock, '::Rack::Auth::Basic', ENV['RAILS_ENV') do |u, p|
  [u, p] == [ENV['BASIC_AUTH_USERNAME'], ENV['BASIC_AUTH_PASSWORD']]
end

I'd imagine my rack attack would contain something like this:

# config/initializers/rack_attack.rb
Rack::Attack.throttle('req/ip', limit: 2, period: 60.seconds) do |req| req.ip 
    # what should belong here???
end

Performance Issue?

I'm using the latest rack-attack 4.1.1 on ruby 2.1.3. I found performance problem, but I can't be sure why.

542192f9c38aa56321aed06c

transactions - wagtail-staging - new relic 2014-09-23 22-34-30

I have very simple setup and I don't have any caching yet in my app:

class Rack::Attack
  whitelist('allow admin ips') do |req|
    ENV['ADMIN_IP_ADDRESSES'] == req.ip
  end

  throttle('req/ip', :limit => 300, :period => 5.minutes) do |req|
    req.ip
  end

  throttle('logins/ip', :limit => 5, :period => 20.seconds) do |req|
    if req.path == '/oauth/token' && req.post?
      req.ip
    end
  end

  throttle("logins/email", :limit => 5, :period => 20.seconds) do |req|
    if req.path == '/oauth/token' && req.post?
      # return the email if present, nil otherwise
      req.params['email'].presence
    end
  end
end

Is there anything I miss or some ways I could do investigation?

Not working in production (memcache via dalli)

I think I'm missing something important. On our staging server (default cache) rack attack is working properly as well as in development. In production however we use the "mem_cache_store" via dalli.

We use the cache method in our views a lot and from that I can tell that caching is performed and persistent. Rack attack however doesn't work (none of the rules). The Fail2Ban rule does block the request but does not ban the IP.

This is our environment:

rack-attack (4.1.0)
dalli (2.7.2)
# production env
config.cache_store = :mem_cache_store

# rack attack config
unless Rails.env.test?
  Rack::Attack.throttle('account public post throttle', limit: 15, period: 10.minutes) do |req|
    req.ip if req.post? && %w[/account/login /account/password].include?(req.path)
  end
end

Rack::Attack.blacklist('fail2ban pentesters') do |req|
  # Substitute Fail2Ban with Allow2Ban to allow these requests until limit reached.
  Rack::Attack::Fail2Ban.filter(req.ip, maxretry: 3, findtime: 15.minutes, bantime: 30.minutes) do
    # The count for the IP is incremented if the return value is truthy.
    [
      CGI.unescape(req.query_string).include?("/etc/passwd"),
      CGI.unescape(req.query_string).include?("../.."),
    ].any?{|s| s }
  end
end

Any ideas what I may have missed here?

Fail2Ban not working in my project

Hello

I configure Rack::Attack Fail2Ban follows:

     Rack::Attack::Fail2Ban.filter(req.ip, :maxretry => 4, :findtime => 10.seconds, :bantime => 5.minutes) do
            CGI.unescape(req.path).match(/\/cv\/.*/)
     end

And Rack::Attack always block the ip, in all the attempts.

Someone can help me?

Rack-attack ignores namespacing

Hi,

I'm using redis-namespace (https://github.com/resque/redis-namespace) too keep my keys in separate namespaces.

I configured Rack-Attack to use my configuration of redis:

Rails.configuration.custom.cache_store_config = :redis_store,
  "redis://#{Rails.configuration.custom.config_redis['url']}:#{Rails.configuration.custom.config_redis['port']}",
  { expires_in: 48.hours, namespace:  Rails.configuration.custom.redis_namespace}
...
Rack::Attack.cache.store = ActiveSupport::Cache.lookup_store(Rails.configuration.custom.cache_store_config)

I'm using allow2ban to prevent bots from hitting my login pages:

Rack::Attack.blacklist('allow2ban scrapers') do |req|
  Rack::Attack::Allow2Ban.filter(req.ip, :maxretry => 20, :findtime => 1.minute, :bantime => 1.hour) do
    protected_paths =
      {
        '/sign_in' => {
          'method' => 'post'
        },
        ...
      }

    p_path = protected_paths.keys.detect { |p| File.fnmatch(p, req.path) }
    p_path.present? && req.public_send((protected_paths[p_path]['method'] + '?').to_sym)
  end
end

these keys are created in the redis store:

127.0.0.1:6379> keys '*'
3) "rack::attack:allow2ban:ban:127.0.0.1"
4) "development/c5308:rack::attack:23508850:allow2ban:count:127.0.0.1"

Number 3 is not namespaced, but the second is.

Plus, when looking for the expiration time, here is what I get:

127.0.0.1:6379> TTL "rack::attack:allow2ban:ban:127.0.0.1"
(integer) 3175
127.0.0.1:6379> TTL "development/c5308:rack::attack:23508850:allow2ban:count:127.0.0.1"
(integer) -1

The second has no expiration date though the same command is sent to Redis.

Ever heard of this behaviour?

Thanks,

Are keys recycled?

Hey guys,

I don't see the keys in Redis store being remove after the period passed. Don't we use the TTL on keys?

Thanks

whitelist not work

In Rails4

# in Gemfile
gem 'rack-attack'
# In config/application.rb
config.middleware.use Rack::Attack
# In config/initializers/rack_attack.rb
Rack::Attack.whitelist('allow from localhost') do |req|
  # Requests are allowed if the return value is truthy
  Rails.logger.debug "................"
  Rails.logger.debug "req.id:   #{req.ip} : #{'127.0.0.1' == req.ip} "
  Rails.logger.debug "................"
  '127.0.0.1' == req.ip
end

when i request a json data , likes /posts.json. The log output:

Started GET "/posts.json" for 192.168.1.101 at 2013-09-10 17:24:33 +0800
................
req.id:   192.168.1.101 : false 
................
Processing by PostsController#index as JSON

it return json data successfully. WHY??

192.168.1.101 is not localhost

The Rack::Attack.whitelist return false and Controller is still fired?

Can I set something, which does not fire controller, or return incorrect json data? And How?

Some Throttle Examples Incorrect

One of our Throttle code looks like this (Only allow 5 requests per IP per second, as long as its not requesting an asset):

Rack::Attack.throttle "req/ip/second", :limit => 5, :period => 1.second do |req|
    req.ip && !req.path.include?('assets')
end

When looking at the actual throttle code, the block passed in is supposed to return the "discriminator" which is used as part of the cache key for the request. It seems to me as if your examples (except the first) will return true or false, and then will limit all requests (not by IP) to whatever the limit is. We experienced this in production earlier today and our logs seem to indicate that we were only letting through 5 total requests per second, not 5 from the same IP address per second.

include license in gemspec

Hi, can you include the license in your gemspec and bump a new version out?

Gem::Specification.new do |s|
    s.name = "my_great_gem"
    s.license = "MIT"
end

This will enable LicenseFinder and others to identify this gem's license.

Support older dalli client versions

Hi @hakanensari, the with method in #53 is only in the latest version of dalli (2.7.0). I'd like to support older versions. Say, back to 1.1.5.

Want to take a whack at falling back to the non-threaded version when the with method is undefined?

Add support for helpers in checks.

We've been duplicating some code or having the urge to create a global method to share code between checks. We could just create our own custom class with the methods we want to share but official integration would be nice.

I created two basic solutions, block-argument and block-scope. The argument option is definitely the cleaner and probably better version.

If it's decided this is worth supporting beyond recommending a custom class I'll create a pull request. Otherwise feel free to close and we'll stick with the custom class.

Large cache sizes?

I'm noticing that this seems to use a lot of space for storage. For example, my rails /tmp/cache folder is 695 MB.

7MB are for assets where the rest seems to be files like this:
rack%3A%3Aattack%3A4716817%3Areq%2Fip%3A178.255.152.2

Is that normal?

Are we able to whitelist a subnet of IPs?

For example, all of CloudFlare's IPs:

199.27.128.0/21
173.245.48.0/20
103.21.244.0/22
103.22.200.0/22
103.31.4.0/22
141.101.64.0/18
108.162.192.0/18
190.93.240.0/20
188.114.96.0/20
197.234.240.0/22
198.41.128.0/17
162.158.0.0/15
104.16.0.0/12

How would I create a rule to whitelist all of those?

NoMethodError when using rack-attack backed by a Redis::DistributedStore

It appears that when we set cache_store to an array of redis uri's, e.g.:

ActiveSupport::Cache::RedisStore.new(["127.0.0.1", "127.0.0.1"])

Rack-attack will throw a NoMethodError error for increment, mainly because the Redis::DistributedStore class does not support the required methods.

This prevents anyone using Redis distributed to use rack-attack.

This can be easily reproduced by adding ActiveSupport::Cache::RedisStore.new(["127.0.0.1", "127.0.0.1"]) in spec/integration/rack_attack_cache_spec.rb into the array of Redis stores it currently supports.

Note that there is no issue when one is passing an array of one element into the RedisStore.new ctor.

Getting 'Attack is not a class (TypeError)' when starting rails

When starting rails server, I get the following error:

/Users/xxx/rails/testapp/config/initializers/rack_attack.rb:1:in `<top (required)>': Attack is not a class (TypeError)
    from /Users/xxx/.rvm/gems/ruby-2.1.2/gems/activesupport-4.1.6/lib/active_support/dependencies.rb:241:in `load'
    from /Users/xxx/.rvm/gems/ruby-2.1.2/gems/activesupport-4.1.6/lib/active_support/dependencies.rb:241:in `block in load'

I used the sample wiki config (class Rack::Attack... end) and also have config.middleware.use Rack::Attack listed under class Application < Rails::Application in application.rb

My environment: Rails 4.1.6/ruby 2.1.2p95

Any thoughts?

How to set cache prefix is unclear from docs

I've asked this before on StackOverflow but didn't get any answers so I'm trying here:

Cache#prefix has a getter and a setter through attr_accessor. But I can't find any examples on how to actually set the prefix due to the fact that I don't have to deal with Rack::Attack::Cache directly.

So either this is a documentation bug (and no one before me attempted to use Rack attack in two apps who share a Memcached) or the prefix is never supposed to be set (in which case it should be attr_reader :prefix).

Can you please help me clear this up?

Track Allow2Ban

I have configured an Allow2Ban rule to filter any clients with say, more than 20 requests from the same IP within 5 seconds and block them for 1 hour. Instead of implementing this in production straightaway, we would like to track and monitor the events without really blocking the users first. However, I can't find a rack.attack.match_type for Allow2Ban event. Ideally, we would like to log every single Allow2Ban occurrence and its details. Any pointers? Thanks.

Rack::Attack.track('allow2ban scraper') do |req|
  Rack::Attack::Allow2Ban.filter(req.ip, maxretry: 20, findtime: 5.seconds, bantime: 1.hour) do
    true
  end
end

ActiveSupport::Notifications.subscribe("rack.attack") do |name, start, finish, request_id, req|
   # how to filter only the Allow2Ban event?
end

Rack-Attack IP Range to CIDR

Does Rack-Attack have any form of way in handling a CIDR? I have the following in my application:

if LOCAL_SETTINGS['ip_address_whitelist'] && LOCAL_SETTINGS['whitelisted_pages']
  class Rack::Attack
    blacklist 'requests' do |req|
      regex = LOCAL_SETTINGS['whitelisted_pages'].map! { |str| Regexp.new(str) }
      if regex.any? {|i| i =~ req.path} 
        LOCAL_SETTINGS['whitelisted_pages'].find do |str|
          regexp = Regexp.new(str) 
          regexp =~ req.path 
          puts ''
        end
      else
        #This is the fallback should someone try to redirect to a page that is
        #not on the list.
        !LOCAL_SETTINGS['ip_address_whitelist'].include?(req.ip)
      end
    end
  end
end

And in my local_setting.yml I have the following:

ip_address_whitelist:
   - 127.0.0.1
   - 1.2.3.4
   - 5.6.7.8

Did scan the documentation for this and wondered if you guys supported CIDR. If I can do this it will save me the time in having to write out a few hundred IP address's

Hooking up to Redis?

Would be nice to see an example in the REDME of how this can be configured to connect to Redis. Can someone provide me with a link which explains how to do that?

request.env['rack.attack.match_discriminator'] not set

Hi,
First of all many thanks for this gem.
I facing an issue where request.env['rack.attack.match_discriminator'] is not set.
Seems like you have not pushed the latest code to rubygems.
My rack-attack gem version is 4.0.0
But it does not have the following code statement
req.env['rack.attack.match_discriminator'] = discriminator
Can you please update the latest code.

Current cache key generation may not be always correct

The method Cache#count at https://github.com/kickstarter/rack-attack/blob/master/lib/rack/attack/cache.rb#L26-L31

uses Time.now.to_i for key formation, along with expiry time.

Along with change over a Time.now, this would affect the key fetching. So throttling currently works only for a minute. And if near to end of minute, only upto the available seconds left.

I agree time based key makes sense, to not block all request from an ip, but the above scenario, bypasses throttling for now.

I was not sure, how to handle this, so filing an issue to discuss, so I could work over a fix.

Prevent hotlink ?

Can I use this gem to prevent hotlinking of images ? is there an example please ?

Noteworthy? Insert middleware before ActionDispatch::ParamsParser

I know this is rails specific but maybe this could be noted somewhere?

I tried to block this with rack-attack: http://stackoverflow.com/questions/25274125/jsonparsererror-n-unexpected-token-at-alihackeval-request-alihack-c

Turned out that the rack-attack middleware is to late to block this request before it breaks.

Solution: Insert the rack-attack middleware before the ParamsParser middelware:

config.middleware.insert_before ActionDispatch::ParamsParser, Rack::Attack

Support for subdomains

I've been implementing rack-attack this morning but had a requirement to work with the request based on it's subdomain, rather than path.

It seem that the Rack::Request class doesn't provide any way to access the subdomain of the request, which made things a little tricky.

At the moment I have borrowed a similar approach to the one described here http://tannerburson.com/2009/01/extracting-subdomains-in-sinatra.html/ but instead monkey-patched the Rack::Attack::Request class to provide the subdomains method. a'la

# We re-open the request class to add the subdomains method
module Rack
  class Attack
    class Request < ::Rack::Request

      def subdomains(tld_len=1) # we set tld_len to 1, use 2 for co.uk or similar
        # cache the result so we only compute it once.
        @env['rack.env.subdomains'] ||= lambda {
          # check if the current host is an IP address, if so return an empty array
          return [] if (host.nil? ||
                        /\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$/.match(host))
          host.split('.')[0...(1 - tld_len - 2)] # pull everything except the TLD
        }.call
      end

    end
  end
end

This then allows me to apply rules based on the subdomain, like this:

# Secure the admin interface to allowed admin IP addresses.
blacklist('Secure admin interface by IP') do |req|
    # Request are blocked if the return value is truthy
    # Check to see if this is an admin interface request and that the
    # IP is not included in the environments whitelist.
    req.subdomains.first == 'admin' && ENV['ADMIN_IP_WHITELIST'].exclude?(req.ip)
end

Do you think there is an argument for adding this to rack-attack code base? If so I'd be happy to put together a pull-request.

Robert

Ruby 1.9.2

I hadn't gotten around to updating from 1.9.2 since I don't want to do anything with rvm that might upset my server (but I still want to use rack-attack to throttle clients).

Is there a reason that the Ruby version is restricted to >=1.9.3? I was under the impression that 1.9.3 was primarily for performance improvements.

Default blacklisted response: 403 instead of 401?

Hi.

A blacklisted response returns a 401 Unauthorized by default. While 'unauthorized' sounds appropriate, I think 403 Forbidden would be a more RFC-conforming status: http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.4.2

401 suggests the request can be retried by supplying basic auth credentials, while 403 explicitly states that the request should not be repeated (ie. the source is blocked in this case).

I'm happy to supply a PR if desired but would like to hear your thoughts first.

Would you agree to support a track response support?

I am seeing a need to be able to apply track functionality based on response codes.
The possible use case I see is stats tracking of API calls by response code.
I am willing to provide a PR if you think it makes sense to integrate with rack-attack.

Rack-Attack Specify Path - Assets Not Loading

So given that I have the following in my application:

class Rack::Attack
    blacklist('allow from localhost') do |req|
      !LOCAL_SETTINGS["whitelisted_pages"].include?(req.path)
    end
  end

I have load in set amount of pages through local_settings.yml file which is set below:

whitelisted_pages:
   - /assets/*.
   - /assets/
   - /assets
   - /assets/public
   - /
   - /login

Now the problem is when I access the login page it seems to not whitelist the assets. Which consequently results in the CSS not loading. An example request of what is returned in the network tab is shown below

Request URL:http://localhost:3000/assets/public-min.js
Request Method:GET
Status Code:403 Forbidden

What would be the correct way in allowing the assets to be loaded. Because it seems to not pick up any of the paths I specify. Thanks.

Specs failing at random intervals

Hi,

I have written somes test specs for my throttling configs.

initializer file

class Rack::Attack
  throttle('req/ip', :limit => 300, :period => 5.minutes) do |req|
      req.ip
    end
  throttle('logins/ip', :limit => 5, :period => 20.seconds) do |req|
      if req.path == '/users/sign_in' && req.post?
        req.ip
      end
    end
  throttle("logins/email", :limit => 5, :period => 20.seconds) do |req|
      if req.path == '/users/sign_in' && req.post?
        req.params['email'].presence # return the email if present, nil otherwise
      end
    end
  end

I tested the first and second throttles and it does not seems to work consistantly.
Sometimes it blocks the 301st request and sometimes it allows it. Same thing happens with 'logins/ip' it allows the 6th request.

My specs file is as follows

require 'spec_helper'

  describe 'Rack::Attack.throttle', type: :rack_attack do
    before do
      @period = 5.minutes # Use a long period; failures due to cache key rotation less likely
      Rack::Attack.cache.store = ActiveSupport::Cache::MemoryStore.new
      # Rack::Attack.throttle('req/ip', :limit => 300, :period => @period) { |req| req.ip }
    end

    it('should have all throttles') { Rack::Attack.throttles.keys.should =~ ['req/ip', 'logins/ip', 'logins/email'] }

    it "must allow ok requests" do
      get '/', {}, 'REMOTE_ADDR' => '127.0.0.1'
      response.status.should == 200
    end

    describe 'a single request' do
      before { get '/', {}, 'REMOTE_ADDR' => '1.2.3.4' }

      it 'should set the counter for one request' do
        key = "rack::attack:#{(Time.now.to_i/@period).to_i}:req/ip:1.2.3.4"
        Rack::Attack.cache.store.read(key).should == 1
      end

      it 'should populate throttle data' do
        data = { :count => 1, :limit => 300, :period => @period.to_i }
        request.env['rack.attack.throttle_data']['req/ip'].should == data
      end
    end

    describe "with 301 requests to home page" do
      before do
        (@period+1).times { get '/', {}, 'REMOTE_ADDR' => '1.2.3.4' }
      end

      it 'should block the last request' do
        response.status.should == 429
      end

      it 'should tag the env' do
        request.env['rack.attack.matched'].should == 'req/ip'
        request.env['rack.attack.match_type'].should == :throttle
        request.env['rack.attack.match_data'].should == {:count => 301, :limit => 300, :period => @period.to_i }
        request.env["REMOTE_ADDR"].should == "1.2.3.4"
      end

      it 'should set a Retry-After header' do
        response.headers['Retry-After'].should == @period.to_s
      end
    end

    describe "with 6 requests to sign_in page" do
      before do
        6.times { post(new_user_session_path, user: {username: 'bogus', password: 'random123'}) }
      end

      it 'should block the last request' do
        response.status.should == 429
      end

      it 'should tag the env' do
        request.env['rack.attack.matched'].should == 'logins/ip'
        request.env['rack.attack.match_type'].should == :throttle
        request.env['rack.attack.match_data'].should == {:count => 6, :limit => 5, :period => 20 }
        request.env["REMOTE_ADDR"].should == "127.0.0.1"
      end
    end

    after(:all) do
      Rack::Attack.clear!
    end
end

Can please help me out.

is it possible to drop the connection entirely?

My understanding is that the best way to deal with things like html form submission scanners is to not return any http response at all (sounds like the default for rack attack is a 429), but rather just drop the connection at the web server. It sounds like there might be no hook available to do this in Rack based applications, so I'm guessing this is a no but, just checking! Thanks!

Selectively increment count

I have a scenario where I have a throttle set up on an API. I want to return rate limit headers for every request, which is easy enough to do via env['rack.attack.throttle_data']. However, I would also like to expose an endpoint on the API that returns rate limit stats without incrementing the rate limit counter.

If I exclude the rate limit path from the throttle, the env['rack.attack.throttle_data'] does not get set; if I include the rate limit path in the throttle, it counts toward the limit and won’t get called if the user is over the limit.

Currently, I’m doing the former and grabbing the data manually, but it isn’t pretty:

  get '/rate_limit' do
    throttle = Rack::Attack.throttles['req/ip']
    discriminator = request.ip  # Note: This must be kept in sync with the initializer
    unprefixed_key = "#{throttle.name}:#{discriminator}"
    epoch_time = Time.now.to_i
    prefixed_key = "#{Rack::Attack.cache.prefix}:#{(epoch_time / throttle.period).to_i}:#{unprefixed_key}"
    count = Rack::Attack.cache.store.read(prefixed_key) || 0
    current_limit = throttle.limit.respond_to?(:call) ? throttle.limit.call(req) : throttle.limit
    # Set the headers, return the response, etc…

There are a couple of issues with this approach, the biggest of which is that the code here for generating keys for lookup in the cache has to stay in sync with Rack::Attack. The other big issue is that the discriminator has to stay in sync with whatever is specified in my initializer.

Any thoughts on a better way to do this? I don’t know if it makes sense to include this kind of “bypass” functionality in Rack::Attack itself.

X-RateLimit headers

I'm building an API with throttling and the API has well educated clients, that can respect the X-RateLimit headers. I haven't found a way to do this in the rack-attack gem. Is there an interest in this feature for throttling? I can develop, but was just wondering if it's desirable to the gem.

Many APIs are using this already:
http://developer.github.com/v3/#rate-limiting
http://developer.vimeo.com/guidelines/rate-limiting
https://dev.twitter.com/docs/rate-limiting/1.1

Looking forward for your feedback.

Errors when using unicorn

Are there some incompatibilites with this gem and unicorn? I get weird errors when the throttling kicks in while using unicorn. Using webrick does not seem to cause those problems.

Here’s the stack trace I get on a request against unicorn and im over the throttle limit:

Rack::Lint::LintError: No Content-Type header found
    /Users/patrick/.gem/ruby/2.1.1/gems/rack-1.4.5/lib/rack/lint.rb:19:in `assert'
    /Users/patrick/.gem/ruby/2.1.1/gems/rack-1.4.5/lib/rack/lint.rb:476:in `check_content_type'
    /Users/patrick/.gem/ruby/2.1.1/gems/rack-1.4.5/lib/rack/lint.rb:54:in `_call'
    /Users/patrick/.gem/ruby/2.1.1/gems/rack-1.4.5/lib/rack/lint.rb:36:in `call'
    /Users/patrick/.gem/ruby/2.1.1/gems/rack-1.4.5/lib/rack/showexceptions.rb:24:in `call'
    /Users/patrick/.gem/ruby/2.1.1/gems/rack-1.4.5/lib/rack/commonlogger.rb:33:in `call'
    /Users/patrick/.gem/ruby/2.1.1/gems/sinatra-1.4.5/lib/sinatra/base.rb:217:in `call'
    /Users/patrick/.gem/ruby/2.1.1/gems/rack-1.4.5/lib/rack/chunked.rb:43:in `call'
    /Users/patrick/.gem/ruby/2.1.1/gems/rack-1.4.5/lib/rack/content_length.rb:14:in `call'
    /Users/patrick/.gem/ruby/2.1.1/gems/unicorn-4.8.3/lib/unicorn/http_server.rb:576:in `process_client'
    /Users/patrick/.gem/ruby/2.1.1/gems/unicorn-4.8.3/lib/unicorn/http_server.rb:670:in `worker_loop'
    /Users/patrick/.gem/ruby/2.1.1/gems/newrelic_rpm-3.7.3.204/lib/new_relic/agent/instrumentation/unicorn_instrumentation.rb:22:in `call'
    /Users/patrick/.gem/ruby/2.1.1/gems/newrelic_rpm-3.7.3.204/lib/new_relic/agent/instrumentation/unicorn_instrumentation.rb:22:in `block (4 levels) in <top (required)>'
    /Users/patrick/.gem/ruby/2.1.1/gems/unicorn-4.8.3/lib/unicorn/http_server.rb:525:in `spawn_missing_workers'
    /Users/patrick/.gem/ruby/2.1.1/gems/unicorn-4.8.3/lib/unicorn/http_server.rb:140:in `start'
    /Users/patrick/.gem/ruby/2.1.1/gems/unicorn-4.8.3/bin/unicorn:126:in `<top (required)>'
    /Users/patrick/.gem/ruby/2.1.1/bin/unicorn:23:in `load'
    /Users/patrick/.gem/ruby/2.1.1/bin/unicorn:23:in `<main>'
Rack::Lint::LintError: No Content-Type header found
    /Users/patrick/.gem/ruby/2.1.1/gems/rack-1.4.5/lib/rack/lint.rb:19:in `assert'
    /Users/patrick/.gem/ruby/2.1.1/gems/rack-1.4.5/lib/rack/lint.rb:476:in `check_content_type'
    /Users/patrick/.gem/ruby/2.1.1/gems/rack-1.4.5/lib/rack/lint.rb:54:in `_call'
    /Users/patrick/.gem/ruby/2.1.1/gems/rack-1.4.5/lib/rack/lint.rb:36:in `call'
    /Users/patrick/.gem/ruby/2.1.1/gems/rack-1.4.5/lib/rack/showexceptions.rb:24:in `call'
    /Users/patrick/.gem/ruby/2.1.1/gems/rack-1.4.5/lib/rack/commonlogger.rb:33:in `call'
    /Users/patrick/.gem/ruby/2.1.1/gems/sinatra-1.4.5/lib/sinatra/base.rb:217:in `call'
    /Users/patrick/.gem/ruby/2.1.1/gems/rack-1.4.5/lib/rack/chunked.rb:43:in `call'
    /Users/patrick/.gem/ruby/2.1.1/gems/rack-1.4.5/lib/rack/content_length.rb:14:in `call'
    /Users/patrick/.gem/ruby/2.1.1/gems/unicorn-4.8.3/lib/unicorn/http_server.rb:576:in `process_client'
    /Users/patrick/.gem/ruby/2.1.1/gems/unicorn-4.8.3/lib/unicorn/http_server.rb:670:in `worker_loop'
    /Users/patrick/.gem/ruby/2.1.1/gems/newrelic_rpm-3.7.3.204/lib/new_relic/agent/instrumentation/unicorn_instrumentation.rb:22:in `call'
    /Users/patrick/.gem/ruby/2.1.1/gems/newrelic_rpm-3.7.3.204/lib/new_relic/agent/instrumentation/unicorn_instrumentation.rb:22:in `block (4 levels) in <top (required)>'
    /Users/patrick/.gem/ruby/2.1.1/gems/unicorn-4.8.3/lib/unicorn/http_server.rb:525:in `spawn_missing_workers'
    /Users/patrick/.gem/ruby/2.1.1/gems/unicorn-4.8.3/lib/unicorn/http_server.rb:140:in `start'
    /Users/patrick/.gem/ruby/2.1.1/gems/unicorn-4.8.3/bin/unicorn:126:in `<top (required)>'
    /Users/patrick/.gem/ruby/2.1.1/bin/unicorn:23:in `load'
    /Users/patrick/.gem/ruby/2.1.1/bin/unicorn:23:in `<main>'
127.0.0.1 - - [21/May/2014 16:57:20] "GET /api/v2/public/vanities/fixteam.json?locale=en HTTP/1.0" 500 68595 1.0995

After install: ThreadError: deadlock; recursive locking

Using Rails 3.2, added rack-attack to Gemfile and bundled. Then created an initializer with only the following rule:

Rack::Attack.throttle("req/ip", :limit => 10, :period => 1) { |req| req.ip }

Now when I make any request to my app, I get the following error over and over:

ThreadError: deadlock; recursive locking
ruby-2.0.0-p247@project/gems/rack-1.4.5/lib/rack/lock.rb:14:in `lock'
ruby-2.0.0-p247@project/gems/rack-1.4.5/lib/rack/lock.rb:14:in `call'
ruby-2.0.0-p247@project/gems/actionpack-3.2.15/lib/action_dispatch/middleware/static.rb:63:in `call'
ruby-2.0.0-p247@project/gems/sentry-raven-0.6.0/lib/raven/rack.rb:47:in `call'
ruby-2.0.0-p247@project/gems/rack-rewrite-1.3.3/lib/rack/rewrite.rb:20:in `call'
ruby-2.0.0-p247@project/gems/railties-3.2.15/lib/rails/engine.rb:484:in `call'
ruby-2.0.0-p247@project/gems/railties-3.2.15/lib/rails/application.rb:231:in `call'
ruby-2.0.0-p247@project/gems/railties-3.2.15/lib/rails/railtie/configurable.rb:30:in `method_missing'
ruby-2.0.0-p247@project/gems/rack-1.4.5/lib/rack/deflater.rb:13:in `call'
ruby-2.0.0-p247@project/gems/rack-1.4.5/lib/rack/lint.rb:48:in `_call'
ruby-2.0.0-p247@project/gems/rack-1.4.5/lib/rack/lint.rb:36:in `call'
ruby-2.0.0-p247@project/gems/rack-1.4.5/lib/rack/showexceptions.rb:24:in `call'
ruby-2.0.0-p247@project/gems/rack-1.4.5/lib/rack/commonlogger.rb:33:in `call'
ruby-2.0.0-p247@project/gems/sinatra-1.4.3/lib/sinatra/base.rb:212:in `call'
ruby-2.0.0-p247@project/gems/rack-1.4.5/lib/rack/chunked.rb:43:in `call'
ruby-2.0.0-p247@project/gems/rack-1.4.5/lib/rack/content_length.rb:14:in `call'
ruby-2.0.0-p247@project/gems/unicorn-4.7.0/lib/unicorn/http_server.rb:580:in `process_client'
ruby-2.0.0-p247@project/gems/unicorn-4.7.0/lib/unicorn/http_server.rb:660:in `worker_loop'
ruby-2.0.0-p247@project/gems/newrelic_rpm-3.6.6.147/lib/new_relic/agent/instrumentation/unicorn_instrumentation.rb:22:in `call'
ruby-2.0.0-p247@project/gems/newrelic_rpm-3.6.6.147/lib/new_relic/agent/instrumentation/unicorn_instrumentation.rb:22:in `block (4 levels) in <top (required)>'
ruby-2.0.0-p247@project/gems/unicorn-4.7.0/lib/unicorn/http_server.rb:527:in `spawn_missing_workers'
ruby-2.0.0-p247@project/gems/unicorn-4.7.0/lib/unicorn/http_server.rb:153:in `start'
ruby-2.0.0-p247@project/gems/unicorn-4.7.0/bin/unicorn:126:in `<top (required)>'
ruby-2.0.0-p247@project/bin/unicorn:23:in `load'
ruby-2.0.0-p247@project/bin/unicorn:23:in `<main>'
ruby-2.0.0-p247@project/bin/ruby_noexec_wrapper:14:in `eval'
ruby-2.0.0-p247@project/bin/ruby_noexec_wrapper:14:in `<main>'

I'm using redis 3.0.4 driver as my Rails cache.

Other rack middleware I'm using:

  • rack-mobile-detect
  • rack-p3p
  • rack-rewrite
  • rack-ssl-enforcer

Any ideas what could be wrong?

Running on console/irb?

Hi,

I'm new on this gem, I'd like to play with it but it's impossible to use it with console or irb. Wonder how I did it...

Event payload does not respond to each

I noticed a large amount of exceptions in the form of

NoMethodError: undefined method `each' for #<Rack::Attack::Request:0x007f6eec788f90>

You can find the full trace here: https://gist.github.com/2called-chaos/b33da8599b16d8929ac5

You may notice that there is no rack attack in the trace and the gem which is causing the exception is appsignal by attempting to call each on the event payload.

The rails documentation states: "The payload is a mechanism that allows instrumenters to pass extra information to subscribers. Payloads consist of a hash whose contents are arbitrary and generally depend on the event."

Hackish fix proposal:

  def each &block
    { request: self }.each(&block)
  end

Undefined method `with_indifferent_access' for #<Rack::Request:0x007f978a215ab0>

Followed setup instructions but getting:

undefined method `with_indifferent_access' for #Rack::Request:0x007f9acc584558

With a plain clean install of rack after bundle install, update.

Rails 3.2.13
Ruby 2.0.0-p0
Rack-attack 2.2.0

Is this known issue, compatibility issues or some error at my side in configuration? kind regards

Started GET "/search" for 127.0.0.1 at 2013-06-23 14:33:26 +0200

NoMethodError - undefined method with_indifferent_access' for #<Rack::Request:0x007f978a215ab0>: meta_request (0.2.6) lib/meta_request/event.rb:8:ininitialize'
meta_request (0.2.6) lib/meta_request/railtie.rb:19:in block (2 levels) in <class:Railtie>' activesupport (3.2.13) lib/active_support/notifications/fanout.rb:47:inpublish'
activesupport (3.2.13) lib/active_support/notifications/fanout.rb:25:in block in publish' activesupport (3.2.13) lib/active_support/notifications/fanout.rb:25:inpublish'
activesupport (3.2.13) lib/active_support/notifications/instrumenter.rb:25:in instrument' activesupport (3.2.13) lib/active_support/notifications.rb:123:ininstrument'
rack-attack (2.2.0) lib/rack/attack.rb:91:in instrument' rack-attack (2.2.0) lib/rack/attack/throttle.rb:37:inblock in []'
rack-attack (2.2.0) lib/rack/attack/throttle.rb:32:in []' rack-attack (2.2.0) lib/rack/attack.rb:80:inblock in throttled?'
rack-attack (2.2.0) lib/rack/attack.rb:79:in throttled?' rack-attack (2.2.0) lib/rack/attack.rb:58:incall'
galetahub-simple_captcha (0.1.5) lib/simple_captcha/middleware.rb:20:in call' warden (1.2.1) lib/warden/manager.rb:35:inblock in call'
warden (1.2.1) lib/warden/manager.rb:34:in call' actionpack (3.2.13) lib/action_dispatch/middleware/best_standards_support.rb:17:incall'
rack (1.4.5) lib/rack/etag.rb:23:in call' rack (1.4.5) lib/rack/conditionalget.rb:25:incall'
actionpack (3.2.13) lib/action_dispatch/middleware/head.rb:14:in call' actionpack (3.2.13) lib/action_dispatch/middleware/params_parser.rb:21:incall'
actionpack (3.2.13) lib/action_dispatch/middleware/flash.rb:242:in call' rack (1.4.5) lib/rack/session/abstract/id.rb:210:incontext'
rack (1.4.5) lib/rack/session/abstract/id.rb:205:in call' actionpack (3.2.13) lib/action_dispatch/middleware/cookies.rb:341:incall'
activerecord (3.2.13) lib/active_record/query_cache.rb:64:in call' activerecord (3.2.13) lib/active_record/connection_adapters/abstract/connection_pool.rb:479:incall'
actionpack (3.2.13) lib/action_dispatch/middleware/callbacks.rb:28:in block in call' activesupport (3.2.13) lib/active_support/callbacks.rb:405:in_run__192911068683204755__call__1327826995642310378__callbacks'
activesupport (3.2.13) lib/active_support/callbacks.rb:405:in __run_callback' activesupport (3.2.13) lib/active_support/callbacks.rb:385:in_run_call_callbacks'
activesupport (3.2.13) lib/active_support/callbacks.rb:81:in run_callbacks' actionpack (3.2.13) lib/action_dispatch/middleware/callbacks.rb:27:incall'
actionpack (3.2.13) lib/action_dispatch/middleware/reloader.rb:65:in call' actionpack (3.2.13) lib/action_dispatch/middleware/remote_ip.rb:31:incall'
better_errors (0.9.0) lib/better_errors/middleware.rb:84:in protected_app_call' better_errors (0.9.0) lib/better_errors/middleware.rb:79:inbetter_errors_call'
better_errors (0.9.0) lib/better_errors/middleware.rb:56:in call' actionpack (3.2.13) lib/action_dispatch/middleware/debug_exceptions.rb:16:incall'
actionpack (3.2.13) lib/action_dispatch/middleware/show_exceptions.rb:56:in call' railties (3.2.13) lib/rails/rack/logger.rb:32:incall_app'
railties (3.2.13) lib/rails/rack/logger.rb:16:in block in call' activesupport (3.2.13) lib/active_support/tagged_logging.rb:22:intagged'
railties (3.2.13) lib/rails/rack/logger.rb:16:in call' config/initializers/logging.rb:6:incall_with_quiet_assets'
actionpack (3.2.13) lib/action_dispatch/middleware/request_id.rb:22:in call' rack (1.4.5) lib/rack/methodoverride.rb:21:incall'
rack (1.4.5) lib/rack/runtime.rb:17:in call' activesupport (3.2.13) lib/active_support/cache/strategy/local_cache.rb:72:incall'
rack (1.4.5) lib/rack/lock.rb:15:in call' actionpack (3.2.13) lib/action_dispatch/middleware/static.rb:63:incall'
railties (3.2.13) lib/rails/engine.rb:479:in call' railties (3.2.13) lib/rails/application.rb:223:incall'
rack (1.4.5) lib/rack/content_length.rb:14:in call' railties (3.2.13) lib/rails/rack/log_tailer.rb:17:incall'
thin (1.5.1) lib/thin/connection.rb:81:in block in pre_process' thin (1.5.1) lib/thin/connection.rb:79:inpre_process'
thin (1.5.1) lib/thin/connection.rb:54:in process' thin (1.5.1) lib/thin/connection.rb:39:inreceive_data'
eventmachine (1.0.3) lib/eventmachine.rb:187:in run' thin (1.5.1) lib/thin/backends/base.rb:63:instart'
thin (1.5.1) lib/thin/server.rb:159:in start' rack (1.4.5) lib/rack/handler/thin.rb:13:inrun'
rack (1.4.5) lib/rack/server.rb:268:in start' railties (3.2.13) lib/rails/commands/server.rb:70:instart'
railties (3.2.13) lib/rails/commands.rb:55:in block in <top (required)>' railties (3.2.13) lib/rails/commands.rb:50:in<top (required)>'
script/rails:6:in <top (required)>' -e:1:in

'

Started POST "/__better_errors/70144483448620/variables" for 127.0.0.1 at 2013-06-23 14:33:26 +0200

ArgumentError (wrong number of arguments (3 for 2)) when used with redis-activesupport

I'd like to use this gem, but use Redis as my cache for throttling. I'm using the redis-activesupport gem, and this line: https://github.com/kickstarter/rack-attack/blob/master/lib/rack/attack/cache.rb#L15 seems to be throwing an error because the Redis activesupport class doesn't accept a third arg for options, seen here: https://github.com/jodosha/redis-store/blob/master/redis-activesupport/lib/active_support/cache/redis_store.rb#L93

I'd be willing to work on a fix, but I'm not really sure what to do given there seems to be a lack of standard here.

Redis error retrieving key value after incrementing

Redis error retrieving key value after incrementing

When attempting to retrieve a key value previously incremented from redis we get an "ArgumentError: marshall data too short". This works for other stores (DalliStore, MemoryStore)

Issue can be reproduced by adding following test to spec/integration/rack_attack_cache_spec.rb

describe "read after increment" do
it "must read the value with a prefix" do
@cache.send(:do_count, @key, @expires_in)
@cache.read("cache-test-key").must_equal "1"
end
end


  1. Error:
    Rack::Attack::Cache::with Rack::Attack::StoreProxy::RedisStoreProxy::read after increment#test_0001_must read the value with a prefix:
    ArgumentError: marshal data too short
    /Users/wkimeria/.rvm/gems/ruby-1.9.3-p547/gems/redis-store-1.1.4/lib/redis/store/marshalling.rb:33:in load' /Users/wkimeria/.rvm/gems/ruby-1.9.3-p547/gems/redis-store-1.1.4/lib/redis/store/marshalling.rb:33:in_unmarshal'
    /Users/wkimeria/.rvm/gems/ruby-1.9.3-p547/gems/redis-store-1.1.4/lib/redis/store/marshalling.rb:17:in get' /Users/wkimeria/workspace/rack-attack/lib/rack/attack/store_proxy/redis_store_proxy.rb:23:inread'
    /Users/wkimeria/workspace/rack-attack/lib/rack/attack/cache.rb:37:in read' /Users/wkimeria/workspace/rack-attack/spec/integration/rack_attack_cache_spec.rb:87:inblock (5 levels) in <top (required)>'

  2. Error:
    Rack::Attack::Cache::with Rack::Attack::StoreProxy::RedisStoreProxy::read after increment#test_0001_must read the value with a prefix:
    ArgumentError: marshal data too short
    /Users/wkimeria/.rvm/gems/ruby-1.9.3-p547/gems/redis-store-1.1.4/lib/redis/store/marshalling.rb:33:in load' /Users/wkimeria/.rvm/gems/ruby-1.9.3-p547/gems/redis-store-1.1.4/lib/redis/store/marshalling.rb:33:in_unmarshal'
    /Users/wkimeria/.rvm/gems/ruby-1.9.3-p547/gems/redis-store-1.1.4/lib/redis/store/marshalling.rb:17:in get' /Users/wkimeria/workspace/rack-attack/lib/rack/attack/store_proxy/redis_store_proxy.rb:23:inread'
    /Users/wkimeria/workspace/rack-attack/lib/rack/attack/cache.rb:37:in read' /Users/wkimeria/workspace/rack-attack/spec/integration/rack_attack_cache_spec.rb:87:inblock (5 levels) in <top (required)>'

  3. Failure:
    Rack::Attack::Cache::with ActiveSupport::Cache::MemoryStore::read after increment#test_0001_must read the value with a prefix [/Users/wkimeria/workspace/rack-attack/spec/integration/rack_attack_cache_spec.rb:87]:
    Expected: "1"
    Actual: 1

Implement Proxy for Dalli

I have a working implementation of a proxy for the Dalli library (not the AS Dalli Store) and can send a pull request if you are interested.

It should make sense since you have a similar wrapper for Redis Store.

And yes, I've fixed #43 and all works fine with cache, cachier, and related ilk 😄

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.