Keeping Secrets Safer in AutoPkg CI Pipelines
Since joining the Gusto CPE team this past year, I’ve had the opportunity to work directly on the increasingly popular GitHub Actions AutoPkg runner. Gusto open sourced this back in 2020 and has since caught on in other organizations looking to move away from managing physical Mac infrastructure (avoiding the load bearing Mac mini) to ephemeral AutoPkg runs utilizing CI in the cloud. GitHub Actions is a natural choice for teams already using GitHub, and because it is one of the few CI solutions which offers Mac runners out of the box. Recently, while working on a recipe for Okta Verify, I needed a way to include an API password in an input variable without including it directly in a recipe override. In the past, when working with static Mac hardware, I would add secrets to recipe overrides and keep them locally only on the Mac used to run AutoPkg. While still mostly insecure, at least those secrets weren’t also available in a code repo, and less prone to being compromised. With ephemeral CI runs though, this isn’t possible. A secret store which can be referenced at runtime, outside of the repo becomes necessary. Thankfull, my colleagues at Gusto had encountered this before and already had a solution to avoid committing plain text secrets.
GitHub Actions Secrets
GitHub Actions, like many CI products, provides its own encrypted secret store. Azure Pipelines has secret variables, CircleCI has contexts, or you use an external source like HashiCorp Vault. Read more about GitHub’s solution in the docs here. This is where to put API keys, tokens, passwords, and any other sensitive data which shouldn’t be exposed. Secrets do not need to be of a certain naming scheme, but should be descriptive enough to easily reference in a workflow later. For example, the Okta Verify download recipe requires Okta organization ID (
OKTA_ORG_ID), API username (
OKTA_USERNAME), and password (
OKTA_PASSWORD), all of which to varying degrees could be considered secrets.
AutoPkg Environment Variables
Enter a little known feature where system environment variables starting with
AUTOPKG_ are included in AutoPkg runs. All that’s required is those environment variables be named like
Take a look at Gusto’s open source GitHub Actions workflow and you’ll see the Run AutoPkg step already has an
env defined. Most likely other CI solutions have similar support in their workflow syntax. Add lines for whatever secrets are required in your recipe, ensuring the values match the associated input variables exactly.
Overrides and Secrets
Continuing on with Okta Verify as an example, run
autopkg make-override OktaVerify.download to create a recipe override. The resulting file will have empty Okta specific input variables for
OKTA_USERNAME as they are meant to be user supplied. Keep them empty. Since these variables were already defined in the workflow file earlier, and start with
AUTOPKG_, they will be included at runtime. Below is a truncated example.
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> <plist version="1.0"> <dict> <key>Identifier</key> <string>local.download.OktaVerify</string> <key>Input</key> <dict> <key>NAME</key> <string>OktaVerify</string> <key>OKTA_ORG_ID</key> <string></string> <key>OKTA_PASSWORD</key> <string></string> <key>OKTA_USERNAME</key> <string></string> </dict> </dict> </plist>
Now when the recipe is run through GitHub Actions, even with input variables not defined in the override, those values will be read from the runner environment. Plain text secrets successfully avoided. Keep in mind secrets included in CI runs, and AutoPkg in general, still aren’t entirely safe. While this method protects secrets from prying eyes and unauthorized repo access, AutoPkg itself does nothing to secure variables. Depending on verbosity level, log output could include these values. Be sure to take this into account when setting up your CI.