|
|
@ -8,12 +8,15 @@
|
|
|
|
package tailssh
|
|
|
|
package tailssh
|
|
|
|
|
|
|
|
|
|
|
|
import (
|
|
|
|
import (
|
|
|
|
|
|
|
|
"bytes"
|
|
|
|
"context"
|
|
|
|
"context"
|
|
|
|
"errors"
|
|
|
|
"errors"
|
|
|
|
"fmt"
|
|
|
|
"fmt"
|
|
|
|
"net"
|
|
|
|
"net"
|
|
|
|
|
|
|
|
"os"
|
|
|
|
"os/exec"
|
|
|
|
"os/exec"
|
|
|
|
"os/user"
|
|
|
|
"os/user"
|
|
|
|
|
|
|
|
"strings"
|
|
|
|
"testing"
|
|
|
|
"testing"
|
|
|
|
"time"
|
|
|
|
"time"
|
|
|
|
|
|
|
|
|
|
|
@ -25,6 +28,7 @@ import (
|
|
|
|
"tailscale.com/tailcfg"
|
|
|
|
"tailscale.com/tailcfg"
|
|
|
|
"tailscale.com/tstest"
|
|
|
|
"tailscale.com/tstest"
|
|
|
|
"tailscale.com/types/logger"
|
|
|
|
"tailscale.com/types/logger"
|
|
|
|
|
|
|
|
"tailscale.com/util/lineread"
|
|
|
|
"tailscale.com/wgengine"
|
|
|
|
"tailscale.com/wgengine"
|
|
|
|
)
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
@ -231,12 +235,78 @@ func TestSSH(t *testing.T) {
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}()
|
|
|
|
}()
|
|
|
|
|
|
|
|
|
|
|
|
got, err := exec.Command("ssh",
|
|
|
|
execSSH := func(args ...string) *exec.Cmd {
|
|
|
|
|
|
|
|
cmd := exec.Command("ssh",
|
|
|
|
"-p", fmt.Sprint(port),
|
|
|
|
"-p", fmt.Sprint(port),
|
|
|
|
"-o", "StrictHostKeyChecking=no",
|
|
|
|
"-o", "StrictHostKeyChecking=no",
|
|
|
|
"user@127.0.0.1", "env").CombinedOutput()
|
|
|
|
"user@127.0.0.1")
|
|
|
|
|
|
|
|
cmd.Args = append(cmd.Args, args...)
|
|
|
|
|
|
|
|
return cmd
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
t.Run("env", func(t *testing.T) {
|
|
|
|
|
|
|
|
cmd := execSSH("env")
|
|
|
|
|
|
|
|
cmd.Env = append(os.Environ(), "LANG=foo")
|
|
|
|
|
|
|
|
got, err := cmd.CombinedOutput()
|
|
|
|
if err != nil {
|
|
|
|
if err != nil {
|
|
|
|
t.Fatal(err)
|
|
|
|
t.Fatal(err)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
t.Logf("Got: %s", got)
|
|
|
|
m := parseEnv(got)
|
|
|
|
|
|
|
|
if got := m["USER"]; got == "" || got != u.Username {
|
|
|
|
|
|
|
|
t.Errorf("USER = %q; want %q", got, u.Username)
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
if got := m["HOME"]; got == "" || got != u.HomeDir {
|
|
|
|
|
|
|
|
t.Errorf("HOME = %q; want %q", got, u.HomeDir)
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
if got := m["PWD"]; got == "" || got != u.HomeDir {
|
|
|
|
|
|
|
|
t.Errorf("PWD = %q; want %q", got, u.HomeDir)
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
if got := m["SHELL"]; got == "" {
|
|
|
|
|
|
|
|
t.Errorf("no SHELL")
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
if got, want := m["LANG"], "foo"; got != want {
|
|
|
|
|
|
|
|
t.Errorf("LANG = %q; want %q", got, want)
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
t.Logf("got: %+v", m)
|
|
|
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
t.Run("stdout_stderr", func(t *testing.T) {
|
|
|
|
|
|
|
|
cmd := execSSH("sh", "-c", "echo foo; echo bar >&2")
|
|
|
|
|
|
|
|
var outBuf, errBuf bytes.Buffer
|
|
|
|
|
|
|
|
cmd.Stdout = &outBuf
|
|
|
|
|
|
|
|
cmd.Stderr = &errBuf
|
|
|
|
|
|
|
|
if err := cmd.Run(); err != nil {
|
|
|
|
|
|
|
|
t.Fatal(err)
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
t.Logf("Got: %q and %q", outBuf.Bytes(), errBuf.Bytes())
|
|
|
|
|
|
|
|
// TODO: figure out why these aren't right. should be
|
|
|
|
|
|
|
|
// "foo\n" and "bar\n", not "\n" and "bar\n".
|
|
|
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
t.Run("stdin", func(t *testing.T) {
|
|
|
|
|
|
|
|
cmd := execSSH("cat")
|
|
|
|
|
|
|
|
var outBuf bytes.Buffer
|
|
|
|
|
|
|
|
cmd.Stdout = &outBuf
|
|
|
|
|
|
|
|
const str = "foo\nbar\n"
|
|
|
|
|
|
|
|
cmd.Stdin = strings.NewReader(str)
|
|
|
|
|
|
|
|
if err := cmd.Run(); err != nil {
|
|
|
|
|
|
|
|
t.Fatal(err)
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
if got := outBuf.String(); got != str {
|
|
|
|
|
|
|
|
t.Errorf("got %q; want %q", got, str)
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
})
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
func parseEnv(out []byte) map[string]string {
|
|
|
|
|
|
|
|
e := map[string]string{}
|
|
|
|
|
|
|
|
lineread.Reader(bytes.NewReader(out), func(line []byte) error {
|
|
|
|
|
|
|
|
i := bytes.IndexByte(line, '=')
|
|
|
|
|
|
|
|
if i == -1 {
|
|
|
|
|
|
|
|
return nil
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
e[string(line[:i])] = string(line[i+1:])
|
|
|
|
|
|
|
|
return nil
|
|
|
|
|
|
|
|
})
|
|
|
|
|
|
|
|
return e
|
|
|
|
}
|
|
|
|
}
|
|
|
|