by Noah Gift
In a recent Red Hat Magazine article, Paul Frields gave some examples of how SSH port forwarding can be used
to remotely gain access to resources, or ports, from a remote location.
This article will show a pragmatic implementation of SSH port
forwarding by demonstrating how to use configuration files and
conditional statements to create permanent, yet dynamic, SSH
configurations for your home, office, and any virtual machines you may
have on your systems.
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
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
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
SMTP Server: 192.168.0.100 Port: 25 DNS Name: smtp.pretendco.comCorporate Wiki: 192.168.0.110 Port: 8080 DNS Name: wiki.pretendco.comIMAP Mail Server: 192.168.0.120 Port: 143 DNS Name: imap.pretendco.comSubversion Server: 192.168.0.140 Port: 22 DNS Name: svn.pretendco.comNFS Server: 192.168.0.150 Port: 2049 DNS Name: nfs.pretendco.comSMB/CIFS Server: 192.168.0.160 Port: 3020 DNS Name: smb.pretendco.comSSH Server: 192.168.0.170 Port: 22 DNS Name. dev.pretendco.comVNC 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.
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)
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
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
###SSH Port Forwarding Configuration### ####Global Configuration Options### #Host * will apply to all hostsHost * 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:22debug1: 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:22debug1: Local forwarding listening on ::1 port 2222.debug1: channel 2: new [port listener]debug1: Local forwarding listening on 127.0.0.1 port 2222.......................
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:
But, with the change to the /etc/hosts file you can also access it this way:
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”
# Internal Web Server running on port 80 LocalForward locahost:80 web.pretendco.com:80
This then allows you to browse to
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
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:
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.
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.