Skip to content

proxy: dial the tunnel through an HTTP proxy (Docker Sandboxes support) - #965

Merged
theoephraim merged 3 commits into
mainfrom
sbx-tunnel-proxy
Jul 31, 2026
Merged

proxy: dial the tunnel through an HTTP proxy (Docker Sandboxes support)#965
theoephraim merged 3 commits into
mainfrom
sbx-tunnel-proxy

Conversation

@theoephraim

@theoephraim theoephraim commented Jul 31, 2026

Copy link
Copy Markdown
Member

What

varlock proxy run --url now dials its WebSocket tunnel through an HTTP CONNECT proxy when the guest's egress requires one, and adds a Docker Sandboxes (sbx) guide.

Why

The tunnel client used the runtime's native WebSocket, which ignores HTTP(S)_PROXY and always dials direct. In a sandbox whose only egress is an explicit proxy gateway (Docker Sandboxes routes everything through gateway.docker.internal:3128), a host or remote broker was unreachable without an external socat CONNECT shim.

How

When a proxy applies to the tunnel URL, the client establishes the CONNECT tunnel to the proxy, optional TLS for wss://, and the WebSocket handshake itself, then speaks RFC 6455 over the socket:

  • Proxy selection: HTTPS_PROXY for wss://, HTTP_PROXY for ws://, falling back to ALL_PROXY, honoring NO_PROXY (upper/lower case). A loopback or NO_PROXY-matched broker still dials direct via the existing native path, which is unchanged.
  • Only a plaintext http:// CONNECT proxy is selected; an https:// proxy (TLS to the proxy itself) and socks* are ignored and fall back to the direct native dial. A wss:// broker still works through an http proxy, since the tunnel's own TLS runs end to end inside the CONNECT.
  • Connection setup is cancellable: the in-flight proxy/TLS socket is tracked before each handshake await, so close() (e.g. the bootstrap timeout) or any setup failure destroys it rather than leaving a socket open and keeping proxy run alive.
  • The hand-rolled client uses only net/tls/crypto, so it behaves identically under Node and the compiled Bun binary (no dependency on native WebSocket proxy support, which differs across runtimes). The tunnel still carries TLS end to end, so an intermediate proxy that terminates TLS only sees the encrypted tunnel.

Verification

  • New unit tests spin up an in-process CONNECT proxy and cover: proxied bootstrap, proxied data path (100 KB both directions), NO_PROXY bypass, bad-token error passthrough, and proxy selection. Full suite green under both vitest (Node) and bun test.
  • Validated end-to-end in a real Docker Sandboxes microVM: host broker via varlock proxy start --expose, sbx policy allow network localhost:PORT, then varlock proxy run --url ws://host.docker.internal:PORT with no shim. Secret injected and response scrubbed; agent saw only the placeholder.

Docs

  • New guide: sandboxes/docker-sandboxes.mdx (host broker and remote broker recipes, the localhost policy quirk, varlock-vs-sbx-secrets comparison, trust model).
  • Updated the sandboxes overview (Docker Sandboxes now in scope, card + sidebar entry) and the proxy running guide (tunnel honors proxy env vars).

Notes for the Docker team (policy check/enforcement mismatch on host.docker.internal, unremovable custom secrets, unscrubbed response bodies) are captured separately in the spike branch, not this PR.

The tunnel client (`proxy run --url`) used the runtime's native WebSocket,
which ignores HTTP(S)_PROXY and always dials direct. In a sandbox whose only
egress is an explicit proxy gateway (e.g. Docker Sandboxes), the broker was
unreachable without an external socat shim.

When a proxy applies to the tunnel URL (HTTP_PROXY/HTTPS_PROXY/ALL_PROXY,
honoring NO_PROXY), the client now establishes an HTTP CONNECT tunnel through
it, optional TLS for wss://, and the WS handshake itself, then speaks RFC 6455
over the socket. This hand-rolled path is runtime-agnostic (net/tls/crypto), so
it behaves identically under Node and the compiled Bun binary; the direct dial
is unchanged. Adds a Docker Sandboxes guide.
@github-actions

github-actions Bot commented Jul 31, 2026

Copy link
Copy Markdown
Contributor

bumpy-frog

The changes in this PR will be included in the next version bump.

minor Minor releases

  • varlock 1.15.0 → 1.16.0

Bump files in this PR

Click here if you want to add another bump file to this PR


This comment is maintained by bumpy.

@github-actions

github-actions Bot commented Jul 31, 2026

Copy link
Copy Markdown
Contributor

📦 Bundle size

⚠️ grows the bundle by 39.4 KB (+0.8%)

Metric main This PR Δ
Total dist 4967.6 KB 5007.0 KB +39.4 KB (+0.8%)
JS 1705.4 KB 1716.6 KB +11.2 KB (+0.7%)
Sourcemaps 3185.6 KB 3213.8 KB +28.2 KB (+0.9%)
Type defs 76.6 KB 76.6 KB

dist/ only; native binaries are versioned separately and not counted here.

@cloudflare-workers-and-pages

cloudflare-workers-and-pages Bot commented Jul 31, 2026

Copy link
Copy Markdown

Deploying with  Cloudflare Workers  Cloudflare Workers

The latest updates on your project. Learn more about integrating Git with Workers.

Status Name Latest Commit Preview URL Updated (UTC)
✅ Deployment successful!
View logs
varlock-website 58fec14 Commit Preview URL

Branch Preview URL
Jul 31 2026, 08:40 PM

@pullfrog pullfrog Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Important

The plaintext proxy path works, but the accepted HTTPS proxy path is broken and pending handshakes cannot be cancelled cleanly.

Reviewed changes in e0953408, covering the proxy selection, custom CONNECT WebSocket transport, tunnel tests, and Docker Sandboxes documentation.

  • Proxy-aware tunnel dialing: Selects proxy environment variables, honors NO_PROXY, and adds a custom RFC 6455 client over HTTP CONNECT.
  • Transport coverage: Adds in-process CONNECT tests for bootstrap, authentication, bypass, and bidirectional data transfer under Node and Bun.
  • Docker Sandboxes guide: Documents host and remote broker setups and links the new guide into the sandbox navigation.

Pullfrog  | Fix all ➔Fix 👍s ➔View workflow run | Using azure/gpt-5.6-sol𝕏

Comment thread packages/varlock/src/proxy/tunnel.ts Outdated
Comment thread packages/varlock/src/proxy/tunnel.ts Outdated
@pkg-pr-new

pkg-pr-new Bot commented Jul 31, 2026

Copy link
Copy Markdown

Open in StackBlitz

npm i https://pkg.pr.new/varlock@965

commit: 58fec14

sbx runs locally (microVMs on your own machine), so it does not belong with
the cloud providers. It is the one local tool reached over the tunnel through
its own gateway rather than loopback, so the section framing notes that.
- proxyForTunnelUrl now rejects https:// proxies. The client opens a plaintext
  TCP socket to the proxy, so an https:// proxy (TLS to the proxy itself) would
  never handshake; advertising it as accepted was broken. Only http:// CONNECT
  proxies are selected now; https:// and socks* fall back to the direct native
  dial. A wss:// broker still works through an http proxy (tunnel TLS runs end
  to end inside the CONNECT).
- Make connection setup cancellable. The in-flight proxy/TLS socket is now
  tracked on the instance before each handshake await, so close() (e.g. the
  bootstrap timeout) or a setup failure destroys it instead of leaving it open
  and keeping proxy run alive. close() destroys outright while connecting;
  readHttpHead also rejects on socket close so a stalled handshake unwinds.
- Tests: https-proxy is not selected; a stalled CONNECT is torn down on timeout.

@pullfrog pullfrog Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

✅ No new issues found.

Reviewed changes in 58fec149, covering the fixes added since the previous incremental pass.

  • Restricted proxy selection: Rejected unsupported https:// proxy hops while preserving wss:// broker TLS through plaintext HTTP CONNECT proxies.
  • Made setup cancellable: Tracked proxy and TLS sockets before each handshake await, destroyed in-flight setup on close, and unwound HTTP-head waits when sockets close.
  • Added regression coverage: Verified unsupported proxy schemes and peer-observed socket teardown when a CONNECT response stalls past the bootstrap timeout.

Pullfrog  | View workflow run | Using azure/gpt-5.6-sol𝕏

@theoephraim
theoephraim merged commit 2df62b9 into main Jul 31, 2026
27 checks passed
@pullfrog pullfrog Bot mentioned this pull request Jul 31, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant