Skip to content

fix(client): route side-channel handshake through configured proxy - #396

Draft
parametalol wants to merge 1 commit into
mainfrom
michael/ROX-37149-side-channel-proxy
Draft

parametalol wants to merge 1 commit into
mainfrom
michael/ROX-37149-side-channel-proxy

Conversation

@parametalol

@parametalol parametalol commented Sep 22, 2026 •

Copy link
Copy Markdown

Description

ConnectViaProxy's data path honors HTTP(S)_PROXY, but the transport credentials' side-channel handshake dialed the endpoint directly with a plain net.Dialer to obtain the TLS AuthInfo. Behind an egress proxy that direct dial hangs, so gRPC calls (e.g. roxctl image scan) failed with authentication handshake failed: dial tcp <central-ip>:443: i/o timeout even though a proxy was configured.

This routes the side-channel handshake through a proxy-aware dialer. The WebSocket data transport gets the same Proxy: http.ProxyFromEnvironment treatment, which it was also missing.

Downstream context: StackRox ROX-37149. The HTTP path is fixed in the main repo; this PR covers the gRPC path.

Changes

  • New client/proxy_dialer.go: dialProxyAwareContext resolves the proxy via httpproxy.FromEnvironment() (per-call, avoiding http.ProxyFromEnvironment's process-wide caching) and tunnels through HTTP/HTTPS CONNECT (with Proxy-Authorization basic auth and TLS-to-proxy support) or SOCKS5, falling back to a direct dial when no proxy matches.
  • client/side_channel_creds.go: use the proxy-aware dialer for the side-channel handshake.
  • client/ws_proxy.go: add Proxy: http.ProxyFromEnvironment to the WebSocket proxy transport.

Testing

  • Added client/proxy_dialer_test.go covering direct dial, HTTP CONNECT (+ basic auth), NO_PROXY bypass, refused CONNECT, unsupported scheme, and default-port resolution.
  • Manual end-to-end through a roxctl build consuming this branch: roxctl image scan against a non-resolvable fake-central.invalid:443 with HTTPS_PROXY set logged CONNECT fake-central.invalid:443 at the proxy with the fix, and produced no proxy traffic (direct-dial timeout) without it.

Partially generated by AI.

ConnectViaProxy's data path honors HTTP(S)_PROXY, but the transport
credentials' side-channel handshake dialed the endpoint directly with a
plain net.Dialer to obtain the TLS AuthInfo. Behind an egress proxy that
direct dial hangs, so gRPC calls (e.g. roxctl image scan) failed with
"authentication handshake failed: dial tcp <central-ip>:443: i/o timeout"
even though a proxy was configured (ROX-37149).

Add a proxy-aware dialer (HTTP/HTTPS CONNECT with basic-auth + SOCKS5,
direct fallback) and use it for the side channel. The WebSocket data
transport gets the same Proxy: http.ProxyFromEnvironment treatment, which
it was also missing.

httpproxy.FromEnvironment is used instead of http.ProxyFromEnvironment to
avoid the latter's process-wide env caching (also makes it testable).

Prompt: "Address ROX-37149 ... Fix HTTP here. Fix grpc-http1 in
../go-grpc-http1".

Partially generated by AI.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented Sep 22, 2026 •

Copy link
Copy Markdown

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Central YAML (base), Organization UI (inherited)

Review profile: CHILL

Plan: Advanced

Run ID: e87f81ec-4fae-4819-ab55-92c2d919226d

📥 Commits

Reviewing files that changed from the base of the PR and between d70bba5 and d2b8534.

📒 Files selected for processing (4)
  • client/proxy_dialer.go
  • client/proxy_dialer_test.go
  • client/side_channel_creds.go
  • client/ws_proxy.go

Included review availability: Your plan provides up to 2 included reviews per hour; 1 remains after this review.


📝 Summary

Summary by CodeRabbit

  • New Features

    • Proxy-aware connections now support HTTP, HTTPS, and SOCKS5 proxies.
    • Initial side-channel connections honor configured proxy settings, including proxy authentication.
    • WebSocket connections now respect HTTP(S)_PROXY and NO_PROXY environment settings.
  • Bug Fixes

    • Improved connectivity when direct access to an endpoint is unavailable but a configured proxy can be used.
    • Proxy failures and unsupported proxy configurations now provide clearer connection errors.

Walkthrough

Changes

Proxy-aware client dialing

Layer / File(s) Summary
Proxy resolution and tunneling
client/proxy_dialer.go
Adds environment-based proxy resolution with direct, HTTP CONNECT, HTTPS CONNECT, and SOCKS5 dialing. It supports proxy credentials, context-aware dialing, default ports, and buffered response bytes.
Proxy dialing validation
client/proxy_dialer_test.go
Adds tests for direct connections, proxy tunneling, authentication, NO_PROXY, rejected CONNECT responses, unsupported schemes, and default ports.
Client connection integration
client/side_channel_creds.go, client/ws_proxy.go
The side-channel handshake and WebSocket transport use environment-based proxy selection.

Priority: ➖ Normal

Estimated code review effort: 3 (Moderate) | ~25 minutes

Change: Bug fix

Sequence Diagram(s)

sequenceDiagram
  participant ClientHandshake
  participant dialProxyAwareContext
  participant HTTPProxy
  participant Target
  ClientHandshake->>dialProxyAwareContext: dial endpoint with context
  dialProxyAwareContext->>HTTPProxy: resolve environment proxy
  dialProxyAwareContext->>HTTPProxy: send CONNECT request
  HTTPProxy->>Target: establish tunnel
  HTTPProxy-->>dialProxyAwareContext: return CONNECT response
  dialProxyAwareContext-->>ClientHandshake: return connection
Loading

Suggested reviewers: guzalv

Merge Risk: ⚪ Minimal · up to d2b85

No actionable merge-blocking risk remains in the proxy dialing changes.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 41.18% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 17 functions across 4 files. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Title check ✅ Passed The title clearly summarizes the main change: routing the side-channel handshake through the configured proxy.
Description check ✅ Passed The description directly explains the proxy-handling issue, the implemented changes, and the related tests.
  • Fix all pre-merge checks with AI
✨ Finishing Touches 💡 1
📝 Generate docstrings 💡
  • Commit to this branch
  • Create a new PR
🧪 Generate unit tests (beta)
  • Commit to this branch
  • Create a new PR

Comment @coderabbitai help to get the list of available commands.

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

Labels

bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant