-
Notifications
You must be signed in to change notification settings - Fork 3.5k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[question] How to implement io.ReadWriter on websocket.Conn? #588
Comments
The basic io.Copy(dst, src) does not in general make sense to use with WebSocket connections because the operation will discard message boundaries. An NTLM proxy should operate at the The websocket protocol can be proxied with little or no knowledge of the WebSocket protocol. That's the approach taken by the elazarl/goproxy package. The package facilitates the WebSocket handshake and then pumps bytes back and forth at the net.Conn level. If your proxy does not need to inspect the message stream, then I recommend following the approach used in the elazarl/goproxy package. |
Thanks @srybacki -- the issue in elazarl/goproxy websocket implementation is that it attempts to dial directly to the requested URL -- no consideration is given to dialcontext, etc. https://github.com/elazarl/goproxy/blob/1f3cb6622dad84fad2c5416d502fe4c2c2dce48b/websocket.go#L32 So essentially what I am trying to do is create a net.Conn with the NTLM handshake done (ntlm auth is connection based). I was able to use gorilla/websocket to create a Conn to the target, but attempting to bridge the client (net.Conn) and target (websocket.Conn) is where I have the issue. Thinking of injecting a net.conn into gorilla/websocket similar to your solution in #573 Is there a better approach? |
https://godoc.org/github.com/gorilla/websocket#Dialer.DialContext
You should be able to:
1. Create a websocket.Dialer{}
2. Provide your own implementation of (NetDial,NetDialContext) that returns
your own net.Conn implementation to the websocket.Dialer instance
3. Call yourconn.DialContext(...)
Does that make sense?
… |
I think so, this is what I've scribbled together. bdwyertech/goproxy@1d2074f#diff-8990d2c9c7f1690e60641d425c8932f7 |
Does this work:
|
I will try to get something like this functional after lunch -- it seems to get through the NTLM part at least bdwyertech/goproxy@1d2074f#diff-8990d2c9c7f1690e60641d425c8932f7 Hopefully this gives an idea of what I'm trying to do... Basically pass this function a net.Conn and let it do its NTLM dance before I present it to the elazarl/goproxy websocket logic. func dialWithConn(c *net.Conn, url url.URL, dialContext proxyplease.DialContext) (err error) {
d := websocket.Dialer{
ReadBufferSize: 1024,
WriteBufferSize: 1024,
HandshakeTimeout: 45 * time.Second,
NetDial: func(network, addr string) (net.Conn, error) { return *c, nil },
NetDialContext: dialContext,
}
_, _, err = d.Dial(url.String(), nil)
return
} |
I am now confused about what you are trying to do and I may have been confused from the get go. Is your goal to create an HTTP proxy server that authenticates connections to a remote server using NTLM? Further, is the expected use case that the client specifies the proxy server using the HTTP_PROXY, HTTPS_PROXY environment variables or a proxy function? |
Yes. My proxy provides a non-authenticated local proxy which forwards to upstream corporate proxy. It is supposed to make developers lives easier behind corporate NTLM proxies. Everything works great except websockets because the elazarl websocket code assumes it can speak directly to the request URL. Basically a developer runs my proxy as a daemon on their machine and set http_proxy, etc. to point to localhost. This alleviates the need to have user/password set as environment variables. Some other projects are available which achieve similar e.g. CNTLM and PX. PX works well but it is a bit of a resource hog and hard to distribute being Python based. I hope this explains the desire for io.copy. if I was to convert the client connection to a websocket.conn, how could I copy raw stream from one to the other? Maybe this would be easier. |
I recommend that you follow the approach in github.com/elazarl/goproxy or net/http/httputil ReverseProxy. Both of these proxies copy bytes back and forth with no understanding of the WebSocket protocol outside of the handshake headers. For the elazarl/goproxy proxy, replace the calls to dial a net.Conn in ProxyHttpServer.serveWebsocketTLS and ProxyHttpServer.serveWebsocket with a function that dials an NTLM authenticated connection. All other websocket related code can remain as is. For the httputil Proxy, provide a transport that's configured to dial NTLM authenticated connections. A proxy can be written above the *websocket.Conn layer. This approach requires getting several details right at the WebSocket layer and it will not provide a benefit over pumping bytes back and forth. If you do go down this path, then work through carefully how PING, PONG and CLOSE messages are passed back and forth. |
I seem to be getting a little further... This is what I've built up. dialer := proxyplease.NewDialContext(proxyplease.Proxy{URL: proxyUrl, TLSConfig: tlsConfig})
cctx, _ := context.WithCancel(context.Background())
targetConn, err := dialer(cctx, "tcp", targetURL.Host) Full context: Seem to be getting stuff about malformed response, will poke around more tomorrow. |
@srybacki I managed to get all of this to work -- I just needed to upgrade the connection to TLS and establish the intial connection via NTLM dialer. Thanks for pointing me back on the right track! |
Hello!
I have written an NTLM Proxy Forwarder (https://github.com/bdwyertech/gontlm-proxy) and I am trying to implement WebSocket support.
I lean on elazarl/goproxy heavily. I am attempting to add NTLM proxy support to the websocket implementation.
https://github.com/elazarl/goproxy/blob/master/websocket.go#L28-L47
It leverages a basic
io.Copy(dst, src)
between the client and target connections. I am wondering how I can implement an io.ReadWriter interface on a websocket.Conn.Thank you kindly for any insight you could provide!
Brian
The text was updated successfully, but these errors were encountered: