Advanced SSH configuration and tunneling: We don’t need no stinking VPN software
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.
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:
<strong<host: 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.







November 27th, 2007 at 12:22 pm
For SSH tunneling to replace a full-blown vpn client/server setup requires that all the traffic you need to tunnel is TCP/IP. It also requires you to manually create and keep track of forwarding tables.
November 27th, 2007 at 12:26 pm
Kaj,
I am being a bit tongue in cheek
, ssh tunneling isn’t a 100% substitute, but it can quite a bit for a simple unix utility, and it is free and easy to setup. Your point is valid though.
November 28th, 2007 at 7:40 am
It’s great to see people using RHM as a skill- and knowledge-building tool. Having articles by different authors that build on one another is a great feature, and I’m almost sure I’ve seen it happen before once or twice. The archives will tell the tale, although I don’t have time to link-forage right now. I wrote my article specifically aimed at beginners, but there’s quite a bit of meat here for the “intermediate and above” user/administrator. Nice going Noah!
November 28th, 2007 at 8:15 am
Please see RFC 2606 for information on using example domain names. Apple, Inc. may have a problem with you using pretendco.com.
November 28th, 2007 at 9:32 am
M,
You raise an interesting point, but I supposed I like being a little different. I have used pretendco quite a bit in examples all over the web. If anyone ever objects from Apple, I will just register my own “fake” domain name for examples, maybe pretendnoahco.com? It is actually kind of weird Apple has a pretend domain just for example purposes now that I think about it…maybe Red Hat needs to come up with a pretend name too.
November 28th, 2007 at 11:58 am
Very interesting, but I doubt, if passive FTP will work.
Any experiences or config suggestions?
November 28th, 2007 at 3:29 pm
Maybe I wasn’t focused enough on the article but the “workTunnel” hostname wouldn’t work in this example since it’s not declared under /etc/hosts to point to the work ssh server. Or i’m missing something..
Also, the system-wide config file for ssh can be found under /etc/ssh/ssh_config, and not /etc/ssh_config
Lastly, There are 4 typos, search for “locahost”
Thanks for this article.
November 28th, 2007 at 6:20 pm
you can send with sftp and use the -b switch to handle batch commands and set up automatic sign on with a private/public key pair to execute automated ftp.
November 29th, 2007 at 1:05 am
Paul W. Frields/Thanks for the comments. I agree it is kind of fun to piggyback on top of other articles. I know someone recently did one from the dual boot OS X Linux article I did recently. There is much more to do with SSH still. I would love to see someone do an article on joining two networks via SSH, or taking this article a step further, and having a cascading set of tunnels that work through multiple virtual machines running off of one host.
Seb/Thanks for the comments, and grabbing the typo’s. I will see if we can get the localhost and /etc/ssh lines fixed. I am jumping all around different *nix boxes, and at the time, my *BSD machines had /etc/ssh_config, but a Red Hat machine will keep it in /etc/ssh/ssh. As for the /etc/hosts file, it is only necessary for non-shell traffic, the HostName option allows you to set nicknames, which are not actually host names. It is kind of confusing unless you walk through the most simple example you can first. Try doing one “nickname” only.
December 19th, 2007 at 12:13 am
Noah,
What is the difference between workTunnel and ssh.pretendco.com? I have them as the same box, and no tunnels get setup.
December 21st, 2007 at 9:49 pm
FYI: There is a gnome tool out called gstm that allows you set up this exact ssh port forwarding. This tool is available on Fedora. Check it out.
December 22nd, 2007 at 5:28 pm
Wow!!! Fantastic Reading. Thank you Paul and Noah… And I like the tool gstm. This is going to be a huge noteable document for my Linux Customers…. Fantastic…
December 23rd, 2007 at 5:36 pm
Hello,
instead of forwarding lots of ports manually, the builtin SOCKS proxy (option -D) is a more flexible solution which may in some cases be preferable.
-Sven
December 25th, 2007 at 9:27 pm
Good document for understanding SSH config and tunneling.
unixfoo
http://unixfoo.blogspot.com
December 28th, 2007 at 9:39 pm
The argument of VPN vs SSH tunnel misses some subtleties.
Both do ‘encryption over the wire’, but the object being encrypted is different:
VPNs use IP datagram encapsulation (thus working at layer 3). This gets you from your machine into the _enclave_ (the corporate enclave in the case you were using).
By contrast, ssh encrypts a layer 7 application — the granularity is much finer. The application is end-to-end — between one end system and another. This greatly decreases the potential for insider attacks (e.g. anyone inside the VPN enclave).
This observation reinforces the point you make: ssh is good stuff (so is secure e-mail, another end-end, layer 7 application).
January 2nd, 2008 at 8:45 am
This is a great article, but some of its contents would be considered quite naughty by many IT departments. Maybe there should a little warning about not enfringing acceptable usage policies etc?
January 4th, 2008 at 1:26 pm
“instead of forwarding lots of ports manually, the builtin SOCKS proxy (option -D) is a more flexible solution which may in some cases be preferable”
True! While not all programs support SOCKS, it allows to completely forget about port forwarding at least for numerous existing and *future* corporate _web_ resources.
I would also mention ‘autossh’ utility. It might be very handy if your remote connection is unstable.
–
…Bye..Dmitry.
January 9th, 2008 at 6:42 am
Implementing a system-wide SSH socks proxy on Mac OS X really isn’t that difficult. And you also can easily install advanced ssh tools like nylon or tsocks (see http://textsnippets.com/posts/show/1326 ).
Cheers,
g. m.
February 6th, 2008 at 12:48 pm
I think there’s a typo in the example config:
Host workTunnel
#Work SSH Server To Initiate Tunneling From
Host ssh.pretendco.com
Port 5001
should read:
Host workTunnel
#Work SSH Server To Initiate Tunneling From
HostName ssh.pretendco.com
Port 5001
February 15th, 2008 at 6:28 am
good…
but what happened if we have windows client both side,,,and when we try to access file system/samba/nfs share and remote desktop for windows system.
have any idea?
February 15th, 2008 at 11:04 am
It’s a great one-off trick, but I question its suitability as a “VPN”:
1. Neither SMB/CIFS filesharing nor DNS can be tunneled, as they use UDP which SSH doesn’t grok. For UDP forwarding, have a look at netcat.
2. Many many webserver configurations use name-based virtual hosting, which uses the ServerName HTTP header to determine what content is served. Such a config *requires* an /etc/hosts entry to 127.0.0.1, rather than using “localhost” in your URI.
4. Your IT / security department may strenuously object to these types of activities; exercise caution in proportion with cleverness.
April 16th, 2008 at 2:47 am
Thanks very much for the explanation!
You have a small error in the config:
Host workTunnel
#Work SSH Server To Initiate Tunneling From
Host ssh.pretendco.com
“Host ssh.pretendco.com” should be “Hostname ssh.pretendco.com”
Also on a Mac I can’t add comments on the same line as a normal configuration option:
.ssh/config line 13: garbage at end of line; “#Note”.
August 31st, 2008 at 8:02 am
I do not believe this
December 3rd, 2008 at 3:02 pm
Christopher DeMarco: You *can* tunnel SMB/CIFS using SSH. You only need to forward either port 139 or 445 for MS Windows networking.
January 7th, 2009 at 10:38 am
Is there a way to use the Host value in the HostName?
Host myhost
Hostname ${HOST}.mydomain.com
Host yourHost
HostName ${HOST}.yourdomain.com
for appending domain names.
March 18th, 2009 at 1:09 pm
Thanks for the tutorial ^^
It worked for me, when the “#Note” for inline comments and the “Host” to “HostName” typo were fixed.
I’ve got 3 porwarded ports (at the moment) 2 VNC and 1 squid, and it is fast and safe.
Keep up the good work…