*****     ***    ******     ***    **   **  ***  **  **   **  **   **  **   **
**        ** **   **   **   ** **   **   **  **** **   ** **    ** **    ** ** 
 *****   **   **  ******   **   **  *******  ** ****    ***      ***      ***  
     **  *******  **  **   *******  **   **  **  ***    ***     ** **    ** ** 
 *****   **   **  **   **  **   **  **   **  **   **    ***    **   **  **   **

Remote access - No Port Forwarding

So, I found myself in a situation where it was… “inconvenient” to mess with the firewall situation, but I still had a system behind the firewall that I needed to be able to access remotely.

I chose to solve this with reverse tunnels.

Note: This solution assumes having a remote server, VPS, etc on the internet with which to use as part of the reverse tunnel.

What is a Reverse Tunnel

A reverse tunnel is essentially when a device opens a connection out to the internet with the purpose of accepting incoming traffic through the connection. Since this is an outbound connection by the firewall’s standards, it lets it through. It typically only blocks inbound connections.

So that’s the theory that solves our issue.

Remote Access

So, for remote access, there’s a handy SSH command that can get us figured out:

For example: ssh -R 2222:localhost:22 [email protected]

Assuming [email protected] is the user and hostname for a valid server with a public IP address, it’ll log into that server and do the reverse tunnel with the -R option, exposing port 2222 on the server, and connect that to localhost’s port 22.

The result is, if we ssh to the remote server using port 2222 that we exposed with the -R option, it’ll shuffle our connection through the reverse tunnel and connect us to the machine we opened the tunnel from.

But, what about persistence?

If this is our remote access strategy, we want to make sure it’s reliable and doesn’t go offline after a network hiccup or whatnot. So, I went ahead and made it a systemd service, though I’m sure there’s other ways to do it too.

I’ve added to the above example to make it more friendly to being wrapped in a service:

/usr/bin/ssh -NT -o ServerAliveInterval=60 -o ExitOnForwardFailure=yes -R 2222:localhost:22 [email protected] -i /home/sarah/.ssh/id_rsa

Let’s break down the options here:

  • -N - “Do not execute a local command” - This skips any autorun commands on the remote server
  • -T - “Disable pseudo-terminal allocation” - This disables launching a shell on the remote server, just enough of a connection to do the reverse tunnel
  • -o ServerAliveInterval=60 - This creates a sort of health check for the connection, instead of letting it stall unbeknownst to the system.
  • -o ExitOnForwardFailure=yes - This makes the session exit if the ports cannot be allocated, instead of silently failing the port allocation and continuing with the connection.
  • -R 2222:localhost:22 - Already explained above
  • [email protected] - the remote server we’ll be hosting our remote access port on.
  • -i /home/sarah/.ssh/id_rsa - This is an SSH key to allow passwordless login, as the systemd service won’t know to ask for a password.

Now, let’s wrap it in a systemd service

There’s a syntax to systemd services, and here’s my /etc/systemd/system/ssh-tunnel.service file below:

[Unit]
Description=Persistant reverse SSH tunnel for remote access without port forwarding. 
After=network.target 

[Service]
Restart=on-failure
RestartSec=5
ExecStart=/usr/bin/ssh -NT -o ServerAliveInterval=60 -o ExitOnForwardFailure=yes -R 2222:localhost:22 [email protected] -i /home/sarah/.ssh/id_rsa

The above simply tells systemd to run this after the system’s online, and that when it fails, it should restart the service.

Now, to initialize the systemd service, we need to run a couple commands:

  1. sudo systemctl daemon-reload - This’ll make systemd rescan the available service configurations
  2. sudo systemctl enable ssh-tunnel.service - This will enable our config file. This will spit out some messages about how it was installed, but the command still will go through just fine.
  3. sudo systemctl start ssh-tunnel.service should bring it up.

You can check the status of the tunnel with sudo systemctl status ssh-tunnel.service for any errors.

With that, we have our persistent remote access configured without forwarding a port on our local network.