Commit b27cf9d5 authored by Markus Koller's avatar Markus Koller

Merge branch '330133-webhook-rate-limit-free-plan' into 'master'

Add webhook rate-limit threshold for Free plan on gitlab.com

See merge request gitlab-org/gitlab!62918
parents bc1ffbca 5d639f3f
...@@ -91,14 +91,12 @@ class WebHookService ...@@ -91,14 +91,12 @@ class WebHookService
end end
def async_execute def async_execute
if rate_limited?(hook)
log_rate_limit(hook)
else
Gitlab::ApplicationContext.with_context(hook.application_context) do Gitlab::ApplicationContext.with_context(hook.application_context) do
break log_rate_limit if rate_limited?
WebHookWorker.perform_async(hook.id, data, hook_name) WebHookWorker.perform_async(hook.id, data, hook_name)
end end
end end
end
private private
...@@ -177,7 +175,7 @@ class WebHookService ...@@ -177,7 +175,7 @@ class WebHookService
response.body.encode('UTF-8', invalid: :replace, undef: :replace, replace: '') response.body.encode('UTF-8', invalid: :replace, undef: :replace, replace: '')
end end
def rate_limited?(hook) def rate_limited?
return false unless Feature.enabled?(:web_hooks_rate_limit, default_enabled: :yaml) return false unless Feature.enabled?(:web_hooks_rate_limit, default_enabled: :yaml)
return false if rate_limit.nil? return false if rate_limit.nil?
...@@ -192,18 +190,13 @@ class WebHookService ...@@ -192,18 +190,13 @@ class WebHookService
@rate_limit ||= hook.rate_limit @rate_limit ||= hook.rate_limit
end end
def log_rate_limit(hook) def log_rate_limit
payload = { Gitlab::AuthLogger.error(
message: 'Webhook rate limit exceeded', message: 'Webhook rate limit exceeded',
hook_id: hook.id, hook_id: hook.id,
hook_type: hook.type, hook_type: hook.type,
hook_name: hook_name hook_name: hook_name,
} **Gitlab::ApplicationContext.current
)
Gitlab::AuthLogger.error(payload)
# Also log into application log for now, so we can use this information
# to determine suitable limits for gitlab.com
Gitlab::AppLogger.error(payload)
end end
end end
# frozen_string_literal: true
class UpdateWebHookCallsLimit < ActiveRecord::Migration[6.0]
include Gitlab::Database::MigrationHelpers
def up
return unless Gitlab.com?
create_or_update_plan_limit('web_hook_calls', 'free', 120)
end
def down
return unless Gitlab.com?
create_or_update_plan_limit('web_hook_calls', 'free', 0)
end
end
63cd83e097a24b39a399918422950caacb6aed8d05d0d8b7bcf66f9155a0d04e
\ No newline at end of file
...@@ -112,7 +112,7 @@ Limit the maximum daily member invitations allowed per group hierarchy. ...@@ -112,7 +112,7 @@ Limit the maximum daily member invitations allowed per group hierarchy.
- GitLab.com: Free members may invite 20 members per day. - GitLab.com: Free members may invite 20 members per day.
- Self-managed: Invites are not limited. - Self-managed: Invites are not limited.
### Webhook calls ### Webhook rate limit
> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/61151) in GitLab 13.12. > - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/61151) in GitLab 13.12.
> - [Deployed behind a feature flag](../user/feature_flags.md), disabled by default. > - [Deployed behind a feature flag](../user/feature_flags.md), disabled by default.
......
...@@ -153,11 +153,13 @@ content directly from common public CDN hostnames. ...@@ -153,11 +153,13 @@ content directly from common public CDN hostnames.
## Webhooks ## Webhooks
A limit of: The following limits apply for [Webhooks](../project/integrations/webhooks.md):
- 100 webhooks applies to projects. | Setting | GitLab.com | Default |
- 50 webhooks applies to groups. **(BRONZE ONLY)** | ------- | ---------- | ------- |
- Payload is limited to 25MB. | [Webhook rate limit](../../administration/instance_limits.md#webhook-rate-limit) | `120` calls per minute for Free tier, unlimited for all paid tiers | Unlimited
| [Number of webhooks](../../administration/instance_limits.md#number-of-webhooks) | `100` per-project, `50` per-group | `100` per-project, `50` per-group
| Maximum payload size | `25 MB` | `25 MB`
## Shared runners ## Shared runners
......
...@@ -34,8 +34,10 @@ Webhooks are available: ...@@ -34,8 +34,10 @@ Webhooks are available:
- Per project, at a project's **Settings > Webhooks** menu. **(FREE)** - Per project, at a project's **Settings > Webhooks** menu. **(FREE)**
- Additionally per group, at a group's **Settings > Webhooks** menu. **(PREMIUM)** - Additionally per group, at a group's **Settings > Webhooks** menu. **(PREMIUM)**
NOTE: GitLab.com enforces various [webhook limits](../../../user/gitlab_com/index.md#webhooks), including:
On GitLab.com, the [maximum number of webhooks and their size](../../../user/gitlab_com/index.md#webhooks) per project, and per group, is limited.
- The maximum number of webhooks and their size, both per project, and per group.
- The number of webhook calls per minute.
## Possible uses for webhooks ## Possible uses for webhooks
......
...@@ -375,15 +375,18 @@ RSpec.describe WebHookService do ...@@ -375,15 +375,18 @@ RSpec.describe WebHookService do
it 'does not queue a worker and logs an error' do it 'does not queue a worker and logs an error' do
expect(WebHookWorker).not_to receive(:perform_async) expect(WebHookWorker).not_to receive(:perform_async)
payload = { expect(Gitlab::AuthLogger).to receive(:error).with(
include(
message: 'Webhook rate limit exceeded', message: 'Webhook rate limit exceeded',
hook_id: project_hook.id, hook_id: project_hook.id,
hook_type: 'ProjectHook', hook_type: 'ProjectHook',
hook_name: 'push_hooks' hook_name: 'push_hooks',
} "correlation_id" => kind_of(String),
"meta.project" => project.full_path,
expect(Gitlab::AuthLogger).to receive(:error).with(payload) "meta.related_class" => 'ProjectHook',
expect(Gitlab::AppLogger).to receive(:error).with(payload) "meta.root_namespace" => project.root_namespace.full_path
)
)
service_instance.async_execute service_instance.async_execute
end end
...@@ -403,7 +406,6 @@ RSpec.describe WebHookService do ...@@ -403,7 +406,6 @@ RSpec.describe WebHookService do
it 'stops queueing workers and logs errors' do it 'stops queueing workers and logs errors' do
expect(Gitlab::AuthLogger).to receive(:error).twice expect(Gitlab::AuthLogger).to receive(:error).twice
expect(Gitlab::AppLogger).to receive(:error).twice
2.times { service_instance.async_execute } 2.times { service_instance.async_execute }
end end
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment