The Tor network, short for The Onion Router, is a free and open-source software that enables anonymous communication by directing internet traffic through a free, worldwide, volunteer overlay network.
It consists of thousands of volunteer-run servers (nodes), which are categorized into three types: relays (also known as middle relays or non-exit relays), exit nodes, and bridges.
When we connect to TOR, the traffic is routed via 3 different servers:
I was really in shock when I checked the asymetry between the number of tor users and tor relays out there. Long story short, about 6000 servers for ~4 million connecting users.
Our focus today will be on running with Docker: TOR Bridges and TOR Middle Relays, as the exit relays have legal considerationsto be aware of.
TOR Middle Relay with Docker
Relays (Middle Relay or Non-Exit Relay) are the TOR nodes that pass your data along the Tor network but do not connect to the final destination.
When you send data over Tor, it gets wrapped in layers of encryption, and each relay peels off a layer to reveal the next relay the data should be sent to. This is where the term onion routing comes from.
Why they are important? Middle relays add to the overall speed and capacity of the Tor network and contribute to the “onion routing” process. You can check the running nodes (relays and bridges) here.
To install TOR without docker there are tons of guides on the internet, we would have to use sudo apt install tor, but…let’s get our containerized version - a Docker image to run a Tor middle relay.
Remember - This container will be running a Tor Middle Relay, make sure to understand the requirements. *The project recommends TOR relays to have >10 Mbps upload and download bandwidth and a public IPv4 address, preferably static one.
Building a TOR Middle Relay Docker Image
For building our Image of the TOR Middle Relay, we need: A Dockerfile
and a torrc.middle
file.
The Dockerfile looks like this:
#We start from a alpine image
FROM alpine:latest
# Note: Tor is only in testing repo
RUN apk update && apk add \
tor \
--update-cache --repository http://dl-3.alpinelinux.org/alpine/edge/testing/ \
&& rm -rf /var/cache/apk/*
#https://dl-cdn.alpinelinux.org/alpine/latest-stable/main/
# default port to used for incoming Tor connections
# can be changed by changing 'ORPort' in torrc
EXPOSE 9001
# copy in our torrc files
#COPY torrc.bridge /etc/tor/torrc.bridge
COPY torrc.middle /etc/tor/torrc.middle
#COPY torrc.exit /etc/tor/torrc.exit
# make sure files are owned by tor user
RUN chown -R tor /etc/tor
USER tor
ENTRYPOINT [ "tor" ]
Before we run our node, We will need this torrc.middle
file: it has to be in the same location as our Dockerfile
sudo nano torrc.middle
ORPort 9001
## A handle for your relay, so people don't have to refer to it by key.
Nickname your_desired_nick
ContactInfo ${CONTACT_GPG_FINGERPRINT} ${CONTACT_NAME} ${CONTACT_EMAIL}
ExitPolicy reject *:*
Now, we just need to execute the following command to Build our Docker Tor Relay Image: you can use any name for the image, I have chosen tor-relay.
DOCKER_BUILDKIT=1 docker build --no-cache --progress=plain -t tor-relay .
Running a TOR Middle Relay with Docker
After the torrc.middle file is created and the image is built, you can just run the Docker container with the tor relay using the name of the image that we have just created: if you follow the guide, the name is tor-relay:
docker run -d \
-v /etc/localtime:/etc/localtime \ # so time is synced
--restart always \ # why not?
-p 9001:9001 \ # expose/publish the port
--name tor-relay \
tor-relay -f /etc/tor/torrc.middle
or if you prefer with docker-compose.yml
:
version: '3.8'
services:
tor-relay:
image: tor-relay
command: -f /etc/tor/torrc.middle
restart: unless-stopped
volumes:
- /etc/localtime:/etc/localtime
ports:
- 9001:9001
This will start the TOR middle relay inside a Docker container. You might want to check the container’s logs to see how is going:
But, wait, what does this log mean? I don’t have an IPv4?!
Jul 01 19:21:37.000 [notice] Now checking whether IPv4 ORPort your_public_ipv4:9001 is reachable... (this may take up to 20 minutes -- look for log messages indicating success)
TOR Relay Through VPN: IPv4 solution
If your ISP is not providing you with a IPv4 anymore, you can try this setup: Gluetun container will direct the traffic through the desired VPN (Here I Used Mullvad with Wireguard configuration, but any wireguard will do) and we can indicate to our TOR Docker Container to Use that Network for the traffic
- We need the Gluetun container:
version: '3.3'
services:
gluetun:
image: qmcgaw/gluetun
container_name: vpn-gluetun
cap_add:
- NET_ADMIN
ports:
- 9001:9001
environment:
- VPN_SERVICE_PROVIDER=mullvad
- VPN_TYPE=wireguard
- WIREGUARD_PRIVATE_KEY= you_will_need_this_input_from_the_vpn_config_file (PrivateKey field)
- WIREGUARD_ADDRESSES= and_also_the_ipv4_version (Address field)
- SERVER_CITIES=New York NY #choose any available city
volumes:
- /Home/Docker/Gluetun:/gluetun
restart: unless-stopped
- And the TOR Docker Image routed Through VPN:
version: '3.3'
services:
tor-relay:
image: tor-relay
command: -f /etc/tor/torrc.middle
restart: unless-stopped
volumes:
- /etc/localtime:/etc/localtime
network_mode: "container:vpn-gluetun"
# ports: #it is routed to gluetun
# - 9001:9001
About Port-Forwarding
If your device that is running the TOR Docker Image is under a NAT - You will need to port forward to the device and port: 192.168.1.10:9001 (saying that thats the local IP of your device)
And also if you want, you can check the Network Interface of the container, remember to use:
docker network ls
And you will see the Network_ID of our tor container. Then look for the network interface that has the same patter: br-<network_id>
ifconfig
When your tor middle relay is validated, you will be part of the network. Also, there is a beautiful interactive map showing the TOR Flow, hope that you will find it as interesting as I do.
TOR Bridge Relay with Docker
Running a TOR Bridge with Docker
Bridge (Bridge Relay) are Tor relays that aren’t listed in the public Tor directory, making them harder for network adversaries to detect and block.
- They are used to help users connect to the Tor network in places where public Tor relays are blocked, such as under oppressive regimes that try to control internet access.
- If someone is unable to connect to the Tor network because their ISP or government has blocked all the known Tor relays, they can try using a bridge instead.
For the Tor Bridge option, there is already a working Docker Image: thetorproject/obfs4-bridge
We just need to use the docker-compose.yml provided:
version: "3.4"
services:
obfs4-bridge:
image: thetorproject/obfs4-bridge:latest
networks:
- obfs4_bridge_external_network
environment:
# Exit with an error message if OR_PORT is unset or empty.
- OR_PORT=${OR_PORT:?Env var OR_PORT is not set.}
# Exit with an error message if PT_PORT is unset or empty.
- PT_PORT=${PT_PORT:?Env var PT_PORT is not set.}
# Exit with an error message if EMAIL is unset or empty.
- EMAIL=${EMAIL:?Env var EMAIL is not set.}
# Nickname with default value: "DockerObfs4Bridge"
- NICKNAME=${NICKNAME:-DockerObfs4Bridge}
env_file:
- .env
volumes:
- data:/var/lib/tor
ports:
- ${OR_PORT}:${OR_PORT}
- ${PT_PORT}:${PT_PORT}
restart: unless-stopped
volumes:
data:
name: tor-datadir-${OR_PORT}-${PT_PORT}
networks:
obfs4_bridge_external_network:
Together with the .env file
OR_PORT=9001
PT_PORT=443
Then, make it run with:
docker-compose up -d
You should see in the log this kind of message:
Jul 02 12:18:25.000 [notice] Bootstrapped 90% (ap_handshake_done): Handshake finished with a relay to build circuits
Jul 02 12:18:25.000 [notice] Bootstrapped 95% (circuit_create): Establishing a Tor circuit
Jul 02 12:18:25.000 [notice] Bootstrapped 100% (done): Done
To use this bridge in Tor Browser, we need the bridge line:
docker exec <container_id> get-bridge-line
FAQ
How to check if I am using TOR?
The torproject has a dedicated space to check if you are being routed through TOR network.
Containers Questions
- How can I Know the default shell of my container?
docker exec <container_name> cat /etc/shells
- How can i have interactive terminal on my container?
docker exec -it your_container_name /bin/bash
To go back, just:
exit
- How can I Check the logs of my container?
docker logs <container_id>