Wire Apple KMS release signing
Some checks failed
Cache: Publish Nix / Publish Nix Cache (push) Waiting to run
Build Rust / Cargo Test (push) Successful in 11m0s
Build Site / Next.js Build (push) Failing after 5s
Lint Governance / BEP Metadata (push) Successful in 1s

This commit is contained in:
Conrad Kramer 2026-06-07 07:28:48 -07:00
parent ed73d7d16a
commit 126af6b5cf
17 changed files with 1913 additions and 16 deletions

View file

@ -47,7 +47,7 @@ The same change also makes the forge itself the release authority: release tags
- `//bazel/apple:release_macos_stamp`
These targets wrap the existing Xcode project and give the workflow one stable entrypoint that shares the same Bazel/Nix cache setup while Burrow decides whether deeper `rules_apple` targets are worth the migration.
- Use Namespace cache setup before Bazel/Nix work. Prefer the Namespace Bazel remote cache when `nsc` is authenticated and fall back to a local disk/repository cache when remote setup is unavailable.
- Produce unsigned Apple validation artifacts when signing is absent, but require signing for an App Store requested iOS release. Uploadable artifacts are produced only when signing assets are installed and provisioning profiles can be synced from App Store Connect credentials.
- Produce unsigned Apple validation artifacts when signing is absent, but require signing for an App Store requested iOS release and for a Sparkle tester release. Uploadable artifacts are produced only after provisioning profiles can be synced from App Store Connect credentials and the relevant Apple certificate is proven to match the selected Google KMS key.
- Resolve release credentials through age/agenix before signed Apple lanes run. Forgejo secrets should carry only the runner age identity fallback and the Authentik WIF client secret until OpenBao can mint the runner token directly. App Store Connect keys and signing certificates live as sealed files under `secrets/`.
- Persist the forge runner SSH key as `/var/lib/forgejo-runner-agent/age_keystore` and export `BURROW_RUNNER_AGE_IDENTITY_PATH` so jobs can resolve agenix identities without embedding long-lived material in every workflow.
- Add `Scripts/forgejo-dispatch.sh` and `Scripts/forgejo-dispatch-via-host.sh`:
@ -58,7 +58,7 @@ The same change also makes the forge itself the release authority: release tags
- Keep the canonical Forgejo repo as an ordinary source repository, not a mirror. The host config disables new mirror creation and removes mirror/push-mirror rows for the canonical Burrow repository at activation time.
- Add `.forgejo/workflows/infra-opentofu.yml` and `Scripts/grafana-tofu.sh` for Grafana API-managed folders and dashboards. NixOS owns the Grafana service, SSO, reverse proxy, datasources, Prometheus, OpenTelemetry collector, Jaeger, secrets, and boot-time configuration; OpenTofu owns API-created dashboard objects.
- Sync provisioning profiles from App Store Connect in CI using the decrypted API key:
- iOS App Store profiles for `com.hackclub.burrow` and `com.hackclub.burrow.network`.
- iOS App Store profiles for `net.burrow.app` and `net.burrow.app.network`.
- macOS Developer ID profiles for the same bundle identifiers.
Provisioning profile `.age` files are not part of the normal release path.
- Add a non-exportable Google KMS RSA-2048/SHA-256 key named
@ -85,6 +85,16 @@ The same change also makes the forge itself the release authority: release tags
sync must verify App Store profiles reference that certificate. Stale profiles
with the same name may be deleted and recreated so CI does not silently sign
against an older distribution identity.
- Apple release signing uses a staged KMS flow because Google publishes the
Cloud KMS PKCS#11 library for Linux release runners, not macOS runners:
- macOS Namespace builders compile unsigned iOS device and macOS app bundles
with the existing Xcode project.
- Linux release runners download those staged artifacts, authenticate to
Google through Authentik WIF, verify the Apple `.cer` public key matches the
intended Google KMS key version, embed the synced provisioning profiles, and
sign the app/extension bundles with the patched `rcodesign` PKCS#11 path.
- The signed iOS IPA is uploaded to App Store Connect, and the signed macOS
ZIP is used as the Sparkle tester artifact.
- Add a non-exportable Google KMS key named `sparkle-ed25519` for Sparkle
appcast signatures. It uses `EC_SIGN_ED25519` with software protection level
because Google KMS rejects Ed25519 for HSM protection. macOS builds embed
@ -97,7 +107,7 @@ The same change also makes the forge itself the release authority: release tags
- Treat Sparkle as a macOS public-channel upload lane:
- Generate staged appcast content from the signed macOS artifact when present.
- Publish `sparkle/<channel>/`, `sparkle/appcast.xml`, and `sparkle/default/` pointers from `publish-store-uploads`.
- Full Sparkle client integration remains a follow-up because the current Apple app does not embed Sparkle.
- The macOS app embeds Sparkle and consumes the `https://releases.burrow.net/sparkle/appcast.xml` feed.
- Add `infra/releases` as the OpenTofu boundary for the GCS release and package repository backup buckets. Garage is the required S3-compatible primary distribution target after host bootstrap. `infra/identity` remains the boundary for Google KMS signing keys, the Authentik WIF pool/provider, and the runner service account.
## Security and Operational Considerations
@ -110,8 +120,13 @@ The same change also makes the forge itself the release authority: release tags
- rclone is not part of the release path. Multi-cloud failover should be added through provider-specific or S3-compatible wrappers with explicit credentials and evidence.
- Required upload credentials are checked explicitly after `./.forgejo/actions/decrypt-release-secrets` materializes them from age:
- `secrets/apple/appstore-connect.env.age`: `ASC_API_KEY_ID`, `ASC_API_ISSUER_ID`, and base64-encoded `.p8` key material.
- `secrets/apple/distribution-signing.env.age`: base64-encoded distribution `.p12` plus its password.
- `secrets/apple/sparkle.env.age`: base64-encoded Sparkle EdDSA private key.
- KMS-backed Apple signing must not use `.p12` private-key material. The
legacy `secrets/apple/distribution-signing.env.age` path remains accepted
only for emergency keychain signing and should not be used for the normal
tester release lane.
- Sparkle appcasts are signed with Google KMS by default; the legacy
`secrets/apple/sparkle.env.age` path remains accepted only for emergency
local EdDSA signing.
- future Microsoft Partner Center credentials
- The temporary CI keychain password is always the distribution certificate password; there is no separate keychain-password release secret.
- KMS-backed Apple certificates do not satisfy the existing Xcode/keychain