Skip to content

agentHost: honor SSH ProxyJump when connecting - #334849

Open
Girish Konda (kondv) wants to merge 1 commit into
microsoft:mainfrom
kondv:dev/kondv/agent-host-ssh-proxy-config
Open

Girish Konda (kondv) wants to merge 1 commit into
microsoft:mainfrom
kondv:dev/kondv/agent-host-ssh-proxy-config

Conversation

@kondv

@kondv Girish Konda (kondv) commented Sep 7, 2026

Copy link
Copy Markdown

Fixes #317445

Problem

Agent Host resolves effective OpenSSH configuration with ssh -G, but did not carry ProxyJump into the ssh2 connection. A host reachable only through its configured jump host was therefore dialed directly from the Agents window and failed with ENOTFOUND or EHOSTUNREACH, while native OpenSSH and Remote SSH succeeded against the same config.

Change

Preserve the resolved ProxyJump value through initial and reconnect configuration. For a single jump host, start native OpenSSH with BatchMode=yes and -W, pass its standard streams to ssh2 through ConnectConfig.sock, and stop the owned helper on connection teardown. The destination is passed after -- so a config-derived value can never be read as an ssh option.

ssh -G reports ProxyJump with its percent tokens unexpanded, so %%, %h, %n, %p and %r are expanded here. Expansion happens per component after the value is split, because a token standing in for an IPv6 address would otherwise be mistaken for a port separator — %h:24321 against an IPv6 HostName becomes ::1:24321. The ssh:// URI form is accepted directly as well, though ssh -G normalizes it away.
A bracketed IPv6 jump host is passed to ssh bare while the -W target keeps its brackets, matching what ssh -J itself emits: glibc and Darwin getaddrinfo reject [...] for a numeric IPv6 destination, so forwarding the brackets fails name resolution everywhere except Windows.
The helper's stderr is the only place DNS, host-key and authentication failures for the jump host are reported; without it they all collapse into ssh2's generic "Connection lost before handshake". A bounded tail is captured and attached to the surfaced error. It is attached by annotating the existing error rather than replacing it, so an SSHHostKeyDeniedError keeps the name the renderer dispatches on. That annotation is the single place it is added — the helper's own exit error carries the code and signal only — so a jump-host failure does not repeat the same OpenSSH diagnostic twice in the message the user sees.

The jump alias, not a pre-resolved hostname, is handed to OpenSSH, so the jump host's own HostName, User, Port and IdentityFile still apply. Final-host authentication, host-key verification, Agent Host bootstrap and WebSocket handling are untouched.

Single-hop only. Comma-separated chains, ProxyCommand, interactive jump-host authentication, askpass IPC and HostKeyAlias behavior are unchanged, and there are no dependency, build or pipeline changes.

Validation

  • npm run compile-client, typecheck-client, valid-layers-check, full eslint, and hygiene on the changed files — all clean
  • vs/platform/agentHost/test/{common,node} — 6787 passing, 0 failing; remoteAgentHost/test — 280 passing. An earlier revision of this section reported three CopilotAgent state-file failures as pre-existing. They were not. getCopilotHomePath() prefers an ambient COPILOT_HOME over the injected userHome, so the value set in my shell escaped the test's sandbox — and failed on unpatched main for the same reason, which is what made it look pre-existing. Cleared, the suite is green.
  • Each production change is mutation-proven: reverting the ProxyJump mapping in the configured-host action, the token expansion, the expand-after-split ordering, the close-instead-of-exit read, the annotate-in-place error handling, the connected gate on helper disposal, the bounded stderr wait, the single attachment of the helper diagnostic, or the IPv6 bracket strip each fails a specific regression test, and the suite returns to green when restored.
  • Live built Code OSS A/B, captured on commit dca870d (base 7b763e653ee2), using disposable profiles, real key-authenticated jump and target SSH servers, and the actual Agent Host WebSocket relay:
    • Native OpenSSH reached proxyjump-target.invalid:2222 through the configured jump host.
    • Unpatched main used the same Agents UI action, dialed the target directly, failed with getaddrinfo ENOTFOUND, and never contacted the jump or target servers.
    • Patched Code OSS contacted the jump, forwarded to the target, authenticated, launched the real Agent Host, and completed the SSH-forwarded WebSocket connection.
    • A direct-host control completed the same Agent Host flow without starting a jump helper.
    • All owned helper and Agent Host processes exited after the UI closed.
  • The -- terminator was added after that run, so this revision is not byte-identical to the Electron-tested one and the Electron harness was not re-run. It is covered separately: against OpenSSH 9.5p2, ssh -o BatchMode=yes -W target.invalid:22 -V prints the version and exits 0, while the same command with -- -V treats it as a hostname and exits 255; ordinary host and user@host destinations are unaffected. A unit regression asserts -- is always immediately before the destination, and it fails without the production change.
  • Percent-token behavior was confirmed against OpenSSH 9.5p2 directly: ssh -G returns proxyjump %n-bastion and proxyjump bastion-%h literally, unexpanded.
  • The bounded wait for stderr that is still in flight when ssh2 rejects on the stdout EOF is covered directly against the proxy transport: one test asserts it does not settle before the helper's stdio closes and that the diagnostic is complete afterwards, another asserts it gives up rather than hanging when the helper never closes. Making whenStderrSettled() resolve immediately fails the first.
  • Agent Host E2E, run locally in the CI configuration, twice per side on d2da7c8d (this branch before the stderr de-duplication): 176 passing / 1 failing, then 175 / 2; merge-base 7b7e49c8 176 / 1 both times. Every failure on both sides is the same Timeout waiting for response to vscode/collectAgentHostDebugLogs (id=6, 8000ms), attributed by mocha to whichever test is running when it lands, which is why the named test moves between runs. The E2E server's import closure is 679 modules and contains none of the files this PR changes.
  • Two production changes have no regression test: the disposeProxyTransport(sshClient) calls added to the relay-replacement timeout catch and the initial-connect catch. That suite's harness wires up no proxy transport, so mutating either line passes for the wrong reason instead of failing. The call is idempotent — a WeakMap delete plus dispose()'s own guard — so the repeated-path case is safe.

Copilot AI balanced review requested due to automatic review settings September 7, 2026 05:17

Copilot AI 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.

🟡 Changes recommended

Host-key alias handling, IPC and artifact cleanup, and localization issues remain unresolved.

Once you've addressed the issues Copilot identified, you can request another Copilot review.

Pull request overview

Adds SSH proxy support to Agent Host connections, including native askpass prompting and secure proxy lifecycle handling.

Changes:

  • Supports ProxyJump, ProxyCommand, ProxyUseFdpass, and HostKeyAlias.
  • Adds native proxy transports and renderer-mediated authentication prompts.
  • Adds parsing, security, cleanup, and reconnection tests.
File summaries
File Description
src/vs/sessions/contrib/providers/remoteAgentHost/browser/remoteAgentHostActions.ts Forwards resolved proxy configuration.
src/vs/platform/agentHost/test/node/sshRemoteAgentHostService.test.ts Tests proxy transport, askpass, cleanup, and reconnection.
src/vs/platform/agentHost/test/electron-browser/sshRemoteAgentHostService.test.ts Tests renderer prompt handling.
src/vs/platform/agentHost/test/common/sshConfigParsing.test.ts Tests proxy configuration parsing.
src/vs/platform/agentHost/node/sshRemoteAgentHostService.ts Implements proxy transports and askpass IPC.
src/vs/platform/agentHost/electron-browser/sshRemoteAgentHostServiceImpl.ts Presents proxy authentication prompts.
src/vs/platform/agentHost/common/sshRemoteAgentHost.ts Extends SSH configuration and IPC contracts.
src/vs/platform/agentHost/common/sshConfigParsing.ts Parses effective proxy configuration.
Review details

Suppressed comments (1)

src/vs/platform/agentHost/node/sshRemoteAgentHostService.ts:1874

  • This newly surfaced failure message is also not localized. Use a localized template with placeholders for proxyKind and detail, consistent with the other user-facing proxy errors in this method.
				socket.destroy(new Error(`${proxyKind} process exited before the SSH connection was established (${detail}).`));
  • Files reviewed: 8/8 changed files
  • Comments generated: 4
  • Review effort level: Balanced

💡 Configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread src/vs/platform/agentHost/node/sshRemoteAgentHostService.ts Outdated
Comment thread src/vs/platform/agentHost/node/sshRemoteAgentHostService.ts Outdated
Comment thread src/vs/platform/agentHost/node/sshRemoteAgentHostService.ts Outdated
Comment thread src/vs/platform/agentHost/node/sshRemoteAgentHostService.ts Outdated
@vs-code-engineering

Copy link
Copy Markdown
Contributor

📬 CODENOTIFY

The following users are being notified based on files changed in this PR:

Ladislau Szomoru (@lszomoru)

Matched files:

  • build/azure-pipelines/common/checkNativeOptionalDeps.ts

@kondv
Girish Konda (kondv) force-pushed the dev/kondv/agent-host-ssh-proxy-config branch 2 times, most recently from 8de3d6f to dca870d Compare September 8, 2026 01:22
@kondv Girish Konda (kondv) changed the title agentHost: Support SSH proxy configuration agentHost: Support SSH ProxyJump Sep 8, 2026
@kondv
Girish Konda (kondv) force-pushed the dev/kondv/agent-host-ssh-proxy-config branch from dca870d to 16e5021 Compare September 8, 2026 18:11
@kondv Girish Konda (kondv) changed the title agentHost: Support SSH ProxyJump agentHost: honor SSH ProxyJump when connecting Sep 8, 2026
@kondv

Copy link
Copy Markdown
Author

@microsoft-github-policy-service agree company="Microsoft"

@kondv
Girish Konda (kondv) force-pushed the dev/kondv/agent-host-ssh-proxy-config branch 3 times, most recently from 0a67175 to 9cf4775 Compare September 9, 2026 01:16
@kondv
Girish Konda (kondv) requested a balanced review from Copilot September 9, 2026 01:20

Copilot AI 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.

🟡 Changes recommended

Valid SSH URI jump hosts must be supported, and helper stderr should provide actionable diagnostics.

Once you've addressed the issues Copilot identified, you can request another Copilot review.

Review details
  • Files reviewed: 6/6 changed files
  • Comments generated: 2
  • Review effort level: Balanced

Comment thread src/vs/platform/agentHost/node/sshRemoteAgentHostService.ts
Comment thread src/vs/platform/agentHost/node/sshRemoteAgentHostService.ts Outdated

Copilot AI 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.

🟡 Changes recommended

Three moderate proxy handling and error-reporting issues remain unresolved.

Once you've addressed the issues Copilot identified, you can request another Copilot review.

Review details
  • Files reviewed: 6/6 changed files
  • Comments generated: 3
  • Review effort level: Balanced

Comment thread src/vs/platform/agentHost/node/sshRemoteAgentHostService.ts Outdated
Comment thread src/vs/platform/agentHost/node/sshRemoteAgentHostService.ts Outdated
Comment thread src/vs/platform/agentHost/node/sshRemoteAgentHostService.ts Outdated

Copilot AI 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.

🟡 Changes recommended

Helper cleanup gaps can leak processes, while eager close handling can discard the diagnostic stderr the new path intends to preserve.

Once you've addressed the issues Copilot identified, you can request another Copilot review.

Review details
  • Files reviewed: 6/6 changed files
  • Comments generated: 2
  • Review effort level: Balanced

Comment thread src/vs/platform/agentHost/node/sshRemoteAgentHostService.ts
Comment thread src/vs/platform/agentHost/node/sshRemoteAgentHostService.ts Outdated

Copilot AI 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.

🟢 Approval recommended

The implementation and regression coverage are coherent; the remaining duplicated diagnostic is minor and non-blocking.

Review details
  • Files reviewed: 7/7 changed files
  • Comments generated: 1
  • Review effort level: Balanced

Comment thread src/vs/platform/agentHost/node/sshRemoteAgentHostService.ts Outdated

Copilot AI 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.

🔵 Needs a closer look

Cross-process SSH transport and teardown behavior warrants final human review despite comprehensive regression coverage.

Review details
  • Files reviewed: 7/7 changed files
  • Comments generated: 2
  • Review effort level: Balanced

Comment thread src/vs/platform/agentHost/node/sshRemoteAgentHostService.ts Outdated

Copilot AI 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.

🟡 Changes recommended

Bracketed IPv6 jump hosts are passed to OpenSSH with brackets intact, causing hostname resolution failure.

Once you've addressed the issues Copilot identified, you can request another Copilot review.

Review details
  • Files reviewed: 7/7 changed files
  • Comments generated: 1
  • Review effort level: Balanced

Comment thread src/vs/platform/agentHost/node/sshRemoteAgentHostService.ts Outdated

Copilot AI 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.

🔵 Needs a closer look

Cross-platform subprocess and stream lifecycle behavior warrants final human review despite the extensive regression coverage.

Review details
  • Files reviewed: 7/7 changed files
  • Comments generated: 0 new
  • Review effort level: Balanced

Copilot AI 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.

🟢 Approval recommended

The implementation is comprehensive and well-tested; the noted clean-exit test gap is minor and non-blocking.

Review details
  • Files reviewed: 7/7 changed files
  • Comments generated: 1
  • Review effort level: Balanced

Copilot AI 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.

🟢 Approval recommended

The implementation is narrowly scoped and comprehensively covers configuration propagation, transport behavior, failure reporting, and lifecycle cleanup.

Review details
  • Files reviewed: 7/7 changed files
  • Comments generated: 0 new
  • Review effort level: Balanced

Copilot AI 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.

🔵 Needs a closer look

Native process lifecycle and cross-platform OpenSSH behavior warrant final human review despite extensive regression coverage.

Review details
  • Files reviewed: 7/7 changed files
  • Comments generated: 0 new
  • Review effort level: Balanced

Copilot AI 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.

🟢 Approval recommended

The implementation is focused, defensively handles proxy lifecycle and diagnostics, and includes comprehensive regression coverage.

Review details
  • Files reviewed: 7/7 changed files
  • Comments generated: 0 new
  • Review effort level: Balanced

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Agents view fails to connect to SSH hosts using ProxyJump, while main editor's SSH works

3 participants