HTTP Tunneling in Go
Kazuki Higashiguchi
April 23, 2022 @ Go Conference 2022 Spring
Slide 2
Slide 2 text
Agenda
HTTP(S)
Client
WebSocket
server
WebSocket
client
HTTP(S)
Server
1. HTTP Tunneling
2. Establish bidirectional connection using WebSocket
3. HTTP Tunneling over WebSocket
Slide 3
Slide 3 text
About Me
Kazuki Higashiguchi
Backend Engineer at Autify.
ID: @hgsgtk
Slide 4
Slide 4 text
Autify Solution
No-code AI-powered software testing automation
services for Web/Mobile applications
AI automatically maintenance test
scripts
No code unlocks automation at
scale
Slide 5
Slide 5 text
Agenda
HTTP(S)
Client
WebSocket
server
WebSocket
client
HTTP(S)
Server
1. HTTP Tunneling
2. Establish bidirectional connection using WebSocket
3. HTTP Tunneling over WebSocket
Slide 6
Slide 6 text
HTTP tunneling
● Create a network link between two computers in
conditions of restricted network connectivity.
e.g. firewalls, NATs, ACLs
● Use a protocol of higher level (HTTP) to transport a
lower level protocol (TCP)
Slide 7
Slide 7 text
HTTP CONNECT method
● Start bidirectional communications with the requested
resource
e.g. access HTTPS websites, HTTP proxy server
HTTP(S)
Client
HTTP(S)
Server
Proxy
(Gateway)
Slide 8
Slide 8 text
HTTP tunneling with CONNECT
Quoted from HTTP: The Definitive Guide / 8.5 Tunnels
1. Client sends CONNECT request
2. Gateway opens TCP connection
to the server
3. Gateway returns HTTP ready
message to the client
4. Start bidirectional
communication of raw packets
of data
Slide 9
Slide 9 text
A case study: elazarl/goproxy
elazarl/goproxy: An HTTP proxy library for Go
$ curl -I -x http://localhost:8080 https://example.com
HTTP/1.0 200 OK
A basic sample code:
Slide 10
Slide 10 text
Tunneling implementation in elazarl/goproxy
https://github.com/elazarl/goproxy/blob/0bfa7c564b
5ffbece2f0832eb4e714fb971f70f3/https.go#L95
Slide 11
Slide 11 text
Tunneling implementation in elazarl/goproxy
1
2
3
Slide 12
Slide 12 text
Step 1: Take over the proxy connection
http.Hijacker interface allows an HTTP handler to take over
the connection.
Slide 13
Slide 13 text
Step 2: Open TCP connections
Opens TCP connection using net.Dial
Returns HTTP ready message: 200 Connection established
Slide 14
Slide 14 text
Step 3: Streaming with io.Copy
Slide 15
Slide 15 text
Streaming: TLS handshake over TCP connection
● TLS handshake is done
using the streaming
● Application data is sent and
receive after TLS
handshake
Slide 16
Slide 16 text
Key takeaways
● HTTP Tunneling uses a protocol of higher level (HTTP)
to transport a lower level protocol (TCP)
● The basic flow of HTTP tunneling using the HTTP
Connect method
● http.Hijack and io.Copy are key functions to implement
HTTP tunneling in Go
Slide 17
Slide 17 text
Agenda
HTTP(S)
Client
WebSocket
server
WebSocket
client
HTTP(S)
Server
1. HTTP Tunneling
2. Establish bidirectional connection using WebSocket
3. HTTP Tunneling over WebSocket
Slide 18
Slide 18 text
Establish bidirectional connections
WebSocket
server
WebSocket
client
Handshake
WebSocket
Conn
WebSocket
Conn
HTTP(S)
Client
WebSocket
server
WebSocket
client
HTTP(S)
Server
WebSockets
● A mechanism for low-cost,
full-duplex communication on Web
● Designed to work over HTTP
(compatible with the HTTP
protocol)
● Handshake -> Messages -> Close
Slide 21
Slide 21 text
WebSocket client and server using gorilla/websocket
Client:
gorilla/websocket: an WebSocket implementation in Go
Server:
Slide 22
Slide 22 text
websocket.Conn implementation
websocket.Conn has net.Conn in the unexported field.
The return value of http.Hijack will be set to the field conn.
https://github.com/gorilla/websocket/blob/eeb877e8a1fd26c449b7af3cb1d71879287d724c/server.go#L125
Slide 23
Slide 23 text
Agenda
HTTP(S)
Client
WebSocket
server
WebSocket
client
HTTP(S)
Server
1. HTTP Tunneling
2. Establish bidirectional connection using WebSocket
3. HTTP Tunneling over WebSocket
Slide 24
Slide 24 text
HTTP Tunneling over WebSocket
HTTP(S)
Client
HTTP(S)
Server
WebSocket
server
WebSocket
client
WebSocket
Conn
WebSocket
Conn
TCP
Conn
TCP
Conn
$ curl -Lv -x http://websocket-server https://target.local
WebSocket server and WebSocket client jointly act as a
proxy(gateway).
https://target.local
http://websocket-server
streaming streaming
Slide 25
Slide 25 text
How HTTP Tunneling over WebSocket
Slide 26
Slide 26 text
How HTTP Tunneling with WebSocket
WebSocket server tells the address of
destination to WebSocket client.
WebSocket client open a TCP connection to
the destination server.
Slide 27
Slide 27 text
How HTTP Tunneling with WebSocket
Bidirectional message over
an established WebSocket
connection.
Slide 28
Slide 28 text
WebSocket server implementation
A sample code available on
github.com/hgsgtk/go-snippets
Slide 29
Slide 29 text
WebSocket server implementation
1
2
3
Slide 30
Slide 30 text
Step 1: Write messages to WebSocket connection
Send a TCP handshake request
with the destination address.
Slide 31
Slide 31 text
Distinguish several types of messages
This design transfers several kinds of message over a
single WebSocket connection
● Request to open TCP connections
● Notify to establish TCP connections
● Streaming raw packets of data
● etc
Slide 32
Slide 32 text
A case study: Chrome Devtools Protocol
CDP defines the JSON protocol, which is used to communicate in the
WebSocket connection.
Request
Members: id, method, params
Response
Members: id, result, (error)
Slide 33
Slide 33 text
Define own protocol for WebSocket communication
A small example of methods
● Handshake:
Request to open TCP connections
● Completed:
Notify to establish TCP connections
● Communication:
Streaming raw packets of data
Slide 34
Slide 34 text
Step 2: Take over the proxy connection
2
Take over the connection
using http.Hijacker.
Slide 35
Slide 35 text
Step 3: Streaming
3
Use io.Copy to combine
WebSocket connection
and TCP connection.
Slide 36
Slide 36 text
Notes on net.Conn returned from http.Hijack
● http.Hijacker returns net.Conn and
bufio.ReadWriter.
[Code comment on http.Hijack]
The returned bufio.Reader may contain
unprocessed buffered data from the
client.
● Reading net.Conn alone may cause data
to be dropped.
● Read buffers from bufio.Reader.
Slide 37
Slide 37 text
Wrap websocket.Conn to use in io.Copy: io.Writer
Pack incoming packets to defined JSON format
ンu��(gT��!"�.
��ҠF��0�Q��Yl/��L�E8/���c<��>���C�
��m$�X�Ax�Y%^��J�v�[�o�U�a���U�t
�u��5��k��m��&|Ǜq����)�z7����?�…
{
"method": 3,
"data": "ンu��(gT��!"�.
��ҠF��0�Q��Yl/��L�E8/���c<��>�
��C���m$�X�Ax�Y%^��J�v�
[�o�U�a���U�t
�u��5��k��m��&|Ǜq����)
�z7����?�…
"
}
Slide 38
Slide 38 text
Wrap websocket.Conn to use in io.Copy: io.Reader
Read packets from a decoded JSON message
ンu��(gT��!"�.
��ҠF��0�Q��Yl/��L�E8/���c<��>���C�
��m$�X�Ax�Y%^��J�v�[�o�U�a���U�t
�u��5��k��m��&|Ǜq����)�z7����?�…
{
"method": 3,
"data": "ンu��(gT��!"�.
��ҠF��0�Q��Yl/��L�E8/���c<��>�
��C���m$�X�Ax�Y%^��J�v�
[�o�U�a���U�t
�u��5��k��m��&|Ǜq����)
�z7����?�…
"
}
Slide 39
Slide 39 text
WebSocket client implementation
Slide 40
Slide 40 text
Open a TCP connection to the given destination
Dial to the destination address
conn, err := net.Dial("tcp",
“autify.com:443”)
Slide 41
Slide 41 text
Key takeaways
● HTTP Tunneling over WebSocket
● Define own protocol for WebSocket communication and
create a wrap struct for streaming with io.Copy
● Should read both net.Conn and bufio.ReadWriter
returned from http.Hijacker
Slide 42
Slide 42 text
Resources
● RFC 6455 The WebSocket Protocol
● IETF: Tunneling TCP based protocols through Web proxy servers
● Mmdn web doc: Proxy servers and tunneling
● Mmdn web doc: CONNECT
● Wikipedia: HTTP Tunnel
● Wikipedia: WebSocket
● HTTP: The Definitive Guide / 8.5 Tunnels
● Cloudflare: What happens in a TLS handshake? | SSL handshake
● Chrome DevTools Protocol