Creating a Permanent SSH Tunnel Back to Your Mac at Home

Uncategorized May 26, 2019

Today’s post is a bit more technical than what I’ve been writing about lately, but it’s also partly for my own reference to save me some googling when I forget everything again in the future.

I was always a big fan of Apple’s Back to My Mac service. I found it incredibly useful to be able to screen share with my Mac at home and access its files. (So useful, in fact, that I even built an app to extend the service’s functionality.) But Apple shut down the service in Mojave. And besides, with all my data now in the cloud it’s become less of an issue since there’s rarely ever a file that’s “only on my Mac” back at the house. I do still find the need to occasionally SSH into my LAN or screen share, though. And for the last few weeks I’ve been spending more time working outside the comforts of my home office. And it looks like that may continue for the foreseeable future – at least for a day or two a week.

So, this holiday weekend I decided it would be fun to finally get around to setting up some sort of permanent connection that would allow me to log in remotely.

For various boring reasons all the devices on my home network are behind two layers of NAT. Opening up a hole in my gateway’s firewall and forwarding a port or two would work, but that has always felt a bit icky to me – I don’t like the idea of some automated bot on the internet finding an open port on my network and just hammering away at it.

Luckily, from my time spent working remotely for Yahoo!, I got pretty good at the ins-and-outs of setting up SSH tunnels. And I happen to have a few web servers on the public internet with bandwidth to spare.

So, I added this to my ~/.ssh/config file:

With that in place, I can run ssh vpn, and a reverse SSH tunnel will be established between my Mac inside my network’s firewall and my remote web server. It will then forward ports 5900 and 9933 on the remote host back to my local Mac’s VNC and SSH daemons.

This means when I’m working remotely I can run ssh home on my laptop (or phone if I’ve got something like Prompt installed) and I’ll be SSH’d into my Mac at home. Further, I can then open up macOS’s Screen and connect to localhost:5901 to access my Mac’s desktop.

So, that all works great. The only issue is that if the SSH connection from my Mac at home to my web server is interrupted, the internet flakes out, or I restart that Mac, I’ll need a way of automatically reconnecting.

For the last few years, whenever I’ve needed to keep a job running on one of my web servers, I’ve used supervisord. It’s easy to install, flexible, and mostly straight-forward. Without giving it much thought I did a quick search to see if it’s available on macOS and – yep! – got it installed a minute later with Homebrew.

For reasons you’ll read about in a minute, I won’t go into the boring details of getting supervisord to launch automatically when macOS boots, or how to configure the program to launch and keep my SSH tunnel open. I’ll just say that after an hour of hitting my head on the desk, I was never able to get the tunnel to connect when my Mac restarted.

The supervisor program I setup would get stuck in a backoff state that would eventually fail after a number of retries and end up as fatal. I don’t fully understand why, but my best guess is that supervisord tried to launch the job before the network came online. SSH would, of course, fail and exit unsuccessfully. At which point it would get retried again, fail, repeat, etc. Again, I don’t know for sure – that’s just my best guess.

Instead, after taking a break to play with my kid and think things through, it dawned on me that I was making this all way too complicated. macOS already has everything I need built-in.

I fired up LaunchControl and two minutes later had a launchd job installed that opened the SSH tunnel on boot and kept relaunching it if it ever failed.

Practically, this meant that it immediately re-spawned the connection over and over at launch until the network came online and SSH connected. Once that happened, the connection typically remained open for hours if not days at a time. And in my testing, any time the network glitched out, launchd would keep restarting it until the internet was back up and a new connection was made.

Here’s the .plist for the launchd job that LaunchControl generated:

So now everything’s working. I can connect back to my Mac at home from wherever I am. Whether I’m on wifi or cellular, on my laptop or iPhone.

I’m also reminded how incredibly robust macOS is and to not reinvent the wheel so quickly. There are a million-and-one ways of automating your Mac. It’s best to turn to one of them before looking at third-party solutions.