Let the configured integration proxy live on a private network - #1731
Open
michielbdejong wants to merge 2 commits into
Open
michielbdejong wants to merge 2 commits into
michielbdejong wants to merge 2 commits into
Conversation
`ProxyOrigin` only exempted a literal loopback address or `localhost`, so `--integration-proxy-url http://host.docker.internal:8787` (192.168.65.x on Docker Desktop, 172.17.0.1 on Linux) or a LAN proxy was refused. Exactly the configured scheme, host and port may now resolve to loopback, private, CGNAT or ULA addresses. The name is resolved once per request and those checked addresses are pinned into the client. Link-local and metadata addresses stay refused even for the proxy (and a literal one is refused at startup), and so do credentials in the URL. Every other destination keeps all the checks. Adds a `Resolve` seam so tests can make a name resolve to a private address, and an `it plugin_proxy` test that runs a JS plugin through `POST /plugin-run` on a real server and checks that `ctx.http` reaches a stub proxy as `GET /proxy/conn-1/demo/items`, v2-signed by the installation's node agent, and that an undeclared platform is refused. Refs #1700 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The private-network exception for the configured integration proxy let through two instance-metadata endpoints that sit inside ranges it allows: Alibaba Cloud's 100.100.100.200 (carrier-grade NAT) and AWS's IPv6 IMDS at fd00:ec2::254 (unique-local). Name them and refuse them before the exception applies, including the IPv4-mapped form, both as a literal at startup and as a resolved answer per request. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Contributor
Author
|
Merge-risk triage (2026-09-28). This is a read-only review of this PR's own diff. The rule it applies: nothing that works today may break for existing users or clients, and new behaviour is added alongside the old. The stack is being rebased onto Verdict: SAFE Loosens the SSRF guard to private/CGNAT/ULA addresses, but only for the operator-configured proxy origin. Link-local and metadata addresses stay refused, the name is resolved once, and the checked addresses are pinned. Nothing changes when the option is unset. 🤖 Generated with Claude Code |
This branch has not been deployed
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.
Refs #1700. Stacked on #1725.
A: a proxy origin on a private network
Before this change,
ProxyOrigin::parseexempted only a literal loopback address orlocalhost. So--integration-proxy-url http://host.docker.internal:8787was refused: Docker Desktop resolves it to 192.168.65.x and Linux to 172.17.0.1. A LAN proxy such ashttp://proxy.lan:8787→ 192.168.1.10 was refused too.New rule (
egress::refuse_proxy_address/destination_addresses_with):--integration-proxy-urlis parsed at startup.localhostis connected to without a lookup. Any other name is resolved once per request, every answer must pass, and exactly those addresses are pinned into the reqwest client withresolve_to_addrs. There is no second lookup.checked_addresseschecks.Resolvetrait (withSystemResolver) is the test seam.The docs for
--integration-proxy-urlare updated inconfig.rsanddocs/src/plugins/creating-plugins.md.B: end-to-end
ctx.http→ proxycargo test -p atomic-server --test it plugin_proxyruns the full path over real sockets:--integration-proxy-urlpointing at the stub./plugin-release-pin), then commits an active Installation withintegrationAppAgentandintegrationConnections: {demo: "conn-1"}. Activation mints the node agent, which the test reads back fromGET /app-agent.POST /plugin-run. The plugin callsctx.http("atomic-proxy:/demo/items").The test asserts that:
GET /proxy/conn-1/demo/items;check_auth_signatureverifies and which a different method does not;ctx.connections;atomic-proxy:/other/..., an undeclared platform, is refused before anything connects, so the stub sees exactly one request.Tests
cargo test -p atomic-server --lib plugins::: 176 passed, 3 ignored.cargo test -p atomic-server --test it plugin_proxy: passes.cargo clippy -p atomic-server --all-targets -- -D warnings: clean.🤖 Generated with Claude Code