debian-mirror-gitlab/spec/requests/rack_attack_global_spec.rb

382 lines
14 KiB
Ruby
Raw Normal View History

2019-12-26 22:10:19 +05:30
# frozen_string_literal: true
2018-03-17 18:26:18 +05:30
require 'spec_helper'
2020-07-28 23:09:34 +05:30
RSpec.describe 'Rack Attack global throttles' do
2019-12-21 20:55:43 +05:30
include RackAttackSpecHelpers
2018-03-17 18:26:18 +05:30
let(:settings) { Gitlab::CurrentSettings.current_application_settings }
# Start with really high limits and override them with low limits to ensure
# the right settings are being exercised
let(:settings_to_set) do
{
throttle_unauthenticated_requests_per_period: 100,
throttle_unauthenticated_period_in_seconds: 1,
throttle_authenticated_api_requests_per_period: 100,
throttle_authenticated_api_period_in_seconds: 1,
throttle_authenticated_web_requests_per_period: 100,
2019-12-21 20:55:43 +05:30
throttle_authenticated_web_period_in_seconds: 1,
throttle_authenticated_protected_paths_request_per_period: 100,
throttle_authenticated_protected_paths_in_seconds: 1
2018-03-17 18:26:18 +05:30
}
end
2019-12-21 20:55:43 +05:30
let(:request_method) { 'GET' }
2018-03-17 18:26:18 +05:30
let(:requests_per_period) { 1 }
let(:period_in_seconds) { 10000 }
let(:period) { period_in_seconds.seconds }
2019-12-21 20:55:43 +05:30
include_context 'rack attack cache store'
2018-03-17 18:26:18 +05:30
describe 'unauthenticated requests' do
2019-12-04 20:38:33 +05:30
let(:url_that_does_not_require_authentication) { '/users/sign_in' }
let(:url_api_internal) { '/api/v4/internal/check' }
2018-03-17 18:26:18 +05:30
before do
2019-12-21 20:55:43 +05:30
# Disabling protected paths throttle, otherwise requests to
# '/users/sign_in' are caught by this throttle.
settings_to_set[:throttle_protected_paths_enabled] = false
2018-03-17 18:26:18 +05:30
# Set low limits
settings_to_set[:throttle_unauthenticated_requests_per_period] = requests_per_period
settings_to_set[:throttle_unauthenticated_period_in_seconds] = period_in_seconds
end
context 'when the throttle is enabled' do
before do
settings_to_set[:throttle_unauthenticated_enabled] = true
stub_application_setting(settings_to_set)
end
it 'rejects requests over the rate limit' do
# At first, allow requests under the rate limit.
requests_per_period.times do
get url_that_does_not_require_authentication
2020-03-13 15:44:24 +05:30
expect(response).to have_gitlab_http_status(:ok)
2018-03-17 18:26:18 +05:30
end
# the last straw
expect_rejection { get url_that_does_not_require_authentication }
end
it 'allows requests after throttling and then waiting for the next period' do
requests_per_period.times do
get url_that_does_not_require_authentication
2020-03-13 15:44:24 +05:30
expect(response).to have_gitlab_http_status(:ok)
2018-03-17 18:26:18 +05:30
end
expect_rejection { get url_that_does_not_require_authentication }
2021-01-03 14:25:43 +05:30
travel_to(period.from_now) do
2018-03-17 18:26:18 +05:30
requests_per_period.times do
get url_that_does_not_require_authentication
2020-03-13 15:44:24 +05:30
expect(response).to have_gitlab_http_status(:ok)
2018-03-17 18:26:18 +05:30
end
expect_rejection { get url_that_does_not_require_authentication }
end
end
it 'counts requests from different IPs separately' do
requests_per_period.times do
get url_that_does_not_require_authentication
2020-03-13 15:44:24 +05:30
expect(response).to have_gitlab_http_status(:ok)
2018-03-17 18:26:18 +05:30
end
2020-01-01 13:55:28 +05:30
expect_next_instance_of(Rack::Attack::Request) do |instance|
expect(instance).to receive(:ip).at_least(:once).and_return('1.2.3.4')
end
2018-03-17 18:26:18 +05:30
# would be over limit for the same IP
get url_that_does_not_require_authentication
2020-03-13 15:44:24 +05:30
expect(response).to have_gitlab_http_status(:ok)
2018-03-17 18:26:18 +05:30
end
2018-03-27 19:54:05 +05:30
context 'when the request is to the api internal endpoints' do
it 'allows requests over the rate limit' do
(1 + requests_per_period).times do
2019-02-15 15:39:39 +05:30
get url_api_internal, params: { secret_token: Gitlab::Shell.secret_token }
2020-03-13 15:44:24 +05:30
expect(response).to have_gitlab_http_status(:ok)
2018-03-27 19:54:05 +05:30
end
end
end
2019-09-04 21:01:54 +05:30
2020-01-01 13:55:28 +05:30
context 'when the request is authenticated by a runner token' do
let(:request_jobs_url) { '/api/v4/jobs/request' }
let(:runner) { create(:ci_runner) }
it 'does not cont as unauthenticated' do
(1 + requests_per_period).times do
post request_jobs_url, params: { token: runner.token }
2020-03-13 15:44:24 +05:30
expect(response).to have_gitlab_http_status(:no_content)
2020-01-01 13:55:28 +05:30
end
end
end
2019-09-04 21:01:54 +05:30
it 'logs RackAttack info into structured logs' do
requests_per_period.times do
get url_that_does_not_require_authentication
2020-03-13 15:44:24 +05:30
expect(response).to have_gitlab_http_status(:ok)
2019-09-04 21:01:54 +05:30
end
2019-09-30 21:07:59 +05:30
arguments = {
message: 'Rack_Attack',
env: :throttle,
2019-12-04 20:38:33 +05:30
remote_ip: '127.0.0.1',
2019-09-30 21:07:59 +05:30
request_method: 'GET',
2021-01-29 00:20:46 +05:30
path: '/users/sign_in',
matched: 'throttle_unauthenticated'
2019-09-30 21:07:59 +05:30
}
expect(Gitlab::AuthLogger).to receive(:error).with(arguments)
2019-09-04 21:01:54 +05:30
get url_that_does_not_require_authentication
end
2018-03-17 18:26:18 +05:30
end
context 'when the throttle is disabled' do
before do
settings_to_set[:throttle_unauthenticated_enabled] = false
stub_application_setting(settings_to_set)
end
it 'allows requests over the rate limit' do
(1 + requests_per_period).times do
get url_that_does_not_require_authentication
2020-03-13 15:44:24 +05:30
expect(response).to have_gitlab_http_status(:ok)
2018-03-17 18:26:18 +05:30
end
end
end
end
describe 'API requests authenticated with personal access token', :api do
let(:user) { create(:user) }
let(:token) { create(:personal_access_token, user: user) }
let(:other_user) { create(:user) }
let(:other_user_token) { create(:personal_access_token, user: other_user) }
let(:throttle_setting_prefix) { 'throttle_authenticated_api' }
2019-12-04 20:38:33 +05:30
let(:api_partial_url) { '/todos' }
2018-03-17 18:26:18 +05:30
context 'with the token in the query string' do
2019-12-21 20:55:43 +05:30
let(:request_args) { [api(api_partial_url, personal_access_token: token)] }
let(:other_user_request_args) { [api(api_partial_url, personal_access_token: other_user_token)] }
2018-03-17 18:26:18 +05:30
it_behaves_like 'rate-limited token-authenticated requests'
end
context 'with the token in the headers' do
2019-12-21 20:55:43 +05:30
let(:request_args) { api_get_args_with_token_headers(api_partial_url, personal_access_token_headers(token)) }
let(:other_user_request_args) { api_get_args_with_token_headers(api_partial_url, personal_access_token_headers(other_user_token)) }
2018-03-17 18:26:18 +05:30
it_behaves_like 'rate-limited token-authenticated requests'
end
end
describe 'API requests authenticated with OAuth token', :api do
let(:user) { create(:user) }
let(:application) { Doorkeeper::Application.create!(name: "MyApp", redirect_uri: "https://app.com", owner: user) }
let(:token) { Doorkeeper::AccessToken.create!(application_id: application.id, resource_owner_id: user.id, scopes: "api") }
2019-12-04 20:38:33 +05:30
2018-03-17 18:26:18 +05:30
let(:other_user) { create(:user) }
let(:other_user_application) { Doorkeeper::Application.create!(name: "MyApp", redirect_uri: "https://app.com", owner: other_user) }
let(:other_user_token) { Doorkeeper::AccessToken.create!(application_id: application.id, resource_owner_id: other_user.id, scopes: "api") }
2019-12-04 20:38:33 +05:30
2018-03-17 18:26:18 +05:30
let(:throttle_setting_prefix) { 'throttle_authenticated_api' }
2019-12-04 20:38:33 +05:30
let(:api_partial_url) { '/todos' }
2018-03-17 18:26:18 +05:30
context 'with the token in the query string' do
2019-12-21 20:55:43 +05:30
let(:request_args) { [api(api_partial_url, oauth_access_token: token)] }
let(:other_user_request_args) { [api(api_partial_url, oauth_access_token: other_user_token)] }
2018-03-17 18:26:18 +05:30
it_behaves_like 'rate-limited token-authenticated requests'
end
context 'with the token in the headers' do
2019-12-21 20:55:43 +05:30
let(:request_args) { api_get_args_with_token_headers(api_partial_url, oauth_token_headers(token)) }
let(:other_user_request_args) { api_get_args_with_token_headers(api_partial_url, oauth_token_headers(other_user_token)) }
2018-03-17 18:26:18 +05:30
it_behaves_like 'rate-limited token-authenticated requests'
end
end
describe '"web" (non-API) requests authenticated with RSS token' do
let(:user) { create(:user) }
let(:other_user) { create(:user) }
let(:throttle_setting_prefix) { 'throttle_authenticated_web' }
context 'with the token in the query string' do
2019-12-21 20:55:43 +05:30
let(:request_args) { [rss_url(user), params: nil] }
let(:other_user_request_args) { [rss_url(other_user), params: nil] }
2018-03-17 18:26:18 +05:30
it_behaves_like 'rate-limited token-authenticated requests'
end
end
describe 'web requests authenticated with regular login' do
2019-12-04 20:38:33 +05:30
let(:throttle_setting_prefix) { 'throttle_authenticated_web' }
2018-03-17 18:26:18 +05:30
let(:user) { create(:user) }
2019-12-04 20:38:33 +05:30
let(:url_that_requires_authentication) { '/dashboard/snippets' }
2018-03-17 18:26:18 +05:30
2019-12-04 20:38:33 +05:30
it_behaves_like 'rate-limited web authenticated requests'
2018-03-17 18:26:18 +05:30
end
2019-12-21 20:55:43 +05:30
describe 'protected paths' do
let(:request_method) { 'POST' }
2018-03-17 18:26:18 +05:30
2019-12-21 20:55:43 +05:30
context 'unauthenticated requests' do
let(:protected_path_that_does_not_require_authentication) do
'/users/sign_in'
end
2020-10-24 23:57:45 +05:30
2019-12-21 20:55:43 +05:30
let(:post_params) { { user: { login: 'username', password: 'password' } } }
2018-03-17 18:26:18 +05:30
2019-12-21 20:55:43 +05:30
before do
settings_to_set[:throttle_protected_paths_requests_per_period] = requests_per_period # 1
settings_to_set[:throttle_protected_paths_period_in_seconds] = period_in_seconds # 10_000
end
2018-03-17 18:26:18 +05:30
2019-12-21 20:55:43 +05:30
context 'when protected paths throttle is disabled' do
before do
settings_to_set[:throttle_protected_paths_enabled] = false
stub_application_setting(settings_to_set)
end
2018-03-17 18:26:18 +05:30
2019-12-21 20:55:43 +05:30
it 'allows requests over the rate limit' do
(1 + requests_per_period).times do
post protected_path_that_does_not_require_authentication, params: post_params
2020-03-13 15:44:24 +05:30
expect(response).to have_gitlab_http_status(:ok)
2019-12-21 20:55:43 +05:30
end
end
end
context 'when protected paths throttle is enabled' do
before do
settings_to_set[:throttle_protected_paths_enabled] = true
stub_application_setting(settings_to_set)
end
it 'rejects requests over the rate limit' do
requests_per_period.times do
post protected_path_that_does_not_require_authentication, params: post_params
2020-03-13 15:44:24 +05:30
expect(response).to have_gitlab_http_status(:ok)
2019-12-21 20:55:43 +05:30
end
2018-03-17 18:26:18 +05:30
2019-12-21 20:55:43 +05:30
expect_rejection { post protected_path_that_does_not_require_authentication, params: post_params }
end
end
end
context 'API requests authenticated with personal access token', :api do
let(:user) { create(:user) }
let(:token) { create(:personal_access_token, user: user) }
let(:other_user) { create(:user) }
let(:other_user_token) { create(:personal_access_token, user: other_user) }
let(:throttle_setting_prefix) { 'throttle_protected_paths' }
let(:api_partial_url) { '/user/emails' }
let(:protected_paths) do
[
'/api/v4/user/emails'
]
end
before do
settings_to_set[:protected_paths] = protected_paths
stub_application_setting(settings_to_set)
end
context 'with the token in the query string' do
let(:request_args) { [api(api_partial_url, personal_access_token: token)] }
let(:other_user_request_args) { [api(api_partial_url, personal_access_token: other_user_token)] }
it_behaves_like 'rate-limited token-authenticated requests'
end
context 'with the token in the headers' do
let(:request_args) { api_get_args_with_token_headers(api_partial_url, personal_access_token_headers(token)) }
let(:other_user_request_args) { api_get_args_with_token_headers(api_partial_url, personal_access_token_headers(other_user_token)) }
it_behaves_like 'rate-limited token-authenticated requests'
end
end
describe 'web requests authenticated with regular login' do
let(:throttle_setting_prefix) { 'throttle_protected_paths' }
let(:user) { create(:user) }
let(:url_that_requires_authentication) { '/users/confirmation' }
let(:protected_paths) do
[
url_that_requires_authentication
]
end
before do
settings_to_set[:protected_paths] = protected_paths
stub_application_setting(settings_to_set)
end
it_behaves_like 'rate-limited web authenticated requests'
end
2018-03-17 18:26:18 +05:30
end
2021-01-29 00:20:46 +05:30
describe 'throttle bypass header' do
let(:headers) { {} }
let(:bypass_header) { 'gitlab-bypass-rate-limiting' }
def do_request
get '/users/sign_in', headers: headers
end
before do
# Disabling protected paths throttle, otherwise requests to
# '/users/sign_in' are caught by this throttle.
settings_to_set[:throttle_protected_paths_enabled] = false
# Set low limits
settings_to_set[:throttle_unauthenticated_requests_per_period] = requests_per_period
settings_to_set[:throttle_unauthenticated_period_in_seconds] = period_in_seconds
stub_env('GITLAB_THROTTLE_BYPASS_HEADER', bypass_header)
settings_to_set[:throttle_unauthenticated_enabled] = true
stub_application_setting(settings_to_set)
end
shared_examples 'reject requests over the rate limit' do
it 'rejects requests over the rate limit' do
# At first, allow requests under the rate limit.
requests_per_period.times do
do_request
expect(response).to have_gitlab_http_status(:ok)
end
# the last straw
expect_rejection { do_request }
end
end
context 'without the bypass header set' do
it_behaves_like 'reject requests over the rate limit'
end
context 'with bypass header set to 1' do
let(:headers) { { bypass_header => '1' } }
it 'does not throttle' do
(1 + requests_per_period).times do
do_request
expect(response).to have_gitlab_http_status(:ok)
end
end
end
context 'with bypass header set to some other value' do
let(:headers) { { bypass_header => 'some other value' } }
it_behaves_like 'reject requests over the rate limit'
end
end
2018-03-17 18:26:18 +05:30
end