Creating a Reverse SSH Tunnel for Reliable Remote Access to Your Raspberry Pi Over 4G

Connecting your Raspberry Pi to 4G is great – until you realize you can’t directly SSH into it because of CGNAT. But don’t worry, I’ve found a simple solution that lets you access your device remotely and log stats to the internet with ease.

I purchased a 4G dongle for my Raspberry Pi so that I could log stats to the internet from my solar shed. I also wanted to be able to connect via SSH in order to make changes and configure things.

My Raspberry Pi Zero with 4G dongle

I use the ZTE MF833U1 https://amzn.to/3jX5jiQ with a SIM card from 1p mobile which I found to be the cheapest for my low data usage https://www.1pmobile.com/

Solution

The solution that found to be the most reliable was to create a reverse SSH tunnel from the Raspberry Pi to my home network. In my case I have another Raspberry Pi in my house which I created the reverse tunnel into. I can then SSH into that Raspberry Pi and then through the SSH tunnel to the Raspberry Pi in my shed over 4G whenever I like.

If you have a server running somewhere on the internet you could use that to establish your tunnel into instead and skip the dynamic DNS and port forwarding steps.

Dynamic DNS

Rather than rely on a particular IP address for my home network I signed up to https://www.noip.com a free dynamic DNS service. I then configured my Netgear router to update my IP address with No-IP.

Port forwarding

The next step was to set up port forwarding on my router from port 22 for SSH to my raspberry Pi inside of my house. I could then connect via SSH to the Raspberry Pi from outside of my house from any device. It is important to set a secure password at this point.

SSH keys

For the SSH tunnel to work without a prompt for a password I set up SSH keys at this point creating a public and private key on my shed’s Raspberry Pi and then adding the public key to my home Raspberry Pi’s authorized keys file. I could test this was working by connecting via SSH from that Pi to my home and checking that there was no prompt for a password.

ssh-copy-id -i ~/.ssh/id_rsa pi@your-no-ip-hostname

Systemd service

At this point I created a service on my shed Raspberry Pi /etc/systemd/system/sshtunnel.service

[Unit]
Description=Service to maintain an ssh reverse tunnel
Wants=network-online.target
After=network-online.target
StartLimitIntervalSec=0

[Service]
Type=simple
ExecStart=/usr/bin/ssh -qNn \
  -o ServerAliveInterval=30 \
  -o ServerAliveCountMax=3 \
  -o ExitOnForwardFailure=yes \
  -o StrictHostKeyChecking=no \
  -o UserKnownHostsFile=/dev/null \
  -i /etc/sshtunnel/id_rsa \
  -R 9001:localhost:22 \
  -v \
  pi@your-no-ip-hostname -p 22
Restart=always
RestartSec=5

[Install]
WantedBy=multi-user.target

I installed this service to run on startup with the command

sudo journalctl -u sshtunnel

The connection now will always re-establish itself if it drops out.

All done!

From this point connecting to the shed Raspberry Pi over SSH is as simple as connecting to my home Raspberry Pi then running

ssh pi@127.0.0.1 -p 9001

And that’s it!

Occasionally the port will get stuck and you will have to kill it to re-establish the connection. You can do this by running on the server being connected to by the tunnel (in my case the Raspberry Pi in my home) the following command to find the process running on port 9001 and then killing that process with the kill command.

sudo lsof -i -P -n | grep LISTEN

Building a cable to connect my EPever charge controller

I recently purchased an EPever Tracer AN solar charge controller. I chose this charge controller because it is one of the only controllers on the market which have a built in Lithium battery cold temperature protection feature. When I purchased this controller I thought it would be easy to get it working with my Raspberry Pi using the official EPever USB to RS-485 cable. It did work fine on Windows but on Linux after several days of trying I gave up trying to get it working on Linux. The chip used in this cable does not have up to date Linux drivers and I could not get drivers that I found on Github working reliably.

EPever Tracer AN – https://amzn.to/3g2qbEi

After some research, I decided to have a go at making my own cable instead which would use a chip that already has reliable Linux drivers.

Parts

DTECH USB to RS485 or RS422 – https://amzn.to/2XrZ3bg
COVVY RJ45 Female To 8 Pin Screw Term Block Terminal Connector – https://amzn.to/2XtlPQ3
Generic Cat 5 Cable – https://amzn.to/3iO1c9v
Jumper wire cables – https://amzn.to/3m4sJWg

Building the cable

The first step is to connect the serial port breakout to the USB connector and wire the serial using the jumper cables. They should be connected to TXD -, TXD+ and GND. RXD is not used for RS-485 communication.

The next step is to connect the jumper cables and the Cat 5 cable to the RJ45 breakout terminal block, connecting to terminals 3, 5 and 7.

TXD- goes to terminal 5
TXD+ goes to terminal 3
GND goes to terminal 7

Your cable is now ready to connect to your computer and the charge controller.

On Linux the cable will show up as something like /dev/ttyUSB0. You shouldn’t need to install any additional drivers. I recommend using my Python library to communicate with the charge controller https://github.com/rosswarren/epevermodbus.