Skip to content

fix(api): bound how long a client may take to send request headers - #29500

Draft
nishantbkl3345-ship-it wants to merge 1 commit into
podman-container-tools:mainfrom
nishantbkl3345-ship-it:fix-api-read-header-timeout
Draft

fix(api): bound how long a client may take to send request headers#29500
nishantbkl3345-ship-it wants to merge 1 commit into
podman-container-tools:mainfrom
nishantbkl3345-ship-it:fix-api-read-header-timeout

Conversation

@nishantbkl3345-ship-it

Copy link
Copy Markdown

The API service's http.Server sets only IdleTimeout, which covers the wait between requests on a keep-alive connection. Nothing bounds the read of the request headers themselves, so a client that connects and sends a partial header block - "GET /_ping HTTP/1.1\r\nHost: x\r\n" with no terminating CRLF - is never disconnected.

Each connection held this way pins a file descriptor and a goroutine in the service for as long as it runs, and nothing limits how many of them a client may open; against a server configured exactly as this one is, 100 out of 100 half-open connections were still held after the API idle window had passed. With "podman system service --time=0" they are held for the lifetime of the process, until it runs out of descriptors and stops serving legitimate clients. This is reachable by anyone who can open a connection to the service, which matters most when it is bound to TCP ("podman system service tcp://...") rather than to a unix socket.

Set ReadHeaderTimeout to 30 seconds. ReadTimeout and WriteTimeout stay unset on purpose: they cover the whole request and would break attach, exec, events, logs and image push/pull, which stream for as long as the client wants them to. ReadHeaderTimeout does not have that problem, because net/http clears the read deadline once the headers have been read and again when a handler hijacks the connection. It bounds HTTP/1.x header reads.

Move the http.Server construction into newHTTPServer() so this can be tested without a libpod runtime. The tests cover a connection stalled mid-header (now closed), the same server with no ReadHeaderTimeout (still held - the regression this guards against), and a hijacked connection that streams well after the header timeout has elapsed (unaffected).

Checklist

Ensure you have completed the following checklist for your pull request to be reviewed:

  • I have read and understood our contributing guidelines and will not have more than two open PRs as a new contributor.
  • PR description, commit message, and GitHub comments are human-written, per LLM Policy
  • Certify you wrote the patch or otherwise have the right to pass it on as an open-source patch by signing all
    commits. (git commit -s). (If needed, use git commit -s --amend). The author email must match
    the sign-off email address. See CONTRIBUTING.md
    for more information.
  • Referenced issues using Fixes: #00000 in commit message (if applicable)
  • Tests have been added/updated (or no tests are needed)
  • Documentation has been updated (or no documentation changes are needed)
  • All commits pass make validatepr (format/lint checks)
  • Release note entered in the section below (or None if no user-facing changes)

Does this PR introduce a user-facing change?


The API service's http.Server sets only IdleTimeout, which covers the
wait between requests on a keep-alive connection. Nothing bounds the read
of the request headers themselves, so a client that connects and sends a
partial header block - "GET /_ping HTTP/1.1\r\nHost: x\r\n" with no
terminating CRLF - is never disconnected.

Each connection held this way pins a file descriptor and a goroutine in
the service for as long as it runs, and nothing limits how many of them a
client may open; against a server configured exactly as this one is, 100
out of 100 half-open connections were still held after the API idle
window had passed. With "podman system service --time=0" they are held
for the lifetime of the process, until it runs out of descriptors and
stops serving legitimate clients. This is reachable by anyone who can
open a connection to the service, which matters most when it is bound to
TCP ("podman system service tcp://...") rather than to a unix socket.

Set ReadHeaderTimeout to 30 seconds. ReadTimeout and WriteTimeout stay
unset on purpose: they cover the whole request and would break attach,
exec, events, logs and image push/pull, which stream for as long as the
client wants them to. ReadHeaderTimeout does not have that problem,
because net/http clears the read deadline once the headers have been read
and again when a handler hijacks the connection. It bounds HTTP/1.x
header reads.

Move the http.Server construction into newHTTPServer() so this can be
tested without a libpod runtime. The tests cover a connection stalled
mid-header (now closed), the same server with no ReadHeaderTimeout (still
held - the regression this guards against), and a hijacked connection
that streams well after the header timeout has elapsed (unaffected).

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Signed-off-by: nishantbkl3345-ship-it <nishantbkl3345-ship-it@users.noreply.github.com>
@github-actions github-actions Bot added the kind/api-change Change to remote API; merits scrutiny label Aug 13, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

kind/api-change Change to remote API; merits scrutiny

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant