Skip to content

Commit

Permalink
fix: Update Rack Attack based on decidim-app
Browse files Browse the repository at this point in the history
  • Loading branch information
Quentinchampenois committed Oct 19, 2023
1 parent 3823ce2 commit 8e78c5c
Show file tree
Hide file tree
Showing 3 changed files with 83 additions and 24 deletions.
11 changes: 10 additions & 1 deletion config/initializers/rack_attack.rb
Original file line number Diff line number Diff line change
Expand Up @@ -6,4 +6,13 @@

# Enabled by default in production
# Can be deactivated with 'ENABLE_RACK_ATTACK=0'
DecidimApp::RackAttack.apply_configuration if DecidimApp::RackAttack.rack_enabled?
DecidimApp::RackAttack.deactivate_decidim_throttling!

if DecidimApp::RackAttack.rack_enabled?
DecidimApp::RackAttack.enable_rack_attack!
DecidimApp::RackAttack.apply_configuration
else
DecidimApp::RackAttack.disable_rack_attack!
end

DecidimApp::RackAttack.info!
24 changes: 21 additions & 3 deletions lib/decidim_app/rack_attack.rb
Original file line number Diff line number Diff line change
Expand Up @@ -4,21 +4,39 @@ module DecidimApp
module RackAttack
def self.rack_enabled?
setting = Rails.application.secrets.dig(:decidim, :rack_attack, :enabled)
return setting == "1" if setting.present?
return setting.to_s == "1" if setting.present?

Rails.env.production?
end

def self.apply_configuration
def self.info!
Rails.logger.info("Rack::Attack is enabled: #{Rack::Attack.enabled}")
Rails.logger.info("Rack::Attack Fail2ban is enabled: #{DecidimApp::RackAttack::Fail2ban.enabled?}")
Rack::Attack.throttles.keys.each do |throttle|
Rails.logger.info("Rack::Attack throttling registered: #{throttle}")
end
end

def self.enable_rack_attack!
Rails.logger.info("Rack::Attack is now enabled")
Rack::Attack.enabled = true
end

def self.disable_rack_attack!
Rails.logger.info("Rack::Attack is now disabled")
Rack::Attack.enabled = false
end

def self.deactivate_decidim_throttling!
# Remove the original throttle from decidim-core
# see https://github.com/decidim/decidim/blob/release/0.26-stable/decidim-core/config/initializers/rack_attack.rb#L19
# see https://github.com/decidim/decidim/blob/release/0.27-stable/decidim-core/config/initializers/rack_attack.rb#L19
DecidimApp::RackAttack::Throttling.deactivate_decidim_throttling! do
Rails.logger.info("Deactivating 'requests by ip' from Decidim Core")
Rack::Attack.throttles.delete("requests by ip")
end
end

def self.apply_configuration
Rack::Attack.throttled_response_retry_after_header = true

Rack::Attack.throttled_responder = lambda do |request|
Expand Down
72 changes: 52 additions & 20 deletions spec/lib/decidim_app/rack_attack_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -62,40 +62,66 @@
end
end

describe "#apply_configuration" do
describe "#enable_rack_attack!" do
before do
described_class.enable_rack_attack!
end

it "enables Rack::Attack" do
expect(Rack::Attack.enabled).to be_truthy
end
end

describe "#disable_rack_attack!" do
before do
described_class.disable_rack_attack!
end

it "enables Rack::Attack" do
expect(Rack::Attack.enabled).to be_falsey
end
end

describe "#deactivate_decidim_throttling!" do
before do
described_class.apply_configuration
Rack::Attack.reset!
described_class.deactivate_decidim_throttling!
end

it "deactivates Decidim throttling" do
# Decidim throttling is deactivated by default in rails env test
# https://github.com/decidim/decidim/blob/release/0.26-stable/decidim-core/config/initializers/rack_attack.rb#L19
expect(Rack::Attack.throttles.keys).to eq([])
end
end

describe "#apply_configuration" do
describe "Throttling" do
let(:headers) { { "REMOTE_ADDR" => "1.2.3.4", "decidim.current_organization" => organization } }
let(:rack_max_requests) { 15 }

it "successful for 100 requests, then blocks the user" do
100.times do
get decidim.root_path, params: {}, headers: headers
expect(response).to have_http_status(:ok)
end

get decidim.root_path, params: {}, headers: headers
expect(response).to have_http_status(:too_many_requests)
expect(response.body).to include("Your connection has been slowed because server received too many requests.")
before do
allow(Rails.application.secrets).to receive(:dig).with(any_args).and_call_original
allow(Rails.application.secrets).to receive(:dig).with(:decidim, :rack_attack, :throttle, :max_requests).and_return(rack_max_requests)
described_class.apply_configuration
Rack::Attack.reset!
described_class.enable_rack_attack!
end

travel_to(1.minute.from_now) do
get decidim.root_path, params: {}, headers: headers
expect(response).to have_http_status(:ok)
end
it "defines default period and max_requests" do
expect(DecidimApp::RackAttack::Throttling.max_requests).to eq(rack_max_requests)
expect(DecidimApp::RackAttack::Throttling.period).to eq(60)
end

it "successful for 99 requests" do
99.times do
it "successful for 15 requests, then blocks the user" do
rack_max_requests.times do
get decidim.root_path, params: {}, headers: headers
expect(response).to have_http_status(:ok)
expect(response.body).not_to include("Your connection has been slowed because server received too many requests.")
end

get decidim.root_path, params: {}, headers: headers
expect(response.body).not_to include("Your connection has been slowed because server received too many requests.")
expect(response).not_to have_http_status(:too_many_requests)
expect(response).to have_http_status(:too_many_requests)
expect(response.body).to include("Your connection has been slowed because server received too many requests.")

travel_to(1.minute.from_now) do
get decidim.root_path, params: {}, headers: headers
Expand All @@ -107,6 +133,12 @@
describe "Fail2Ban" do
let(:headers) { { "REMOTE_ADDR" => "1.2.3.4", "decidim.current_organization" => organization } }

before do
described_class.apply_configuration
Rack::Attack.reset!
described_class.enable_rack_attack!
end

%w(/etc/passwd /wp-admin/index.php /wp-login/index.php SELECT CONCAT /.git/config).each do |path|
it "blocks user for specific request : '#{path}'" do
get "#{decidim.root_path}#{path}", params: {}, headers: headers
Expand Down

0 comments on commit 8e78c5c

Please sign in to comment.