diff options
author | GitLab Bot <gitlab-bot@gitlab.com> | 2020-12-17 14:59:07 +0300 |
---|---|---|
committer | GitLab Bot <gitlab-bot@gitlab.com> | 2020-12-17 14:59:07 +0300 |
commit | 8b573c94895dc0ac0e1d9d59cf3e8745e8b539ca (patch) | |
tree | 544930fb309b30317ae9797a9683768705d664c4 /lib/gitlab/rack_attack.rb | |
parent | 4b1de649d0168371549608993deac953eb692019 (diff) |
Add latest changes from gitlab-org/gitlab@13-7-stable-eev13.7.0-rc42
Diffstat (limited to 'lib/gitlab/rack_attack.rb')
-rw-r--r-- | lib/gitlab/rack_attack.rb | 118 |
1 files changed, 118 insertions, 0 deletions
diff --git a/lib/gitlab/rack_attack.rb b/lib/gitlab/rack_attack.rb new file mode 100644 index 00000000000..7c336153e32 --- /dev/null +++ b/lib/gitlab/rack_attack.rb @@ -0,0 +1,118 @@ +# frozen_string_literal: true + +# When adding new user-configurable throttles, remember to update the documentation +# in doc/user/admin_area/settings/user_and_ip_rate_limits.md +# +# Integration specs for throttling can be found in: +# spec/requests/rack_attack_global_spec.rb +module Gitlab + module RackAttack + def self.configure(rack_attack) + # This adds some methods used by our throttles to the `Rack::Request` + rack_attack::Request.include(Gitlab::RackAttack::Request) + # Send the Retry-After header so clients (e.g. python-gitlab) can make good choices about delays + Rack::Attack.throttled_response_retry_after_header = true + # Configure the throttles + configure_throttles(rack_attack) + + configure_user_allowlist + end + + def self.configure_user_allowlist + @user_allowlist = nil + user_allowlist + end + + def self.configure_throttles(rack_attack) + throttle_or_track(rack_attack, 'throttle_unauthenticated', Gitlab::Throttle.unauthenticated_options) do |req| + if !req.should_be_skipped? && + Gitlab::Throttle.settings.throttle_unauthenticated_enabled && + req.unauthenticated? + req.ip + end + end + + throttle_or_track(rack_attack, 'throttle_authenticated_api', Gitlab::Throttle.authenticated_api_options) do |req| + if req.api_request? && + Gitlab::Throttle.settings.throttle_authenticated_api_enabled + req.throttled_user_id([:api]) + end + end + + # Product analytics feature is in experimental stage. + # At this point we want to limit amount of events registered + # per application (aid stands for application id). + throttle_or_track(rack_attack, 'throttle_product_analytics_collector', limit: 100, period: 60) do |req| + if req.product_analytics_collector_request? + req.params['aid'] + end + end + + throttle_or_track(rack_attack, 'throttle_authenticated_web', Gitlab::Throttle.authenticated_web_options) do |req| + if req.web_request? && + Gitlab::Throttle.settings.throttle_authenticated_web_enabled + req.throttled_user_id([:api, :rss, :ics]) + end + end + + throttle_or_track(rack_attack, 'throttle_unauthenticated_protected_paths', Gitlab::Throttle.protected_paths_options) do |req| + if req.post? && + !req.should_be_skipped? && + req.protected_path? && + Gitlab::Throttle.protected_paths_enabled? && + req.unauthenticated? + req.ip + end + end + + throttle_or_track(rack_attack, 'throttle_authenticated_protected_paths_api', Gitlab::Throttle.protected_paths_options) do |req| + if req.post? && + req.api_request? && + req.protected_path? && + Gitlab::Throttle.protected_paths_enabled? + req.throttled_user_id([:api]) + end + end + + throttle_or_track(rack_attack, 'throttle_authenticated_protected_paths_web', Gitlab::Throttle.protected_paths_options) do |req| + if req.post? && + req.web_request? && + req.protected_path? && + Gitlab::Throttle.protected_paths_enabled? + req.throttled_user_id([:api, :rss, :ics]) + end + end + + rack_attack.safelist('throttle_bypass_header') do |req| + Gitlab::Throttle.bypass_header.present? && + req.get_header(Gitlab::Throttle.bypass_header) == '1' + end + end + + def self.throttle_or_track(rack_attack, throttle_name, *args, &block) + if track?(throttle_name) + rack_attack.track(throttle_name, *args, &block) + else + rack_attack.throttle(throttle_name, *args, &block) + end + end + + def self.track?(name) + dry_run_config = ENV['GITLAB_THROTTLE_DRY_RUN'].to_s.strip + + return false if dry_run_config.empty? + return true if dry_run_config == '*' + + dry_run_config.split(',').map(&:strip).include?(name) + end + + def self.user_allowlist + @user_allowlist ||= begin + list = UserAllowlist.new(ENV['GITLAB_THROTTLE_USER_ALLOWLIST']) + Gitlab::AuthLogger.info(gitlab_throttle_user_allowlist: list.to_a) + list + end + end + end +end +::Gitlab::RackAttack.prepend_if_ee('::EE::Gitlab::RackAttack') |