Skip to content

Commit

Permalink
Save/Load to include SBOMS (#2)
Browse files Browse the repository at this point in the history
* fix bulk save/load e2e typos
* fix registry option for bulk save/load e2e
* correct lint issues for bulk save/load changes
* include sbom with save and load
* handle not finding an sbom


Signed-off-by: Adam Martin <adam.martin@rancherfederal.com>
  • Loading branch information
amartin120 authored Nov 6, 2023
1 parent da46c25 commit e5eb332
Show file tree
Hide file tree
Showing 7 changed files with 62 additions and 17 deletions.
4 changes: 1 addition & 3 deletions cmd/cosign/cli/load.go
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,6 @@ func Load() *cobra.Command {
}

func LoadCmd(ctx context.Context, opts options.LoadOptions, imageRef string) error {

if opts.Registry.Name != "" && imageRef != "" {
return fmt.Errorf("both --registry and image argument provided, only one should be used")
}
Expand Down Expand Up @@ -82,7 +81,6 @@ func LoadCmd(ctx context.Context, opts options.LoadOptions, imageRef string) err

if opts.Registry.Name == "" {
return remote.WriteSignedImageIndexImages(ref, sii, ociremoteOpts...)
} else {
return remote.WriteSignedImageIndexImagesBulk(opts.Registry.Name, sii, ociremoteOpts...)
}
return remote.WriteSignedImageIndexImagesBulk(opts.Registry.Name, sii, ociremoteOpts...)
}
2 changes: 1 addition & 1 deletion cmd/cosign/cli/options/registry.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ type RegistryOptions struct {
AllowInsecure bool
AllowHTTPRegistry bool
KubernetesKeychain bool
Name string
Name string
RefOpts ReferenceOptions
Keychain Keychain

Expand Down
3 changes: 2 additions & 1 deletion pkg/oci/layout/index.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,10 +27,11 @@ import (
const (
KindAnnotation = "kind"
ImageAnnotation = "dev.cosignproject.cosign/image"
ImageRefAnnotation = "org.opencontainers.image.ref.name"
ImageRefAnnotation = "org.opencontainers.image.ref.name"
ImageIndexAnnotation = "dev.cosignproject.cosign/imageIndex"
SigsAnnotation = "dev.cosignproject.cosign/sigs"
AttsAnnotation = "dev.cosignproject.cosign/atts"
SbomsAnnotation = "dev.cosignproject.cosign/sboms"
)

// SignedImageIndex provides access to a local index reference, and its signatures.
Expand Down
9 changes: 9 additions & 0 deletions pkg/oci/layout/write.go
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,16 @@ func writeSignedEntity(path layout.Path, se oci.SignedEntity, ref name.Reference
return fmt.Errorf("appending atts: %w", err)
}
}

// TODO (priyawadhwa@) and attachments
// temp handle sboms - amartin120@
sboms, err := se.Attachment("sbom")
if err != nil {
return nil //no sbom found
}
if err := appendImage(path, sboms, ref, SbomsAnnotation); err != nil {
return fmt.Errorf("appending attachments: %w", err)
}
return nil
}

Expand Down
2 changes: 1 addition & 1 deletion pkg/oci/layout/write_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,13 +21,13 @@ import (
"testing"

"github.com/google/go-cmp/cmp"
"github.com/google/go-containerregistry/pkg/name"
v1 "github.com/google/go-containerregistry/pkg/v1"
"github.com/google/go-containerregistry/pkg/v1/random"
"github.com/sigstore/cosign/v2/pkg/oci"
"github.com/sigstore/cosign/v2/pkg/oci/mutate"
"github.com/sigstore/cosign/v2/pkg/oci/signed"
"github.com/sigstore/cosign/v2/pkg/oci/static"
"github.com/google/go-containerregistry/pkg/name"
)

func TestReadWrite(t *testing.T) {
Expand Down
38 changes: 36 additions & 2 deletions pkg/oci/remote/write.go
Original file line number Diff line number Diff line change
Expand Up @@ -89,21 +89,35 @@ func WriteSignedImageIndexImages(ref name.Reference, sii oci.SignedImageIndex, o
}
return remoteWrite(attsTag, atts, o.ROpt...)
}

// write the attachments
// implementing sboms for starters
sboms, err := sii.Attachment("sbom")
if err != nil {
return err
}
if sboms != nil { // will be nil if there are no associated sboms
sbomTag, err := SBOMTag(ref, opts...)
if err != nil {
return fmt.Errorf("sbom tag: %w", err)
}
if err := remoteWrite(sbomTag, sboms, o.ROpt...); err != nil {
return err
}
}
return nil
}

// WriteSignedImageIndexImagesBulk writes the images within the image index.
// Bulk version. Uses targetRegistry for multiple images/sigs/atts.
// This includes the signed image and associated signatures in the image index
func WriteSignedImageIndexImagesBulk(targetRegistry string, sii oci.SignedImageIndex, opts ...Option) error {

// loop through all of the items in the manifest
manifest, err := sii.IndexManifest()
if err != nil {
return err
}
for _, m := range manifest.Manifests {

// write image index if exists
if val, ok := m.Annotations[layout.KindAnnotation]; ok && val == layout.ImageIndexAnnotation {
imgTitle := m.Annotations[layout.ImageRefAnnotation]
Expand Down Expand Up @@ -188,6 +202,26 @@ func WriteSignedImageIndexImagesBulk(targetRegistry string, sii oci.SignedImageI
}
}

// write the sboms
if val, ok := m.Annotations[layout.KindAnnotation]; ok && val == layout.SbomsAnnotation {
imgTitle := m.Annotations[layout.ImageRefAnnotation]
sboms, err := sii.SignedImage(m.Digest)
if err != nil {
return err
}
if sboms != nil { // will be nil if there are no associated attestations
ref, err := name.ParseReference(targetRegistry + "/" + imgTitle)
sbomsTag, err := SBOMTag(ref, opts...)
if err != nil {
return fmt.Errorf("sboms tag: %w", err)
}
repo := ref.Context()
o := makeOptions(repo, opts...)
if err := remoteWrite(sbomsTag, sboms, o.ROpt...); err != nil {
return err
}
}
}
}
return nil
}
Expand Down
21 changes: 12 additions & 9 deletions test/e2e_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1716,15 +1716,16 @@ func TestSaveLoadBulk(t *testing.T) {
// Generate multiple image names
imageNames := []string{fmt.Sprintf("save-load-%d-1", i), fmt.Sprintf("save-load-%d-2", i)}

ctx := context.Background()
_, privKeyPath, pubKeyPath := keypair(t, keysDir)
imageDir := t.TempDir()

for _, imgName := range imageNames {
imgName := path.Join(regName, imgName)

_, _, cleanup := test.getSignedEntity(t, imgName)
defer cleanup()

_, privKeyPath, pubKeyPath := keypair(t, keysDir)

ctx := context.Background()
// Now sign the image and verify it
ko := options.KeyOpts{KeyRef: privKeyPath, PassFunc: passFunc}
so := options.SignOptions{
Expand All @@ -1734,18 +1735,20 @@ func TestSaveLoadBulk(t *testing.T) {
must(verify(pubKeyPath, imgName, true, nil, ""), t)

// Save the image to a temp dir
imageDir := t.TempDir()
must(cli.SaveCmd(ctx, options.SaveOptions{Directory: imageDir}, imgName), t)

// Verify the local image using a local key
must(verifyLocal(pubKeyPath, imageDir, true, nil, ""), t)
must(verifyLocal(pubKeyPath, imageDir, true, nil, ""), t)
}
// Load the images from the temp dir into a registry
must(cli.LoadCmd(ctx, options.LoadOptions{Directory: imageDir, Registry: regName), t)
// Load the images from the temp dir into a registry
ro := options.RegistryOptions{
Name: regName,
}
must(cli.LoadCmd(ctx, options.LoadOptions{Directory: imageDir, Registry: ro}, ""), t)

// verify the new images
must(verify(pubKeyPath, path.Join(regName, imageName[0]), true, nil, ""), t)
must(verify(pubKeyPath, path.Join(regName, imageName[1]), true, nil, ""), t)
must(verify(pubKeyPath, path.Join(regName, imageNames[0]), true, nil, ""), t)
must(verify(pubKeyPath, path.Join(regName, imageNames[1]), true, nil, ""), t)
})
}
}
Expand Down

0 comments on commit e5eb332

Please sign in to comment.