From 2691e729cdcddea2c8571068a36e0733778735c6 Mon Sep 17 00:00:00 2001 From: Pascal Fischer Date: Fri, 23 Jun 2023 12:20:14 +0200 Subject: [PATCH 1/4] fix ssh --- client/cmd/ssh.go | 12 +++++----- client/ssh/server.go | 52 ++++++++++++++++++++++++++++++++++---------- 2 files changed, 46 insertions(+), 18 deletions(-) diff --git a/client/cmd/ssh.go b/client/cmd/ssh.go index d0f506ff8..f97ce5f90 100644 --- a/client/cmd/ssh.go +++ b/client/cmd/ssh.go @@ -9,7 +9,6 @@ import ( "strings" "syscall" - log "github.com/sirupsen/logrus" "github.com/spf13/cobra" "github.com/netbirdio/netbird/client/internal" @@ -73,7 +72,8 @@ var sshCmd = &cobra.Command{ go func() { // blocking if err := runSSH(sshctx, host, []byte(config.SSHKey), cmd); err != nil { - log.Print(err) + os.Exit(1) + // log.Print(err) } cancel() }() @@ -92,11 +92,9 @@ func runSSH(ctx context.Context, addr string, pemKey []byte, cmd *cobra.Command) c, err := nbssh.DialWithKey(fmt.Sprintf("%s:%d", addr, port), user, pemKey) if err != nil { cmd.Printf("Error: %v\n", err) - cmd.Printf("Couldn't connect. " + - "You might be disconnected from the NetBird network, or the NetBird agent isn't running.\n" + - "Run the status command: \n\n" + - " netbird status\n\n" + - "It might also be that the SSH server is disabled on the agent you are trying to connect to.\n") + cmd.Printf("Couldn't connect. Please check the connection status or if the ssh server is enabled on the other peer" + + "You can verify the connection by running:\n\n" + + " netbird status\n\n") return nil } go func() { diff --git a/client/ssh/server.go b/client/ssh/server.go index 5d63362b9..f08c5a2f1 100644 --- a/client/ssh/server.go +++ b/client/ssh/server.go @@ -2,9 +2,6 @@ package ssh import ( "fmt" - "github.com/creack/pty" - "github.com/gliderlabs/ssh" - log "github.com/sirupsen/logrus" "io" "net" "os" @@ -13,6 +10,11 @@ import ( "runtime" "strings" "sync" + "time" + + "github.com/creack/pty" + "github.com/gliderlabs/ssh" + log "github.com/sirupsen/logrus" ) // DefaultSSHPort is the default SSH port of the NetBird's embedded SSH server @@ -137,6 +139,8 @@ func (srv *DefaultServer) sessionHandler(session ssh.Session) { } }() + log.Infof("Establishing SSH session for %s from host %s", session.User(), session.RemoteAddr().String()) + localUser, err := userNameLookup(session.User()) if err != nil { _, err = fmt.Fprintf(session, "remote SSH server couldn't find local user %s\n", session.User()) //nolint @@ -172,6 +176,7 @@ func (srv *DefaultServer) sessionHandler(session ssh.Session) { } } + log.Debugf("Login command: %s", cmd.String()) file, err := pty.Start(cmd) if err != nil { log.Errorf("failed starting SSH server %v", err) @@ -199,24 +204,49 @@ func (srv *DefaultServer) sessionHandler(session ssh.Session) { return } } + log.Debugf("SSH session ended") } func (srv *DefaultServer) stdInOut(file *os.File, session ssh.Session) { go func() { // stdin - _, err := io.Copy(file, session) - if err != nil { - return - } + io.Copy(file, session) }() + // For nodes on AWS the terminal takes a while to be ready so we need to wait + terminalIsReady := make(chan bool) go func() { - // stdout - _, err := io.Copy(session, file) - if err != nil { - return + for { + log.Debugf("Checking if terminal is ready") + if checkIfFileIsReady(file) { + terminalIsReady <- true + } + time.Sleep(100 * time.Millisecond) } }() + timer := time.NewTimer(30 * time.Second) + for { + select { + case <-timer.C: + session.Write([]byte("Reached timeout while opening connection\n")) + session.Exit(1) + case <-terminalIsReady: + // stdout + io.Copy(session, file) + session.Exit(0) + } + } +} + +func checkIfFileIsReady(file *os.File) bool { + buffer := make([]byte, 0) + _, err := file.Read(buffer) + // _, err := file.Stat() + // log.Infof("file stat: %v", err) + if err == nil { + return true + } + return false } // Start starts SSH server. Blocking From f7d97b02fd585e882b778f382b83aa94a4d06621 Mon Sep 17 00:00:00 2001 From: Pascal Fischer Date: Fri, 23 Jun 2023 16:27:10 +0200 Subject: [PATCH 2/4] fix error codes on cli --- client/cmd/ssh.go | 5 +++-- client/ssh/server.go | 37 +++++++++++-------------------------- 2 files changed, 14 insertions(+), 28 deletions(-) diff --git a/client/cmd/ssh.go b/client/cmd/ssh.go index f97ce5f90..dd9407738 100644 --- a/client/cmd/ssh.go +++ b/client/cmd/ssh.go @@ -9,6 +9,7 @@ import ( "strings" "syscall" + log "github.com/sirupsen/logrus" "github.com/spf13/cobra" "github.com/netbirdio/netbird/client/internal" @@ -72,8 +73,8 @@ var sshCmd = &cobra.Command{ go func() { // blocking if err := runSSH(sshctx, host, []byte(config.SSHKey), cmd); err != nil { + log.Debug(err) os.Exit(1) - // log.Print(err) } cancel() }() @@ -95,7 +96,7 @@ func runSSH(ctx context.Context, addr string, pemKey []byte, cmd *cobra.Command) cmd.Printf("Couldn't connect. Please check the connection status or if the ssh server is enabled on the other peer" + "You can verify the connection by running:\n\n" + " netbird status\n\n") - return nil + return err } go func() { <-ctx.Done() diff --git a/client/ssh/server.go b/client/ssh/server.go index f08c5a2f1..b9128845e 100644 --- a/client/ssh/server.go +++ b/client/ssh/server.go @@ -20,6 +20,9 @@ import ( // DefaultSSHPort is the default SSH port of the NetBird's embedded SSH server const DefaultSSHPort = 44338 +// TerminalTimeout is the timeout for terminal session to be ready +const TerminalTimeout = 10 * time.Second + // DefaultSSHServer is a function that creates DefaultServer func DefaultSSHServer(hostKeyPEM []byte, addr string) (Server, error) { return newDefaultServer(hostKeyPEM, addr) @@ -213,42 +216,24 @@ func (srv *DefaultServer) stdInOut(file *os.File, session ssh.Session) { io.Copy(file, session) }() - // For nodes on AWS the terminal takes a while to be ready so we need to wait - terminalIsReady := make(chan bool) - go func() { - for { - log.Debugf("Checking if terminal is ready") - if checkIfFileIsReady(file) { - terminalIsReady <- true - } - time.Sleep(100 * time.Millisecond) - } - }() - timer := time.NewTimer(30 * time.Second) + timer := time.NewTimer(TerminalTimeout) for { select { case <-timer.C: session.Write([]byte("Reached timeout while opening connection\n")) session.Exit(1) - case <-terminalIsReady: + return + default: // stdout - io.Copy(session, file) - session.Exit(0) + writtenBytes, err := io.Copy(session, file) + if err != nil && writtenBytes != 0 { + session.Exit(0) + return + } } } } -func checkIfFileIsReady(file *os.File) bool { - buffer := make([]byte, 0) - _, err := file.Read(buffer) - // _, err := file.Stat() - // log.Infof("file stat: %v", err) - if err == nil { - return true - } - return false -} - // Start starts SSH server. Blocking func (srv *DefaultServer) Start() error { log.Infof("starting SSH server on addr: %s", srv.listener.Addr().String()) From 68a8687c8043c4e4d04335e73edc03f215625739 Mon Sep 17 00:00:00 2001 From: Pascal Fischer Date: Fri, 23 Jun 2023 16:45:07 +0200 Subject: [PATCH 3/4] fix linter --- client/ssh/server.go | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/client/ssh/server.go b/client/ssh/server.go index b9128845e..fe53a24cd 100644 --- a/client/ssh/server.go +++ b/client/ssh/server.go @@ -213,21 +213,25 @@ func (srv *DefaultServer) sessionHandler(session ssh.Session) { func (srv *DefaultServer) stdInOut(file *os.File, session ssh.Session) { go func() { // stdin - io.Copy(file, session) + _, err := io.Copy(file, session) + if err != nil { + _ = session.Exit(1) + return + } }() timer := time.NewTimer(TerminalTimeout) for { select { case <-timer.C: - session.Write([]byte("Reached timeout while opening connection\n")) - session.Exit(1) + _, _ = session.Write([]byte("Reached timeout while opening connection\n")) + _ = session.Exit(1) return default: // stdout writtenBytes, err := io.Copy(session, file) if err != nil && writtenBytes != 0 { - session.Exit(0) + _ = session.Exit(0) return } } From 8eb098d6fde77e296e2bed0e26450cf4b409b17f Mon Sep 17 00:00:00 2001 From: Pascal Fischer Date: Fri, 23 Jun 2023 17:02:34 +0200 Subject: [PATCH 4/4] add sleep and comment --- client/ssh/server.go | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/client/ssh/server.go b/client/ssh/server.go index fe53a24cd..ae5c65c4a 100644 --- a/client/ssh/server.go +++ b/client/ssh/server.go @@ -23,6 +23,9 @@ const DefaultSSHPort = 44338 // TerminalTimeout is the timeout for terminal session to be ready const TerminalTimeout = 10 * time.Second +// TerminalBackoffDelay is the delay between terminal session readiness checks +const TerminalBackoffDelay = 500 * time.Millisecond + // DefaultSSHServer is a function that creates DefaultServer func DefaultSSHServer(hostKeyPEM []byte, addr string) (Server, error) { return newDefaultServer(hostKeyPEM, addr) @@ -220,6 +223,7 @@ func (srv *DefaultServer) stdInOut(file *os.File, session ssh.Session) { } }() + // AWS Linux 2 machines need some time to open the terminal so we need to wait for it timer := time.NewTimer(TerminalTimeout) for { select { @@ -234,6 +238,7 @@ func (srv *DefaultServer) stdInOut(file *os.File, session ssh.Session) { _ = session.Exit(0) return } + time.Sleep(TerminalBackoffDelay) } } }