Skip to content

fix(podprobe): pin probes to pod IP and reject Host header to prevent SSRF#2471

Open
tanaygoyal1111 wants to merge 1 commit into
openkruise:masterfrom
tanaygoyal1111:fix/podprobe-ssrf-host
Open

fix(podprobe): pin probes to pod IP and reject Host header to prevent SSRF#2471
tanaygoyal1111 wants to merge 1 commit into
openkruise:masterfrom
tanaygoyal1111:fix/podprobe-ssrf-host

Conversation

@tanaygoyal1111

@tanaygoyal1111 tanaygoyal1111 commented May 29, 2026

Copy link
Copy Markdown

Describe what this PR does

PodProbeMarker probes are executed by kruise-daemon, which runs with hostNetwork: true. The daemon used to honor the Host field from the probe spec directly and only fell back to the Pod IP when it was empty:

host := p.TCPSocket.Host
if host == "" {
    host = probeKey.podIP
}

The only thing rejecting a non-empty Host was the PodProbeMarker validating webhook. But the daemon actually reads NodePodProbe, which has no admission webhook of its own, and the controller copies the probe spec into it verbatim. So a Host set directly on a NodePodProbe was dialed as-is from the node network namespace letting a probe reach node-local services or the cloud metadata endpoint (169.254.169.254). On top of that, the webhook forbade httpGet.Host but still allowed an HTTP header literally named Host, which the daemon copies into req.Host.

This PR closes both gaps:

  • Adds a resolveProbeHost chokepoint in the daemon (pkg/daemon/podprobe/prober.go) used by both the TCP and HTTP paths. A probe may only target its Pod IP a non-empty Host that differs from the Pod IP is rejected with a clear error instead of being silently dropped, so a direct NodePodProbe injection is both blocked and visible in the logs. An empty Pod IP is rejected too, which also closes a latent net.JoinHostPort("", port)":port" loopback dial.
  • Rejects an HTTP header named Host in validateHTTPGetAction (pkg/webhook/podprobemarker/validating), since the daemon would otherwise apply it as req.Host.

The protection now lives in the daemon itself, so it no longer depends solely on admission being in the path.

Does this pull request fix one issue?

NONE

Describe how to verify it

Unit tests cover both layers:

go test ./pkg/daemon/podprobe/...
go test ./pkg/webhook/podprobemarker/validating/...
  • prober_test.go: a TCP/HTTP probe with Host: 169.254.169.254 and a different Pod IP is now rejected; an empty Pod IP is rejected; the happy paths (empty Host, or Host equal to the Pod IP) still pass.
  • probe_validating_test.go: a PodProbeMarker carrying a Host HTTP header is now rejected by the webhook.

To check it end-to-end on a cluster: create a PodProbeMarker whose httpGet probe sets a Host header pointing at 169.254.169.254 it is rejected at admission; and a NodePodProbe written directly with a Host pointing off-pod no longer causes the daemon to dial that address.

Special notes for reviews

  • Existing tests in TestNewRequestForHTTPGetAction previously asserted that arbitrary hosts like localhost / secure.example.com were honored I updated those to use the Pod IP, since that behavior was the vulnerability. The cases that test invalid port/path now leave Host empty so they exercise the intended validation rather than short-circuiting on the host check.
  • The daemon-side check intentionally errors loudly rather than silently coercing to the Pod IP, so unexpected Host values surface in daemon logs for debugging.

Copilot AI review requested due to automatic review settings May 29, 2026 18:01
@kruise-bot kruise-bot requested review from Fei-Guo and FillZpp May 29, 2026 18:01
@kruise-bot

Copy link
Copy Markdown

[APPROVALNOTIFIER] This PR is NOT APPROVED

This pull-request has been approved by:
Once this PR has been reviewed and has the lgtm label, please assign fillzpp for approval by writing /assign @fillzpp in a comment. For more information see:The Kubernetes Code Review Process.

The full list of commands accepted by this bot can be found here.

Details Needs approval from an approver in each of these files:

Approvers can indicate their approval by writing /approve in a comment
Approvers can cancel approval by writing /approve cancel in a comment

@kruise-bot

Copy link
Copy Markdown

Welcome @tanaygoyal1111! It looks like this is your first PR to openkruise/kruise 🎉

@kruise-bot kruise-bot added the size/L size/L: 100-499 label May 29, 2026

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Pull request overview

Note

Copilot was unable to run its full agentic suite in this review.

This PR hardens pod probe execution against SSRF by ensuring probes can only target the Pod IP, and by rejecting the Host HTTP header override path.

Changes:

  • Reject Host header in HTTPGet probe validation to prevent host override via headers.
  • Enforce daemon-side probe host pinning to the Pod IP for both HTTP and TCP probes.
  • Add/adjust tests to cover SSRF-style host overrides and empty Pod IP handling.

Reviewed changes

Copilot reviewed 4 out of 4 changed files in this pull request and generated 1 comment.

File Description
pkg/webhook/podprobemarker/validating/probe_validating_test.go Adds a validating-webhook test ensuring Host HTTP header is rejected.
pkg/webhook/podprobemarker/validating/probe_create_update_handler.go Adds validation rejecting Host header in HTTPGetAction headers.
pkg/daemon/podprobe/prober_test.go Updates/extends daemon tests for Pod-IP-only host enforcement and SSRF cases.
pkg/daemon/podprobe/prober.go Implements resolveProbeHost and uses it to pin HTTP/TCP probes to the Pod IP.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +273 to +277
// The daemon copies a "Host" header into req.Host (see newProbeRequest), so allowing
// it would reintroduce the host override that the Host field above forbids.
if strings.EqualFold(header.Name, "Host") {
allErrors = append(allErrors, field.Invalid(fldPath.Child("httpHeaders"), header.Name, "Host header is not allowed"))
}
… SSRF

Signed-off-by: tanaygoyal1111 <goyaltanay.1111@gmail.com>
@tanaygoyal1111 tanaygoyal1111 force-pushed the fix/podprobe-ssrf-host branch from d7e85bc to f5f60c6 Compare May 29, 2026 18:07
@codecov

codecov Bot commented May 29, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 49.21%. Comparing base (e8af952) to head (f5f60c6).
⚠️ Report is 14 commits behind head on master.

Additional details and impacted files
@@            Coverage Diff             @@
##           master    #2471      +/-   ##
==========================================
- Coverage   49.22%   49.21%   -0.02%     
==========================================
  Files         325      325              
  Lines       28155    28163       +8     
==========================================
  Hits        13860    13860              
- Misses      12713    12720       +7     
- Partials     1582     1583       +1     
Flag Coverage Δ
unittests 49.21% <100.00%> (-0.02%) ⬇️

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size/L size/L: 100-499

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants