Skip to content

bootstrap: set ReadHeaderTimeout on the HTTP server#421

Open
randomizedcoder wants to merge 1 commit into
prometheus:masterfrom
randomizedcoder:bootstrap-read-header-timeout
Open

bootstrap: set ReadHeaderTimeout on the HTTP server#421
randomizedcoder wants to merge 1 commit into
prometheus:masterfrom
randomizedcoder:bootstrap-read-header-timeout

Conversation

@randomizedcoder

@randomizedcoder randomizedcoder commented Jul 20, 2026

Copy link
Copy Markdown
Contributor

Hi again 👋 — a small hardening follow-up, as mentioned in #420.

Why

Runner.newServer in bootstrap/ returns &http.Server{Handler: mux} with ReadHeaderTimeout unset, so request-header reads are unbounded — a Slowloris-style connection-exhaustion vector (gosec G112). Because the web package does not set server timeouts either, every bootstrap-based exporter inherits the gap.

This is the toolkit-side counterpart to prometheus/blackbox_exporter#1626, which set the same timeout on the caller side. Fixing it here addresses it once for all toolkit users.

What

Add a ReadHeaderTimeout time.Duration field to bootstrap.Config, defaulting to one minute when unset. It bounds only header reading, not the metrics handler, so it never affects legitimate scrapes (which can run for the full scrape timeout), and — like every other Config field — downstream exporters can tune it to their own needs.

Table-driven tests assert the field maps onto the server (default when unset, configured value otherwise, non-positive treated as unset); a behavioral test confirms the server closes a connection whose headers never complete while a well-formed request still succeeds.

Thanks to @nicolastakashi for the excellent suggestion to make this a Config field rather than a package-level var — it's a much better fit for how the rest of Config is configured (plain struct fields, not functional options) and lets downstream exporters tune it too.

go build, go vet, go test ./... (incl. -race) pass; gosec G112 clears. Happy to adjust the value if a different bound is preferred.

🤖 Generated with Claude Code

@randomizedcoder
randomizedcoder force-pushed the bootstrap-read-header-timeout branch from b4a5428 to 950fed8 Compare July 20, 2026 21:52
@randomizedcoder
randomizedcoder marked this pull request as ready for review July 20, 2026 21:52
@SuperQ
SuperQ requested a review from nicolastakashi July 23, 2026 06:16
Comment thread bootstrap/bootstrap.go Outdated

// serverReadHeaderTimeout bounds request-header reads to mitigate Slowloris
// (gosec G112). Package var so tests can shorten it.
var serverReadHeaderTimeout = time.Minute

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

suggestion: Rather than a package-level var (only overridable by this package's own tests), I'd consider adding a ReadHeaderTimeout time.Duration field to Config, defaulting to time.Minute when unset, that matches how the rest of Config is configured (plain struct fields, not functional options) and lets downstream exporters tune it too.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

@nicolastakashi Good idea! Thanks

@randomizedcoder
randomizedcoder force-pushed the bootstrap-read-header-timeout branch 3 times, most recently from 317d519 to cdc4a37 Compare July 23, 2026 15:52
The *http.Server built by Runner.newServer left ReadHeaderTimeout unset,
so request-header reads were unbounded -- a Slowloris-style connection
exhaustion vector (gosec G112). The exporter-toolkit web package does not
set server timeouts either, so every bootstrap-based exporter inherited
the gap.

Add a ReadHeaderTimeout field to bootstrap.Config, defaulting to one
minute when unset. It bounds only header reading, not the metrics
handler, so it never affects legitimate scrapes, and -- like every other
Config field -- downstream exporters can tune it to their own needs.

Follow-up to prometheus/blackbox_exporter#1626, which made the same change
on the caller side; setting it here fixes it once for all toolkit users.

go build, go vet, and go test ./... (incl. -race) pass.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Signed-off-by: randomizedcoder dave.seddon.ca@gmail.com <dave.seddon.ca@gmail.com>
@randomizedcoder
randomizedcoder force-pushed the bootstrap-read-header-timeout branch from cdc4a37 to 59d9579 Compare July 23, 2026 15:58
@nicolastakashi

Copy link
Copy Markdown
Contributor

@SuperQ LGTM, waiting for your final review.

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.

2 participants