tl;dr

  • Have an SSH server and client on-site
  • Have an SSH server at your remote location
  • Open a remote port forwarding to your remote location
  • Use sshuttle to start the VPN

Situation

With the beginning of the Corona-crisis, I had to work from home most of the time to separate the people in the office from each other.

Since we did not have any real solutions for remote work for our in-house staff (we actually have VPN accounts for our external staff) the official solution was to work via TeamViewer. And while TeamViewer might be a great product, which helped developers and supporters all around the world, it has some serious drawbacks in comparison to a VPN connection:

  • Since every keystroke needs to be transmitted and handled by a remote system, usage can be quite jerky
  • Multi-monitor support is given, but feels really clumsy in contrast to local usage of multiple monitors
  • You need enough licenses for all the parallel connections your company uses (although this is not checked at the time of writing, at least during the Corona-crisis)
  • If anything between you and your company workstation fails, you are not able to continue (TeamViewer has problems at least once, preventing successful login) So another more viable solution was needed.

Problem

As I said, I had to work remotely. Our company firewall is not allowing any incoming connections and is blocking all outgoing traffic except on some ports. Luckily, since we are using ssh connections to some remote servers, port 22 was one of the open ports for outgoing traffic, so that it was possible to do remote port forwarding to another machine at home. But since we use some nice site-to-site VPN connections in our company network to access more remote machines, forwarding every needed port to every remote machine would have been tedious and error prone. Therefore I was hoping for a more comfortable solution.

Solution

Then I found sshuttle which changed the game completely on this topic. As they state on their GitHub page it does the following:

Transparent proxy server that works as a poor man’s VPN. Forwards over ssh. Doesn’t require admin. Works with Linux and MacOS. Supports DNS tunneling.

Sounds good so far. Now, since our firewall is not allowing incoming connections, I had to combine these two ideas. I set up a VM on my company workstation with an SSH server and client. The client connected to the SSH server in my home network, opening a remote port to the SSH server in the VM. Then I used sshuttle to connect to the opened remote port at my local SSH server, which effectively was a connection to the VM in the company network and - boom - I was able to connect to all the machines reachable by my company workstation as if I would have been sitting there.

Implementation

1. On-site SSH server

I used Arch as a base for this, but use whatever you like. Needed software is:

  • openssh
  • autossh
  • python

autossh is a small tool, which opens an SSH connection and monitors it. If it goes down for whatever reason, it automatically reconnects it. This together with the right SSH parameters should get us an always-on SSH tunnel.

Python is needed for the forwarding features of sshuttle. Since there is an open bug in python-3.8 (or sshuttle) we need an older version of python. As we used the great Arch, we are stuck on the newest one and need to install an older one like this:

$ sudo pacman -S pyenv
$ pyenv install 3.7.7 # use whatever is the newest 3.7 version

Then we write a small script to remember the correct options for autossh and ssh.

#!/bin/bash
autossh -M 0 -o "ServerAliveInterval 10" -o "ServerAliveCountMax 3" -o "ExitOnForwardFailure yes" -l USER -N -p 22 -R 10022:localhost:22 YOUR.LOCAL.SSH.SERVER

For ease of use and better security, you should also generate an SSH key and use that to login to your local SSH server.

$ ssh-keygen -t rsa -b 4096

2. Local SSH server

For security reasons, you should add a unprivileged user with no login shell (/bin/false) as the USER which connects from the On-site SSH server to the local SSH server. Additionally, your SSH server configuration (/etc/ssh/sshd_config) should contain the following things:

PermitRootLogin no
MaxAuthTries 3
GatewayPorts yes
ClientAliveInterval 8
ClientAliveCountMax 2

The GatewayPorts setting allows the machines in your local network to use the opened port to your company. This way you can start sshuttle only on your workstation without affecting the other users in your network. The ClientAlive settings will check the ssh connection to the client every 8 seconds and close it after 2 failures. This ensures that the forwarded port is free, when autossh reconnects from the client side.

3. Local machine

Last but not least, we add little script for our convenience to open the VPN on your local machine. Since we need to use a custom python version on the on-site machine (that we installed earlier), we need to pass that as a parameter to sshuttle. Then we need to give the connection data to our local SSH server (-r) and add all subnets, that should be routed via sshuttle.

#!/bin/bash
sshuttle -D --python=/PATH/TO/.pyenv/versions/3.7.7/bin/python -r USER2@LOCALSSH:10022 172.16.20.0/24 192.1.2.0/24 10.236.189.0/24

Thats it. Fire up your script on the on-site machine, start your local script, enter your root password (since sshuttle needs to add iptables rules on your local machine) and all machines in the routed networks should be reachable from your local machine.