Rate this page del.icio.us  Digg slashdot StumbleUpon

Advanced SSH configuration and tunneling: We don’t need no stinking VPN software

by

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.

The information provided in this article is for your information only. The origin of this information may be internal or external to Red Hat. While Red Hat attempts to verify the validity of this information before it is posted, Red Hat makes no express or implied claims to its validity. Please review and comply with any relevant IT policies at your company.

26 responses to “Advanced SSH configuration and tunneling: We don’t need no stinking VPN software”

  1. Kaj says:

    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.

  2. Noah Gift says:

    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.

  3. Paul W. Frields says:

    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!

  4. M says:

    Please see RFC 2606 for information on using example domain names. Apple, Inc. may have a problem with you using pretendco.com.

  5. Noah Gift says:

    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.

  6. Paul says:

    Very interesting, but I doubt, if passive FTP will work.
    Any experiences or config suggestions?

  7. Seb says:

    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.

  8. dietrich says:

    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.

  9. Noah Gift says:

    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.

  10. Chris says:

    Noah,

    What is the difference between workTunnel and ssh.pretendco.com? I have them as the same box, and no tunnels get setup.

  11. Peter Meyer says:

    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.

  12. Mark St. Laurent says:

    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…

  13. Sven Neuhaus says:

    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

  14. unixfoo says:

    Good document for understanding SSH config and tunneling.

    unixfoo
    http://unixfoo.blogspot.com

  15. Rex Buddenberg says:

    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).

  16. David Legg says:

    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?

  17. Linulin says:

    “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.

  18. gerd meller says:

    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.

  19. new299 says:

    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

  20. bhupesh says:

    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?

  21. Christopher DeMarco says:

    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.

  22. Tunneler says:

    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”.

  23. fornetti says:

    I do not believe this

  24. Herman says:

    Christopher DeMarco: You *can* tunnel SMB/CIFS using SSH. You only need to forward either port 139 or 445 for MS Windows networking.

  25. Ed says:

    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.

  26. nicopsycho says:

    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…