cmd/tailscale: mostly fix 'tailscale ssh' on macOS (sandbox)

Still a little wonky, though. See the tcsetattr error and inability to
hit Ctrl-D, for instance:

    bradfitz@laptop ~ % tailscale.app ssh foo@bar
    tcsetattr: Operation not permitted
    # Authentication checked with Tailscale SSH.
    # Time since last authentication: 1h13m22s
    foo@bar:~$ ^D
    ^D
    ^D

Updates #4518
Updates #4529

Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>
This commit is contained in:
Brad Fitzpatrick 2022-04-26 06:57:44 -07:00 committed by Brad Fitzpatrick
parent 48e5f4ff88
commit 50eb8c5add

View File

@ -23,7 +23,6 @@
"tailscale.com/client/tailscale" "tailscale.com/client/tailscale"
"tailscale.com/envknob" "tailscale.com/envknob"
"tailscale.com/ipn/ipnstate" "tailscale.com/ipn/ipnstate"
"tailscale.com/version"
) )
var sshCmd = &ffcli.Command{ var sshCmd = &ffcli.Command{
@ -76,36 +75,52 @@ func runSSH(ctx context.Context, args []string) error {
return err return err
} }
argv := append([]string{ argv := []string{ssh}
ssh,
if envknob.Bool("TS_DEBUG_SSH_EXEC") {
argv = append(argv, "-vvv")
}
argv = append(argv,
// Only trust SSH hosts that we know about. // Only trust SSH hosts that we know about.
"-o", fmt.Sprintf("UserKnownHostsFile %q", knownHostsFile), "-o", fmt.Sprintf("UserKnownHostsFile %q", knownHostsFile),
"-o", "UpdateHostKeys no", "-o", "UpdateHostKeys no",
"-o", "StrictHostKeyChecking yes", "-o", "StrictHostKeyChecking yes",
)
"-o", fmt.Sprintf("ProxyCommand %q --socket=%q nc %%h %%p", // TODO(bradfitz): nc is currently broken on macOS:
tailscaleBin, // https://github.com/tailscale/tailscale/issues/4529
rootArgs.socket, // So don't use it for now. MagicDNS is usually working on macOS anyway
), // and they're not in userspace mode, so 'nc' isn't very useful.
if runtime.GOOS != "darwin" {
argv = append(argv,
"-o", fmt.Sprintf("ProxyCommand %q --socket=%q nc %%h %%p",
tailscaleBin,
rootArgs.socket,
))
}
// Explicitly rebuild the user@host argument rather than // Explicitly rebuild the user@host argument rather than
// passing it through. In general, the use of OpenSSH's ssh // passing it through. In general, the use of OpenSSH's ssh
// binary is a crutch for now. We don't want to be // binary is a crutch for now. We don't want to be
// Hyrum-locked into passing through all OpenSSH flags to the // Hyrum-locked into passing through all OpenSSH flags to the
// OpenSSH client forever. We try to make our flags and args // OpenSSH client forever. We try to make our flags and args
// be compatible, but only a subset. The "tailscale ssh" // be compatible, but only a subset. The "tailscale ssh"
// command should be a simple and portable one. If they want // command should be a simple and portable one. If they want
// to use a different one, we'll later be making stock ssh // to use a different one, we'll later be making stock ssh
// work well by default too. (doing things like automatically // work well by default too. (doing things like automatically
// setting known_hosts, etc) // setting known_hosts, etc)
username + "@" + hostForSSH, argv = append(argv, username+"@"+hostForSSH)
}, argRest...)
if runtime.GOOS == "windows" || version.IsSandboxedMacOS() { argv = append(argv, argRest...)
// Don't use syscall.Exec on Windows or in the macOS sandbox.
if envknob.Bool("TS_DEBUG_SSH_EXEC") {
log.Printf("Running: %q, %q ...", ssh, argv)
}
if runtime.GOOS == "windows" {
// Don't use syscall.Exec on Windows.
cmd := exec.Command(ssh, argv[1:]...) cmd := exec.Command(ssh, argv[1:]...)
cmd.Stderr = os.Stderr cmd.Stdin = os.Stdin
cmd.Stdout = os.Stdout cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr cmd.Stderr = os.Stderr
var ee *exec.ExitError var ee *exec.ExitError
@ -116,9 +131,6 @@ func runSSH(ctx context.Context, args []string) error {
return err return err
} }
if envknob.Bool("TS_DEBUG_SSH_EXEC") {
log.Printf("Running: %q, %q ...", ssh, argv)
}
if err := syscall.Exec(ssh, argv, os.Environ()); err != nil { if err := syscall.Exec(ssh, argv, os.Environ()); err != nil {
return err return err
} }