Check out enproxy

53 views
Skip to first unread message

Ox Cart

unread,
Oct 20, 2014, 1:56:44 PM10/20/14
to David Fifield, traff...@googlegroups.com
David,

I noticed that Tor has some open tickets regarding Meek performance.  In particular:



Lantern (flashlight) uses enproxy to implement the core of our domain fronting.  Enproxy already supports streaming downloads and uploads.  It also uses two parallel channels, one for uploading and one for downloading.  This allows it to start sending subsequent upload requests even if a prior download request is still pending.*

On the client side, enproxy actually just implements net.Conn, so you can pretty much plug it in anywhere.  On the server side, it's just an HTTP handler.

Perhaps it can be of use to you.  If you have any questions or run into any issues, please let me know and I'll happily assist.

* - The only exception to this is the first download request, which has to complete in order for us to get some needed metadata back from the server.

Cheers,
Ox

-----------------------------------------------------------------------------------------

"I love people who harness themselves, an ox to a heavy cart,
who pull like water buffalo, with massive patience,
who strain in the mud and the muck to move things forward,
who do what has to be done, again and again."

- Marge Piercy

David Fifield

unread,
Oct 23, 2014, 2:09:55 AM10/23/14
to traff...@googlegroups.com
On Mon, Oct 20, 2014 at 12:56:42PM -0500, Ox Cart wrote:
> I noticed that Tor has some open tickets regarding Meek performance.  In
> particular:
>
> [1]Use Streaming Downloads
>
> [2]Make it possible to have multiple requests and responses in flight
>
> Lantern (flashlight) uses [3]enproxy to implement the core of our domain
> fronting.  Enproxy already supports streaming downloads and uploads.  It also
> uses two parallel channels, one for uploading and one for downloading.  This
> allows it to start sending subsequent upload requests even if a prior download
> request is still pending.*

So if I understand it, the upstream channel sends many requests, in
either buffered or streaming mode, and for each one the server sends as
much data as is available, until a timeout happens. Upstream requests
are still strictly serialized; i.e., only one can be in flight at a
time, in order to keep them in order.

It sounds like a reasonable way to do streaming downloads. Rod suggested
a similar design to me, and he said that the timeout was necessary
because the net/http library won't notify you of a new received request
until you've finished writing the previous request, even when it's
pipelining as it does by default:
https://code.google.com/p/go/source/browse/src/net/http/server.go?r=3266b7e742ba13bf2207d7e1447155653289629d#1199

I'm working on adding sequence numbers to requests, in order to handle
duplicates and retransmissions, so that you can have multiple upstream
requests in flight. I'll bet that what you've done with streaming in the
downstream direction has a bigger effect on performance, though.

David Fifield

Ox Cart

unread,
Oct 23, 2014, 9:15:02 AM10/23/14
to David Fifield, traff...@googlegroups.com
Sort of.  The basic flow is as follows:

On Upstream Channel: Client sends request to a server with whatever data was written to the enproxy.Conn.  The server is identified by a round-robin mechanism.  Once the caller stops writing for more than some timeout, the client finishes the request.
On Upstream Channel: Server streams back as much data in response to request as it can. Once it stops receiving data from the upstream destination for more than some timeout, the server finishes the response.

At this point, the client starts a second parallel channel which we'll call the Downstream Channel.  We have to wait until the first response because we use a value from the response header to route subsequent requests to whatever server was assigned by the round-robin, since that server is the one that's holding open a connection to the upstream destination.

On Upstream Channel: Client continues to send requests to server to send upstream data
On Upstream Channel: Server simply responds OK, does not return any data.

On Downstream Channel: Client polls server with empty requests
On Downstream Channel: Server streams back as much data in response to request as it can. Once it stops receiving data from the upstream destination for more than some timeout, the server finishes the response.

So in a nutshell, the basic idea is to operate parallel channels for uploading and downloading, with the one catch being that the parallelism only starts after the first upstream request and, as an optimization, we immediately return data to the first upstream request if available.

In practice, for typical HTTP requests with small uploads and large downloads, if we've gotten the timeouts right, the whole thing is handled by the first request/response pair and we never even need to use the downstream channel.  For HTTPS, we end up getting into the parallel streams mode because of the TLS handshake, though for this case it's not particularly helpful.  Where the parallel streams are likely most helpful is for persistent connections on which the client is constantly uploading and downloading.

I really need to diagram this out at some point, but hopefully that helps.

Cheers,
Ox

-----------------------------------------------------------------------------------------

"I love people who harness themselves, an ox to a heavy cart,
who pull like water buffalo, with massive patience,
who strain in the mud and the muck to move things forward,
who do what has to be done, again and again."

- Marge Piercy



David Fifield

--
You received this message because you are subscribed to the Google Groups "Network Traffic Obfuscation" group.
To unsubscribe from this group and stop receiving emails from it, send an email to traffic-obf...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

Ox Cart

unread,
Oct 23, 2014, 9:17:22 AM10/23/14
to David Fifield, traff...@googlegroups.com
Oh, btw, my understanding is that Go's http library doesn't support pipelining at all, on either the client or server end.  I've seen statements from Brad Fitzpatrick that indicate he's not a fan of the spec.  Plus with SPDY ostensibly replacing pipelining, I suspect that SPDY, not pipelining, is what will get added to the standard library eventually.

Cheers,
Ox

-----------------------------------------------------------------------------------------

"I love people who harness themselves, an ox to a heavy cart,
who pull like water buffalo, with massive patience,
who strain in the mud and the muck to move things forward,
who do what has to be done, again and again."

- Marge Piercy


Rod Hynes

unread,
Oct 23, 2014, 11:30:12 AM10/23/14
to Ox Cart, traff...@googlegroups.com
On Mon, Oct 20, 2014 at 1:56 PM, Ox Cart <o...@getlantern.org> wrote:
On the client side, enproxy actually just implements net.Conn, so you can pretty much plug it in anywhere.

This is cool!

We're implementing a new Psiphon client in Go (https://github.com/Psiphon-Labs/psiphon-tunnel-core) and use the net.Conn interface as a way to compose transports directly in code -- so as an alternative to SOCKS proxy-style chaining.

E.g, we implemented a meek client with a net.Conn interface, and have an obfuscated SSH net.Conn that transforms the SSH traffic flowing through an underlying net.Conn. So we build a fronted, obfuscated SSH connection by layering some net.Conns and pass the result into the stock Go SSH stack (http://godoc.org/code.google.com/p/go.crypto/ssh#NewClient).

We should be able to easily integrate enproxy. (Eventually -- our small patch to meek, mentioned previously, performs sufficiently well for video streaming and browsing use cases at the moment.)

Ox Cart

unread,
Oct 24, 2014, 10:45:12 AM10/24/14
to Rod Hynes, traff...@googlegroups.com
On Thu, Oct 23, 2014 at 10:30 AM, Rod Hynes <r.h...@psiphon.ca> wrote:
On Mon, Oct 20, 2014 at 1:56 PM, Ox Cart <o...@getlantern.org> wrote:
On the client side, enproxy actually just implements net.Conn, so you can pretty much plug it in anywhere.

This is cool! 

Thanks!
 
We're implementing a new Psiphon client in Go (https://github.com/Psiphon-Labs/psiphon-tunnel-core) and use the net.Conn interface as a way to compose transports directly in code -- so as an alternative to SOCKS proxy-style chaining.


Cool!  Implementing the net.Conn interface with enproxy was challenging from a concurrency perspective.  I'm still not 100% sure I've got all the edge cases worked out.  If I can find the time, I'll have to study your approach to see what I can learn.

E.g, we implemented a meek client with a net.Conn interface, and have an obfuscated SSH net.Conn that transforms the SSH traffic flowing through an underlying net.Conn. So we build a fronted, obfuscated SSH connection by layering some net.Conns and pass the result into the stock Go SSH stack (http://godoc.org/code.google.com/p/go.crypto/ssh#NewClient).

So the domain-fronted connection is the base transport, with some sort of obfuscation layered on top of that and ssh layered on top of that?  What is the purpose of using SSH in this scheme?

We should be able to easily integrate enproxy. (Eventually -- our small patch to meek, mentioned previously, performs sufficiently well for video streaming and browsing use cases at the moment.)

Ah, the all-important streaming test.  My wife marvels at the fact that watching an occasional YouTube video is actually part of my job :)  If you ever do decide to give enproxy a whirl, feel free to ask if you need any assistance.

Rod Hynes

unread,
Oct 29, 2014, 2:16:59 PM10/29/14
to Ox Cart, traff...@googlegroups.com

On Fri, Oct 24, 2014 at 10:45 AM, Ox Cart <o...@getlantern.org> wrote:
So the domain-fronted connection is the base transport, with some sort of obfuscation layered on top of that and ssh layered on top of that?  What is the purpose of using SSH in this scheme?

Our client tries to connect with various on-the-wire protocols, including plain SSH, obfuscated SSH, fronting (HTTPS to a front with obfuscated SSH as the payload), and unfronted plain HTTP (similar to meek in the tunneling mechanism, but looks like HTTP; e.g., for the case where HTTP is whitelisted as in Iran during the last election).

Yes, in code we're able to compose the transports by layering net.Conns.

SSH provides privacy and authentication end-to-end between the Psiphon client and Psiphon server, and that's why we always use that. We also make use of SSH channels to tunnel each client TCP connection.

We use obfuscated SSH in fronted mode since our obfuscation transforms add some random padding to each SSH message (in the KEX phase, so the first few messages only). Without this padding, the first few packets exchanged are always the same size -- and that would also be the case with the meek outer layer added -- which could be an easily exploitable fingerprint.

Ox Cart

unread,
Nov 2, 2014, 12:25:34 PM11/2/14
to Rod Hynes, traff...@googlegroups.com

Nice!  Thanks for explaining.

Reply all
Reply to author
Forward
0 new messages