environmentd: make webhook request size limit configurable via dyncfg#37252
Closed
sjwiesman wants to merge 1 commit into
Closed
environmentd: make webhook request size limit configurable via dyncfg#37252sjwiesman wants to merge 1 commit into
sjwiesman wants to merge 1 commit into
Conversation
Webhook source requests were hardcoded to a 5 MiB body size limit, enforced globally for all environmentd HTTP routes by an Axum `DefaultBodyLimit` layer. Operators had no way to raise it for webhook sources that legitimately accept larger payloads. This introduces the `webhook_max_request_size` dyncfg (default 5 MiB) that bounds the size of a webhook request body, read live on every request. Other HTTP routes keep the existing 5 MiB `MAX_REQUEST_SIZE`. The webhook handler is deliberately coordinator-free in steady state (it caches the appender), so reading the limit must not add a coordinator round-trip. Instead of `get_system_vars().await`, the handler reads a clone of the process-wide live `ConfigSet` carried on the shared `PersistClientCache`. Cloning a `ConfigSet` shares the inner atomics, so reads are O(1) atomic loads that reflect runtime updates; the storage controller already applies the full dyncfg updates to that set via `apply_from`. Enforcement moves into the handler: the body extractor switches from `Bytes` (which silently inherits the global `DefaultBodyLimit`) to `axum::body::Body`, collected via `to_bytes(body, limit)`. Because the handler runs inside the request-decompression layer, the limit applies to the decompressed body, preserving today's decompression-bomb protection. Oversized requests are rejected with `413 Payload Too Large`. Adds a testdrive case to test/testdrive/webhook.td that shrinks the limit, confirms a larger body is rejected with 413, then raises the limit and confirms the same body is accepted without a restart. This release will allow the maximum webhook request body size to be configured at runtime via the `webhook_max_request_size` system parameter. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01YZotyjwhUpWkDViMF5hwjS
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.
Motivation
Webhook source requests are hardcoded to a 5 MiB body size limit, enforced globally for all environmentd HTTP routes by an Axum
DefaultBodyLimitlayer (src/environmentd/src/http.rs). Operators have no way to raise it for webhook sources that legitimately need to accept larger payloads.What this does
Introduces the
webhook_max_request_sizedyncfg (default 5 MiB) that bounds the size of a webhook request body, read live on every request. Other HTTP routes (SQL API, WebSocket, etc.) keep the existing 5 MiBMAX_REQUEST_SIZE. Oversized requests are rejected with413 Payload Too Large.Reading the limit cheaply
The webhook handler is deliberately coordinator-free in steady state (it caches the appender via
WebhookAppenderCache), so reading the limit must not add a coordinator round-trip. Rather thanget_system_vars().await(which sends a command to the coordinator and awaits a reply), the handler reads a clone of the process-wide liveConfigSetcarried on the sharedPersistClientCache. Cloning aConfigSetshares its inner atomics, so reads are O(1) atomic loads that reflect runtime updates. The storage controller already applies the full set of dyncfg updates to thatConfigSetviaapply_from, andcatalog_config.persist_clients/controller.persist_clientsare the sameArc<PersistClientCache>, so updates are visible to the webhook's clone.Enforcement
The handler's body extractor switches from
Bytes(which silently inherits the globalDefaultBodyLimit) toaxum::body::Body, collected viato_bytes(body, limit). Because the handler runs inside the request-decompression layer, the limit applies to the decompressed body, preserving today's decompression-bomb protection. Afrom_fnmiddleware would have been forced outside decompression by Axum's body-type plumbing (limiting only the compressed wire size), which is why enforcement lives in the handler.Testing
Adds a case to
test/testdrive/webhook.tdthat shrinks the limit and confirms a larger body is rejected with413, then raises the limit and confirms the same body is accepted without a restart — proving the dyncfg is read live per request.Release note
This release will allow the maximum webhook request body size to be configured at runtime via the
webhook_max_request_sizesystem parameter.🤖 Generated with Claude Code