# 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:

```sh
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:

```sh
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:

```sh
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 directly from the
decrypted `SPARKLE_EDDSA_KEY_PATH` release seed so the appcast contains the
standard full-archive EdDSA signature Sparkle verifies.

The public EdDSA key embedded in macOS release builds is:

```text
uugZuJeqvvKd91NZ6F1Fv2cQenUbIG/ZW3L9MuaEz30=
```

The appcast signer is:

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

## Run

```sh
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:

```sh
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:

```sh
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.
