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:
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
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 email@example.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 firstname.lastname@example.org
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
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 email@example.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)
-fcauses 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.
-qtells it to suppress most messages. Since this is going to be a background process, there is no need to be verbose.
-Aforwards 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).
-Cenables 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 sgarretttells it to log in as the user sgarrett
-p 22tells 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
-Rclauses 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=notells 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 nonetells 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.