Building a Mac Installer for Gravitl Netclient

To make an installer that will work on a Mac, there’s a LOT of requirements. But to make sure the project succeeds into the future we need to do all of this to make the project useful for ‘normal’ people. This means it has to be easy to install, uninstall, get working and stay working!
You will need-
1. a Mac running recent operating system
5. a ‘Developer ID Application’ signing certificate in Keychain
6. a ‘Developer ID Installer’ certificate in Keychain
7. ‘Packages’ app
1. Download the binaries
1. netclient (pick a version for your chosen platform)
2. wireguard-go
2. Make, re-name and make executable 
On an Intel Mac, netclient-darwin becomes netclient
For wireguard-tools you’ll need to ‘make’ this and rename to wg
You’ll also need to ‘make’ wireguard-go according to the instructions on the website
Example-
git clone https://git.zx2c4.com/wireguard-go
cd wireguard-go
make
then make each binary executable with
chmod +x appname
3. Hardening binaries
Place the executable binary in a folder with a .plist file called ‘binaryname.entitlements’
– this describes the security entitlements that the binary requires to run, and your executable will not work unless hardened
– the ones I made might be completely wrong but they work, I added these-
com.apple.developer.networking.multicast

com.apple.developer.networking.manage-thread-network-credentials
4. Signing binaries
For each of the 3 binaries, do something like-
codesign -s "Developer ID Application: Servicemax (XXXXXXXXX)" --timestamp --options runtime -f --entitlements entitlements.plist --deep YourApp.app
At this point you have 3 signed binaries, they need to be added to an installer. We will use a product called ‘Packages’ to do this
4b. Make a Postinstall Script
This is optional- In this case, we are on a Mac and can name our clients in a repeatable way. This postinstall script will
a. find the computer serial number
b. find the long name of the user
c. use the join command to join the desired network
d. patch the automatically generated launchd for the correct location of the binary

#!/bin/sh

export PATH=/usr/bin:/bin:/usr/sbin:/sbin:/usr/local/bin

# Now let's join our network

# this script is for installing Netmaker client on Macs

# Find machime Serial number

serial=$(/usr/sbin/system_profiler SPHardwareDataType | /usr/bin/awk '/Serial Number/ { print $4; }')

echo $serial

# Next find machine users long name

fullname=$(id -P $(stat -f%Su /dev/console) | cut -d : -f 8)

echo $fullname

# Let's see if it worked

echo joining Netmaker network for $serial $fullname

cd /usr/local/

./bin/netclient join --name "${serial} ${fullname}" -t <token>

echo joined "$serial$fullname" to Netmaker network

# Unload old launchds

launchctl unload /Library/LaunchDaemons/com.gravitl.netclient.plist &>/dev/null

# Patch launchd so it works- this is to cope with the binary location changing from /etc to /usr/local...

sudo sed -i '' 's|/etc/netclient/netclient|/usr/local/bin/netclient|g' /Library/LaunchDaemons/com.gravitl.netclient.plist

# Load new launchd

launchctl load -w /Library/LaunchDaemons/com.gravitl.netclient.plist &>/dev/null

# Need to enable it so it launches at startup?

sudo launchctl enable system/com.gravitl.netclient

echo restarted patched launch daemon
4c. Making a Pre-Install Script
This is optional as well, but I’ve installed and uninstalled netclient so many times that it seems prudent to uninstall properly…
#!/bin/sh

# unload lanchd

launchctl unload /Library/LaunchDaemons/com.gravitl.netclient.plist

# remove launchd

rm -f /Library/LaunchDaemons/com.gravitl.netclient.plist

# remove old bits from Servicemax install

rm /usr/local/bin/netclient

rm /usr/local/bin/wg

rm /usr/local/bin/wireguard-go

# remove receipts

rm /private/var/db/receipts/com.gravitl.pkg.NetclientWireguard.bom

rm /private/var/db/receipts/com.gravitl.pkg.NetclientWireguard.plist

# remove bits from the Netmaker standard install that doesn't work on macOS

rm -rf /private/etc/netclient
5. Putting it all together
Here is a good tutorial on Packages
In the app named ‘Packages’ create a new Project (in Packages this is a ‘Raw Package’)
In ‘Settings’ Add the identifier in reverse domain notation
Add an Apple ID Installer certificate under ‘Project’
In ‘Payload’ go to ‘Hierarchy’ and select ’Show hidden Folders’
Add all 3 binaries to /usr/local/bin
– NOTE- we are putting the binaries here because /etc seems to be a protected location in modern macOS. If you think there’s a better spot, please let us know…
Packages>File>Save
Packages>Build>Build
This will output a signed package to the Build folder in your project folder
6. Notarise the Installer
Use a script to upload the new .pkg and notarise it. I used one kindly provided Armin Briegel here
I’ll try to clean up the script and add here but this could take some time…
(But it’s also possible to do this manually- adding this info in case it’s useful in future, this might be better if not using an installer?)

For notarisation we need to zip the app like this (don’t zip normally, it won’t work)

/usr/bin/ditto -c -k --keepParent YourApp.app YourApp.zip

 

Then upload the app like this (you need to set up an app specific password to use this method)

xcrun altool --notarize-app --primary-bundle-id "<id>" -u "<appleid>" -p "<app-specific password>" --file YourApp.zip
When notified of success, you need to staple the notarisation to the app-
xcrun stapler staple "YourApp.app"
7. Check your work
Download a copy of Suspicious Package and drop your new installer package on it- see any errors? Nope?
Go ahead and distribute you legend!

Recent posts