Introduction
An ad-hoc forward of a specific port or two from the command line can be very handy, for example forwarding a work-only accessible wiki to your home machine, or from a local coffee shop so that you can read it. But there is a better way to architect a permanent solution. Many people don’t realize that it is completely possible to access at home every single resource that you have at work by simply creating SSH configuration files that have comprehensive tunneling instructions. Contrary to popular belief, this does not require VPN software. It requires only an open SSH port, which can be listening on port 22 or 4010. It doesn’t matter. While this is bad news for commercial vendors of VPN software, it is good news for you.
The world has changed, and now people work from home, from the road, and from the coffee shop. In addition, IT workers often use virtual machines running on their laptops to simulate production environments. By creating comprehensive SSH tunneling configurations, it is possible to make a remote machine, along with virtual machines, completely integrated into a production environment.
About SSH config files
Although reading a man page for something as big as
SSH can be daunting, I would suggest doing a cursory
skim through some of the options. It is always a good
idea to do at least some skimming before engaging in
heavy duty use of a command line tool in ways you have
never used it before. There are two configuration files
to be aware of for this article. The system-wide
configuration file lives in /etc/ssh_config
, and the user ssh file lives in ~/.ssh/config.
Since this is a more advanced article on SSH, it is
important to know the distinction between the
system-wide configuration file and the user
configuration file. If you run a system cron job and
would like SSH forwarding to be involved, it is
important to note that the /etc/ssh_config
file needs to be edited. If you need to enable
forwarding for a user shell, which is most typical, then
you should edit the user configuration file. The SSH
config files also have a man page (man ssh_config), and
it would be helpful to view that man page as well.
Creating A Basic SSH Config File
Getting basic requirements together
The basics of a creating a customized config file are easy. The general idea is to create a configuration that forwards local ports that bind to ports on a remote machine. While you are setting up your SSH configuration file, it would be a good to keep the iana.org list of ports handy. This will help you decide what port IMAP uses, for example.
A work network behind a firewall may consist of the following resources that are not accessible from outside of the local area network.
SMTP Server: 192.168.0.100 Port: 25 DNS Name: smtp.pretendco.com Corporate Wiki: 192.168.0.110 Port: 8080 DNS Name: wiki.pretendco.com IMAP Mail Server: 192.168.0.120 Port: 143 DNS Name: imap.pretendco.com Subversion Server: 192.168.0.140 Port: 22 DNS Name: svn.pretendco.com NFS Server: 192.168.0.150 Port: 2049 DNS Name: nfs.pretendco.com SMB/CIFS Server: 192.168.0.160 Port: 3020 DNS Name: smb.pretendco.com SSH Server: 192.168.0.170 Port: 22 DNS Name. dev.pretendco.com VNC Server/Dev Machine: 192.168.0.180 Port: 5900 DNS Name: vnc.pretendco.com
All that is needed to gain access to these resources
from elsewhere is to have a publicly accessible SSH
server with one open port inside of the LAN. Let’s
suppose this server is called ssh.pretendco.com
and that it has sshd (the SSH daemon) listening on port
5001. We can now build an SSH config file based on this
information.
Understanding options
Now that we have a list of internal IP addresses, DNS names, and the ports with the services we would like to access, we can create a configuration file with a set of cascading configuration options. SSH has many helpful options that I would recommend perusing in the ssh_config man page. We are going to focus on the following options:
The most basic of configuration parameters. This gives you the ability to designate sections of the config file by hostname and pattern. For example:
Host * (this means a section applies to every host) Host ssh.pretendco.com (this means it only applies to ssh.pretendco.com)
Hostname:
You can nest multiple host configurations to create
custom setups and alias hostnames so that they
appear locally like they do at work. For example, a
localhost:8080 tunnel could be turned into an alias
like:
Host wiki.pretendco.com Hostname localhost Port 2200
This is handy because it allows you to completely simulate working inside of a local area network. And if you have scripts or configuration files that are hard coded to work with names inside of your LAN, then you will very much enjoy using this configuration.
ServerAliveInterval: This option can be configured to send a message every N seconds to a remote server so that a connection will not die. By default it is set to 0 seconds.
Host and Hostname are really the only options you will need for most configurations, but knowing about other options like ServerAliveInterval and ServerAliveCountMax can be helpful too. Now that we have the basic requirements and understand how to use the options, let’s write a configuration file that will create our VPN to work.
Writing ~/.ssh/config
Here is a configuration file based on the things we just talked about. You should be able to use this as a template by plugging in the names and ports of the devices you would like to connect to.
If you have a config file in ~/.ssh/config,
make a backup and move the original file. You can do
something like this:
cd ~/.ssh/ mv config backup_config_file
Now you can cut and paste this into a new file you
call config.
###SSH Port Forwarding Configuration###
####Global Configuration Options###
#Host * will apply to all hosts
Host *
ServerAliveCountMax 4 #Note default is 3
ServerAliveInterval 15 #Note default is 0
####Port Forwarding Directives###
#Network Reference, Cheat Sheet:
#Refer to http://www.iana.org/assignments/port-numbers for full list of port numbers
#SMTP Server: 192.168.0.100 Port: 25 DNS Name: smtp.pretendco.com
#Corporate Wiki: 192.168.0.110 Port: 8080 DNS Name: wiki.pretendco.com
#IMAP Mail Server: 192.168.0.120 Port: 143 DNS Name: imap.pretendco.com
#Subversion Server: 192.168.0.140 Port: 22 DNS Name: svn.pretendco.com
#NFS Server: 192.168.0.150 Port: 2049 DNS Name: nfs.pretendco.com
#SMB/CIFS Server: 192.168.0.160 Port: 3020 DNS Name: smb.pretendco.com
#SSH Server: 192.168.0.170 Port: 22 DNS Name. dev.pretendco.com
#VNC Server/Dev Machine: 192.168.0.180 Port: 5900 DNS Name: vnc.pretendco.com
#(Note we just made up the name workTunnel.)
#The workTunnel alias holds both the nested ssh server configuration,
#and the actual port forwarding directives.
#Note you can forward to either an IP Address or a hostname.
Host workTunnel
#Work SSH Server To Initiate Tunneling From
Host ssh.pretendco.com
Port 5001
# SMTP Server
LocalForward localhost:2525 smtp.pretendco.com:25
# Corporate Wiki
# Note I am forwarding to an IP address just to show that you can.
LocalForward localhost:8080 192.168.0.110:8080
# IMAP Mail Server
LocalForward locahost:1430 imap.pretendco.com:143
# Subversion Server
LocalForward locahost:2222 svn.pretendco.com:22
# NFS Server
LocalForward locahost:2049 nfs.pretendco.com:2049
# SMB/CIFS Server
LocalForward locahost:3020 smb.pretendco.com:3020
# SSH Server
LocalForward locahost:2220 dev.pretendco.com:22
# VNC Server
LocalForward locahost:5900 dev.pretendco.com:5900
###Hostname alias directives###
#These allow you to mimic hostnames as they appear at work.
#We just take the localhost names from the above section and add alias names.
#Note that you don't need to use a FQDN; you can use a short name ,such as smtp instead of smtp.pretendco.com.
Host smtp.pretendco.com
HostName localhost
Port 2525
Host wiki.pretendco.com
HostName localhost
Port 8080
Host imap.pretendco.com
HostName localhost
Port 1430
Host svn.pretendco.com
HostName localhost
Port 2222
Host nfs.pretendco.com
HostName localhost
Port 2049
Host smb.pretendco.com
HostName localhost
Port 3020
Host dev.pretendco.com
HostName localhost
Port 2220
Host vnc.pretendco.com
HostName localhost
Port 5900
#End Config File
Using the SSH Tunnel
Once you customize the ~/.ssh/config
file from the template shown above, you will need to
open an SSH connection to the master alias, which holds
the nested port forwarding for everyone. To do this, I
would highly recommend connecting in SSH verbose mode
first. SSH is extremely quiet by default, and it will be
a good idea to use a -v option as shown below:
ssh -v workTunnel
If you configured things correctly, you should see output that looks like this:
.................... debug1: Local connections to localhost:2200 forwarded to remote address svn.pretendco.com:22 debug1: Local forwarding listening on ::1 port 2200. debug1: channel 0: new [port listener] debug1: Local forwarding listening on 127.0.0.1 port 2200. debug1: channel 1: new [port listener] debug1: Local connections to localhost:2222 forwarded to remote address dev.pretendco.com:22 debug1: Local forwarding listening on ::1 port 2222. debug1: channel 2: new [port listener] debug1: Local forwarding listening on 127.0.0.1 port 2222. ......................
Tip:
Something very important to note, is that if you setup a
HostName alias properly, then you can perform an ssh
directory to that resource, just as you would inside of
work. For example, if you need to access your inside the
firewall you can type:
svn list svn+ssh://svn.pretendco.com/project
If you need to access a machine with a protocol other than ssh, say your internal wiki running on port 8080, then you can quite simply add an alias to your /etc/hosts file as show below:
# that require network functionality will fail. 127.0.0.1 localhost.localdomain localhost wiki ::1 localhost6.localdomain6 localhost6
You can always get to the forwarded wiki port by using:
http://localhost:8080
But, with the change to the /etc/hosts file you can also access it this way:
http://wiki:8080
If you are really motivated you can also do port forwarding to a “privilaged port”, port 80, if you have a server running on port 80 inside of the firewire, and you would then get to the http resource like you would at work, be slightly changing the ssh configuration file. Please note you will also need to do tunneling with “sudo” privilages.
# Internal Web Server running on port 80
LocalForward locahost:80 web.pretendco.com:80
This then allows you to browse to http://web.
Conclusion
Now that you have a sophisticated SSH configuration set up, you can connect to resources in exactly the same way you connect at work because of the host alias directives we applied. If you have an internal wiki server at work and you applied your custom information to the template I have supplied, then you can just type in wiki:8080 (substituting your actual wiki server), and it will work. I hope you find this as cool as I do!
Please note that things are not exactly perfect though. When you get back to work, if you try to SSH into your development box, you will have a problem, as your configuration file is set up to think you are at home. There is a simple solution, though. (Remember this only applies SSH connections to machines you have listed in your config file.)
You can do quite a few things, and I will leave it to you to decide which to use:
1. You could customize your Bash or Z-Shell startup
script to detect whether you are at home or at work, and
then source configuration files based on what IP address
your local machine is assigned.
2. You could create an alternate SSH file named
remoteConfig and leave your regular config file
blank. When you are at home or on the road, you can use
ssh -F ~/.ssh/remoteConfig
3. An even easier way would be to make an alias out of
option 2.
You can then create an alias in your bashrc file or your .zshenv file, that looks something like this:
alias stunnel='ssh -v -F ~/.ssh/remoteConfig workTunnel'
Now when you are at work, you use SSH like you normally do, but when you are on the road or at home you type in:
stunnel
With one command, you have access to your complete LAN in exactly the manner you use it at work, and you don’t need to muck around with writing conditional statements and sourcing bash config files to do it.
Summary
In this article we took ad hoc SSH tunneling and turned it into a full-blown VPN. I hope this took some of the mystery out of working from home via SSH tunneling and gave you an idea of how you can customize SSH tunneling to do just about anything.
I mentioned virtual machines in the beginning of the article, but then only hinted at how they could be included in this setup. As an exercise to the reader, I will leave integrating virtual machines to you. As a hint, look at how we aliased the stunnel command, and that should give you all the head start you need.