golang / go

The Go programming language
https://go.dev
BSD 3-Clause "New" or "Revised" License
120.74k stars 17.33k forks source link

net/http,x/crypto/ssh: Hijack hangs when underlying net.Conn doesn't support SetReadDeadline #67152

Open fasmide opened 1 month ago

fasmide commented 1 month ago

Go version

go version go1.22.2 linux/amd64

Output of go env in your module/workspace:

GO111MODULE=''
GOARCH='amd64'
GOBIN=''
GOCACHE='/home/fas/.cache/go-build'
GOENV='/home/fas/.config/go/env'
GOEXE=''
GOEXPERIMENT=''
GOFLAGS=''
GOHOSTARCH='amd64'
GOHOSTOS='linux'
GOINSECURE=''
GOMODCACHE='/home/fas/go/pkg/mod'
GONOPROXY=''
GONOSUMDB=''
GOOS='linux'
GOPATH='/home/fas/go'
GOPRIVATE=''
GOPROXY='https://proxy.golang.org,direct'
GOROOT='/snap/go/10585'
GOSUMDB='sum.golang.org'
GOTMPDIR=''
GOTOOLCHAIN='auto'
GOTOOLDIR='/snap/go/10585/pkg/tool/linux_amd64'
GOVCS=''
GOVERSION='go1.22.2'
GCCGO='gccgo'
GOAMD64='v1'
AR='ar'
CC='gcc'
CXX='g++'
CGO_ENABLED='1'
GOMOD='/home/fas/github.com/fasmide/the-thing-about-websockets-and-ssh-tunnels/go.mod'
GOWORK=''
CGO_CFLAGS='-O2 -g'
CGO_CPPFLAGS=''
CGO_CXXFLAGS='-O2 -g'
CGO_FFLAGS='-O2 -g'
CGO_LDFLAGS='-O2 -g'
PKG_CONFIG='pkg-config'
GOGCCFLAGS='-fPIC -m64 -pthread -Wl,--no-gc-sections -fmessage-length=0 -ffile-prefix-map=/tmp/go-build565532914=/tmp/go-build -gno-record-gcc-switches'

What did you do?

I was trying to have a web server forwarded through an SSH tunnel - I've made an example of this bug in fasmide/the-thing-about-websockets-and-ssh-tunnels

This all works out really well until web sockets are introduced.

This is because the http.response, which does Hijacking, tries to set its net.Conn's deadline to the past to have it unblock its current Read() - but x/crypto/ssh's implementation of net.Conn does not support this.

Furthermore, the http.response does not do any error checking when setting the deadline - so effectively the conn locks up without any indication to anyone about whats going on.

What did you see happen?

Using the example, one should find that using a local net.Listener — everything is fine; however, forwarding the listener through SSH tunnel's, the browser (both Chrome and Firefox) hangs indefinitely trying to connect.

Example of a working local listener:

the-thing-about-websockets-and-ssh-tunnels $  go run main.go -local
2024/05/03 10:44:29 *net.TCPListener listening on [::]:13337

When opening the webpage, look in the debugger for messages: image

Example of a broken forwarded listener:

the-thing-about-websockets-and-ssh-tunnels $  go run main.go 127.0.0.1:22
2024/05/03 10:46:39 *ssh.tcpListener listening on 127.0.0.1:13337

Now, when opening the same webpage, the websocket is stuck in (pending): image

What did you expect to see?

I did not expect there to be any differences :)

I don't know if this bug is in net/http or x/crypto/ssh - but given that HTTP hijack'ing usually returns an error if it is unable to hijack a connection, I would imagine some error checking is to be expected in net/http when SetReadDeadline returns an error

adrianosela commented 1 week ago

Big ➕1 on this one... I have an HTTP reverse proxy (i.e. httputil.ReverseProxy ) behind an SSH tunnel. Can't do WebSockets over that because the ssh Conn does not support deadlines....

It seems to me like adding setting/checking deadlines to the SSH channel objects (i.e. here) wouldn't be too hard?

gopherbot commented 1 day ago

Change https://go.dev/cl/562756 mentions this issue: ssh: add deadlines support for channels