tessel / t1-runtime

[UNMAINTAINED] Tessel 1 JavaScript runtime.
Other
117 stars 33 forks source link

Automagic socket sharing for net/tls connections via proxy server #702

Open natevw opened 9 years ago

natevw commented 9 years ago

This integrates a client for https://github.com/tessel/proxy into the runtime net/tls modules. In this way, some of the built-in CC3000 WiFi module's limitations and quirks may be avoided. Socket number limitation is raised, and perhaps even IPv6 endpoints via proxy [untested].

Overview

The proxy is enabled and optionally configured via the following process.env keys. These can be set manually, before any require statements that may cause the net module to be evaluated, or via the tessel CLI interface once https://github.com/tessel/cli/pull/167 is released.

In short, you will need to at least provide PROXY_TOKEN/TM_API_KEY to enable proxied net connections, and PROXY_TRUSTED=1 once you understand its implications.

Sample usage

Once the proxy settings are configured, they are applied automatically to all relevant outgoing network connections:

process.env.TM_API_KEY = "FIXME";    // get via https://oauth.tessel.io
require('http').get("http://ipcalf.com/?format=text", function (res) {
  res.on('data', function (d) {
    console.log("proxy ip is:", d.toString())
  });
});

Configurable environment variables

When using the options form of the net.connect or tls.connect helpers, there is now an additional setting:

The HTTP/HTTPS modules tend to forward their options objects into the respective network modules, so you may also be able to control proxying at that level as well. This has not been thoroughly reviewed, and connection-reuse via the default global Agent may interfere in unintended ways.

Important TLS information

Although the client-proxy "tunnel" connection is always done over a tls socket, it was impractical to connect the Tessel runtime's built-in TLS library to a proxied (virtual) socket. Therefore, there is no end-to-end encryption for proxied sockets: the proxy server has access to all data sent/received.

By default, if you create an outgoing connection via the tls (or https) module, the proxy tunnel will not be used — it will use a native CC3K socket to make a direct connection. If you trust the proxy server and would like it to handle tls connections on your behalf, you will need to set PROXY_TRUSTED=1.

Implementation details

There is a new quasi-"abstract base class" Socket which now underlies the original TCPSocket and the new ProxiedSocket classes. Should you create a socket via socket = new net.Socket(), all expected method calls are buffered until you call socket.connect(). At this point, the target host is examined and used (along with all other relevant settings) to determine which type of socket should be used. You socket instance is converted in-place to the appropriate "concrete" subclass and completes its initialization via Don't Try This At Home™ means.

Now, if the connection is via a TCPSocket it pretty much [mis-]behaves as it did before. If it is a ProxiedSocket, however, then a whole different mechanism rolls into action. First, we check to see if there is already a tunnel open to the proxy server. If so, our job is relatively easy: just create a new "substream" (passing a tls/net flag to the server based on the local socket type) and forward the provided host/port information (and a few other methods/events) over to the proxy server.

When no tunnel is active, one is set up by connecting to the proxy server, sending the authorization token, and waiting for its signal to proceed. Most of this is managed by way of the streamplex module, the proxy server and client just add back some of the socket-specific handling. Once all sockets using a tunnel are closed, the tunnel itself is shut down.

Any error on the underlying tunnel will cause it to be closed. The event will propagate to all subsockets, however, so a new tunnel can be re-established when a new proxied socket is opened.

An oracle concerning the CC3000

While tunneling all end-user sockets through a single proxy connection can avoid some pitfalls, any issue that would plague a single network socket will now plague all of your (proxied) sockets together. Beware. Beware!

Like such as in the this issue: https://github.com/tessel/firmware/issues/128

natevw commented 9 years ago

Original TODO:

natevw commented 9 years ago

The floor is now open.

tcr commented 9 years ago

Comment the first: the entire PR message is a thing of beauty. :see_no_evil:

tcr commented 9 years ago

This PR is of good quality and most of my complaints are minor. I don't want to land this without at least some additional proxy-specific tests, which we should talk about over Zulip; otherwise this generally improves code quality and doesn't have any standout issues.

jiahuang commented 9 years ago

lgtm, will do more testing of this over the weekend.