-
Notifications
You must be signed in to change notification settings - Fork 669
Description
Description
We currently push attestations to container registries using tags. I want to enable our builds to start pushing all attestations using the referrer's API but there are some tooling gaps in being able to download and verify this metadata.
Commands WITH OCI 1.1 Support:
cosign tree---registry-referrers-mode=oci-1-1+--experimental-oci11cosign sign---registry-referrers-mode=oci-1-1cosign attach sbom---registry-referrers-mode=oci-1-1cosign verify---experimental-oci11
Commands MISSING OCI 1.1 Support:
cosign download attestationcosign download signaturecosign download sbomcosign verify-attestationcosign attach signaturecosign attach attestationcosign attest(creates and attaches new attestations)
I think that we should have universal support for the OCI 1.1 referrer's API. The inconsistency here is confusing. When proposing how to make Chains use the referrer's API, I didn't realized that I ended up trying to use the new bundle format instead of just changing the storage mechanism.
All endpoints should ideally have options such that it would be possible to achieve all of the following:
# Download commands get OCI 1.1 support
cosign download attestation --experimental-oci11 example.com/app:v1.0
# Verify attestation gets OCI 1.1 support
cosign verify-attestation --experimental-oci11 example.com/app:v1.0
# Attach commands get OCI 1.1 support
cosign attach signature --experimental-oci11 --signature=sig.txt example.com/app:v1.0
# Create commands get OCI 1.1 support
cosign attest --experimental-oci11 --predicate=predicate.json --type=slsaprovenance example.com/app:v1.0
cosign sign --experimental-oci11 --key=cosign.key example.com/app:v1.0I see two potential approaches that address different priorities. Is there a preference between these approaches? I feel like I likely don't understand the full complexity of the latter approach, but I also feel like it is the better approach as I expect it would/could be more consistent with future bundle support as well. The first approach would definitely be simpler to implement.
Option A: Extend the current pattern
The current implementations just handle interfacing with the referrer's API directly within the functions. For example, verify and tree use ociremote.Referrers() while attach sbom calls mutate.Subject().
If we were to leverage this pattern, there would likely be duplicated code across multiple subcommands. This would work, is likely going to be faster, and would have fewer potential side effects.
Option B: Create a unified abstraction for managing artifact operations
In looking at the code, it seems like we have many locations where three different types of operations are performed: find, attach, and create. If we extract these all into a centralized artifact management abstraction then we could potentially get a benefit of code reuse across them (i.e. in a new pkg/oci/artifacts/ package).
This approach could theoretically also simplify cosign's use as a dependency.
Its primary interface would be something like this:
// Unified interface for all artifact types
type ArtifactManager interface {
FindArtifacts(ctx context.Context, subject name.Digest, artifactType string, filters ...Filter) ([]Artifact, error)
AttachArtifact(ctx context.Context, subject name.Digest, artifact Artifact, opts AttachOptions) error
CreateArtifact(ctx context.Context, subject name.Digest, content []byte, artifactType string, signingOpts SigningOptions, attachOpts AttachOptions) error
}This would let the CLI implementations follow patterns to defer common work to the manager
// CLI command implementations using the unified manager:
func DownloadAttestationCmd(...) error {
manager, _ := artifacts.NewArtifactManager(storageMode, format, regOpts)
filters := []Filter{PredicateTypeFilter(predicateType)}
artifacts, err := manager.FindArtifacts(ctx, digest, "att", filters...)
// output results
}
func AttachSBOMCmd(...) error {
manager, _ := artifacts.NewArtifactManager(storageMode, format, regOpts)
artifact := Artifact{Type: "sbom", Content: sbomData, MediaType: mediaType}
return manager.AttachArtifact(ctx, digest, artifact, opts)
}
func AttestCmd(...) error {
manager, _ := artifacts.NewArtifactManager(storageMode, format, regOpts)
return manager.CreateArtifact(ctx, digest, predicate, "att", signingOpts, attachOpts)
}I think that this approach should be able to be reused for bundles which might also simplify planning related to v3.