Remember Cloudflare announcing their “Cloudflare Onion Service” a few months ago? This was a huge step for them towards becoming more Tor-friendly and an experiment to reduce the number of CAPTCHAs TorBrowser users have to solve. Besides that, it introduced a quite interesting concept of handling Tor traffic.
I reproduced this idea for my setup. Websites hosted by me become step by step tor-enabled now and those who use the TorBrowser will use a hidden service to connect to this blog and other websites hosted by me.
The basic idea
When you request a web page, like this one, the first thing your browser does is a DNS lookup. This resolves the name to an IP-address and this IP-address is now used to set up a TCP connection. On top of this TCP connection, there is now a TLS handshake happening to communicate securely and authenticate the other side. With all that done, your browser will request the content of the site, like this article.
Those of you, who are aware of network basics, should notice that there are various so-called layers involved. Those are defined by the OSI 7 layer model. The idea of this model is, that you can swap anything underneath a layer without the need to touch any of the above. The layer interesting for this setup is layer 4, the transport layer. This is where TCP and UDP lives. TCP is a protocol to create stateful connections between two endpoints, by using their IP addresses and port numbers.
Guess what is also living on layer 4 and provides stateful connections? Exactly, Tor.
Now all you need to do is bring both things together and swap out TCP for Tor, which means you replace IP-Addresses and the Port number with Tor addresses and a port number. Everything on top can stay as it is. TLS still works, HTTP inside of TLS anyway and you proofed that the OSI model works. Great!
The problem: Discovery
Not so fast. There is one big problem around. Onion addresses are rather random by their nature and tend to be hard to remember. May reminds you of IP addresses and this association is not completely wrong. Still, there is no real DNS for tor addresses that would map a name you can remember to a tor address that is rather random.
Instead you have something called
alt-svc header in HTTP.
alt-svc tells your browser about alternative ways to reach a website. And while it’s now used to inform browsers about QUIC, it can also be used to tell it about an onion address.
Implementation with Traefik
Enough theory, let’s get to work! The first step to do is, of course, setting up Traefik itself. Since there are more than enough tutorials about that out there, I’ll leave the details to them. (Also it should work with any other reverse proxy that terminates your HTTPS connections, as well)
I use the following configuration:
SocksPort 0 HiddenServiceDir /data/traefik HiddenServicePort 80 proxy:80 HiddenServicePort 443 proxy:443 HiddenServiceNonAnonymousMode 1 HiddenServiceSingleHopMode 1 SafeLogging 1
Mandatory for things to work are:
HiddenServiceDir /data/traefik HiddenServicePort 80 proxy:80 HiddenServicePort 443 proxy:443
proxy is the hostname of the Traefik instance, if you run it on the same host, and without containers, you can just use localhost here. This configures your hidden service to forward port 80 and port 443 to your Traefik instance.
HiddenServiceSingleHopMode is used to optimize the latency a bit. Usually, your onion service2 and is behind 3 hops as well as your users who connect to it. The 3rd hop from both directions is identical.
Means your connection looks like this:
HiddenServiceSingleHopMode 1 the connection gets 2 hops less:
As you may notice your anonymity is no longer guaranteed, which is the reason for
HiddenServiceNonAnonymousMode 1 to be set. Since you run your proxy publicly anyway and you are not anonymous this way, it’s reasonable to get rid of the extra hops, but you can also stay safe and keep them if you prefer that.
SocksPort 0 tells tor to not listen on any port for the Socks proxy that you would use with other applications. And
SafeLogging 1 should prevent sensitive information of your clients to be leaked to your logs.
With the hidden service running, it’s time to make your services aware of the new endpoint.
When you combine Traefik with docker3, you can use the Traefik labels to enable the onion service support:
version: '2' services: website: image: some/image labels: - "traefik.frontend.headers.customResponseHeaders=alt-svc:h2=<your onion service address>:443; ma=2592000"
<your onion service address> with the content of the
hostname file that you can find in the data directory of your tor service
The label will tell Traefik to set the
alt-svc-header. So you don’t have to modify any server code running inside your container. And that’s basically it, the TorBrowser will now automatically pick the alternative service up and run all subsequent requests through it.
With the Shivering-Isles Onion Service as well as your implementation, we can help TorBrowser users to get connections without leaving the Tor network after the first connect and put load off from exit nodes.
When you are using the TorBrowser right now, it already picked up the Shivering-Isles Onion Service and do all subsequent requests were using the hidden service instead of the public IP address.
Hope you consider this helpful and feel inspired to set up your own version of this. If you need more inspiration, just check the Traefik role I wrote for my infrastructure repository. This works together with my static_websites role which automatically picks up the hidden service and configures it for all hosted pages. Looking forward to your feedback!
Additional note: Even when you run behind Cloudflare but want to improve the experience for Tor users that don’t want their data shared with Cloudflare, this setup and pass the
alt-svc of your local onion service through Cloudflare. Only the initial request is visible to Cloudflare, all follow up requests go directly to your hidden service.