rack / rack-attack Goto Github PK
View Code? Open in Web Editor NEWRack middleware for blocking & throttling
License: MIT License
Rack middleware for blocking & throttling
License: MIT License
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
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.
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?
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?
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?
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,
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
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?
I know we could add these headers to response in controller from "request.env['rack.attack.throttle_data'][name]", but if we could config add this custom header in rack-attack, it would be great.
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.
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.
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?
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.
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?
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?
Getting this error when going to run rspec.
Failure/Error: post resend_confirmation_path, :user => {:email => @user2.email}
NoMethodError:
undefined method `with_indifferent_access' for #Rack::Request:0x1060b304
Not sure why this is occurring, it was working before. pls suggest me on this asap.
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.
It looks like Rack::Attack became a class in c3a0774 by @stevehodgkiss. Is there a reason it's a class?
It seems like this is interfering with the default configuration for some people: https://twitter.com/pmarreck/status/461626413331021824
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?
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?
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
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
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?
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.
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.
Can I use this gem to prevent hotlinking of images ? is there an example please ?
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
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
I added an example rack-attack.rb
configuration file to the Wiki. Could someone take a look and see if it's ready to be linked to in the README?
https://github.com/kickstarter/rack-attack/wiki/Example-Configuration
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.
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.
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
.
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.
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.
Hi,
def write(key, value, options={})
if (expires_in = options[:expires_in])
=> self.setex(key, expires_in, value)
else
self.set(key, value)
end
rescue Redis::BaseError
end
returns nil instead of "OK".
Do I have to have a specific version of redis-store?
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!
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.
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.
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
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:
Any ideas what could be wrong?
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...
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
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:in
initialize'
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:in
publish'
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:in
publish'
activesupport (3.2.13) lib/active_support/notifications/instrumenter.rb:25:in instrument' activesupport (3.2.13) lib/active_support/notifications.rb:123:in
instrument'
rack-attack (2.2.0) lib/rack/attack.rb:91:in instrument' rack-attack (2.2.0) lib/rack/attack/throttle.rb:37:in
block in []'
rack-attack (2.2.0) lib/rack/attack/throttle.rb:32:in []' rack-attack (2.2.0) lib/rack/attack.rb:80:in
block in throttled?'
rack-attack (2.2.0) lib/rack/attack.rb:79:in throttled?' rack-attack (2.2.0) lib/rack/attack.rb:58:in
call'
galetahub-simple_captcha (0.1.5) lib/simple_captcha/middleware.rb:20:in call' warden (1.2.1) lib/warden/manager.rb:35:in
block 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:in
call'
rack (1.4.5) lib/rack/etag.rb:23:in call' rack (1.4.5) lib/rack/conditionalget.rb:25:in
call'
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:in
call'
actionpack (3.2.13) lib/action_dispatch/middleware/flash.rb:242:in call' rack (1.4.5) lib/rack/session/abstract/id.rb:210:in
context'
rack (1.4.5) lib/rack/session/abstract/id.rb:205:in call' actionpack (3.2.13) lib/action_dispatch/middleware/cookies.rb:341:in
call'
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:in
call'
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:in
call'
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:in
call'
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:in
better_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:in
call'
actionpack (3.2.13) lib/action_dispatch/middleware/show_exceptions.rb:56:in call' railties (3.2.13) lib/rails/rack/logger.rb:32:in
call_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:in
tagged'
railties (3.2.13) lib/rails/rack/logger.rb:16:in call' config/initializers/logging.rb:6:in
call_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:in
call'
rack (1.4.5) lib/rack/runtime.rb:17:in call' activesupport (3.2.13) lib/active_support/cache/strategy/local_cache.rb:72:in
call'
rack (1.4.5) lib/rack/lock.rb:15:in call' actionpack (3.2.13) lib/action_dispatch/middleware/static.rb:63:in
call'
railties (3.2.13) lib/rails/engine.rb:479:in call' railties (3.2.13) lib/rails/application.rb:223:in
call'
rack (1.4.5) lib/rack/content_length.rb:14:in call' railties (3.2.13) lib/rails/rack/log_tailer.rb:17:in
call'
thin (1.5.1) lib/thin/connection.rb:81:in block in pre_process' thin (1.5.1) lib/thin/connection.rb:79:in
pre_process'
thin (1.5.1) lib/thin/connection.rb:54:in process' thin (1.5.1) lib/thin/connection.rb:39:in
receive_data'
eventmachine (1.0.3) lib/eventmachine.rb:187:in run' thin (1.5.1) lib/thin/backends/base.rb:63:in
start'
thin (1.5.1) lib/thin/server.rb:159:in start' rack (1.4.5) lib/rack/handler/thin.rb:13:in
run'
rack (1.4.5) lib/rack/server.rb:268:in start' railties (3.2.13) lib/rails/commands/server.rb:70:in
start'
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
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.
Let's add the option to receive email notifications about new jobs posted on Dribbble from this page:
http://dribbble.com/account/notifications
Let's support options for 1) all jobs and 2) jobs in a user's location.
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
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:in
read'
/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:in
block (5 levels) in <top (required)>'
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:in
read'
/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:in
block (5 levels) in <top (required)>'
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
I'm using the configuration found here: https://github.com/kickstarter/rack-attack/wiki/Example-Configuration
I'm beging throttled after clicking around a few links in my app. Doesn't seem right. Any ideas?
It would be really awesome if Rack::Attack supported exponential backoff so as people continue to misbehave, their ban times increase (maybe up to some max time).
This article has some useful thoughts and tables about it:
https://devcentral.f5.com/articles/implementing-the-exponential-backoff-algorithm-to-thwart-dictionary-attacks
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 😄
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.