I have a WebSocket Client which was connected to WebSocket Server directly. WS_Server <--> WS_Client
I need a forward proxy to intercept the data between client and server. WS_Server <--> Proxy <--> WS_Client
I was experimenting with different implementations for Proxy Servers. This is what I have thus far. I am using nhooyr.io/websocket
It is a simple HTTP server that WS client dials to. The Proxy Server accepts the WS connection and then itself dials to the target WS server. Once these connections are established, then it just copies data between these two connections. The WS_Client to Proxy connection is not TLS encrypted but the proxy to WS server conn is TLS encrypted.
...
import (
...
"nhooyr.io/websocket"
)
func main() {
http.HandleFunc("/rest/v1/client/proxy", handleWebSocketConnection)
err := http.ListenAndServe(":8081", nil)
if err != nil {
log.Fatalf("Failed to start proxy server: %v", err)
}
}
func handleWebSocketConnection(w http.ResponseWriter, r *http.Request) {
// Accept WebSocket conn from Client
connA, err := websocket.Accept(w, r, nil)
if err != nil {
log.Printf("Failed to accept WebSocket connection: %v", err)
return
}
defer connA.Close(200, "Closing connectionA")
// Connect to Dest Server
caCert, err := os.ReadFile(caCertPath)
caPool := x509.NewCertPool()
if !caPool.AppendCertsFromPEM(caCert) {
log.Fatalf("Failed to append CA certificate")
}
tlsConfig := &tls.Config{
RootCAs: caPool,
}
httpClient := &http.Client{
Transport: &http.Transport{
TLSClientConfig: tlsConfig,
},
}
// Dial to dest
connB, _, err := websocket.Dial(r.Context(), DestWsAddr, &websocket.DialOptions{
HTTPClient: httpClient,
})
if err != nil {
log.Printf("Failed to connect to Dest: %v", err)
return
}
defer connB.Close(200, "Closing connectionB")
// Start goroutines to forward messages between client and agg
go forwardMessages(connA, connB)
go forwardMessages(connB, connA)
// Keep the connection open indefinitely
select {}
}
func forwardMessages(src, dst *websocket.Conn) {
ctx := context.Background()
for {
// Read messages from the source connection
msgType, msg, err := src.Read(ctx)
log.Printf(" : [%v]", string(msg))
if err != nil {
log.Printf("Failed to read message: %v", err)
return
}
// Write the message to the destination connection
err = dst.Write(ctx, msgType, msg)
if err != nil {
log.Printf("Failed to write message: %v", err)
return
}
}
}
This seems to be working fine, and I am able to intercept the data (msg). Some (very little data makes sense but most of it is gibberish. How can I make sense of this info? Why am I understand some data but not all of it? I verified this in Wireshark as well, but I see the same info there after unmasking that Wireshark does.
Another point that might be useful is that it is not just a simple WS connection. Once the WS is established, then we also do multiplexing on top of this WS conn using yamux
...
...
2024/11/13 06:13:56 : [HTTP/1.1 200 OK
]
2024/11/13 06:13:56 : []
2024/11/13 06:13:56 : [Content-Length: 0
]
2024/11/13 06:13:56 : []
2024/11/13 06:13:56 : [
]
2024/11/13 06:13:56 : []
2024/11/13 06:13:56 : [��p� V�Cd^너+�Cn2A�/�RS�x�e8 �W��R����c;E�.�ى�+D2td>�K�,*'internalServer.server.abc.xyz.com
�+3&$ ����,�-)��b�a(�%/�s�L����9]
2024/11/13 06:13:56 : [�]
0��M&�� �3 06:13:56 : [zv�-ee�j�N�=���&�m8T[���=��P �W��R����c;E�.�ى�+D2td>�K.+3$ ��{6�
`�N���NAw��^X��y,R5`��i_���>K4�6Y��aɂ�w;vSѰ]
�n��[���`���ab�+J��%�=���bu���O�p�iUSV�/_���9�U������[Z�~cn-�l�v��i>X���}U�I�ds�Z�v�?&�r������<�����Y�T��λ���T܌
Any pointers would be very useful. Thanks!