-
Notifications
You must be signed in to change notification settings - Fork 380
Description
Problem Statement
The sandbox proxy only handles CONNECT tunneling. When a client sets HTTP_PROXY and makes a plain http:// request, standard HTTP libraries (Python's requests, httpx, aiohttp, Go's net/http, etc.) send a forward proxy request — not a CONNECT tunnel. The proxy rejects this with 403 Forbidden, forcing users to write custom CONNECT tunnel code (raw sockets, manual HTTP framing) for plain HTTP calls to internal services. This is a poor developer experience and a recurring source of confusion.
Technical Context
The sandbox proxy (crates/navigator-sandbox/src/proxy.rs) is CONNECT-tunnel-only. At line 199, any non-CONNECT method is immediately rejected with 403. For plain HTTP targets (e.g., internal tool servers on private IPs), this means the standard proxy behavior that every HTTP library implements — forward proxy requests like GET http://host/path HTTP/1.1 — doesn't work. Users must either write custom CONNECT tunnel code or use our sandbox_http.py wrapper, both of which are fragile and non-obvious.
The proxy already has the infrastructure to handle this: OPA policy evaluation, SSRF checks with allowed_ips override, and L7 relay/inspection. The missing piece is dispatching non-CONNECT requests through that same pipeline.
Affected Components
| Component | Key Files | Role |
|---|---|---|
| Sandbox proxy | crates/navigator-sandbox/src/proxy.rs |
Main proxy server — CONNECT-only dispatch, SSRF checks, OPA eval |
| L7 relay | crates/navigator-sandbox/src/l7/relay.rs, l7/rest.rs |
HTTP request parsing, forwarding, and inspection |
| OPA engine | crates/navigator-sandbox/src/opa.rs |
Policy evaluation for network decisions |
| Policy schema | architecture/security-policy.md |
Documents endpoint config including allowed_ips |
Proposed Approach
Add forward proxy support restricted to private IP endpoints that are explicitly allowed by policy. When the proxy receives a non-CONNECT request (e.g., GET http://10.86.8.223:8000/path), parse the absolute-form URI, run the same OPA + SSRF pipeline as CONNECT, and if the destination is a private IP with allowed_ips configured, connect upstream, rewrite the request to origin-form, and relay the response. Reject if the destination is public (plain HTTP should never traverse the public internet), if the scheme is https:// (use CONNECT), or if no policy matches.
No policy schema changes are needed — allowed_ips already serves as both the SSRF override and the opt-in signal for private endpoint access.
Scope Assessment
- Complexity: Medium
- Confidence: High — clear path, reuses existing infrastructure
- Estimated files to change: 2-3 (proxy.rs primarily, minor touches to l7/rest.rs)
- Issue type:
feat
Risks & Open Questions
- Connection reuse / keep-alive: Should the forward proxy support HTTP keep-alive (multiple request-response cycles on one connection), or close after each response? Keep-alive is more performant but adds complexity. L7 relay already handles multi-request loops, so reuse may come naturally.
- Request body forwarding: Need to handle
Content-LengthandTransfer-Encoding: chunkedon the forwarded request. The L7 rest provider already hasrelay_fixedandrelay_chunked— can these be reused? - Hop-by-hop headers: Per RFC 7230, the proxy should strip hop-by-hop headers (
Connection,Proxy-Authorization, etc.) when forwarding. Need to implement this correctly. Viaheader: RFC 7230 §5.7 recommends adding aViaheader. Minor but good practice.
Test Considerations
- Unit tests for URI parsing (
parse_proxy_uri) and request rewriting (rewrite_forward_request) - Integration tests: forward proxy to allowed private endpoint (200), to public IP (403), to private IP without
allowed_ips(403), to unmatched policy (403), HTTPS forward proxy (403) - Integration test with L7 inspection:
access: read-onlyallows GET, denies POST - Regression: all existing CONNECT tunnel behavior unchanged
Created by spike investigation. Use build-from-issue to plan and implement.