Skip to content

Tutorial: Expose a private SSH server over a TCP tunnel

In this tutorial we will use inlets-pro to access your computer behind NAT or a firewall. We'll do this by tunnelling SSH over inlets-pro, and clients will connect to your exit-server.

Scenario: You want to allow SSH access to a computer that doesn't have a public IP, is inside a private network or behind a firewall. A common scenario is connecting to a Raspberry Pi on a home network or a home-lab.

You can subscribe to inlets for personal or commercial use via Gumroad

Setup your tunnel server with inletsctl

For this tutorial you will need to have an account and API key with one of the supported providers, or you can create an exit-server manually and install inlets Pro there yourself.

For this tutorial, the DigitalOcean provider will be used. You can get free credits on DigitalOcean with this link.

Create an API key in the DigitalOcean dashboard with Read and Write permissions, and download it to a file called do-access-token in your home directory.

You need to know the IP of the machine you to connect to on your local network, for instance 192.168.0.35 or 127.0.0.1 if you are running inlets Pro on the same host as SSH.

You can use the inletsctl utility to provision exit-servers with inlets Pro preinstalled, it can also download the inlets-pro CLI.

curl -sLSf https://inletsctl.inlets.dev | sh
sudo mv inletsctl /usr/local/bin/
sudo inletsctl download

If you already have inletsctl installed, then make sure you update it with inletsctl update.

Create an tunnel server

A) Automate your tunnel server

The inletsctl tool can create a tunnel server for you in the region and cloud of your choice.

inletsctl create \
  --provider digitalocean \
  --access-token-file ~/do-access-token \
  --region lon1

Run inletsctl create --help to see all the options.

After the machine has been created, inletsctl will output a sample command for the inlets-pro client command:

inlets-pro tcp client --url "wss://206.189.114.179:8123/connect" \
    --token "4NXIRZeqsiYdbZPuFeVYLLlYTpzY7ilqSdqhA0HjDld1QjG8wgfKk04JwX4i6c6F"

Don't run this command, but note down the --url and --token parameters for later

B) Manual setup of your tunnel server

Use B) if you want to provision your virtual machine manually, or if you already have a host from another provider.

Log in to your remote tunnel server with ssh and obtain the binary using inletsctl:

curl -sLSf https://inletsctl.inlets.dev | sh
sudo mv inletsctl /usr/local/bin/
sudo inletsctl download

Find your public IP:

export IP=$(curl -s ifconfig.co)

Confirm the IP with echo $IP and save it, you need it for the client

Get an auth token and save it for later to use with the client

export TOKEN="$(head -c 16 /dev/urandom |shasum|cut -d'-' -f1)"

echo $TOKEN

Start the server:

inlets-pro \
  tcp \
  server \
  --auto-tls \
  --auto-tls-san $IP \
  --token $TOKEN

If running the inlets client on the same host as SSH, you can simply set PROXY_TO_HERE to localhost. Or if you are running SSH on a different computer to the inlets client, then you can specify a DNS entry or an IP address like 192.168.0.15.

If using this manual approach to install inlets Pro, you should create a systemd unit file.

The easiest option is to run the server with the --generate=systemd flag, which will generate a systemd unit file to stdout. You can then copy the output to /etc/systemd/system/inlets-pro.service and enable it with systemctl enable inlets-pro.

Configure the private SSH server's listening port

It's very likely (almost certain) that your exit server will already be listening for traffic on the standard ssh port 22. Therefore you will need to configure your internal server to use an additional TCP port such as 2222.

Once configured, you'll still be able to connect to the internal server on port 22, but to connect via the tunnel, you'll use port 2222

Add the following to /etc/ssh/sshd_config:

Port 22
Port 2222

For (optional) additional security, you could also disable password authentication, but make sure that you have inserted your SSH key to the internal server with ssh-copy-id user@ip before reloading the SSH service.

PasswordAuthentication no

Now need to reload the service so these changes take effect

sudo systemctl daemon-reload
sudo systemctl restart sshd

Check that you can still connect on the internal IP on port 22, and the new port 2222.

Use the -p flag to specify the SSH port:

export IP="192.168.0.35"

ssh -p 22 $IP "uptime"
ssh -p 2222 $IP "uptime"

Start the inlets Pro client

First download the inlets-pro client onto the private SSH server:

sudo inletsctl download

Use the command from earlier to start the client on the server:

export IP="206.189.114.179"
export TCP_PORTS="2222"
export LICENSE_FILE="$HOME/LICENSE.txt"
export UPSTREAM="localhost"

inlets-pro tcp client --url "wss://$IP:8123/connect" \
  --token "4NXIRZeqsiYdbZPuFeVYLLlYTpzY7ilqSdqhA0HjDld1QjG8wgfKk04JwX4i6c6F" \
  --license-file "$LICENSE_FILE" \
  --upstream "$UPSTREAM" \
  --ports $TCP_PORTS

The localhost value will be used for --upstream because the tunnel client is running on the same machine as the SSH service. However, you could run the client on another machine within the network, and then change the flag to point to the private SSH server's IP.

Try it out

Verify the installation by trying to SSH to the public IP, using port 2222.

ssh -p 2222 user@206.189.114.179

You should now have access to your server via SSH over the internet with the IP of the exit server.

You can also use other compatible tools like sftp, scp and rsync, just make sure that you set the appropriate port flag. The port flag for sftp is -P rather than -p.

Wrapping up

The principles in this tutorial can be adapted for other protocols that run over TCP such as MongoDB or PostgreSQL, just adapt the port number as required.