Simplified Service Deployments with Traefik Proxy and Tor
From buying a domain name to setting up the DNS and handing an exposed port on
your ISP router, hosting a web application is often a hassle. Today, I want to show you how to deliver your application to your targeted audience in a matter of seconds using the Tor Network and Traefik Proxy.
But what's that Tor thing, you say?
The Tor Network was originally a project created by an MIT graduate to be used as a one-of-a-kind decentralized network that does not rely on big centralized entities to operate. This software is free and open to maximize transparency and decentralization.
Those of you who are already familiar with Tor, naturally have some reservations about its legal status. Allow me to put your mind at ease. The Tor Network is perfectly legal. That said, it can and has been used for illicit activities, so caution is always advised. Think of Tor as a knife — it is a powerful tool that can cause harm if used improperly.
Disclaimer: Traefik Labs does not condone any illicit acts performed using the Tor Network.
In this article, I am using the Tor Network purely for the purpose of exploring different use cases of Traefik Proxy. There is no official integration between Traefik Proxy and Tor. I will show you a way to configure Tor to expose a web application to the Tor Network via Traefik Proxy, and without touching any network configuration.
The source code for this blog post can be found here.
Prerequisites for this tutorial
Tor can work in many ways with Traefik Proxy. For the sake of this article, I assume that you have already installed and configured:
The entire stack that I'm about to deploy will be architectured like in the following diagram:
Here, Tor will serve as a VPN that allows you to build a tunnel from the Tor Network to the application. Doing so removes the pain of setting up other key components in classic hosting, like setting up DNS, routers NAT, and so on.
Creating the Tor Docker image
To get things started, let's create a Docker container and have Tor run as a service. Create a Dockerfile containing this:
FROM alpine:3.15
RUN apk --no-cache add tor
COPY ./torrc /torrc
USER tor
ENTRYPOINT ["tor"]
CMD ["-f", "/torrc"]
This Docker image contains the Tor binary and a torrc
configuration file.
Check out man tor
for more information on possible configurations of this file and take a look at this article for hosting best practices. For more in-depth configuration guides, make sure to take a look at the official Tor documentation.
But to our example, this torrc
file should contain the following:
HiddenServiceDir /var/lib/tor/traefik/
HiddenServicePort 80 traefik:80
HiddenServicePort 443 traefik:443
HiddenServiceSingleHopMode 1
HiddenServiceNonAnonymousMode 1
SocksPolicy reject *
SocksPort 0
The three first lines define how Tor will transfer incoming requests to Traefik Proxy. Thanks to Docker, the Traefik Proxy service will be named traefik
, and so the tor
service will be able to contact Traefik Proxy directly via this domain name.
Note: I only set the 80
and 443
ports to limit incoming requests to these two ports. Feel free to open/close more ports according to your needs.
Use HiddenServiceSingleHopMode
to run the tor
service as non-anonymous to allow direct connections to Tor. HiddenServiceNonAnonymousMode
is set to 1
as a requirement to enableHiddenServiceSingleHopMode
.
I set SocksPolicy
to prohibit the usage of this service as an entry node to the Tor Network from this host. SocksPort
is set to 0
to disable all client-side services on this host.
Note: You may want to store (using a volume) the folder
/var/lib/tor/traefik/
because it contains all Tor's necessary keys. The folder contains:
- The hostname as the
<base32-encoded-fingerprint>.onion
domain name for this hidden service. - The private key for the symmetric cryptography of the hidden service.
- The
client_keys
that contains the authorization data for the service to whitelist users.
Finally, let's use docker compose
to host the deployment of my Docker services. I need to create a docker-compose.yml
file, and fill it with the following:
version: '3.9'
services:
tor:
build: .
Deploying Traefik Proxy
Now that I can run Tor, it is time to use Tor's power to route requests to my web application.
First, I need to create a Traefik Docker container that will be able to accept requests from Tor and forward them to my web application. For this, I have to create a new service inside the docker-compose.yml
file:
traefik:
image: traefik:v2.7
container_name: traefik
command:
- --providers.docker
volumes:
- /var/run/docker.sock:/var/run/docker.sock:ro
Note: This configuration is the bare minimum required to use Traefik Proxy, and it might not be suited for running a production-grade web application.
The Traefik service does not publish port because all Tor requests will go through to Traefik Proxy thanks to Docker. I also name the traefik
service to fix the connection between Tor and
Traefik Proxy.
There is no need to use TLS as Tor already uses a complete encrypted tunnel and Perfect Forward Secrecy (PFS), so there's no need to use TLS. If you still want to do so, take a look at this guide and keep in mind that only Digicert or HARICA will be able to provide certificates in this case.
Firing up Tor and Traefik Proxy
Now that I have both Tor and Traefik services all set up, I can start both by running:
$ docker compose up -d --build
It is now time to get the hostname of my Tor endpoint to be later used when deploying a simple web application. To do so, I need to do the following:
$ docker compose exec tor cat /var/lib/tor/traefik/hostname
obz7zd7XXXXX.onion
I need to keep this for later, to tell Traefik Proxy to match this hostname.
Setting up a web application
Let's create a simple example service.
web-app:
image: tommoulard/cat
labels:
traefik.http.routers.web-app.rule: Host(`obz7zd7XXXXX.onion`)
Note: I placed my Tor hostname on the rule definition. This way, Traefik Proxy will know to forward requests coming from the Tor service to this web app.
Let's run this new service:
$ docker compose up -d web-app
Ta-da! I just created a Docker container and exposed it on Tor.
Go to your favorite Tor web browser on your Tor host service, type obz7zd7XXXXX.onion
, and you should be able to see a beautiful cat, as your request came through to the web app!
Note: Your browser might say that this page is not secure, since the application uses images hosted on the internet, without the Tor encryption
A small bonus
Now that I have my web application accessible through the Tor network, let's show a little banner on my Tor enabled browser to tell users that my application is accessible through Tor.
For this, I have to add a special header to my non-Tor router. I
created a header middleware that adds a special header for each response to my new router:
web-app:
image: tommoulard/cat
labels:
# my Tor router
traefik.http.routers.web-app-tor.rule: Host(`obz7zd7XXXXX.onion`)
# my header middleware
traefik.http.middlewares.tor-headers.headers.customresponseheaders.Onion-Location: "http://obz7zd7XXXXX.onion"
# my classic router with a middleware
traefik.http.routers.web-app-classic.middlewares: tor-headers
traefik.http.routers.web-app-classic.rule: Host(`cat.example.org`)
Going further
Now that you know how to connect Traefik Proxy to Tor, it will be easy to do much more than just expose a few cats. Feel free to use this knowledge to expose more useful services.
Please make sure to use the Tor Network responsibly and ethically!
I also recommend you visit the Traefik documentation to harness more of Traefik Proxy's power and explore more possibilities to build robust environments.
Bonus point: You can protect your services using the Fail2ban plugin!
Don't hesitate to reach out to our Community Forum with questions.