Many of the server environments I manage follow a simple pattern: Isolated Execution Environments.
Introducing the home lab
Typically a server environment does not consist of a single machine (those were the LAMP day for small web presences – but we are now way past that ;), but a collection of servers that provide fundamental infrastructure (database server) or individual (micro-)services. I use this for production but also in my own home. Let’s look at a common scenario:
You are on the road with your laptop – connected to the internet. Your production environment – or your home lab – is connected to the internet through a single point of contact. Let’s say we use the network at home, where you will most likely have some sort of (DSL) modem to connect to the net. This modem is completely accessible, all your servers and machines are hooked up to the modem or router. They can all get out, but it is not possible to directly address an individual server from the interwebs.
Now how can you access files on the private network from the internet? One technique is to use port forwarding from the router. If you ping the address 22.214.171.124 in our example you will get a response from the router, regardless which port you try – unless you define ports to forward you’re always talking to the router.
You could forward a different port for each server – say port 2022 goes to the test server, port 2023 to the application server. Then you could set up ssh on those ports for each machine (or you could simply map port 2023 on the router to port 22 on IP 10.0.0.120). Unfortunately that gets really confusing really quickly – especially if you do network redesigns like adding or moving servers and forget to update the port forwarding. Let’s quickly forget about this approach (which is great for making single purpose machines like a web proxy available, though).
Instead, you can set up a gateway machine (technically this could be your router, but in many cases those devices are too limited, so I prefer a dedicated machine. Plus in a production environment you will not have any routers…). Forward port 22 (ssh connections) to a proxy machine so that each
ssh email@example.com will log you into that proxy, not the router. Once you are logged into the router you can ssh into the next machine, which can be any IP inside the private network because technically you are now behind the wall.
While technically you can accomplish all you want by server hopping via ssh, it is cumbersome. It’s much nicer to let you access Application Server directly from your workstation, without explicitely logging into Web Proxy first. If only you had a VPN connection to that home environment? No problem – you can accomplish a similar effect just by using SSH and establishing a tunnel.
How to establish an SSH tunnel
An VPN tunnel will route all(!) traffic from your box for a specific port through the tunnel endpoint. This is done securely and nobody will be able to tell what you do – which is also great for hotel WLAN or corporate networks which block sites and you wish to still access them 😉
Once the tunnel is established you are still active on your workstation – not inside a Terminal session. All actions on the tunneled port are going through the remote machine, even if you address it via
A single command will establish the SSH tunnel:
ssh -L <localPort>:<remoteAddress>:<remotePort> <remoteUser>@<remoteSshHost> -p <remoteSshPort>
If you want to establish a tunnel that connects your local port 2345 directly to the SSH server running on Application Server (port 22) you use the command:
$ ssh -L 2345:10.0.0.120:22 firstname.lastname@example.org -p 2222
Now you can do a
ssh user-on-app-server@localhost -p 2345 and you log into the Application Server. Your local port 2345 is tunneled into the private network! For copying the file you can use scp:
$ scp -P 2345 user-on-app-server@localhost:/home/user/file local-copy
Note that the port is now specified with a capital
p, unlike the lower-case
p used by the ssh command!
Practical usage with MongoDB
I do not trust the security model of many applications, for example I am reluctant to expose MongoDB and its port 27017 to the world. What I prefer is to go through a ssh tunnel and then access the database. Doing this each time I need to send a command to the database is exhausting, so I set up the tunnel to forward a local port to the database.
The benefit of accessing MongoDB that way is that I get all the benefits from connecting locally from the console. It acts as if I am directly logged into the Mongo host.
When you do not want to actually establish a Terminal connection, but want to have the SSH tunnel available in the background, pass the
-f option to the command for setting up the tunnel. This will send it to the background, but complains that it does not have to execute a command. Hence you also need to pass the
-N option, which means that no specific command will be passed immediatebly, but the tunnel should remain open in the background.
If you need to close the tunnel runinng in the background the simplest way is to use
killall ssh or (on Mac OS X) use the Activity Monitor to search for the
ssh process and quit it.
Browsing the private nets – SOCKS proxy
Point-to-point tunnels are nice. However, what if you have multiple webservers running behind a proxy (like Meteor upstreams in nginx) and all of them are only accessible from the private network? If you want to check they are all working correctly it would be nice to be able to browser the web as if you were in the private segment. For this purpose you can use SSH to set up a SOCKS proxy.
The command is simple:
$ ssh -D 2345 email@example.com -p 22
-D option establishes a SOCKS server on your local port 2345. All requests to that port will go through the SSH tunnel established with host 126.96.36.199 on port 22 (of course you can leave out the
-p option if your SSH server listens on the standard port).
Configure the browser (or system settings) to use a SOCKS proxy. In OS X simply use the networking settings under Advanced. Now you can access all private sites as if you were inside the same net.
Take it further
There are many ways to use SSH tunnels more efficiently – like using scripts to execute the required commands for you or using SSH config file. Now that you got a glimpse at the power you already have at hand with a simple SSH server go forth and tunnel all the machines!