Description
What version of Go are you using (go version
)?
$ go version go version go1.15.2 linux/amd64
Does this issue reproduce with the latest release?
Yes
What operating system and processor architecture are you using (go env
)?
go env
Output
$ go env GO111MODULE="" GOARCH="amd64" GOBIN="" GOCACHE="/home/ncw/.cache/go-build" GOENV="/home/ncw/.config/go/env" GOEXE="" GOFLAGS="" GOHOSTARCH="amd64" GOHOSTOS="linux" GOINSECURE="" GOMODCACHE="/home/ncw/go/pkg/mod" GONOPROXY="" GONOSUMDB="" GOOS="linux" GOPATH="/home/ncw/go" GOPRIVATE="" GOPROXY="https://proxy.golang.org,direct" GOROOT="/opt/go/go1.15" GOSUMDB="sum.golang.org" GOTMPDIR="" GOTOOLDIR="/opt/go/go1.15/pkg/tool/linux_amd64" GCCGO="/usr/bin/gccgo" AR="ar" CC="gcc" CXX="g++" CGO_ENABLED="1" GOMOD="" CGO_CFLAGS="-g -O2" CGO_CPPFLAGS="" CGO_CXXFLAGS="-g -O2" CGO_FFLAGS="-g -O2" CGO_LDFLAGS="-g -O2" PKG_CONFIG="pkg-config" GOGCCFLAGS="-fPIC -m64 -pthread -fmessage-length=0 -fdebug-prefix-map=/tmp/go-build874025211=/tmp/go-build -gno-record-gcc-switches"
What did you do?
I followed the example here for building an ssh server: https://gist.github.com/jpillora/b480fde82bff51a06238
This contains the code
for {
tcpConn, err := listener.Accept()
if err != nil {
log.Printf("Failed to accept incoming connection (%s)", err)
continue
}
// Before use, a handshake must be performed on the incoming net.Conn.
sshConn, chans, reqs, err := ssh.NewServerConn(tcpConn, config)
if err != nil {
log.Printf("Failed to handshake (%s)", err)
continue
}
log.Printf("New SSH connection from %s (%s)", sshConn.RemoteAddr(), sshConn.ClientVersion())
// Discard all global out-of-band Requests
go ssh.DiscardRequests(reqs)
// Accept all channels
go handleChannels(chans)
}
The problem with this code is that calling ssh.NewServerConn
can take an arbitrary length of time as it might have to ask the user for a password. In fact calling it like this is setting up a potential DoS attack - one user can take as long as desired to do the authentication, blocking any other users from connecting.
The documentation for ssh.NewServerConn
should be changed to say that it must be called in a new go routine and should not be called in in the same go routine as the Listen
loop.
It might be a good idea if the example for ssh.NewServerConn was changed to show a loop around the Listen
call with the ssh.NewServerConn
being called in a go routine.
This was originally discovered in rclone/rclone#4882 - thanks to @FiloSottile for working out the cause of the problem and suggesting making this issue.