How to Hide Your Service from Prying Eyes (Using Tor as a Tunnel to the Open Internet)
Introduction
At Trustcrypt, we understand that privacy and resilience against unwanted attention are crucial when hosting services online. Whether you run a web app, a gaming server, or any other type of online service, ensuring anonymity and uninterrupted availability can be a top priority.
This article introduces TORTCB (Tor TCP Chain Balancer) – a handy solution that sets up multiple Tor hidden services, then balances incoming traffic across them. The project is open-source on GitHub. Below, we explain what it does, why it might be useful, and how you can set it up.
Why Hide Your Service?
Tor can do a lot for you:
- Provide anonymity: When a server is placed behind a
.onionaddress, its real IP and physical location remain hidden. - Streamline access without public IPs: No need for manual port forwarding or obtaining a public IP address.
- Protect privacy: Only you know your server’s real IP. Externally, only the
.onionaddress is visible.
However, a typical Tor hidden service uses a single Tor process and a single tunnel. If you want extra redundancy or want to handle higher traffic by distributing load across multiple .onion addresses, TORTCB does exactly that.
What Is TORTCB?
TORTCB (Tor TCP Chain Balancer) is essentially two Docker images working in tandem:
tor_forward:- Spins up multiple Tor hidden services.
- Each service listens on a unique
.oniondomain and forwards traffic to a local port (where your actual app runs). - Generates a
domains.listfile containing all the new.oniondomains and their corresponding ports.
haproxy_receiver:- Reads the
domains.listfile and launches a dedicated Tor client for each.oniondomain, ensuring each has its own isolated Tor process. - Uses
socatto receive traffic separately for each onion domain. - Aggregates everything with HAProxy, combining all Tor tunnels into a single front-end listening port.
- Reads the
The end result is a cluster of .onion addresses (for redundancy) consolidated behind one external HAProxy port, enabling a robust load balancing setup for Tor hidden services.
How It Works in Practice
- Multiple Onion Addresses:
tor_forwardcreates several.oniondomains. - Unified Entry Point:
haproxy_receivermerges traffic from those domains into one HAProxy port. - Load Spreading: Instead of overloading one single Tor tunnel, your incoming connections are distributed across multiple
.onionendpoints.
A simplified diagram:
[User] -> .onion addresses -> [tor_forward] -> localhost:5000 (your app)
|
v
[HAProxy on a single port] ->[haproxy_receiver]
You can choose to connect through any of the .onion addresses, or simply direct all users to that single HAProxy entry port (useful for local or external balancing).
Quick Start
1. Requirements
- Docker and (preferably) Docker Compose.
- The TORTCB repository: GitHub – keklick1337/tortcb.
- An application running locally, for example on port
5000, that you want hidden behind Tor.
2. Clone the Repository
git clone https://github.com/keklick1337/tortcb.git
cd tortcb
Inside, you will see two main folders: tor_forward/ and haproxy_receiver/.
Setting Up tor_forward
tor_forward is the container that creates multiple Tor hidden services, each forwarding traffic to your actual application.
Build the Docker image:
cd tor_forward
docker build -t tor_forward .
Run the container:
docker run -d \
--name tor_forward \
-e HOST_IP=host.docker.internal \
-e HOST_PORT=5000 \
-e TOR_INSTANCES=3 \
-e RANDOM_PORT_START=20000 \
-v $(pwd)/data/tor_forward:/var/lib/tor/hidden_services \
tor_forward
Key parameters:
HOST_IP/HOST_PORT: IP and port of the application you’re hiding (e.g.,5000).TOR_INSTANCES: Number of.onionaddresses to generate.RANDOM_PORT_START: The initial port number Tor will start binding to on your host.
After launching, check ./data/tor_forward/domains.list for lines like:
abcde12345.onion:20000
fghij67890.onion:20001
Each .onion domain maps to a local port.
Setting Up haproxy_receiver
haproxy_receiver reads all .onion domains from the domains.list file, starts a Tor client and socat for each one, and then merges them behind HAProxy.
Build the Docker image:
cd ../haproxy_receiver
docker build -t haproxy_receiver .
Run the container:
docker run -d \
--name haproxy_receiver \
-e HAPROXY_LISTEN_PORT=9080 \
-e BASE_LOCAL_PORT=30000 \
-e BASE_SOCKS_PORT=9050 \
-e STATS_PORT=9090 \
-e STATS_USER=admin \
-e STATS_PASS=mypassword \
-v /path/to/domains.list:/etc/domains.list:ro \
-p 9080:9080 \
-p 9090:9090 \
haproxy_receiver
Key parameters:
-v /path/to/domains.list:/etc/domains.list:ro: Points to thedomains.listfile generated bytor_forward.HAPROXY_LISTEN_PORT=9080: The port exposed by HAProxy (mapped to-p 9080:9080).STATS_PORT=9090,STATS_USER=admin,STATS_PASS=mypassword: Optional HAProxy stats page credentials.
After launching, the logs will confirm each .onion domain is paired with its own Tor process and socat listener, all combined into HAProxy on port 9080.
Verifying Your Setup
- Check Your App: Make sure your target service is up on
HOST_IP:HOST_PORT(for instance,localhost:5000). - Test an Onion Address: Pick any generated onion domain (e.g.,
abcde12345.onion:20000). Try connecting to it via Tor Browser orcurlthrough a SOCKS proxy. You should reach your service anonymously. - Check HAProxy: Port
9080will be your consolidated external entry point. You can open that port publicly, tunnel it, or restrict it behind a VPN—whatever suits your environment. - HAProxy Stats: Go to
http://, enter the credentials you set, and view connection and load metrics in real time.:9090/stats
Using Docker Compose
For convenience, you can automate everything with a docker-compose.yml. Below are two sample files—one for each container. Adjust them to suit your environment.
docker-compose.yml for tor_forward:
version: "3.8"
services:
tor_forward:
build: ./tor_forward
container_name: tor_forward
environment:
- HOST_IP=host.docker.internal
- HOST_PORT=5000
- TOR_INSTANCES=5
- RANDOM_PORT_START=20000
volumes:
- ./data/tor_forward:/var/lib/tor/hidden_services
docker-compose.yml for haproxy_receiver:
version: "3.8"
services:
tor_forward:
build: ./tor_forward
container_name: tor_forward
environment:
- HOST_IP=host.docker.internal
- HOST_PORT=5000
- TOR_INSTANCES=5
- RANDOM_PORT_START=20000
volumes:
- ./data/tor_forward:/var/lib/tor/hidden_services
Once configured, run:
docker-compose up -d --build
This spawns both containers. tor_forward generates the domains.list, while haproxy_receiver automatically sets up Tor, socat, and HAProxy for each onion domain.
Tips and Caveats
- IP Address Issues on Linux:
host.docker.internalmay not work on some Linux distributions. Use a real IP (e.g.,172.17.0.1) orlocalhostinstead, or considernetwork: hostmode. - Resource Usage: Spinning up multiple
.oniondomains can be CPU-intensive. Each instance runs a Tor client plussocat, so don’t overdo it if your server has limited resources. - Preserving Onion Addresses: As long as you keep the same volume (
./data/tor_forward), the generated.onionaddresses remain the same across container restarts. - Security: For a purely hidden service, you don’t need to map HAProxy’s port
9080to the outside world. You can keep it internal within a Docker network, so all external traffic goes solely through.onionaddresses. - Scaling: If you want more
.onionaddresses, just increaseTOR_INSTANCESintor_forward. Then update your system resources accordingly.
Conclusion
With TORTCB, you can hide your server behind multiple Tor hidden services while balancing load across several tunnels. The process is made simple through Docker containers or Docker Compose, providing:
- Anonymity: True
.onionaddresses with no exposed IP. - Scalability: Multiple Tor tunnels for traffic distribution and resilience.
- Convenience: Quick, containerized deployment using a minimal config.
Explore the TORTCB GitHub repository to dive deeper, open issues, or contribute. Enjoy building faster, safer, and more private services with Tor and TORTCB!
© 2025 Trustcrypt. All rights reserved.