Skip to content

feat(sign): support for keyless signing and offline verification#4891

Open
brandtkeller wants to merge 31 commits into
mainfrom
2805_keyless_signing
Open

feat(sign): support for keyless signing and offline verification#4891
brandtkeller wants to merge 31 commits into
mainfrom
2805_keyless_signing

Conversation

@brandtkeller
Copy link
Copy Markdown
Member

@brandtkeller brandtkeller commented May 11, 2026

Description

Note - Breaking Changes

  • The cosign functionality under src/pkg/utils has been moved to `src/pkg/signing
  • The embedded structs in signing.Sign|VerifyBlobOptions have been updated to align with cosign.

This PR enables the use of connected keyless signing workflows and offline-compatible verification for a keyless signed package via an embedded trusted root.

This uses the previously implemented zarf tools trusted-root create command to refresh the embedded trusted root. Think of this as a mechanism to streamline the UX of enabling offline verification of keyless-signed packages so that users are not required to bring additional verification material by default - but of which they can overwrite as required.

Additionally this PR enables the bundlesignature feature by default. Note that the VersionRequirement for a package with a bundle signature is v0.71.0 which is the version that introduced the ability to load packages with the bundle file. The feature flag gates are retained such that users who cannot use the bundle still have an opportunity to opt-out until we fully remove.

One early improvement is the UX of retrieving the keyless-signed identity and issuer. These are retrieved on package sign so that we can log the entries that package creators need to provide to those who want to verify - but additionally I believe we could add a zarf package inspect signature <package> command to make this discoverable. We can't place it directly in the build data because it is only accessible post-signing - whereby the zarf definition can no longer be modified without invalidating the signature.

Best practices documentation to follow.

Try it Yourself

# Build the CLI
make build

# Pull a package for signing
zarf tools download-init

# Sign the package (this will conduct browser auth)
./build/zarf package sign zarf-init-arm64-v0.75.0.tar.zst --keyless

# (optional) turn off wifi

# Verify the package
./build/zarf package verify zarf-init-arm64-v0.75.0.tar.zst --certificate-identity brandt.keller@defenseunicorns.com --certificate-oidc-issuer https://github.com/login/oauth

Related Issue

Fixes #2805
Fixes #4571

Relates to #

Checklist before merging

Signed-off-by: Brandt Keller <brandt.keller@defenseunicorns.com>
Signed-off-by: Brandt Keller <brandt.keller@defenseunicorns.com>
Signed-off-by: Brandt Keller <brandt.keller@defenseunicorns.com>
Signed-off-by: Brandt Keller <brandt.keller@defenseunicorns.com>
Signed-off-by: Brandt Keller <brandt.keller@defenseunicorns.com>
Signed-off-by: Brandt Keller <brandt.keller@defenseunicorns.com>
Signed-off-by: Brandt Keller <brandt.keller@defenseunicorns.com>
Signed-off-by: Brandt Keller <brandt.keller@defenseunicorns.com>
Signed-off-by: Brandt Keller <brandt.keller@defenseunicorns.com>
Signed-off-by: Brandt Keller <brandt.keller@defenseunicorns.com>
Signed-off-by: Brandt Keller <brandt.keller@defenseunicorns.com>
Signed-off-by: Brandt Keller <brandt.keller@defenseunicorns.com>
Signed-off-by: Brandt Keller <brandt.keller@defenseunicorns.com>
Signed-off-by: Brandt Keller <brandt.keller@defenseunicorns.com>
@codecov
Copy link
Copy Markdown

codecov Bot commented May 11, 2026

Codecov Report

❌ Patch coverage is 40.37855% with 189 lines in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
src/cmd/package.go 19.62% 86 Missing ⚠️
src/pkg/utils/oci_artifacts.go 0.00% 39 Missing ⚠️
src/pkg/packager/layout/package.go 70.00% 16 Missing and 5 partials ⚠️
src/pkg/signing/cosign.go 5.00% 19 Missing ⚠️
src/pkg/signing/trustedroot.go 30.76% 6 Missing and 3 partials ⚠️
src/pkg/packager/publish.go 0.00% 4 Missing and 3 partials ⚠️
src/pkg/signing/bundle.go 88.88% 4 Missing and 1 partial ⚠️
src/cmd/tools_trustedroot.go 0.00% 1 Missing ⚠️
src/pkg/packager/load.go 0.00% 1 Missing ⚠️
src/pkg/packager/pull.go 0.00% 1 Missing ⚠️
Files with missing lines Coverage Δ
src/cmd/viper.go 60.19% <100.00%> (+4.28%) ⬆️
src/pkg/feature/feature.go 91.12% <100.00%> (ø)
src/pkg/packager/layout/assemble.go 43.17% <100.00%> (ø)
src/cmd/tools_trustedroot.go 75.64% <0.00%> (ø)
src/pkg/packager/load.go 62.89% <0.00%> (ø)
src/pkg/packager/pull.go 50.00% <0.00%> (ø)
src/pkg/signing/bundle.go 88.88% <88.88%> (ø)
src/pkg/packager/publish.go 63.10% <0.00%> (-3.21%) ⬇️
src/pkg/signing/trustedroot.go 30.76% <30.76%> (ø)
src/pkg/signing/cosign.go 1.01% <5.00%> (ø)
... and 3 more
🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

Signed-off-by: Brandt Keller <brandt.keller@defenseunicorns.com>
Signed-off-by: Brandt Keller <brandt.keller@defenseunicorns.com>
…tion

Signed-off-by: Brandt Keller <brandt.keller@defenseunicorns.com>
Base automatically changed from 4572_sign_verify_flags to main May 12, 2026 17:25
Signed-off-by: Brandt Keller <brandt.keller@defenseunicorns.com>
@netlify
Copy link
Copy Markdown

netlify Bot commented May 12, 2026

Deploy Preview for zarf-docs canceled.

Name Link
🔨 Latest commit 3f63c37
🔍 Latest deploy log https://app.netlify.com/projects/zarf-docs/deploys/6a0549920bad3d00087a05f0

Signed-off-by: Brandt Keller <brandt.keller@defenseunicorns.com>
Signed-off-by: Brandt Keller <brandt.keller@defenseunicorns.com>
#!/usr/bin/env bash
# Refresh the embedded Sigstore TrustedRoot used for keyless verification.
# Run before each release. Commit the result.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Making a mental note to schedule daily checks and notify the zarf channel using this process. A lag in releasing new trusted roots won't be critical but should be nice to have.

Signed-off-by: Brandt Keller <brandt.keller@defenseunicorns.com>
@brandtkeller brandtkeller marked this pull request as ready for review May 13, 2026 01:48
@brandtkeller brandtkeller requested review from a team as code owners May 13, 2026 01:48
Signed-off-by: Brandt Keller <brandt.keller@defenseunicorns.com>
Signed-off-by: Brandt Keller <brandt.keller@defenseunicorns.com>
Copy link
Copy Markdown
Member

@AustinAbro321 AustinAbro321 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Excited how signing is taking shape, no doubt this will improve the UX

Comment thread src/pkg/packager/layout/package.go
Comment thread src/cmd/package.go
Comment thread src/cmd/package.go Outdated
Comment thread src/pkg/signing/bundle.go
Comment thread src/api/v1alpha1/package.go Outdated
Comment thread src/pkg/packager/layout/package.go Outdated
@github-project-automation github-project-automation Bot moved this to In progress in Zarf May 13, 2026
Signed-off-by: Brandt Keller <brandt.keller@defenseunicorns.com>
Signed-off-by: Brandt Keller <brandt.keller@defenseunicorns.com>
Signed-off-by: Brandt Keller <brandt.keller@defenseunicorns.com>
)

// GetCosignArtifacts returns signatures and attestations for the given image.
func GetCosignArtifacts(image string) ([]string, error) {
Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I kept this function in utils - it is not soo much a signing function as it is a helper for signature/attestation retrieval.

Comment thread hack/refresh-trusted-root.sh Outdated
Co-authored-by: Austin Abro <37223396+AustinAbro321@users.noreply.github.com>
Signed-off-by: Brandt Keller <43887158+brandtkeller@users.noreply.github.com>
Signed-off-by: Brandt Keller <brandt.keller@defenseunicorns.com>
Comment thread src/pkg/signing/bundle.go
// BundleInfo contains parsed metadata from a Sigstore bundle file.
type BundleInfo struct {
// Method is "keyless" for Fulcio-issued certificate bundles, "key" for public-key bundles.
Method string
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: can we make this an enum


// Keyless signatures require bundle format — the cert chain cannot be stored in the
// legacy .sig file. For key-based signing, respect the BundleSignature feature flag.
bundleEnabled := feature.IsEnabled(feature.BundleSignature) || opts.Keyless
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: I think it makes more sense to error if the feature is not enabled and keyless is true. Probably doesn't matter much now that we're enabled by default, but that's the preferred pattern for the codebase imo

Comment thread src/pkg/signing/bundle.go
// ReadKeylessIdentityFromBundle parses a Sigstore bundle file and returns the
// signer identity (cert SAN) and OIDC issuer claim. Returns an error if the
// bundle does not contain a certificate (i.e. is not a keyless signature).
func ReadKeylessIdentityFromBundle(bundlePath string) (identity, issuer string, err error) {
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This function is not used

Comment thread src/pkg/signing/bundle.go
// extractIdentityFromCert extracts the signer identity (cert SAN) and OIDC issuer
// from a Fulcio-issued X.509 certificate using Sigstore OID extensions.
// SAN priority: email > URI > DNS. OID priority: V2 > legacy.
func extractIdentityFromCert(cert *x509.Certificate) (identity, issuer string) {
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I believe we can replace this function with github.com/sigstore/sigstore-go/pkg/fulcio/certificate.SummarizeCertificate

Comment thread src/cmd/package.go
Comment on lines +2031 to +2033
if o.keyless && !cmd.Flags().Changed("tlog-upload") {
signOpts.TlogUpload = true
}
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is the current plan to support tsa using the same enable flag if keyless method? Cosign does use tsa by default and our default trusted root has the timestampAuthorities field

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: In progress

Development

Successfully merging this pull request may close these issues.

Embed the Public Good Sigstore Trusted Root Integrate Sigstore for "keyless signing" of packages and their build provenence

2 participants