From f6a58432bf4bde5a92d0ee81f8a082eeb53efee1 Mon Sep 17 00:00:00 2001 From: Patrick Zheng Date: Wed, 11 Jan 2023 17:48:16 +0800 Subject: [PATCH 01/28] initial update Signed-off-by: Patrick Zheng --- cmd/notation/sign.go | 9 ++++++--- go.mod | 4 +++- go.sum | 4 ++-- 3 files changed, 11 insertions(+), 6 deletions(-) diff --git a/cmd/notation/sign.go b/cmd/notation/sign.go index 74ce1d8dd..d0e16e079 100644 --- a/cmd/notation/sign.go +++ b/cmd/notation/sign.go @@ -19,9 +19,10 @@ type signOpts struct { cmd.LoggingFlagOpts cmd.SignerFlagOpts SecureFlagOpts - expiry time.Duration - pluginConfig []string - reference string + expiry time.Duration + pluginConfig []string + reference string + ociImageManifest bool } func signCommand(opts *signOpts) *cobra.Command { @@ -66,6 +67,7 @@ Example - Sign an OCI artifact stored in a registry and specify the signature ex opts.SecureFlagOpts.ApplyFlags(command.Flags()) cmd.SetPflagExpiry(command.Flags(), &opts.expiry) cmd.SetPflagPluginConfig(command.Flags(), &opts.pluginConfig) + command.Flags().BoolVar(&opts.ociImageManifest, "use-image-manifest", false, "optional for using OCI image manifest to store signatures in a registry") return command } @@ -121,6 +123,7 @@ func prepareSigningContent(ctx context.Context, opts *signOpts, sigRepo notation SignatureMediaType: mediaType, ExpiryDuration: opts.expiry, PluginConfig: pluginConfig, + OCIImageManifest: true, } return signOpts, ref, nil diff --git a/go.mod b/go.mod index 1af3fae1b..b2f77ab84 100644 --- a/go.mod +++ b/go.mod @@ -11,7 +11,6 @@ require ( github.com/sirupsen/logrus v1.9.0 github.com/spf13/cobra v1.6.1 github.com/spf13/pflag v1.0.5 - github.com/veraison/go-cose v1.0.0-rc.2 oras.land/oras-go/v2 v2.0.0-rc.6 ) @@ -22,8 +21,11 @@ require ( github.com/go-ldap/ldap/v3 v3.4.4 // indirect github.com/golang-jwt/jwt/v4 v4.4.3 // indirect github.com/inconshreveable/mousetrap v1.1.0 // indirect + github.com/veraison/go-cose v1.0.0-rc.2 // indirect github.com/x448/float16 v0.8.4 // indirect golang.org/x/crypto v0.3.0 // indirect golang.org/x/sync v0.1.0 // indirect golang.org/x/sys v0.2.0 // indirect ) + +replace github.com/notaryproject/notation-go => github.com/patrickzheng200/notation-go v0.9.0-alpha.1.0.20230111041406-0a0293893b03 diff --git a/go.sum b/go.sum index 661deb4ef..6a844ef1b 100644 --- a/go.sum +++ b/go.sum @@ -20,12 +20,12 @@ 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 v1.0.0-rc.1 h1:ACi0gr6mD1bzp9+gu3P0meJ/N6iWHlyM9zgtdnooNAA= github.com/notaryproject/notation-core-go v1.0.0-rc.1/go.mod h1:n8Gbvl9sKa00KptkKEL5XKUyMTIALe74QipKauE2rj4= -github.com/notaryproject/notation-go v1.0.0-rc.1 h1:WobIGCUPcPUDCD2qGMCccTfLm2J5y1bsh4SFVsyMIaA= -github.com/notaryproject/notation-go v1.0.0-rc.1/go.mod h1:xk4q0GXqGfEiy7WmyHi3Om3OM2dgHk0OHL6NIiJv5vA= 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= github.com/opencontainers/image-spec v1.1.0-rc2/go.mod h1:3OVijpioIKYWTqjiG0zfF6wvoJ4fAXGbjdZuI2NgsRQ= +github.com/patrickzheng200/notation-go v0.9.0-alpha.1.0.20230111041406-0a0293893b03 h1:EPG9o704Mqq7ehcIJ8vAr6dG8kWzCqL1oQzIoN3Z948= +github.com/patrickzheng200/notation-go v0.9.0-alpha.1.0.20230111041406-0a0293893b03/go.mod h1:zPpwoqwbgoR3tZH/oQT3vi22D3p2UdvM0DpVmBp7r+U= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= From ac6e77c816ff09676b6ada14a19da38fefac1121 Mon Sep 17 00:00:00 2001 From: Patrick Zheng Date: Thu, 12 Jan 2023 16:04:56 +0800 Subject: [PATCH 02/28] Added OCI image manifest support Signed-off-by: Patrick Zheng --- cmd/notation/sign.go | 2 +- go.mod | 2 +- go.sum | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/cmd/notation/sign.go b/cmd/notation/sign.go index d0e16e079..cb9aeda37 100644 --- a/cmd/notation/sign.go +++ b/cmd/notation/sign.go @@ -123,7 +123,7 @@ func prepareSigningContent(ctx context.Context, opts *signOpts, sigRepo notation SignatureMediaType: mediaType, ExpiryDuration: opts.expiry, PluginConfig: pluginConfig, - OCIImageManifest: true, + OCIimageManifest: opts.ociImageManifest, } return signOpts, ref, nil diff --git a/go.mod b/go.mod index b2f77ab84..22b85713a 100644 --- a/go.mod +++ b/go.mod @@ -28,4 +28,4 @@ require ( golang.org/x/sys v0.2.0 // indirect ) -replace github.com/notaryproject/notation-go => github.com/patrickzheng200/notation-go v0.9.0-alpha.1.0.20230111041406-0a0293893b03 +replace github.com/notaryproject/notation-go => github.com/patrickzheng200/notation-go v0.9.0-alpha.1.0.20230112075024-e97809923987 diff --git a/go.sum b/go.sum index 6a844ef1b..f15ec846d 100644 --- a/go.sum +++ b/go.sum @@ -24,8 +24,8 @@ github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8 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= github.com/opencontainers/image-spec v1.1.0-rc2/go.mod h1:3OVijpioIKYWTqjiG0zfF6wvoJ4fAXGbjdZuI2NgsRQ= -github.com/patrickzheng200/notation-go v0.9.0-alpha.1.0.20230111041406-0a0293893b03 h1:EPG9o704Mqq7ehcIJ8vAr6dG8kWzCqL1oQzIoN3Z948= -github.com/patrickzheng200/notation-go v0.9.0-alpha.1.0.20230111041406-0a0293893b03/go.mod h1:zPpwoqwbgoR3tZH/oQT3vi22D3p2UdvM0DpVmBp7r+U= +github.com/patrickzheng200/notation-go v0.9.0-alpha.1.0.20230112075024-e97809923987 h1:J1vzS7UwoAj/Yz8Ez1lwIBP1vrkDSRZ4UD+u6ew97GE= +github.com/patrickzheng200/notation-go v0.9.0-alpha.1.0.20230112075024-e97809923987/go.mod h1:zPpwoqwbgoR3tZH/oQT3vi22D3p2UdvM0DpVmBp7r+U= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= From bb3b0e7d767b5430470ac3cdadc0fdcb07861766 Mon Sep 17 00:00:00 2001 From: Patrick Zheng Date: Fri, 13 Jan 2023 19:16:45 +0800 Subject: [PATCH 03/28] update Signed-off-by: Patrick Zheng --- cmd/notation/list.go | 2 +- cmd/notation/registry.go | 11 +++++++---- cmd/notation/sign.go | 35 ++++++++++++++++++++++++++++------- cmd/notation/verify.go | 2 +- go.mod | 2 +- go.sum | 4 ++-- internal/slices/slices.go | 10 ++++++++++ 7 files changed, 50 insertions(+), 16 deletions(-) diff --git a/cmd/notation/list.go b/cmd/notation/list.go index 92c5c8880..4fc5a98f4 100644 --- a/cmd/notation/list.go +++ b/cmd/notation/list.go @@ -50,7 +50,7 @@ func runList(ctx context.Context, opts *listOpts) error { // initialize reference := opts.reference - sigRepo, err := getSignatureRepository(ctx, &opts.SecureFlagOpts, reference) + sigRepo, err := getSignatureRepository(ctx, &opts.SecureFlagOpts, reference, false) if err != nil { return err } diff --git a/cmd/notation/registry.go b/cmd/notation/registry.go index 64f3d17e3..491b044f6 100644 --- a/cmd/notation/registry.go +++ b/cmd/notation/registry.go @@ -18,14 +18,14 @@ import ( "oras.land/oras-go/v2/registry/remote/auth" ) -func getSignatureRepository(ctx context.Context, opts *SecureFlagOpts, reference string) (notationregistry.Repository, error) { +func getSignatureRepository(ctx context.Context, opts *SecureFlagOpts, reference string, ociImageManifest bool) (notationregistry.Repository, error) { ref, err := registry.ParseReference(reference) if err != nil { return nil, err } // generate notation repository - return getRepositoryClient(ctx, opts, ref) + return getRepositoryClient(ctx, opts, ref, ociImageManifest) } func getRegistryClient(ctx context.Context, opts *SecureFlagOpts, serverAddress string) (*remote.Registry, error) { @@ -41,7 +41,7 @@ func getRegistryClient(ctx context.Context, opts *SecureFlagOpts, serverAddress return reg, nil } -func getRepositoryClient(ctx context.Context, opts *SecureFlagOpts, ref registry.Reference) (notationregistry.Repository, error) { +func getRepositoryClient(ctx context.Context, opts *SecureFlagOpts, ref registry.Reference, ociImageManifest bool) (notationregistry.Repository, error) { authClient, plainHTTP, err := getAuthClient(ctx, opts, ref) if err != nil { return nil, err @@ -52,7 +52,10 @@ func getRepositoryClient(ctx context.Context, opts *SecureFlagOpts, ref registry Reference: ref, PlainHTTP: plainHTTP, } - return notationregistry.NewRepository(remoteRepo), nil + repositoryOpts := notationregistry.RepositoryOptions{ + OCIImageManifest: ociImageManifest, + } + return notationregistry.NewRepositoryWithOptions(remoteRepo, repositoryOpts), nil } func setHttpDebugLog(ctx context.Context, authClient *auth.Client) { diff --git a/cmd/notation/sign.go b/cmd/notation/sign.go index cb9aeda37..a125db046 100644 --- a/cmd/notation/sign.go +++ b/cmd/notation/sign.go @@ -10,6 +10,7 @@ import ( notationregistry "github.com/notaryproject/notation-go/registry" "github.com/notaryproject/notation/internal/cmd" "github.com/notaryproject/notation/internal/envelope" + "github.com/notaryproject/notation/internal/slices" ocispec "github.com/opencontainers/image-spec/specs-go/v1" "github.com/spf13/cobra" "oras.land/oras-go/v2/registry" @@ -19,12 +20,20 @@ type signOpts struct { cmd.LoggingFlagOpts cmd.SignerFlagOpts SecureFlagOpts - expiry time.Duration - pluginConfig []string - reference string - ociImageManifest bool + expiry time.Duration + pluginConfig []string + reference string + imageSpec string } +const ( + artifactManifest = "v1.1-artifact" + + imageManifest = "v1.1-image" +) + +var supportedImageSpec = []string{artifactManifest, imageManifest} + func signCommand(opts *signOpts) *cobra.Command { if opts == nil { opts = &signOpts{} @@ -67,11 +76,16 @@ Example - Sign an OCI artifact stored in a registry and specify the signature ex opts.SecureFlagOpts.ApplyFlags(command.Flags()) cmd.SetPflagExpiry(command.Flags(), &opts.expiry) cmd.SetPflagPluginConfig(command.Flags(), &opts.pluginConfig) - command.Flags().BoolVar(&opts.ociImageManifest, "use-image-manifest", false, "optional for using OCI image manifest to store signatures in a registry") + command.Flags().StringVar(&opts.imageSpec, "image-spec", "v1.1-artifact", "manifest type for signatures. options: v1.1-artifact, v1.1-image") return command } func runSign(command *cobra.Command, cmdOpts *signOpts) error { + // sanity check + if !validateImageSpec(cmdOpts.imageSpec) { + return fmt.Errorf("image spec has to be in %v, got %s", supportedImageSpec, cmdOpts.imageSpec) + } + // set log level ctx := cmdOpts.LoggingFlagOpts.SetLoggerLevel(command.Context()) @@ -80,7 +94,11 @@ func runSign(command *cobra.Command, cmdOpts *signOpts) error { if err != nil { return err } - sigRepo, err := getSignatureRepository(ctx, &cmdOpts.SecureFlagOpts, cmdOpts.reference) + var ociImageManifest bool + if cmdOpts.imageSpec == imageManifest { + ociImageManifest = true + } + sigRepo, err := getSignatureRepository(ctx, &cmdOpts.SecureFlagOpts, cmdOpts.reference, ociImageManifest) if err != nil { return err } @@ -123,8 +141,11 @@ func prepareSigningContent(ctx context.Context, opts *signOpts, sigRepo notation SignatureMediaType: mediaType, ExpiryDuration: opts.expiry, PluginConfig: pluginConfig, - OCIimageManifest: opts.ociImageManifest, } return signOpts, ref, nil } + +func validateImageSpec(imageSpec string) bool { + return slices.ContainsGenerics(supportedImageSpec, imageSpec) +} diff --git a/cmd/notation/verify.go b/cmd/notation/verify.go index 387e045dc..5e2a81fe9 100644 --- a/cmd/notation/verify.go +++ b/cmd/notation/verify.go @@ -65,7 +65,7 @@ func runVerify(command *cobra.Command, opts *verifyOpts) error { // initialize reference := opts.reference - sigRepo, err := getSignatureRepository(ctx, &opts.SecureFlagOpts, reference) + sigRepo, err := getSignatureRepository(ctx, &opts.SecureFlagOpts, reference, false) if err != nil { return err } diff --git a/go.mod b/go.mod index 22b85713a..1d1ce2d7e 100644 --- a/go.mod +++ b/go.mod @@ -28,4 +28,4 @@ require ( golang.org/x/sys v0.2.0 // indirect ) -replace github.com/notaryproject/notation-go => github.com/patrickzheng200/notation-go v0.9.0-alpha.1.0.20230112075024-e97809923987 +replace github.com/notaryproject/notation-go => github.com/patrickzheng200/notation-go v0.9.0-alpha.1.0.20230113071526-1a12b76d2c40 diff --git a/go.sum b/go.sum index f15ec846d..23ed41a4f 100644 --- a/go.sum +++ b/go.sum @@ -24,8 +24,8 @@ github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8 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= github.com/opencontainers/image-spec v1.1.0-rc2/go.mod h1:3OVijpioIKYWTqjiG0zfF6wvoJ4fAXGbjdZuI2NgsRQ= -github.com/patrickzheng200/notation-go v0.9.0-alpha.1.0.20230112075024-e97809923987 h1:J1vzS7UwoAj/Yz8Ez1lwIBP1vrkDSRZ4UD+u6ew97GE= -github.com/patrickzheng200/notation-go v0.9.0-alpha.1.0.20230112075024-e97809923987/go.mod h1:zPpwoqwbgoR3tZH/oQT3vi22D3p2UdvM0DpVmBp7r+U= +github.com/patrickzheng200/notation-go v0.9.0-alpha.1.0.20230113071526-1a12b76d2c40 h1:Uo05aBoihwRoVkImaDFvDt45u5mzX4vo4fTY5SVPJFc= +github.com/patrickzheng200/notation-go v0.9.0-alpha.1.0.20230113071526-1a12b76d2c40/go.mod h1:zPpwoqwbgoR3tZH/oQT3vi22D3p2UdvM0DpVmBp7r+U= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= diff --git a/internal/slices/slices.go b/internal/slices/slices.go index 1fffa0883..76b23d0da 100644 --- a/internal/slices/slices.go +++ b/internal/slices/slices.go @@ -25,3 +25,13 @@ func Contains[E isser](s []E, name string) bool { func Delete[S ~[]E, E isser](s S, i int) S { return append(s[:i], s[i+1:]...) } + +// Contains reports whether v is present in s. +func ContainsGenerics[E comparable](s []E, v E) bool { + for _, vs := range s { + if v == vs { + return true + } + } + return false +} From c8c3cc061f9c1bf72ad3093e7455ebe9052bf9f6 Mon Sep 17 00:00:00 2001 From: Patrick Zheng Date: Mon, 16 Jan 2023 09:39:09 +0800 Subject: [PATCH 04/28] fixed unit tests Signed-off-by: Patrick Zheng --- cmd/notation/sign.go | 18 +++++++++--------- cmd/notation/sign_test.go | 8 ++++++-- go.mod | 2 +- go.sum | 4 ++-- 4 files changed, 18 insertions(+), 14 deletions(-) diff --git a/cmd/notation/sign.go b/cmd/notation/sign.go index a125db046..eb5d31eee 100644 --- a/cmd/notation/sign.go +++ b/cmd/notation/sign.go @@ -16,6 +16,14 @@ import ( "oras.land/oras-go/v2/registry" ) +const ( + artifactManifest = "v1.1-artifact" + + imageManifest = "v1.1-image" +) + +var supportedImageSpec = []string{artifactManifest, imageManifest} + type signOpts struct { cmd.LoggingFlagOpts cmd.SignerFlagOpts @@ -26,14 +34,6 @@ type signOpts struct { imageSpec string } -const ( - artifactManifest = "v1.1-artifact" - - imageManifest = "v1.1-image" -) - -var supportedImageSpec = []string{artifactManifest, imageManifest} - func signCommand(opts *signOpts) *cobra.Command { if opts == nil { opts = &signOpts{} @@ -76,7 +76,7 @@ Example - Sign an OCI artifact stored in a registry and specify the signature ex opts.SecureFlagOpts.ApplyFlags(command.Flags()) cmd.SetPflagExpiry(command.Flags(), &opts.expiry) cmd.SetPflagPluginConfig(command.Flags(), &opts.pluginConfig) - command.Flags().StringVar(&opts.imageSpec, "image-spec", "v1.1-artifact", "manifest type for signatures. options: v1.1-artifact, v1.1-image") + command.Flags().StringVar(&opts.imageSpec, "image-spec", artifactManifest, "manifest type for signatures. options: v1.1-artifact, v1.1-image") return command } diff --git a/cmd/notation/sign_test.go b/cmd/notation/sign_test.go index 587f9bafc..2debdb436 100644 --- a/cmd/notation/sign_test.go +++ b/cmd/notation/sign_test.go @@ -23,6 +23,7 @@ func TestSignCommand_BasicArgs(t *testing.T) { Key: "key", SignatureFormat: envelope.JWS, }, + imageSpec: artifactManifest, } if err := command.ParseFlags([]string{ expected.reference, @@ -53,7 +54,8 @@ func TestSignCommand_MoreArgs(t *testing.T) { Key: "key", SignatureFormat: envelope.COSE, }, - expiry: 24 * time.Hour, + expiry: 24 * time.Hour, + imageSpec: imageManifest, } if err := command.ParseFlags([]string{ expected.reference, @@ -62,7 +64,8 @@ func TestSignCommand_MoreArgs(t *testing.T) { "--key", expected.Key, "--plain-http", "--signature-format", expected.SignerFlagOpts.SignatureFormat, - "--expiry", expected.expiry.String()}); err != nil { + "--expiry", expected.expiry.String(), + "--image-spec", imageManifest}); err != nil { t.Fatalf("Parse Flag failed: %v", err) } if err := command.Args(command, command.Flags().Args()); err != nil { @@ -84,6 +87,7 @@ func TestSignCommand_CorrectConfig(t *testing.T) { }, expiry: 365 * 24 * time.Hour, pluginConfig: []string{"key0=val0", "key1=val1"}, + imageSpec: artifactManifest, } if err := command.ParseFlags([]string{ expected.reference, diff --git a/go.mod b/go.mod index 1d1ce2d7e..284bd95ac 100644 --- a/go.mod +++ b/go.mod @@ -28,4 +28,4 @@ require ( golang.org/x/sys v0.2.0 // indirect ) -replace github.com/notaryproject/notation-go => github.com/patrickzheng200/notation-go v0.9.0-alpha.1.0.20230113071526-1a12b76d2c40 +replace github.com/notaryproject/notation-go => github.com/patrickzheng200/notation-go v0.9.0-alpha.1.0.20230113104720-50a970a3cb8d diff --git a/go.sum b/go.sum index 23ed41a4f..1a3555dd6 100644 --- a/go.sum +++ b/go.sum @@ -24,8 +24,8 @@ github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8 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= github.com/opencontainers/image-spec v1.1.0-rc2/go.mod h1:3OVijpioIKYWTqjiG0zfF6wvoJ4fAXGbjdZuI2NgsRQ= -github.com/patrickzheng200/notation-go v0.9.0-alpha.1.0.20230113071526-1a12b76d2c40 h1:Uo05aBoihwRoVkImaDFvDt45u5mzX4vo4fTY5SVPJFc= -github.com/patrickzheng200/notation-go v0.9.0-alpha.1.0.20230113071526-1a12b76d2c40/go.mod h1:zPpwoqwbgoR3tZH/oQT3vi22D3p2UdvM0DpVmBp7r+U= +github.com/patrickzheng200/notation-go v0.9.0-alpha.1.0.20230113104720-50a970a3cb8d h1:5TtrHXs2YKujrKNgKbxXwS665BNtl5Dl04ZS4IKtfaw= +github.com/patrickzheng200/notation-go v0.9.0-alpha.1.0.20230113104720-50a970a3cb8d/go.mod h1:zPpwoqwbgoR3tZH/oQT3vi22D3p2UdvM0DpVmBp7r+U= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= From 525e1f68b12e77b7b5ba82ff4ed1a0355653fa5e Mon Sep 17 00:00:00 2001 From: Patrick Zheng Date: Mon, 16 Jan 2023 10:13:44 +0800 Subject: [PATCH 05/28] go mod tidy Signed-off-by: Patrick Zheng --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 284bd95ac..118e2c6e1 100644 --- a/go.mod +++ b/go.mod @@ -28,4 +28,4 @@ require ( golang.org/x/sys v0.2.0 // indirect ) -replace github.com/notaryproject/notation-go => github.com/patrickzheng200/notation-go v0.9.0-alpha.1.0.20230113104720-50a970a3cb8d +replace github.com/notaryproject/notation-go => github.com/patrickzheng200/notation-go v0.9.0-alpha.1.0.20230116020158-f3e0421fae95 diff --git a/go.sum b/go.sum index 1a3555dd6..f5cd0db53 100644 --- a/go.sum +++ b/go.sum @@ -24,8 +24,8 @@ github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8 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= github.com/opencontainers/image-spec v1.1.0-rc2/go.mod h1:3OVijpioIKYWTqjiG0zfF6wvoJ4fAXGbjdZuI2NgsRQ= -github.com/patrickzheng200/notation-go v0.9.0-alpha.1.0.20230113104720-50a970a3cb8d h1:5TtrHXs2YKujrKNgKbxXwS665BNtl5Dl04ZS4IKtfaw= -github.com/patrickzheng200/notation-go v0.9.0-alpha.1.0.20230113104720-50a970a3cb8d/go.mod h1:zPpwoqwbgoR3tZH/oQT3vi22D3p2UdvM0DpVmBp7r+U= +github.com/patrickzheng200/notation-go v0.9.0-alpha.1.0.20230116020158-f3e0421fae95 h1:03nSJeCy4yfQj+GEOPv+IjYkgBYQGN0kFywLJmAdp40= +github.com/patrickzheng200/notation-go v0.9.0-alpha.1.0.20230116020158-f3e0421fae95/go.mod h1:zPpwoqwbgoR3tZH/oQT3vi22D3p2UdvM0DpVmBp7r+U= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= From 1130d221854b8c72a5438ed3bac9eec3a31e70bf Mon Sep 17 00:00:00 2001 From: Patrick Zheng Date: Mon, 16 Jan 2023 11:26:49 +0800 Subject: [PATCH 06/28] go mod tidy Signed-off-by: Patrick Zheng --- go.mod | 2 -- go.sum | 4 ++-- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/go.mod b/go.mod index 118e2c6e1..728313595 100644 --- a/go.mod +++ b/go.mod @@ -27,5 +27,3 @@ require ( golang.org/x/sync v0.1.0 // indirect golang.org/x/sys v0.2.0 // indirect ) - -replace github.com/notaryproject/notation-go => github.com/patrickzheng200/notation-go v0.9.0-alpha.1.0.20230116020158-f3e0421fae95 diff --git a/go.sum b/go.sum index f5cd0db53..661deb4ef 100644 --- a/go.sum +++ b/go.sum @@ -20,12 +20,12 @@ 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 v1.0.0-rc.1 h1:ACi0gr6mD1bzp9+gu3P0meJ/N6iWHlyM9zgtdnooNAA= github.com/notaryproject/notation-core-go v1.0.0-rc.1/go.mod h1:n8Gbvl9sKa00KptkKEL5XKUyMTIALe74QipKauE2rj4= +github.com/notaryproject/notation-go v1.0.0-rc.1 h1:WobIGCUPcPUDCD2qGMCccTfLm2J5y1bsh4SFVsyMIaA= +github.com/notaryproject/notation-go v1.0.0-rc.1/go.mod h1:xk4q0GXqGfEiy7WmyHi3Om3OM2dgHk0OHL6NIiJv5vA= 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= github.com/opencontainers/image-spec v1.1.0-rc2/go.mod h1:3OVijpioIKYWTqjiG0zfF6wvoJ4fAXGbjdZuI2NgsRQ= -github.com/patrickzheng200/notation-go v0.9.0-alpha.1.0.20230116020158-f3e0421fae95 h1:03nSJeCy4yfQj+GEOPv+IjYkgBYQGN0kFywLJmAdp40= -github.com/patrickzheng200/notation-go v0.9.0-alpha.1.0.20230116020158-f3e0421fae95/go.mod h1:zPpwoqwbgoR3tZH/oQT3vi22D3p2UdvM0DpVmBp7r+U= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= From be4d7bcf68c7f7f86548c9ba075a2f5399adb140 Mon Sep 17 00:00:00 2001 From: Patrick Zheng Date: Mon, 16 Jan 2023 13:09:43 +0800 Subject: [PATCH 07/28] updated spec docs Signed-off-by: Patrick Zheng --- go.mod | 2 ++ go.sum | 4 ++-- specs/commandline/list.md | 2 ++ specs/commandline/sign.md | 4 +++- specs/commandline/verify.md | 14 ++++++++------ 5 files changed, 17 insertions(+), 9 deletions(-) diff --git a/go.mod b/go.mod index 728313595..118e2c6e1 100644 --- a/go.mod +++ b/go.mod @@ -27,3 +27,5 @@ require ( golang.org/x/sync v0.1.0 // indirect golang.org/x/sys v0.2.0 // indirect ) + +replace github.com/notaryproject/notation-go => github.com/patrickzheng200/notation-go v0.9.0-alpha.1.0.20230116020158-f3e0421fae95 diff --git a/go.sum b/go.sum index 661deb4ef..f5cd0db53 100644 --- a/go.sum +++ b/go.sum @@ -20,12 +20,12 @@ 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 v1.0.0-rc.1 h1:ACi0gr6mD1bzp9+gu3P0meJ/N6iWHlyM9zgtdnooNAA= github.com/notaryproject/notation-core-go v1.0.0-rc.1/go.mod h1:n8Gbvl9sKa00KptkKEL5XKUyMTIALe74QipKauE2rj4= -github.com/notaryproject/notation-go v1.0.0-rc.1 h1:WobIGCUPcPUDCD2qGMCccTfLm2J5y1bsh4SFVsyMIaA= -github.com/notaryproject/notation-go v1.0.0-rc.1/go.mod h1:xk4q0GXqGfEiy7WmyHi3Om3OM2dgHk0OHL6NIiJv5vA= 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= github.com/opencontainers/image-spec v1.1.0-rc2/go.mod h1:3OVijpioIKYWTqjiG0zfF6wvoJ4fAXGbjdZuI2NgsRQ= +github.com/patrickzheng200/notation-go v0.9.0-alpha.1.0.20230116020158-f3e0421fae95 h1:03nSJeCy4yfQj+GEOPv+IjYkgBYQGN0kFywLJmAdp40= +github.com/patrickzheng200/notation-go v0.9.0-alpha.1.0.20230116020158-f3e0421fae95/go.mod h1:zPpwoqwbgoR3tZH/oQT3vi22D3p2UdvM0DpVmBp7r+U= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= diff --git a/specs/commandline/list.md b/specs/commandline/list.md index e62c60c74..4b1b39f23 100644 --- a/specs/commandline/list.md +++ b/specs/commandline/list.md @@ -27,10 +27,12 @@ Aliases: list, ls Flags: + -d, --debug debug mode -h, --help help for list -p, --password string password for registry operations (default to $NOTATION_PASSWORD if not specified) --plain-http registry access via plain HTTP -u, --username string username for registry operations (default to $NOTATION_USERNAME if not specified) + -v, --verbose verbose mode ``` ## Usage diff --git a/specs/commandline/sign.md b/specs/commandline/sign.md index b8ea9c9f4..50e8e3279 100644 --- a/specs/commandline/sign.md +++ b/specs/commandline/sign.md @@ -29,9 +29,10 @@ Usage: notation sign [flags] Flags: + -d, --debug debug mode -e, --expiry duration optional expiry that provides a "best by use" time for the artifact. The duration is specified in minutes(m) and/or hours(h). For example: 12h, 30m, 3h20m -h, --help help for sign - --image-spec string manifest type for signatures. options: v1.1-artifact, v1.1-image (default: v1.1-artifact) + --image-spec string manifest type for signatures. options: v1.1-artifact, v1.1-image (default "v1.1-artifact") -k, --key string signing key name, for a key previously added to notation's key list. -p, --password string password for registry operations (default to $NOTATION_PASSWORD if not specified) --plain-http registry access via plain HTTP @@ -39,6 +40,7 @@ Flags: --signature-format string signature envelope format, options: 'jws', 'cose' (default "jws") -u, --username string username for registry operations (default to $NOTATION_USERNAME if not specified) -m, --user-metadata strings {key}={value} pairs that are added to the signature payload + -v, --verbose verbose mode ``` ## Use OCI image manifest to store signatures diff --git a/specs/commandline/verify.md b/specs/commandline/verify.md index e0d8b930d..091ef3452 100644 --- a/specs/commandline/verify.md +++ b/specs/commandline/verify.md @@ -36,12 +36,14 @@ Usage: notation verify [flags] Flags: - -h, --help help for verify - -p, --password string password for registry operations (default to $NOTATION_PASSWORD if not specified) - --plain-http registry access via plain HTTP - --plugin-config strings {key}={value} pairs that are passed as it is to a plugin, if the verification is associated with a verification plugin, refer plugin documentation to set appropriate values - -u, --username string username for registry operations (default to $NOTATION_USERNAME if not specified) - -m, --user-metadata strings user defined {key}={value} pairs that must be present in the signature for successful verification if provided + -d, --debug debug mode + -h, --help help for verify + -p, --password string password for registry operations (default to $NOTATION_PASSWORD if not specified) + --plain-http registry access via plain HTTP + -c, --plugin-config stringArray {key}={value} pairs that are passed as it is to a plugin, if the verification is associated with a verification plugin, refer plugin documentation to set appropriate values + -u, --username string username for registry operations (default to $NOTATION_USERNAME if not specified) + -m, --user-metadata strings user defined {key}={value} pairs that must be present in the signature for successful verification if provided + -v, --verbose verbose mode ``` ## Usage From 647fe0c71b3689d17765c7ab9dec1d240c27836e Mon Sep 17 00:00:00 2001 From: Patrick Zheng Date: Mon, 16 Jan 2023 17:30:45 +0800 Subject: [PATCH 08/28] update spec Signed-off-by: Patrick Zheng --- specs/commandline/sign.md | 22 +++++++++++----------- specs/commandline/verify.md | 2 +- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/specs/commandline/sign.md b/specs/commandline/sign.md index 50e8e3279..cbd775b78 100644 --- a/specs/commandline/sign.md +++ b/specs/commandline/sign.md @@ -29,17 +29,17 @@ Usage: notation sign [flags] Flags: - -d, --debug debug mode - -e, --expiry duration optional expiry that provides a "best by use" time for the artifact. The duration is specified in minutes(m) and/or hours(h). For example: 12h, 30m, 3h20m - -h, --help help for sign - --image-spec string manifest type for signatures. options: v1.1-artifact, v1.1-image (default "v1.1-artifact") - -k, --key string signing key name, for a key previously added to notation's key list. - -p, --password string password for registry operations (default to $NOTATION_PASSWORD if not specified) - --plain-http registry access via plain HTTP - --plugin-config strings {key}={value} pairs that are passed as it is to a plugin, refer plugin's documentation to set appropriate values - --signature-format string signature envelope format, options: 'jws', 'cose' (default "jws") - -u, --username string username for registry operations (default to $NOTATION_USERNAME if not specified) - -m, --user-metadata strings {key}={value} pairs that are added to the signature payload + -d, --debug debug mode + -e, --expiry duration optional expiry that provides a "best by use" time for the artifact. The duration is specified in minutes(m) and/or hours(h). For example: 12h, 30m, 3h20m + -h, --help help for sign + --image-spec string manifest type for signatures. options: v1.1-artifact, v1.1-image (default "v1.1-artifact") + -k, --key string signing key name, for a key previously added to notation's key list. + -p, --password string password for registry operations (default to $NOTATION_PASSWORD if not specified) + --plain-http registry access via plain HTTP + --plugin-config stringArray {key}={value} pairs that are passed as it is to a plugin, refer plugin's documentation to set appropriate values + --signature-format string signature envelope format, options: 'jws', 'cose' (default "jws") + -u, --username string username for registry operations (default to $NOTATION_USERNAME if not specified) + -m, --user-metadata stringArray {key}={value} pairs that are added to the signature payload -v, --verbose verbose mode ``` diff --git a/specs/commandline/verify.md b/specs/commandline/verify.md index 091ef3452..92e6445a7 100644 --- a/specs/commandline/verify.md +++ b/specs/commandline/verify.md @@ -42,7 +42,7 @@ Flags: --plain-http registry access via plain HTTP -c, --plugin-config stringArray {key}={value} pairs that are passed as it is to a plugin, if the verification is associated with a verification plugin, refer plugin documentation to set appropriate values -u, --username string username for registry operations (default to $NOTATION_USERNAME if not specified) - -m, --user-metadata strings user defined {key}={value} pairs that must be present in the signature for successful verification if provided + -m, --user-metadata stringArray user defined {key}={value} pairs that must be present in the signature for successful verification if provided -v, --verbose verbose mode ``` From 0db62d501c44d5b833ef28b445ec6ed86acef061 Mon Sep 17 00:00:00 2001 From: Patrick Zheng Date: Tue, 17 Jan 2023 11:45:49 +0800 Subject: [PATCH 09/28] updated per code review Signed-off-by: Patrick Zheng --- cmd/notation/registry.go | 5 +++++ cmd/notation/sign.go | 12 +++++------- go.mod | 2 +- go.sum | 4 ++-- 4 files changed, 13 insertions(+), 10 deletions(-) diff --git a/cmd/notation/registry.go b/cmd/notation/registry.go index 491b044f6..791fde8bd 100644 --- a/cmd/notation/registry.go +++ b/cmd/notation/registry.go @@ -18,6 +18,11 @@ import ( "oras.land/oras-go/v2/registry/remote/auth" ) +// getSignatureRepository returns a registry.Repository. +// ociImageManifest denotes the type of manifest used to store signatures during +// Sign process. +// Setting ociImageManifest to true means using OCI image manifest. +// Otherwise, notation will use OCI artifact manifest. func getSignatureRepository(ctx context.Context, opts *SecureFlagOpts, reference string, ociImageManifest bool) (notationregistry.Repository, error) { ref, err := registry.ParseReference(reference) if err != nil { diff --git a/cmd/notation/sign.go b/cmd/notation/sign.go index eb5d31eee..ba48a16b0 100644 --- a/cmd/notation/sign.go +++ b/cmd/notation/sign.go @@ -18,8 +18,7 @@ import ( const ( artifactManifest = "v1.1-artifact" - - imageManifest = "v1.1-image" + imageManifest = "v1.1-image" ) var supportedImageSpec = []string{artifactManifest, imageManifest} @@ -68,6 +67,10 @@ Example - Sign an OCI artifact stored in a registry and specify the signature ex return nil }, RunE: func(cmd *cobra.Command, args []string) error { + // sanity check + if !validateImageSpec(opts.imageSpec) { + return fmt.Errorf("image spec has to be in %v, got %s", supportedImageSpec, opts.imageSpec) + } return runSign(cmd, opts) }, } @@ -81,11 +84,6 @@ Example - Sign an OCI artifact stored in a registry and specify the signature ex } func runSign(command *cobra.Command, cmdOpts *signOpts) error { - // sanity check - if !validateImageSpec(cmdOpts.imageSpec) { - return fmt.Errorf("image spec has to be in %v, got %s", supportedImageSpec, cmdOpts.imageSpec) - } - // set log level ctx := cmdOpts.LoggingFlagOpts.SetLoggerLevel(command.Context()) diff --git a/go.mod b/go.mod index 118e2c6e1..1dbca0156 100644 --- a/go.mod +++ b/go.mod @@ -28,4 +28,4 @@ require ( golang.org/x/sys v0.2.0 // indirect ) -replace github.com/notaryproject/notation-go => github.com/patrickzheng200/notation-go v0.9.0-alpha.1.0.20230116020158-f3e0421fae95 +replace github.com/notaryproject/notation-go => github.com/patrickzheng200/notation-go v0.9.0-alpha.1.0.20230116090032-4e4d1f0aca9b diff --git a/go.sum b/go.sum index f5cd0db53..8ed1689a4 100644 --- a/go.sum +++ b/go.sum @@ -24,8 +24,8 @@ github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8 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= github.com/opencontainers/image-spec v1.1.0-rc2/go.mod h1:3OVijpioIKYWTqjiG0zfF6wvoJ4fAXGbjdZuI2NgsRQ= -github.com/patrickzheng200/notation-go v0.9.0-alpha.1.0.20230116020158-f3e0421fae95 h1:03nSJeCy4yfQj+GEOPv+IjYkgBYQGN0kFywLJmAdp40= -github.com/patrickzheng200/notation-go v0.9.0-alpha.1.0.20230116020158-f3e0421fae95/go.mod h1:zPpwoqwbgoR3tZH/oQT3vi22D3p2UdvM0DpVmBp7r+U= +github.com/patrickzheng200/notation-go v0.9.0-alpha.1.0.20230116090032-4e4d1f0aca9b h1:53MjRfuZKwIsbX7QLk3itCbeUGlmtpFvEHrD4l3Eph4= +github.com/patrickzheng200/notation-go v0.9.0-alpha.1.0.20230116090032-4e4d1f0aca9b/go.mod h1:zPpwoqwbgoR3tZH/oQT3vi22D3p2UdvM0DpVmBp7r+U= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= From 04723f21028b94ba62ae06f10ce59e9c83c3a2f3 Mon Sep 17 00:00:00 2001 From: Patrick Zheng Date: Thu, 19 Jan 2023 16:18:13 +0800 Subject: [PATCH 10/28] update Signed-off-by: Patrick Zheng --- cmd/notation/list.go | 2 +- cmd/notation/registry.go | 47 +++++++++++++++++++++++++++++++++------- cmd/notation/sign.go | 4 ++-- cmd/notation/verify.go | 2 +- go.mod | 4 ++-- go.sum | 4 ++-- 6 files changed, 47 insertions(+), 16 deletions(-) diff --git a/cmd/notation/list.go b/cmd/notation/list.go index 4fc5a98f4..92c5c8880 100644 --- a/cmd/notation/list.go +++ b/cmd/notation/list.go @@ -50,7 +50,7 @@ func runList(ctx context.Context, opts *listOpts) error { // initialize reference := opts.reference - sigRepo, err := getSignatureRepository(ctx, &opts.SecureFlagOpts, reference, false) + sigRepo, err := getSignatureRepository(ctx, &opts.SecureFlagOpts, reference) if err != nil { return err } diff --git a/cmd/notation/registry.go b/cmd/notation/registry.go index 791fde8bd..8baf9af30 100644 --- a/cmd/notation/registry.go +++ b/cmd/notation/registry.go @@ -18,19 +18,14 @@ import ( "oras.land/oras-go/v2/registry/remote/auth" ) -// getSignatureRepository returns a registry.Repository. -// ociImageManifest denotes the type of manifest used to store signatures during -// Sign process. -// Setting ociImageManifest to true means using OCI image manifest. -// Otherwise, notation will use OCI artifact manifest. -func getSignatureRepository(ctx context.Context, opts *SecureFlagOpts, reference string, ociImageManifest bool) (notationregistry.Repository, error) { +func getSignatureRepository(ctx context.Context, opts *SecureFlagOpts, reference string) (notationregistry.Repository, error) { ref, err := registry.ParseReference(reference) if err != nil { return nil, err } // generate notation repository - return getRepositoryClient(ctx, opts, ref, ociImageManifest) + return getRepositoryClient(ctx, opts, ref) } func getRegistryClient(ctx context.Context, opts *SecureFlagOpts, serverAddress string) (*remote.Registry, error) { @@ -46,7 +41,36 @@ func getRegistryClient(ctx context.Context, opts *SecureFlagOpts, serverAddress return reg, nil } -func getRepositoryClient(ctx context.Context, opts *SecureFlagOpts, ref registry.Reference, ociImageManifest bool) (notationregistry.Repository, error) { +func getRepositoryClient(ctx context.Context, opts *SecureFlagOpts, ref registry.Reference) (notationregistry.Repository, error) { + authClient, plainHTTP, err := getAuthClient(ctx, opts, ref) + if err != nil { + return nil, err + } + + remoteRepo := &remote.Repository{ + Client: authClient, + Reference: ref, + PlainHTTP: plainHTTP, + } + return notationregistry.NewRepository(remoteRepo), nil +} + +// getSignatureRepositoryForSign returns a registry.Repository for Sign. +// ociImageManifest denotes the type of manifest used to store signatures during +// Sign process. +// Setting ociImageManifest to true means using OCI image manifest and Index. +// Otherwise, notation will use OCI artifact manifest and Referrers API. +func getSignatureRepositoryForSign(ctx context.Context, opts *SecureFlagOpts, reference string, ociImageManifest bool) (notationregistry.Repository, error) { + ref, err := registry.ParseReference(reference) + if err != nil { + return nil, err + } + + // generate notation repository + return getRepositoryClientForSign(ctx, opts, ref, ociImageManifest) +} + +func getRepositoryClientForSign(ctx context.Context, opts *SecureFlagOpts, ref registry.Reference, ociImageManifest bool) (notationregistry.Repository, error) { authClient, plainHTTP, err := getAuthClient(ctx, opts, ref) if err != nil { return nil, err @@ -60,6 +84,13 @@ func getRepositoryClient(ctx context.Context, opts *SecureFlagOpts, ref registry repositoryOpts := notationregistry.RepositoryOptions{ OCIImageManifest: ociImageManifest, } + // using OCI artifact manifest to store signatures. This requires the + // existence of Referrers API for Sign process. + if !ociImageManifest { + if err := remoteRepo.SetReferrersCapability(true); err != nil { + return nil, err + } + } return notationregistry.NewRepositoryWithOptions(remoteRepo, repositoryOpts), nil } diff --git a/cmd/notation/sign.go b/cmd/notation/sign.go index ba48a16b0..3b1f51336 100644 --- a/cmd/notation/sign.go +++ b/cmd/notation/sign.go @@ -79,7 +79,7 @@ Example - Sign an OCI artifact stored in a registry and specify the signature ex opts.SecureFlagOpts.ApplyFlags(command.Flags()) cmd.SetPflagExpiry(command.Flags(), &opts.expiry) cmd.SetPflagPluginConfig(command.Flags(), &opts.pluginConfig) - command.Flags().StringVar(&opts.imageSpec, "image-spec", artifactManifest, "manifest type for signatures. options: v1.1-artifact, v1.1-image") + command.Flags().StringVar(&opts.imageSpec, "artifact-type", artifactManifest, "manifest type for signatures. options: v1.1-artifact, v1.1-image") return command } @@ -96,7 +96,7 @@ func runSign(command *cobra.Command, cmdOpts *signOpts) error { if cmdOpts.imageSpec == imageManifest { ociImageManifest = true } - sigRepo, err := getSignatureRepository(ctx, &cmdOpts.SecureFlagOpts, cmdOpts.reference, ociImageManifest) + sigRepo, err := getSignatureRepositoryForSign(ctx, &cmdOpts.SecureFlagOpts, cmdOpts.reference, ociImageManifest) if err != nil { return err } diff --git a/cmd/notation/verify.go b/cmd/notation/verify.go index 5e2a81fe9..387e045dc 100644 --- a/cmd/notation/verify.go +++ b/cmd/notation/verify.go @@ -65,7 +65,7 @@ func runVerify(command *cobra.Command, opts *verifyOpts) error { // initialize reference := opts.reference - sigRepo, err := getSignatureRepository(ctx, &opts.SecureFlagOpts, reference, false) + sigRepo, err := getSignatureRepository(ctx, &opts.SecureFlagOpts, reference) if err != nil { return err } diff --git a/go.mod b/go.mod index 1dbca0156..ddf821b2e 100644 --- a/go.mod +++ b/go.mod @@ -5,7 +5,7 @@ go 1.19 require ( github.com/docker/docker-credential-helpers v0.7.0 github.com/notaryproject/notation-core-go v1.0.0-rc.1 - github.com/notaryproject/notation-go v1.0.0-rc.1 + github.com/notaryproject/notation-go v1.0.0-rc.1.0.20230119020804-36d97f79abc8 github.com/opencontainers/go-digest v1.0.0 github.com/opencontainers/image-spec v1.1.0-rc2 github.com/sirupsen/logrus v1.9.0 @@ -28,4 +28,4 @@ require ( golang.org/x/sys v0.2.0 // indirect ) -replace github.com/notaryproject/notation-go => github.com/patrickzheng200/notation-go v0.9.0-alpha.1.0.20230116090032-4e4d1f0aca9b +replace github.com/notaryproject/notation-go => github.com/patrickzheng200/notation-go v0.9.0-alpha.1.0.20230119071817-79b30887f89a diff --git a/go.sum b/go.sum index 8ed1689a4..584d09dd1 100644 --- a/go.sum +++ b/go.sum @@ -24,8 +24,8 @@ github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8 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= github.com/opencontainers/image-spec v1.1.0-rc2/go.mod h1:3OVijpioIKYWTqjiG0zfF6wvoJ4fAXGbjdZuI2NgsRQ= -github.com/patrickzheng200/notation-go v0.9.0-alpha.1.0.20230116090032-4e4d1f0aca9b h1:53MjRfuZKwIsbX7QLk3itCbeUGlmtpFvEHrD4l3Eph4= -github.com/patrickzheng200/notation-go v0.9.0-alpha.1.0.20230116090032-4e4d1f0aca9b/go.mod h1:zPpwoqwbgoR3tZH/oQT3vi22D3p2UdvM0DpVmBp7r+U= +github.com/patrickzheng200/notation-go v0.9.0-alpha.1.0.20230119071817-79b30887f89a h1:MA1EZQj9uXBmaXY7LTI9JnfG9WQDDbfWdbXzEn+wVx0= +github.com/patrickzheng200/notation-go v0.9.0-alpha.1.0.20230119071817-79b30887f89a/go.mod h1:zPpwoqwbgoR3tZH/oQT3vi22D3p2UdvM0DpVmBp7r+U= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= From c249ef7b0fb3e3a4c0e413418bbdf459f11ce2a7 Mon Sep 17 00:00:00 2001 From: Patrick Zheng Date: Thu, 19 Jan 2023 16:21:41 +0800 Subject: [PATCH 11/28] unit test Signed-off-by: Patrick Zheng --- cmd/notation/sign_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmd/notation/sign_test.go b/cmd/notation/sign_test.go index 2debdb436..6ffa76e7a 100644 --- a/cmd/notation/sign_test.go +++ b/cmd/notation/sign_test.go @@ -65,7 +65,7 @@ func TestSignCommand_MoreArgs(t *testing.T) { "--plain-http", "--signature-format", expected.SignerFlagOpts.SignatureFormat, "--expiry", expected.expiry.String(), - "--image-spec", imageManifest}); err != nil { + "--artifact-type", imageManifest}); err != nil { t.Fatalf("Parse Flag failed: %v", err) } if err := command.Args(command, command.Flags().Args()); err != nil { From 812403e58983c7763a4037843cecde81cd24ad89 Mon Sep 17 00:00:00 2001 From: Patrick Zheng Date: Thu, 19 Jan 2023 19:07:02 +0800 Subject: [PATCH 12/28] update Signed-off-by: Patrick Zheng --- cmd/notation/registry.go | 15 +++++++++++++++ go.mod | 2 -- go.sum | 4 ++-- 3 files changed, 17 insertions(+), 4 deletions(-) diff --git a/cmd/notation/registry.go b/cmd/notation/registry.go index 8baf9af30..eade79fa5 100644 --- a/cmd/notation/registry.go +++ b/cmd/notation/registry.go @@ -3,6 +3,7 @@ package main import ( "context" "errors" + "fmt" "net" "net/http" @@ -12,12 +13,15 @@ import ( "github.com/notaryproject/notation/internal/version" loginauth "github.com/notaryproject/notation/pkg/auth" "github.com/notaryproject/notation/pkg/configutil" + ocispec "github.com/opencontainers/image-spec/specs-go/v1" "github.com/sirupsen/logrus" "oras.land/oras-go/v2/registry" "oras.land/oras-go/v2/registry/remote" "oras.land/oras-go/v2/registry/remote/auth" ) +const zeroDigest = "sha256:0000000000000000000000000000000000000000000000000000000000000000" + func getSignatureRepository(ctx context.Context, opts *SecureFlagOpts, reference string) (notationregistry.Repository, error) { ref, err := registry.ParseReference(reference) if err != nil { @@ -90,6 +94,17 @@ func getRepositoryClientForSign(ctx context.Context, opts *SecureFlagOpts, ref r if err := remoteRepo.SetReferrersCapability(true); err != nil { return nil, err } + // when uploading OCI artifact manifest, Notation requires the registry + // to support the Referrers API as well. + // Reference: https://github.com/opencontainers/distribution-spec/blob/v1.1.0-rc1/spec.md#listing-referrers + var checkReferrerDesc ocispec.Descriptor + checkReferrerDesc.Digest = zeroDigest + err := remoteRepo.Referrers(ctx, checkReferrerDesc, "", func(referrers []ocispec.Descriptor) error { + return nil + }) + if err != nil { + return nil, fmt.Errorf("failed to ping Referrers API on uploading OCI artifact manifest with error: %v. Try OCI image manifest instead", err) + } } return notationregistry.NewRepositoryWithOptions(remoteRepo, repositoryOpts), nil } diff --git a/go.mod b/go.mod index ddf821b2e..b445c2139 100644 --- a/go.mod +++ b/go.mod @@ -27,5 +27,3 @@ require ( golang.org/x/sync v0.1.0 // indirect golang.org/x/sys v0.2.0 // indirect ) - -replace github.com/notaryproject/notation-go => github.com/patrickzheng200/notation-go v0.9.0-alpha.1.0.20230119071817-79b30887f89a diff --git a/go.sum b/go.sum index 584d09dd1..e4d6a52ac 100644 --- a/go.sum +++ b/go.sum @@ -20,12 +20,12 @@ 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 v1.0.0-rc.1 h1:ACi0gr6mD1bzp9+gu3P0meJ/N6iWHlyM9zgtdnooNAA= github.com/notaryproject/notation-core-go v1.0.0-rc.1/go.mod h1:n8Gbvl9sKa00KptkKEL5XKUyMTIALe74QipKauE2rj4= +github.com/notaryproject/notation-go v1.0.0-rc.1.0.20230119020804-36d97f79abc8 h1:yMECf0ucr7cyWD5ArDSQUPjUEQ9U8DEASqBjHUNhL7s= +github.com/notaryproject/notation-go v1.0.0-rc.1.0.20230119020804-36d97f79abc8/go.mod h1:zPpwoqwbgoR3tZH/oQT3vi22D3p2UdvM0DpVmBp7r+U= 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= github.com/opencontainers/image-spec v1.1.0-rc2/go.mod h1:3OVijpioIKYWTqjiG0zfF6wvoJ4fAXGbjdZuI2NgsRQ= -github.com/patrickzheng200/notation-go v0.9.0-alpha.1.0.20230119071817-79b30887f89a h1:MA1EZQj9uXBmaXY7LTI9JnfG9WQDDbfWdbXzEn+wVx0= -github.com/patrickzheng200/notation-go v0.9.0-alpha.1.0.20230119071817-79b30887f89a/go.mod h1:zPpwoqwbgoR3tZH/oQT3vi22D3p2UdvM0DpVmBp7r+U= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= From 5e5b520876f6c505656fb1f899d3584b74fd94ed Mon Sep 17 00:00:00 2001 From: Patrick Zheng Date: Thu, 19 Jan 2023 19:16:14 +0800 Subject: [PATCH 13/28] update spec Signed-off-by: Patrick Zheng --- specs/commandline/sign.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/specs/commandline/sign.md b/specs/commandline/sign.md index cbd775b78..81828ccaf 100644 --- a/specs/commandline/sign.md +++ b/specs/commandline/sign.md @@ -32,7 +32,7 @@ Flags: -d, --debug debug mode -e, --expiry duration optional expiry that provides a "best by use" time for the artifact. The duration is specified in minutes(m) and/or hours(h). For example: 12h, 30m, 3h20m -h, --help help for sign - --image-spec string manifest type for signatures. options: v1.1-artifact, v1.1-image (default "v1.1-artifact") + --artifact-type string manifest type for signatures. options: v1.1-artifact, v1.1-image (default "v1.1-artifact") -k, --key string signing key name, for a key previously added to notation's key list. -p, --password string password for registry operations (default to $NOTATION_PASSWORD if not specified) --plain-http registry access via plain HTTP From 314a7f48b9a007987db3d4a1fb3becd24ca8fc1b Mon Sep 17 00:00:00 2001 From: Patrick Zheng Date: Mon, 30 Jan 2023 13:38:04 +0800 Subject: [PATCH 14/28] update Signed-off-by: Patrick Zheng --- cmd/notation/registry.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cmd/notation/registry.go b/cmd/notation/registry.go index eade79fa5..3f1b7f7a8 100644 --- a/cmd/notation/registry.go +++ b/cmd/notation/registry.go @@ -62,8 +62,8 @@ func getRepositoryClient(ctx context.Context, opts *SecureFlagOpts, ref registry // getSignatureRepositoryForSign returns a registry.Repository for Sign. // ociImageManifest denotes the type of manifest used to store signatures during // Sign process. -// Setting ociImageManifest to true means using OCI image manifest and Index. -// Otherwise, notation will use OCI artifact manifest and Referrers API. +// Setting ociImageManifest to true means using OCI image manifest. +// Otherwise, use OCI artifact manifest and requires Referrers API. func getSignatureRepositoryForSign(ctx context.Context, opts *SecureFlagOpts, reference string, ociImageManifest bool) (notationregistry.Repository, error) { ref, err := registry.ParseReference(reference) if err != nil { From fe171e176fd9f21fd40e3876fae6a25c0c6f41d0 Mon Sep 17 00:00:00 2001 From: Patrick Zheng Date: Thu, 2 Feb 2023 10:52:55 +0800 Subject: [PATCH 15/28] updated per code review Signed-off-by: Patrick Zheng --- cmd/notation/registry.go | 88 ++++++++++++++++------------------ cmd/notation/sign.go | 17 +++---- cmd/notation/sign_test.go | 8 ++-- cmd/notation/verify.go | 2 +- internal/slices/slices.go | 4 +- internal/slices/slices_test.go | 22 ++++++++- pkg/configutil/util.go | 2 +- specs/commandline/sign.md | 2 +- specs/commandline/verify.md | 2 +- 9 files changed, 80 insertions(+), 67 deletions(-) diff --git a/cmd/notation/registry.go b/cmd/notation/registry.go index 3f1b7f7a8..76af6bcc7 100644 --- a/cmd/notation/registry.go +++ b/cmd/notation/registry.go @@ -29,40 +29,18 @@ func getSignatureRepository(ctx context.Context, opts *SecureFlagOpts, reference } // generate notation repository - return getRepositoryClient(ctx, opts, ref) -} - -func getRegistryClient(ctx context.Context, opts *SecureFlagOpts, serverAddress string) (*remote.Registry, error) { - reg, err := remote.NewRegistry(serverAddress) - if err != nil { - return nil, err - } - - reg.Client, reg.PlainHTTP, err = getAuthClient(ctx, opts, reg.Reference) - if err != nil { - return nil, err - } - return reg, nil -} - -func getRepositoryClient(ctx context.Context, opts *SecureFlagOpts, ref registry.Reference) (notationregistry.Repository, error) { - authClient, plainHTTP, err := getAuthClient(ctx, opts, ref) + remoteRepo, err := getRepositoryClient(ctx, opts, ref) if err != nil { return nil, err } - - remoteRepo := &remote.Repository{ - Client: authClient, - Reference: ref, - PlainHTTP: plainHTTP, - } return notationregistry.NewRepository(remoteRepo), nil } // getSignatureRepositoryForSign returns a registry.Repository for Sign. // ociImageManifest denotes the type of manifest used to store signatures during // Sign process. -// Setting ociImageManifest to true means using OCI image manifest. +// Setting ociImageManifest to true means using OCI image manifest and tag +// schema. // Otherwise, use OCI artifact manifest and requires Referrers API. func getSignatureRepositoryForSign(ctx context.Context, opts *SecureFlagOpts, reference string, ociImageManifest bool) (notationregistry.Repository, error) { ref, err := registry.ParseReference(reference) @@ -71,44 +49,62 @@ func getSignatureRepositoryForSign(ctx context.Context, opts *SecureFlagOpts, re } // generate notation repository - return getRepositoryClientForSign(ctx, opts, ref, ociImageManifest) -} - -func getRepositoryClientForSign(ctx context.Context, opts *SecureFlagOpts, ref registry.Reference, ociImageManifest bool) (notationregistry.Repository, error) { - authClient, plainHTTP, err := getAuthClient(ctx, opts, ref) + remoteRepo, err := getRepositoryClient(ctx, opts, ref) if err != nil { return nil, err } - - remoteRepo := &remote.Repository{ - Client: authClient, - Reference: ref, - PlainHTTP: plainHTTP, - } - repositoryOpts := notationregistry.RepositoryOptions{ - OCIImageManifest: ociImageManifest, + // 1. OCI artifact manifest requires Referrers API. + // Reference: https://github.com/opencontainers/distribution-spec/blob/v1.1.0-rc1/spec.md#listing-referrers + // 2. OCI image manifest requires Referrers Tag Schema. + // Reference: https://github.com/opencontainers/distribution-spec/blob/v1.1.0-rc1/spec.md#referrers-tag-schema + if err := remoteRepo.SetReferrersCapability(!ociImageManifest); err != nil { + return nil, err } - // using OCI artifact manifest to store signatures. This requires the + // using OCI artifact manifest to store signatures. Notation requires the // existence of Referrers API for Sign process. if !ociImageManifest { - if err := remoteRepo.SetReferrersCapability(true); err != nil { - return nil, err - } - // when uploading OCI artifact manifest, Notation requires the registry - // to support the Referrers API as well. - // Reference: https://github.com/opencontainers/distribution-spec/blob/v1.1.0-rc1/spec.md#listing-referrers var checkReferrerDesc ocispec.Descriptor checkReferrerDesc.Digest = zeroDigest err := remoteRepo.Referrers(ctx, checkReferrerDesc, "", func(referrers []ocispec.Descriptor) error { return nil }) if err != nil { - return nil, fmt.Errorf("failed to ping Referrers API on uploading OCI artifact manifest with error: %v. Try OCI image manifest instead", err) + return nil, fmt.Errorf("failed to ping Referrers API with error: %v. Try OCI image manifest using `--image-spec`", err) } } + repositoryOpts := notationregistry.RepositoryOptions{ + OCIImageManifest: ociImageManifest, + } return notationregistry.NewRepositoryWithOptions(remoteRepo, repositoryOpts), nil } +func getRepositoryClient(ctx context.Context, opts *SecureFlagOpts, ref registry.Reference) (*remote.Repository, error) { + authClient, plainHTTP, err := getAuthClient(ctx, opts, ref) + if err != nil { + return nil, err + } + + remoteRepo := &remote.Repository{ + Client: authClient, + Reference: ref, + PlainHTTP: plainHTTP, + } + return remoteRepo, nil +} + +func getRegistryClient(ctx context.Context, opts *SecureFlagOpts, serverAddress string) (*remote.Registry, error) { + reg, err := remote.NewRegistry(serverAddress) + if err != nil { + return nil, err + } + + reg.Client, reg.PlainHTTP, err = getAuthClient(ctx, opts, reg.Reference) + if err != nil { + return nil, err + } + return reg, nil +} + func setHttpDebugLog(ctx context.Context, authClient *auth.Client) { if logrusLog, ok := log.GetLogger(ctx).(*logrus.Logger); ok && logrusLog.Level != logrus.DebugLevel { return diff --git a/cmd/notation/sign.go b/cmd/notation/sign.go index 3b1f51336..132fc0f67 100644 --- a/cmd/notation/sign.go +++ b/cmd/notation/sign.go @@ -17,11 +17,11 @@ import ( ) const ( - artifactManifest = "v1.1-artifact" - imageManifest = "v1.1-image" + imageSpecArtifact = "v1.1-artifact" + imageSpecImage = "v1.1-image" ) -var supportedImageSpec = []string{artifactManifest, imageManifest} +var supportedImageSpec = []string{imageSpecArtifact, imageSpecImage} type signOpts struct { cmd.LoggingFlagOpts @@ -69,7 +69,7 @@ Example - Sign an OCI artifact stored in a registry and specify the signature ex RunE: func(cmd *cobra.Command, args []string) error { // sanity check if !validateImageSpec(opts.imageSpec) { - return fmt.Errorf("image spec has to be in %v, got %s", supportedImageSpec, opts.imageSpec) + return fmt.Errorf("image spec must be one of the following %v but got %s", supportedImageSpec, opts.imageSpec) } return runSign(cmd, opts) }, @@ -79,7 +79,7 @@ Example - Sign an OCI artifact stored in a registry and specify the signature ex opts.SecureFlagOpts.ApplyFlags(command.Flags()) cmd.SetPflagExpiry(command.Flags(), &opts.expiry) cmd.SetPflagPluginConfig(command.Flags(), &opts.pluginConfig) - command.Flags().StringVar(&opts.imageSpec, "artifact-type", artifactManifest, "manifest type for signatures. options: v1.1-artifact, v1.1-image") + command.Flags().StringVar(&opts.imageSpec, "image-spec", imageSpecArtifact, "manifest type for signatures. options: v1.1-artifact, v1.1-image") return command } @@ -92,10 +92,7 @@ func runSign(command *cobra.Command, cmdOpts *signOpts) error { if err != nil { return err } - var ociImageManifest bool - if cmdOpts.imageSpec == imageManifest { - ociImageManifest = true - } + ociImageManifest := cmdOpts.imageSpec == imageSpecImage sigRepo, err := getSignatureRepositoryForSign(ctx, &cmdOpts.SecureFlagOpts, cmdOpts.reference, ociImageManifest) if err != nil { return err @@ -145,5 +142,5 @@ func prepareSigningContent(ctx context.Context, opts *signOpts, sigRepo notation } func validateImageSpec(imageSpec string) bool { - return slices.ContainsGenerics(supportedImageSpec, imageSpec) + return slices.ContainerElement(supportedImageSpec, imageSpec) } diff --git a/cmd/notation/sign_test.go b/cmd/notation/sign_test.go index 6ffa76e7a..ce4b609ee 100644 --- a/cmd/notation/sign_test.go +++ b/cmd/notation/sign_test.go @@ -23,7 +23,7 @@ func TestSignCommand_BasicArgs(t *testing.T) { Key: "key", SignatureFormat: envelope.JWS, }, - imageSpec: artifactManifest, + imageSpec: imageSpecArtifact, } if err := command.ParseFlags([]string{ expected.reference, @@ -55,7 +55,7 @@ func TestSignCommand_MoreArgs(t *testing.T) { SignatureFormat: envelope.COSE, }, expiry: 24 * time.Hour, - imageSpec: imageManifest, + imageSpec: imageSpecImage, } if err := command.ParseFlags([]string{ expected.reference, @@ -65,7 +65,7 @@ func TestSignCommand_MoreArgs(t *testing.T) { "--plain-http", "--signature-format", expected.SignerFlagOpts.SignatureFormat, "--expiry", expected.expiry.String(), - "--artifact-type", imageManifest}); err != nil { + "--image-spec", imageSpecImage}); err != nil { t.Fatalf("Parse Flag failed: %v", err) } if err := command.Args(command, command.Flags().Args()); err != nil { @@ -87,7 +87,7 @@ func TestSignCommand_CorrectConfig(t *testing.T) { }, expiry: 365 * 24 * time.Hour, pluginConfig: []string{"key0=val0", "key1=val1"}, - imageSpec: artifactManifest, + imageSpec: imageSpecArtifact, } if err := command.ParseFlags([]string{ expected.reference, diff --git a/cmd/notation/verify.go b/cmd/notation/verify.go index 387e045dc..7e3a9e42b 100644 --- a/cmd/notation/verify.go +++ b/cmd/notation/verify.go @@ -55,7 +55,7 @@ Example - Verify a signature on an OCI artifact identified by a tag (Notation w } opts.LoggingFlagOpts.ApplyFlags(command.Flags()) opts.SecureFlagOpts.ApplyFlags(command.Flags()) - command.Flags().StringArrayVarP(&opts.pluginConfig, "plugin-config", "c", nil, "{key}={value} pairs that are passed as it is to a plugin, if the verification is associated with a verification plugin, refer plugin documentation to set appropriate values") + command.Flags().StringArrayVar(&opts.pluginConfig, "plugin-config", nil, "{key}={value} pairs that are passed as it is to a plugin, if the verification is associated with a verification plugin, refer plugin documentation to set appropriate values") return command } diff --git a/internal/slices/slices.go b/internal/slices/slices.go index 76b23d0da..52e9915d2 100644 --- a/internal/slices/slices.go +++ b/internal/slices/slices.go @@ -26,8 +26,8 @@ func Delete[S ~[]E, E isser](s S, i int) S { return append(s[:i], s[i+1:]...) } -// Contains reports whether v is present in s. -func ContainsGenerics[E comparable](s []E, v E) bool { +// ContainerElement reports whether v is present in s. +func ContainerElement[E comparable](s []E, v E) bool { for _, vs := range s { if v == vs { return true diff --git a/internal/slices/slices_test.go b/internal/slices/slices_test.go index ae24e6313..d78ec1f2b 100644 --- a/internal/slices/slices_test.go +++ b/internal/slices/slices_test.go @@ -42,7 +42,7 @@ func TestContains(t *testing.T) { } for _, tt := range tests { if got := Contains(tt.s, tt.v); got != tt.want { - t.Errorf("Index() = %v, want %v", got, tt.want) + t.Errorf("Contains() = %v, want %v", got, tt.want) } } } @@ -63,3 +63,23 @@ func TestDelete(t *testing.T) { } } } + +func TestContainerElement(t *testing.T) { + tests := []struct { + c []string + v string + want bool + }{ + {nil, "", false}, + {[]string{}, "", false}, + {[]string{"1", "2", "3"}, "4", false}, + {[]string{"1", "2", "3"}, "2", true}, + {[]string{"1", "2", "2", "3"}, "2", true}, + {[]string{"1", "2", "3", "2"}, "2", true}, + } + for _, tt := range tests { + if got := ContainerElement(tt.c, tt.v); got != tt.want { + t.Errorf("ContainerElement() = %v, want %v", got, tt.want) + } + } +} diff --git a/pkg/configutil/util.go b/pkg/configutil/util.go index 145f1d781..1b6399471 100644 --- a/pkg/configutil/util.go +++ b/pkg/configutil/util.go @@ -45,7 +45,7 @@ func ResolveKey(name string) (config.KeySuite, error) { " Please set default singing key or specify a key name") } } - + idx := slices.Index(signingKeys.Keys, name) if idx < 0 { return config.KeySuite{}, ErrKeyNotFound diff --git a/specs/commandline/sign.md b/specs/commandline/sign.md index 81828ccaf..cbd775b78 100644 --- a/specs/commandline/sign.md +++ b/specs/commandline/sign.md @@ -32,7 +32,7 @@ Flags: -d, --debug debug mode -e, --expiry duration optional expiry that provides a "best by use" time for the artifact. The duration is specified in minutes(m) and/or hours(h). For example: 12h, 30m, 3h20m -h, --help help for sign - --artifact-type string manifest type for signatures. options: v1.1-artifact, v1.1-image (default "v1.1-artifact") + --image-spec string manifest type for signatures. options: v1.1-artifact, v1.1-image (default "v1.1-artifact") -k, --key string signing key name, for a key previously added to notation's key list. -p, --password string password for registry operations (default to $NOTATION_PASSWORD if not specified) --plain-http registry access via plain HTTP diff --git a/specs/commandline/verify.md b/specs/commandline/verify.md index 92e6445a7..f0c2b07cc 100644 --- a/specs/commandline/verify.md +++ b/specs/commandline/verify.md @@ -40,7 +40,7 @@ Flags: -h, --help help for verify -p, --password string password for registry operations (default to $NOTATION_PASSWORD if not specified) --plain-http registry access via plain HTTP - -c, --plugin-config stringArray {key}={value} pairs that are passed as it is to a plugin, if the verification is associated with a verification plugin, refer plugin documentation to set appropriate values + --plugin-config stringArray {key}={value} pairs that are passed as it is to a plugin, if the verification is associated with a verification plugin, refer plugin documentation to set appropriate values -u, --username string username for registry operations (default to $NOTATION_USERNAME if not specified) -m, --user-metadata stringArray user defined {key}={value} pairs that must be present in the signature for successful verification if provided -v, --verbose verbose mode From 48379ac56c66d8e2c094f4d99de5374a6c201e22 Mon Sep 17 00:00:00 2001 From: Patrick Zheng Date: Thu, 2 Feb 2023 11:22:44 +0800 Subject: [PATCH 16/28] updated per code review Signed-off-by: Patrick Zheng --- cmd/notation/registry.go | 12 +++++------- cmd/notation/sign.go | 6 ++++++ go.mod | 4 ++-- go.sum | 8 ++++---- 4 files changed, 17 insertions(+), 13 deletions(-) diff --git a/cmd/notation/registry.go b/cmd/notation/registry.go index 76af6bcc7..bc00faa61 100644 --- a/cmd/notation/registry.go +++ b/cmd/notation/registry.go @@ -53,16 +53,14 @@ func getSignatureRepositoryForSign(ctx context.Context, opts *SecureFlagOpts, re if err != nil { return nil, err } - // 1. OCI artifact manifest requires Referrers API. - // Reference: https://github.com/opencontainers/distribution-spec/blob/v1.1.0-rc1/spec.md#listing-referrers - // 2. OCI image manifest requires Referrers Tag Schema. - // Reference: https://github.com/opencontainers/distribution-spec/blob/v1.1.0-rc1/spec.md#referrers-tag-schema - if err := remoteRepo.SetReferrersCapability(!ociImageManifest); err != nil { - return nil, err - } // using OCI artifact manifest to store signatures. Notation requires the // existence of Referrers API for Sign process. + // Reference: https://github.com/opencontainers/distribution-spec/blob/v1.1.0-rc1/spec.md#listing-referrers if !ociImageManifest { + if err := remoteRepo.SetReferrersCapability(true); err != nil { + return nil, err + } + // ping Referrers API var checkReferrerDesc ocispec.Descriptor checkReferrerDesc.Digest = zeroDigest err := remoteRepo.Referrers(ctx, checkReferrerDesc, "", func(referrers []ocispec.Descriptor) error { diff --git a/cmd/notation/sign.go b/cmd/notation/sign.go index ae1ade404..e1ef9a6a2 100644 --- a/cmd/notation/sign.go +++ b/cmd/notation/sign.go @@ -59,6 +59,12 @@ Example - Sign an OCI artifact identified by a tag (Notation will resolve tag to Example - Sign an OCI artifact stored in a registry and specify the signature expiry duration, for example 24 hours notation sign --expiry 24h /@ + +Example - Sign an OCI image using the default signing key, with the default JWS envelope: + notation sign --image-spec v1.1-image /@ + +Example - Sign an OCI image using the default signing key, with the COSE envelope: + notation sign --image-spec v1.1-image --signature-format cose /@ `, Args: func(cmd *cobra.Command, args []string) error { if len(args) == 0 { diff --git a/go.mod b/go.mod index 479b5a3e0..8a73d0b00 100644 --- a/go.mod +++ b/go.mod @@ -5,7 +5,7 @@ go 1.19 require ( github.com/docker/docker-credential-helpers v0.7.0 github.com/notaryproject/notation-core-go v1.0.0-rc.1 - github.com/notaryproject/notation-go v1.0.0-rc.1.0.20230119020804-36d97f79abc8 + github.com/notaryproject/notation-go v1.0.0-rc.1.0.20230202012840-ed6ff0c14241 github.com/opencontainers/go-digest v1.0.0 github.com/opencontainers/image-spec v1.1.0-rc2 github.com/sirupsen/logrus v1.9.0 @@ -21,7 +21,7 @@ require ( github.com/go-ldap/ldap/v3 v3.4.4 // indirect github.com/golang-jwt/jwt/v4 v4.4.3 // indirect github.com/inconshreveable/mousetrap v1.1.0 // indirect - github.com/veraison/go-cose v1.0.0-rc.2 // indirect + github.com/veraison/go-cose v1.0.0 // indirect github.com/x448/float16 v0.8.4 // indirect golang.org/x/crypto v0.3.0 // indirect golang.org/x/sync v0.1.0 // indirect diff --git a/go.sum b/go.sum index 5b3c29d51..6f7c66cf6 100644 --- a/go.sum +++ b/go.sum @@ -20,8 +20,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 v1.0.0-rc.1 h1:ACi0gr6mD1bzp9+gu3P0meJ/N6iWHlyM9zgtdnooNAA= github.com/notaryproject/notation-core-go v1.0.0-rc.1/go.mod h1:n8Gbvl9sKa00KptkKEL5XKUyMTIALe74QipKauE2rj4= -github.com/notaryproject/notation-go v1.0.0-rc.1.0.20230119020804-36d97f79abc8 h1:yMECf0ucr7cyWD5ArDSQUPjUEQ9U8DEASqBjHUNhL7s= -github.com/notaryproject/notation-go v1.0.0-rc.1.0.20230119020804-36d97f79abc8/go.mod h1:zPpwoqwbgoR3tZH/oQT3vi22D3p2UdvM0DpVmBp7r+U= +github.com/notaryproject/notation-go v1.0.0-rc.1.0.20230202012840-ed6ff0c14241 h1:hY4PuFdZlzmz5TbyKEzotDPClvWgShZYsECLffbNkN8= +github.com/notaryproject/notation-go v1.0.0-rc.1.0.20230202012840-ed6ff0c14241/go.mod h1:0wGeTOs3nBr9zILyGpt90JR4Z48RGsfDjKXwqPLHwpA= 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= @@ -39,8 +39,8 @@ github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+ github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.2 h1:4jaiDzPyXQvSd7D0EjG45355tLlV3VOECpq10pLC+8s= github.com/stretchr/testify v1.7.2/go.mod h1:R6va5+xMeoiuVRoj+gSkQ7d3FALtqAAGI1FQKckRals= -github.com/veraison/go-cose v1.0.0-rc.2 h1:zH3QmP4N5kwpdGauceIT3aJm8iUyV9OqpUOb+7CF7rQ= -github.com/veraison/go-cose v1.0.0-rc.2/go.mod h1:7ziE85vSq4ScFTg6wyoMXjucIGOf4JkFEZi/an96Ct4= +github.com/veraison/go-cose v1.0.0 h1:Jxirc0rl3gG7wUFgW+82tBQNeK8T8e2Bk1Vd298ob4A= +github.com/veraison/go-cose v1.0.0/go.mod h1:7ziE85vSq4ScFTg6wyoMXjucIGOf4JkFEZi/an96Ct4= github.com/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM= github.com/x448/float16 v0.8.4/go.mod h1:14CWIYCyZA/cWjXOioeEpHeN/83MdbZDRQHoFcYsOfg= golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= From 455f1c4ab8369dfd06d40c2dde67175d43483c16 Mon Sep 17 00:00:00 2001 From: Patrick Zheng Date: Thu, 2 Feb 2023 13:02:16 +0800 Subject: [PATCH 17/28] updated per code review Signed-off-by: Patrick Zheng --- cmd/notation/key_test.go | 2 +- internal/cmd/flags.go | 7 +++---- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/cmd/notation/key_test.go b/cmd/notation/key_test.go index 45313905c..fe5785157 100644 --- a/cmd/notation/key_test.go +++ b/cmd/notation/key_test.go @@ -17,7 +17,7 @@ func TestKeyAddCommand_BasicArgs(t *testing.T) { if err := cmd.ParseFlags([]string{ "--plugin", expected.plugin, "--id", expected.id, - "-c", "pluginconfig", + "--plugin-config", "pluginconfig", expected.name}); err != nil { t.Fatalf("Parse Flag failed: %v", err) } diff --git a/internal/cmd/flags.go b/internal/cmd/flags.go index 53512c1be..b19ace832 100644 --- a/internal/cmd/flags.go +++ b/internal/cmd/flags.go @@ -64,12 +64,11 @@ var ( } PflagPluginConfig = &pflag.Flag{ - Name: "plugin-config", - Shorthand: "c", - Usage: "{key}={value} pairs that are passed as it is to a plugin, refer plugin's documentation to set appropriate values", + Name: "plugin-config", + Usage: "{key}={value} pairs that are passed as it is to a plugin, refer plugin's documentation to set appropriate values", } SetPflagPluginConfig = func(fs *pflag.FlagSet, p *[]string) { - fs.StringArrayVarP(p, PflagPluginConfig.Name, PflagPluginConfig.Shorthand, nil, PflagPluginConfig.Usage) + fs.StringArrayVar(p, PflagPluginConfig.Name, nil, PflagPluginConfig.Usage) } ) From 166c5f6fde63059f7874fb9e3a882a9e3b295dae Mon Sep 17 00:00:00 2001 From: Patrick Zheng Date: Thu, 2 Feb 2023 15:18:18 +0800 Subject: [PATCH 18/28] update Signed-off-by: Patrick Zheng --- cmd/notation/registry.go | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/cmd/notation/registry.go b/cmd/notation/registry.go index bc00faa61..2d4c64c97 100644 --- a/cmd/notation/registry.go +++ b/cmd/notation/registry.go @@ -53,16 +53,20 @@ func getSignatureRepositoryForSign(ctx context.Context, opts *SecureFlagOpts, re if err != nil { return nil, err } - // using OCI artifact manifest to store signatures. Notation requires the - // existence of Referrers API for Sign process. + // Notation enforces the following two paths during Sign process: + // 1. OCI artifact manifest uses the Referrers API // Reference: https://github.com/opencontainers/distribution-spec/blob/v1.1.0-rc1/spec.md#listing-referrers + // 2. OCI image manifest uses the Referrers Tag Schema + // Reference: https://github.com/opencontainers/distribution-spec/blob/v1.1.0-rc1/spec.md#referrers-tag-schema + if err := remoteRepo.SetReferrersCapability(!ociImageManifest); err != nil { + return nil, err + } + // when using OCI artifact manifest to store signatures, needs to ping the + // Referrers API before Signing if !ociImageManifest { - if err := remoteRepo.SetReferrersCapability(true); err != nil { - return nil, err - } - // ping Referrers API var checkReferrerDesc ocispec.Descriptor checkReferrerDesc.Digest = zeroDigest + // ping Referrers API err := remoteRepo.Referrers(ctx, checkReferrerDesc, "", func(referrers []ocispec.Descriptor) error { return nil }) From c4b039c0fd462aa60210387f146eddb343731169 Mon Sep 17 00:00:00 2001 From: Patrick Zheng Date: Fri, 3 Feb 2023 10:00:19 +0800 Subject: [PATCH 19/28] update Signed-off-by: Patrick Zheng --- cmd/notation/registry.go | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/cmd/notation/registry.go b/cmd/notation/registry.go index 2d4c64c97..98ede47b4 100644 --- a/cmd/notation/registry.go +++ b/cmd/notation/registry.go @@ -86,12 +86,11 @@ func getRepositoryClient(ctx context.Context, opts *SecureFlagOpts, ref registry return nil, err } - remoteRepo := &remote.Repository{ + return &remote.Repository{ Client: authClient, Reference: ref, PlainHTTP: plainHTTP, - } - return remoteRepo, nil + }, nil } func getRegistryClient(ctx context.Context, opts *SecureFlagOpts, serverAddress string) (*remote.Registry, error) { From 50f382737d7faf75cf572ec53052afbe52a9a342 Mon Sep 17 00:00:00 2001 From: Patrick Zheng Date: Mon, 6 Feb 2023 11:20:54 +0800 Subject: [PATCH 20/28] update Signed-off-by: Patrick Zheng --- cmd/notation/sign.go | 2 +- internal/slices/slices.go | 11 +++++++++++ internal/slices/slices_test.go | 25 +++++++++++++++++++++++++ 3 files changed, 37 insertions(+), 1 deletion(-) create mode 100644 internal/slices/slices.go create mode 100644 internal/slices/slices_test.go diff --git a/cmd/notation/sign.go b/cmd/notation/sign.go index e1ef9a6a2..3dbd307c0 100644 --- a/cmd/notation/sign.go +++ b/cmd/notation/sign.go @@ -148,5 +148,5 @@ func prepareSigningContent(ctx context.Context, opts *signOpts, sigRepo notation } func validateImageSpec(imageSpec string) bool { - return slices.ContainerElement(supportedImageSpec, imageSpec) + return slices.Contains(supportedImageSpec, imageSpec) } diff --git a/internal/slices/slices.go b/internal/slices/slices.go new file mode 100644 index 000000000..0d348cdf1 --- /dev/null +++ b/internal/slices/slices.go @@ -0,0 +1,11 @@ +package slices + +// Contains reports whether v is present in s. +func Contains[E comparable](s []E, v E) bool { + for _, vs := range s { + if v == vs { + return true + } + } + return false +} diff --git a/internal/slices/slices_test.go b/internal/slices/slices_test.go new file mode 100644 index 000000000..2c19ff559 --- /dev/null +++ b/internal/slices/slices_test.go @@ -0,0 +1,25 @@ +package slices + +import ( + "testing" +) + +func TestContainerElement(t *testing.T) { + tests := []struct { + c []string + v string + want bool + }{ + {nil, "", false}, + {[]string{}, "", false}, + {[]string{"1", "2", "3"}, "4", false}, + {[]string{"1", "2", "3"}, "2", true}, + {[]string{"1", "2", "2", "3"}, "2", true}, + {[]string{"1", "2", "3", "2"}, "2", true}, + } + for _, tt := range tests { + if got := Contains(tt.c, tt.v); got != tt.want { + t.Errorf("ContainerElement() = %v, want %v", got, tt.want) + } + } +} From b81bb0a86bbaec4f0df3489ed4335da1f280347e Mon Sep 17 00:00:00 2001 From: Patrick Zheng Date: Tue, 7 Feb 2023 11:32:57 +0800 Subject: [PATCH 21/28] updated per community discussion Signed-off-by: Patrick Zheng --- cmd/notation/registry.go | 8 ++++---- cmd/notation/sign.go | 34 +++++++++++++++++----------------- cmd/notation/sign_test.go | 16 ++++++++-------- specs/commandline/sign.md | 12 ++++++------ 4 files changed, 35 insertions(+), 35 deletions(-) diff --git a/cmd/notation/registry.go b/cmd/notation/registry.go index 98ede47b4..58a925cf3 100644 --- a/cmd/notation/registry.go +++ b/cmd/notation/registry.go @@ -39,9 +39,9 @@ func getSignatureRepository(ctx context.Context, opts *SecureFlagOpts, reference // getSignatureRepositoryForSign returns a registry.Repository for Sign. // ociImageManifest denotes the type of manifest used to store signatures during // Sign process. -// Setting ociImageManifest to true means using OCI image manifest and tag -// schema. -// Otherwise, use OCI artifact manifest and requires Referrers API. +// Setting ociImageManifest to true means using OCI image manifest and the +// Referrers tag schema. +// Otherwise, use OCI artifact manifest and requires the Referrers API. func getSignatureRepositoryForSign(ctx context.Context, opts *SecureFlagOpts, reference string, ociImageManifest bool) (notationregistry.Repository, error) { ref, err := registry.ParseReference(reference) if err != nil { @@ -71,7 +71,7 @@ func getSignatureRepositoryForSign(ctx context.Context, opts *SecureFlagOpts, re return nil }) if err != nil { - return nil, fmt.Errorf("failed to ping Referrers API with error: %v. Try OCI image manifest using `--image-spec`", err) + return nil, fmt.Errorf("failed to ping Referrers API with error: %v. Try store signatures with OCI image manifest using `--signature-manifest` flag", err) } } repositoryOpts := notationregistry.RepositoryOptions{ diff --git a/cmd/notation/sign.go b/cmd/notation/sign.go index 3dbd307c0..d97426e59 100644 --- a/cmd/notation/sign.go +++ b/cmd/notation/sign.go @@ -18,20 +18,20 @@ import ( ) const ( - imageSpecArtifact = "v1.1-artifact" - imageSpecImage = "v1.1-image" + sigArtifactManifest = "artifact" + sigImageManifest = "image" ) -var supportedImageSpec = []string{imageSpecArtifact, imageSpecImage} +var supportedSignatureManifest = []string{sigArtifactManifest, sigImageManifest} type signOpts struct { cmd.LoggingFlagOpts cmd.SignerFlagOpts SecureFlagOpts - expiry time.Duration - pluginConfig []string - reference string - imageSpec string + expiry time.Duration + pluginConfig []string + reference string + signatureManifest string } func signCommand(opts *signOpts) *cobra.Command { @@ -60,11 +60,11 @@ Example - Sign an OCI artifact identified by a tag (Notation will resolve tag to Example - Sign an OCI artifact stored in a registry and specify the signature expiry duration, for example 24 hours notation sign --expiry 24h /@ -Example - Sign an OCI image using the default signing key, with the default JWS envelope: - notation sign --image-spec v1.1-image /@ +Example - Sign an OCI artifact and use OCI image manifest to store the signature, with the default JWS envelope: + notation sign --signature-manifest image /@ -Example - Sign an OCI image using the default signing key, with the COSE envelope: - notation sign --image-spec v1.1-image --signature-format cose /@ +Example - Sign an OCI artifact and use OCI image manifest to store the signature, with the COSE envelope: + notation sign --signature-manifest image --signature-format cose /@ `, Args: func(cmd *cobra.Command, args []string) error { if len(args) == 0 { @@ -75,8 +75,8 @@ Example - Sign an OCI image using the default signing key, with the COSE envelop }, RunE: func(cmd *cobra.Command, args []string) error { // sanity check - if !validateImageSpec(opts.imageSpec) { - return fmt.Errorf("image spec must be one of the following %v but got %s", supportedImageSpec, opts.imageSpec) + if !validateSignatureManifest(opts.signatureManifest) { + return fmt.Errorf("image spec must be one of the following %v but got %s", supportedSignatureManifest, opts.signatureManifest) } return runSign(cmd, opts) }, @@ -86,7 +86,7 @@ Example - Sign an OCI image using the default signing key, with the COSE envelop opts.SecureFlagOpts.ApplyFlags(command.Flags()) cmd.SetPflagExpiry(command.Flags(), &opts.expiry) cmd.SetPflagPluginConfig(command.Flags(), &opts.pluginConfig) - command.Flags().StringVar(&opts.imageSpec, "image-spec", imageSpecArtifact, "manifest type for signatures. options: v1.1-artifact, v1.1-image") + command.Flags().StringVar(&opts.signatureManifest, "signature-manifest", sigArtifactManifest, "manifest type for signatures. options: artifact, image") return command } @@ -99,7 +99,7 @@ func runSign(command *cobra.Command, cmdOpts *signOpts) error { if err != nil { return err } - ociImageManifest := cmdOpts.imageSpec == imageSpecImage + ociImageManifest := cmdOpts.signatureManifest == sigImageManifest sigRepo, err := getSignatureRepositoryForSign(ctx, &cmdOpts.SecureFlagOpts, cmdOpts.reference, ociImageManifest) if err != nil { return err @@ -147,6 +147,6 @@ func prepareSigningContent(ctx context.Context, opts *signOpts, sigRepo notation return signOpts, ref, nil } -func validateImageSpec(imageSpec string) bool { - return slices.Contains(supportedImageSpec, imageSpec) +func validateSignatureManifest(signatureManifest string) bool { + return slices.Contains(supportedSignatureManifest, signatureManifest) } diff --git a/cmd/notation/sign_test.go b/cmd/notation/sign_test.go index ce4b609ee..9103b8c14 100644 --- a/cmd/notation/sign_test.go +++ b/cmd/notation/sign_test.go @@ -23,7 +23,7 @@ func TestSignCommand_BasicArgs(t *testing.T) { Key: "key", SignatureFormat: envelope.JWS, }, - imageSpec: imageSpecArtifact, + signatureManifest: "artifact", } if err := command.ParseFlags([]string{ expected.reference, @@ -54,8 +54,8 @@ func TestSignCommand_MoreArgs(t *testing.T) { Key: "key", SignatureFormat: envelope.COSE, }, - expiry: 24 * time.Hour, - imageSpec: imageSpecImage, + expiry: 24 * time.Hour, + signatureManifest: sigImageManifest, } if err := command.ParseFlags([]string{ expected.reference, @@ -65,7 +65,7 @@ func TestSignCommand_MoreArgs(t *testing.T) { "--plain-http", "--signature-format", expected.SignerFlagOpts.SignatureFormat, "--expiry", expected.expiry.String(), - "--image-spec", imageSpecImage}); err != nil { + "--signature-manifest", sigImageManifest}); err != nil { t.Fatalf("Parse Flag failed: %v", err) } if err := command.Args(command, command.Flags().Args()); err != nil { @@ -83,11 +83,11 @@ func TestSignCommand_CorrectConfig(t *testing.T) { reference: "ref", SignerFlagOpts: cmd.SignerFlagOpts{ Key: "key", - SignatureFormat: envelope.JWS, + SignatureFormat: envelope.COSE, }, - expiry: 365 * 24 * time.Hour, - pluginConfig: []string{"key0=val0", "key1=val1"}, - imageSpec: imageSpecArtifact, + expiry: 365 * 24 * time.Hour, + pluginConfig: []string{"key0=val0", "key1=val1"}, + signatureManifest: "artifact", } if err := command.ParseFlags([]string{ expected.reference, diff --git a/specs/commandline/sign.md b/specs/commandline/sign.md index 420d430b1..59b9cc13d 100644 --- a/specs/commandline/sign.md +++ b/specs/commandline/sign.md @@ -31,7 +31,7 @@ Flags: -d, --debug debug mode -e, --expiry duration optional expiry that provides a "best by use" time for the artifact. The duration is specified in minutes(m) and/or hours(h). For example: 12h, 30m, 3h20m -h, --help help for sign - --image-spec string manifest type for signatures. options: v1.1-artifact, v1.1-image (default "v1.1-artifact") + --signature-manifest string manifest type for signatures. options: artifact, image (default "artifact") -k, --key string signing key name, for a key previously added to notation's key list. -p, --password string password for registry operations (default to $NOTATION_PASSWORD if not specified) --plain-http registry access via plain HTTP @@ -44,13 +44,13 @@ Flags: ## Use OCI image manifest to store signatures -By default, Notation uses [OCI artifact manifest][oci-artifact-manifest] to store signatures in registries. For backward compatibility, Notation supports using `OCI image manifest` to store signatures in registries that partially implement the [OCI Image specification v1.1][oci-image-spec]. Use flag `--image-spec v1.1-image` to force Notation to store the signatures using OCI image manifest. +By default, Notation uses [OCI artifact manifest][oci-artifact-manifest] to store signatures in registries. For backward compatibility, Notation supports using `OCI image manifest` to store signatures in registries that partially implement the [OCI Image specification v1.1][oci-image-spec]. Use flag `--signature-manifest image` to force Notation to store the signatures using OCI image manifest. Registries MAY not implement or enable the `Referrers API`, which is used by clients to fetch referrers. In the context of Notation, the referrers are signatures. Notation follows the fallback procedure defined in [OCI distribution spec][oci-backward-compatibility] if `Referrers API` is unavailable. ### Set config property for OCI image manifest -OCI image manifest requires additional property `config` of type `descriptor`, which is not required by OCI artifact manifest. Notation creates a default config descriptor for the user if flag `--image-spec v1.1-image` is used. +OCI image manifest requires additional property `config` of type `descriptor`, which is not required by OCI artifact manifest. Notation creates a default config descriptor for the user if flag `--signature-manifest image` is used. Notation uses empty JSON object `{}` as the default configuration content, and thus the default `config` property is fixed, as following: @@ -64,9 +64,9 @@ Notation uses empty JSON object `{}` as the default configuration content, and t ### When to use OCI image manifest -[Registry support][registry-support] lists registries with different compatibilities. For registries not supporting `OCI artifact manifest`, users can use flag `--image-spec v1.1-image` to sign artifacts stored in those registries. +[Registry support][registry-support] lists registries with different compatibilities. For registries not supporting `OCI artifact manifest`, users can use flag `--signature-manifest image` to sign artifacts stored in those registries. -For registries not listed in the page, users can consider using flag `--image-spec v1.1-image` by checking the error message. Note that there is no deterministic way to determine whether a registry supports `OCI artifact manifest` or not. The error message is just for reference. The following response status contained in error messages MAY indicate that the registry doesn't support `OCI artifact manifest`: +For registries not listed in the page, users can consider using flag `--signature-manifest image` by checking the error message. Note that there is no deterministic way to determine whether a registry supports `OCI artifact manifest` or not. The error message is just for reference. The following response status contained in error messages MAY indicate that the registry doesn't support `OCI artifact manifest`: - Response status `400 BAD Request` with error code `MANIFEST_INVALID` or `UNSUPPORTED` @@ -163,7 +163,7 @@ Successfully signed localhost:5000/net-monitor@sha256:b94d27b9934d3e08a52e52d7da ### Sign an artifact and store the signature using OCI image manifest ```shell -notation sign --image-spec v1.1-image /@ +notation sign --signature-manifest image /@ ``` [oci-artifact-manifest]: https://github.com/opencontainers/image-spec/blob/v1.1.0-rc2/artifact.md From 7a52d77d9b1c1bef3c36f87341682ef350cdd021 Mon Sep 17 00:00:00 2001 From: Patrick Zheng Date: Tue, 7 Feb 2023 12:46:01 +0800 Subject: [PATCH 22/28] update Signed-off-by: Patrick Zheng --- cmd/notation/sign.go | 2 +- specs/commandline/sign.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/cmd/notation/sign.go b/cmd/notation/sign.go index d97426e59..ecf6100a6 100644 --- a/cmd/notation/sign.go +++ b/cmd/notation/sign.go @@ -76,7 +76,7 @@ Example - Sign an OCI artifact and use OCI image manifest to store the signature RunE: func(cmd *cobra.Command, args []string) error { // sanity check if !validateSignatureManifest(opts.signatureManifest) { - return fmt.Errorf("image spec must be one of the following %v but got %s", supportedSignatureManifest, opts.signatureManifest) + return fmt.Errorf("signature manifest must be one of the following %v but got %s", supportedSignatureManifest, opts.signatureManifest) } return runSign(cmd, opts) }, diff --git a/specs/commandline/sign.md b/specs/commandline/sign.md index 59b9cc13d..95be17a21 100644 --- a/specs/commandline/sign.md +++ b/specs/commandline/sign.md @@ -31,12 +31,12 @@ Flags: -d, --debug debug mode -e, --expiry duration optional expiry that provides a "best by use" time for the artifact. The duration is specified in minutes(m) and/or hours(h). For example: 12h, 30m, 3h20m -h, --help help for sign - --signature-manifest string manifest type for signatures. options: artifact, image (default "artifact") -k, --key string signing key name, for a key previously added to notation's key list. -p, --password string password for registry operations (default to $NOTATION_PASSWORD if not specified) --plain-http registry access via plain HTTP --plugin-config stringArray {key}={value} pairs that are passed as it is to a plugin, refer plugin's documentation to set appropriate values --signature-format string signature envelope format, options: 'jws', 'cose' (default "jws") + --signature-manifest string manifest type for signatures. options: artifact, image (default "artifact") -u, --username string username for registry operations (default to $NOTATION_USERNAME if not specified) -m, --user-metadata stringArray {key}={value} pairs that are added to the signature payload -v, --verbose verbose mode From 216cd58c38e326231d2f18b436e61f5b6745204a Mon Sep 17 00:00:00 2001 From: Patrick Zheng Date: Tue, 7 Feb 2023 17:00:36 +0800 Subject: [PATCH 23/28] updated error messages Signed-off-by: Patrick Zheng --- cmd/notation/registry.go | 2 +- cmd/notation/sign.go | 4 ++++ cmd/notation/verify.go | 2 +- 3 files changed, 6 insertions(+), 2 deletions(-) diff --git a/cmd/notation/registry.go b/cmd/notation/registry.go index 58a925cf3..b25e4e833 100644 --- a/cmd/notation/registry.go +++ b/cmd/notation/registry.go @@ -71,7 +71,7 @@ func getSignatureRepositoryForSign(ctx context.Context, opts *SecureFlagOpts, re return nil }) if err != nil { - return nil, fmt.Errorf("failed to ping Referrers API with error: %v. Try store signatures with OCI image manifest using `--signature-manifest` flag", err) + return nil, fmt.Errorf("failed to ping Referrers API with error: %v. Try store signatures with OCI image manifest using `--signature-manifest image`", err) } } repositoryOpts := notationregistry.RepositoryOptions{ diff --git a/cmd/notation/sign.go b/cmd/notation/sign.go index ecf6100a6..3a4c5a9bb 100644 --- a/cmd/notation/sign.go +++ b/cmd/notation/sign.go @@ -112,6 +112,10 @@ func runSign(command *cobra.Command, cmdOpts *signOpts) error { // core process _, err = notation.Sign(ctx, signer, sigRepo, opts) if err != nil { + var errorPushSignatureFailed notation.ErrorPushSignatureFailed + if errors.As(err, &errorPushSignatureFailed) { + return fmt.Errorf("%v. This might be due to target registry does not support OCI artifact manifest. Try store signatures with OCI image manifest using `--signature-manifest image`", err) + } return err } diff --git a/cmd/notation/verify.go b/cmd/notation/verify.go index bfb6de0b0..ac300cac3 100644 --- a/cmd/notation/verify.go +++ b/cmd/notation/verify.go @@ -104,7 +104,7 @@ func runVerify(command *cobra.Command, opts *verifyOpts) error { // write out on failure if err != nil || len(outcomes) == 0 { if err != nil { - var errorVerificationFailed *notation.ErrorVerificationFailed + var errorVerificationFailed notation.ErrorVerificationFailed if !errors.As(err, &errorVerificationFailed) { return fmt.Errorf("signature verification failed: %w", err) } From e0888b901c30ed7bec6bf24f16cb85ac24871df8 Mon Sep 17 00:00:00 2001 From: Patrick Zheng Date: Wed, 8 Feb 2023 10:07:53 +0800 Subject: [PATCH 24/28] updated dependency Signed-off-by: Patrick Zheng --- cmd/notation/registry.go | 2 +- go.mod | 2 +- go.sum | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/cmd/notation/registry.go b/cmd/notation/registry.go index b25e4e833..077873fe8 100644 --- a/cmd/notation/registry.go +++ b/cmd/notation/registry.go @@ -71,7 +71,7 @@ func getSignatureRepositoryForSign(ctx context.Context, opts *SecureFlagOpts, re return nil }) if err != nil { - return nil, fmt.Errorf("failed to ping Referrers API with error: %v. Try store signatures with OCI image manifest using `--signature-manifest image`", err) + return nil, fmt.Errorf("failed to ping Referrers API with error: %v. This might be due to target registry does not support the Referrers API. Try store signatures with OCI image manifest using `--signature-manifest image`", err) } } repositoryOpts := notationregistry.RepositoryOptions{ diff --git a/go.mod b/go.mod index 2f825c0fc..ed87f5cdd 100644 --- a/go.mod +++ b/go.mod @@ -5,7 +5,7 @@ go 1.20 require ( github.com/docker/docker-credential-helpers v0.7.0 github.com/notaryproject/notation-core-go v1.0.0-rc.1 - github.com/notaryproject/notation-go v1.0.0-rc.1.0.20230203031935-510def1a3f48 + github.com/notaryproject/notation-go v1.0.0-rc.1.0.20230207181533-5e5cba1e9adf github.com/opencontainers/go-digest v1.0.0 github.com/opencontainers/image-spec v1.1.0-rc2 github.com/sirupsen/logrus v1.9.0 diff --git a/go.sum b/go.sum index 2525aad58..e9555db96 100644 --- a/go.sum +++ b/go.sum @@ -20,8 +20,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 v1.0.0-rc.1 h1:ACi0gr6mD1bzp9+gu3P0meJ/N6iWHlyM9zgtdnooNAA= github.com/notaryproject/notation-core-go v1.0.0-rc.1/go.mod h1:n8Gbvl9sKa00KptkKEL5XKUyMTIALe74QipKauE2rj4= -github.com/notaryproject/notation-go v1.0.0-rc.1.0.20230203031935-510def1a3f48 h1:MHjaRqAn+uCBYkDuIGaVo91CnJY9MlTcZdYFfoE4yek= -github.com/notaryproject/notation-go v1.0.0-rc.1.0.20230203031935-510def1a3f48/go.mod h1:B/26FcjJ9GVXm1j7z+/pWKck80LdFi3KiX4Zu7gixB8= +github.com/notaryproject/notation-go v1.0.0-rc.1.0.20230207181533-5e5cba1e9adf h1:cJF1bWntutigjznJC65Tb2+oHHLrgN1g/vDxcWFeslw= +github.com/notaryproject/notation-go v1.0.0-rc.1.0.20230207181533-5e5cba1e9adf/go.mod h1:B/26FcjJ9GVXm1j7z+/pWKck80LdFi3KiX4Zu7gixB8= 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= From d5dbb29435325ccde7d84a78066883c8f6ab12d7 Mon Sep 17 00:00:00 2001 From: Patrick Zheng Date: Wed, 8 Feb 2023 16:03:25 +0800 Subject: [PATCH 25/28] update with unit tests Signed-off-by: Patrick Zheng --- cmd/notation/internal/errors/errors.go | 14 +++ cmd/notation/registry.go | 56 ++++++++-- cmd/notation/registry_test.go | 135 +++++++++++++++++++++++++ 3 files changed, 195 insertions(+), 10 deletions(-) create mode 100644 cmd/notation/internal/errors/errors.go create mode 100644 cmd/notation/registry_test.go diff --git a/cmd/notation/internal/errors/errors.go b/cmd/notation/internal/errors/errors.go new file mode 100644 index 000000000..18d39e234 --- /dev/null +++ b/cmd/notation/internal/errors/errors.go @@ -0,0 +1,14 @@ +package errors + +// ErrorReferrersAPINotSupported is used when the target registry does not +// support the Referrers API +type ErrorReferrersAPINotSupported struct { + Msg string +} + +func (e ErrorReferrersAPINotSupported) Error() string { + if e.Msg != "" { + return e.Msg + } + return "referrers API not supported" +} diff --git a/cmd/notation/registry.go b/cmd/notation/registry.go index 077873fe8..1faa71523 100644 --- a/cmd/notation/registry.go +++ b/cmd/notation/registry.go @@ -9,6 +9,7 @@ import ( "github.com/notaryproject/notation-go/log" notationregistry "github.com/notaryproject/notation-go/registry" + notationerrors "github.com/notaryproject/notation/cmd/notation/internal/errors" "github.com/notaryproject/notation/internal/trace" "github.com/notaryproject/notation/internal/version" loginauth "github.com/notaryproject/notation/pkg/auth" @@ -18,6 +19,7 @@ import ( "oras.land/oras-go/v2/registry" "oras.land/oras-go/v2/registry/remote" "oras.land/oras-go/v2/registry/remote/auth" + "oras.land/oras-go/v2/registry/remote/errcode" ) const zeroDigest = "sha256:0000000000000000000000000000000000000000000000000000000000000000" @@ -43,6 +45,7 @@ func getSignatureRepository(ctx context.Context, opts *SecureFlagOpts, reference // Referrers tag schema. // Otherwise, use OCI artifact manifest and requires the Referrers API. func getSignatureRepositoryForSign(ctx context.Context, opts *SecureFlagOpts, reference string, ociImageManifest bool) (notationregistry.Repository, error) { + logger := log.GetLogger(ctx) ref, err := registry.ParseReference(reference) if err != nil { return nil, err @@ -53,25 +56,25 @@ func getSignatureRepositoryForSign(ctx context.Context, opts *SecureFlagOpts, re if err != nil { return nil, err } + // Notation enforces the following two paths during Sign process: // 1. OCI artifact manifest uses the Referrers API // Reference: https://github.com/opencontainers/distribution-spec/blob/v1.1.0-rc1/spec.md#listing-referrers // 2. OCI image manifest uses the Referrers Tag Schema // Reference: https://github.com/opencontainers/distribution-spec/blob/v1.1.0-rc1/spec.md#referrers-tag-schema - if err := remoteRepo.SetReferrersCapability(!ociImageManifest); err != nil { - return nil, err - } - // when using OCI artifact manifest to store signatures, needs to ping the - // Referrers API before Signing if !ociImageManifest { + logger.Info("Use OCI artifact manifest to store signature") var checkReferrerDesc ocispec.Descriptor checkReferrerDesc.Digest = zeroDigest // ping Referrers API - err := remoteRepo.Referrers(ctx, checkReferrerDesc, "", func(referrers []ocispec.Descriptor) error { - return nil - }) - if err != nil { - return nil, fmt.Errorf("failed to ping Referrers API with error: %v. This might be due to target registry does not support the Referrers API. Try store signatures with OCI image manifest using `--signature-manifest image`", err) + if err := pingReferrersAPI(ctx, remoteRepo); err != nil { + return nil, err + } + logger.Info("Successfully pinged Referrers API on target registry") + } else { + logger.Info("Use OCI image manifest to store signature") + if err := remoteRepo.SetReferrersCapability(false); err != nil { + return nil, err } } repositoryOpts := notationregistry.RepositoryOptions{ @@ -178,3 +181,36 @@ func getSavedCreds(ctx context.Context, serverAddress string) (auth.Credential, return nativeStore.Get(serverAddress) } + +func pingReferrersAPI(ctx context.Context, remoteRepo *remote.Repository) error { + if err := remoteRepo.SetReferrersCapability(true); err != nil { + return err + } + var checkReferrerDesc ocispec.Descriptor + checkReferrerDesc.Digest = zeroDigest + // core process + err := remoteRepo.Referrers(ctx, checkReferrerDesc, "", func(referrers []ocispec.Descriptor) error { + return nil + }) + if err != nil { + var errResp *errcode.ErrorResponse + if !errors.As(err, &errResp) || errResp.StatusCode != http.StatusNotFound { + return err + } + if isErrorCode(errResp, errcode.ErrorCodeNameUnknown) { + // The repository is not found. + return err + } + // A 404 returned by Referrers API indicates that Referrers API is + // not supported. + errMsg := fmt.Sprintf("failed to ping Referrers API with error: %v. This is due to target registry does not support the Referrers API. Try store signatures with OCI image manifest using `--signature-manifest image`", err) + return notationerrors.ErrorReferrersAPINotSupported{Msg: errMsg} + } + return nil +} + +// isErrorCode returns true if err is an Error and its Code equals to code. +func isErrorCode(err error, code string) bool { + var ec errcode.Error + return errors.As(err, &ec) && ec.Code == code +} diff --git a/cmd/notation/registry_test.go b/cmd/notation/registry_test.go new file mode 100644 index 000000000..41699d272 --- /dev/null +++ b/cmd/notation/registry_test.go @@ -0,0 +1,135 @@ +package main + +import ( + "context" + "errors" + "net/http" + "net/http/httptest" + "net/url" + "reflect" + "testing" + + notationerrors "github.com/notaryproject/notation/cmd/notation/internal/errors" + "oras.land/oras-go/v2/registry/remote" + "oras.land/oras-go/v2/registry/remote/errcode" +) + +func TestRegistry_pingReferrersAPI_Success(t *testing.T) { + ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + if r.Method == http.MethodGet && r.URL.Path == "/v2/test/referrers/"+zeroDigest { + w.WriteHeader(http.StatusOK) + w.Write([]byte(`{ "test": "TEST" }`)) + return + } + t.Errorf("unexpected access: %s %q", r.Method, r.URL) + w.WriteHeader(http.StatusNotFound) + })) + defer ts.Close() + uri, err := url.Parse(ts.URL) + if err != nil { + t.Fatalf("invalid test http server: %v", err) + } + repo, err := remote.NewRepository(uri.Host + "/test") + if err != nil { + t.Fatalf("NewRepository() error = %v", err) + } + repo.PlainHTTP = true + ctx := context.Background() + err = pingReferrersAPI(ctx, repo) + if err != nil { + t.Errorf("pingReferrersAPI() expected nil error, but got error: %v", err) + } +} + +func TestRegistry_pingReferrersAPI_ReferrersAPINotSupported(t *testing.T) { + ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + if r.Method == http.MethodGet && r.URL.Path == "/v2/test/referrers/"+zeroDigest { + w.WriteHeader(http.StatusNotFound) + w.Write([]byte(`{ "errorresponse": { "method": "GET", "statuscode": 404 } }`)) + return + } + t.Errorf("unexpected access: %s %q", r.Method, r.URL) + w.WriteHeader(http.StatusNotFound) + })) + defer ts.Close() + uri, err := url.Parse(ts.URL) + if err != nil { + t.Fatalf("invalid test http server: %v", err) + } + ctx := context.Background() + repo, err := remote.NewRepository(uri.Host + "/test") + if err != nil { + t.Fatalf("NewRepository() error = %v", err) + } + repo.PlainHTTP = true + err = pingReferrersAPI(ctx, repo) + var errorReferrersAPINotSupported notationerrors.ErrorReferrersAPINotSupported + if err == nil || !errors.As(err, &errorReferrersAPINotSupported) { + t.Errorf("pingReferrersAPI() expected ErrorReferrersAPINotSupported, but got: %v", err) + } +} + +func TestRegistry_pingReferrersAPI_Failed(t *testing.T) { + ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + if r.Method == http.MethodGet && r.URL.Path == "/v2/test/referrers/"+zeroDigest { + w.WriteHeader(http.StatusOK) + return + } + t.Errorf("unexpected access: %s %q", r.Method, r.URL) + w.WriteHeader(http.StatusNotFound) + })) + defer ts.Close() + uri, err := url.Parse(ts.URL) + if err != nil { + t.Fatalf("invalid test http server: %v", err) + } + ctx := context.Background() + repo, err := remote.NewRepository(uri.Host + "/test") + if err != nil { + t.Fatalf("NewRepository() error = %v", err) + } + repo.PlainHTTP = true + err = pingReferrersAPI(ctx, repo) + if err == nil { + t.Errorf("pingReferrersAPI expected to get error but got nil") + } +} + +func TestRegistry_pingReferrersAPI_RepositoryNotFound(t *testing.T) { + ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + if r.Method == http.MethodGet && r.URL.Path == "/v2/test/referrers/"+zeroDigest { + w.WriteHeader(http.StatusNotFound) + w.Write([]byte(`{ "errors": [ { "code": "NAME_UNKNOWN", "message": "repository name not known to registry" } ] }`)) + return + } + t.Errorf("unexpected access: %s %q", r.Method, r.URL) + w.WriteHeader(http.StatusNotFound) + })) + defer ts.Close() + uri, err := url.Parse(ts.URL) + if err != nil { + t.Fatalf("invalid test http server: %v", err) + } + ctx := context.Background() + expectedErr := errcode.Error{ + Code: errcode.ErrorCodeNameUnknown, + Message: "repository name not known to registry", + } + + repo, err := remote.NewRepository(uri.Host + "/test") + if err != nil { + t.Fatalf("NewRepository() error = %v", err) + } + repo.PlainHTTP = true + err = pingReferrersAPI(ctx, repo) + if err == nil { + t.Fatalf("pingReferrersAPI() expected error but got nil") + } + var ec errcode.Error + if !errors.As(err, &ec) { + t.Errorf("pingReferrersAPI() expected errcode.Error") + } + if !reflect.DeepEqual(ec, expectedErr) { + t.Errorf("pingReferrersAPI() expected error: %v, but got: %v", expectedErr, err) + } +} From c8d2ce2dcb9a3279a29e53b599167346cc05452e Mon Sep 17 00:00:00 2001 From: Patrick Zheng Date: Wed, 8 Feb 2023 16:20:40 +0800 Subject: [PATCH 26/28] updated log Signed-off-by: Patrick Zheng --- cmd/notation/registry.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cmd/notation/registry.go b/cmd/notation/registry.go index 1faa71523..cc1a41e78 100644 --- a/cmd/notation/registry.go +++ b/cmd/notation/registry.go @@ -63,7 +63,7 @@ func getSignatureRepositoryForSign(ctx context.Context, opts *SecureFlagOpts, re // 2. OCI image manifest uses the Referrers Tag Schema // Reference: https://github.com/opencontainers/distribution-spec/blob/v1.1.0-rc1/spec.md#referrers-tag-schema if !ociImageManifest { - logger.Info("Use OCI artifact manifest to store signature") + logger.Info("Use OCI artifact manifest and Referrers API to store signature") var checkReferrerDesc ocispec.Descriptor checkReferrerDesc.Digest = zeroDigest // ping Referrers API @@ -72,7 +72,7 @@ func getSignatureRepositoryForSign(ctx context.Context, opts *SecureFlagOpts, re } logger.Info("Successfully pinged Referrers API on target registry") } else { - logger.Info("Use OCI image manifest to store signature") + logger.Info("Use OCI image manifest and Referrers Tag Schema to store signature") if err := remoteRepo.SetReferrersCapability(false); err != nil { return nil, err } From d2fb35623125e5d3b06ea72dfb509ca1bb1c0a68 Mon Sep 17 00:00:00 2001 From: Patrick Zheng Date: Wed, 8 Feb 2023 16:38:14 +0800 Subject: [PATCH 27/28] update Signed-off-by: Patrick Zheng --- cmd/notation/registry.go | 2 -- 1 file changed, 2 deletions(-) diff --git a/cmd/notation/registry.go b/cmd/notation/registry.go index cc1a41e78..68ffdf350 100644 --- a/cmd/notation/registry.go +++ b/cmd/notation/registry.go @@ -64,8 +64,6 @@ func getSignatureRepositoryForSign(ctx context.Context, opts *SecureFlagOpts, re // Reference: https://github.com/opencontainers/distribution-spec/blob/v1.1.0-rc1/spec.md#referrers-tag-schema if !ociImageManifest { logger.Info("Use OCI artifact manifest and Referrers API to store signature") - var checkReferrerDesc ocispec.Descriptor - checkReferrerDesc.Digest = zeroDigest // ping Referrers API if err := pingReferrersAPI(ctx, remoteRepo); err != nil { return nil, err From 65f2fcb0385a81fdeedbbabb40610f4588b0e6d6 Mon Sep 17 00:00:00 2001 From: Patrick Zheng Date: Wed, 8 Feb 2023 19:14:49 +0800 Subject: [PATCH 28/28] updated per code review Signed-off-by: Patrick Zheng --- cmd/notation/registry.go | 9 ++++++--- cmd/notation/sign.go | 15 ++++++--------- cmd/notation/sign_test.go | 4 ++-- 3 files changed, 14 insertions(+), 14 deletions(-) diff --git a/cmd/notation/registry.go b/cmd/notation/registry.go index 68ffdf350..40788d003 100644 --- a/cmd/notation/registry.go +++ b/cmd/notation/registry.go @@ -3,7 +3,6 @@ package main import ( "context" "errors" - "fmt" "net" "net/http" @@ -181,6 +180,7 @@ func getSavedCreds(ctx context.Context, serverAddress string) (auth.Credential, } func pingReferrersAPI(ctx context.Context, remoteRepo *remote.Repository) error { + logger := log.GetLogger(ctx) if err := remoteRepo.SetReferrersCapability(true); err != nil { return err } @@ -196,12 +196,15 @@ func pingReferrersAPI(ctx context.Context, remoteRepo *remote.Repository) error return err } if isErrorCode(errResp, errcode.ErrorCodeNameUnknown) { - // The repository is not found. + // The repository is not found in the target registry. + // This is triggered when putting signatures to an empty repository. + // For notation, this path should never be triggered. return err } // A 404 returned by Referrers API indicates that Referrers API is // not supported. - errMsg := fmt.Sprintf("failed to ping Referrers API with error: %v. This is due to target registry does not support the Referrers API. Try store signatures with OCI image manifest using `--signature-manifest image`", err) + logger.Infof("failed to ping Referrers API with error: %v", err) + errMsg := "Target registry does not support the Referrers API. Try the flag `--signature-manifest image` to store signatures using OCI image manifest for backwards compatibility" return notationerrors.ErrorReferrersAPINotSupported{Msg: errMsg} } return nil diff --git a/cmd/notation/sign.go b/cmd/notation/sign.go index 3a4c5a9bb..7e30edec6 100644 --- a/cmd/notation/sign.go +++ b/cmd/notation/sign.go @@ -18,11 +18,11 @@ import ( ) const ( - sigArtifactManifest = "artifact" - sigImageManifest = "image" + signatureManifestArtifact = "artifact" + signatureManifestImage = "image" ) -var supportedSignatureManifest = []string{sigArtifactManifest, sigImageManifest} +var supportedSignatureManifest = []string{signatureManifestArtifact, signatureManifestImage} type signOpts struct { cmd.LoggingFlagOpts @@ -62,9 +62,6 @@ Example - Sign an OCI artifact stored in a registry and specify the signature ex Example - Sign an OCI artifact and use OCI image manifest to store the signature, with the default JWS envelope: notation sign --signature-manifest image /@ - -Example - Sign an OCI artifact and use OCI image manifest to store the signature, with the COSE envelope: - notation sign --signature-manifest image --signature-format cose /@ `, Args: func(cmd *cobra.Command, args []string) error { if len(args) == 0 { @@ -86,7 +83,7 @@ Example - Sign an OCI artifact and use OCI image manifest to store the signature opts.SecureFlagOpts.ApplyFlags(command.Flags()) cmd.SetPflagExpiry(command.Flags(), &opts.expiry) cmd.SetPflagPluginConfig(command.Flags(), &opts.pluginConfig) - command.Flags().StringVar(&opts.signatureManifest, "signature-manifest", sigArtifactManifest, "manifest type for signatures. options: artifact, image") + command.Flags().StringVar(&opts.signatureManifest, "signature-manifest", signatureManifestArtifact, "manifest type for signatures. options: artifact, image") return command } @@ -99,7 +96,7 @@ func runSign(command *cobra.Command, cmdOpts *signOpts) error { if err != nil { return err } - ociImageManifest := cmdOpts.signatureManifest == sigImageManifest + ociImageManifest := cmdOpts.signatureManifest == signatureManifestImage sigRepo, err := getSignatureRepositoryForSign(ctx, &cmdOpts.SecureFlagOpts, cmdOpts.reference, ociImageManifest) if err != nil { return err @@ -114,7 +111,7 @@ func runSign(command *cobra.Command, cmdOpts *signOpts) error { if err != nil { var errorPushSignatureFailed notation.ErrorPushSignatureFailed if errors.As(err, &errorPushSignatureFailed) { - return fmt.Errorf("%v. This might be due to target registry does not support OCI artifact manifest. Try store signatures with OCI image manifest using `--signature-manifest image`", err) + return fmt.Errorf("%v. Target registry does not seem to support OCI artifact manifest. Try the flag `--signature-manifest image` to store signatures using OCI image manifest for backwards compatibility", err) } return err } diff --git a/cmd/notation/sign_test.go b/cmd/notation/sign_test.go index 9103b8c14..bd2bca29e 100644 --- a/cmd/notation/sign_test.go +++ b/cmd/notation/sign_test.go @@ -55,7 +55,7 @@ func TestSignCommand_MoreArgs(t *testing.T) { SignatureFormat: envelope.COSE, }, expiry: 24 * time.Hour, - signatureManifest: sigImageManifest, + signatureManifest: signatureManifestImage, } if err := command.ParseFlags([]string{ expected.reference, @@ -65,7 +65,7 @@ func TestSignCommand_MoreArgs(t *testing.T) { "--plain-http", "--signature-format", expected.SignerFlagOpts.SignatureFormat, "--expiry", expected.expiry.String(), - "--signature-manifest", sigImageManifest}); err != nil { + "--signature-manifest", signatureManifestImage}); err != nil { t.Fatalf("Parse Flag failed: %v", err) } if err := command.Args(command, command.Flags().Args()); err != nil {