SSH access through Tor Hidden Service

I have a number of computers that are connected to different networks, sometimes accessible from the Internet, sometimes behind a NAT that I can configure, sometimes behind NAT that I cannot modify and sometimes behind multiple obstacles.

Recently, I discovered that there is a very easy way of dealing with this: Tor Hidden Services. Usually when we think about Tor, we think about the anonymity part of the network, but it also provides a connection that is unfiltered and thus allow access to our services even if they are behind NAT, have a dynamic IP address or are protected by firewalls.

Configuration of a Hidden Service

We will install and configure Tor to expose our SSH daemon to the Tor network through a hidden service.

$ sudo apt update && sudo apt install tor

$ sudo tee -a /etc/tor/torrc <<EOF
HiddenServiceDir /var/lib/tor/onion-ssh/
HiddenServicePort 22

Restart the service so that the configuration directory for your new service is created with the correct permissions and content.

systemctl restart tor@default

Once the service is restarted, you can get the generated address from the service configuration directory.

sudo cat /var/lib/tor/onion-ssh/hostname

Wait a few seconds so that it connects to the network and your service should be available.

Vanity addresses

This part is optional but allows you to create so called Vanity addresses for your hidden service. A vanity address is an address starting with a human readable prefix or suffix. I like to start my services with a given prefix depending on where I’m using it, just because it’s possible.

There are a number of programs available to generate a number of hashes and generate collisions with your prefix. I use Eschalot for this.

sudo apt install build-essential libssl-dev
git clone
cd eschalot && make

As the process is essentially brute-force, don’t try generating something with too long a prefix. On my computer, a collision with a prefix of length 4 will take less than a tenth of a second, a prefix of length 5 will take twice as long and a prefix of length 6 will take around 20 seconds.

./eschalot -vnp inf3 -t 4
Verbose, single result, digits ok, 4 threads, prefixes 4-4 characters long.
Thread #1 started.
Thread #2 started.
Thread #3 started.
Thread #4 started.
Running, collecting performance data...
Found a key for inf3 (4) - inf3melq3f3fpokb.onion

Once Eschalot finds a result, copy the RSA private key from the output and replace the content of the private key file used for your service, located in the service folder at /var/lib/tor/onion-ssh/private_key.

Then, restart Tor so that the new key is taken into account.

systemctl restart tor@default

You can check that the address of your service changed to the one found by Eschalot in the /var/lib/tor/onion-ssh/hostname file. This file is rewritten by Tor on every execution to reflect the Tor address of the service and this changes when the private key is changed.

sudo cat /var/lib/tor/onion-ssh/hostname

SSH Client configuration

By default, SSH won’t know how to connect to the Tor network or an .onion address. We will need to also install Tor on the client service and tell our SSH client that .onion hosts are somewhat special and need to be accessed through a socks proxy.

sudo apt update && sudo apt install tor ncat

We will alter our SSH configuration so that, when encountering an .onion address, SSH will use a proxy instead of trying to connect directly. We also add an easier way to remember the address of our server now available through a hidden service.

cat - >> .ssh/config <<EOF
Host *.onion
    proxyCommand ncat --proxy --proxy-type socks5 %h %p

Host tor-myserver
    hostname inf3melq3f3fpokb.onion
    proxyCommand ncat --proxy --proxy-type socks5 %h %p

Once the SSH client is configured and some minutes passed to allow both the client and the server to be connected to the Tor network, we can try to connect to our server, through the Tor network, bypassing every NAT or other block that would impede our direct connection:

ssh tor-myserver