Skip to content

Commit

Permalink
serve sftp: fix authentication on one connection blocking others - fixes
Browse files Browse the repository at this point in the history
 rclone#4882

Before this change, if one connection was authenticating this would
block any others from authenticating.

This was due to ssh.NewServerConn not being called in a go routine
after the Accept call.

This is fixed by running the ssh authentication in a go routine.

Thanks to @FiloSottile for advice on how to fix this.

See: golang/go#43521
  • Loading branch information
ncw authored and negative0 committed Aug 13, 2021
1 parent cf2b493 commit 28af7b6
Showing 1 changed file with 34 additions and 27 deletions.
61 changes: 34 additions & 27 deletions cmd/serve/sftp/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,39 @@ func (s *server) getVFS(what string, sshConn *ssh.ServerConn) (VFS *vfs.VFS) {
return VFS
}

// Accept a single connection - run in a go routine as the ssh
// authentication can block
func (s *server) acceptConnection(nConn net.Conn) {
what := describeConn(nConn)

// Before use, a handshake must be performed on the incoming net.Conn.
sshConn, chans, reqs, err := ssh.NewServerConn(nConn, s.config)
if err != nil {
fs.Errorf(what, "SSH login failed: %v", err)
return
}

fs.Infof(what, "SSH login from %s using %s", sshConn.User(), sshConn.ClientVersion())

// Discard all global out-of-band Requests
go ssh.DiscardRequests(reqs)

c := &conn{
what: what,
vfs: s.getVFS(what, sshConn),
}
if c.vfs == nil {
fs.Infof(what, "Closing unauthenticated connection (couldn't find VFS)")
_ = nConn.Close()
return
}
c.handlers = newVFSHandler(c.vfs)

// Accept all channels
go c.handleChannels(chans)
}

// Accept connections and call them in a go routine
func (s *server) acceptConnections() {
for {
nConn, err := s.listener.Accept()
Expand All @@ -88,33 +121,7 @@ func (s *server) acceptConnections() {
fs.Errorf(nil, "Failed to accept incoming connection: %v", err)
continue
}
what := describeConn(nConn)

// Before use, a handshake must be performed on the incoming net.Conn.
sshConn, chans, reqs, err := ssh.NewServerConn(nConn, s.config)
if err != nil {
fs.Errorf(what, "SSH login failed: %v", err)
continue
}

fs.Infof(what, "SSH login from %s using %s", sshConn.User(), sshConn.ClientVersion())

// Discard all global out-of-band Requests
go ssh.DiscardRequests(reqs)

c := &conn{
what: what,
vfs: s.getVFS(what, sshConn),
}
if c.vfs == nil {
fs.Infof(what, "Closing unauthenticated connection (couldn't find VFS)")
_ = nConn.Close()
continue
}
c.handlers = newVFSHandler(c.vfs)

// Accept all channels
go c.handleChannels(chans)
go s.acceptConnection(nConn)
}
}

Expand Down

0 comments on commit 28af7b6

Please sign in to comment.