At the beginning of the pandemic I wrote a tool called OnAir that changes the color of a Philips Hue light when you’re on some sort of audio or video call. It’s a helpful signal to your loved ones, roommates, or kids that you’re stuck in Videoconferencing Hell1.
It’s a neat tool, but I’m pretty sure I’m still the only user. That makes sense: some assembly is required, it requires Micro Snitch, and I don’t have a UI to go with it. But also: I didn’t have a package download for it; adventurous users had to download a Golang toolchain to build OnAir. Making someone download a build toolchain to use your app is mean in the year 2020 I’m told, so I wanted to fix that.
While reading about how the code signature and notarization processes work on macOS, somehow I came across Little Snitch’s Internet Access Policies and thought it’d be cool to include one. The tl;dr on IAPs are: you can compile your application with some added context about what calls it makes out to the Internet, and that context will show up when Little Snitch prompts you about a connection your application is making. It looks like this:
Little Snitch tells you that OnAir wants to connect to
discovery.meethue.com on port
443. It also
mentions what OnAir does (“OnAir changes the color of IP-based lighting when you are on a audio or video
call”), a link to the Github page, and a specific statement about connections to the
meethue.com domain. In
the image above, it reads “OnAir uses the Hue Broker Server to discover Hue Bridge devices on your network. If
denied, OnAir will not be able to find the Hue Bridge device on your network.”
Useful security information is useful. More applications should probably have useful information. Except, getting that useful information into a single binary when you’re not using Xcode is trickier than I anticipated. So how do? Enough of the recipe intro: here’s how to do the thing.
Building Your Personal Plist Pizza
An Info.plist file has attributes about the application. You’ll need to make one.
There are tools that can help with this like PlistEdit Pro. In this case “it’s just xml” so you’d be able to use what I threw together as a template for your project. PlistEdit Pro was nice to use, though.
If you’re wondering what some of the CoreFoundation (CF) keys and values are, Apple’s Core Foundation Keys page has the answer. Though, you probably only need the ones listed in the Code Signing Tasks doc.
We’re here to talk about IAPs, though: so take a look at the specification on Little Snitch’s
Research Assistant developer documentation. To summarize,
you’ll need to add a dictionary entry for each endpoint your application connects to. The text that shows up for the
IAP is put into the
You can also reference a string that is localized, which is a nice thing to do in general. The OnAir Info.plist does this - though only for English today2.
The One Weird Linker Trick
OK, so you’ve made your XML. How can you build it into your application?
Info.plist lives as inside of an app bundle. However, it is also possible for the data to live as
a text segment in a Mach-O binary. We’re going to do that with the power of some linker flags.
- Add something like
-ldflags='-extldflags "-sectcreate __TEXT __info_plist $(shell pwd)/Info.plist"'to your
go buildcommand. I copied this from my Makefile, you may need to do something special to get the full path to the file.
- Toss an
import "C"into your application’s imports.
import "C" thing is important, even if you’re not doing Cgo things in your application. If you don’t import
ldflags component will be ignored, and you won’t see your IAP.
You can confirm that the text segment made it in to the binary by running
otool -s __TEXT __info_plist ./path/to/your/binary. Convert it to human-readable text by piping the output to
I talk about this in the context of a Golang application - but this is a linker flag. I imagine you can pass the
flag to whatever your build toolchain uses and you’ll get that
License to Distribute
Little Snitch will only display an IAP for a signed application, so get your credit card out. Apple’s Developer Program costs $99 a year, but: you get unlimited certificates! This is an unverified claim, but I have at least two (2) certificates now, which feels like a lot for me.
After your account is approved (it took ~10 min for me on a Saturday morning), open up Xcode, go to Preferences, pop into
the Accounts tab, and add your Apple ID. Then, click
Manage Certificates... and generate you a
Developer ID Application
certificate, and possibly a
Developer ID Installer certificate. These will be saved directly into your keychain, though
you should export them with a secure password and put them somewhere safe.
I should note that the certificate generation step is different if you have an enterprise account; I think you’ll need to send
a Certificate Signing Request (CSR) to the person who manages your account. I gleaned this from googling, so at least something
has been written about it if you find yourself lost at this point. You’ll probably need the
--acr-provider option with
Since you probably have MFA turned on for your Apple ID, you’ll need an “Application Password” which you can make
over on the (Apple ID Management Portal](https://appleid.apple.com). Kinda funny that the developer tools don’t support
native MFA, but security is hard. Anyways, once you’ve got that one-time password, get it into your keychain under
appleid by running:
xcrun altool --store-password-in-keychain-item appleid -u email@example.com -p asdf-asdf-asdf-asdf
Signing Your Binary
security find-identity -v and get the hash for your
Developer ID Application certificate. Sign your binary
codesign -s DEV_APP_HASH_HERE -f -v --timestamp --options runtime BINARY_PATH_HERE. Congratulations: you made a
signed binary on macOS.
At this point, if you delete the Little Snitch rules that exist for your program and then run it, you’ll probably see the words that you wrote in the Little Snitch alert dialog box. Congrats!
Notarizing Your Binary
That’s fine and good, but let’s say you want to distribute your application. Apple’s notarization process seems to be a very quick static analysis pass on your binary to make sure that you’re not distributing obvious malware. So I tried to zip up my binary and ship it off to Apple… without much luck. It worked once, at least?
So instead I built a package. I moved my signed binary into an empty folder creatively named
package/ and made an
installer with this pkgbuild:
pkgbuild --root package --identifier com.example.yourapp.dontusethis.makeyourown --version $(VERSION:v%=%) --install-location /usr/local/bin onair-$(VERSION)-raw.pkg
Signed the package with my developer installer key (grab that hash using
security find-identity -v):
productsign -s DEV_INSTALL_HASH_HERE --timestamp onair-$(VERSION)-raw.pkg onair-$(VERSION).pkg
And submitted for notarization 3
xcrun altool --notarize-app --primary-bundle-id com.example.yourapp.dontusethis.makeyourown --username 'firstname.lastname@example.org' --password "@keychain:appleid" --file onair-$(VERSION).pkg
Notarization takes a little bit of time: generally <5 min, but could be longer than an hour, too. Your request will get a UUID, which you can use to check on the status of notarization, or you can wait for the email. For the impatient, keep asking Apple for an update using this command:
xcrun altool --notarization-info UUID_HERE -u 'email@example.com' --password "@keychain:appleid"
If it passes, then staple your notarization to your package (
xcrun stapler staple onair-$(VERSION).pkg) and
distribute. I just uplodaded my package as part of a Github release.
Embedded Data? In A Binary?
Well, that’s about it. If you’re building a tool for users who might use Little Snitch, this is a nice way to give additional context on what your app is connecting to. Maybe there are other things you can put in this header that get parsed by the OS too! 😜