SSH Reverse Tunnel on Mac OS X

Phone Home

original source

I want to always be able to securely connect to my Mac Book Pro anywhere in the world. The following guide will enable me to connect to the laptop if I leave it at home, leave it at work, or in the unfortunate event that someone steals it and the thief defeats File Vault full disk encryption. This phone home technique will work as long as the laptop can create an outgoing ssh connection. It will work behind NAT routers, but probably not behind strong corporate firewalls that only allow web proxy traffic out (something like corkscrew could help with this if you need it).

A lot of other people have written guides about how to do this and have done it wrong. For example, people play tricks to avoid executing arbitrary commands by forcing ssh to execute something like /usr/bin/false. If you don’t to execute, don’t execute anything. Don’t pretend not allocating a pty is secure, it’s not, just prevents the obvious shell logins. Furthermore, use the Mac OS X launchd correctly to create and maintain connections automatically, don’t fork the ssh client, rely on the launchd KeepAlive feature.

In addition to the obvious purpose of connecting to a remote laptop that moves around alot, I’ve used this technique to circumvent poorly configured firewalls and to bring ad-hoc servers in other countries online before jumping on a transpacific flight with only 30 minutes to implement a “vpn”.

Fun stuff.

Setup The Server

First step is to setup an authorized_keys file to allow logins for a private key, run the following on the client machine (Mac OS X laptop in my case). If Elliptic Curve DSA (ECSDA) is available and supported on both ends, it can be used by adding “-t ecdsa” to the ssh-keygen command. Example default dsa key generation:

client $ ssh-keygen -f ~/.ssh/servername-home-fwd
Generating public/private rsa key pair.
Enter passphrase (empty for no passphrase):
Enter same passphrase again:
Your identification has been saved in /Users/user/.ssh/servername-home-fwd.
Your public key has been saved in /Users/user/.ssh/servername-home-fwd.pub.
The key fingerprint is:
3b:c7:7f:77:49:5d:5f:35:1d:82:ad:20:c8:7d:1e:d2 user@client
The key's randomart image is:
+--[ RSA 2048]----+
|    . o .   o. .o|
|     o + E . ...o|
|        = o .   o|
|         . .    o|
|        S       =|
|         o     .o|
|        o o   . .|
|         o .  ..o|
|            .. ..|
+-----------------+

Now a private key has been generated, read the public key on the client and copy it:

client $ cat ~/.ssh/servername-home-fwd.pub

On the server paste the private key on it’s own line in ~/.ssh/authorized_keys or use ssh-copy-id for easy installation if available. Prefix the public key with command="",no-pty to prevent any commands from being executed using this private key and to prevent wasting resources for a pty (not a security feature). Optionally add a comment to the end so you can keep track of the purpose of this installed public key. The result should look something like the following:

server $ cat ~/.ssh/authorized_keys
command="",no-pty ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDUWUrEMfnP65pGSwdLFP323K7AG8Ah5JC0ArC660z7V0i3wSBf1lXnN3coc/ndw7C7NQw+wLTNp7FjkxheyNiMhf65pZI41HR+JJleQsAwCCpFwftKutfknpvai6FRkfE173iXGAU5YFGmlHBo3oAiSn09ZOAXieZ0+Sr0ZEAF5gJLLG4E94JUdEtCXcLYPWu0YX//k+PVvaK1/IjJ6gJGbzT+KA5Gv4VZecL+UC7VvgtcO6UBVNHo4eQBPdhiT1Asg71e3io2y+AwGg0J2smBcuhmrf3ud/BFNgHFjF3a7ilF2Hh7AYV16rRJrOazH83IBzgiBysiakF3OSHQXozV client@server:phone-home

Now you’ve allowed any remote machine to login to the user machine on the server with the private key generated above (~/.ssh/servername-home-fwd). The login won’t be able to run any commands and won’t be able to allocate a pty (which probably is pointless after no command execution, better safe then sorry).

Additionally, if it’s possible to modify the sshd_config file on the server, it should be modified to send ssh alive packets (similar to TCP keep alive) packets to the client. Root access on the server is typically necessary for this. On an Ubuntu server this can be accomplished with:

server $ echo "ClientAliveInterval 60" | sudo tee -a /etc/ssh/sshd_config
server $ sudo restart ssh
ssh start/running, process 6446

The above modification will ping the client every 60 seconds the connection is idle as defined the ClientAliveInterval which is disabled by default. If ClientAliveCountMax (defaults to 3) number of pings go unanswered, the server will drop the connection. This is critical to detecting the remote client has disappeared and freeing up the port defined below for a reconnect from the client when it comes back online. It isn’t strictly necessary as the server will drop the connection after a while on its own, but significantly speeds up reconnects.

Test the Server Setup from the Client

Verify that the server is correctly setup by running the ssh command manually. This is important for two reasons:

  1. The first time the ssh client connects to the server, it by default needs the user to manually accept the host’s ssh key. This will never succeed in the automated launchd task described below and must be done ahead of time.
  2. Verify nothing is broken.

To test the configuration, run the following:

client $ ssh -i ~/.ssh/servername-home-fwd -NT -R 12345:localhost:22 remoteuser@servername

The result should be that the command blocks and appears to hang. At the same time, verify that port 12345 is now listening on the server. If port 22 on the client is in fact the ssh server this can be quickly tested by reading some data over the connection such as the SSH server’s version using netcat:

server $ netcat localhost 12345
SSH-2.0-OpenSSH_5.9

After testing is complete, use CTRL-c to break both the ssh and netcat command. If something didn’t work, double check the steps above for errors before proceeding.

Setup The Client on OS X

Apple uses launchd to launch system services. The purpose of launchd is very similar to Ubuntu’s upststart and Freedesktop’s systemd in that it’s goal is to start services and manage them.

In a nuthsell the primary features needed for this phone home script are:

  1. Run at start-up without user intervention
  2. Run as another user
  3. Restart a process when it dies

First, we need to setup a plist file for OS X, create the following file and modify it as necessary for items such as the user and host name, place the following @ /Library/LaunchDaemons/server.name.client.name.home.plist

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
   <dict>
   <key>Label</key>
   <string>server.name.client.name.home</string>
   <key>ProgramArguments</key>
   <array>
	  <string>ssh</string>
	  <string>-NTC</string>
	  <string>-o ServerAliveInterval=60</string>
	  <string>-o ExitOnForwardFailure=yes</string>
	  <string>-i</string>
	  <string>/Users/<username>/.ssh/servername-home-fwd</string>
	  <string>-R 12345:localhost:22</string>
	  <string>remoteuser@servername</string>
   </array>
   <key>UserName</key>
   <string>user</string>
   <key>RunAtLoad</key>
   <true/>
   <key>KeepAlive</key>
   <true/>
</dict>
</plist>

This will switch to Username user on the Mac OS X client and then attempt to run the ssh command described by ProgramArguments. SSH command description:

  • -N – Don’t run a remote command. Attempting to run a remote command will fail due to the restrictions imposed by the authorized_keys file.
  • -T – Disable pty allocation. There is no need for this when only port-forwarding is desired.
  • -C – Request compression. This is optional, typically my processors out pace my network speed, especially when on 4G/LTE networks. This is optional.
  • -o ServerAliveInterval=60 – The client will attempt to send pings to the server ever 60 seconds. After 3 failed pings (Default ServerAliveCountMax is 3), the client will drop the connection and ssh with return.
  • -o ExitOnForwardFailure=yes – If port forwarding fails to get setup due to something like another process (or old ssh process) being bound to the hardcoded port, fail and return.
  • -i /Users/<username>/.ssh/servername-home-fwd – Use the specified ssh private key (generated above) for this connection. This must be the the private key for the public key in the authorized_keys file on the server.
  • -R 12345:localhost:22 – Remotely forward the localhost port 22 (sshd) to the server’s port 12345. This allows the server to connect to the client’s ssh port.
  • remoteuser@servername – Connect to ssh servername with user remoteuser.

When the ssh tunnel dies due to a change in network connection or fails to setup the initial port forwarding as requested, the launchd manager will restart it in 10 seconds due to the KeepAlivekey. The default restart time is 10 seconds and should work just fine for this task.

The RunAtLoad does as the name suggests and runs this launchd task at load and boot time.

If all goes according to plan, the launchd plist can be loaded and it will connect to the server:

sudo launchctl load /Library/LaunchDaemons/server.name.client.name.home.plist

Launchd will start up ssh as directed by the plist and connect to the remote server. The netcat command described above can be run on the remote server to verify it’s working. If it’s not working, check the logs on the client and server under /var/log for hints as to what went wrong. After modifying the plist file, be sure to unload it and then reload it by changing “load” to “unload” in the command described above followed again by “load.”

If it is working, your Mac OS X will automagically open a reverse tunnel to the server described above. You can then login to the client Mac OS X machine by using ssh -p12345 user@localhost on the server. Note that the host will always be localhost due to port forwarding, and the user is the user on the Mac OS X client.

Doing Even More

I’m looking only to ssh back in to my laptop, but with a few modifications to the launchd plist, it’s possible to use this to setup ssh vpn tunnels using tun interfaces. Refer to the ssh man page for the the “-w” option. You’ll need to setup routes and what not to fully use it. Things get complicated quick and many times OpenVPN is a better solution.

Other uses? Let me know!

Как поменять hostname, computername и bonjour name в OS X

ComputerName is the so-called “user-friendly” name for the Mac, it’s what will show up on the Mac itself and what will be visible to others when connecting to it over a local network. This is also what’s visible under the Sharing preference panel.

scutil --set ComputerName "MacBook Willy"

HostName is the name assigned to the computer as visible from the command line, and it’s also used
by local and remote networks when connecting through SSH and Remote Login.

scutil --set HostName "centauri"

LocalHostName is the name identifier used by Bonjour and visible through file sharing services likeAirDrop

scutil --set LocalHostName "MacBookPro"

Проверяем:

scutil --get HostName

Как слушать (стримить) свою библиотеку iTunes через интернет

Mac OS X users and faithful readers of TUAW know that there is great power found in the command-line, but one of the greatest advantages of OS X’s UNIX heritage is the secure shell (SSH) client and server. From the Apple Matters article on SSH tunnels for the common man: “SSH is a network protocol that provides secure communication between two computers.” Its power comes from the ability to create an encrypted tunnel through which a client can send many types of internet traffic.

The Lifehacker article on setting up an SSH server covers the steps necessary to roll your own SSH server whether you are running Windows or OS X. Parts one and two of the Apple Matters series on SSH tunnels provide a lot of useful information on SSH as well as creating an SSH tunnel for encrypting traffic. One such use that has not yet been covered is how it is possible to stream your iTunes library across the internet while using software that is either built-in or available for free.

This tutorial will get pretty technical and assumes that you already have an SSH server and you know how to access it from a remote location (e.g. from work). If you are already past the point of just setting up an SSH server and are asking yourself “what now?” then read on to find out how you too can listen to your tunes at work even though they are trapped at home.

For starters, we must set-up an SSH tunnel from our local machine to our remote SSH server. There are several applications that can be used to create the tunnel such asJellyfiSSH (free) or Meerkat (not free) for OS X and PuTTY for Windows. Additionally, the tunnel can be created from the command-line using Terminal; if you are on Windows you will need to get Cygwin. The tunnel should be created such that you wish to access a remote service on port 3689 of your SSH server and have that forwarded to some random local port above 1024. If you use a port lower than 1024 it will require root privileges and for this reason I recommend something simple like 36890. If you’re doing this the command-line way you’re going to run something akin to the following:

“ssh user@remote_server_ip -L 36890:127.0.0.1:3689”

In a nutshell the command above says to create an SSH tunnel that forwards the service running on port 3689 at the remote server to port 36890 on your local computer. To test the connection you can telnet to “127.0.0.1 36890” and then hit <enter> twice. This should list a killed connection to DAAP-Server: iTunes; if it does then your tunnel is up and everything is ready to go. If this does not work check to make sure that iTunes is running on the remote server, make sure library sharing is enabled or try verifying port 3689 is open at the remote server.

Now that the SSH tunnel is running smoothly, it’s time for the magic portion of the show. You’re going to need to grab an application called RendezvousProxy from here. RendezvousProxy is required for iTunes to be notified of the shared library’s availability. If you are using Windows grab the only binary available and download it. On OS X you will need to download the binary for Java as the OS X binary does not seem to work in Leopard. There is no need to run the included script called “runme.sh” (it will not harm anything, it is simply unnecessary). Launch RendezvousProxy by double-clicking the included jar file; when the application appears you will need to add a host and configure it as shown here:

Once the new host has been added you should see a link to the shared library in iTunes:

At this point you can click on the newly added iTunes library share and start jamming out to the new Britney Spears Killers album. Keep in mind that if you are on dial-up or a similar low-bandwidth connection, the music may skip and problems could occur for users on the network where the SSH server resides. However, if you’re rocking with a good connection and tons of tracks to choose from then you should have no problem finding some music to listen to — probably annoying all of your co-workers.

One more thing: if you happen to have Xcode installed and want to go a step further with this little project it is incredibly easy to fix the OS X binary. All you have to do isdownload the source, open the project in Xcode, and re-build. Also, if you don’t need an interactive terminal session open for the purpose of sending other commands to the SSH server you can add the “-N” flag to the command so it reads “-NL” instead.