Skip to content

feat(bundle): KMS-backed signing for CI/CD environments without OIDC #407

Description

@mchmarny

Problem

CI environments without OIDC token support—such as Jenkins, TeamCity, or internal pipelines—cannot attest bundles using keyless (Fulcio) signing. There is currently no way to sign bundles with a KMS-backed key.

Proposal

Add a --signing-key flag to aicr bundle that accepts a KMS URI for cosign-compatible key-based signing. This ensures key material never touches disk.

Supported Backends

All backends natively supported by cosign via URI scheme:

Backend URI Scheme
AWS KMS awskms://arn:aws:kms:...
GCP KMS gcpkms://projects/...
Azure Key Vault azurekms://...
HashiCorp Vault hashivault://...

Behavior

  • --signing-key <kms-uri> triggers KMS-backed signing instead of keyless OIDC flow
  • The URI resolves to the appropriate KMS provider at runtime
  • The resulting signature bundle uses the same format as keyless signing (cosign bundle)
  • Verification works with the corresponding public key (cosign verify --key <kms-uri>)

Mutual Exclusivity

--signing-key is mutually exclusive with keyless OIDC signing flags (--identity-token, --oidc-device-flow). Providing both is a validation error.

Implementation Notes

Updated 2026-06-02: signing has migrated off cosign's Go library to sigstore-go's sign package; the original notes (which referenced cosign.LoadKeyFromKMS() and "no new dependencies") no longer reflect the code.

  • The signing path is now pkg/bundler/attestation/signing.go (SignStatement), which uses sigstore-go's sign package (github.com/sigstore/sigstore-go/pkg/sign) for the keyless Fulcio + Rekor flow — not cosign's Go SignerVerifier API.
  • KMS-backed signing means providing a key-based signer in place of the keyless Fulcio/Rekor path. With sigstore-go this is a keypair/signer abstraction; the most direct route is a cosign KMS SignerVerifier adapter (sigstore/cosign/v2/pkg/signature + the cloud KMS provider packages). This will pull in new dependencies — the earlier "no new dependencies required" claim is no longer accurate.
  • The branch point is the attester resolver (attestation.ResolveAttesterLazy in pkg/bundler/attestation/resolver.go) plus SignStatement: select key-based vs keyless based on whether --signing-key is set, rather than the pkg/evidence/ / pkg/bundler/ location guessed originally.
  • There is no aicr bundle-sign subcommand; signing is folded into aicr bundle --attest.

Relationship to other attestation work

This is the foundation for air-gapped signing (#409) — keyless signing requires Fulcio network access, so offline attestation needs a key-based path. Tracked under the attestation epic.

Success Criteria

  • aicr bundle --signing-key gcpkms://projects/my-proj/locations/global/keyRings/kr/cryptoKeys/k produces a valid signed bundle
  • Signature is verifiable with cosign verify --key <same-kms-uri>
  • Works in CI without OIDC token availability
  • make test and make lint pass with no regressions

Metadata

Metadata

Assignees

Fields

No fields configured for Enhancement.

Projects

No projects

Milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions