Skip to content

Commit

Permalink
Add option to verify local image (#1159)
Browse files Browse the repository at this point in the history
This builds on the previous work to support saving an image and
its signature for airgapped environments. This adds a flag,
--local-image, to verify a persisted image using the on-disk sig.

Signed-off-by: Hayden Blauzvern <hblauzvern@google.com>
  • Loading branch information
haydentherapper authored Dec 8, 2021
1 parent bd8b7d5 commit 10b7f9d
Show file tree
Hide file tree
Showing 8 changed files with 109 additions and 15 deletions.
4 changes: 4 additions & 0 deletions cmd/cosign/cli/options/verify.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ type VerifyOptions struct {
Attachment string
Output string
SignatureRef string
LocalImage bool

SecurityKey SecurityKeyOptions
Rekor RekorOptions
Expand Down Expand Up @@ -67,6 +68,9 @@ func (o *VerifyOptions) AddFlags(cmd *cobra.Command) {

cmd.Flags().StringVar(&o.SignatureRef, "signature", "",
"signature content or path or remote URL")

cmd.Flags().BoolVar(&o.LocalImage, "local-image", false,
"whether the path to the image is a local directory")
}

// VerifyAttestationOptions is the top level wrapper for the `verify attestation` command.
Expand Down
4 changes: 4 additions & 0 deletions cmd/cosign/cli/verify.go
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,9 @@ against the transparency log.`,
# signature digest algorithm
cosign verify --key cosign.pub --signature-digest-algorithm sha512 <IMAGE>
# verify image with an on-disk signed image from 'cosign save'
cosign verify --key cosign.pub --local-image <PATH>
# verify image with public key provided by URL
cosign verify --key https://host.for/[FILE] <IMAGE>
Expand Down Expand Up @@ -97,6 +100,7 @@ against the transparency log.`,
Annotations: annotations,
HashAlgorithm: hashAlgorithm,
SignatureRef: o.SignatureRef,
LocalImage: o.LocalImage,
}

return v.Exec(cmd.Context(), args)
Expand Down
38 changes: 24 additions & 14 deletions cmd/cosign/cli/verify/verify.go
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ type VerifyCommand struct {
Annotations sigs.AnnotationsMap
SignatureRef string
HashAlgorithm crypto.Hash
LocalImage bool
}

// Exec runs the verification command
Expand Down Expand Up @@ -138,22 +139,31 @@ func (c *VerifyCommand) Exec(ctx context.Context, images []string) (err error) {
co.SigVerifier = pubKey

for _, img := range images {
ref, err := name.ParseReference(img)
if err != nil {
return errors.Wrap(err, "parsing reference")
}
ref, err = sign.GetAttachedImageRef(ref, c.Attachment, ociremoteOpts...)
if err != nil {
return errors.Wrapf(err, "resolving attachment type %s for image %s", c.Attachment, img)
}
if c.LocalImage {
verified, bundleVerified, err := cosign.VerifyLocalImageSignatures(ctx, img, co)
if err != nil {
return err
}
PrintVerificationHeader(img, co, bundleVerified)
PrintVerification(img, verified, c.Output)
} else {
ref, err := name.ParseReference(img)
if err != nil {
return errors.Wrap(err, "parsing reference")
}
ref, err = sign.GetAttachedImageRef(ref, c.Attachment, ociremoteOpts...)
if err != nil {
return errors.Wrapf(err, "resolving attachment type %s for image %s", c.Attachment, img)
}

verified, bundleVerified, err := cosign.VerifyImageSignatures(ctx, ref, co)
if err != nil {
return err
}
verified, bundleVerified, err := cosign.VerifyImageSignatures(ctx, ref, co)
if err != nil {
return err
}

PrintVerificationHeader(ref.Name(), co, bundleVerified)
PrintVerification(ref.Name(), verified, c.Output)
PrintVerificationHeader(ref.Name(), co, bundleVerified)
PrintVerification(ref.Name(), verified, c.Output)
}
}

return nil
Expand Down
1 change: 1 addition & 0 deletions doc/cosign_dockerfile_verify.md

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

1 change: 1 addition & 0 deletions doc/cosign_manifest_verify.md

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

4 changes: 4 additions & 0 deletions doc/cosign_verify.md

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

53 changes: 52 additions & 1 deletion pkg/cosign/verify.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ import (

ssldsse "github.com/secure-systems-lab/go-securesystemslib/dsse"
"github.com/sigstore/cosign/pkg/oci"
"github.com/sigstore/cosign/pkg/oci/layout"
ociremote "github.com/sigstore/cosign/pkg/oci/remote"
"github.com/sigstore/rekor/pkg/generated/client"
"github.com/sigstore/rekor/pkg/generated/models"
Expand Down Expand Up @@ -202,7 +203,7 @@ func (fos *fakeOCISignatures) Get() ([]oci.Signature, error) {
return fos.signatures, nil
}

// VerifySignatures does all the main cosign checks in a loop, returning the verified signatures.
// VerifyImageSignatures does all the main cosign checks in a loop, returning the verified signatures.
// If there were no valid signatures, we return an error.
func VerifyImageSignatures(ctx context.Context, signedImgRef name.Reference, co *CheckOpts) (checkedSignatures []oci.Signature, bundleVerified bool, err error) {
// Enforce this up front.
Expand Down Expand Up @@ -231,6 +232,56 @@ func VerifyImageSignatures(ctx context.Context, signedImgRef name.Reference, co
}
}

return verifySignatures(ctx, sigs, h, co)
}

// VerifyLocalImageSignatures verifies signatures from a saved, local image, without any network calls, returning the verified signatures.
// If there were no valid signatures, we return an error.
func VerifyLocalImageSignatures(ctx context.Context, path string, co *CheckOpts) (checkedSignatures []oci.Signature, bundleVerified bool, err error) {
// Enforce this up front.
if co.RootCerts == nil && co.SigVerifier == nil {
return nil, false, errors.New("one of verifier or root certs is required")
}

se, err := layout.SignedImageIndex(path)
if err != nil {
return nil, false, err
}

var h v1.Hash
// Verify either an image index or image.
ii, err := se.SignedImageIndex(v1.Hash{})
if err != nil {
return nil, false, err
}
i, err := se.SignedImage(v1.Hash{})
if err != nil {
return nil, false, err
}
switch {
case ii != nil:
h, err = ii.Digest()
if err != nil {
return nil, false, err
}
case i != nil:
h, err = i.Digest()
if err != nil {
return nil, false, err
}
default:
return nil, false, errors.New("must verify either an image index or image")
}

sigs, err := se.Signatures()
if err != nil {
return nil, false, err
}

return verifySignatures(ctx, sigs, h, co)
}

func verifySignatures(ctx context.Context, sigs oci.Signatures, h v1.Hash, co *CheckOpts) (checkedSignatures []oci.Signature, bundleVerified bool, err error) {
sl, err := sigs.Get()
if err != nil {
return nil, false, err
Expand Down
19 changes: 19 additions & 0 deletions test/e2e_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,22 @@ var verify = func(keyRef, imageRef string, checkClaims bool, annotations map[str
return cmd.Exec(context.Background(), args)
}

// Used to verify local images stored on disk
var verifyLocal = func(keyRef, path string, checkClaims bool, annotations map[string]interface{}, attachment string) error {
cmd := cliverify.VerifyCommand{
KeyRef: keyRef,
CheckClaims: checkClaims,
Annotations: sigs.AnnotationsMap{Annotations: annotations},
Attachment: attachment,
HashAlgorithm: crypto.SHA256,
LocalImage: true,
}

args := []string{path}

return cmd.Exec(context.Background(), args)
}

func TestSignVerify(t *testing.T) {
repo, stop := reg(t)
defer stop()
Expand Down Expand Up @@ -648,6 +664,9 @@ func TestSaveLoad(t *testing.T) {
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)

// load the image from the temp dir into a new image and verify the new image
imgName2 := path.Join(repo, fmt.Sprintf("save-load-%d-2", i))
must(cli.LoadCmd(ctx, options.LoadOptions{Directory: imageDir}, imgName2), t)
Expand Down

0 comments on commit 10b7f9d

Please sign in to comment.