fix: add Desktop PAC-aware egress with SSRF protections - #3999
fix: add Desktop PAC-aware egress with SSRF protections#3999aheritier wants to merge 17 commits into
Conversation
Review: Desktop PAC-aware egress with SSRF protectionsRight direction and a high-quality implementation overall — but I'd request changes. Three items should be fixed before merge (one security-relevant), and the guarded-proxy design deserves an explicit security sign-off since it changes the SSRF trust model. The issue itself flagged this approach as the "alternative worth security review," and that review should be visible on the PR. What the PR doesRoot-cause fix matches the issue precisely: Strengths worth calling out
Must-address1. The guarded proxy path weakens the anti-rebinding guarantee — and fails open on resolver errors. 2. Guarded standalone requests pay a wasted DNS lookup on every call. 3. Should-fix / design notes
TestsCoverage is genuinely good (hermetic Desktop detection override, cooldown preservation, detection-flip caching, typed-error HTTP mapping per route). Gaps:
Nits / follow-ups (fine to defer)
|
Sayt-0
left a comment
There was a problem hiding this comment.
Thanks for tracking this down — #3998 is a real, well-diagnosed bug, and the OCI-vs-URL asymmetry (pkg/remote/pull.go already uses the Desktop transport, urlSource.Read does not) is convincing evidence. The commit-per-phase split, the hermetic detection hook, keeping loopback direct, shipping a kill switch and documenting the behaviour across every affected tool page are all solid.
Requesting changes on one substantive point plus four cheap ones. Details are inline.
Blocking — the security property, not the feature. For guarded clients this PR replaces an enforced SSRF control with an advisory, fail-open one whenever Docker Desktop runs. NewDesktopTransport overwrites the guarded DialContext, so SSRFDialControl never executes on the proxy branch, and proxySafe then returns true on any resolver error. Since fetch takes model-chosen URLs, a prompt-injected http://intranet.corp/ that fails local resolution is handed to the corporate PAC proxy, which resolves it — with no allow_private_ips: true involved.
#3998 anticipated this trade-off and asked for it to be arbitrated: remediation (1) scoped the Desktop transport to docker.com source URLs and flagged the broader option as "worth security review: teach NewSSRFSafeTransport the Desktop proxy socket while keeping its dial-time allowlist". This PR takes the broad route — every guarded consumer (fetch, api, openapi, a2a, webhook, skills, toolinstall, MCP OAuth, tui/image, URL sources) — without preserving the enforcement point. That decision deserves to be explicit, ideally with a security reviewer.
Any of these unblocks: scope the PAC branch to trusted Docker hosts; or fail closed on local DNS errors with proxy-side resolution behind an explicit allowlist; or move IsPublicIP enforcement into whatever performs the final dial.
Blocking-adjacent: an explicitly configured HTTPS_PROXY/NO_PROXY is now silently ignored for guarded clients — reproduced locally, the configured proxy is never contacted. New versus baseline, undocumented and untested.
Cheap fixes before merge, all covered inline: the kill switch accepts only the exact string "1" while every other boolean env var in the repo is permissive; the proxySafe DNS lookup runs before the DesktopRunning() check, costing an extra lookup per request and per redirect hop even where Docker Desktop is absent; every teamloader.Load/config.Load error becomes a 502, so malformed local YAML now reports as a gateway failure; and agentSourceHTTPError returning nil in its default branch lets getAgentConfig answer 200 with an empty body.
On validation. The description notes that PAC-only/Desktop-host smoke validation was not run. Given that PAC behaviour is the entire point of the change and that an SSRF control is being relaxed, task build/test/lint alone looks insufficient. Minimum ask: one PAC-only manual run, a test pinning the NO_PROXY interaction, and a test for the true → false → true detection flap — the current cooldown test only performs a single transition.
Suggestion. The HTTP-status refactor and the startup retry are independent and uncontroversial, and between them they cover remediations (2) and (3) of #3998. Splitting them into their own PR would ship most of the user-visible fix now and let the transport work take the security review and PAC validation it needs; as it stands, 881 additions across 12 commits mix four concerns and none can be reverted independently.
One caveat on the evidence: findings 1 and 2 are code-proven and the proxy-precedence one was reproduced locally, but no Docker Desktop + PAC environment was available, so whether Desktop's own proxy refuses private destinations and compensates for the missing client-side guard remains unverified. If it does, the first finding downgrades considerably — which is precisely the fact worth establishing before merge.
|
Thanks for the detailed review. The reviewed feedback has been addressed across the follow-up commits below:
The final security/behavior decision is explicit: Desktop-selected egress—including PAC Outstanding NON-ACTION (not claimed resolved): a live Desktop PAC-only smoke test and independent security acceptance of the guarded-proxy trust model. Neither has been performed or accepted by these changes. These commits address the implementation, error mapping, proxy opt-out/precedence, resolver, concurrency, pooling, test, and documentation feedback described above. This is a feedback-resolution summary, not an approval or a claim that review-level change requests are approved. Please re-review the current head |
3deb9fd to
b3620f8
Compare
Summary
Closes #3998.
Adds Desktop-optional, PAC-aware egress for configured HTTP clients while preserving standalone proxy behavior and SSRF protections. The implementation covers all supported consumers, including agent/source fetches, sessions, tools, MCP HTTP transports, and MCP OAuth flows. Remote MCP Streamable HTTP/SSE remains intentionally excluded; MCP OAuth flows are supported.
Behavior
404) from upstream/source failure (502) semantics.DOCKER_AGENT_DISABLE_DESKTOP_PROXY=1disables Desktop proxy use (kill switch).HTTP_PROXY,HTTPS_PROXY, andNO_PROXYas appropriate.Commit / phase map
Validation
task buildpassed.task testpassed.task lintpassed.Manual PAC-only / Desktop-host smoke validation was not run and remains environment-limited coverage.