Tor hidden services allow various types of services (web server, telnet server, chat server, etc) to be operated within the Tor network. This allows both users and service operators to conceal their identities and locations. Just about anything that can be run on the clearnet can be run within the Tor darknet.

Setting up a hidden service on Tor is a simple process and depending on the level of detail, an operator can keep their service completely anonymous. Depending on your use-case, you may or may not choose to anonymize your service at all. For anonymous operation, it is recommended to bind services being offered to localhost and make sure that they do not leak information such as an IP address or hostname in any situation (such as with error messages).

For this guide, we assume a Debian Stretch (or similar) Linux system with a non-root, sudo user. It is also assumed that the target machine has been set up with some standard security practices such as disallowing root logins over SSH, and basic firewall rules. This Tor hidden service will be masked on the darknet, but if the hosting server is deanonymized, a malicious party could uncover the machine’s actual clearnet IP address and attempt to penetrate it or otherwise disrupt service. Depending on the software running the services you are hiding, you may wish to install into a virtual machine to limit damage to the system by code vulnerabilities.

Installing Tor

Before configuring a relay, the Tor package must be set up on the system. While Debian does have a Tor package in the standard repositories, we will want to add the official Tor repositories and install from there to get the latest software and be able to verify its authenticity.

First, we will edit the sources list so that Debian will know about the official Tor repositories.

$ sudo nano /etc/apt/sources.list

At the bottom of the file, paste the following two lines and save/exit.

deb http://deb.torproject.org/torproject.org stretch main
deb-src http://deb.torproject.org/torproject.org stretch main

Now back in the console, we will add the Tor Project’s GPG key used to sign the Tor packages. This will allow verification that the software we are installing has not been tampered with.

$ gpg --keyserver keys.gnupg.net --recv A3C4F0F979CAA22CDBA8F512EE8CBC9E886DDD89
$ gpg --export A3C4F0F979CAA22CDBA8F512EE8CBC9E886DDD89 | sudo apt-key add -

Lastly, run an update and install Tor from the repositories we just added.

$ apt-get update
$ apt-get install tor deb.torproject.org-keyring

Configuring the Hidden Service

We will be editing the torrc file, so let’s bring it up in our text editor:

$ sudo nano /etc/tor/torrc

Going line by line in this file is tedious, so to minimize confusion, we will ultimately rewrite the whole file. We will implement logging into a file located at /var/log/tor/notices.log and assume the local machine has a web server running on port 80. Paste the following over the existing contents in your torrc file:

Log notice file /var/log/tor/notices.log

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

## Once you have configured a hidden service, you can look at the
## contents of the file ".../hidden_service/hostname" for the address
## to tell people.
##
## HiddenServicePort x y:z says to redirect requests on port x to the
## address y:z.

HiddenServiceDir /var/lib/tor/hs_name_of_my_service/
HiddenServicePort 80 127.0.0.1:80

#HiddenServiceDir /var/lib/tor/other_hidden_service/
#HiddenServicePort 80 127.0.0.1:80
#HiddenServicePort 22 127.0.0.1:22

After saving the file, make and permission a log file, then we are ready to restart Tor:

$ sudo touch /var/log/tor/notices.log
$ chown debian-tor:debian-tor /var/log/tor/notices.log
$ sudo service tor restart

If the restart was successful, the Tor hidden service is active. If not, be sure to check the log file for hints as to the failure:

$ sudo nano /var/log/tor/notices.log

Now that the hidden service is working, Tor has created the hidden service directory we defined in the torrc, /var/lib/tor/hs_name_of_my_service/. There are two files of importance within this directory.

There is a hostname file at /var/lib/tor/hs_name_of_my_service/hostname that contains the hidden service’s public key. This public key acts as a .onion address which users on the Tor network can use to access your service. Make a note of this address after reading it from the file with cat:

$ sudo cat /var/lib/tor/hs_name_of_my_service/hostname
nb2tidpl4j4jnoxr.onion

There is also a private_key file that contains the hidden service’s private key. This private key pairs with the service’s public key. It should not be known or read by anyone or anything except Tor, otherwise someone else will be able to impersonate the hidden service. If you need to move your Tor hidden service for any reason, make sure to backup the hostname and private_key files before restoring them on a new machine.

After restarting the hidden service, it may not be available right away. It can take a few minutes before the .onion address resolves on a client machine.

Example - Configure A Web Server with Nginx

Let’s use this hidden service to host a website with Nginx.

First, we will install Nginx and create a directory for our HTML files

$ sudo apt-get install nginx
$ sudo mkdir -p /var/www/hidden_service/

Now, we will create an HTML file to serve, so we need to bring one up in our editor:

$ sudo nano /var/www/hidden_service/index.html

Paste the following basic HTML and save it:

<html><head><title>Hidden Service</title></head><body><h1>It works!</h1></body></html>

Next, we will set the owner of the files we created to www-data for the web server and change the permissions on the /var/www directory.

$ sudo chown -R www-data:www-data /var/www/hidden_service/
$ sudo chmod -R 755 /var/www

We want to make some configuration changes for anonymity. First, let’s edit the default server block:

$ sudo nano /etc/nginx/sites-available/default

Find the block that starts with server { and you should see a line below that reads #listen 80;. Replace this line with to explicitly listen on localhost:

listen localhost:80 default_server;

Now find the line in the block for server_name  set the server name explicitly:

server_name _;

Next we need to edit the Nginx configuration file:

$ sudo nano /etc/nginx/nginx.conf

Find the block that starts with http { and set the following options:

server_name_in_redirect off;
server_tokens off;
port_in_redirect off;

The first option will make sure the server name isn’t used in any redirects. The second option removes server information in error pages and headers. The third option will make sure the port number Nginx listens on will not be included when generating a redirect.

Now we need to create a server block so Nginx knows which directory to serve content from when our hidden service is accessed. Using our text editor, we will create a new server block file:

$ sudo nano /etc/nginx/sites-available/hidden_service

In the empty file, paste the following configuration block. Make sure that the server_name field contains your onion address which you read from the hostname file earlier and not my address, nb2ticpl4j4hnoxq.onion.

server {
listen   127.0.0.1:80;
server_name nb2tidpl4j4jnoxr.onion;

error_log   /var/log/nginx/hidden_service.error.log;
access_log  off;

location / {
        root /var/www/hidden_service/;
        index index.html;
    }
}

After saving the file, we need to symlink it to the sites-enabled directory and then restart Nginx:

$ sudo ln -s /etc/nginx/sites-available/hidden_service /etc/nginx/sites-enabled/hidden_service
$ sudo service nginx restart

To test the hidden service, download and install the Tor Browser on any machine and load up your .onion address.

Example - Configure A Web Server with Apache

Let’s use this hidden service to host a website with Apache. Note: Many criticize Apache for leaking server information by default. Apache takes more effort to secure.

First, we will install Apache and create a directory for our HTML files

$ sudo apt-get install apache2
$ sudo mkdir -p /var/www/hidden_service/

Now, we will create an HTML file to serve, so we need to bring one up in our editor:

$ sudo nano /var/www/hidden_service/index.html

Paste the following basic HTML and save it:

<html><head><title>Hidden Service</title></head><body><h1>It works!</h1></body></html>

Next, we will set the owner of the files we created to www-data for the web server and change the permissions on the /var/www directory.

$ sudo chown -R www-data:www-data /var/www/hidden_service/
$ sudo chmod -R 755 /var/www

Now, we need to make a few changes to the Apache configuration. Let’s start by setting Apache up to only listen to port 80 on 127.0.1.1:

$ sudo nano /etc/apache2/ports.conf

Change the line Listen 80 to Listen 127.0.0.1:80 and save the file.

Now we will access the security configuration file:

$ sudo nano /etc/apache2/conf-enabled/security.conf

Change the line for ServerSignature to ServerSignature Off and the line for ServerTokens to ServerTokens Prod to restrict information the httpd reports about the server.

Then, we will make an edit to the main Apache configuration file to override the server name Apache uses:

$ sudo nano /etc/apache2/apache2.conf

At the very bottom of the file, paste the following. Make sure that the ServerName field contains your onion address which you read from the hostname file earlier and not my address, nb2tidpl4j4jnoxr.onion.

ServerName nb2tidpl4j4jnoxr.onion

Next, we will disable Apache’s mod_status module to turn off status information:

$ sudo a2dismod status

Now we need to create a virtual host so Nginx knows which directory to serve content from when our hidden service is accessed. Using our text editor, we will create a new server block file:

$ sudo nano /etc/apache2/sites-available/hidden_service

In the empty file, paste the following configuration block. Make sure that the server_name field contains your onion address which you read from the hostname file earlier and not my address, nb2tidpl4j4jnoxr.onion.

<VirtualHost *:80>

 ServerName  nb2ticpl4j4hnoxq.onion

 DirectoryIndex index.html
 DocumentRoot /var/www/hidden_service/

  CustomLog /dev/null common

</VirtualHost>

After saving the file, we need to symlink it to the sites-enabled directory and then restart Nginx:

$ sudo ln -s /etc/apache2/sites-available/hidden_service /etc/apache2/sites-enabled/hidden_service
$ sudo service apache2 restart

To test the hidden service, download and install the Tor Browser on any machine and load up your .onion address.

Conclusion

Your hidden service should be up and running, ready to server Tor users. Now that your relay is functioning, it may be a good idea to back up your hostname and private_key files mentioned earlier in the /var/lib/tor/hs_name_of_my_service/ directory.

I would strongly recommend taking a look at riseup.net’s Tor Hidden Services Best Practices guide to learn more about proper setup of your hidden service.

Additionally, subscribe to the tor-onions mailing list for operator news and support!

Sources