Skip to content

feat: add rate limiting support with token bucket algorithm#18

Open
bornakapusta wants to merge 5 commits intomainfrom
feature/rate-limiting
Open

feat: add rate limiting support with token bucket algorithm#18
bornakapusta wants to merge 5 commits intomainfrom
feature/rate-limiting

Conversation

@bornakapusta
Copy link
Contributor

@bornakapusta bornakapusta commented Jan 27, 2026

Summary

  • Configure global and per-route rate limiters
  • Token bucket with total_rps, per_ip_rps, burst settings
  • Automatic cleanup of stale per-IP entries
  • Returns HTTP 429 with Retry-After header when exceeded
  • New metric: gatekeeper_rate_limited_total{route,limiter,reason}
  • Helm chart support: rateLimiters, defaultRateLimiter, and per-route rateLimiter values
  • Fixed Helm configmap template missing gitlab verifier type (pre-existing bug)

Manual Testing — Minikube + hey

Deployed to minikube with a strict rate limiter (totalRps: 5, perIpRps: 2, burst: 3) applied to the /webhook/direct route. The /webhook/relay route was left without rate limiting for comparison.

Setup

eval $(minikube docker-env)
docker build -t gatekeeperd:dev .
helm upgrade --install gatekeeperd ./charts/gatekeeperd \
  -f config/minikube-gatekeeperd.yaml -n gatekeeper --create-namespace

Config applied via config/minikube-gatekeeperd.yaml:

rateLimiters:
  test-strict:
    totalRps: 5
    perIpRps: 2
    burst: 3

routes:
  - hostname: test.local
    path: /webhook/direct
    rateLimiter: test-strict   # rate limited
    ...
  - hostname: test.local
    path: /webhook/relay       # NOT rate limited
    ...

Pod logs confirmed rate limiter initialization:

{"level":"INFO","msg":"rate limiter loaded","name":"test-strict","total_rps":5,"per_ip_rps":2,"burst":3}

Test Results

Load testing with hey (-host "test.local" to set Host header):

Test Command Expected Result
A — Below limit hey -n 3 -c 1 -host test.local All 200s [200] 3
B — Exceed total RPS hey -n 100 -c 10 -host test.local 429s after burst [200] 4, [429] 96
C — Exceed per-IP hey -n 50 -c 1 -host test.local 429s after burst [200] 5, [429] 45
D — Unrated route hey -n 100 -c 10/webhook/relay Zero 429s [200] 10, [503] 90 (503 = no relay client; zero 429s)

Metrics Verified

gatekeeper_rate_limited_total{limiter="test-strict",reason="per_ip",route="/webhook/direct"} 5
gatekeeper_rate_limited_total{limiter="test-strict",reason="total",route="/webhook/direct"} 136

Both total and per_ip reasons are tracked with correct route and limiter labels.

Test plan

  • make test — all tests pass with 100% coverage
  • Helm template renders rate_limiters, rate_limiter on routes, default_rate_limiter in global
  • helm lint passes with default and minikube values
  • Minikube deploy: rate limiter initializes correctly
  • Below-limit requests pass through (200)
  • Above-limit requests are rejected (429) for both total and per-IP limits
  • Unrated routes are unaffected (no 429s)
  • Prometheus metrics track rate-limited requests with correct labels

Closes #14

- Configure global and per-route rate limiters
- Token bucket with total_rps, per_ip_rps, burst settings
- Automatic cleanup of stale per-IP entries
- Returns HTTP 429 with Retry-After header when exceeded
- New metric: gatekeeper_rate_limited_total{route,limiter,reason}

Closes #14
@bornakapusta bornakapusta marked this pull request as draft January 27, 2026 22:22
@codecov
Copy link

codecov bot commented Jan 27, 2026

Codecov Report

✅ All modified and coverable lines are covered by tests.

📢 Thoughts on this report? Let us know!

@github-actions
Copy link

github-actions bot commented Jan 27, 2026

Docker Images Built

Images are available for testing:

# gatekeeperd
docker pull ghcr.io/tight-line/gatekeeperd:pr-18-88fd7c0

# gatekeeper-relay
docker pull ghcr.io/tight-line/gatekeeper-relay:pr-18-88fd7c0

docker-compose.yml

GATEKEEPERD_IMAGE=ghcr.io/tight-line/gatekeeperd:pr-18-88fd7c0 \
RELAY_IMAGE=ghcr.io/tight-line/gatekeeper-relay:pr-18-88fd7c0 \
docker-compose --profile relay up

Helm (values override)

image:
  repository: ghcr.io/tight-line/gatekeeperd  # or gatekeeper-relay
  tag: "pr-18-88fd7c0"

Images expire ~15 days after PR closes.

nickmarden and others added 3 commits February 10, 2026 11:57
Resolve conflicts with v0.2.7 release (GitLab verifier support):
- CHANGELOG.md: keep rate limiting under [Unreleased], GitLab under [0.2.7]
- config_test.go: keep both GitLab and rate limiter validation tests
@bornakapusta bornakapusta marked this pull request as ready for review February 11, 2026 20:36
@sonarqubecloud
Copy link

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

feat: Add rate limiting support (global and per-route)

2 participants