Using SSH as a mini-VPN

Sometimes you need a way to get in to a system that is not accessible publicly, and for whatever reason, you can’t use a full VPN. This is a good time for the connectivity Swiss-Army-Knife, and your best friend, ssh and, if available, its companion autossh. And for you Windows people, I mean ssh, not PuTTY. PuTTY is not ssh. PuTTY is an ssh client. You don’t “putty” to a client, you ssh to a client. In fact, you can now install OpenSSH on your Windows machine and perform these operations without PuTTY.

ssh has many ways to pass traffic above and beyond a simple terminal session. The simplest and quickest is a simple port forward with the -R option. From the ssh man page:

     -R [bind_address:]port:host:hostport
     -R [bind_address:]port:local_socket
     -R remote_socket:host:hostport
     -R remote_socket:local_socket
     -R [bind_address:]port
             Specifies that connections to the given TCP port or Unix socket on the remote (server) host
             are to be forwarded to the local side.

             This works by allocating a socket to listen to either a TCP port or to a Unix socket on the
             remote side.  Whenever a connection is made to this port or Unix socket, the connection is
             forwarded over the secure channel, and a connection is made from the local machine to either
             an explicit destination specified by host port hostport, or local_socket, or, if no explicit
             destination was specified, ssh will act as a SOCKS 4/5 proxy and forward connections to the
             destinations requested by the remote SOCKS client.

             Port forwardings can also be specified in the configuration file.  Privileged ports can be
             forwarded only when logging in as root on the remote machine.  IPv6 addresses can be specified
             by enclosing the address in square brackets.

             By default, TCP listening sockets on the server will be bound to the loopback interface only.
             This may be overridden by specifying a bind_address.  An empty bind_address, or the address
             ‘*’, indicates that the remote socket should listen on all interfaces.  Specifying a remote
             bind_address will only succeed if the server's GatewayPorts option is enabled (see
             sshd_config(5)).

             If the port argument is ‘0’, the listen port will be dynamically allocated on the server and
             reported to the client at run time.  When used together with -O forward the allocated port
             will be printed to the standard output.

While this sounds like a lot, and may look intimidating, it’s really very simple. If you have two locations, I’ll call them “work” and “home” for the sake of clarity. You want to be able to connect to a machine at work from home, but there is no way for you to do so. But you are able to connect from work to home with no problems. So what you would do is verify you can establish a connection from work to home. I am not going to go in to authentication methods here. Once you are able to able to “ssh myusername@home.machine.com” and get a session on your home machine named “home.machine.com” as the user “myusername”, then you have done the hardest part. Now, you will want to create a connection that has your tunnel back.

The -R syntax is broken down into three parts. You are creating a “Local” port forward, which tunnels back through your ssh connection and to the specified host and port. This is better explained with an example. If you wanted an RDP session to a machine at your work location called “work-windows.work.com” (which does NOT have to be, but can be, the same as the machine you are running ssh from), you would run a command like:

ssh -R 33890:work-windows.work.com:3389 myusername@home.machine.com

Now let’s break that down into the different parts:

ssh creates the session between work-machine.work.com to home.machine.com. The -R creates a “remote” port on the client side, i.e. home.machine.com, listening on 33890. On home.machine.com, you would then use your RDP client to connect to localhost (127.0.0.1), port 33890 (in URL form: rdp://127.0.0.1:33890). This will send traffic back through the established ssh session, and send it to the machine work-windows.work.com on port 3389, thus establishing an RDP session from home.machine.com to work-windows.work.com, now enabling you to use a machine at work through the ssh session.

ssh also has the -L option which is sort of the opposite of -R. The syntax for -L works exactly the same as -R, but instead of creating a port on the other end, it creates a “local” port that listens on the machine were you run ssh, and will pass traffic from that port to the machine and port on the remote side you specify. E.g.

ssh -L 33890:home-windows.home.com:3389 myusername@home.machine.com

This listens locally on port 33890 on the machine where you are running the ssh command, and forwards the traffic to the machine home-windows.home.com on the remote side, on port 3389. This allows you to tell your local RDP client to connect to localhost:33890, and it will connect to the machine at home, home-windows.home.com on port 3389.

Now this does not solve the problem of you needing to first establish the connection, which is the chicken and egg problem; if you’re at home, you can’t log in to work to establish the connection from work to home so you can get back in to work.

This is where the autossh program fits in. You can set this to run automatically on a work machine of your choice. It is a part of most linux distros. There might be a Windows port, but I have never looked for it. An example of how I have used autossh in the past. From a linux machine, I add a line like the following to /etc/rc.d/rc.local to run at start up:

su sgarrett -c "/usr/bin/autossh -f -M 22222 -q -A -C -N -D 1080 -l sgarrett -p 22 -R 2222:127.0.0.1:22 -R 2211:192.168.36.151:22 -R 5959:192.168.45.150:5900 -R 32833:192.168.36.20:3283 -T -o PasswordAuthentication=no -S none host.home.com"
  • What this does is run autossh as the user sgarrett (remember rc.local runs as root during bootup). (you could also integrate this with systemd)
  • -f causes autossh to drop from a foreground process to a background process. This is very important as if you do not have this option, your system will never complete booting until the autossh command ends, which it never will, by design.
  • The monitoring port is set to 22222/22223 with -M. autossh will send periodic test packets out on 22222 and expect a response on 22223. If there is no response, it detects that the ssh session is no longer passing data, and it will re-establish a new connection.
  • The remaining options are passed to ssh.
  • -q tells it to suppress most messages. Since this is going to be a background process, there is no need to be verbose.
  • -A forwards ssh agent information (out of scope for this tutorial; if you don’t use the agent, it is not necessary to have this, but there is no harm if it is here).
  • -C enables compression; this usually improves performance, but is optional.
  • -N tells it not to run a remote command (i.e. a shell). Since all we care about here is the forwarded ports, this is the ideal option.
  • -D 1080 sets up a SOCKS5 proxy. This allows you to point your browser to “localhost:1080” as a socks port, and all browser web traffic will behave as if the browser were run on the machine where autossh is run. Useful for browsing intranet web sites.
  • -l sgarrett tells it to log in as the user sgarrett
  • -p 22 tells it the port to connect to at your home to establish ssh. If your ISP blocks port 22, you can have your ssh listen on any port you wish.
  • The chain of -R clauses work as above, and forward various ports to various machines. This is left up to you how many ports you need and where you need them to go.
  • -T disables pseudo-terminal allocation. This essentially means it doesn’t show up as a logged in user on the target machine.
  • -o PasswordAuthentication=no tells it not to attempt password authentication. Since this is an automated connection, you do not want it to stop and ask for a password. You would be using key/agent based authentication
  • -S none tells it not to use existing control masters, which will cause autossh to fail.
  • Finally, the name of the host you connect to at your home. If you do not have a static IP address, you can use a dynamic hostname service to allow autossh to find your home.

Now, when the machine boots up at work, it will automatically establish a connection to your home in the background, and re-establish the connection should it drop, fail or otherwise be terminated.

Leave a Comment