diff options
author | Michael Kozono <mkozono@gmail.com> | 2017-09-15 10:31:32 -0700 |
---|---|---|
committer | Francisco Lopez <fjlopez@gitlab.com> | 2017-11-17 09:58:18 +0100 |
commit | dc9266fbeacd24446b52e4dad328c8286be40b31 (patch) | |
tree | f882ee2eac622aa6daafa71d80663d6a5014d3b9 | |
parent | 732b122644bf56729996b3cc239453f537a798f4 (diff) | |
download | gitlab-ce-dc9266fbeacd24446b52e4dad328c8286be40b31.tar.gz |
Add request throttles
-rw-r--r-- | app/controllers/application_controller.rb | 12 | ||||
-rw-r--r-- | app/helpers/application_settings_helper.rb | 9 | ||||
-rw-r--r-- | app/views/admin/application_settings/_form.html.haml | 51 | ||||
-rw-r--r-- | changelogs/unreleased/mk-add-user-rate-limits.yml | 6 | ||||
-rw-r--r-- | config/application.rb | 2 | ||||
-rw-r--r-- | config/initializers/rack_attack_global.rb | 73 | ||||
-rw-r--r-- | lib/gitlab/auth.rb | 30 |
7 files changed, 180 insertions, 3 deletions
diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb index 3be7aee69bc..42eae408fdc 100644 --- a/app/controllers/application_controller.rb +++ b/app/controllers/application_controller.rb @@ -11,8 +11,7 @@ class ApplicationController < ActionController::Base include EnforcesTwoFactorAuthentication include WithPerformanceBar - before_action :authenticate_user_from_personal_access_token! - before_action :authenticate_user_from_rss_token! + before_action :authenticate_sessionless_user! before_action :authenticate_user! before_action :validate_user_service_ticket! before_action :check_password_expiration @@ -100,6 +99,7 @@ class ApplicationController < ActionController::Base return try(:authenticated_user) end +<<<<<<< HEAD def authenticate_user_from_personal_access_token! token = params[:private_token].presence || request.headers['PRIVATE-TOKEN'].presence @@ -121,6 +121,14 @@ class ApplicationController < ActionController::Base user = User.find_by_rss_token(token) sessionless_sign_in(user) +======= + # This filter handles private tokens, personal access tokens, and atom + # requests with rss tokens + def authenticate_sessionless_user! + user = Gitlab::Auth.find_sessionless_user(request) + + sessionless_sign_in(user) if user +>>>>>>> Add request throttles end def log_exception(exception) diff --git a/app/helpers/application_settings_helper.rb b/app/helpers/application_settings_helper.rb index cd1ecaadb85..e5d2693b01e 100644 --- a/app/helpers/application_settings_helper.rb +++ b/app/helpers/application_settings_helper.rb @@ -231,6 +231,15 @@ module ApplicationSettingsHelper :sign_in_text, :signup_enabled, :terminal_max_session_time, + :throttle_unauthenticated_enabled, + :throttle_unauthenticated_requests_per_period, + :throttle_unauthenticated_period_in_seconds, + :throttle_authenticated_web_enabled, + :throttle_authenticated_web_requests_per_period, + :throttle_authenticated_web_period_in_seconds, + :throttle_authenticated_api_enabled, + :throttle_authenticated_api_requests_per_period, + :throttle_authenticated_api_period_in_seconds, :two_factor_grace_period, :unique_ips_limit_enabled, :unique_ips_limit_per_user, diff --git a/app/views/admin/application_settings/_form.html.haml b/app/views/admin/application_settings/_form.html.haml index 3a4d5ce0b5c..12658dddc06 100644 --- a/app/views/admin/application_settings/_form.html.haml +++ b/app/views/admin/application_settings/_form.html.haml @@ -743,5 +743,56 @@ installations. Set to 0 to completely disable polling. = link_to icon('question-circle'), help_page_path('administration/polling') + %fieldset + %legend User and IP Rate Limits + .form-group + .col-sm-offset-2.col-sm-10 + .checkbox + = f.label :throttle_unauthenticated_enabled do + = f.check_box :throttle_unauthenticated_enabled + Enable unauthenticated request rate limit + %span.help-block + Helps reduce request volume (e.g. from crawlers or abusive bots) + .form-group + = f.label :throttle_unauthenticated_requests_per_period, 'Max requests per period per IP', class: 'control-label col-sm-2' + .col-sm-10 + = f.number_field :throttle_unauthenticated_requests_per_period, class: 'form-control' + .form-group + = f.label :throttle_unauthenticated_period_in_seconds, 'Rate limit period in seconds', class: 'control-label col-sm-2' + .col-sm-10 + = f.number_field :throttle_unauthenticated_period_in_seconds, class: 'form-control' + .form-group + .col-sm-offset-2.col-sm-10 + .checkbox + = f.label :throttle_authenticated_api_enabled do + = f.check_box :throttle_authenticated_api_enabled + Enable authenticated API request rate limit + %span.help-block + Helps reduce request volume (e.g. from crawlers or abusive bots) + .form-group + = f.label :throttle_authenticated_api_requests_per_period, 'Max requests per period per user', class: 'control-label col-sm-2' + .col-sm-10 + = f.number_field :throttle_authenticated_api_requests_per_period, class: 'form-control' + .form-group + = f.label :throttle_authenticated_api_period_in_seconds, 'Rate limit period in seconds', class: 'control-label col-sm-2' + .col-sm-10 + = f.number_field :throttle_authenticated_api_period_in_seconds, class: 'form-control' + .form-group + .col-sm-offset-2.col-sm-10 + .checkbox + = f.label :throttle_authenticated_web_enabled do + = f.check_box :throttle_authenticated_web_enabled + Enable authenticated web request rate limit + %span.help-block + Helps reduce request volume (e.g. from crawlers or abusive bots) + .form-group + = f.label :throttle_authenticated_web_requests_per_period, 'Max requests per period per user', class: 'control-label col-sm-2' + .col-sm-10 + = f.number_field :throttle_authenticated_web_requests_per_period, class: 'form-control' + .form-group + = f.label :throttle_authenticated_web_period_in_seconds, 'Rate limit period in seconds', class: 'control-label col-sm-2' + .col-sm-10 + = f.number_field :throttle_authenticated_web_period_in_seconds, class: 'form-control' + .form-actions = f.submit 'Save', class: 'btn btn-save' diff --git a/changelogs/unreleased/mk-add-user-rate-limits.yml b/changelogs/unreleased/mk-add-user-rate-limits.yml new file mode 100644 index 00000000000..512757da5fc --- /dev/null +++ b/changelogs/unreleased/mk-add-user-rate-limits.yml @@ -0,0 +1,6 @@ +--- +title: Add anonymous rate limit per IP, and authenticated (web or API) rate limits + per user +merge_request: 14708 +author: +type: added diff --git a/config/application.rb b/config/application.rb index 5100ec5d2b7..6436f887d14 100644 --- a/config/application.rb +++ b/config/application.rb @@ -113,7 +113,7 @@ module Gitlab config.action_view.sanitized_allowed_protocols = %w(smb) - config.middleware.insert_before Warden::Manager, Rack::Attack + config.middleware.insert_after Warden::Manager, Rack::Attack # Allow access to GitLab API from other domains config.middleware.insert_before Warden::Manager, Rack::Cors do diff --git a/config/initializers/rack_attack_global.rb b/config/initializers/rack_attack_global.rb new file mode 100644 index 00000000000..0b51fadbd02 --- /dev/null +++ b/config/initializers/rack_attack_global.rb @@ -0,0 +1,73 @@ +class Rack::Attack + def self.settings + Gitlab::CurrentSettings.current_application_settings + end + + def self.throttle_unauthenticated_options + limit_proc = proc { |req| settings.throttle_unauthenticated_requests_per_period } + period_proc = proc { |req| settings.throttle_unauthenticated_period_in_seconds.seconds } + { limit: limit_proc, period: period_proc } + end + + def self.throttle_authenticated_api_options + limit_proc = proc { |req| settings.throttle_authenticated_api_requests_per_period } + period_proc = proc { |req| settings.throttle_authenticated_api_period_in_seconds.seconds } + { limit: limit_proc, period: period_proc } + end + + def self.throttle_authenticated_web_options + limit_proc = proc { |req| settings.throttle_authenticated_web_requests_per_period } + period_proc = proc { |req| settings.throttle_authenticated_web_period_in_seconds.seconds } + { limit: limit_proc, period: period_proc } + end + + def self.define_throttles + throttle('throttle_unauthenticated', throttle_unauthenticated_options) do |req| + settings.throttle_unauthenticated_enabled && + req.unauthenticated? && + req.ip + end + + throttle('throttle_authenticated_api', throttle_authenticated_api_options) do |req| + settings.throttle_authenticated_api_enabled && + req.api_request? && + req.authenticated_user_id + end + + throttle('throttle_authenticated_web', throttle_authenticated_web_options) do |req| + settings.throttle_authenticated_web_enabled && + req.web_request? && + req.authenticated_user_id + end + end + + define_throttles unless Rails.env.test? + + class Request + def unauthenticated? + !authenticated_user_id + end + + def authenticated_user_id + session_user_id || sessionless_user_id + end + + def api_request? + path.start_with?('/api') + end + + def web_request? + !api_request? + end + + private + + def session_user_id + Gitlab::Auth.find_session_user(self)&.id + end + + def sessionless_user_id + Gitlab::Auth.find_sessionless_user(self)&.id + end + end +end diff --git a/lib/gitlab/auth.rb b/lib/gitlab/auth.rb index cbbc51db99e..8f39885c608 100644 --- a/lib/gitlab/auth.rb +++ b/lib/gitlab/auth.rb @@ -82,6 +82,36 @@ module Gitlab end end + # request may be Rack::Attack::Request which is just a Rack::Request, so + # we cannot use ActionDispatch::Request methods. + def find_user_by_private_token(request) + token = request.params['private_token'].presence || request.env['HTTP_PRIVATE_TOKEN'].presence + + return unless token.present? + + User.find_by_authentication_token(token) || User.find_by_personal_access_token(token) + end + + # request may be Rack::Attack::Request which is just a Rack::Request, so + # we cannot use ActionDispatch::Request methods. + def find_user_by_rss_token(request) + return unless request.params['format'] == 'atom' + + token = request.params['rss_token'].presence + + return unless token.present? + + User.find_by_rss_token(token) + end + + def find_session_user(request) + request.env['warden']&.authenticate + end + + def find_sessionless_user(request) + find_user_by_private_token(request) || find_user_by_rss_token(request) + end + private def service_request_check(login, password, project) |