tool for detachable ptys with optional tailscale discovery
Something went wrong. Try again.
12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182package remote
import ( "bytes" "context" "errors" "fmt" "os" "os/exec" "path/filepath" "strconv" "strings" "syscall" "testing" "time"
"github.com/creack/pty")
// a batch ssh run must not reach the terminal: tssh ignores BatchMode and// reads host key answers from /dev/tty, stealing keys typed into the picker.// the check re-runs this test under a pty it owns, with a fake tssh that// reports whether it could open /dev/ttyfunc TestBatchSSHHasNoTerminal(t *testing.T) { if os.Getenv("TOBI_TEST_TTY_PROBE") == "1" { _, err := os.Open("/dev/tty") out, _ := sshRun(context.Background(), "host", nil, sshOpts{cmd: "true", batch: true, timeout: "1"}) fmt.Printf("helper:%v ssh:%s", err == nil, strings.TrimSpace(out)) return } dir := t.TempDir() fake := "#!/bin/sh\n(exec 3</dev/tty) 2>/dev/null && echo tty || echo notty\n" if err := os.WriteFile(filepath.Join(dir, "tssh"), []byte(fake), 0755); err != nil { t.Fatal(err) } master, tty, err := pty.Open() if err != nil { t.Fatal(err) } defer master.Close() defer tty.Close() var out bytes.Buffer c := exec.Command(os.Args[0], "-test.run=^TestBatchSSHHasNoTerminal$") c.Env = append(os.Environ(), "TOBI_TEST_TTY_PROBE=1", "PATH="+dir+":/usr/bin:/bin") c.Stdin, c.Stdout, c.Stderr = tty, &out, &out c.SysProcAttr = &syscall.SysProcAttr{Setsid: true, Setctty: true} if err := c.Run(); err != nil { t.Fatalf("%v: %s", err, out.String()) } if !strings.Contains(out.String(), "helper:true ssh:notty") { t.Fatalf("want the helper on a terminal and ssh off it, got %q", out.String()) }}
// a probe that times out takes its ProxyCommand with it: killing only ssh// orphaned a tailscale nc per unreachable hostfunc TestBatchSSHTimeoutKillsItsChildren(t *testing.T) { dir := t.TempDir() pidFile := filepath.Join(dir, "child.pid") fake := "#!/bin/sh\nsleep 60 &\necho $! > " + pidFile + "\nwait\n" if err := os.WriteFile(filepath.Join(dir, "tssh"), []byte(fake), 0755); err != nil { t.Fatal(err) } t.Setenv("PATH", dir+":/usr/bin:/bin") ctx, cancel := context.WithTimeout(context.Background(), 300*time.Millisecond) defer cancel() if _, err := sshRun(ctx, "host", nil, sshOpts{cmd: "true", batch: true, timeout: "1"}); !errors.Is(err, ErrUnreachable) { t.Fatalf("a timed out run should be unreachable, got %v", err) } data, err := os.ReadFile(pidFile) if err != nil { t.Fatal(err) } pid, _ := strconv.Atoi(strings.TrimSpace(string(data))) for deadline := time.Now().Add(time.Second); syscall.Kill(pid, 0) == nil; time.Sleep(10 * time.Millisecond) { if time.Now().After(deadline) { _ = syscall.Kill(pid, syscall.SIGKILL) t.Fatalf("the fake ssh's child %d outlived the timeout", pid) } }}