* perf(ssh): reuse one proxy connection for system SSH on Windows
Windows OpenSSH cannot multiplex (ControlMaster fails with "getsockname
failed: Not a socket"), so every system-SSH exec for a ProxyCommand or
ProxyJump target re-ran the proxy handshake. Hold one `ssh -N -L` through
the real proxy per session and dial later execs over 127.0.0.1 with
HostName/Port/ProxyCommand=none/ProxyJump=none/HostKeyAlias overrides.
The route starts after connect, is proven with a login before use, falls
back to per-command proxying on any failure, honors
systemSshConnectionReuse: false, and is torn down with its proxy child.
Rebased from #31731 (commits b1cdd46, 3835c2d, 6268f2f) onto current main,
including the pending-route cancellation fix from b301b6a.
Co-authored-by: Neil <neil@stably.ai>
* refactor(ssh): stop the loopback forward through process-host's tree kill
Reuse signalProcessTree instead of a hand-rolled taskkill-then-kill
fallback: it already joins an in-flight tree kill, refuses a reaped PID,
and falls back to killing the root, so the route keeps no parallel copy
of that logic.
* fix(ssh): harden the Windows loopback route against review findings
- Manual targets: take the forward's sshd port and the HostKeyAlias from
the stored endpoint; `ssh -G` ran on the label and named another host.
- Hold the forward with ServerAlive probes so a silently dead proxy ends
it and commands fall back instead of stalling on 127.0.0.1.
- Never route forwards: their argv must stay stable for orphan recovery,
and the route's own forward must take the real proxy.
- Upload sessions read the route per file, so an exited route's port is
not dialled after it closes.
- Skip the route for security-key identities, whose forward and probe
logins would each ask for an unprompted touch.
* fix(ssh): keep the loopback route for manual targets no Host block claims
The unclaimed-alias endpoint rewrite only applies to config-backed targets,
so manual targets with a configHost no block names stay eligible.
* fix(ssh): keep the held loopback forward non-interactive
The forward is an optional background login; with BatchMode=no a password or
passphrase login raised an extra prompt right after connect, which the
non-interactive probe then discarded anyway.
* fix(ssh): admit the loopback route only when it changes nothing but the route
Overriding HostName to 127.0.0.1 changes which ssh_config blocks match, so a
`Match host <real host>` block could silently switch the remote User (or
identities, certificates, known-hosts files) for every routed command. Run
`ssh -G` with and without the route options and refuse the route on any
difference outside HostName/Port/HostKeyAlias/ProxyCommand/ProxyJump.
Also ask the agent with `ssh-add -L` and refuse the route when it may hold a
security key, so the background forward and probe never request a touch.
Both checks run through process-host and fail closed.
* feat(ssh): make Windows proxy connection reuse an opt-in per-target setting
The loopback route rewrites HostName, which ssh_config can key settings on,
so it no longer turns on by itself: a new optional systemSshProxyConnectionReuse
field (default off, Windows only) enables it from the SSH host's Advanced
settings. Old clients and servers ignore or omit the field.
Admission stays as the safety net and now:
- takes the forward's sshd port and HostKeyAlias from the direct `ssh -G`,
so a manual host's own Host block cannot send the route to another sshd;
- refuses any non-routing value with a `%` token, which `ssh -G` can leave
unexpanded while a routed login expands `%h` to 127.0.0.1;
- checks the security-key and agent identities of every ProxyJump hop;
- asserts the routed config really dials 127.0.0.1 with no proxy left.
* fix(ssh): offer proxy reuse only for hosts this desktop dials, refuse ${VAR} paths
- The switch now appears only for this desktop's own SSH hosts. A paired
server's add-host dialog never shows or sends it, so an older server's
strict schema is never handed the new field, and a client's platform no
longer decides a server's setting.
- Admission also refuses `${VAR}` references, which `ssh -G` prints
unexpanded while a login expands them.
- Advanced no longer auto-opens just for the opt-in.
---------
Co-authored-by: Bingxin Xu <xubingxin.xbx@gmail.com>
* fix(ssh): the SSH password dialog no longer closes itself and comes back
A host that drops an unanswered login no longer replaces the open prompt
with a new one, a background dial no longer replaces a connection whose
reconnect is asking the user, one Cancel closes every open prompt for that
host, and a paired client's automatic connect stays automatic.
Co-Authored-By: Claude <noreply@anthropic.com>
* fix(ssh): a typed password its reconnect has yet to use is not replaced by a background dial
Co-Authored-By: Claude <noreply@anthropic.com>
---------
Co-authored-by: m4air <m4air@Mac.localdomain>
Co-authored-by: Claude <noreply@anthropic.com>