Skip to content

fix(macos): auth-capable GIT_SSH_COMMAND via socat when present - #452

Open
smolyn wants to merge 2 commits into
anthropics:mainfrom
ubc:gs/macos-socat-git-ssh
Open

smolyn wants to merge 2 commits into
anthropics:mainfrom
ubc:gs/macos-socat-git-ssh

Conversation

@smolyn

@smolyn smolyn commented Aug 6, 2026

Copy link
Copy Markdown

Problem

On macOS, the sandbox's GIT_SSH_COMMAND uses BSD nc as its ProxyCommand:

ssh -o ControlMaster=no -o ControlPath=none -o ProxyCommand='nc -X 5 -x localhost:<socks-port> %h %p'

BSD nc only speaks the SOCKS5 no-auth method, but when SRT owns the proxy it always sets a per-session auth token — so every sandboxed git-over-SSH attempt dies at the SOCKS handshake with nc: authentication method negotiation failed. The code comment in generateProxyEnvVars acknowledges this and punts users to git-over-HTTPS.

Linux never had this problem: it emits

socat - PROXY:localhost:%h:%p,proxyport=<http-port>,proxyauth=<user>:<token>

— an HTTP CONNECT tunnel that can present Proxy-Authorization.

Fix

macOS now emits the identical socat spelling whenever whichSync('socat') finds socat on the host PATH and httpProxyPort is set, keeping the nc spelling as the fallback for hosts without socat. Unlike Linux (where socat is a required dependency), socat stays optional on macOS — no new install requirement, just better behavior when it's present.

This works because:

  • the mux serves HTTP CONNECT on the same port on macOS,
  • the terminate proxy already sniffs for a TLS ClientHello before MITM-ing, so non-TLS SSH bytes pass through opaquely (pinned by connect-non-tls.test.ts),
  • userRaw (not the percent-encoded form) goes into proxyauth=, matching the Linux branch — socat sends it verbatim in the Basic header.

whichSync is called per generateProxyEnvVars invocation (~1-5 ms spawnSync); the macOS wrapper already probes PATH per wrap for the shell, so this adds no meaningful overhead and keeps the check trivially mockable in tests.

Verified end-to-end on macOS: with an ssh-agent socket allowed via network.allowUnixSockets and socat installed, git ls-remote git@github.com:... succeeds inside the sandbox against the auth-required session proxy (and against a private GitLab instance).

Tests

Four new cases in test/sandbox/proxy-env-vars.test.ts (spy pattern from check-dependencies.test.ts) pin all macOS shapes:

  • socat + auth token → socat spelling with proxyauth=srt:<token>
  • socat + per-command attribution → proxyauth=srt.<base64>:<token> (userRaw verbatim)
  • socat, no token → socat spelling without proxyauth
  • no socat → existing nc -X 5 fallback

Also fixes stale "Linux-only" wording in http-proxy.ts / connect-non-tls.test.ts (the non-TLS CONNECT path now matters on macOS too) and the SOCKS5→HTTP CONNECT misnomer in the linux-sandbox-utils.ts docblock.

The macOS GIT_SSH_COMMAND used BSD nc for its ProxyCommand, and nc only
speaks the SOCKS5 no-auth method. When SRT owns the proxy it always sets
a per-session auth token, so every sandboxed git-over-ssh attempt died
at the SOCKS handshake ("authentication method negotiation failed") —
the code comment acknowledged this and punted users to git-over-https.

Linux never had the problem because it emits
  socat - PROXY:localhost:%h:%p,proxyport=<port>,proxyauth=<user>:<token>
— an HTTP CONNECT tunnel that can present Proxy-Authorization. The mux
serves HTTP CONNECT on the same port on macOS, and the terminate proxy
already sniffs for a ClientHello before MITM-ing, so non-TLS SSH bytes
pass through opaquely (pinned by connect-non-tls.test.ts).

macOS now emits the identical socat spelling whenever socat is found on
PATH (whichSync, per generateProxyEnvVars call — the wrapper already
probes PATH per wrap for the shell) and httpProxyPort is set, keeping
the nc spelling as the fallback for hosts without socat. socat stays an
optional macOS dependency, unlike Linux where it is mandatory.

Tests pin all four macOS shapes: socat+auth, socat+per-command
attribution username (userRaw, not percent-encoded — the Basic header
takes it verbatim), socat without auth, and the nc fallback. Also fixes
stale "Linux-only" wording in http-proxy.ts / connect-non-tls.test.ts
and the SOCKS5 misnomer in the linux-sandbox-utils docblock.

This branch has not been deployed

No deployments
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.

1 participant