My Profile Photo

Sheogorath's Blog


Depending on the time of the day a friend, a colleague, a wise guy. The beauty of the world is its sense of humor to show humans their way by letting them search their own.


Cover image for this blog post

Shivering-Isles Onion Service

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)

Once Traefik is set up, you should set up a Tor hidden service. You can use my tor container image1 for that.

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:

browser -> 1st hop -> 2nd hop -> 3rd hop <- 2nd hop <- 1st hop <- your onion service

With HiddenServiceSingleHopMode 1 the connection gets 2 hops less:

browser -> 1st hop -> 2nd hop -> 3rd hop/1st hop <- your onion service

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"

Replace <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.

Conclusion

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.

Photo by Jamie Templeton on Unsplash


  1. Ready to use builds can be found on quay.io 

  2. Guide for onion services by Torproject 

  3. Which I would expect to be the main use case