Skip to content
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

Tunneling over HTTP/2 #37

Open
mmatczuk opened this issue Oct 13, 2016 · 6 comments
Open

Tunneling over HTTP/2 #37

mmatczuk opened this issue Oct 13, 2016 · 6 comments

Comments

@mmatczuk
Copy link
Contributor

@rjeczalik I took your tip and did an experiment to replace yamux with HTTP/2. At first I wanted to make it a small change but it turned out that there were many incompatibilities so I decided to start fresh.

I did a POC that can proxy HTTP and TCP and uses ProxyFunc design (no default functions yet). It turns out that the implementation can be really short and concise with http2 package. Server is ~300LOC and client ~100LOC (mostly consumed by structs and comments). The code is available at https://github.com/mmatczuk/h2tun.

Performance using HTTP/2 is slightly better than using yamux but I think the key benefit is improved stability, you can see a report that I wrote https://github.com/mmatczuk/h2tun/blob/master/benchmark/report/README.md.

This implementation follows a similar design that the current tunnel, I'd like to highlight some changes here

  • There is no identifier sent by client, instead certificate pinning is used
  • ProxyFunc takes io.Writer and io.Reader instead of net.Conn
  • ControlMessage is changed, protocol is a string, it has extra fields, in general it follows some version of Forwarded HTTP Extension https://tools.ietf.org/html/rfc7239.

It's a POC, some things that exists in the tunnel should be migrated to make it truly usable. I also have some new ideas you can see in https://github.com/mmatczuk/h2tun/blob/master/TODO.md.

Please let me know what do you think.
I'd be really grateful for a review if find some time.

Cheers.

cc @cihangir

@rjeczalik
Copy link
Member

aww

@mmatczuk This is so cool! I will definitely take a look soon!

@mmatczuk
Copy link
Contributor Author

Hi @rjeczalik did you find time to have a closer look?

@rjeczalik
Copy link
Member

@mmatczuk Yes sir, took a look. Really nice implementation, however I'm wondering how it'd be possible to use raw TCP/WebSocket without wrapping the stream with TLS handshake. In particular how we could e.g. ssh tunnelserver:12345, where :12345 is a server-side TCP listener that tunnels the connection to client.

@mmatczuk
Copy link
Contributor Author

@rjeczalik thanks for the effort. Addressing your concerns the problem might be that TLS client sees that server certificate and host do not match... On that front TCP and WS are quite a different beasts.

For WS you can run Server on any http server as it's http.Handler so you could fallback to plain http. The way it works with server on https thought is that you have tree separate TLS connection not one end-to-end (if you want ent-to-end TCP proxing can do the job). When Server proxies a request it's agnostic of type of the connection to the server, except that you can check the scheme. Client must open a TLS connection to the end service (if it's required). In practice you can add TLS to services that run on http or take https off, this is a standard stuff that http proxies can do.

SSH should work just fine as it seems not to care i.e.

mmatczuk@shark:~$ ssh ubuntu@foobar
The authenticity of host 'foobar (XX.XX.XXX.XXX)' can't be established.
ECDSA key fingerprint is c2:6b:94:48:51:f6:8b:f6:32:c8:9a:cd:43:17:48:bd.
Are you sure you want to continue connecting (yes/no)? yes
Warning: Permanently added 'foobar' (ECDSA) to the list of known hosts.

On server you do net.Listen("tcp", ":12345"), than you proxy anything regardless if it's TLS or not and on client side you do a net.Dial to TLS backend so you get a TCP connection and then proxy TLS handshake. As you know TLS wraps TCP connections (see listening and wrapping).

Anyway I'd like to start making h2tun production ready (on elementary level) by:

  • Adding dynamic client management on server
  • Moving client connections description (from AllowedClient) to ClientConfig so that, client informs server what it wants on connect
  • Migrating default proxy functions
  • Adding WS support

Then I will add examples to cover your concerns as well.

Have an awesome weekend!

@rjeczalik
Copy link
Member

rjeczalik commented Oct 22, 2016

@mmatczuk Got it, on server side we use plain net.Listener that listens on TCP, we accept connection on public endpoint and multiplex the stream over http2 connection to client, and client proxies raw tcp back to the service. For WS we use http1, upgrade and again stream hijacked TCP over the same http2 connection. I've noticed piece of code that initializes http.Client with only http2.Transport so I thought we do not use http1. All clear and sound. I'm wondering, while we're at a major rewrite, whether we could change the proto a bit and instead of returning just VirtualHostname we could return full URI (so it'd be possible to support path-routing #).

Have an awesome weekend!

Likewise!

@mmatczuk
Copy link
Contributor Author

change the proto a bit and instead of returning just VirtualHostname we could return full URI

This is done to some extent see here, this allows for Client to do the dispatching. What I'd like to do, however, is to enable Server to do dispatching to different clients as well, see point 8 in TODO. The final solution would be to replace host with collection of urlprefix- like expressions.

Note that host and URL path are separated in h2tunc control message (unlike in URI) this is to remove port. The current koding tunnel has some difficulties if you do not run server on default port or if you simultaneously run on http and https. H2tun ignores port as server is agnostic of how it's being run and client should know nothing about it.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants