..

Reverse Tunneling

I previously kept my backup NAS in the same location as my primary NAS, connected to the same power grid and without a UPS. I have now been offered a new location for the backup NAS and plan to move it there to introduce geographic redundancy.

The idea is to have the backup NAS operate in pull mode. It will connect as a VPN client and replicate datasets incrementally using ZFS. In addition, I plan to deploy an old Raspberry Pi at the new location. The Pi will run 24/7 and wake the backup NAS once a week so it can start the replication process.

I do not want to set up a site-to-site VPN tunnel, as I prefer not to modify the router at the new location. Instead, I plan to keep a persistent reverse tunnel from the Raspberry Pi to a jump host in my homelab for maintenance purposes. This setup will allow me to connect to the Pi via SSH, wake the backup NAS, and access the backup NAS web interface. The Raspberry Pi hardware should be more than sufficient for these lightweight maintenance tasks, while the VPN handling the heavy data transfer will run on the much more powerful backup NAS hardware.

There are certainly other ways to achieve this. The reasons I chose this architecture are:

  • It relies on tools I already know and have configured
  • The tools are available on almost any Linux system
  • I have long wanted to experiment with reverse tunnels, both out of curiosity and to better understand malicious intrusions and data exfiltration techniques

If you can think of a better approach, feel free to let me know. SSH tunneling is certainly not the most performant solution, especially on a Raspberry Pi.

The following diagram visualizes the a simple sample setup and introduces the names that I will reuse later.

simple reverse ssh tunnel

Preparing the Pi and the Reverse Jump

Requirements:

  • The system should be highly isolated, as it needs to be exposed to the internet.
  • The SSH server running on it must be hardened.
  • The local firewall must be enabled.

To meet these requirements, I placed the system in my DMZ VLAN and added a very restrictive firewall rule set. By default, it is not allowed to connect to anything: not hosts in the same subnet, not other internal subnets, and not the internet. When updates are required, internet access is temporarily enabled.

In addition, I hardened the SSH server on both the Raspberry Pi and the Reverse Jump as follows:

AllowUsers user-reverse-jump user-pi
PermitRootLogin no
PasswordAuthentication no
VersionAddendum none

For testing, I initiated the reverse tunnel on the Raspberry Pi using: ssh -p 231 -R 2222:localhost:22 user-reverse-jump@reverse-jump. I then connected to it from the Reverse Jump host with: ssh -p 2222 user-pi@localhost.

Tunnel http traffic through

To access the web interface of my backup NAS, I also need to proxy web requests from the Reverse Jump through the tunnel, effectively allowing me to browse as if I were on the Raspberry Pi. To achieve this, I start a SOCKS1 v5 proxy on the Pi and expose it through the tunnel.

socks v5 proxy

What I did to make this work:

  • Started the SOCKS proxy on the Pi: ssh -N -D 1080 localhost.
  • Exposed the proxy through the tunnel by running the following on the Pi: ssh -p 231 -R 1080:localhost:1080 toob@10.0.0.220.
  • On the Reverse Jump, I run a lightweight desktop environment with Firefox configured to use the SOCKS proxy.

Finalizing the Setup

Raspberry Pi

On the Raspberry Pi side, I needed to make everything persistent and ensure it starts automatically on boot. It also needed to reconnect automatically in case of network outages. While looking for a solution, I came across autossh, which describes itself in its man page as follows:

autossh is a program to start a copy of ssh and monitor it, restarting it as necessary should it die or stop passing traffic.

autossh -M 0 -N -p 231 \
-R 2222:localhost:22 \
-R 1080:localhost:1080 \
user-reverse-jump@reverse-jump

Using systemd units, I configured everything to start at boot. This makes the setup easy to restart, redeploy, or move to a new location, and it ensures that I always regain a reverse shell from that network as long as outbound internet access is permitted.

/etc/systemd/system/autossh-reverse.service

[Unit]
Description=AutoSSH reverse SSH + reverse SOCKS
After=network-online.target ssh-socks.service
Wants=network-online.target ssh-socks.service

[Service]
User=YOUR_LOCAL_USERNAME
Environment="AUTOSSH_GATETIME=0"
ExecStart=/usr/bin/autossh \
  -M 0 -N \
  -o ServerAliveInterval=30 \
  -o ServerAliveCountMax=3 \
  -o ExitOnForwardFailure=yes \
  -p 231 \
  -R 2222:localhost:22 \
  -R 1080:localhost:1080 \
  toob@10.0.9.102
Restart=always
RestartSec=10

[Install]
WantedBy=multi-user.target

/etc/systemd/system/ssh-socks.service

[Unit]
Description=Local SSH SOCKS proxy
After=network.target

[Service]
User=YOUR_LOCAL_USERNAME
ExecStart=/usr/bin/ssh -N -D 127.0.0.1:1080 localhost
Restart=always
RestartSec=5

[Install]
WantedBy=multi-user.target

Using the Reverse Jump as a Proxy

To allow other machines on the same network as the Reverse Jump to connect to the tunnel (SSH and SOCKS), I needed to make a few small adjustments.

On the Reverse Jump, I updated the SSH daemon configuration as follows:

AllowTcpForwarding yes
GatewayPorts yes

final setup


  1. A SOCKS server is a proxy that forwards network traffic at the transport layer, allowing applications to route their connections through another host without being aware of the underlying network topology. Its main benefits are flexibility and protocol independence, making it useful for securely accessing services across restricted or segmented networks.