Set Up an Onion Service on Debian

Install and Configure tor

In this article, I’ll show you how to configure Tor with Apache, and Unix Socket on Debian 11 - Bullseye. Each setup will be tested locally. Some months back, I installed tor on Ubuntu Focal. You can use it as a guide to install Tor. Just replace the distribution with the one you’re using. You can find a list of repositories for Debian and Ubuntu here.

Verify Tor Client

I’ll be using python to test the connection. The required dependencies are requests, and pysocks. Install both by running:

pip3 install requests
pip3 install requests[socks]

The script:

import requests as req

proxies = {
            "http": "socks5://127.0.0.1:9050",
            "https": "socks5://127.0.0.1:9050"
        }

ip = req.get('https://ident.me').text
print(f"My IP: {ip}")

torip = req.get("https://ident.me", proxies=proxies).text
print(f"Tor IP: {torip}")

Run it:

root@debian:~# python3 checktorip.py
My IP: 102.112.126.171
Tor IP: 199.195.250.77

Apache

First, edit /etc/tor/torrc, look for:

## This section is just for location-hidden services ###

Below, you’ll see HiddenServiceDir, and HiddenServicePort. Uncomment both lines. Here’s mine:

HiddenServiceDir /var/lib/tor/hidden_service/
HiddenServicePort 80 127.0.0.1:80
  • HiddenServiceDir: Will contain information about your onion service. It could be any directory that the Tor user(debian-tor user on debian) have read/write permission. I’ll stick with the default. Once you restart Tor, the directory will be populated with the required information.

  • HiddenServicePort: The above configuration specifies 80 127.0.0.1:80. Which says, clients accessing your server on port 80 will be redirected to 127.0.0.1:80(on the localhost side).

Restart Tor, and you should see a list of files, in your HiddenServiceDir:

root@debian:/var/lib/tor/hidden_service# ls
authorized_clients  hostname  hs_ed25519_public_key  hs_ed25519_secret_key

The one we’re interested in, is hostname. This file contains your onion service name or domain name:

root@debian:/var/lib/tor/hidden_service# cat hostname
bkg4yzo752uounfarllk4z7ieav3ylv4kd6vstzi2xofu5sm55ov7tyd.onion

I’ll edit the default Apache Webpage configuration, and change the ServerName to the onion domain:

<VirtualHost *:80>
        ServerName bkg4yzo752uounfarllk4z7ieav3ylv4kd6vstzi2xofu5sm55ov7tyd.onion

        DocumentRoot /var/www/html


        ErrorLog ${APACHE_LOG_DIR}/error.log
        CustomLog ${APACHE_LOG_DIR}/access.log combined

</VirtualHost>

The content of /var/www/html/index.html:

Tor service is up and running.

Add your server’s IP and onion domain to /etc/hosts, and browse to http://domain.onion. To browse on the command-line, run the following script:

import requests

session = requests.session()

session.proxies["http"] = "socks5h://localhost:9050"
#session.proxies["https"] = "socks5h://localhost:9050"


req = session.get("http://bkg4yzo752uounfarllk4z7ieav3ylv4kd6vstzi2xofu5sm55ov7tyd.onion")
print(req.text)

or this one:

import requests
import sys

url = sys.argv[1]

session = requests.session()

session.proxies["http"] = "socks5h://localhost:9050"
session.proxies["https"] = "socks5h://localhost:9050"

req = session.get(url)
print("")
print(req.text)

I’m using socks5h here, because the server is being resolved by the socks server itself:

root@debian:~# python3 checktorip.py  http://bkg4yzo752uounfarllk4z7ieav3ylv4kd6vstzi2xofu5sm55ov7tyd.onion/

Tor Service is up and running.

Multiple Onion Service

To have multiple onion sites, just add another HiddenServiceDir, and HiddedServicePort in /etc/tor/torrc:

### blog
HiddenServiceDir /var/lib/tor/hidden_service_blog/
HiddenServicePort 80 127.0.0.1:8787

Then restart Tor. Don’t create the directory by yourself, or else you’d have to manually change the owner and give it the required permissions. Once done, add an additional VirtualHost section for apache(am using the same file):

<VirtualHost *:80>
        ServerName bkg4yzo752uounfarllk4z7ieav3ylv4kd6vstzi2xofu5sm55ov7tyd.onion

        DocumentRoot /var/www/html


        ErrorLog ${APACHE_LOG_DIR}/error.log
        CustomLog ${APACHE_LOG_DIR}/access.log combined

</VirtualHost>


<VirtualHost *:8787>
        ServerName 5yl4tewyiftvxsgan5mtuxyqtzprl6uwqlwrymknqjkzpsvqg66dqqqd.onion

        DocumentRoot /var/www/blog


        ErrorLog ${APACHE_LOG_DIR}/error.log
        CustomLog ${APACHE_LOG_DIR}/access.log combined

</VirtualHost>

Add Listen 8787 in /etc/apache2/ports.conf:

Listen 80
Listen 8787

Follow the same steps as above, by restarting both Apache and Tor, edit your hosts file, and browse to the domain:

root@debian:~# python3 checktorip.py http://5yl4tewyiftvxsgan5mtuxyqtzprl6uwqlwrymknqjkzpsvqg66dqqqd.onion

Blog is up and running as an onion service.

Unix Socket on Nginx

According to the documentation of Tor, a unix socket will avoid leaking an onion service to a local network(e.g wireshark/tcpdump). Apache doesn’t support unix socket. You’ll need Nginx. I’m starting from scratch.

My torrc configuration and hostname:

HiddenServiceDir /var/lib/tor/hidden_service/
HiddenServicePort 80 unix:/var/run/main-onion-service.sock

Instead of an internal port, specify a unix socket. View the hostname:

qlahjdyjbilvohq5s3ryxcg75qhfjjhbh5ghwxxejhnellpvfvisuoyd.onion

Specify the same socket in your nginx configuration like so:

root@debian:~# cat /etc/nginx/sites-available/tor_services
server {
	listen unix:/var/run/main-onion-service.sock;
	server_name qlahjdyjbilvohq5s3ryxcg75qhfjjhbh5ghwxxejhnellpvfvisuoyd.onion;
	index index.html;
	root /var/www/tor/main;
}

Create the root, and index.html. Make a symlink in /etc/nginx/site-enabled:

ln -s /etc/nginx/sites-available/tor_services /etc/nginx/sites-enabled/

Restart both Tor, and Nginx, and browse to the domain:

root@debian:~# python3 checktorip.py http://qlahjdyjbilvohq5s3ryxcg75qhfjjhbh5ghwxxejhnellpvfvisuoyd.onion

Main Tor service is up and running.

Multiple Onion Services Listening on Unix Sockets

You follow the same steps as above. Add both a HiddenServiceDir and HiddenServicePort:

# Main
HiddenServiceDir /var/lib/tor/hidden_service/
HiddenServicePort 80 unix:/var/run/main-onion-service.sock

# Blog
HiddenServiceDir /var/lib/tor/hidden_service_blog/
HiddenServicePort 80 unix:/var/run/blog-onion-service.sock

Restart Tor, and view the domain name:

root@debian:~# cat /var/lib/tor/hidden_service_blog/hostname
ysbwxbknrx5xvmx3slxli42b4ze34pgpjrsdlmjywljskvczxobi6xad.onion

Modify your nginx configuration or create a new one. No symlink is require this time:

server {
        listen unix:/var/run/main-onion-service.sock;
        server_name qlahjdyjbilvohq5s3ryxcg75qhfjjhbh5ghwxxejhnellpvfvisuoyd.onion;
        index index.html;
        root /var/www/tor/main;
}

server {
        listen unix:/var/run/blog-onion-service.sock;
        server_name ysbwxbknrx5xvmx3slxli42b4ze34pgpjrsdlmjywljskvczxobi6xad.onion;
        index index.html;
        root /var/www/tor/blog;
}

Restart both services, and verify it:

root@debian:~# python3 checktorip.py http://ysbwxbknrx5xvmx3slxli42b4ze34pgpjrsdlmjywljskvczxobi6xad.onion

Blog is up and running as an onion service.

Sometimes Nginx won’t restart because the unix sockets in specified in HiddenServicePort are still present in /var/run/. A lot of users are facing this issue. You will have to remove them manually and then restart the web server.

“Want to keep Christ in Christmas? Feed the hungry, clothe the naked, forgive the guilty, welcome the unwanted, care for the ill, love your enemies, and do unto others as you would have done unto you.”Steve Maraboli