Add Rack::Attack Gem to Throttle Requests, Only Search to start (#6841) [deploy]

* throttle requests made to search endpoints

* stub elasticsearch requests in rack attack specs and disable for running specs unless tagged

* use original client IP from fastly to throttle requests to search endpoints
This commit is contained in:
Molly Struve 2020-03-30 11:09:40 -05:00 committed by GitHub
parent 4baa9d3165
commit 55fe5a40a8
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 45 additions and 0 deletions

View file

@ -72,6 +72,7 @@ gem "puma", "~> 4.3" # Puma is a simple, fast, threaded, and highly concurrent H
gem "pundit", "~> 2.1" # Object oriented authorization for Rails applications
gem "pusher", "~> 1.3" # Ruby library for Pusher Channels HTTP API
gem "pusher-push-notifications", "~> 1.1" # Pusher Push Notifications Ruby server SDK
gem "rack-attack", "~> 6.2.2" # Used to throttle requests to prevent brute force attacks
gem "rack-cors", "~> 1.1" # Middleware that will make Rack-based apps CORS compatible
gem "rack-timeout", "~> 0.6" # Rack middleware which aborts requests that have been running for longer than a specified timeout
gem "rails", "~> 5.2" # Ruby on Rails

View file

@ -568,6 +568,8 @@ GEM
rest-client (~> 2.0, >= 2.0.2)
pusher-signature (0.1.8)
rack (2.2.2)
rack-attack (6.2.2)
rack (>= 1.0, < 3)
rack-cors (1.1.1)
rack (>= 2.0.0)
rack-host-redirect (1.3.0)
@ -958,6 +960,7 @@ DEPENDENCIES
pundit-matchers (~> 1.6)
pusher (~> 1.3)
pusher-push-notifications (~> 1.1)
rack-attack (~> 6.2.2)
rack-cors (~> 1.1)
rack-host-redirect (~> 1.3)
rack-timeout (~> 0.6)

View file

@ -0,0 +1,7 @@
class Rack::Attack
throttle("search_throttle", limit: 5, period: 1) do |request|
if request.path.starts_with?("/search/") && request.env["HTTP_FASTLY_CLIENT_IP"].present?
request.env["HTTP_FASTLY_CLIENT_IP"].to_s
end
end
end

View file

@ -0,0 +1,26 @@
require "rails_helper"
describe Rack::Attack, type: :request, throttle: true do
describe "search_throttle" do
before do
redis_url = "redis://localhost:6379"
cache_db = ActiveSupport::Cache::RedisStore.new(redis_url)
allow(Rails).to receive(:cache) { cache_db }
end
it "throttles /search endpoints based on IP" do
Timecop.freeze do
allow(Search::User).to receive(:search_documents).and_return({})
valid_responses = Array.new(5).map do
get "/search/users", headers: { "HTTP_FASTLY_CLIENT_IP" => "5.6.7.8" }
end
throttled_response = get "/search/users", headers: { "HTTP_FASTLY_CLIENT_IP" => "5.6.7.8" }
new_ip_response = get "/search/users", headers: { "HTTP_FASTLY_CLIENT_IP" => "1.1.1.1" }
valid_responses.each { |r| expect(r).not_to eq(429) }
expect(throttled_response).to eq(429)
expect(new_ip_response).not_to eq(429)
end
end
end
end

View file

@ -53,6 +53,8 @@ WebMock.disable_net_connect!(allow_localhost: true, allow: allowed_sites)
RSpec::Matchers.define_negated_matcher :not_change, :change
Rack::Attack.enabled = false
RSpec.configure do |config|
config.use_transactional_fixtures = true
config.fixture_path = "#{::Rails.root}/spec/fixtures"
@ -77,6 +79,12 @@ RSpec.configure do |config|
example.run
end
config.around(:each, throttle: true) do |example|
Rack::Attack.enabled = true
example.run
Rack::Attack.enabled = false
end
config.after do
SiteConfig.clear_cache
end