zarf-dev / zarf

DevSecOps for Air Gap & Limited-Connection Systems. https://zarf.dev/
Apache License 2.0
1.41k stars 171 forks source link

Integrate Sigstore for "keyless signing" of packages and their build provenence #2805

Open marshall007 opened 3 months ago

marshall007 commented 3 months ago

Is your feature request related to a problem? Please describe.

Preserving supply chain integrity when moving artifacts downloaded from the Internet into an air-gapped environment is very challenging. Presently, there is no native way for zarf to verify that a downloaded package was built in a CI system using a "trusted workflow".

Examples

Image Signature

For a practical example, refer to the "Provenance" tab for an image in Chainguard's directory: https://images.chainguard.dev/directory/image/busybox/provenance

Notice that you can not only verify that the image is signed, you are also intrinsically verifying the image's "build provenance". Specifically, you can verify that it was built on GitHub Actions using a workflow maintained by Chainguard (i.e. https://github.com/chainguard-images/images/.github/workflows/release.yaml@refs/heads/main):

cosign verify \
  --certificate-oidc-issuer=https://token.actions.githubusercontent.com \
  --certificate-identity=https://github.com/chainguard-images/images/.github/workflows/release.yaml@refs/heads/main \
  cgr.dev/chainguard/busybox | jq
Additional Image Attestations

Chainguard provides additional, verifiable, guarantees about their images in the form of additional attestations:

This is important because, in addition to being able to download these attestations without downloading the entire image, their signatures can be verified in the same way:

# download SPDX SBOM
cosign download attestation \
  --platform=linux/amd64 \
  --predicate-type=https://spdx.dev/Document \
  cgr.dev/chainguard/busybox | jq -r .payload | base64 -d | jq .predicate

# verify its signatures
cosign verify-attestation \
  --type https://spdx.dev/Document \
  --certificate-oidc-issuer=https://token.actions.githubusercontent.com \
  --certificate-identity=https://github.com/chainguard-images/images/.github/workflows/release.yaml@refs/heads/main \
  cgr.dev/chainguard/busybox

Describe the solution you'd like

IMHO packages should be signed on zarf package push and verified on zarf package pull by default. This behavior should be opt-out via something like --insecure on respective commands.

Describe alternatives you've considered

We could sign packages with cosign after they are built and pushed to a registry. This limits our ability to provide "secure-by-default" behavior. It also leaves build provenance attestations as an exercise for the package authors, which is really annoying for things like SBOM generation.

Additional context

There is a lot to unpack in terms of what attestations we provide. Intentionally left open for discussion

bburky commented 3 months ago

To implement keyless signing verification zarf will need to provide more options than it's current --key to verify signatures. To support verifying signatures using the built-in default sigstore Public-Good Instance CA roots, an equivalent to Coign's --certificate-oidc and --predicate-type is required at a minimum, but Cosign has many other certificate matching options we may want too. If we want to support self hosted Sigstore we would need to allow configuring the Fulcio roots, I think Cosign supports this by configuring the SIGSTORE_ROOT_FILE environment variable (I think there's a way to do it through TUF too).

Please ensure these options are exposed in the go api so that external tools integrating Zarf can use them.