Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

download attestation: support --platform flag #2980

Merged
merged 2 commits into from
May 16, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
48 changes: 47 additions & 1 deletion cmd/cosign/cli/download/attestation.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,11 @@ import (
"fmt"

"github.com/google/go-containerregistry/pkg/name"
v1 "github.com/google/go-containerregistry/pkg/v1"
"github.com/sigstore/cosign/v2/cmd/cosign/cli/options"
"github.com/sigstore/cosign/v2/pkg/cosign"
"github.com/sigstore/cosign/v2/pkg/oci"
ociremote "github.com/sigstore/cosign/v2/pkg/oci/remote"
)

func AttestationCmd(ctx context.Context, regOpts options.RegistryOptions, attOptions options.AttestationDownloadOptions, imageRef string) error {
Expand All @@ -43,7 +46,50 @@ func AttestationCmd(ctx context.Context, regOpts options.RegistryOptions, attOpt
}
}

attestations, err := cosign.FetchAttestationsForReference(ctx, ref, predicateType, ociremoteOpts...)
se, err := ociremote.SignedEntity(ref, ociremoteOpts...)
if err != nil {
return err
}

idx, isIndex := se.(oci.SignedImageIndex)

// We only allow --platform on multiarch indexes
if attOptions.Platform != "" && !isIndex {
return fmt.Errorf("specified reference is not a multiarch image")
}

if attOptions.Platform != "" && isIndex {
targetPlatform, err := v1.ParsePlatform(attOptions.Platform)
if err != nil {
return fmt.Errorf("parsing platform: %w", err)
}
platforms, err := getIndexPlatforms(idx)
if err != nil {
return fmt.Errorf("getting available platforms: %w", err)
}

platforms = matchPlatform(targetPlatform, platforms)
if len(platforms) == 0 {
return fmt.Errorf("unable to find an attestation for %s", targetPlatform.String())
}
if len(platforms) > 1 {
return fmt.Errorf(
"platform spec matches more than one image architecture: %s",
platforms.String(),
)
}

nse, err := idx.SignedImage(platforms[0].hash)
if err != nil {
return fmt.Errorf("searching for %s image: %w", platforms[0].hash.String(), err)
}
if nse == nil {
return fmt.Errorf("unable to find image %s", platforms[0].hash.String())
}
se = nse
}

attestations, err := cosign.FetchAttestations(se, predicateType)
if err != nil {
return err
}
Expand Down
3 changes: 3 additions & 0 deletions cmd/cosign/cli/options/download.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ type SBOMDownloadOptions struct {

type AttestationDownloadOptions struct {
PredicateType string // Predicate type of attestation to retrieve
Platform string // Platform to download attestations
}

var _ Interface = (*SBOMDownloadOptions)(nil)
Expand All @@ -40,4 +41,6 @@ func (o *SBOMDownloadOptions) AddFlags(cmd *cobra.Command) {
func (o *AttestationDownloadOptions) AddFlags(cmd *cobra.Command) {
cmd.Flags().StringVar(&o.PredicateType, "predicate-type", "",
"download attestation with matching predicateType annotation")
cmd.Flags().StringVar(&o.Platform, "platform", "",
"download attestation for a specific platform image")
}
1 change: 1 addition & 0 deletions doc/cosign_download_attestation.md

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

11 changes: 8 additions & 3 deletions pkg/cosign/fetch.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,13 +19,15 @@ import (
"context"
"crypto/x509"
"encoding/json"
"errors"
"fmt"
"os"
"runtime"
"sync"

"github.com/google/go-containerregistry/pkg/name"
"github.com/sigstore/cosign/v2/pkg/cosign/bundle"
"github.com/sigstore/cosign/v2/pkg/oci"
ociremote "github.com/sigstore/cosign/v2/pkg/oci/remote"
"golang.org/x/sync/errgroup"
)
Expand Down Expand Up @@ -121,12 +123,15 @@ func FetchSignaturesForReference(_ context.Context, ref name.Reference, opts ...
}

func FetchAttestationsForReference(_ context.Context, ref name.Reference, predicateType string, opts ...ociremote.Option) ([]AttestationPayload, error) {
simg, err := ociremote.SignedEntity(ref, opts...)
se, err := ociremote.SignedEntity(ref, opts...)
if err != nil {
return nil, err
}
return FetchAttestations(se, predicateType)
}

atts, err := simg.Attestations()
func FetchAttestations(se oci.SignedEntity, predicateType string) ([]AttestationPayload, error) {
atts, err := se.Attestations()
if err != nil {
return nil, fmt.Errorf("remote image: %w", err)
}
Expand All @@ -135,7 +140,7 @@ func FetchAttestationsForReference(_ context.Context, ref name.Reference, predic
return nil, fmt.Errorf("fetching attestations: %w", err)
}
if len(l) == 0 {
return nil, fmt.Errorf("no attestations associated with %s", ref)
return nil, errors.New("found no attestations")
}

attestations := make([]AttestationPayload, 0, len(l))
Expand Down