iOS provisioning profiles management with Fastlane Match
Fastlane Match makes certificate and provisioning profile management easier already for many years. Commonly used with a configuration file (often called Fastfile or Matchfile) added to the repository with sources of your application. It seems to be a tool that is very tightly integrated into a project of an app, but that doesn’t have to be true. It is a script with plenty of configuration options and can be used standalone in the command line.
Certificates and provisioning profiles are stored in an encrypted form in the location you choose. Feel free to use a private repository on GitHub or set it up on NAS in your LAN. You can use a local git repository on your development machine too.
General idea
Instead of creating a Matchfile, set a few environment variables before executing
fastlane match
. I like to set all the account-specific attributes like this so that the command I execute has the shortest possible form.
Let’s generate a wildcard development provisioning profile:
1 2 3 4 5 |
export FASTLANE_USER="apple.developer@example.com" export FASTLANE_PASSWORD="ICLOUD_ACCOUNT_PASSWORD" export MATCH_PASSWORD="REPOSITORY_ENCRYPTION_PASSPHRASE" export MATCH_GIT_URL="git@github.com:example/apple-certificates.git" fastlane match development --app_identifier "com.example.*" |
If the repository is empty, a new development certificate will be created before generating the provisioning profile. Fastlane Match will never reuse a certificate that you have manually created.
Let’s create two more provisioning profiles with the specific application identifier. In the same shell session execute:
1 2 |
fastlane match development --app_identifier "com.example.helloworld" fastlane match appstore --app_identifier "com.example.helloworld" |
The first command will generate and download a new provisioning profile. The second one will create a distribution certificate first, following with a provisioning profile that will let you submit the app to the AppStore.
Way less effort than doing it in GUI, but in this form, it might not be convincing enough.
Advanced setup
When managing certificates and profiles from multiple users, where each one belongs to multiple Apple Developer Organizations, I find it useful to set up environment variables with shell scripts. Here is an example:
apple_developer_at_example.sh
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
#!/usr/bin/env bash export FASTLANE_USER="apple.developer@example.com" export FASTLANE_PASSWORD="ICLOUD_ACCOUNT_PASSWORD" export MATCH_PASSWORD="REPOSITORY_ENCRYPTION_PASSPHRASE" export MATCH_GIT_URL="git@github.com:example/apple-certificates.git" # You should define developer team id if the user belongs to more then # one organization. export FASTLANE_TEAM_ID="ABCDE12345" # On my machine, each account has its own keychain. In order not to use login # keychain for all of them set the following variables. # (this is not necessary, just keeps your keychain clean) export MATCH_KEYCHAIN_NAME="developer_x_at_example.keychain" export MATCH_KEYCHAIN_PASSWORD="KEYCHAIN_PASSWORD" # You can use a single repository for storing certificates and profiles # from different users, define a specific branch for each one of them. export MATCH_GIT_BRANCH="$FASTLANE_TEAM_ID/$FASTLANE_USER" |
With this script, things start to become more simple. Generating provisioning profiles as we did before, will now look like this:
1 2 |
source apple_developer_at_example.sh fastlane match development --app_identifier "com.example.*" |
You can have a configuration script for each user – organization combination.
Hide the secrets
It is not a good idea to keep your iCloud account and encryption key stored as a plain text in a bash script. If you use a password manager which has a command-line interface, you can use it in your script. Define passwords like this:
Example using pass:
1 2 |
export FASTLANE_PASSWORD="$(pass show appleid/apple.developer@example.com)" export MATCH_PASSWORD="$(pass show match/apple-certificates)" |
Expired certificate or profile
When provisioning profile expires after twelve months, you can renew it with the same command:
1 |
fastlane match development --app_identifier "com.example.*" |
If you need to regenerate it when it is still valid (for example, because you registered new devices in developer portal), use --force
flag.
1 |
fastlane match development --force --app_identifier "com.example.*" |
If your certificate expires, you have to remove it from the repository manually.
1 2 3 4 5 6 7 |
git clone git@github.com:example/apple-certificates.git cd apple-certificates git rm -r certs/development profiles/development git commit -m 'Remove expired development certificate and profiles' git push origin cd .. rm -Rf apple-certificates |
Next time when you try to generate a new profile, a certificate will be created automatically.
Using with Continous Integration services
Fastlane Match is also handy in CI. Since you only want to retrieve certificate and profile from the repository, you can use --readonly
flag.
1 |
fastlane match appstore --readonly --app_identifier "com.example.helloworld" |
It would not try to connect to the Apple Developer portal in case a profile or certificate expired. There is no need to define FASTLANE_PASSWORD
variable (password to your iCloud account) because of that.
Generated certificates are stored in the keychain, and provisioning profiles are copied to ~/Library/MobileDevice/Provisioning Profiles/. These are locations where Xcode is looking for them, so you don’t need to take any additional steps for your app to be signed.
Your development machine
If you use Fastlane Match as I describe, Xcode does not need to have access to your Apple Developer account; you can safely remove it from the “Accounts” tab in Xcode Preferences. It will also prevent Xcode from trying to automatically generate certificates and “Xcode managed provisioning profile”. If you’re using the Fastlane Match tool, you don’t want Xcode to do this.
Security concerns
If you keep your encryption passphrase secure, belive in cryptography, and trust AES, you should not be concerned about security even if you use a public repository. A potential attacker in possession of profiles and keys cannot do any harm without compromising other secrets, like your iCloud password or access to the software you use for distributing apps in your organization. If you’re interested in potential security issues analysis, I recommend “Is it secure?” section on Codesigning Guide.
Manual decryption
When in need to inspect the contents of the repository, you can decrypt files with openssl
. Use it with the following parameters:
1 |
openssl aes-256-cbc -k "${MATCH_PASSWORD}" -in "<fileYouWantToDecryptPath>" -out "<decryptedFilePath>" -a -d |
Always keep it up to date
When executing tasks, the tool logs into the Apple Developer portal and AppStore Connect on your behalf. It is important to keep Fastlane up-to-date because APIs of those services change from time to time. You might expect it not to work correctly if not using the latest available version.
Summary
As an iOS developer on the Tabris team, I find myself saving a lot of time when using Match. There are plenty of actions I would have to do in GUI and on the Apple Developer portal when maintaining certificates and keys. After getting familiar with this tool, you can set up a new certificate and plenty of profiles in seconds.
Feedback is welcome!
Want to join the discussion?Feel free to contribute!