Tailscale: macOS: get a DMG version working for distribution outside App Store (required for beta testing)

Created on 27 Aug 2020  路  5Comments  路  Source: tailscale/tailscale

History, as best I've been able to figure it: Apple introduced the NetworkExtension as an _App Extension_ for iOS to make it possible to run background networking code. This was then ported to macOS in the name of compatibility, so the exact same appex could run in the Mac Store sandbox. Since then, there appears to have been a slight shift in Apple's thinking: compatibility is done by Mac Catalyst (think: Android-on-ChromeOS), and for the future of macOS-specific apps Apple have introduced a _System Extension_ variant of the NetworkExtension in macOS 10.15 Catalina.

For the NetworkExtension in particular, the System Extension is very similar to the App Extension. The same APIs for invoking the packet tunnel work, so code can be shared. However there are several notable differences that make sysex interesting:

  1. A System Extension can run when the user is not logged in. (I haven't tested this, but it may get us headless mac mode for Tailscale.)
  2. It is accepted by Apple that you can do your own IPC. See IPCConnection.swift in the FilteringNetworkTraffic example. This means we could use our common protocol from Linux/Windows for talking between the frontend and the sysex.
  3. With care, a NetworkExtension sysex can be signed by a Developer ID, meaning it can be deployed outside the (see the comment by eskimo in this thread that mentions app-proxy-provider-systemextension, I've tested this, it works.) This is not possible with an appex.
  4. There is formal "replacement" logic in the sysex API that may make store upgrades smoother. (Untested hypothesis.)

The significant downside: this only works on 10.15. New macOS versions roll out a lot slower than iOS, so far only ~55% of macs run Catalina. So we cannot switch to it yet. However, it is worth maintaining a minimal sysex variant of our app today for the sole reason of letting users beta test our code outside the mac store. Eventually, I believe we should migrate completely to sysex on macOS.

Front logo Front conversations

OS-macos

All 5 comments

I played with this a bit a couple of weeks ago. My main discoveries were:

  • the SystemExtension UI is very fiddly, requiring about 4 levels of confirmation (in addition to dragging the app into the /Applications folder first, or it won't work, unlike every other macOS app). This will confuse users for sure.

  • if you have more than one app installed with the same SystemExtension name, macOS gets totally confused, as it does with NetworkExtensions. I got myself into a situation a couple of times where rebooting seemed to be the only fix. (It's slightly better than NetworkExtensions because only apps in /Applications can have a SystemExtension at all, which makes the registration/unregistration process easier to understand.)

  • SystemExtensions run as root, not as the user, so they don't have access to the user's keychains. We can fudge this into working by adopting the same mechanism we use on Windows (where the backend also runs as a system user), where the frontend keeps a "session key" that indexes into the backend's private storage, which contains the real keys. This is arguably an improvement over our current design: it allows "server mode" (ie. Tailscale works before a user logs in) and it could work exactly like Windows, ie. one less variation in the state machine.

  • On the other hand, SystemExtensions cannot be distributed in the app store (as far as I know), and the app store app would not be able to store its keys in the system keychain. This creates major differences between the NetworkExtension (app store) version and the SystemExtension (separate .dmg) version.

Another variation on the same theme would be to simply distribute tailscaled (the command line daemon) and a launchd profile for it, and install using a .pkg script. This would work on pre-macOS-10.15 systems, but would be even more different from the NetworkExtension app, because we'd have to use the darwin unix-like APIs rather than the NetworkExtension APIs. Support for that was contributed by open source users, but I don't think the network namespace stuff is implemented there (so there can be routing loops, at the moment). Also, installing the launchd profile requires root access, so we'd have to tell people to run a particular command, or make a GUI that captures your password and sends it to sudo, both of which are pretty gross.

A final option, which is pretty tempting, is to collect the macOS system ids from a small, hand-curated set of beta testers (Apple limits us to a max of 100 registered macOS devices, sigh, and lets us reset it only once a year). Then we could distribute the actual dmg that really goes in the app store.

The downside of that option is we run into the "multiple installed apps using the same NetworkExtension name" problem. This will definitely happen, because users will surely install the app store app and the beta app at the same time. Or, you could have two or more betas downloaded onto the filesystem at the same time.

I don't know if we've tried this, but we might be able to avoid these conflicts by giving the NetworkExtension a different name (eg. including the version number) every time we make a build.

Oh, yet another option we considered is to upload a different variation of the app to the app store (call it "Tailscale leading edge" or something, and hopefully make it unsearchable so people can only find it if they really know they're looking for the beta. That would allow an unlimited number of testers, but we'd have to go through app store review every time. On the up side, we'd get automatic updates pushed by Apple.

As a possible side benefit, people could install both versions at once and (I think) be able to connect to personal and corporate tailscale networks at the same time.

We are using a Raspberry Pi as a relay node to access our office Macs at the moment. Happy to help test any of the above to help get access to the machines directly.

Just adding our use case:
We want to automatically install Tailscale on our new employee laptops through our MDM (in this case, Fleetsmith). Fleetsmith has an app catalog for official apps, but also allows installing custom apps from a .pkg file. Either route would work for us.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

JayDoubleu picture JayDoubleu  路  11Comments

gitirabassi picture gitirabassi  路  5Comments

dg9999cn picture dg9999cn  路  7Comments

cee-dub picture cee-dub  路  6Comments

crawshaw picture crawshaw  路  8Comments