Skip to content

Conversation

@brandtkeller
Copy link
Member

@brandtkeller brandtkeller commented Oct 21, 2025

Description

Adds a new zarf package sign command that enables signing existing Zarf packages with cryptographic signatures using Cosign. This allows packages to be signed independently from the build process.

Key Changes

New Command: zarf package sign
Functionality: Signs existing Zarf packages (local tarballs or OCI packages) with a private key
Key Features:

  • Sign local package files or pull from OCI registries
  • Output to local directory or publish directly to OCI registry
  • Support for multiple key providers (local files, AWS KMS, GCP KMS, Azure KMS,)
  • --overwrite flag to re-sign packages with a new key
  • Optional signature validation before re-signing

Core Implementation Changes

Package Layout:

  • Refactored SignPackage() to use context and structured options
  • Atomic signing process: updates Build.Signed metadata, writes signed zarf.yaml, then creates signature
  • Proper rollback handling on signing failures
    Build Metadata:
  • Added Signed *bool field to ZarfBuildData to track package signing status
    Cosign Utilities:
  • New SignBlobOptions struct with ShouldSign() helper
  • CosignSignBlobWithOptions() wrapper for consistent signing interface
  • DefaultSignBlobOptions() for standard signing configuration
    Assembly Process:
  • Refactored to use new PackageLayout.SignPackage() method
  • Removed standalone signPackage() function
  • Signing now integrated with package layout lifecycle

Testing & Documentation

E2E Test: New test case for package signing workflow
Unit Tests: Comprehensive coverage for signing logic (success, failure, rollback scenarios)
Examples

# Sign an unsigned package
zarf package sign zarf-package-demo-amd64-1.0.0.tar.zst --signing-key ./private-key.pem

# Re-sign with a new key
zarf package sign package.tar.zst --signing-key ./new-key.pem --overwrite

# Sign and publish to OCI
zarf package sign package.tar.zst --signing-key ./key.pem --output oci://ghcr.io/org/packages

Related Issue

Fixes #3959

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>
@netlify
Copy link

netlify bot commented Oct 21, 2025

Deploy Preview for zarf-docs ready!

Name Link
🔨 Latest commit dbf8c92
🔍 Latest deploy log https://app.netlify.com/projects/zarf-docs/deploys/690580537e169d0008c2c38c
😎 Deploy Preview https://deploy-preview-4301--zarf-docs.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.

To edit notification comments on pull requests, go to your Netlify project configuration.

@codecov
Copy link

codecov bot commented Oct 21, 2025

Codecov Report

❌ Patch coverage is 26.65094% with 311 lines in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
src/internal/cosign/cosign.go 0.00% 148 Missing ⚠️
src/cmd/package.go 15.33% 127 Missing ⚠️
src/pkg/packager/layout/package.go 69.30% 26 Missing and 5 partials ⚠️
src/pkg/packager/layout/assemble.go 83.33% 2 Missing and 1 partial ⚠️
src/pkg/packager/find_images.go 50.00% 1 Missing ⚠️
src/pkg/packager/publish.go 80.00% 0 Missing and 1 partial ⚠️
Files with missing lines Coverage Δ
src/api/v1alpha1/package.go 32.55% <ø> (ø)
src/cmd/viper.go 55.43% <ø> (ø)
src/pkg/packager/find_images.go 55.79% <50.00%> (ø)
src/pkg/packager/publish.go 64.59% <80.00%> (+0.90%) ⬆️
src/pkg/packager/layout/assemble.go 42.39% <83.33%> (-1.24%) ⬇️
src/pkg/packager/layout/package.go 52.25% <69.30%> (+6.57%) ⬆️
src/cmd/package.go 37.62% <15.33%> (-2.70%) ⬇️
src/internal/cosign/cosign.go 0.00% <0.00%> (ø)
🚀 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.

@brandtkeller brandtkeller changed the title 3959 package sign feat!(sign): add package sign command Oct 22, 2025
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>
@brandtkeller brandtkeller moved this to In progress in Zarf Oct 24, 2025
@brandtkeller brandtkeller self-assigned this Oct 24, 2025
@brandtkeller brandtkeller marked this pull request as ready for review October 24, 2025 16:17
@brandtkeller brandtkeller requested review from a team as code owners October 24, 2025 16:17
@brandtkeller brandtkeller moved this from In progress to PR Review in Zarf Oct 24, 2025
Copy link
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.

Some comments for now, will need to do a more in-depth review at another point

@github-project-automation github-project-automation bot moved this from PR Review to In progress in Zarf Oct 27, 2025
Signed-off-by: Brandt Keller <brandt.keller@defenseunicorns.com>
Signed-off-by: Brandt Keller <brandt.keller@defenseunicorns.com>
// this is a no-op as there may be many different ways to sign
// input validation should be performed in the calling function
if !opts.ShouldSign() {
l.Info("skipping package signing (no signing key material configured)")
Copy link
Member

Choose a reason for hiding this comment

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

Given that this line now prints on zarf package create for unsigned packages, I think there is a potential impact on user psychology. This will likely serve as a call to action for users who are not currently signing their package to look into what it takes to sign a package. I know there are improvements to signing packages in the pipeline. Keeping this as a debug log until we have newer, documented, signing methods we want to encourage, could be a boon on future signing adoption. User's will likely not look into signing twice.

Not formally requesting a change, but I do think this is an interesting topic.

Copy link
Member Author

Choose a reason for hiding this comment

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

I agree with the sentiment - although I think things like signing, SBOMs, and any other attestations will continue to be reminded and required as regulation continues or begins to require as much. Not overpromising current capabilities is always a good reminder.

brandtkeller and others added 5 commits October 29, 2025 14:24
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>
@brandtkeller brandtkeller moved this from In progress to PR Review in Zarf Oct 30, 2025
Comment on lines 1619 to 1629
if helpers.IsOCIURL(packageSource) {
// For OCI sources, use current working directory
wd, err := os.Getwd()
if err != nil {
return err
}
outputDest = wd
} else {
// For file sources, use the same directory as the source
outputDest = filepath.Dir(packageSource)
}
Copy link
Member

Choose a reason for hiding this comment

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

As I'm trying out the UX of this command something that confused me was that when I ran the command on a local package it signed my package in place by default, however when I ran the command on a package in a registry, it gave me a copy of the package. I assumed that the package in the registry was signed and got confused when it wasn't. Thoughts on signing the package in the registry by default?

Copy link
Member Author

Choose a reason for hiding this comment

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

That is good UX feedback. I'd say that is a reasonable and maybe optimal expectation.

}

// Handle output - OCI or local file
if helpers.IsOCIURL(outputDest) {
Copy link
Member

Choose a reason for hiding this comment

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

This could be a future enhancement, but one option to eliminate duplication is using packagePublishOptions.Run()

Copy link
Member

@AustinAbro321 AustinAbro321 Oct 30, 2025

Choose a reason for hiding this comment

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

This is definitely out of scope, but I think it would be a good issue would be to move this file from utils to it's own cosign package. Ideally the package would be internal as well

Copy link
Member Author

Choose a reason for hiding this comment

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

I am not against doing that now - it still provides surface area to support an embedded zarf tools sign-blob command if requested.

@brandtkeller brandtkeller moved this from PR Review to In progress in Zarf Oct 30, 2025
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>
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.

Command to re-sign Zarf packages

3 participants