Skip to content

feat: Add option to disable rate limiting#2708

Open
leechristensen wants to merge 3 commits into
SpecterOps:mainfrom
leechristensen:disable-rate-limiting-option
Open

feat: Add option to disable rate limiting#2708
leechristensen wants to merge 3 commits into
SpecterOps:mainfrom
leechristensen:disable-rate-limiting-option

Conversation

@leechristensen
Copy link
Copy Markdown

@leechristensen leechristensen commented Apr 25, 2026

Description

Adds an environment variables to disable API/login rate limiting:

  • DisableLoginProtections- Removes login rate limits and username brute force protections.
  • APIRateLimitRequestsPerSecond - defaults to 55 (current default). 0=disabled.

Addresses #1444 as well.

Motivation and Context

For testing purposes or in controlled networks(where BloodHound has restricted access), the rate limits get in the way. Examples include AI automations or when wanting to do security testing against the APIs.

Why is this change required? What problem does it solve?
The rate limiting slows down testing and presents an unecesary hurdle in controlled/testing environments.

How Has This Been Tested?

Enabled the new configuration flag and tested that the rate limits no longer existed.

Types of changes

  • New feature (non-breaking change which adds functionality)

Checklist:

Summary by CodeRabbit

  • New Features

    • Add configurable API rate limit and a separate 1 req/s login rate limit; option to disable login protections.
    • Startup warns when login protections or API rate limiting are disabled/turned off.
  • Chores

    • Dev and example Docker Compose and env files updated to expose the new settings and defaults.
  • Tests

    • Tests expanded to cover enabled vs disabled behavior for API and login rate limiting and login-timer no-op.

@github-actions
Copy link
Copy Markdown

github-actions Bot commented Apr 25, 2026

All contributors have signed the CLA ✍️ ✅
Posted by the CLA Assistant Lite bot.

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Apr 25, 2026

📝 Walkthrough

Walkthrough

Threads configuration through API rate-limiting and login-protection code: adds APIRateLimitRequestsPerSecond and DisableLoginProtections to config, makes default and login rate-limit middleware configurable and no-op when disabled, updates LoginTimer to respect the flag, adjusts defaults, startup validation/warnings, tests, and example envs.

Changes

API Rate Limiting & Login Protections

Layer / File(s) Summary
Data Shape / Defaults
cmd/api/src/config/config.go, cmd/api/src/config/default.go
Adds APIRateLimitRequestsPerSecond int64 and DisableLoginProtections bool to Configuration and sets default APIRateLimitRequestsPerSecond = DefaultAPIRateLimit, DisableLoginProtections = false.
Core Implementation
cmd/api/src/api/middleware/rate_limit.go, cmd/api/src/api/middleware/auth.go
DefaultRateLimitMiddleware now accepts cfg config.Configuration and derives limit from cfg.APIRateLimitRequestsPerSecond. RateLimitMiddleware short-circuits to a no-op when limit <= 0. Adds LoginRateLimitMiddleware(cfg, db) that returns no-op when cfg.DisableLoginProtections is true; defines fixed LoginRateLimit. LoginTimer now takes cfg and is a no-op when login protections are disabled.
Wiring / Route Registration
cmd/api/src/api/registration/registration.go, cmd/api/src/api/registration/v2.go
Passes resources.Config into DefaultRateLimitMiddleware and LoginRateLimitMiddleware when constructing route-group middleware.
Startup Validation & Logging
cmd/api/src/services/entrypoint.go
Validates APIRateLimitRequestsPerSecond is non-negative; logs warnings when DisableLoginProtections is enabled and when API rate limiting is explicitly disabled (0).
Tests
cmd/api/src/api/middleware/rate_limit_test.go, cmd/api/src/api/middleware/auth_test.go, cmd/api/src/config/config_test.go, cmd/api/src/config/default_test.go
Refactors rate-limit tests into enabled/disabled subtests, adds helpers (newRateLimitedRouter, newConfiguredRateLimitedRouter, newTestRequest), adds test ensuring LoginTimer is no-op when protections disabled, and tests env mapping for new config fields and defaults.
Examples & Dev Compose
docker-compose.dev.yml, examples/docker-compose/*, .env.example
Adds commented example env vars bhe_api_rate_limit_requests_per_second and bhe_disable_login_protections to examples and dev compose/service envs (default 55 and false respectively).

Sequence Diagram(s)

sequenceDiagram
    participant Client as Client
    participant Router as Router
    participant Middleware as RateLimitMiddleware
    participant DB as Store/DB
    participant Handler as Handler

    Client->>Router: HTTP request
    Router->>Middleware: invoke middleware
    alt DisableLoginProtections / limit <= 0
        Middleware->>Handler: passthrough (call next)
        Handler-->>Client: 200 OK
    else rate limiting enabled
        Middleware->>DB: fetch/update per-IP state
        DB-->>Middleware: allow / deny decision
        alt allow
            Middleware->>Handler: call next
            Handler-->>Client: 200 OK
        else deny
            Middleware-->>Client: 429 Too Many Requests
        end
    end
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 31.82% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title 'feat: Add option to disable rate limiting' accurately summarizes the main change—adding configuration options to disable rate limiting for testing purposes.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Description check ✅ Passed PR description includes all required sections: description of changes, motivation/context, testing approach, type of change, and completed checklist items.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Tip

💬 Introducing Slack Agent: The best way for teams to turn conversations into code.

Slack Agent is built on CodeRabbit's deep understanding of your code, so your team can collaborate across the entire SDLC without losing context.

  • Generate code and open pull requests
  • Plan features and break down work
  • Investigate incidents and troubleshoot customer tickets together
  • Automate recurring tasks and respond to alerts with triggers
  • Summarize progress and report instantly

Built for teams:

  • Shared memory across your entire org—no repeating context
  • Per-thread sandboxes to safely plan and execute work
  • Governance built-in—scoped access, auditability, and budget controls

One agent for your entire SDLC. Right inside Slack.

👉 Get started


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (2)
cmd/api/src/api/middleware/rate_limit.go (1)

130-136: Consider logging a startup warning when rate limiting is disabled.

The passthrough is correct, but disabling rate limiting (particularly on /api/v2/login) removes brute-force protection on authentication. Since this flag is intended for controlled environments, emitting a one-time slog.Warn (e.g., at server startup or on first middleware construction) helps make the security trade-off visible in operational logs and reduces the risk of it being silently enabled in production.

Also, ensure the new DisableRateLimiting setting is documented in the user-facing configuration reference / sample config so operators understand the security impact.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@cmd/api/src/api/middleware/rate_limit.go` around lines 130 - 136, The
RateLimitMiddleware currently returns a passthrough when cfg.DisableRateLimiting
is true; add a one-time startup warning via the standard logger (e.g.,
slog.Warn) when this flag is set so operators see that rate limiting (and
brute-force protection) is disabled; implement the warning at middleware
construction in RateLimitMiddleware (guarded so it only logs once) and ensure
the message references DisableRateLimiting and the security impact (e.g.,
removal of brute-force protection on login). Also update the configuration
docs/sample to document the new DisableRateLimiting setting and its security
implications.
cmd/api/src/api/middleware/rate_limit_test.go (1)

115-144: Add t.Helper() to test helpers.

Both newRateLimitedRouter and newTestRequest accept *testing.T and may invoke t.Fatal. Marking them as helpers ensures failure locations are reported at the call site in the subtest rather than inside the helper.

♻️ Proposed fix
 func newRateLimitedRouter(t *testing.T, cfg config.Configuration, useDefaultRateLimit bool, limit int64) (*mux.Router, *CountingHandler) {
+	t.Helper()
 	mockCtl := gomock.NewController(t)
 	mockDatabase := mocks.NewMockDatabase(mockCtl)
@@
 func newTestRequest(t *testing.T) *http.Request {
+	t.Helper()
 	req, err := http.NewRequest("GET", "/teapot", nil)
 	if err != nil {
 		t.Fatal(err)
 	}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@cmd/api/src/api/middleware/rate_limit_test.go` around lines 115 - 144, Both
test helper functions newRateLimitedRouter and newTestRequest take *testing.T
and may call t.Fatal; add t.Helper() as the first statement inside each function
(i.e., at the top of newRateLimitedRouter and newTestRequest) so failures are
attributed to the caller location. Ensure you add the call in both functions
(newRateLimitedRouter(...) and newTestRequest(...)) before any other logic.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@cmd/api/src/api/middleware/rate_limit_test.go`:
- Around line 115-144: Both test helper functions newRateLimitedRouter and
newTestRequest take *testing.T and may call t.Fatal; add t.Helper() as the first
statement inside each function (i.e., at the top of newRateLimitedRouter and
newTestRequest) so failures are attributed to the caller location. Ensure you
add the call in both functions (newRateLimitedRouter(...) and
newTestRequest(...)) before any other logic.

In `@cmd/api/src/api/middleware/rate_limit.go`:
- Around line 130-136: The RateLimitMiddleware currently returns a passthrough
when cfg.DisableRateLimiting is true; add a one-time startup warning via the
standard logger (e.g., slog.Warn) when this flag is set so operators see that
rate limiting (and brute-force protection) is disabled; implement the warning at
middleware construction in RateLimitMiddleware (guarded so it only logs once)
and ensure the message references DisableRateLimiting and the security impact
(e.g., removal of brute-force protection on login). Also update the
configuration docs/sample to document the new DisableRateLimiting setting and
its security implications.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository YAML (base), Organization UI (inherited)

Review profile: CHILL

Plan: Pro

Run ID: e9a778a2-5e0b-43f6-945e-32dc7980ec86

📥 Commits

Reviewing files that changed from the base of the PR and between ec71b2c and ae27657.

📒 Files selected for processing (11)
  • cmd/api/src/api/middleware/rate_limit.go
  • cmd/api/src/api/middleware/rate_limit_test.go
  • cmd/api/src/api/registration/registration.go
  • cmd/api/src/api/registration/v2.go
  • cmd/api/src/config/config.go
  • cmd/api/src/config/config_test.go
  • cmd/api/src/config/default.go
  • cmd/api/src/services/entrypoint.go
  • docker-compose.dev.yml
  • examples/docker-compose/.env.example
  • examples/docker-compose/docker-compose.yml

@leechristensen leechristensen force-pushed the disable-rate-limiting-option branch from 50d56e9 to 24c5408 Compare April 25, 2026 20:12
@leechristensen
Copy link
Copy Markdown
Author

recheck

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (1)
cmd/api/src/api/middleware/rate_limit_test.go (1)

115-137: Optional: split the helper to avoid the unused limit when using the default middleware.

The useDefaultRateLimit bool, limit int64 shape requires callers to pass a no-op 0 when picking the default middleware (e.g., line 76 and line 96). Two small helpers (or a functional-option style) would make call sites self-documenting and remove the dead parameter:

♻️ Suggested refactor
-func newRateLimitedRouter(t *testing.T, cfg config.Configuration, useDefaultRateLimit bool, limit int64) (*mux.Router, *CountingHandler) {
-	t.Helper()
-
-	mockCtl := gomock.NewController(t)
-	mockDatabase := mocks.NewMockDatabase(mockCtl)
-
-	if !cfg.DisableRateLimiting {
-		mockDatabase.EXPECT().GetConfigurationParameter(gomock.Any(), appcfg.TrustedProxiesConfig).Return(appcfg.Parameter{}, nil).AnyTimes()
-	}
-
-	testHandler := &CountingHandler{}
-	router := mux.NewRouter()
-
-	if useDefaultRateLimit {
-		router.Use(middleware.DefaultRateLimitMiddleware(cfg, mockDatabase))
-	} else {
-		router.Use(middleware.RateLimitMiddleware(cfg, mockDatabase, limit))
-	}
-
-	router.Handle("/teapot", testHandler)
-
-	return router, testHandler
-}
+func newRateLimitedRouterWithLimit(t *testing.T, cfg config.Configuration, limit int64) (*mux.Router, *CountingHandler) {
+	t.Helper()
+	return buildRouter(t, cfg, func(db database.Database) mux.MiddlewareFunc {
+		return middleware.RateLimitMiddleware(cfg, db, limit)
+	})
+}
+
+func newDefaultRateLimitedRouter(t *testing.T, cfg config.Configuration) (*mux.Router, *CountingHandler) {
+	t.Helper()
+	return buildRouter(t, cfg, func(db database.Database) mux.MiddlewareFunc {
+		return middleware.DefaultRateLimitMiddleware(cfg, db)
+	})
+}

(Plus a small private buildRouter helper holding the shared mock + handler wiring.)

Feel free to skip if you prefer to keep diffs minimal.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@cmd/api/src/api/middleware/rate_limit_test.go` around lines 115 - 137, The
helper newRateLimitedRouter currently takes (useDefaultRateLimit bool, limit
int64) which forces callers to pass a dummy limit when using
DefaultRateLimitMiddleware; split it into two small helpers—e.g.,
newRateLimitedRouterWithDefault(cfg, ...) that calls
router.Use(middleware.DefaultRateLimitMiddleware(cfg, mockDatabase)) and
newRateLimitedRouterWithLimit(cfg, limit, ...) that calls
router.Use(middleware.RateLimitMiddleware(cfg, mockDatabase, limit))—or
implement a small private buildRouter to factor shared setup (mockDatabase
creation, CountingHandler, router.Handle) and then two thin wrappers that call
DefaultRateLimitMiddleware or RateLimitMiddleware respectively; update call
sites to use the clearer helpers and remove the unused limit parameter path in
the original function.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@cmd/api/src/api/middleware/rate_limit_test.go`:
- Around line 115-137: The helper newRateLimitedRouter currently takes
(useDefaultRateLimit bool, limit int64) which forces callers to pass a dummy
limit when using DefaultRateLimitMiddleware; split it into two small
helpers—e.g., newRateLimitedRouterWithDefault(cfg, ...) that calls
router.Use(middleware.DefaultRateLimitMiddleware(cfg, mockDatabase)) and
newRateLimitedRouterWithLimit(cfg, limit, ...) that calls
router.Use(middleware.RateLimitMiddleware(cfg, mockDatabase, limit))—or
implement a small private buildRouter to factor shared setup (mockDatabase
creation, CountingHandler, router.Handle) and then two thin wrappers that call
DefaultRateLimitMiddleware or RateLimitMiddleware respectively; update call
sites to use the clearer helpers and remove the unused limit parameter path in
the original function.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository YAML (base), Organization UI (inherited)

Review profile: CHILL

Plan: Pro

Run ID: 1a17c085-20da-48b6-b204-86681973039a

📥 Commits

Reviewing files that changed from the base of the PR and between 50d56e9 and 24c5408.

📒 Files selected for processing (11)
  • cmd/api/src/api/middleware/rate_limit.go
  • cmd/api/src/api/middleware/rate_limit_test.go
  • cmd/api/src/api/registration/registration.go
  • cmd/api/src/api/registration/v2.go
  • cmd/api/src/config/config.go
  • cmd/api/src/config/config_test.go
  • cmd/api/src/config/default.go
  • cmd/api/src/services/entrypoint.go
  • docker-compose.dev.yml
  • examples/docker-compose/.env.example
  • examples/docker-compose/docker-compose.yml
✅ Files skipped from review due to trivial changes (5)
  • examples/docker-compose/docker-compose.yml
  • cmd/api/src/config/default.go
  • cmd/api/src/services/entrypoint.go
  • examples/docker-compose/.env.example
  • docker-compose.dev.yml
🚧 Files skipped from review as they are similar to previous changes (4)
  • cmd/api/src/config/config.go
  • cmd/api/src/api/registration/registration.go
  • cmd/api/src/config/config_test.go
  • cmd/api/src/api/registration/v2.go

@leechristensen leechristensen changed the title Add option to disable rate limiting feat: Add option to disable rate limiting Apr 25, 2026
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.

1 participant