From 6a5d5c6e3ac32e32a1fbae349afe4beaf3d11002 Mon Sep 17 00:00:00 2001 From: Mathias Fredriksson Date: Thu, 6 Apr 2023 12:10:57 +0000 Subject: [PATCH 1/3] feat(agentssh): Gracefully close SSH sessions on Close By tracking and closing sessions manually before closing the underlying connections, we ensure that the termination is propagated to SSH/SFTP clients and they're not left waiting for a connection timeout. Refs: #6177 --- agent/agentssh/agentssh.go | 47 +++++++++++++++++++++++++++++++++++++- 1 file changed, 46 insertions(+), 1 deletion(-) diff --git a/agent/agentssh/agentssh.go b/agent/agentssh/agentssh.go index c882380bacf48..6f0d132407321 100644 --- a/agent/agentssh/agentssh.go +++ b/agent/agentssh/agentssh.go @@ -50,6 +50,7 @@ type Server struct { mu sync.RWMutex // Protects following. listeners map[net.Listener]struct{} conns map[net.Conn]struct{} + sessions map[ssh.Session]struct{} closing chan struct{} // Wait for goroutines to exit, waited without // a lock on mu but protected by closing. @@ -86,6 +87,7 @@ func NewServer(ctx context.Context, logger slog.Logger, maxTimeout time.Duration s := &Server{ listeners: make(map[net.Listener]struct{}), conns: make(map[net.Conn]struct{}), + sessions: make(map[ssh.Session]struct{}), logger: logger, } @@ -129,7 +131,7 @@ func NewServer(ctx context.Context, logger slog.Logger, maxTimeout time.Duration } }, SubsystemHandlers: map[string]ssh.SubsystemHandler{ - "sftp": s.sftpHandler, + "sftp": s.sessionHandler, }, MaxTimeout: maxTimeout, } @@ -152,7 +154,25 @@ func (s *Server) ConnStats() ConnStats { } func (s *Server) sessionHandler(session ssh.Session) { + if !s.trackSession(session, true) { + session.Exit(MagicSessionErrorCode) + return + } + defer s.trackSession(session, false) + ctx := session.Context() + + switch ss := session.Subsystem(); ss { + case "": + case "sftp": + s.sftpHandler(session) + return + default: + s.logger.Debug(ctx, "unsupported subsystem", slog.F("subsystem", ss)) + _ = session.Exit(1) + return + } + err := s.sessionStart(session) var exitError *exec.ExitError if xerrors.As(err, &exitError) { @@ -560,6 +580,25 @@ func (s *Server) trackConn(l net.Listener, c net.Conn, add bool) (ok bool) { return true } +// trackSession registers the session with the server. If the server is +// closing, the session is not registered and should be closed. +// +//nolint:revive +func (s *Server) trackSession(ss ssh.Session, add bool) (ok bool) { + s.mu.Lock() + defer s.mu.Unlock() + if add { + if s.closing != nil { + // Server closed. + return false + } + s.sessions[ss] = struct{}{} + return true + } + delete(s.sessions, ss) + return true +} + // Close the server and all active connections. Server can be re-used // after Close is done. func (s *Server) Close() error { @@ -573,6 +612,12 @@ func (s *Server) Close() error { } s.closing = make(chan struct{}) + // Close all active sessions to gracefully + // terminate client connections. + for ss := range s.sessions { + _ = ss.Close() + } + // Close all active listeners and connections. for l := range s.listeners { _ = l.Close() From e17ad956f122bbdf2909cc7f2345c73ace80f573 Mon Sep 17 00:00:00 2001 From: Mathias Fredriksson Date: Thu, 6 Apr 2023 12:20:18 +0000 Subject: [PATCH 2/3] Add comment --- agent/agentssh/agentssh.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/agent/agentssh/agentssh.go b/agent/agentssh/agentssh.go index 6f0d132407321..eec9d60d661ce 100644 --- a/agent/agentssh/agentssh.go +++ b/agent/agentssh/agentssh.go @@ -615,6 +615,9 @@ func (s *Server) Close() error { // Close all active sessions to gracefully // terminate client connections. for ss := range s.sessions { + // We call Close on the underlying channel here because we don't + // want to send an exit status to the client (via Exit()). + // Typically OpenSSH clients will return 255 as the exit status. _ = ss.Close() } From 7b13417578087cc65c0b7ebb88f2c60ad2450a8a Mon Sep 17 00:00:00 2001 From: Mathias Fredriksson Date: Thu, 6 Apr 2023 12:28:13 +0000 Subject: [PATCH 3/3] Fix lint --- agent/agentssh/agentssh.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/agent/agentssh/agentssh.go b/agent/agentssh/agentssh.go index eec9d60d661ce..86e1eb9e36af4 100644 --- a/agent/agentssh/agentssh.go +++ b/agent/agentssh/agentssh.go @@ -155,7 +155,8 @@ func (s *Server) ConnStats() ConnStats { func (s *Server) sessionHandler(session ssh.Session) { if !s.trackSession(session, true) { - session.Exit(MagicSessionErrorCode) + // See (*Server).Close() for why we call Close instead of Exit. + _ = session.Close() return } defer s.trackSession(session, false) pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy