burrow/infra/identity/README.md
Conrad Kramer 1d45016a63
Some checks failed
Build Rust / Cargo Test (push) Successful in 4m1s
Build Site / Next.js Build (push) Failing after 5s
Infra: OpenTofu / OpenTofu (grafana) (push) Successful in 4s
Lint Governance / BEP Metadata (push) Successful in 1s
Use Sparkle signer for full appcasts
2026-06-07 16:13:53 -07:00

5.6 KiB

Burrow Identity KMS and WIF

This OpenTofu stack owns Google Cloud KMS and Workload Identity Federation resources for Burrow identity and release automation.

It may manage:

  • the burrow-identity Google KMS key ring
  • non-exportable KMS signing keys for Authentik, the SAML CA, and release signing
  • dedicated non-exportable KMS signing keys for APT, RPM, pacman, Flatpak, and AUR source release surfaces
  • a non-exportable KMS signing key for the Apple Developer ID Application CSR path (apple-developer-id-application)
  • a non-exportable KMS signing key for the Apple iOS Distribution CSR path (apple-ios-distribution)
  • a non-exportable software KMS signing key for Sparkle Ed25519 appcast signatures (sparkle-ed25519)
  • an Authentik-backed WIF pool/provider for Forgejo runners
  • the runner service account and its narrow IAM binding for the Authentik WIF audience/client value google-wif.burrow.net; human/group grants are opt-in

It must not manage:

  • Authentik private signing key material
  • OpenBao bootstrap tokens
  • store credentials or service-account JSON keys

Forgejo CI should authenticate with Scripts/ci/google-wif-auth.sh. That helper uses Authentik to obtain an OIDC token and lets Google WIF impersonate burrow-forgejo-runner@project-88c23ce9-918a-470a-b33.iam.gserviceaccount.com in project project-88c23ce9-918a-470a-b33.

Apple Developer ID CSR

Scripts/apple/google-kms-csr.py builds a standard PKCS#10 CSR using the public key from projects/project-88c23ce9-918a-470a-b33/locations/global/keyRings/burrow-identity/cryptoKeys/apple-developer-id-application and asks Google KMS to sign the CSR payload. The private key remains non-exportable in KMS.

Forgejo can generate the Apple-uploadable CSR with:

Scripts/forgejo-dispatch.sh apple-developer-id-kms-csr.yml --ref main

Apple currently documents Developer ID certificate creation through the Developer website or Xcode. After downloading the returned .cer, keep it with release artifacts; do not convert this key path into an exportable .p12.

The active certificate created from this path is Apple certificate 9JKN6HXBHC, stored at Apple/Certificates/developer-id-application-9JKN6HXBHC.cer. Its public key matches KMS key version apple-developer-id-application/cryptoKeyVersions/1.

Apple iOS Distribution CSR

The iOS App Store distribution certificate uses the same CSR builder with the apple-ios-distribution KMS key:

Scripts/apple/google-kms-csr.py \
  --kms-key apple-ios-distribution \
  --common-name "Burrow iOS Distribution" \
  --output ios-distribution.certSigningRequest

The App Store Connect API can create the certificate from that CSR:

Scripts/apple/create-asc-certificate.mjs \
  --certificate-type IOS_DISTRIBUTION \
  --csr-file ios-distribution.certSigningRequest \
  --out-dir Apple/Certificates

The active certificate created from this path is Apple certificate 3G42677598, stored at Apple/Certificates/ios-distribution-3G42677598.cer. Its public key matches KMS key version apple-ios-distribution/cryptoKeyVersions/1.

Sparkle Appcast Signing

Sparkle appcasts keep sparkle-ed25519, a Google KMS key with algorithm EC_SIGN_ED25519 and software protection level, for small signing probes and future signing-service work. Google KMS rejects Ed25519 at HSM protection level and also rejects full-size macOS release archives as direct Ed25519 messages. The tester pipeline therefore signs normal Sparkle archives with Sparkle's sign_update and the decrypted SPARKLE_EDDSA_KEY_PATH release secret so the appcast contains the standard full-archive EdDSA signature Sparkle verifies.

The public EdDSA key embedded in macOS release builds is:

Myv9ZNZT6YGKMtMezh52ra4WqaeEKc4VlvVU0evhJeI=

The appcast signer is:

Scripts/sparkle/sign-appcast-kms.py \
  --appcast dist/builds/<build>/sparkle/<channel>/appcast.xml \
  --artifact-dir dist/builds/<build>/sparkle/<channel>

Run

Scripts/identity-tofu.sh init -backend-config=backend.hcl
Scripts/identity-tofu.sh plan -var-file=terraform.tfvars

For local syntax work without remote state:

Scripts/identity-tofu.sh init -backend=false
Scripts/identity-tofu.sh validate

Start with manage_google = false. Import existing resources before enabling management, then turn on one resource group at a time.

Bootstrap Imports

The first live Apple Developer ID pass may need these resources imported before an identity apply can own them:

Scripts/identity-tofu.sh import 'google_kms_key_ring.identity[0]' \
  projects/project-88c23ce9-918a-470a-b33/locations/global/keyRings/burrow-identity

Scripts/identity-tofu.sh import 'google_kms_crypto_key.signing["apple_developer_id_application"]' \
  projects/project-88c23ce9-918a-470a-b33/locations/global/keyRings/burrow-identity/cryptoKeys/apple-developer-id-application

Scripts/identity-tofu.sh import 'google_kms_crypto_key.signing["apple_ios_distribution"]' \
  projects/project-88c23ce9-918a-470a-b33/locations/global/keyRings/burrow-identity/cryptoKeys/apple-ios-distribution

Scripts/identity-tofu.sh import 'google_kms_crypto_key.signing["sparkle_ed25519"]' \
  projects/project-88c23ce9-918a-470a-b33/locations/global/keyRings/burrow-identity/cryptoKeys/sparkle-ed25519

Scripts/identity-tofu.sh import 'google_service_account.runner[0]' \
  projects/project-88c23ce9-918a-470a-b33/serviceAccounts/burrow-forgejo-runner@project-88c23ce9-918a-470a-b33.iam.gserviceaccount.com

Run imports with the same manage_google, key-ring, and service-account variables that will be used for apply. Key-level IAM bindings should be imported after the signing keys and service account exist.