diff --git a/cmd/notation/list.go b/cmd/notation/list.go index 5914f9df8..001994c2f 100644 --- a/cmd/notation/list.go +++ b/cmd/notation/list.go @@ -5,7 +5,7 @@ import ( "errors" "fmt" - notationRegistry "github.com/notaryproject/notation-go/registry" + notationregistry "github.com/notaryproject/notation-go/registry" "github.com/opencontainers/go-digest" ocispec "github.com/opencontainers/image-spec/specs-go/v1" "github.com/spf13/cobra" @@ -50,7 +50,7 @@ func runList(command *cobra.Command, opts *listOpts) error { } // core process - manifestDesc, err := getManifestDescriptorFromReference(command.Context(), &opts.SecureFlagOpts, reference) + manifestDesc, _, err := getManifestDescriptor(command.Context(), &opts.SecureFlagOpts, reference) if err != nil { return err } @@ -61,7 +61,7 @@ func runList(command *cobra.Command, opts *listOpts) error { // printSignatureManifestDigests returns the signature manifest digests of // the subject manifest. -func printSignatureManifestDigests(ctx context.Context, manifestDigest digest.Digest, sigRepo notationRegistry.Repository, reference string) error { +func printSignatureManifestDigests(ctx context.Context, manifestDigest digest.Digest, sigRepo notationregistry.Repository, reference string) error { // prepare title ref, err := registry.ParseReference(reference) if err != nil { @@ -72,7 +72,7 @@ func printSignatureManifestDigests(ctx context.Context, manifestDigest digest.Di printTitle := func() { if !titlePrinted { fmt.Println(ref) - fmt.Printf("└── %s\n", notationRegistry.ArtifactTypeNotation) + fmt.Printf("└── %s\n", notationregistry.ArtifactTypeNotation) titlePrinted = true } } diff --git a/cmd/notation/manifest.go b/cmd/notation/manifest.go index 472c56e15..d9fe26f68 100644 --- a/cmd/notation/manifest.go +++ b/cmd/notation/manifest.go @@ -8,22 +8,27 @@ import ( "oras.land/oras-go/v2/registry" ) -func getManifestDescriptorFromContext(ctx context.Context, opts *SecureFlagOpts, ref string) (ocispec.Descriptor, error) { - if ref == "" { - return ocispec.Descriptor{}, errors.New("missing reference") +// getManifestDescriptor returns target artifact manifest descriptor and +// registry.Reference given user input reference. +func getManifestDescriptor(ctx context.Context, opts *SecureFlagOpts, reference string) (ocispec.Descriptor, registry.Reference, error) { + if reference == "" { + return ocispec.Descriptor{}, registry.Reference{}, errors.New("missing reference") } - - return getManifestDescriptorFromReference(ctx, opts, ref) -} - -func getManifestDescriptorFromReference(ctx context.Context, opts *SecureFlagOpts, reference string) (ocispec.Descriptor, error) { ref, err := registry.ParseReference(reference) if err != nil { - return ocispec.Descriptor{}, err + return ocispec.Descriptor{}, registry.Reference{}, err + } + if ref.Reference == "" { + return ocispec.Descriptor{}, registry.Reference{}, errors.New("reference is missing digest or tag") } repo, err := getRepositoryClient(opts, ref) if err != nil { - return ocispec.Descriptor{}, err + return ocispec.Descriptor{}, registry.Reference{}, err + } + + manifestDesc, err := repo.Resolve(ctx, ref.Reference) + if err != nil { + return ocispec.Descriptor{}, registry.Reference{}, err } - return repo.Resolve(ctx, ref.ReferenceOrDefault()) + return manifestDesc, ref, nil } diff --git a/cmd/notation/sign.go b/cmd/notation/sign.go index f8d3e8280..9331ab395 100644 --- a/cmd/notation/sign.go +++ b/cmd/notation/sign.go @@ -11,6 +11,7 @@ import ( "github.com/notaryproject/notation/internal/envelope" ocispec "github.com/opencontainers/image-spec/specs-go/v1" "github.com/spf13/cobra" + "oras.land/oras-go/v2/registry" ) type signOpts struct { @@ -74,7 +75,7 @@ func runSign(command *cobra.Command, cmdOpts *signOpts) error { } // core process - desc, opts, err := prepareSigningContent(command.Context(), cmdOpts) + opts, ref, err := prepareSigningContent(command.Context(), cmdOpts) if err != nil { return err } @@ -88,30 +89,34 @@ func runSign(command *cobra.Command, cmdOpts *signOpts) error { } // write out - fmt.Println(desc.Digest) + fmt.Println("Successfully signed", ref) return nil } -func prepareSigningContent(ctx context.Context, opts *signOpts) (ocispec.Descriptor, notation.SignOptions, error) { - manifestDesc, err := getManifestDescriptorFromContext(ctx, &opts.SecureFlagOpts, opts.reference) +func prepareSigningContent(ctx context.Context, opts *signOpts) (notation.SignOptions, registry.Reference, error) { + ref, err := resolveReference(ctx, &opts.SecureFlagOpts, opts.reference, func(ref registry.Reference, manifestDesc ocispec.Descriptor) { + fmt.Printf("Warning: Always sign the artifact using digest(`@sha256:...`) rather than a tag(`:%s`) because tags are mutable and a tag reference can point to a different artifact than the one signed.\n", ref.Reference) + fmt.Printf("Resolved artifact tag `%s` to digest `%s` before signing.\n", ref.Reference, manifestDesc.Digest.String()) + }) if err != nil { - return ocispec.Descriptor{}, notation.SignOptions{}, err + return notation.SignOptions{}, registry.Reference{}, err } + mediaType, err := envelope.GetEnvelopeMediaType(opts.SignerFlagOpts.SignatureFormat) if err != nil { - return ocispec.Descriptor{}, notation.SignOptions{}, err + return notation.SignOptions{}, registry.Reference{}, err } pluginConfig, err := cmd.ParseFlagPluginConfig(opts.pluginConfig) if err != nil { - return ocispec.Descriptor{}, notation.SignOptions{}, err + return notation.SignOptions{}, registry.Reference{}, err } signOpts := notation.SignOptions{ - ArtifactReference: opts.reference, + ArtifactReference: ref.String(), SignatureMediaType: mediaType, ExpiryDuration: opts.expiry, PluginConfig: pluginConfig, } - return manifestDesc, signOpts, nil + return signOpts, ref, nil } diff --git a/cmd/notation/verify.go b/cmd/notation/verify.go index 155d9d0d8..f8cbc2a4a 100644 --- a/cmd/notation/verify.go +++ b/cmd/notation/verify.go @@ -1,16 +1,16 @@ package main import ( + "context" "errors" + "fmt" "math" - "os" - "strings" "github.com/notaryproject/notation-go" notationregistry "github.com/notaryproject/notation-go/registry" "github.com/notaryproject/notation-go/verifier" "github.com/notaryproject/notation/internal/cmd" - "github.com/notaryproject/notation/internal/ioutil" + ocispec "github.com/opencontainers/image-spec/specs-go/v1" "github.com/spf13/cobra" "oras.land/oras-go/v2/registry" @@ -57,13 +57,16 @@ Example - Verify a signature on an OCI artifact identified by a tag (Notation w } func runVerify(command *cobra.Command, opts *verifyOpts) error { - // resolve the given reference and set the digest. - ref, err := resolveReference(command, opts) + // resolve the given reference and set the digest + ref, err := resolveReference(command.Context(), &opts.SecureFlagOpts, opts.reference, func(ref registry.Reference, manifestDesc ocispec.Descriptor) { + fmt.Printf("Resolved artifact tag `%s` to digest `%s` before verification.\n", ref.Reference, manifestDesc.Digest.String()) + fmt.Println("Warning: The resolved digest may not point to the same signed artifact, since tags are mutable.") + }) if err != nil { return err } - // initialize verifier. + // initialize verifier verifier, err := verifier.NewFromConfig() if err != nil { return err @@ -76,7 +79,7 @@ func runVerify(command *cobra.Command, opts *verifyOpts) error { } repo := notationregistry.NewRepository(&remoteRepo) - // set up verification plugin config. + // set up verification plugin config configs, err := cmd.ParseFlagPluginConfig(opts.pluginConfig) if err != nil { return err @@ -90,39 +93,44 @@ func runVerify(command *cobra.Command, opts *verifyOpts) error { MaxSignatureAttempts: math.MaxInt64, } - // core verify process. + // core verify process _, outcomes, err := notation.Verify(command.Context(), verifier, repo, verifyOpts) - // write out. - return ioutil.PrintVerificationResults(os.Stdout, outcomes, err, ref.Reference) + // write out + // on failure + if err != nil || len(outcomes) == 0 { + return fmt.Errorf("signature verification failed for all the signatures associated with %s", ref.String()) + } + + // on success + outcome := outcomes[0] + // print out warning for any failed result with logged verification action + for _, result := range outcome.VerificationResults { + if result.Error != nil { + // at this point, the verification action has to be logged and + // it's failed + fmt.Printf("Warning: %v was set to \"logged\" and failed with error: %v\n", result.Type, result.Error) + } + } + fmt.Println("Successfully verified signature for", ref.String()) + return nil } -func resolveReference(command *cobra.Command, opts *verifyOpts) (registry.Reference, error) { - ref, err := registry.ParseReference(opts.reference) +func resolveReference(ctx context.Context, opts *SecureFlagOpts, reference string, fn func(registry.Reference, ocispec.Descriptor)) (registry.Reference, error) { + manifestDesc, ref, err := getManifestDescriptor(ctx, opts, reference) if err != nil { return registry.Reference{}, err } - if isDigestReference(opts.reference) { + // reference is a digest reference + if err := ref.ValidateReferenceAsDigest(); err == nil { return ref, nil } - // Resolve tag reference to digest reference. - manifestDesc, err := getManifestDescriptorFromReference(command.Context(), &opts.SecureFlagOpts, opts.reference) - if err != nil { - return registry.Reference{}, err - } - + // reference is a tag reference + fn(ref, manifestDesc) + // resolve tag to digest reference ref.Reference = manifestDesc.Digest.String() - return ref, nil -} -func isDigestReference(reference string) bool { - parts := strings.SplitN(reference, "/", 2) - if len(parts) == 1 { - return false - } - - index := strings.Index(parts[1], "@") - return index != -1 + return ref, nil } diff --git a/go.mod b/go.mod index 9517dc154..2f7a21808 100644 --- a/go.mod +++ b/go.mod @@ -6,7 +6,7 @@ require ( github.com/distribution/distribution/v3 v3.0.0-20220729163034-26163d82560f github.com/docker/docker-credential-helpers v0.7.0 github.com/notaryproject/notation-core-go v0.2.0-beta.1.0.20221123104522-9b5de089a023 - github.com/notaryproject/notation-go v0.12.0-beta.1.0.20221202040523-bc022cc61d50 + github.com/notaryproject/notation-go v0.12.0-beta.1.0.20221205052202-e9545a718368 github.com/opencontainers/go-digest v1.0.0 github.com/opencontainers/image-spec v1.1.0-rc2 github.com/spf13/cobra v1.6.1 diff --git a/go.sum b/go.sum index 80bc38f57..f736ce582 100644 --- a/go.sum +++ b/go.sum @@ -21,8 +21,8 @@ github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2 github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= github.com/notaryproject/notation-core-go v0.2.0-beta.1.0.20221123104522-9b5de089a023 h1:Z/2hxPJOjWfmgOPTNkGBDp/LVIEtizd9uJNQvjFE0Dc= github.com/notaryproject/notation-core-go v0.2.0-beta.1.0.20221123104522-9b5de089a023/go.mod h1:n8Gbvl9sKa00KptkKEL5XKUyMTIALe74QipKauE2rj4= -github.com/notaryproject/notation-go v0.12.0-beta.1.0.20221202040523-bc022cc61d50 h1:1i9PCRE6fLzYDxAE2HjDvXD1+U+5z25bWWwJOV2mk78= -github.com/notaryproject/notation-go v0.12.0-beta.1.0.20221202040523-bc022cc61d50/go.mod h1:2Xy40C9rJip3h9XPC6ei2HEEdUoZJ5KDC6mlX/FD0oQ= +github.com/notaryproject/notation-go v0.12.0-beta.1.0.20221205052202-e9545a718368 h1:OIrolpRY9PpFeWPtMPEOKNdYf+2TI13XY6gmmkJc+JY= +github.com/notaryproject/notation-go v0.12.0-beta.1.0.20221205052202-e9545a718368/go.mod h1:2Xy40C9rJip3h9XPC6ei2HEEdUoZJ5KDC6mlX/FD0oQ= github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U= github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM= github.com/opencontainers/image-spec v1.1.0-rc2 h1:2zx/Stx4Wc5pIPDvIxHXvXtQFW/7XWJGmnM7r3wg034= diff --git a/internal/ioutil/print.go b/internal/ioutil/print.go index 8d91a9252..c7661d76e 100644 --- a/internal/ioutil/print.go +++ b/internal/ioutil/print.go @@ -5,7 +5,6 @@ import ( "io" "text/tabwriter" - "github.com/notaryproject/notation-go" "github.com/notaryproject/notation-go/config" ) @@ -33,17 +32,3 @@ func PrintKeyMap(w io.Writer, target string, v []config.KeySuite) error { } return tw.Flush() } - -func PrintVerificationResults(w io.Writer, v []*notation.VerificationOutcome, resultErr error, digest string) error { - tw := newTabWriter(w) - - if resultErr == nil { - fmt.Fprintf(tw, "Successfully verified for %s\n", digest) - // TODO[https://github.com/notaryproject/notation/issues/304]: print out failed validations as warnings. - return nil - } - fmt.Printf("Signature verification failed for all the signatures associated with digest: %s\n", digest) - tw.Flush() - - return resultErr -}