Linux, Ceph, Openstack and Privacy Tech

Creating your first hidden service Part 2

7th July 2017

Great, you've got your first hidden service set up, and you're ready to do a little more work to secure it. In this post, we will be looking at ensuring Nginx only serves traffic through the Tor process and set up a very basic firewall configuration to block inbound traffic other than whitelisted protocols and services.

As with my previous guide, all instructions on this have been tested to work on Debian 9 (Stretch), and they should be identical for Ubuntu 14.04 onwards too. All of the following commands can be executed as root, or as a regular user with Sudo privileges.

Install and configure ufw

First, let's install ufw. Ufw stands for "Uncomplicated Firewall" and is designed to be an easy to use interface to iptables, which is one of the most widely used software firewall packages around. As ufw is built in to the standard Debian repositories, all we need to do is update our cache and install as shown below

apt-get update && apt-get install -y ufw

Once installed, you can check the status of the firewall using the status command

root@testbox:~# ufw status
Status: inactive

So, now to set up a few rules. We want to allow SSH to log in, but block all other traffic. From a firewall perspective, we should not expect to receive any legitimate traffic inbound. All incoming traffic for hidden services traverses the Tor network through established tunnels.

Use these commands to allow ssh inbound, and block all other incoming traffic by default.

ufw allow ssh
ufw default deny incoming

Now to enable these rules to take effect

ufw enable

With the firewall now up, we need to turn our attention to the Nginx configuration. In the last guide we didn't make any changes to this as the default was sufficient for our needs. However, this worked straight away as the configuration listens on all interfaces by default, and will respond to all requests on that port. Instead, what we want to do is ensure Nginx is only listening on the interface Tor will be (localhost), and that only requests for the correct domain are responded to.

Binding nginx to the loopback interface

If you have followed these steps precisely so far, your configuration will probably be located in /etc/nginx/conf.d/default.conf. As with the torrc from part 1, there is a lot of extra fluff found in the default configuration than we can safely clean out.

echo "" > /etc/nginx/conf.d/default.conf
nano /etc/nginx/conf.d/default.conf

In the now open nano window, paste the following configuration, replacing the value after server_name with the result of your hostname (hidden service address) we covered in Part 1.

server {
    server_name  bvu2luyt2jm33ey5.onion;

    location / {
        root   /usr/share/nginx/html;
        index  index.html index.htm;

Then to ensure the configuration takes effect, restart Nginx. Although configuration changes normally only require a reload, to rebind interfaces requires a full restart of the service.

service nginx restart

This new configuration ensures that Nginx binds itself to listen on port 80 of only the loopback interface. This loopback interface is commonly used to route traffic internally on a device, thus making it easier for software to communicate using a common protocol. You can see this in action by opening up port 80 on your firewall momentarily and attempting to connect directly to the IP address.

ufw allow http

Once you have run this quick test, don't forget to close the firewall again.

ufw delete allow http
ufw reload

Recommended (but optional) extra precautions

To help protect from giving away more information than you really need to, and to ensure we are only seeing requests we are expecting, there are some further configuration changes we can make. These are not strictly required, but it would be good practice to add them in.

The first change we should look at is disabling server_tokens, which are just a means of advertising what webserver software you are running. If you visit your new hidden service and try to find a file that does not exist, you wil see it displays information about your Nginx version. As the visitors have no need to see this, we can disable this by modifying /etc/nginx/nginx.conf and adding the annotated line within the http block:

http {
    include       /etc/nginx/mime.types;
    default_type  application/octet-stream;
    log_format  main  '$remote_addr - $remote_user [$time_local] "$request" '
                      '$status $body_bytes_sent "$http_referer" '
                      '"$http_user_agent" "$http_x_forwarded_for"';

    access_log  /var/log/nginx/access.log  main;

    server_tokens off; # Add this line

    sendfile        on;
    keepalive_timeout  65;
    include /etc/nginx/conf.d/*.conf;
service nginx reload

After you have reloaded the configuration, you should now see the version number of Nginx is no longer displayed.


Next, we can add some very basic protections to the server block itself. As we are only serving static content right now, we should expect only to see GET and HEAD HTTP Requests. Furthermore, we should block any attempts to use the page within iframes. To do this, add the below configuration lines to you /etc/nginx/conf.d/default.conf configuration where denoted with #add

server {
    server_name  bvu2luyt2jm33ey5.onion;

    add_header X-Frame-Options SAMEORIGIN; # Add
    add_header X-Content-Type-Options nosniff; # Add
    add_header X-XSS-Protection "1; mode=block"; # Add

    if ($request_method !~ ^(GET|HEAD)$ ) # Add
    { return 405; } # Add

    location / {
        root   /usr/share/nginx/html;
        index  index.html index.htm;

Once this is updated, give nginx another reload and everything should be good to go.

service nginx reload

To cover what we have done with this section of the guide, we have now ensured that we have bound nginx to only listen to the loopback interface to prevent answering requesting coming from external sources. In addition, we have set up a firewall with ufw to block any would-be request to the server from reaching your application, and minimise what information is leaked out if somebody was to scan your server using tools such as nmap.


Thomas White

View Comments