Rate limit GitHub OAuth webhook deliveries per project
What does this MR do and why?
This adds rate limiting to the GitHub webhook delivery endpoint
(POST /projects/:id/github/webhooks), which is part of the GitHub
continuous sync feature.
The rate limiter only counts requests after their signature has been verified against the project's webhook secret. Unsigned or incorrectly signed requests are rejected with a 404 (not found) the same way they always were, without ever being checked against the rate limit. This matters because if we checked the rate limiter first, someone who knows a project ID could send junk requests to burn through the whole rate limit budget and silently block the real GitHub webhooks. By checking the signature first, we avoid this problem.
The real protection here isn't about blocking bad actors with fake requests. GitLab.com already rate-limits unauthenticated traffic per IP address at the edge. What this prevents is a single legitimately connected repository sending an unusually high volume of valid webhook deliveries (from a bot pushing constantly or opening many pull requests, for example), which would otherwise use up a lot of processing capacity at the expense of other connected repositories.
The limit is set to 300 requests per minute, keyed by project. It's
registered as a standard github_oauth_webhook_delivery rate limit
using the existing Gitlab::ApplicationRateLimiter system. Throttled
requests get a 429 (Too Many Requests) response with a Retry-After
header. Rate limit events are logged to auth.log like other
rate-limited endpoints, and the limit respects the standard rate-limit
bypass header for administrative access. The 300/minute limit is set
generously above what a typical repository would generate from a burst
of pushes or pull requests.
There's a separate follow-up tracked in https://gitlab.com/gitlab-org/gitlab/-/issues/632707 about whether GitLab.com's unauthenticated API rate limit needs an exemption for this endpoint, since GitHub sends webhooks from a small shared pool of IPs. That's out of scope for this MR.
References
Resolves #630413 (closed)
Depends on #630402 (closed) (the OAuth webhook receiver this rate limit protects), already merged.
Related follow-up: https://gitlab.com/gitlab-org/gitlab/-/issues/632707
Screenshots or screen recordings
Not applicable, API-only change with no UI.
How to set up and validate locally
- Set up a project with GitHub OAuth continuous sync configured (an
Import::Sync::Repositoryrecord withwebhook_secretset). - In a Rails console, lower the threshold for a quick check:
or send more than 300 correctly signed webhook deliveries to
allow(Gitlab::ApplicationRateLimiter).to receive(:throttled_request?).and_return(true)POST /projects/:id/github/webhookswithin a minute. - Confirm the 301st signed request in that window returns
429 Too Many Requestswith aRetry-Afterheader, while requests for a different project are unaffected, and that an unsigned request is never counted (still just a plain404).
MR acceptance checklist
Evaluate this MR against the MR acceptance checklist. It helps you analyze changes to reduce risks in quality, performance, reliability, security, and maintainability.