How to SSH to your Raspberry Pi through a TCP Tunnel

I recently bought a Raspberry Pi. My first thought when I receive it is how to connect to it from outside my home network. The difficulty is I live in an apartment building so my router does not connect to ISP but to another switch of the building. I have no way to ask the building to forward a specific port to my RPi from the outside world.

The solution for my problem is a TCP tunnel. There are plenty of services out there like ngrok, PiTunnel, etc. that can perform this feature. But being a tinker, I pushed myself to search for a more DIY method. Along the way I realize I can customize a lot more with a simple OSS tool, and less than half the price I would pay for existing services.

Introduce Chisel

https://github.com/jpillora/chisel

Chisel is a fast TCP/UDP tunnel, transported over HTTP, secured via SSH. Single executable including both client and server. Written in Go (golang).

Overview

Tunnel flow overview

We will use a VM from either AWS or GCP as our reverse proxy server. Also they serve as a static IP endpoint for our setup.

The RPi will automatically connect to this server every time it starts up and should have a retry mechanism.

Being a simple personal reverse proxy, I chose one of the cheapest solutions on AWS `t4g.nano` which cost about $3/month. You should get the same price on GCP with `f1-micro`. They even offer a free tier machine if you are in the US. But after testing it out, the latency is too much (300ms) since I connect to it from Vietnam.

For this tutorial, I will use the AWS `t4g.nano` but the setup should be the same if you choose GCP.

Setup the AWS EC2 Instance

Go to AWS EC2 console and click ‘Launch Instances’

then pick the option for Ubuntu Server 20.04 the `x86` or `arm` architect both work with this tutorial.

AWS launch instance

Pick `t4g.nano` for the spec that we would like to run.

AWS t4g.nano

Continue with your settings. I keep the defaults.

AWS Config Security Group

For Configure Security Group. There are 2 important ports:

  • Port 22 for SSH into our server
  • Port 12345 for our RPi to connect the client into this server. Prefer this port below as <PUBLIC_PORT>

The purpose of other ports are example:

  • Port 2222 for make SSH connection from your RPi
  • Port 3333 for Grafana from your RPi
AWS key pair

At this step, you should create a key pair (a .pem file). It will be used to access AWS VM. Download this file into a safe place. I use the `~/.ssh` folder on my Macbook.

VM Public IP

Go to the EC2 console and look for your Instance information. Take note of this PUBLIC_IP.

Setup the Reverse Tunnel Server

First SSH into your AWS Instance using your downloaded `.pem` file.

ssh -i ~/.ssh/t4gnano-ap-southeast-1.pem ubuntu@<PUBLIC_IP>

This will use your key to connect to your server. Then you can start to set up the server by installing the go lang environment. And install the chisel package and binary.

sudo apt-get install golang-go
export PATH=$PATH:~/go/bin

Then you need to prepare a <USER_NAME>:<PASSWORD> pair to protect this tunnel server. Replace them in the command below and prefer it in the client.

go get github.com/jpillora/chisel
chisel server --auth <USER_NAME>:<PASSWORD> --port 12398 --reverse &

That’s it! You have a reverse TCP proxy running on AWS. To confirm the server process is running, run the following command:

ps aux |grep chisel

Setup the Raspberry Pi client connection

You can either connect to your RPi by ssh or using mouse and keyboard.

Then also install the Golang environment and chisel.

sudo apt-get install golang-go
export PATH=$PATH:~/go/bin
go get github.com/jpillora/chisel

Then based on your services you can connect to the Reverse server port in the bellow format

chisel client --auth <USER_NAME>:<PASSWORD> \
<PUBLIC_IP>:<PUBLIC_PORT> R:<SERVER_PORT>:localhost:<LOCAL_PORT>

The <SERVER_PORT> is the port you will connect to your server from anywhere. The <LOCAL_PORT> is the port of the service that the RPi exposes.

Example for SSH:

chisel client --auth <USER_NAME>:<PASSWORD> \
<PUBLIC_IP>:<PUBLIC_PORT> R:2222:localhost:22 &

Again after running the above you can confirm the process running by

ps aux |grep chisel

Congratulations!!! You now can connect to your RPi from anywhere without forwarding a router port to your RPi.

Now use a laptop to make connection by enter

ssh username@<PUBLIC_IP> -p 2222

Username normally is ubuntu if you use the ubuntu server image.

Now we have the power to tunnel any port from our RPi to the world. I have my Grafana on port `R:3333:localhost:3000` , `R:8888:localhost:8080` for example.

Automation on start

Of course, manually start the chisel client script every time you boot up your RPi is a nuisance. It’s better to have it in a auto service that start when you turn on your RPi.

Have this script in your home bin folder:

/home/<username>/bin/start-chisel-client.sh
/home/<username>/bin/start-chisel-client.sh

Change the username and <PUBLIC_IP>, <PUBLIC_PORT>, <USER_NAME>, <PASSWORD> in the script before continue.

[Unit]
Description = Chisel tunnel service
Wants = network-online.target
After = network-online.target
[Service]
User = ubuntu
Type = simple
WorkingDirectory= /home/ubuntu
ExecStart = /bin/bash -c '/home/ubuntu/bin/start-chisel-client'
KillSignal=SIGINT
RestartKillSignal=SIGINT
TimeoutStopSec=2
LimitNOFILE=32768
Restart=always
RestartSec=5
[Install]
WantedBy = multi-user.target

Again change ubuntu to your username.

Start the service with

sudo systemctl start chisel-tunnel.service

With this service, every time you boot up your RPi. It will automatically start the chisel client and connect to the reverse tunnel server. If it’s fail, it will retry every 30 seconds until success.

Conclusion

With just under $3/month, I was able to connect to my RPi from anywhere without the worry of port forwarding the at router level. It was a great time setup the whole thing.

Programming with passion to understand how everythings fit together.

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store