fix(ssh): put tailscale ssh args after the destination (swamp-club #2561, #2604) #330

Merged
stack72 merged 2 commits from 2561 into main 2026-09-28 18:50:00 +00:00
Owner

Fixes swamp-club #2561 and #2604 (@swamp/ssh, tailscale transport).

Problem

tailscale ssh defines no flags and rejects any before the destination (flag provided but not defined), but hands everything after the destination to the system ssh, which re-parses options that follow the host. The extension got this wrong in two ways:

  • #2561: exec/script/check/collect-host-public-key never added -o SendEnv=<key> for tailscale hosts, so host and per-call env never reached the remote.
  • #2604: forward built tailscale ssh -N -L <spec> <dest>, which always failed on -N; any non-empty sshExtraArgs (spliced before the destination) failed the same way.

Change

  • New tailscaleSshArgv in runner.ts builds the one accepted shape: tailscale ssh -- <dest> [sshExtraArgs] <ssh args>.
  • Exec: tailscale ssh -- <dest> [sshExtraArgs] -o SendEnv=K... -- <cmd>. The trailing -- stops ssh reading a --leading command as options.
  • Forward: tailscale ssh -- <dest> [sshExtraArgs] -N -L|-R <spec>.
  • sendEnvKeys drops keys that are not shell identifiers (both transports): ssh reads */? in SendEnv as wildcards, so a key like AWS_* forwarded every matching runner env var. Raised by the pre-PR adversarial review.
  • README: Tailscale SSH acceptEnv (destination on v1.76.0+) alongside sshd AcceptEnv, new sshExtraArgs meaning (not used by copy), forward argv, wildcard-key note.
  • Model version 2026.09.28.1 with an upgrades entry; manifest bumped.

Verification

  • Tests pin exact argv for exec and forward, including an invariant that only <bin> ssh -- precedes the destination; operations_test.ts drives runExec on a tailscale host and checks SendEnv argv + spawn env. The new tests fail on the old builders; 323 pass.
  • Manual: argv from the new builders run through the installed tailscale 1.102.4 with a fake ssh on PATH is accepted, SendEnv/-N/-L/sshExtraArgs arrive after the host, and the env value is inherited. Real OpenSSH 10.3 with -G parses them (sendenv APP, serveraliveinterval 15, localforward 9090).
  • Pre-PR verification on 769159d8a: verify-build 13/14 passed (codegen idempotency skipped), including the upgrade-path test; verify-reviews code-review pass, adversarial-review pass (no critical/high/medium), ci-security-review skipped. Attestation posted.

🤖 Generated with Claude Code

Fixes swamp-club #2561 and #2604 (`@swamp/ssh`, tailscale transport). ## Problem `tailscale ssh` defines no flags and rejects any before the destination (`flag provided but not defined`), but hands everything after the destination to the system `ssh`, which re-parses options that follow the host. The extension got this wrong in two ways: - **#2561**: exec/script/check/collect-host-public-key never added `-o SendEnv=<key>` for tailscale hosts, so host and per-call `env` never reached the remote. - **#2604**: `forward` built `tailscale ssh -N -L <spec> <dest>`, which always failed on `-N`; any non-empty `sshExtraArgs` (spliced before the destination) failed the same way. ## Change - New `tailscaleSshArgv` in `runner.ts` builds the one accepted shape: `tailscale ssh -- <dest> [sshExtraArgs] <ssh args>`. - Exec: `tailscale ssh -- <dest> [sshExtraArgs] -o SendEnv=K... -- <cmd>`. The trailing `--` stops ssh reading a `-`-leading command as options. - Forward: `tailscale ssh -- <dest> [sshExtraArgs] -N -L|-R <spec>`. - `sendEnvKeys` drops keys that are not shell identifiers (both transports): ssh reads `*`/`?` in `SendEnv` as wildcards, so a key like `AWS_*` forwarded every matching runner env var. Raised by the pre-PR adversarial review. - README: Tailscale SSH `acceptEnv` (destination on v1.76.0+) alongside sshd `AcceptEnv`, new `sshExtraArgs` meaning (not used by `copy`), forward argv, wildcard-key note. - Model version 2026.09.28.1 with an upgrades entry; manifest bumped. ## Verification - Tests pin exact argv for exec and forward, including an invariant that only `<bin> ssh --` precedes the destination; `operations_test.ts` drives `runExec` on a tailscale host and checks SendEnv argv + spawn env. The new tests fail on the old builders; 323 pass. - Manual: argv from the new builders run through the installed tailscale 1.102.4 with a fake `ssh` on PATH is accepted, SendEnv/-N/-L/sshExtraArgs arrive after the host, and the env value is inherited. Real OpenSSH 10.3 with `-G` parses them (`sendenv APP`, `serveraliveinterval 15`, `localforward 9090`). - Pre-PR verification on 769159d8a: verify-build 13/14 passed (codegen idempotency skipped), including the upgrade-path test; verify-reviews code-review pass, adversarial-review pass (no critical/high/medium), ci-security-review skipped. Attestation posted. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
`tailscale ssh` defines no flags and rejects any placed before the
destination, but passes everything after it to the system ssh, which
parses options that follow the host. Build every tailscale argv as
`tailscale ssh -- <dest> [sshExtraArgs] <ssh args>`:

- exec/script/check/collect-host-public-key now forward host and
  per-call env with `-o SendEnv=<key>`, and end with `-- <command>`
  (#2561).
- forward emits `-N -L|-R <spec>` after the destination; it previously
  failed with `flag provided but not defined: -N` (#2604).
- sshExtraArgs reach the system ssh instead of failing the call (#2604).

README documents Tailscale SSH `acceptEnv` (v1.76.0+) alongside sshd
`AcceptEnv`. Model version 2026.09.28.1 with an upgrades entry.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
fix(ssh): never emit wildcard SendEnv patterns for env keys
All checks were successful
CI / Review Integrity (pull_request) Successful in 1m1s
CI / Validate Attestation (pull_request) Successful in 1m15s
769159d8ae
ssh reads `*` and `?` in a SendEnv pattern as wildcards, so an env key
such as `AWS_*` forwarded every matching variable from the runner's own
environment. With tailscale hosts now forwarding env too (swamp-club
#2561), drop keys that are not shell identifiers from the SendEnv set
for both transports. Raised by the pre-PR adversarial review.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
stack72 deleted branch 2561 2026-09-28 18:50:00 +00:00
Sign in to join this conversation.
No reviewers
No labels
No milestone
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
swamp-club/swamp-extensions!330
No description provided.