diff --git a/internal/sshdialer/server_test.go b/internal/sshdialer/server_test.go new file mode 100644 index 000000000..0a301c4ac --- /dev/null +++ b/internal/sshdialer/server_test.go @@ -0,0 +1,592 @@ +package sshdialer_test + +import ( + "bytes" + "context" + "crypto/md5" + "encoding/binary" + "errors" + "fmt" + "io" + "net" + "net/http" + "os" + "path/filepath" + "strconv" + "strings" + "sync" + "testing" + "time" + + "golang.org/x/crypto/ssh" +) + +type SSHServer struct { + lock sync.Locker + dockerServer http.Server + dockerListener listener + dockerHost string + hostIPv4 string + hostIPv6 string + portIPv4 int + portIPv6 int + hasDialStdio bool + isWin bool +} + +func (s *SSHServer) SetIsWindows(v bool) { + s.lock.Lock() + defer s.lock.Unlock() + s.isWin = v +} + +func (s *SSHServer) IsWindows() bool { + s.lock.Lock() + defer s.lock.Unlock() + return s.isWin +} + +func (s *SSHServer) SetDockerHostEnvVar(host string) { + s.lock.Lock() + defer s.lock.Unlock() + s.dockerHost = host +} + +func (s *SSHServer) GetDockerHostEnvVar() string { + s.lock.Lock() + defer s.lock.Unlock() + return s.dockerHost +} + +func (s *SSHServer) HasDialStdio() bool { + s.lock.Lock() + defer s.lock.Unlock() + return s.hasDialStdio +} + +func (s *SSHServer) SetHasDialStdio(v bool) { + s.lock.Lock() + defer s.lock.Unlock() + s.hasDialStdio = v +} + +const dockerUnixSocket = "/home/testuser/test.sock" +const dockerTCPSocket = "localhost:1234" + +// We need to set up SSH server against which we will run the tests. +// This will return SSHServer structure representing the state of the testing server. +// It also returns clean up procedure stopSSH used to shut down the server. +func prepareSSHServer(t *testing.T) (sshServer *SSHServer, stopSSH func(), err error) { + ctx, cancel := context.WithCancel(context.Background()) + defer func() { + if err != nil { + cancel() + } + }() + httpServerErrChan := make(chan error) + pollingLoopErr := make(chan error) + pollingLoopIPv6Err := make(chan error) + + handlePing := http.HandlerFunc(func(writer http.ResponseWriter, request *http.Request) { + writer.Header().Add("Content-Type", "text/plain") + writer.WriteHeader(200) + _, _ = writer.Write([]byte("OK")) + }) + + sshServer = &SSHServer{ + dockerServer: http.Server{ + Handler: handlePing, + }, + dockerListener: listener{conns: make(chan net.Conn), closed: make(chan struct{})}, + lock: &sync.Mutex{}, + } + + sshTCPListener, err := net.Listen("tcp4", "localhost:0") + if err != nil { + return + } + + hasIPv6 := true + sshTCP6Listener, err := net.Listen("tcp6", "localhost:0") + if err != nil { + hasIPv6 = false + t.Log(err) + } + + host, p, err := net.SplitHostPort(sshTCPListener.Addr().String()) + if err != nil { + return + } + port, err := strconv.ParseInt(p, 10, 32) + if err != nil { + return + } + sshServer.hostIPv4 = host + sshServer.portIPv4 = int(port) + + if hasIPv6 { + host, p, err = net.SplitHostPort(sshTCP6Listener.Addr().String()) + if err != nil { + return + } + port, err = strconv.ParseInt(p, 10, 32) + if err != nil { + return + } + sshServer.hostIPv6 = host + sshServer.portIPv6 = int(port) + } + + t.Logf("Listening on %s", sshTCPListener.Addr()) + if hasIPv6 { + t.Logf("Listening on %s", sshTCP6Listener.Addr()) + } + + go func() { + httpServerErrChan <- sshServer.dockerServer.Serve(&sshServer.dockerListener) + }() + + stopSSH = func() { + var err error + cancel() + + stopCtx, cancel := context.WithTimeout(context.Background(), time.Second*5) + defer cancel() + err = sshServer.dockerServer.Shutdown(stopCtx) + if err != nil { + t.Error(err) + } + + err = <-httpServerErrChan + if err != nil && !strings.Contains(err.Error(), "Server closed") { + t.Error(err) + } + + sshTCPListener.Close() + err = <-pollingLoopErr + if err != nil && !errors.Is(err, net.ErrClosed) { + t.Error(err) + } + + if hasIPv6 { + sshTCP6Listener.Close() + err = <-pollingLoopIPv6Err + if err != nil && !errors.Is(err, net.ErrClosed) { + t.Error(err) + } + } + } + + connChan := make(chan net.Conn) + + go func() { + for { + tcpConn, err := sshTCPListener.Accept() + if err != nil { + pollingLoopErr <- err + return + } + connChan <- tcpConn + } + }() + + if hasIPv6 { + go func() { + for { + tcpConn, err := sshTCP6Listener.Accept() + if err != nil { + pollingLoopIPv6Err <- err + return + } + connChan <- tcpConn + } + }() + } + + go func() { + for { + conn := <-connChan + go func(conn net.Conn) { + err := sshServer.handleConnection(ctx, conn) + if err != nil { + fmt.Fprintf(os.Stderr, "err: %v\n", err) + } + }(conn) + } + }() + + return sshServer, stopSSH, err +} + +func setupServerAuth(conf *ssh.ServerConfig) (err error) { + passwd := map[string]string{ + "testuser": "idkfa", + "root": "iddqd", + } + + authorizedKeysFiles := []string{"id_ed25519.pub", "id_rsa.pub"} + authorizedKeys := make(map[[16]byte][]byte, len(authorizedKeysFiles)) + for _, key := range authorizedKeysFiles { + keyFileName := filepath.Join("testdata", key) + var bs []byte + bs, err = os.ReadFile(keyFileName) + if err != nil { + return + } + var pk ssh.PublicKey + pk, _, _, _, err = ssh.ParseAuthorizedKey(bs) + if err != nil { + return + } + + bs = pk.Marshal() + authorizedKeys[md5.Sum(bs)] = bs + } + + *conf = ssh.ServerConfig{ + PasswordCallback: func(conn ssh.ConnMetadata, password []byte) (*ssh.Permissions, error) { + if p, ok := passwd[conn.User()]; ok && p == string(password) { + return nil, nil + } + return nil, fmt.Errorf("incorrect password %q for user %q", string(password), conn.User()) + }, + PublicKeyCallback: func(conn ssh.ConnMetadata, key ssh.PublicKey) (*ssh.Permissions, error) { + keyBytes := key.Marshal() + if b, ok := authorizedKeys[md5.Sum(keyBytes)]; ok && bytes.Equal(b, keyBytes) { + return &ssh.Permissions{}, nil + } + return nil, fmt.Errorf("untrusted public key: %q", string(keyBytes)) + }, + } + + hostKeys := []string{"ssh_host_ecdsa_key", "ssh_host_ed25519_key", "ssh_host_rsa_key"} + serverKeysDir := filepath.Join("testdata", "etc", "ssh") + for _, key := range hostKeys { + keyFileName := filepath.Join(serverKeysDir, key) + var b []byte + b, err = os.ReadFile(keyFileName) + if err != nil { + return + } + var signer ssh.Signer + signer, err = ssh.ParsePrivateKey(b) + if err != nil { + return + } + conf.AddHostKey(signer) + } + + return nil +} + +func (s *SSHServer) handleConnection(ctx context.Context, conn net.Conn) error { + var config ssh.ServerConfig + setupServerAuth(&config) + sshConn, newChannels, reqs, err := ssh.NewServerConn(conn, &config) + if err != nil { + return err + } + + go func() { + <-ctx.Done() + err = sshConn.Close() + if err != nil && !errors.Is(err, net.ErrClosed) { + fmt.Fprintf(os.Stderr, "err: %v\n", err) + } + }() + + var wg sync.WaitGroup + + wg.Add(1) + go func() { + defer wg.Done() + ssh.DiscardRequests(reqs) + }() + + for newChannel := range newChannels { + wg.Add(1) + go func(newChannel ssh.NewChannel) { + defer wg.Done() + s.handleChannel(newChannel) + }(newChannel) + } + + wg.Wait() + + return nil +} + +func (s *SSHServer) handleChannel(newChannel ssh.NewChannel) { + var err error + switch newChannel.ChannelType() { + case "session": + s.handleSession(newChannel) + case "direct-streamlocal@openssh.com", "direct-tcpip": + s.handleTunnel(newChannel) + default: + err = newChannel.Reject(ssh.UnknownChannelType, fmt.Sprintf("type of channel %q is not supported", newChannel.ChannelType())) + if err != nil { + fmt.Fprintf(os.Stderr, "err: %v\n", err) + } + } +} + +func (s *SSHServer) handleSession(newChannel ssh.NewChannel) { + ch, reqs, err := newChannel.Accept() + if err != nil { + fmt.Fprintf(os.Stderr, "err: %v\n", err) + return + } + + defer ch.Close() + for req := range reqs { + if req.Type == "exec" { + s.handleExec(ch, req) + break + } + } +} + +func (s *SSHServer) handleExec(ch ssh.Channel, req *ssh.Request) { + var err error + err = req.Reply(true, nil) + if err != nil { + fmt.Fprintf(os.Stderr, "err: %v\n", err) + return + } + execData := struct { + Command string + }{} + err = ssh.Unmarshal(req.Payload, &execData) + if err != nil { + fmt.Fprintf(os.Stderr, "err: %v\n", err) + return + } + + sendExitCode := func(ret uint32) { + msg := []byte{0, 0, 0, 0} + binary.BigEndian.PutUint32(msg, ret) + _, err = ch.SendRequest("exit-status", false, msg) + if err != nil && !errors.Is(err, io.EOF) { + fmt.Fprintf(os.Stderr, "err: %v\n", err) + } + } + + var ret uint32 + switch { + case execData.Command == "set": + ret = 0 + dh := s.GetDockerHostEnvVar() + if dh != "" { + _, _ = fmt.Fprintf(ch, "DOCKER_HOST=%s\n", dh) + } + case execData.Command == "systeminfo" && s.IsWindows(): + _, _ = fmt.Fprintln(ch, "something Windows something") + ret = 0 + case execData.Command == "docker system dial-stdio" && s.HasDialStdio(): + pr, pw, conn := newPipeConn() + + select { + case s.dockerListener.conns <- conn: + case <-s.dockerListener.closed: + err = ch.Close() + if err != nil { + fmt.Fprintf(os.Stderr, "err: %v\n", err) + } + } + + cpDone := make(chan struct{}) + go func() { + var err error + _, err = io.Copy(pw, ch) + if err != nil { + fmt.Fprintf(os.Stderr, "err: %v\n", err) + } + err = pw.Close() + if err != nil { + fmt.Fprintf(os.Stderr, "err: %v\n", err) + } + cpDone <- struct{}{} + }() + + _, err = io.Copy(ch, pr) + if err != nil { + fmt.Fprintf(os.Stderr, "err: %v\n", err) + } + err = pr.Close() + if err != nil { + fmt.Fprintf(os.Stderr, "err: %v\n", err) + } + + <-cpDone + + <-conn.closed + if err != nil { + fmt.Fprintf(os.Stderr, "err: %v\n", err) + } + + ret = 0 + default: + _, _ = fmt.Fprintf(ch.Stderr(), "unknown command: %q\n", execData.Command) + ret = 127 + } + sendExitCode(ret) +} + +func newPipeConn() (*io.PipeReader, *io.PipeWriter, *rwcConn) { + pr0, pw0 := io.Pipe() + pr1, pw1 := io.Pipe() + rwc := pipeReaderWriterCloser{r: pr0, w: pw1} + return pr1, pw0, newRWCConn(rwc) +} + +type pipeReaderWriterCloser struct { + r *io.PipeReader + w *io.PipeWriter +} + +func (d pipeReaderWriterCloser) Read(p []byte) (n int, err error) { + return d.r.Read(p) +} + +func (d pipeReaderWriterCloser) Write(p []byte) (n int, err error) { + return d.w.Write(p) +} + +func (d pipeReaderWriterCloser) Close() error { + err := d.r.Close() + if err != nil { + return err + } + return d.w.Close() +} + +func (s *SSHServer) handleTunnel(newChannel ssh.NewChannel) { + var err error + + switch newChannel.ChannelType() { + case "direct-streamlocal@openssh.com": + bs := newChannel.ExtraData() + unixExtraData := struct { + SocketPath string + Reserved0 string + Reserved1 uint32 + }{} + err = ssh.Unmarshal(bs, &unixExtraData) + if err != nil { + fmt.Fprintf(os.Stderr, "err: %v\n", err) + return + } + if unixExtraData.SocketPath != dockerUnixSocket { + err = newChannel.Reject(ssh.ConnectionFailed, fmt.Sprintf("bad socket: %q", unixExtraData.SocketPath)) + if err != nil { + fmt.Fprintf(os.Stderr, "err: %v\n", err) + } + return + } + case "direct-tcpip": + bs := newChannel.ExtraData() + tcpExtraData := struct { //nolint:maligned + HostLocal string + PortLocal uint32 + HostRemote string + PortRemote uint32 + }{} + err = ssh.Unmarshal(bs, &tcpExtraData) + if err != nil { + fmt.Fprintf(os.Stderr, "err: %v\n", err) + return + } + + hostPort := fmt.Sprintf("%s:%d", tcpExtraData.HostLocal, tcpExtraData.PortLocal) + if hostPort != dockerTCPSocket { + err = newChannel.Reject(ssh.ConnectionFailed, fmt.Sprintf("bad socket: '%s:%d'", tcpExtraData.HostLocal, tcpExtraData.PortLocal)) + if err != nil { + fmt.Fprintf(os.Stderr, "err: %v\n", err) + } + return + } + } + + ch, _, err := newChannel.Accept() + if err != nil { + fmt.Fprintf(os.Stderr, "err: %v\n", err) + return + } + conn := newRWCConn(ch) + select { + case s.dockerListener.conns <- conn: + case <-s.dockerListener.closed: + err = ch.Close() + if err != nil { + fmt.Fprintf(os.Stderr, "err: %v\n", err) + } + return + } + <-conn.closed +} + +type listener struct { + conns chan net.Conn + closed chan struct{} + o sync.Once +} + +func (l *listener) Accept() (net.Conn, error) { + select { + case <-l.closed: + return nil, net.ErrClosed + case conn := <-l.conns: + return conn, nil + } +} + +func (l *listener) Close() error { + l.o.Do(func() { + close(l.closed) + }) + return nil +} + +func (l *listener) Addr() net.Addr { + return &net.UnixAddr{Name: dockerUnixSocket, Net: "unix"} +} + +func newRWCConn(rwc io.ReadWriteCloser) *rwcConn { + return &rwcConn{rwc: rwc, closed: make(chan struct{})} +} + +type rwcConn struct { + rwc io.ReadWriteCloser + closed chan struct{} + o sync.Once +} + +func (c *rwcConn) Read(b []byte) (n int, err error) { + return c.rwc.Read(b) +} + +func (c *rwcConn) Write(b []byte) (n int, err error) { + return c.rwc.Write(b) +} + +func (c *rwcConn) Close() error { + c.o.Do(func() { + close(c.closed) + }) + return c.rwc.Close() +} + +func (c *rwcConn) LocalAddr() net.Addr { + return &net.UnixAddr{Name: dockerUnixSocket, Net: "unix"} +} + +func (c *rwcConn) RemoteAddr() net.Addr { + return &net.UnixAddr{Name: "@", Net: "unix"} +} + +func (c *rwcConn) SetDeadline(t time.Time) error { return nil } + +func (c *rwcConn) SetReadDeadline(t time.Time) error { return nil } + +func (c *rwcConn) SetWriteDeadline(t time.Time) error { return nil } diff --git a/internal/sshdialer/ssh_dialer_test.go b/internal/sshdialer/ssh_dialer_test.go index d86e9ada2..8708d7077 100644 --- a/internal/sshdialer/ssh_dialer_test.go +++ b/internal/sshdialer/ssh_dialer_test.go @@ -11,19 +11,16 @@ import ( "os/exec" "path/filepath" "runtime" - "strconv" "strings" "sync" "testing" "text/template" "time" - "github.com/docker/docker/api/types" - "github.com/docker/docker/api/types/container" - "github.com/docker/docker/client" "github.com/docker/docker/pkg/homedir" - "github.com/docker/go-connections/nat" "github.com/pkg/errors" + "github.com/sclevine/spec" + "github.com/sclevine/spec/report" "golang.org/x/crypto/ssh" "golang.org/x/crypto/ssh/agent" @@ -31,11 +28,18 @@ import ( th "github.com/buildpacks/pack/testhelpers" ) -const ( - imageName = "buildpacks/sshdialer-test-img" - containerName = "sshdialer-test-ctr" - sshPort = "22/tcp" -) +type args struct { + connStr string + credentialConfig sshdialer.Config +} +type testParams struct { + name string + args args + setUpEnv setUpEnvFn + skipOnWin bool + CreateError string + DialError string +} func TestCreateDialer(t *testing.T) { for _, privateKey := range []string{"id_ed25519", "id_rsa", "id_dsa"} { @@ -47,23 +51,11 @@ func TestCreateDialer(t *testing.T) { defer withCleanHome(t)() connConfig, cleanUp, err := prepareSSHServer(t) - if err != nil { - t.Fatal(err) - } + th.AssertNil(t, err) + defer cleanUp() + time.Sleep(time.Second * 1) - type args struct { - connStr string - credentialConfig sshdialer.Config - } - type testParams struct { - name string - args args - setUpEnv setUpEnvFn - skipOnWin bool - CreateError string - DialError string - } tests := []testParams{ { name: "read password from input", @@ -386,14 +378,16 @@ func TestCreateDialer(t *testing.T) { } for _, ttx := range tests { - tt := ttx - t.Run(tt.name, func(t *testing.T) { - // this test cannot be parallelized as they use process wide environment variable $HOME + spec.Run(t, "sshDialer/"+ttx.name, testCreateDialer(connConfig, ttx), spec.Report(report.Terminal{})) + } +} +// this test cannot be parallelized as they use process wide environment variable $HOME +func testCreateDialer(connConfig *SSHServer, tt testParams) func(t *testing.T, when spec.G, it spec.S) { + return func(t *testing.T, when spec.G, it spec.S) { + it("creates a dialer", func() { u, err := url.Parse(tt.args.connStr) - if err != nil { - t.Fatal(err) - } + th.AssertNil(t, err) if net.ParseIP(u.Hostname()).To4() == nil && connConfig.hostIPv6 == "" { t.Skip("skipping ipv6 test since test environment doesn't support ipv6 connection") @@ -424,6 +418,7 @@ func TestCreateDialer(t *testing.T) { transport := http.Transport{DialContext: dialContext} httpClient := http.Client{Transport: &transport} + defer httpClient.CloseIdleConnections() resp, err := httpClient.Get("http://docker/") if tt.DialError == "" { th.AssertEq(t, err, nil) @@ -446,171 +441,11 @@ func TestCreateDialer(t *testing.T) { if err != nil { return } - th.AssertEq(t, string(b), "Hello there!") + th.AssertEq(t, string(b), "OK") }) } } -type ConnectionConfig struct { - hostIPv4 string - hostIPv6 string - portIPv4 int - portIPv6 int -} - -// We need to set up the test container running sshd against which we will run tests. -// This will return IPv4 and IPv6 of the container, -// cleanUp procedure to remove the test container and possibly error. -func prepareSSHServer(t *testing.T) (connConfig *ConnectionConfig, cleanUp func(), err error) { - th.RequireDocker(t) - - var cleanUps []func() - cleanUp = func() { - for i := range cleanUps { - cleanUps[i]() - } - } - - ctx := context.Background() - - cli, err := client.NewClientWithOpts( - client.FromEnv, - client.WithAPIVersionNegotiation(), - ) - if err != nil { - return - } - - info, err := cli.Info(ctx) - th.SkipIf(t, info.OSType == "windows", "These tests are not yet compatible with Windows-based containers") - - wd, err := os.Getwd() - if err != nil { - return - } - - th.CreateImageFromDir(t, cli, imageName, filepath.Join(wd, "testdata")) - - config := container.Config{ - Image: imageName, - } - - connConfig = &ConnectionConfig{ - hostIPv4: "127.0.0.1", - hostIPv6: "", - portIPv4: 0, - portIPv6: 0, - } - - portBindings := []nat.PortBinding{ - {HostIP: connConfig.hostIPv4}, - } - - // docker desktop doesn't support ipv6 port bindings - // see https://github.com/docker/for-win/issues/8211 - // and https://github.com/docker/for-mac/issues/1432 - if runtime.GOOS == "linux" { - connConfig.hostIPv6 = "::1" - portBindings = append(portBindings, nat.PortBinding{HostIP: connConfig.hostIPv6}) - } - - hostConfig := &container.HostConfig{ - PortBindings: map[nat.Port][]nat.PortBinding{sshPort: portBindings}, - } - - // just in case the container has not been cleaned up - _ = cli.ContainerRemove(ctx, containerName, types.ContainerRemoveOptions{Force: true}) - - ctr, err := cli.ContainerCreate(ctx, &config, hostConfig, nil, nil, containerName) - if err != nil { - return - } - - defer func() { - f := func() { cli.ContainerRemove(ctx, ctr.ID, types.ContainerRemoveOptions{Force: true}) } - if err != nil { - f() - } else { - cleanUps = append(cleanUps, f) - } - }() - - ctrStartOpts := types.ContainerStartOptions{} - err = cli.ContainerStart(ctx, ctr.ID, ctrStartOpts) - if err != nil { - return - } - - defer func() { - f := func() { cli.ContainerKill(ctx, ctr.ID, "SIGKILL") } - if err != nil { - f() - } else { - cleanUps = append(cleanUps, f) - } - }() - - var ctrJSON types.ContainerJSON - ctrJSON, err = cli.ContainerInspect(ctx, ctr.ID) - if err != nil { - return - } - - sshPortBinds := ctrJSON.NetworkSettings.Ports[sshPort] - - var found bool - connConfig.portIPv4, found = portForHost(sshPortBinds, connConfig.hostIPv4) - if !found { - err = errors.Errorf("SSH port for %s not found in %+v", connConfig.hostIPv4, sshPortBinds) - return - } - - if connConfig.hostIPv6 != "" { - connConfig.portIPv6, found = portForHost(sshPortBinds, connConfig.hostIPv6) - if !found { - err = errors.Errorf("SSH port for %s not found in %+v", connConfig.hostIPv6, sshPortBinds) - return - } - } - - // wait for ssh container to start serving ssh - // overall timeout before giving up on connecting - timeout := time.After(20 * time.Second) - // wait this amount between retries - waitTicker := time.Tick(2 * time.Second) - for { - select { - case <-timeout: - err = fmt.Errorf("test container failed to start serving ssh") - return - case <-waitTicker: - } - - t.Logf("connecting to ssh: %s:%d", connConfig.hostIPv4, connConfig.portIPv4) - conn, err := net.Dial("tcp", net.JoinHostPort(connConfig.hostIPv4, strconv.Itoa(connConfig.portIPv4))) - if err != nil { - continue - } - conn.Close() - - break - } - - return connConfig, cleanUp, err -} - -func portForHost(bindings []nat.PortBinding, host string) (int, bool) { - for _, pb := range bindings { - if pb.HostIP == host { - if port, err := strconv.Atoi(pb.HostPort); err == nil { - return port, true - } - } - } - - return 0, false -} - // function that prepares testing environment and returns clean up function // this should be used in conjunction with defer: `defer fn()()` // e.g. sets environment variables or starts mock up services @@ -664,21 +499,15 @@ func withKey(t *testing.T, keyName string) setUpEnvFn { var err error home, err := os.UserHomeDir() - if err != nil { - t.Fatal(err) - } + th.AssertNil(t, err) err = os.MkdirAll(filepath.Join(home, ".ssh"), 0700) - if err != nil { - t.Fatal(err) - } + th.AssertNil(t, err) keySrc := filepath.Join("testdata", keyName) keyDest := filepath.Join(home, ".ssh", keyName) err = cp(keySrc, keyDest) - if err != nil { - t.Fatal(err) - } + th.AssertNil(t, err) return func() { os.Remove(keyDest) @@ -693,20 +522,14 @@ func withInaccessibleKey(keyName string) setUpEnvFn { var err error home, err := os.UserHomeDir() - if err != nil { - t.Fatal(err) - } + th.AssertNil(t, err) err = os.MkdirAll(filepath.Join(home, ".ssh"), 0700) - if err != nil { - t.Fatal(err) - } + th.AssertNil(t, err) keyDest := filepath.Join(home, ".ssh", keyName) _, err = os.OpenFile(keyDest, os.O_CREATE|os.O_WRONLY, 0000) - if err != nil { - t.Fatal(err) - } + th.AssertNil(t, err) return func() { os.Remove(keyDest) @@ -723,9 +546,8 @@ func withCleanHome(t *testing.T) func() { homeName = "USERPROFILE" } tmpDir, err := ioutil.TempDir("", "tmpHome") - if err != nil { - t.Fatal(err) - } + th.AssertNil(t, err) + oldHome, hadHome := os.LookupEnv(homeName) os.Setenv(homeName, tmpDir) @@ -740,16 +562,14 @@ func withCleanHome(t *testing.T) func() { } // withKnowHosts creates $HOME/.ssh/known_hosts with correct entries -func withKnowHosts(connConfig *ConnectionConfig) setUpEnvFn { +func withKnowHosts(connConfig *SSHServer) setUpEnvFn { return func(t *testing.T) func() { t.Helper() knownHosts := filepath.Join(homedir.Get(), ".ssh", "known_hosts") err := os.MkdirAll(filepath.Join(homedir.Get(), ".ssh"), 0700) - if err != nil { - t.Fatal(err) - } + th.AssertNil(t, err) _, err = os.Stat(knownHosts) if err == nil || !errors.Is(err, os.ErrNotExist) { @@ -757,9 +577,7 @@ func withKnowHosts(connConfig *ConnectionConfig) setUpEnvFn { } f, err := os.OpenFile(knownHosts, os.O_CREATE|os.O_WRONLY, 0600) - if err != nil { - t.Fatal(err) - } + th.AssertNil(t, err) defer f.Close() // generate known_hosts @@ -767,9 +585,7 @@ func withKnowHosts(connConfig *ConnectionConfig) setUpEnvFn { for _, k := range []string{"ecdsa"} { keyPath := filepath.Join(serverKeysDir, fmt.Sprintf("ssh_host_%s_key.pub", k)) key, err := ioutil.ReadFile(keyPath) - if err != nil { - t.Fatal(t) - } + th.AssertNil(t, err) fmt.Fprintf(f, "%s %s", connConfig.hostIPv4, string(key)) fmt.Fprintf(f, "[%s]:%d %s", connConfig.hostIPv4, connConfig.portIPv4, string(key)) @@ -787,16 +603,14 @@ func withKnowHosts(connConfig *ConnectionConfig) setUpEnvFn { } // withBadKnownHosts creates $HOME/.ssh/known_hosts with incorrect entries -func withBadKnownHosts(connConfig *ConnectionConfig) setUpEnvFn { +func withBadKnownHosts(connConfig *SSHServer) setUpEnvFn { return func(t *testing.T) func() { t.Helper() knownHosts := filepath.Join(homedir.Get(), ".ssh", "known_hosts") err := os.MkdirAll(filepath.Join(homedir.Get(), ".ssh"), 0700) - if err != nil { - t.Fatal(err) - } + th.AssertNil(t, err) _, err = os.Stat(knownHosts) if err == nil || !errors.Is(err, os.ErrNotExist) { @@ -804,9 +618,7 @@ func withBadKnownHosts(connConfig *ConnectionConfig) setUpEnvFn { } f, err := os.OpenFile(knownHosts, os.O_CREATE|os.O_WRONLY, 0600) - if err != nil { - t.Fatal(err) - } + th.AssertNil(t, err) defer f.Close() knownHostTemplate := `{{range $host := .}}{{$host}} ssh-dss AAAAB3NzaC1kc3MAAACBAKH4ufS3ABVb780oTgEL1eu+pI1p6YOq/1KJn5s3zm+L3cXXq76r5OM/roGEYrXWUDGRtfVpzYTAKoMWuqcVc0AZ2zOdYkoy1fSjJ3MqDGF53QEO3TXIUt3gUzmLOewwmZWle0RgMa9GHccv7XVVIZB36RR68ZEUswLaTnlVhXQ1AAAAFQCl4t/LnY7kuUI+tL2qT2XmxmiyqwAAAIB72XaO+LfyIiqBOaTkQf+5rvH1i6y6LDO1QD9pzGWUYw3y03AEveHJMjW0EjnYBKJjK39wcZNTieRyU54lhH/HWeWABn9NcQ3duEf1WSO/s7SPsFO2R6quqVSsStkqf2Yfdy4fl24mH41olwtNA6ft5nkVfkqrIa51si4jU8fBVAAAAIB8SSvyYBcyMGLUlQjzQqhhhAHer9x/1YbknVz+y5PHJLLjHjMC4ZRfLgNEojvMKQW46Te9Pwnudcwv19ho4F+kkCOfss7xjyH70gQm6Sj76DxClmnnPoSRq3qEAOMy5Oh+7vyzxm68KHqd/aOmUaiT1LgqgViS9+kNdCoVMGAMOg== mvasek@bellatrix @@ -817,9 +629,7 @@ func withBadKnownHosts(connConfig *ConnectionConfig) setUpEnvFn { tmpl := template.New(knownHostTemplate) tmpl, err = tmpl.Parse(knownHostTemplate) - if err != nil { - t.Fatal(err) - } + th.AssertNil(t, err) hosts := make([]string, 0, 4) hosts = append(hosts, connConfig.hostIPv4, fmt.Sprintf("[%s]:%d", connConfig.hostIPv4, connConfig.portIPv4)) @@ -828,9 +638,7 @@ func withBadKnownHosts(connConfig *ConnectionConfig) setUpEnvFn { } err = tmpl.Execute(f, hosts) - if err != nil { - t.Fatal(err) - } + th.AssertNil(t, err) return func() { os.Remove(knownHosts) @@ -845,9 +653,7 @@ func withBrokenKnownHosts(t *testing.T) func() { knownHosts := filepath.Join(homedir.Get(), ".ssh", "known_hosts") err := os.MkdirAll(filepath.Join(homedir.Get(), ".ssh"), 0700) - if err != nil { - t.Fatal(err) - } + th.AssertNil(t, err) _, err = os.Stat(knownHosts) if err == nil || !errors.Is(err, os.ErrNotExist) { @@ -855,15 +661,11 @@ func withBrokenKnownHosts(t *testing.T) func() { } f, err := os.OpenFile(knownHosts, os.O_CREATE|os.O_WRONLY, 0600) - if err != nil { - t.Fatal(err) - } + th.AssertNil(t, err) defer f.Close() _, err = f.WriteString("somegarbage\nsome rubish\n stuff\tqwerty") - if err != nil { - t.Fatal(err) - } + th.AssertNil(t, err) return func() { os.Remove(knownHosts) @@ -877,9 +679,7 @@ func withInaccessibleKnownHosts(t *testing.T) func() { knownHosts := filepath.Join(homedir.Get(), ".ssh", "known_hosts") err := os.MkdirAll(filepath.Join(homedir.Get(), ".ssh"), 0700) - if err != nil { - t.Fatal(err) - } + th.AssertNil(t, err) _, err = os.Stat(knownHosts) if err == nil || !errors.Is(err, os.ErrNotExist) { @@ -887,9 +687,7 @@ func withInaccessibleKnownHosts(t *testing.T) func() { } f, err := os.OpenFile(knownHosts, os.O_CREATE|os.O_WRONLY, 0000) - if err != nil { - t.Fatal(err) - } + th.AssertNil(t, err) defer f.Close() return func() { @@ -904,9 +702,7 @@ func withEmptyKnownHosts(t *testing.T) func() { knownHosts := filepath.Join(homedir.Get(), ".ssh", "known_hosts") err := os.MkdirAll(filepath.Join(homedir.Get(), ".ssh"), 0700) - if err != nil { - t.Fatal(err) - } + th.AssertNil(t, err) _, err = os.Stat(knownHosts) if err == nil || !errors.Is(err, os.ErrNotExist) { @@ -914,9 +710,7 @@ func withEmptyKnownHosts(t *testing.T) func() { } _, err = os.Create(knownHosts) - if err != nil { - t.Fatal(err) - } + th.AssertNil(t, err) return func() { os.Remove(knownHosts) @@ -960,13 +754,10 @@ func withGoodSSHAgent(t *testing.T) func() { t.Helper() key, err := ioutil.ReadFile(filepath.Join("testdata", "id_ed25519")) - if err != nil { - t.Fatal(err) - } + th.AssertNil(t, err) + signer, err := ssh.ParsePrivateKey(key) - if err != nil { - t.Fatal(err) - } + th.AssertNil(t, err) return withSSHAgent(t, signerAgent{signer}) } @@ -981,14 +772,12 @@ func withBadSSHAgent(t *testing.T) func() { func withSSHAgent(t *testing.T, ag agent.Agent) func() { t.Helper() tmpDirForSocket, err := ioutil.TempDir("", "forAuthSock") - if err != nil { - t.Fatal(err) - } + th.AssertNil(t, err) + agentSocketPath := filepath.Join(tmpDirForSocket, "agent.sock") unixListener, err := net.Listen("unix", agentSocketPath) - if err != nil { - t.Fatal(err) - } + th.AssertNil(t, err) + os.Setenv("SSH_AUTH_SOCK", agentSocketPath) ctx, cancel := context.WithCancel(context.Background()) @@ -1025,9 +814,8 @@ func withSSHAgent(t *testing.T, ag agent.Agent) func() { os.Unsetenv("SSH_AUTH_SOCK") err := unixListener.Close() - if err != nil { - t.Fatal(err) - } + th.AssertNil(t, err) + err = <-errChan if !errors.Is(err, net.ErrClosed) { @@ -1127,9 +915,8 @@ func withFixedUpSSHCLI(t *testing.T) func() { } out, err := exec.Command(which, "ssh").CombinedOutput() - if err != nil { - t.Fatal(err) - } + th.AssertNil(t, err) + sshAbsPath := string(out) sshAbsPath = strings.Trim(sshAbsPath, "\r\n") @@ -1144,15 +931,11 @@ SSH_BIN -o PasswordAuthentication=no -o ConnectTimeout=3 -o UserKnownHostsFile=% sshScript = strings.ReplaceAll(sshScript, "SSH_BIN", sshAbsPath) home, err := os.UserHomeDir() - if err != nil { - t.Fatal(err) - } + th.AssertNil(t, err) homeBin := filepath.Join(home, "bin") err = os.MkdirAll(homeBin, 0700) - if err != nil { - t.Fatal(err) - } + th.AssertNil(t, err) sshScriptName := "ssh" if runtime.GOOS == "windows" { @@ -1161,9 +944,7 @@ SSH_BIN -o PasswordAuthentication=no -o ConnectTimeout=3 -o UserKnownHostsFile=% sshScriptFullPath := filepath.Join(homeBin, sshScriptName) err = ioutil.WriteFile(sshScriptFullPath, []byte(sshScript), 0700) - if err != nil { - t.Fatal(err) - } + th.AssertNil(t, err) oldPath := os.Getenv("PATH") os.Setenv("PATH", homeBin+string(os.PathListSeparator)+oldPath) @@ -1175,96 +956,38 @@ SSH_BIN -o PasswordAuthentication=no -o ConnectTimeout=3 -o UserKnownHostsFile=% // withEmulatedDockerSystemDialStdio makes `docker system dial-stdio` viable in the testing ssh server. // It does so by appending definition of shell function named `docker` into .bashrc . -func withEmulatedDockerSystemDialStdio(connConfig *ConnectionConfig) setUpEnvFn { +func withEmulatedDockerSystemDialStdio(sshServer *SSHServer) setUpEnvFn { return func(t *testing.T) func() { t.Helper() - _, err := runRemote(`echo 'docker () { - if [ "$1" = "system" ] && [ "$2" = "dial-stdio" ]; then - if [ "$3" = "--help" ]; then - echo "\nProxy the stdio stream to the daemon connection."; - else - socat - /home/testuser/test.sock; - fi - fi - }' >> ~/.bashrc`, connConfig) - if err != nil { - t.Fatal(err) - } - + oldHasDialStdio := sshServer.HasDialStdio() + sshServer.SetHasDialStdio(true) return func() { - _, _ = runRemote(`echo 'unset -f docker' >> ~/.bashrc`, connConfig) + sshServer.SetHasDialStdio(oldHasDialStdio) } } } // withEmulatingWindows makes changes to the testing ssh server such that // the server appears to be Windows server for simple check done calling the `systeminfo` command -func withEmulatingWindows(connConfig *ConnectionConfig) setUpEnvFn { +func withEmulatingWindows(sshServer *SSHServer) setUpEnvFn { return func(t *testing.T) func() { - t.Helper() - - _, err := runRemote(`echo 'systeminfo () { - echo '\nWindows\n' - }' >> ~/.bashrc`, connConfig) - if err != nil { - t.Fatal(err) - } - + oldIsWindows := sshServer.IsWindows() + sshServer.SetIsWindows(true) return func() { - _, _ = runRemote(`echo 'unset -f systeminfo' >> ~/.bashrc`, connConfig) + sshServer.SetIsWindows(oldIsWindows) } } } -// withRemoteDockerHost sets the DOCKER_HOST environment variable in the testing ssh server. -// It does so by appending export statement to .bashrc . -func withRemoteDockerHost(host string, connConfig *ConnectionConfig) setUpEnvFn { +// withRemoteDockerHost makes changes to the testing ssh server such that +// the DOCKER_HOST environment is set to host parameter +func withRemoteDockerHost(host string, sshServer *SSHServer) setUpEnvFn { return func(t *testing.T) func() { - t.Helper() - _, err := runRemote(fmt.Sprintf(`echo 'export DOCKER_HOST=%s' >> ~/.bashrc`, host), connConfig) - if err != nil { - t.Fatal(err) - } + oldHost := sshServer.GetDockerHostEnvVar() + sshServer.SetDockerHostEnvVar(host) return func() { - runRemote(`echo 'unset DOCKER_HOST' >> ~/.bashrc`, connConfig) + sshServer.SetDockerHostEnvVar(oldHost) } } } - -// runRemote runs command it the testing ssh server -func runRemote(cmd string, connConfig *ConnectionConfig) ([]byte, error) { - u, err := url.Parse(fmt.Sprintf("ssh://testuser@%s:%d", - connConfig.hostIPv4, - connConfig.portIPv4, - )) - if err != nil { - return nil, errors.Wrap(err, "parsing url") - } - - sshClientConfig, err := sshdialer.NewSSHClientConfig(u, sshdialer.Config{ - HostKeyCallback: func(hostPort string, pubKey ssh.PublicKey) error { - return nil - }, - PasswordCallback: func() (string, error) { - return "idkfa", nil - }, - }) - if err != nil { - return nil, errors.Wrap(err, "creating ssh config") - } - - sshClient, err := ssh.Dial("tcp", u.Host, sshClientConfig) - if err != nil { - return nil, errors.Wrap(err, "connecting") - } - defer sshClient.Close() - - session, err := sshClient.NewSession() - if err != nil { - return nil, errors.Wrap(err, "starting session") - } - defer session.Close() - - return session.CombinedOutput(cmd) -} diff --git a/internal/sshdialer/testdata/Dockerfile b/internal/sshdialer/testdata/Dockerfile deleted file mode 100644 index 49dbf9607..000000000 --- a/internal/sshdialer/testdata/Dockerfile +++ /dev/null @@ -1,31 +0,0 @@ -FROM docker.io/library/golang:1.16 AS builder - -RUN mkdir /workspace/ -COPY main.go go.mod /workspace/ -WORKDIR /workspace/ -ENV CGO_ENABLED=0 -RUN go build -o serve-socket - -FROM fedora:33 - -RUN dnf in -y openssh-server passwd socat - -COPY etc/ssh /etc/ssh -RUN chmod og-rwx /etc/ssh/*_key -COPY entrypoint.sh /usr/local/bin -RUN chmod a+x /usr/local/bin/entrypoint.sh - -RUN groupadd testuser && \ - useradd testuser -g testuser -m -s /bin/bash && \ - echo iddqd | passwd root --stdin && \ - echo idkfa | passwd testuser --stdin - -RUN su testuser && mkdir /home/testuser/.ssh/ -COPY --chown=testuser:testuser id_ed25519.pub id_rsa.pub /tmp/ -RUN cat /tmp/id_ed25519.pub /tmp/id_rsa.pub >> /home/testuser/.ssh/authorized_keys - -COPY --from=builder /workspace/serve-socket /usr/local/bin - -EXPOSE 22 - -CMD ["/usr/local/bin/entrypoint.sh"] diff --git a/internal/sshdialer/testdata/entrypoint.sh b/internal/sshdialer/testdata/entrypoint.sh deleted file mode 100755 index 1396f7d74..000000000 --- a/internal/sshdialer/testdata/entrypoint.sh +++ /dev/null @@ -1,5 +0,0 @@ -#!/usr/bin/env bash - -/usr/sbin/sshd - -su - testuser -c '/usr/local/bin/serve-socket "/home/testuser/test.sock" "1234"' diff --git a/internal/sshdialer/testdata/etc/ssh/ssh_host_dsa_key b/internal/sshdialer/testdata/etc/ssh/ssh_host_dsa_key deleted file mode 100644 index b6cfa3ba1..000000000 --- a/internal/sshdialer/testdata/etc/ssh/ssh_host_dsa_key +++ /dev/null @@ -1,21 +0,0 @@ ------BEGIN OPENSSH PRIVATE KEY----- -b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAABsgAAAAdzc2gtZH -NzAAAAgQDhtFRnJxDyn1xru4PNNnOyvaYlPzt+l8Jwu1PozLsU+2LPJmZQtYXjUl7d7w/P -MklwmwH/4xuQ+YjkaWD8I+4IA4FZfAJjEuTPXJsze48Fefcorg9TkxRxDPk6vetn3TKSuR -YeBpLDq+Dq1vo/2vBM70KahRQDxVNlYdNbiBG5dQAAABUAjg1kaaW8DHwj326XhjKpkoXR -zwEAAACAfSdptBoh5kQHp1ZAMr4EBzH+PMhRf6sdfHhpUOO+4uhRC5hqIgIm3IA1UG8H5V -2Py6xNvIvELX/UVrtzTeqjxZ8+47WKM+j6fTUp3gx+InLq23bD9U43BP+sUcXIs322LUkb -fBoQzWnXx8lkOQzaeofGc4Cg2NIIA5pWewsEACQAAACBAK2mPPqr8PSHjnW9bqv8MEuc3K -qKHuL4a5fGQA5G5upbFxXgB3MtstTtWO27LVD5wrlLOpEz7Lzwv51GUFpxYRTSv49qliKP -c31QAoEjlpST6Jv9JpERGcCTETwNwyH8TH4gx7Ku0JXaysyJH0rn2UTCUAhNmMpiO5tSb5 -PCwK94AAAB6ItbM9KLWzPSAAAAB3NzaC1kc3MAAACBAOG0VGcnEPKfXGu7g802c7K9piU/ -O36XwnC7U+jMuxT7Ys8mZlC1heNSXt3vD88ySXCbAf/jG5D5iORpYPwj7ggDgVl8AmMS5M -9cmzN7jwV59yiuD1OTFHEM+Tq962fdMpK5Fh4GksOr4OrW+j/a8EzvQpqFFAPFU2Vh01uI -Ebl1AAAAFQCODWRppbwMfCPfbpeGMqmShdHPAQAAAIB9J2m0GiHmRAenVkAyvgQHMf48yF -F/qx18eGlQ477i6FELmGoiAibcgDVQbwflXY/LrE28i8Qtf9RWu3NN6qPFnz7jtYoz6Pp9 -NSneDH4icurbdsP1TjcE/6xRxcizfbYtSRt8GhDNadfHyWQ5DNp6h8ZzgKDY0ggDmlZ7Cw -QAJAAAAIEAraY8+qvw9IeOdb1uq/wwS5zcqooe4vhrl8ZADkbm6lsXFeAHcy2y1O1Y7bst -UPnCuUs6kTPsvPC/nUZQWnFhFNK/j2qWIo9zfVACgSOWlJPom/0mkREZwJMRPA3DIfxMfi -DHsq7QldrKzIkfSufZRMJQCE2YymI7m1Jvk8LAr3gAAAAUdboNp8quoeOloagm/Or8qP1d -zwMAAAAQbXZhc2VrQGJlbGxhdHJpeAEC ------END OPENSSH PRIVATE KEY----- diff --git a/internal/sshdialer/testdata/etc/ssh/ssh_host_dsa_key.pub b/internal/sshdialer/testdata/etc/ssh/ssh_host_dsa_key.pub deleted file mode 100644 index 291a3f583..000000000 --- a/internal/sshdialer/testdata/etc/ssh/ssh_host_dsa_key.pub +++ /dev/null @@ -1 +0,0 @@ -ssh-dss AAAAB3NzaC1kc3MAAACBAOG0VGcnEPKfXGu7g802c7K9piU/O36XwnC7U+jMuxT7Ys8mZlC1heNSXt3vD88ySXCbAf/jG5D5iORpYPwj7ggDgVl8AmMS5M9cmzN7jwV59yiuD1OTFHEM+Tq962fdMpK5Fh4GksOr4OrW+j/a8EzvQpqFFAPFU2Vh01uIEbl1AAAAFQCODWRppbwMfCPfbpeGMqmShdHPAQAAAIB9J2m0GiHmRAenVkAyvgQHMf48yFF/qx18eGlQ477i6FELmGoiAibcgDVQbwflXY/LrE28i8Qtf9RWu3NN6qPFnz7jtYoz6Pp9NSneDH4icurbdsP1TjcE/6xRxcizfbYtSRt8GhDNadfHyWQ5DNp6h8ZzgKDY0ggDmlZ7CwQAJAAAAIEAraY8+qvw9IeOdb1uq/wwS5zcqooe4vhrl8ZADkbm6lsXFeAHcy2y1O1Y7bstUPnCuUs6kTPsvPC/nUZQWnFhFNK/j2qWIo9zfVACgSOWlJPom/0mkREZwJMRPA3DIfxMfiDHsq7QldrKzIkfSufZRMJQCE2YymI7m1Jvk8LAr3g= mvasek@bellatrix diff --git a/internal/sshdialer/testdata/etc/ssh/sshd_config b/internal/sshdialer/testdata/etc/ssh/sshd_config deleted file mode 100644 index d9b622233..000000000 --- a/internal/sshdialer/testdata/etc/ssh/sshd_config +++ /dev/null @@ -1,118 +0,0 @@ -# $OpenBSD: sshd_config,v 1.103 2018/04/09 20:41:22 tj Exp $ - -# This is the sshd server system-wide configuration file. See -# sshd_config(5) for more information. - -# This sshd was compiled with PATH=/bin:/usr/bin:/sbin:/usr/sbin - -# The strategy used for options in the default sshd_config shipped with -# OpenSSH is to specify options with their default value where -# possible, but leave them commented. Uncommented options override the -# default value. - -Port 22 -AddressFamily any -ListenAddress 0.0.0.0 -ListenAddress :: - -HostKey /etc/ssh/ssh_host_ecdsa_key -HostKey /etc/ssh/ssh_host_ed25519_key -HostKey /etc/ssh/ssh_host_rsa_key -HostKey /etc/ssh/ssh_host_dsa_key - -# Ciphers and keying -#RekeyLimit default none - -# Logging -#SyslogFacility AUTH -#LogLevel INFO - -# Authentication: - -#LoginGraceTime 2m -PermitRootLogin yes -#StrictModes yes -#MaxAuthTries 6 -#MaxSessions 10 - -#PubkeyAuthentication yes - -# The default is to check both .ssh/authorized_keys and .ssh/authorized_keys2 -# but this is overridden so installations will only check .ssh/authorized_keys -AuthorizedKeysFile .ssh/authorized_keys - -#AuthorizedPrincipalsFile none - -#AuthorizedKeysCommand none -#AuthorizedKeysCommandUser nobody - -# For this to work you will also need host keys in /etc/ssh/ssh_known_hosts -#HostbasedAuthentication no -# Change to yes if you don't trust ~/.ssh/known_hosts for -# HostbasedAuthentication -#IgnoreUserKnownHosts no -# Don't read the user's ~/.rhosts and ~/.shosts files -#IgnoreRhosts yes - -# To disable tunneled clear text passwords, change to no here! -PasswordAuthentication yes -#PermitEmptyPasswords no - -# Change to no to disable s/key passwords -#ChallengeResponseAuthentication yes - -# Kerberos options -#KerberosAuthentication no -#KerberosOrLocalPasswd yes -#KerberosTicketCleanup yes -#KerberosGetAFSToken no - -# GSSAPI options -#GSSAPIAuthentication no -#GSSAPICleanupCredentials yes - -# Set this to 'yes' to enable PAM authentication, account processing, -# and session processing. If this is enabled, PAM authentication will -# be allowed through the ChallengeResponseAuthentication and -# PasswordAuthentication. Depending on your PAM configuration, -# PAM authentication via ChallengeResponseAuthentication may bypass -# the setting of "PermitRootLogin without-password". -# If you just want the PAM account and session checks to run without -# PAM authentication, then enable this but set PasswordAuthentication -# and ChallengeResponseAuthentication to 'no'. -#UsePAM no - -#AllowAgentForwarding yes -# Feel free to re-enable these if your use case requires them. -AllowTcpForwarding yes -GatewayPorts no -X11Forwarding no -#X11DisplayOffset 10 -#X11UseLocalhost yes -#PermitTTY yes -#PrintMotd yes -#PrintLastLog yes -#TCPKeepAlive yes -#PermitUserEnvironment no -#Compression delayed -#ClientAliveInterval 0 -#ClientAliveCountMax 3 -#UseDNS no -#PidFile /run/sshd.pid -#MaxStartups 10:30:100 -#PermitTunnel no -#ChrootDirectory none -#VersionAddendum none - -# no default banner path -#Banner none - -# override default of no subsystems -Subsystem sftp /usr/lib/ssh/sftp-server - -# Example of overriding settings on a per-user basis -#Match User anoncvs -# X11Forwarding no -# AllowTcpForwarding no -# PermitTTY no -# ForceCommand cvs server diff --git a/internal/sshdialer/testdata/go.mod b/internal/sshdialer/testdata/go.mod deleted file mode 100644 index 0d7da0a73..000000000 --- a/internal/sshdialer/testdata/go.mod +++ /dev/null @@ -1,3 +0,0 @@ -module serve-socket - -go 1.17 diff --git a/internal/sshdialer/testdata/main.go b/internal/sshdialer/testdata/main.go deleted file mode 100644 index 8badbb478..000000000 --- a/internal/sshdialer/testdata/main.go +++ /dev/null @@ -1,83 +0,0 @@ -package main - -import ( - "context" - "fmt" - "net" - "net/http" - "os" - "os/signal" - "sync" - "syscall" - "time" -) - -// simple HTTP server to verify that tunneling works -func main() { - if len(os.Args) != 3 { - panic("exactly two positional parameters expected: path to unix socket and tcp port.") - } - - ctx, cancel := context.WithCancel(context.Background()) - sigs := make(chan os.Signal, 1) - signal.Notify(sigs, syscall.SIGINT, syscall.SIGTERM) - - go func() { - <-sigs - cancel() - <-sigs - os.Exit(1) - }() - - var handler http.HandlerFunc = func(w http.ResponseWriter, req *http.Request) { - w.WriteHeader(200) - _, err := w.Write([]byte("Hello there!")) - if err != nil { - panic(err) - } - } - - serverUnix := http.Server{Handler: handler} - go func() { - <-ctx.Done() - shutdownCtx, _ := context.WithTimeout(context.Background(), time.Second*5) - _ = serverUnix.Shutdown(shutdownCtx) - }() - - var wg sync.WaitGroup - - wg.Add(1) - go func() { - unixListener, err := net.Listen("unix", os.Args[1]) - if err != nil { - panic(err) - } - defer wg.Done() - err = serverUnix.Serve(unixListener) - if err != nil { - panic(err) - } - }() - - serverTcp := http.Server{Handler: handler} - go func() { - <-ctx.Done() - shutdownCtx, _ := context.WithTimeout(context.Background(), time.Second*5) - _ = serverTcp.Shutdown(shutdownCtx) - }() - - wg.Add(1) - go func() { - defer wg.Done() - tcpListener, err := net.Listen("tcp4", fmt.Sprintf("0.0.0.0:%s", os.Args[2])) - if err != nil { - panic(err) - } - err = serverTcp.Serve(tcpListener) - if err != nil { - panic(err) - } - }() - - wg.Wait() -}