From 04fcd46a9d6dda9954da9a3c6c460513b19d1c11 Mon Sep 17 00:00:00 2001 From: Monika Pawluczuk Date: Fri, 29 Sep 2023 08:50:04 +0800 Subject: [PATCH 1/8] first draft --- cli/remoteforward.go | 50 +++++++++++++++++++++++++++++++++++++++----- 1 file changed, 45 insertions(+), 5 deletions(-) diff --git a/cli/remoteforward.go b/cli/remoteforward.go index 95daa46663ea5..57b476380d49f 100644 --- a/cli/remoteforward.go +++ b/cli/remoteforward.go @@ -23,15 +23,24 @@ type cookieAddr struct { // Format: // remote_port:local_address:local_port -var remoteForwardRegex = regexp.MustCompile(`^(\d+):(.+):(\d+)$`) +var remoteForwardRegexTCP = regexp.MustCompile(`^(\d+):(.+):(\d+)$`) -func validateRemoteForward(flag string) bool { - return remoteForwardRegex.MatchString(flag) +// remote_socket_path:local_socket_path (both absolute paths) +var remoteForwardRegexUnixSocket = regexp.MustCompile(`^(\\.+):(\\.+)$`) + +func remoteForwardTCP(flag string) bool { + return remoteForwardRegexTCP.MatchString(flag) } -func parseRemoteForward(flag string) (net.Addr, net.Addr, error) { - matches := remoteForwardRegex.FindStringSubmatch(flag) +func remoteForwardUnixSocket(flag string) bool { + return remoteForwardRegexUnixSocket.MatchString(flag) +} +func validateRemoteForward(flag string) bool { + return remoteForwardTCP(flag) || remoteForwardUnixSocket(flag) +} + +func parseRemoteForwardTCP(matches []string) (net.Addr, net.Addr, error) { remotePort, err := strconv.Atoi(matches[1]) if err != nil { return nil, nil, xerrors.Errorf("remote port is invalid: %w", err) @@ -57,6 +66,37 @@ func parseRemoteForward(flag string) (net.Addr, net.Addr, error) { return localAddr, remoteAddr, nil } +func parseRemoteForwardUnixSocket(matches []string) (net.Addr, net.Addr, error) { + remoteSocket := matches[1] + localSocket := matches[2] + + remoteAddr := &net.UnixAddr{ + Name: remoteSocket, + Net: "unix", + } + + localAddr := &net.UnixAddr{ + Name: localSocket, + Net: "unix", + } + return localAddr, remoteAddr, nil +} + +func parseRemoteForward(flag string) (net.Addr, net.Addr, error) { + tcpMatches := remoteForwardRegexTCP.FindStringSubmatch(flag) + + if len(tcpMatches) > 0 { + return parseRemoteForwardTCP(tcpMatches) + } + + unixSocketMatches := remoteForwardRegexUnixSocket.FindStringSubmatch(flag) + if len(unixSocketMatches) > 0 { + return parseRemoteForwardUnixSocket(unixSocketMatches) + } + + return nil, nil, xerrors.New("Could not match forward arguments") +} + // sshRemoteForward starts forwarding connections from a remote listener to a // local address via SSH in a goroutine. // From 3e912eb26814c937277699ee99ea7013d4e0a7ac Mon Sep 17 00:00:00 2001 From: Monika Pawluczuk Date: Fri, 29 Sep 2023 09:29:46 +0800 Subject: [PATCH 2/8] test --- cli/remoteforward.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cli/remoteforward.go b/cli/remoteforward.go index 57b476380d49f..5686269cc7bdd 100644 --- a/cli/remoteforward.go +++ b/cli/remoteforward.go @@ -37,7 +37,7 @@ func remoteForwardUnixSocket(flag string) bool { } func validateRemoteForward(flag string) bool { - return remoteForwardTCP(flag) || remoteForwardUnixSocket(flag) + return remoteForwardUnixSocket(flag) || remoteForwardTCP(flag) } func parseRemoteForwardTCP(matches []string) (net.Addr, net.Addr, error) { From e81c1894ffb7927360f4c284035a912e78b4cf59 Mon Sep 17 00:00:00 2001 From: Monika Pawluczuk Date: Fri, 29 Sep 2023 09:34:14 +0800 Subject: [PATCH 3/8] test 2 --- cli/remoteforward.go | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/cli/remoteforward.go b/cli/remoteforward.go index 5686269cc7bdd..04f13c1d5b0e5 100644 --- a/cli/remoteforward.go +++ b/cli/remoteforward.go @@ -29,11 +29,15 @@ var remoteForwardRegexTCP = regexp.MustCompile(`^(\d+):(.+):(\d+)$`) var remoteForwardRegexUnixSocket = regexp.MustCompile(`^(\\.+):(\\.+)$`) func remoteForwardTCP(flag string) bool { - return remoteForwardRegexTCP.MatchString(flag) + reg := remoteForwardRegexTCP.MatchString(flag) + fmt.Println("remoteForwardTCP", reg) + return reg } func remoteForwardUnixSocket(flag string) bool { - return remoteForwardRegexUnixSocket.MatchString(flag) + reg := remoteForwardRegexUnixSocket.MatchString(flag) + fmt.Println("remoteForwardUnixSocket", reg) + return reg } func validateRemoteForward(flag string) bool { From add7b4584e2664171d8ec78c516f136cd82503a0 Mon Sep 17 00:00:00 2001 From: Monika Pawluczuk Date: Fri, 29 Sep 2023 10:40:54 +0800 Subject: [PATCH 4/8] cleanup --- cli/remoteforward.go | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/cli/remoteforward.go b/cli/remoteforward.go index 04f13c1d5b0e5..d19faf604d33f 100644 --- a/cli/remoteforward.go +++ b/cli/remoteforward.go @@ -26,22 +26,18 @@ type cookieAddr struct { var remoteForwardRegexTCP = regexp.MustCompile(`^(\d+):(.+):(\d+)$`) // remote_socket_path:local_socket_path (both absolute paths) -var remoteForwardRegexUnixSocket = regexp.MustCompile(`^(\\.+):(\\.+)$`) +var remoteForwardRegexUnixSocket = regexp.MustCompile(`^(\/.+):(\/.+)$`) func remoteForwardTCP(flag string) bool { - reg := remoteForwardRegexTCP.MatchString(flag) - fmt.Println("remoteForwardTCP", reg) - return reg + return remoteForwardRegexTCP.MatchString(flag) } func remoteForwardUnixSocket(flag string) bool { - reg := remoteForwardRegexUnixSocket.MatchString(flag) - fmt.Println("remoteForwardUnixSocket", reg) - return reg + return remoteForwardRegexUnixSocket.MatchString(flag) } func validateRemoteForward(flag string) bool { - return remoteForwardUnixSocket(flag) || remoteForwardTCP(flag) + return remoteForwardTCP(flag) || remoteForwardUnixSocket(flag) } func parseRemoteForwardTCP(matches []string) (net.Addr, net.Addr, error) { From 819fd9af9501b4d0c4fb5d6fd3ebe16790185ddf Mon Sep 17 00:00:00 2001 From: Monika Pawluczuk Date: Fri, 29 Sep 2023 12:09:36 +0800 Subject: [PATCH 5/8] add tests and check for socket file --- cli/remoteforward.go | 10 +++++++++ cli/ssh_test.go | 48 ++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 58 insertions(+) diff --git a/cli/remoteforward.go b/cli/remoteforward.go index d19faf604d33f..296791c9cc045 100644 --- a/cli/remoteforward.go +++ b/cli/remoteforward.go @@ -5,6 +5,7 @@ import ( "fmt" "io" "net" + "os" "regexp" "strconv" @@ -70,6 +71,15 @@ func parseRemoteForwardUnixSocket(matches []string) (net.Addr, net.Addr, error) remoteSocket := matches[1] localSocket := matches[2] + fileInfo, err := os.Stat(localSocket) + if err != nil { + return nil, nil, err + } + + if fileInfo.Mode()&os.ModeSocket == 0 { + return nil, nil, xerrors.New("File is not a Unix domain socket file") + } + remoteAddr := &net.UnixAddr{ Name: remoteSocket, Net: "unix", diff --git a/cli/ssh_test.go b/cli/ssh_test.go index 90b34ca9f4b70..c2c14cf11ff77 100644 --- a/cli/ssh_test.go +++ b/cli/ssh_test.go @@ -428,6 +428,54 @@ func TestSSH(t *testing.T) { <-cmdDone }) + t.Run("RemoteForwardUnixSocket", func(t *testing.T) { + if runtime.GOOS == "windows" { + t.Skip("Test not supported on windows") + } + + t.Parallel() + + client, workspace, agentToken := setupWorkspaceForAgent(t, nil) + + _ = agenttest.New(t, client.URL, agentToken) + coderdtest.AwaitWorkspaceAgents(t, client, workspace.ID) + + ctx, cancel := context.WithTimeout(context.Background(), testutil.WaitLong) + defer cancel() + + tmpdir := tempDirUnixSocket(t) + agentSock := filepath.Join(tmpdir, "agent.sock") + l, err := net.Listen("unix", agentSock) + require.NoError(t, err) + defer l.Close() + + inv, root := clitest.New(t, + "ssh", + workspace.Name, + "--remote-forward", + agentSock+":/tmp/test.sock", + ) + clitest.SetupConfig(t, client, root) + pty := ptytest.New(t).Attach(inv) + inv.Stderr = pty.Output() + cmdDone := tGo(t, func() { + err := inv.WithContext(ctx).Run() + assert.NoError(t, err, "ssh command failed") + }) + + // Wait for the prompt or any output really to indicate the command has + // started and accepting input on stdin. + _ = pty.Peek(ctx, 1) + + // Download the test page + pty.WriteLine("ss -xl state listening src /var/roo/daemon.sock | wc -l") + pty.ExpectMatch("2") + + // And we're done. + pty.WriteLine("exit") + <-cmdDone + }) + t.Run("FileLogging", func(t *testing.T) { t.Parallel() From 0eb016982ecf00ace8cc970e089ac83b156ce862 Mon Sep 17 00:00:00 2001 From: Monika Pawluczuk Date: Mon, 2 Oct 2023 07:52:14 +0800 Subject: [PATCH 6/8] rename remoteForwardUnixSocket->isRemoteForwardUnixSocket; remoteForwardTCP->isRemoteForwardTCP --- cli/remoteforward.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/cli/remoteforward.go b/cli/remoteforward.go index 296791c9cc045..2c4207583b289 100644 --- a/cli/remoteforward.go +++ b/cli/remoteforward.go @@ -29,16 +29,16 @@ var remoteForwardRegexTCP = regexp.MustCompile(`^(\d+):(.+):(\d+)$`) // remote_socket_path:local_socket_path (both absolute paths) var remoteForwardRegexUnixSocket = regexp.MustCompile(`^(\/.+):(\/.+)$`) -func remoteForwardTCP(flag string) bool { +func isRemoteForwardTCP(flag string) bool { return remoteForwardRegexTCP.MatchString(flag) } -func remoteForwardUnixSocket(flag string) bool { +func isRemoteForwardUnixSocket(flag string) bool { return remoteForwardRegexUnixSocket.MatchString(flag) } func validateRemoteForward(flag string) bool { - return remoteForwardTCP(flag) || remoteForwardUnixSocket(flag) + return isRemoteForwardTCP(flag) || isRemoteForwardUnixSocket(flag) } func parseRemoteForwardTCP(matches []string) (net.Addr, net.Addr, error) { From 3302700ff70497e811cb02a16805f715b5eab825 Mon Sep 17 00:00:00 2001 From: Monika Pawluczuk Date: Mon, 2 Oct 2023 11:13:55 +0800 Subject: [PATCH 7/8] fix test --- cli/ssh_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cli/ssh_test.go b/cli/ssh_test.go index c2c14cf11ff77..3ebb7a4ee87c1 100644 --- a/cli/ssh_test.go +++ b/cli/ssh_test.go @@ -453,7 +453,7 @@ func TestSSH(t *testing.T) { "ssh", workspace.Name, "--remote-forward", - agentSock+":/tmp/test.sock", + "/tmp/test.sock:"+agentSock, ) clitest.SetupConfig(t, client, root) pty := ptytest.New(t).Attach(inv) From 6996495a3899eec90f0146c65ac254cec98e1386 Mon Sep 17 00:00:00 2001 From: Monika Pawluczuk Date: Mon, 2 Oct 2023 14:14:50 +0800 Subject: [PATCH 8/8] reference correct socket path --- cli/ssh_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cli/ssh_test.go b/cli/ssh_test.go index 3ebb7a4ee87c1..0f5f00cbd01ba 100644 --- a/cli/ssh_test.go +++ b/cli/ssh_test.go @@ -468,7 +468,7 @@ func TestSSH(t *testing.T) { _ = pty.Peek(ctx, 1) // Download the test page - pty.WriteLine("ss -xl state listening src /var/roo/daemon.sock | wc -l") + pty.WriteLine("ss -xl state listening src /tmp/test.sock | wc -l") pty.ExpectMatch("2") // And we're done.