Package Signing
Zarf supports cryptographic signing and verification of packages using Cosign. Two signing modes are supported: key-based signing using a local private key or cloud KMS, and keyless signing using a Sigstore OIDC identity (no private key required). Package signing provides:
- Authenticity: Verify that a package comes from a trusted source
- Integrity: Ensure the package has not been modified or corrupted
- Non-repudiation: Prove who signed the package and when
Zarf signs the zarf.yaml file within a package, which contains metadata and checksums for all package contents. This allows verification of the entire package through a single signature.
Packages are signed using the Sigstore bundle format, stored as zarf.bundle.sig within the package. This format provides:
- Offline Verification: Bundles include all verification materials in a single file
- Standardized Format: Based on the widely-adopted Sigstore specification
- Better interoperability: Compatible with other Sigstore tooling and services
The bundle format follows the Sigstore Bundle Specification.
The most common approach uses local private/public key pairs:
# Generate a key pair (prompts for password)zarf tools gen-key
# Creates:# - cosign.key (private key - keep this secure!)# - cosign.pub (public key - share for verification)For production environments, cloud-based KMS provides enhanced security and key management:
zarf package sign zarf-package-example-amd64.tar.zst \ --signing-key awskms://alias/my-signing-keyRequires AWS credentials configured and appropriate IAM permissions. The KMS key must be an asymmetric signing key.
zarf package sign zarf-package-example-amd64.tar.zst \ --signing-key gcpkms://projects/PROJECT/locations/LOCATION/keyRings/RING/cryptoKeys/KEYRequires gcloud authentication and appropriate IAM roles. The key must be an asymmetric signing key.
zarf package sign zarf-package-example-amd64.tar.zst \ --signing-key azurekms://VAULT_NAME.vault.azure.net/keys/KEY_NAME/KEY_VERSIONRequires Azure CLI authentication and appropriate access policies. The key must be an asymmetric signing key.
zarf package sign zarf-package-example-amd64.tar.zst \ --signing-key hashivault://KEY_NAMERequires Vault token authentication and appropriate policies. The transit key must be an asymmetric signing key.
Keyless signing uses Sigstore’s Fulcio certificate authority to issue a short-lived signing certificate bound to your OIDC identity (GitHub Actions workflow, Google account, etc.).
When to use keyless signing:
- CI/CD pipelines (GitHub Actions, GitLab CI) where managing a private key is impractical
- Signing official release artifacts with an identity pinned to the specific workflow that ran
- Organizations that want to eliminate private key management entirely
The command is generally the same whether running interactively or in GitHub Actions — zarf detects the OIDC environment automatically:
zarf package sign zarf-package-example-amd64.tar.zst --keyless --confirmThe --confirm flag skips the prompt before uploading to the Rekor transparency log. --tlog-upload is automatically enabled with --keyless. When run interactively, the command opens a browser for OIDC login. In GitHub Actions, zarf uses the ambient OIDC token — ensure the workflow job has id-token: write permission:
permissions: id-token: write contents: readOther non-interactive environments where OIDC auto-detection does not apply, supply a pre-acquired token with --identity-token. The flag accepts either a raw token value or a path to a file containing one:
# Pass the token value directly (register it as a masked secret in your CI platform)zarf package sign zarf-package-example-amd64.tar.zst \ --keyless \ --identity-token "$MY_OIDC_TOKEN" \ --confirm
# Or pass a path to a file containing the token (e.g., written by a prior step or secret manager)zarf package sign zarf-package-example-amd64.tar.zst \ --keyless \ --identity-token /path/to/oidc-token \ --confirmEnsure the token value is registered as a masked secret in your CI platform so it is redacted from log output.
Automatically sign a package during creation:
zarf package create . --signing-key cosign.key --signing-key-pass <password>The signature is embedded in the package archive during the build process.
Sign a package after it has been created:
zarf package sign zarf-package-example-amd64.tar.zst --signing-key cosign.keyThe signature is added to the package archive, and the zarf.yaml is updated to indicate it is signed.
Replace an existing signature (useful for key rotation):
zarf package sign zarf-package-example-amd64.tar.zst \ --signing-key new-cosign.key \ --overwriteThe --overwrite flag is required when a signature already exists.
Zarf supports signing packages stored in OCI registries:
# Sign a package from OCI and output to local directoryzarf package sign oci://ghcr.io/my-org/my-package:1.0.0 \ --signing-key cosign.key \ --output ./signed/
# Sign a package and publish directly to OCI registryzarf package sign zarf-package-example-amd64.tar.zst \ --signing-key cosign.key \ --output oci://ghcr.io/my-org/signed-packages
# Sign a package from OCI and re-publish to OCI (in place)zarf package sign oci://ghcr.io/my-org/my-package:1.0.0 \ --signing-key cosign.keyKey-based signing — verify using the public key:
zarf package verify zarf-package-example-amd64.tar.zst --key cosign.pubKeyless signing — verify using the expected OIDC identity of the signer. Use --certificate-identity for an exact match or --certificate-identity-regexp to match a pattern (useful when the identity encodes a workflow path and ref):
# Exact identity match (e.g., a service account email)zarf package verify zarf-package-example-amd64.tar.zst \ --certificate-identity "signer@example.com" \ --certificate-oidc-issuer "https://accounts.google.com"
# Regexp match — pins the identity to a specific workflow and ref typezarf package verify zarf-package-example-amd64.tar.zst \ --certificate-identity-regexp "https://github.com/my-org/my-repo/.github/workflows/release.yml@refs/tags/" \ --certificate-oidc-issuer "https://token.actions.githubusercontent.com"Using --certificate-identity-regexp prevents accepting packages signed by an unrelated pipeline in the same repository. For the official Zarf init package (signed during the release workflow):
zarf package verify zarf-init-amd64-vX.Y.Z.tar.zst \ --certificate-identity-regexp "https://github.com/zarf-dev/zarf/.github/workflows/release.yml@refs/tags/" \ --certificate-oidc-issuer "https://token.actions.githubusercontent.com"Successful verification output:
2025-11-15 14:17:13 INF checksum verification status=PASSEDVerified OK2025-11-15 14:17:16 INF signature verification status=PASSED2025-11-15 14:17:16 INF verification complete status=SUCCESSIf verification fails, the command exits with a non-zero status code and displays an error message.
Embedded TrustedRoot and Offline Verification
Section titled “Embedded TrustedRoot and Offline Verification”Zarf embeds Sigstore verification material (a TrustedRoot JSON) directly in the binary. This enables zarf package verify to verify keyless-signed packages offline without reaching Sigstore’s TUF infrastructure — no extra flags required.
If you need fresher trust material (e.g., after a Sigstore key rotation), retrieve a new TrustedRoot and pass it in at verify time:
# Retrieve a fresh TrustedRoot from Sigstorezarf tools trusted-root create --with-default-services > trusted-root.json
# Use it during verificationzarf package verify zarf-package-example-amd64.tar.zst \ --trusted-root trusted-root.json \ --certificate-identity-regexp "https://github.com/my-org/my-repo/.github/workflows/release.yml@refs/tags/" \ --certificate-oidc-issuer "https://token.actions.githubusercontent.com"Users running private Sigstore infrastructure can supply their own TrustedRoot the same way via --trusted-root /path/to/custom.json.
Air-gap environments: when a keyless package was signed with --tlog-upload (the default for --keyless), the Rekor inclusion proof is embedded in the bundle. Combined with the embedded TrustedRoot (which includes Rekor’s public key), tlog verification works fully offline — no extra flags required.
If instead the package was signed without tlog (using --tsa-server-url --tlog-upload=false), the bundle contains a TSA timestamp instead of a tlog entry. When using keyless identity flags, Zarf auto-enables tlog verification — explicitly disable it and enable timestamp verification:
zarf package verify zarf-package-example-amd64.tar.zst \ --certificate-identity "signer@example.com" \ --certificate-oidc-issuer "https://accounts.google.com" \ --insecure-ignore-tlog \ --use-signed-timestampsVerify package integrity without signature verification:
zarf package verify zarf-package-example-amd64.tar.zstThis confirms that package files match their expected checksums but does not verify authenticity or source.
Verify signatures during package deployment:
zarf package deploy zarf-package-example-amd64.tar.zst --key cosign.pub --verifyIf signature verification fails and --verify is specified, Zarf aborts the deployment to prevent deploying potentially compromised packages.
- Protect private keys: Store private keys securely with restricted file system permissions
- Use strong passwords: Encrypt private keys with strong, unique passwords
- Consider KMS or keyless for production: Use cloud KMS for centralized key management, or use keyless signing to eliminate private key management entirely
- Document key locations: Maintain clear documentation of where keys are stored and who has access
- Verify before deployment: Always verify package signatures before deploying to critical environments
- Automate verification: Integrate signature verification into CI/CD pipelines
- Enforce verification: Use
--verifyflag during deployment to make verification mandatory - Distribute public keys securely: Establish a trusted channel for distributing public keys
- Maintain key inventory: Keep records of which keys were used to sign which packages
- Prefer keyless in CI/CD: Keyless signing eliminates private key management risk — use it for automated pipelines
- Pin the workflow identity: Use
--certificate-identity-regexpto restrict verification to packages signed by a specific workflow and ref type, not just any signer in the repository - Use TSA timestamps when anonymity is required: Rekor is a public transparency log — if publishing your signing event is a concern, use
--tsa-server-url --tlog-upload=falseto embed an RFC3161 timestamp instead
- Never commit private keys: Exclude private keys from version control
- Audit signature operations: Log all signing and verification operations
- Validate key sources: Verify public keys come from trusted sources before using them
- Monitor for warnings: Pay attention to deprecation warnings about signature formats
- Plan for key compromise: Have a procedure for responding to compromised signing keys
✖ failed to verify signature: invalid signature when validating ASN.1 encoded signatureSolution: Verify you are using the correct public key for verification.
✖ package is signed but no key was providedSolution: Provide the public key when deploying or inspecting a signed package:
zarf package deploy zarf-package-example-amd64.tar.zst --key cosign.pubERR failed to sign package: failed to sign package: reading key: decrypt: encrypted: decryption failedSolution: Verify the password is correct. Use --signing-key-pass to provide the password:
zarf package sign zarf-package-example-amd64.tar.zst \ --signing-key cosign.key \ --signing-key-pass <correct-password>✖ signature verification failed: invalid signaturePossible causes:
- Wrong public key: The public key doesn’t match the private key used to sign
- Package modified: The package contents have been altered after signing
- Corrupted package: The package file is corrupted or incomplete
- Format mismatch: Using wrong verification method for signature format
Solutions:
- Verify you’re using the correct public key that corresponds to the signing key
- Re-download the package if corruption is suspected
- Check package checksums to verify integrity
- Inspect the package to confirm which signature format it uses
✖ failed to sign package: kms authentication failedSolutions:
- Verify cloud provider credentials are configured correctly
- Ensure the KMS key exists and is accessible
- Verify IAM permissions allow signing operations
- Check that the key is an asymmetric signing key (not encryption key)
- Refer to your KMS provider’s documentation for authentication setup
Keyless Signing: OIDC Browser Flow Fails in CI
Section titled “Keyless Signing: OIDC Browser Flow Fails in CI”Symptom: zarf package sign --keyless hangs or fails trying to open a browser in a CI environment.
Solution: In CI environments that don’t expose OIDC tokens automatically, supply the token directly:
zarf package sign zarf-package-example-amd64.tar.zst \ --keyless \ --identity-token "$MY_OIDC_TOKEN" \ --confirmFor GitHub Actions specifically, ensure id-token: write is set in the job’s permissions block — cosign will acquire the token automatically.
Keyless Verification: Certificate Identity Does Not Match
Section titled “Keyless Verification: Certificate Identity Does Not Match”Symptom: Verification fails with an identity mismatch error even though the package was signed correctly.
Solution: The value passed to --certificate-identity or --certificate-identity-regexp must match what was encoded in the Fulcio certificate at signing time. For GitHub Actions, the identity takes the form https://github.com/<org>/<repo>/.github/workflows/<workflow>.yml@<ref>. Use --certificate-identity-regexp with a partial pattern if the exact ref is variable.
Keyless Verification: Signature No Longer Verifiable (No Tlog Entry)
Section titled “Keyless Verification: Signature No Longer Verifiable (No Tlog Entry)”Symptom: Verification of a keyless-signed package fails after ~10 minutes with a certificate expiry error.
Cause: The package was signed without --tlog-upload, so there is no Rekor inclusion proof to extend verifiability past the Fulcio certificate’s short validity window.
Solution: Re-sign the package with tlog upload enabled (the default when --keyless is set) or with a TSA timestamp (--tsa-server-url).
- zarf package sign - Sign a Zarf package
- zarf package verify - Verify a package signature
- zarf package create - Create a package with optional signing
- zarf tools gen-key - Generate a signing key pair
- zarf tools trusted-root create - Retrieve and manage the Sigstore TrustedRoot