These days it seems like we all live in the the cloud. Whether it’s your personal site, email, or photos to show your family at the next get-together, odds are you’re storing something, if not everything, up there. It’s fantastic! But it’s not everything, and there’s plenty of times when you still need to be communicating with individual machines located anywhere from the same room to across the world. For that, and so much more, there’s SSH.
Are you telling me to be quiet?
For anyone who says TL;DR to the Wikipedia article, SSH is one of the most simple and secure (if you’re doing it right) ways to communicate between networked devices. Clients are usually available by default on Unix based systems (Linux, Mac, and even the Chromebook I’m editing from) and can be easily obtained for the other popular OSes (Windows, Android, iOS). At the other end, SSH server software is a staple of most, if not all, servers out there, although it may not be enabled by default, so keep that in mind when you decide to tinker.
How I started using SSH
I stumbled upon SSH shortly after stumbling upon Linux. Since then, it’s only become more valuable for both work and play as I learn additional features of SSH and how to leverage them. Due to its secure nature, it can be used to encrypt traffic, transfer files, launch remote applications, and even help turn your tablet into a second screen! And while there are plenty of apps and other ways to do these things, few have the same availability as SSH, especially when dealing with a University or campus network, and potentially even some ISPs. But today I just want to focus on one aspect of SSH, and that’s tunneling. There’s 3 kinds, two of which I’m familiar with, and one that has become incredibly useful as my development workflow has changed.
1. When you want it locally, but it’s remote
One thing I’ve found incredibly difficult about SSH tunnels is keeping the syntax and function of each kind straight in my head. My main use for a Local tunnel is when the resources I need are not accessible to me locally, but I want them to be. For example, you’re working on an application locally, complete with a local database instance. You want to move to testing with a more populated database, maybe even your production one, but unfortunately it’s a pain to switch them in your code (at least until that refactor you have planned…). This is where the “local” version can help, by creating a tunnel to the remote database. Here’s the syntax:
ssh -L <local host>:<local port>:<remote host>:<remote port> <user>@<ssh server>
Taking a closer look at the command:
- ssh is the command to execute SSH
- -L signifies we want a “Local” tunnel
- <local host> is the host of the computer you will be accessing the resources on, in my examples it’s always your own computer, so this will be localhost
- <local port> is a port of your choosing to access the resources on, note that this can’t be just any port depending on your permissions, so it’s best to choose an ephemeral port for testing at first
- <remote host> is the hostname/IP address of the remote resources you’re looking to get at
- <remote port> is similarly the associated port of those remote resources
- <user>@<ssh server> finally, this is the machine you have ssh access to, which must also have access to the remote resources you are targeting (note, they CAN be the same machine, and in my cases most often are)
So there’s quite a number of pieces here, so let’s tie it in with the example, say your remote database is at 192.168.1.5:3306, you also have ssh access to it with your user name ‘bob‘ and your application is set up to use a database at localhost:9876. In this case, your command would be:
ssh -L localhost:9876:192.168.1.5:3306 bob@192.168.1.5
After a successful connection, any data sent to localhost:9876 will be sent through the tunnel to 192.168.1.5:3306, and vice versa. Note that the remote IP and port are evaluated on the ssh server’s side, so since the ssh server and database server are one in the same, this is also a valid command:
ssh -L localhost:9876:localhost:3306 bob@192.168.1.5
which is where most of my confusion with this syntax comes from… Another great use case for this may help you to remember the nuance. In this case the remote resource we want to get at is a website, say cnn.com, port 80. Most likely you don’t have ssh access to cnn.com, so you’ll remember that you have to put cnn.com:80 for the remote information and the ssh server separately, as seen here:
ssh -L localhost:9987:cnn.com:80 bob@192.168.1.5
By navigating to http://localhost:9987, you should see the same information as http://cnn.com. Note however, that with increased use of CDNs and distributed server environments, you may have trouble accessing certain sites if they see that there’s some discrepancy with the requests, YMMV.
2. I have it here, but I want it to be remote
Remote port forwarding is the killer accessory for my toolchain that I mentioned earlier. And similar to its name, it does the opposite of local port forwarding. In this case, you have a resource locally that you would like to make available remotely. In my case, it’s usually a prototype of a webapp function that I’m messing around with. I want to show it off to some people and get some feedback, but the team isn’t co-located, so walking around with my laptop is a no-go. Instead, I can set up a remote port forward to a development machine, send them a link, and they can view it right on my machine. This way neither of us need to mess with firewall settings, and there’s no need for any special deployments or the time that comes with them. For remote forwarding, the syntax is as follows:
ssh -R <local host>:<local port>:<remote host>:<remote port> <user>@<ssh server>
As you can see, most of the syntax remains the same, except now there’s a -R to indicate remote port forwarding, the local fields designate the resource you’re accessing, and the remote fields are where others will access the resource.
An important difference here is that by default, the ssh server config doesn’t allow a user to “bind” to interfaces except the loopback address, so you can set everything up correctly and no one will be able to see it. To get around this, you should specify GatewayPorts yes in the ssh server configuration and restart the ssh server program (my suggestion would be to set this up under a MatchUser section so that this functionality is only available to specified ids).
So once you’ve made that change, say you had a demo site you wanted to show to a client to get some feedback about color shades. You have the site running locally at localhost:9080 and also have a server at colordemo.com, and you want to make the demo available on port 9090, your command would resemble the following:
ssh -R localhost:9080:colordemo.com:9090 bob@colordemo.com
Now your client can access the demo on your machine by going to colordemo.com:9090, and when they ask you to tweak a couple hex values, you can make those changes, ask them to refresh, and they’ll see it. They can get instant feedback, and you can keep using your favorite editor to make changes, and no need to wait for a build/deploy.
3. A SOCKS Proxy
The final kind of port forwarding is a dynamic forward. This allows requests to be made by the client, but forwarded to the server who then resolves the requests, returning the information via the secure tunnel. This allows for things like an impromptu SOCKS proxy, and admittedly I’m not sure what else. I don’t have much experience with these, so rather than guess, I’ll leave it to you to search out a more informative search for those.
Final Thoughts
So that’s a not so short and sweet low-down on SSH port forwards, but hopefully some of the additional details helps you to understand and remember the syntax for each version. If this sort of thing interests you, I really recommend getting acquainted with SSH in general, including how to utilize the “escape character” to kill frozen sessions, add in port forwards on the fly, and a couple other useful tricks. Then there’s fun stuff you can do such as modify the message users see when they SSH into a machine (may I recommend including some ascii art?) which helps keep things fun when you’re in a mostly text world of the terminal. And finally, if videos are more your thing, there’s a great video that details port forwarding and a WHOLE lot more about ssh called “The Black Magic Of SSH / SSH Can Do That?” which can be found on Vimeo. Thanks!