docs: update Apple docs for standalone guidance (#7589)

Updated the swift/apple README with new added tips for making release
builds from your local machine.

refs #7581

---------

Signed-off-by: Jamil <jamilbk@users.noreply.github.com>
Co-authored-by: Thomas Eizinger <thomas@eizinger.io>
This commit is contained in:
Jamil
2024-12-29 13:36:30 -08:00
committed by GitHub
parent f82478b877
commit 1c2c350b8f
3 changed files with 105 additions and 71 deletions

View File

@@ -7,7 +7,7 @@
"proseWrap": "preserve",
"overrides": [
{
"files": ["website/**/*.md", "website/**/*.mdx"],
"files": ["swift/apple/*.md", "website/**/*.md", "website/**/*.mdx"],
"options": {
"proseWrap": "always"
}

View File

@@ -2,8 +2,12 @@
Firezone clients for macOS and iOS.
## Pre-requisites
This document is intended as a reference for developers working on the Apple
clients.
## Prerequisites
1. Ensure you have the latest stable version of Xcode installed and selected.
1. Rust: `curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh`
1. Request your Firezone email added to our Apple Developer Account
1. Open Xcode, go to Settings -> Account and log in.
@@ -11,19 +15,23 @@ Firezone clients for macOS and iOS.
If you're working on the macOS client, you'll need to disable SIP and enable
system extension development mode:
1. Follow [these instructions](https://developer.apple.com/documentation/security/disabling-and-enabling-system-integrity-protection) to disable SIP.
1. Follow
[these instructions](https://developer.apple.com/documentation/security/disabling-and-enabling-system-integrity-protection)
to disable SIP.
1. After that's complete, turn on system extension development mode:
```bash
systemextensionsctl developer on
```
This will prevent macOS from blocking the Network Extension from loading due to notarization or filepath restrictions.
This will prevent macOS from blocking the Network Extension from loading due to
notarization or filepath restrictions.
**Be sure to re-enable SIP to test the app in a production-like environment.**
You may consider using a macOS VM (such as Parallels Desktop) to develop the macOS client, as it's easier to
disable SIP, take snapshots, and muck around with your system configuration without risking your main machine.
You may consider using a macOS VM (such as Parallels Desktop) to test the
standalone macOS client, as it can be easier to test different macOS versions
and configurations without risking your main machine.
## Building
@@ -49,54 +57,54 @@ disable SIP, take snapshots, and muck around with your system configuration with
cd swift/apple
```
1. Copy an appropriate xcconfig:
If building for Development, the debug.xcconfig should work out-of-the-box.
```bash
cp Firezone/xcconfig/debug.xcconfig Firezone/xcconfig/config.xcconfig
vim Firezone/xcconfig/config.xcconfig
```
1. Open project in Xcode:
```bash
open Firezone.xcodeproj
```
```bash
open Firezone.xcodeproj
```
1. Build and run the `Firezone` target.
### Making macOS standalone release builds for local testing
**Note**: To test the iOS app, you'll need a physical iOS device such as an
iPhone or iPad. Network Extensions can't be debugged in the iOS simulator.
For the macOS standalone app, it's a good idea to smoke test release builds of the app
whenever anything related to the app's packaging or notarization changes. This
is because standalone binaries on macOS don't go through the App Store submission
and distribution process which typically catches any packaging issues. Standalone binaries
are subject to Gatekeeper restrictions which means they can build and run just fine in
Development but fail to run successfully on another user's machine.
### Making release builds for local testing
To build a standalone release binary:
1. Install the needed signing certificates to your keychain by exporting them
from 1Password and double-clicking them to install. Contact a team member if
you need access. Once installed, you should see the distribution, developer
ID, and installer certificates in your keychain:
1. You'll need the developer ID certificate (including private key) we use to sign the app. Developer ID certificates
are not compatible with "Automatically manage signing" in Xcode, so you need to download this certificate from 1Password and
install it into your local keychain. The certificate stored in the Apple Developer Portal does not include the private key.
1. Go to https://developer.apple.com/account/resources/profiles/list.
1. Download both of the "Developer ID Application" provisioning profiles (one each for the App and Network Extension).
1. Copy the `standalone` release xcconfig:
```bash
> security find-identity -v -p codesigning
```bash
cp Firezone/xcconfig/standalone.xcconfig Firezone/xcconfig/config.xcconfig
```
...
6) A6815986DDB2A0FA999DA89F04E4F6E0B3ACD724 "Apple Distribution: Firezone, Inc. (47R2M6779T)"
7) 281CCA77645E0399F9E80D6190D8F412EE7BA871 "3rd Party Mac Developer Installer: Firezone, Inc. (47R2M6779T)"
8) 8BA4CA21B9737F37397253A6AA483196033ABAE2 "Developer ID Application: Firezone, Inc. (47R2M6779T)"
8 valid identities found
```
1. Open Xcode, drag the provisioning profiles onto the Xcode app icon in the Dock to install them.
1. In Xcode, ensure the `Firezone` scheme is selected, then go to `Product -> Archive`. This will build the app and open the Organizer window.
1. In the Organizer window, select the latest build and click `Distribute App`.
1. Choose `Direct Distribution` and click `Distribute`.
1. Apple will then sign and notarize the app. Notarization typically takes a minute or two, but can take up to an hour during busy times.
1. Once notarization is complete, Xcode will notify you that the app is ready to distribute. Click `Export` and save the app to the `/Applications` folder. macOS will not allow system extensions to be activated unless they are in `/Applications`.
1. Launch the app from `/Applications` and ensure it works as expected.
1. Download the provisioning profiles from the Apple Developer Portal and
install them by dragging them onto the Xcode icon in the Dock.
Because it can be a bit of a hassle to ensure your development machine will mimic the behavior of a user's machine, it's a good idea to test standalone builds on a clean macOS VM. Parallels for Mac is a good choice for this.
1. Run the appropriate build script:
```bash
scripts/build/ios-appstore.sh
```
or
```bash
scripts/build/macos-appstore.sh
```
or
```bash
scripts/build/macos-standalone.sh
```
## Debugging
@@ -111,7 +119,16 @@ simulator, so you'll need a physical iOS device to develop the iOS build on.
### NetworkExtension not loading (macOS)
If the tunnel fails to come up after signing in, it can be for a large number of reasons. Here are some of the more common ones:
If the tunnel fails to come up after signing in, it can be for a number of
reasons. Start by checking the system logs for errors -- commonly it is due to
entitlements, signing, notarization, or some other security issue.
One technique is to start a `log stream` in another terminal while replicating
the issue, looking for errors reported by other macOS subsystems hinting at why
the Network Extension failed to load.
If nothing seem obviously wrong, it could be that the Network Extension isn't
loading because of a LaunchAgent issue.
Try clearing your LaunchAgent db:
@@ -128,7 +145,7 @@ reflected when you run/debug, it could be that PluginKit is still launching your
old NetworkExtension. Try this to remove it:
```bash
pluginkit -v -m -D -i <bundle-id>
pluginkit -v -m -D -i dev.firezone.firezone.network-extension
pluginkit -a <path>
pluginkit -r <path>
```
@@ -167,6 +184,7 @@ cd swift/apple
```
rm -rf $HOME/Library/Group\ Containers/47R2M6779T.dev.firezone.firezone/Library/Caches/logs/connlib
sudo rm -rf /private/var/root/Library/Group\ Containers/47R2M6779T.dev.firezone.firezone/Library/Caches/logs/connlib
```
### Clearing the Keychain item
@@ -178,13 +196,14 @@ item is missing. You can remove the keychain item with the following command:
security delete-generic-password -s "dev.firezone.firezone"
```
## Generating new signing certificates and provisioning profiles
## Generating new signing certificates and provisioning profiles for app store distribution
Certs are only good for a year, then you need to generate new ones. Since we use
GitHub CI, we have to use manually-managed signing and provisioning. Here's how
you populate the required GitHub secrets.
App Store distribution certifications are only good for a year, then you need to
generate new ones. Since we use GitHub CI, we must manually manage signing and
provisioning since it's not possible (nor advised) to sign into Xcode from the
GitHub runner CI. Here's how you populate the required GitHub secrets.
**Note**: Be sure to enter these variables for Dependabot as well, otherwise its
**Note**: Be sure to enter these secrets for Dependabot as well, otherwise its
CI runs will fail.
### Certificates
@@ -211,15 +230,20 @@ How to do it:
Certificate Assistant -> Request a Certificate from a Certificate Authority
and follow the prompts. Make sure to select "save to disk" to save the CSR.
1. Upload the CSR to Apple Developer. Download the resulting certificate.
Double-click to install it in Keychain Access.
1. **Important**: Back up the downloaded certificate into 1Password. You will no
longer have have access to its private key (required for signing) if you lose
it.
1. Double-click to install it in Keychain Access.
1. Right-click the cert in Keychain access. Export the certificate, choose p12
file. Make sure to set a password -- this is the
`APPLE_BUILD_CERTIFICATE_P12_PASSWORD`.
1. Convert the p12 file to base64:
```bash
cat cert.p12 | base64
base64 < cert.p12
```
1. Save the base64 output as `APPLE_BUILD_CERTIFICATE_BASE64`.
1. Delete cert.p12 and the cert from Keychain Access once you're sure it's
backed up to 1Password.
Repeat the steps above but choose "Mac Installer certificate" instead of
"distribution certificate" in step 2, and save the resulting base64 and password
@@ -245,22 +269,32 @@ APPLE_MACOS_NE_PROVISIONING_PROFILE
1. Encode to base64 and save each using the secrets names above:
```bash
cat profile.mobileprovision | base64
base64 < profile.mobileprovision
```
1. Now, you need to update the XCConfig to use these. Edit
Firezone/xcconfig/release.xcconfig and update the provisioning profile UUIDs.
The UUID can be found by grepping for them in the provisioning profile files
1. Now, you need to update the build scripts to use these. Edit
`scripts/build/*-appstore.sh` and update the provisioning profile UUIDs. The
UUID can be found by grepping for them in the provisioning profile files
themselves, or just opening them in a text editor and looking halfway down
the file.
1. Now, for iOS only, you need to edit Firezone/ExportOptions.plist and update
the provisioning profile UUIDs there as well.
the file. They should be the only UUIDs in the file.
### Runner keychain password
## Generating new signing certificates and provisioning profiles for standalone distribution
This can be randomly generated. It's only used ephemerally to load the secrets
into the runner's keychain for the build.
The process is much the same as above for the macOS standalone client, with one
important difference: the signing certificate must be a Developer ID Application
certificate, not an Apple Distribution certificate. **DO NOT GENERATE A NEW
CERTIFICATE UNLESS THE OLD ONE HAS EXPIRED OR IS LOST.** Developer ID
Application certificates are **precious** and we only have a limited number of
them. They also cannot be revoked. So do not generate them. Instead, obtain it
from 1Password.
Once you've done that, you can create the provisioning profiles and update the
GitHub secrets using the same steps as above, only using the following secrets
names:
```
APPLE_RUNNER_KEYCHAIN_PASSWORD
APPLE_STANDALONE_BUILD_CERTIFICATE_BASE64
APPLE_STANDALONE_BUILD_CERTIFICATE_P12_PASSWORD
APPLE_STANDALONE_MACOS_APP_PROVISIONING_PROFILE
APPLE_STANDALONE_MACOS_NE_PROVISIONING_PROFILE
```

View File

@@ -12,14 +12,14 @@ by advanced users and admins.
## Log directory locations
| Component | Log directory |
| ----------------------- | ------------------------------------------------------------------------------------------------------------------------------- |
| macOS Client | `~/Library/Group Containers/47R2M6779T.dev.firezone.firezone/Library/Caches/logs` |
| iOS Client | N/A |
| Android/ChromeOS Client | `/data/data/dev.firezone.android/caches/logs` |
| Linux Client | Set by the user via the `LOG_DIR` environment variable, otherwise STDOUT |
| Windows Client | `%APPDATA%\Local\dev.firezone.client\data\logs` |
| Gateway | Logs are written to STDOUT by default and viewable either by `docker logs firezone-gateway` or `journalctl -u firezone-gateway` |
| Component | Log directory |
| ----------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| macOS Client | `~/Library/Group Containers/47R2M6779T.dev.firezone.firezone/Library/Caches/logs` and `/private/var/root/Library/Group Containers/47R2M6779T.dev.firezone.firezone/Library/Caches/logs |
| iOS Client | N/A |
| Android/ChromeOS Client | `/data/data/dev.firezone.android/caches/logs` |
| Linux Client | Set by the user via the `LOG_DIR` environment variable, otherwise STDOUT |
| Windows Client | `%APPDATA%\Local\dev.firezone.client\data\logs` |
| Gateway | Logs are written to STDOUT by default and viewable either by `docker logs firezone-gateway` or `journalctl -u firezone-gateway` |
## Exporting Client logs