fix(proxy): bound total request time to stop a slow-body permit DoS#161
Open
arseniycodes wants to merge 1 commit into
Open
fix(proxy): bound total request time to stop a slow-body permit DoS#161arseniycodes wants to merge 1 commit into
arseniycodes wants to merge 1 commit into
Conversation
An admission permit (only ~64 exist, shared across all tenants) is held from before the body read until the body is fully read/forwarded, and there was no per-request/body-read timeout — only keepAliveTimeout and headersTimeout, which don't cover a slow body. A single valid ingest key can open 64 connections and dribble their bodies a few bytes at a time, pinning every permit and starving all other tenants (cross-tenant DoS). Set server.requestTimeout (default 120s, env INGEST_REQUEST_TIMEOUT_MS) so a stalled request is aborted and its permit released. Kept above headersTimeout and generous enough for a 64 MiB body over a slow link.
Contributor
There was a problem hiding this comment.
1 issue found across 1 file
Prompt for AI agents (unresolved issues)
Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.
<file name="apps/proxy/src/index.ts">
<violation number="1" location="apps/proxy/src/index.ts:882">
P2: Setting `INGEST_REQUEST_TIMEOUT_MS=0` would disable `server.requestTimeout`, so one misconfigured deploy can restore the slow-body permit DoS. Consider validating this timeout as a positive value (or falling back on 0) instead of using `readNonNegativeIntEnv` here.</violation>
</file>
Reply with feedback, questions, or to request a fix.
Fix all with cubic | Re-trigger cubic
Comment on lines
+882
to
+885
| server.requestTimeout = readNonNegativeIntEnv( | ||
| process.env.INGEST_REQUEST_TIMEOUT_MS, | ||
| 120_000, | ||
| ); |
Contributor
There was a problem hiding this comment.
P2: Setting INGEST_REQUEST_TIMEOUT_MS=0 would disable server.requestTimeout, so one misconfigured deploy can restore the slow-body permit DoS. Consider validating this timeout as a positive value (or falling back on 0) instead of using readNonNegativeIntEnv here.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At apps/proxy/src/index.ts, line 882:
<comment>Setting `INGEST_REQUEST_TIMEOUT_MS=0` would disable `server.requestTimeout`, so one misconfigured deploy can restore the slow-body permit DoS. Consider validating this timeout as a positive value (or falling back on 0) instead of using `readNonNegativeIntEnv` here.</comment>
<file context>
@@ -872,6 +872,17 @@ const server = serve({ fetch: app.fetch, port: PORT });
+ // requestTimeout is a lax 300s; tighten it so a stalled body is aborted and
+ // its permit released. Kept well above headersTimeout and generous enough for
+ // a 64 MiB body over a slow link (~0.5 MB/s floor at the default).
+ server.requestTimeout = readNonNegativeIntEnv(
+ process.env.INGEST_REQUEST_TIMEOUT_MS,
+ 120_000,
</file context>
Suggested change
| server.requestTimeout = readNonNegativeIntEnv( | |
| process.env.INGEST_REQUEST_TIMEOUT_MS, | |
| 120_000, | |
| ); | |
| server.requestTimeout = Math.max( | |
| 1, | |
| readNonNegativeIntEnv( | |
| process.env.INGEST_REQUEST_TIMEOUT_MS, | |
| 120_000, | |
| ), | |
| ); |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
An admission permit is acquired before the request body is read and released only after it's fully read/forwarded. There are only ~64 permits and they're shared across all tenants. The server set
keepAliveTimeoutandheadersTimeoutbut no request/body-read timeout, and Node's defaultrequestTimeoutis a lax 300s — and headers complete instantly, soheadersTimeoutdoesn't help against a slow body.Impact
With one valid ingest key, an attacker opens 64 connections to
/v1/tracesand sends the body a few bytes at a time (slowloris). All 64 permits stay pinned and every other tenant's requests queue behind them — cross-tenant denial of service from a single low-privilege key.Fix
Set
server.requestTimeout(default 120s, overridable viaINGEST_REQUEST_TIMEOUT_MS) so a stalled request is aborted and its permit released. Kept aboveheadersTimeout(76s) and generous enough for a 64 MiB body over a slow link (~0.5 MB/s floor).Note: this closes the slow-body vector. Per-tenant rate limiting (a separate noisy-neighbor concern) is intentionally out of scope here.
Summary by cubic
Bound total request time to stop slow-body DoS that pinned admission permits across tenants. Adds
server.requestTimeout(120s default, configurable viaINGEST_REQUEST_TIMEOUT_MS) so stalled bodies are aborted and permits released.headersTimeout(76s) and is generous for ~64 MiB bodies on slow links.Written for commit d290fc2. Summary will update on new commits.