Skip to content

Commit

Permalink
update: updated notation sign command based on spec (notaryproject#417)
Browse files Browse the repository at this point in the history
This PR updates notation sign command based on the
[sign_spec](https://github.com/notaryproject/notation/blob/main/specs/commandline/sign.md).

Tested with `notation certificate generate-test` and `notation sign` on
ACR.

Signed-off-by: Patrick Zheng <[email protected]>
  • Loading branch information
Two-Hearts authored Oct 31, 2022
1 parent 2992190 commit a08dc9e
Show file tree
Hide file tree
Showing 12 changed files with 48 additions and 220 deletions.
41 changes: 0 additions & 41 deletions cmd/notation/common.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,25 +39,6 @@ var (
setFlagPlainHTTP = func(fs *pflag.FlagSet, p *bool) {
fs.BoolVar(p, flagPlainHTTP.Name, false, flagPlainHTTP.Usage)
}

flagMediaType = &pflag.Flag{
Name: "media-type",
Usage: "specify the media type of the manifest read from file or stdin",
DefValue: defaultMediaType,
}
setFlagMediaType = func(fs *pflag.FlagSet, p *string) {
fs.StringVar(p, flagMediaType.Name, defaultMediaType, flagMediaType.Usage)
}

flagLocal = &pflag.Flag{
Name: "local",
Shorthand: "l",
Usage: "reference is a local file",
DefValue: "false",
}
setFlagLocal = func(fs *pflag.FlagSet, p *bool) {
fs.BoolVarP(p, flagLocal.Name, flagLocal.Shorthand, false, flagLocal.Usage)
}
)

type SecureFlagOpts struct {
Expand All @@ -74,25 +55,3 @@ func (opts *SecureFlagOpts) ApplyFlags(fs *pflag.FlagSet) {
opts.Username = os.Getenv(defaultUsernameEnv)
opts.Password = os.Getenv(defaultPasswordEnv)
}

type CommonFlagOpts struct {
Local bool
MediaType string
}

// ApplyFlags set flags and their default values for the FlagSet
func (opts *CommonFlagOpts) ApplyFlags(fs *pflag.FlagSet) {
setFlagMediaType(fs, &opts.MediaType)
setFlagLocal(fs, &opts.Local)
}

type RemoteFlagOpts struct {
SecureFlagOpts
CommonFlagOpts
}

// ApplyFlags set flags and their default values for the FlagSet
func (opts *RemoteFlagOpts) ApplyFlags(fs *pflag.FlagSet) {
opts.SecureFlagOpts.ApplyFlags(fs)
opts.CommonFlagOpts.ApplyFlags(fs)
}
4 changes: 3 additions & 1 deletion cmd/notation/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,9 @@ func main() {
keyCommand(),
pluginCommand(),
loginCommand(nil),
logoutCommand(nil))
logoutCommand(nil),
versionCommand(),
)
if err := cmd.Execute(); err != nil {
log.Fatal(err)
}
Expand Down
44 changes: 2 additions & 42 deletions cmd/notation/manifest.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,32 +3,17 @@ package main
import (
"context"
"errors"
"io"
"math"
"os"

"github.com/notaryproject/notation-go"
"github.com/opencontainers/go-digest"
"oras.land/oras-go/v2/registry"
)

func getManifestDescriptorFromContext(ctx context.Context, opts *RemoteFlagOpts, ref string) (notation.Descriptor, error) {
func getManifestDescriptorFromContext(ctx context.Context, opts *SecureFlagOpts, ref string) (notation.Descriptor, error) {
if ref == "" {
return notation.Descriptor{}, errors.New("missing reference")
}
return getManifestDescriptorFromContextWithReference(ctx, opts, ref)
}

func getManifestDescriptorFromContextWithReference(ctx context.Context, opts *RemoteFlagOpts, ref string) (notation.Descriptor, error) {
if opts.Local {
mediaType := opts.MediaType
if ref == "-" {
return getManifestDescriptorFromReader(os.Stdin, mediaType)
}
return getManifestDescriptorFromFile(ref, mediaType)
}

return getManifestDescriptorFromReference(ctx, &opts.SecureFlagOpts, ref)
return getManifestDescriptorFromReference(ctx, opts, ref)
}

func getManifestDescriptorFromReference(ctx context.Context, opts *SecureFlagOpts, reference string) (notation.Descriptor, error) {
Expand All @@ -42,28 +27,3 @@ func getManifestDescriptorFromReference(ctx context.Context, opts *SecureFlagOpt
}
return repo.Resolve(ctx, ref.ReferenceOrDefault())
}

func getManifestDescriptorFromFile(path, mediaType string) (notation.Descriptor, error) {
file, err := os.Open(path)
if err != nil {
return notation.Descriptor{}, err
}
defer file.Close()
return getManifestDescriptorFromReader(file, mediaType)
}

func getManifestDescriptorFromReader(r io.Reader, mediaType string) (notation.Descriptor, error) {
lr := &io.LimitedReader{
R: r,
N: math.MaxInt64,
}
digest, err := digest.SHA256.FromReader(lr)
if err != nil {
return notation.Descriptor{}, err
}
return notation.Descriptor{
MediaType: mediaType,
Digest: digest,
Size: math.MaxInt64 - lr.N,
}, nil
}
41 changes: 10 additions & 31 deletions cmd/notation/sign.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,45 +7,39 @@ import (
"time"

"github.com/notaryproject/notation-go"
"github.com/notaryproject/notation-go/crypto/timestamp"
"github.com/notaryproject/notation/internal/cmd"
"github.com/notaryproject/notation/internal/envelope"
"github.com/spf13/cobra"
)

type signOpts struct {
cmd.SignerFlagOpts
RemoteFlagOpts
timestamp string
expiry time.Duration
originReference string
pluginConfig []string
reference string
SecureFlagOpts
expiry time.Duration
pluginConfig []string
reference string
}

func signCommand(opts *signOpts) *cobra.Command {
if opts == nil {
opts = &signOpts{}
}
command := &cobra.Command{
Use: "sign [reference]",
Short: "Sign OCI artifacts",
Long: `Sign OCI artifacts
Use: "sign [flags] <reference>",
Short: "Sign artifacts",
Long: `Sign artifacts
Prerequisite: a signing key needs to be configured using the command "notation key".
Example - Sign a container image using the default signing key, with the default JWS envelope:
notation sign <registry>/<repository>:<tag>
Example - Sign a container image using the default signing key, with the COSE envelope:
notation sign --envelope-type cose <registry>/<repository>:<tag>
notation sign --signature-format cose <registry>/<repository>:<tag>
Example - Sign a container image using the specified key name
notation sign --key <key_name> <registry>/<repository>:<tag>
Example - Sign a container image using a local testing key and certificate file directly
notation sign --key-file <key_path> --cert-file <cert_path> <registry>/<repository>:<tag>
Example - Sign a container image using the image digest
notation sign <registry>/<repository>@<digest>
`,
Expand All @@ -61,11 +55,8 @@ Example - Sign a container image using the image digest
},
}
opts.SignerFlagOpts.ApplyFlags(command.Flags())
opts.RemoteFlagOpts.ApplyFlags(command.Flags())

cmd.SetPflagTimestamp(command.Flags(), &opts.timestamp)
opts.SecureFlagOpts.ApplyFlags(command.Flags())
cmd.SetPflagExpiry(command.Flags(), &opts.expiry)
cmd.SetPflagReference(command.Flags(), &opts.originReference)
cmd.SetPflagPluginConfig(command.Flags(), &opts.pluginConfig)

return command
Expand Down Expand Up @@ -103,28 +94,16 @@ func runSign(command *cobra.Command, cmdOpts *signOpts) error {
}

func prepareSigningContent(ctx context.Context, opts *signOpts) (notation.Descriptor, notation.SignOptions, error) {
manifestDesc, err := getManifestDescriptorFromContext(ctx, &opts.RemoteFlagOpts, opts.reference)
manifestDesc, err := getManifestDescriptorFromContext(ctx, &opts.SecureFlagOpts, opts.reference)
if err != nil {
return notation.Descriptor{}, notation.SignOptions{}, err
}
if identity := opts.originReference; identity != "" {
manifestDesc.Annotations = map[string]string{
"identity": identity,
}
}
var tsa timestamp.Timestamper
if endpoint := opts.timestamp; endpoint != "" {
if tsa, err = timestamp.NewHTTPTimestamper(nil, endpoint); err != nil {
return notation.Descriptor{}, notation.SignOptions{}, err
}
}
pluginConfig, err := cmd.ParseFlagPluginConfig(opts.pluginConfig)
if err != nil {
return notation.Descriptor{}, notation.SignOptions{}, err
}
return manifestDesc, notation.SignOptions{
Expiry: cmd.GetExpiry(opts.expiry),
TSA: tsa,
PluginConfig: pluginConfig,
}, nil
}
Expand Down
67 changes: 14 additions & 53 deletions cmd/notation/sign_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,31 +15,20 @@ func TestSignCommand_BasicArgs(t *testing.T) {
command := signCommand(opts)
expected := &signOpts{
reference: "ref",
RemoteFlagOpts: RemoteFlagOpts{
SecureFlagOpts: SecureFlagOpts{
Username: "user",
Password: "password",
},
CommonFlagOpts: CommonFlagOpts{
MediaType: defaultMediaType,
},
SecureFlagOpts: SecureFlagOpts{
Username: "user",
Password: "password",
},
SignerFlagOpts: cmd.SignerFlagOpts{
Key: "key",
KeyFile: "keyfile",
CertFile: "certfile",
EnvelopeType: envelope.JWS,
},
pluginConfig: []string{"key0=val0"},
}
if err := command.ParseFlags([]string{
expected.reference,
"-u", expected.Username,
"--password", expected.Password,
"--key", expected.Key,
"--key-file", expected.KeyFile,
"--cert-file", expected.CertFile,
"--plugin-config", "key0=val0"}); err != nil {
"--key", expected.Key}); err != nil {
t.Fatalf("Parse Flag failed: %v", err)
}
if err := command.Args(command, command.Flags().Args()); err != nil {
Expand All @@ -55,39 +44,25 @@ func TestSignCommand_MoreArgs(t *testing.T) {
command := signCommand(opts)
expected := &signOpts{
reference: "ref",
RemoteFlagOpts: RemoteFlagOpts{
SecureFlagOpts: SecureFlagOpts{
Username: "user",
Password: "password",
PlainHTTP: true,
},
CommonFlagOpts: CommonFlagOpts{
MediaType: "mediaT",
Local: true,
},
SecureFlagOpts: SecureFlagOpts{
Username: "user",
Password: "password",
PlainHTTP: true,
},
SignerFlagOpts: cmd.SignerFlagOpts{
Key: "key",
KeyFile: "keyfile",
CertFile: "certfile",
EnvelopeType: envelope.COSE,
},
expiry: 24 * time.Hour,
pluginConfig: []string{"key0=val0"},
expiry: 24 * time.Hour,
}
if err := command.ParseFlags([]string{
expected.reference,
"-u", expected.Username,
"-p", expected.Password,
"--key", expected.Key,
"--key-file", expected.KeyFile,
"--cert-file", expected.CertFile,
"--plain-http",
"--media-type", expected.MediaType,
"-l",
"--envelope-type", expected.SignerFlagOpts.EnvelopeType,
"--expiry", expected.expiry.String(),
"--plugin-config", "key0=val0"}); err != nil {
"--signature-format", expected.SignerFlagOpts.EnvelopeType,
"--expiry", expected.expiry.String()}); err != nil {
t.Fatalf("Parse Flag failed: %v", err)
}
if err := command.Args(command, command.Flags().Args()); err != nil {
Expand All @@ -103,31 +78,17 @@ func TestSignCommand_CorrectConfig(t *testing.T) {
command := signCommand(opts)
expected := &signOpts{
reference: "ref",
RemoteFlagOpts: RemoteFlagOpts{
CommonFlagOpts: CommonFlagOpts{
MediaType: "mediaT",
Local: true,
},
},
SignerFlagOpts: cmd.SignerFlagOpts{
Key: "key",
KeyFile: "keyfile",
CertFile: "certfile",
EnvelopeType: envelope.JWS,
},
expiry: 365 * 24 * time.Hour,
pluginConfig: []string{"key0=val0", "key1=val1"},
originReference: "originref",
expiry: 365 * 24 * time.Hour,
pluginConfig: []string{"key0=val0", "key1=val1"},
}
if err := command.ParseFlags([]string{
expected.reference,
"--key", expected.Key,
"--key-file", expected.KeyFile,
"--cert-file", expected.CertFile,
"--media-type", expected.MediaType,
"-r", expected.originReference,
"--local",
"--envelope-type", expected.SignerFlagOpts.EnvelopeType,
"--signature-format", expected.SignerFlagOpts.EnvelopeType,
"--expiry", expected.expiry.String(),
"--plugin-config", "key0=val0",
"--plugin-config", "key1=val1"}); err != nil {
Expand Down
6 changes: 1 addition & 5 deletions cmd/notation/verify.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,11 +27,7 @@ func verifyCommand(opts *verifyOpts) *cobra.Command {
command := &cobra.Command{
Use: "verify [flags] <reference>",
Short: "Verify Artifacts",
Long: `Verify signatures associated with the artifact.
Prerequisite: a trusted certificate needs to be generated or added using the command "notation cert".
notation verify [--plugin-config <key>=<value>...] [--username <username>] [--password <password>] <reference>`,
Long: "Verify signatures associated with the artifact.",
Args: func(cmd *cobra.Command, args []string) error {
if len(args) == 0 {
return errors.New("missing reference")
Expand Down
4 changes: 2 additions & 2 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@ go 1.19
require (
github.com/distribution/distribution/v3 v3.0.0-20220729163034-26163d82560f
github.com/docker/docker-credential-helpers v0.7.0
github.com/notaryproject/notation-core-go v0.1.0-alpha.4
github.com/notaryproject/notation-go v0.11.0-alpha.4
github.com/notaryproject/notation-core-go v0.1.0-alpha.4.0.20221017041709-56bd40a80d26
github.com/notaryproject/notation-go v0.11.0-alpha.4.0.20221025011337-7ad4eca1a568
github.com/opencontainers/go-digest v1.0.0
github.com/spf13/cobra v1.6.0
github.com/spf13/pflag v1.0.5
Expand Down
4 changes: 4 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,12 @@ github.com/inconshreveable/mousetrap v1.0.1 h1:U3uMjPSQEBMNp1lFxmllqCPM6P5u/Xq7P
github.com/inconshreveable/mousetrap v1.0.1/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
github.com/notaryproject/notation-core-go v0.1.0-alpha.4 h1:0OhA2PjwT0TAouHOrU4K+8H9YM6E/e4/ocoq+JiHeOw=
github.com/notaryproject/notation-core-go v0.1.0-alpha.4/go.mod h1:s8DZptmN1rZS0tBLTPt/w+d4o6eAcGWTYYJlXaJhQ4U=
github.com/notaryproject/notation-core-go v0.1.0-alpha.4.0.20221017041709-56bd40a80d26 h1:tAM+m4MocXBXdRBr8GDWABWHw3XiO3PE9+L38aEECLc=
github.com/notaryproject/notation-core-go v0.1.0-alpha.4.0.20221017041709-56bd40a80d26/go.mod h1:s8DZptmN1rZS0tBLTPt/w+d4o6eAcGWTYYJlXaJhQ4U=
github.com/notaryproject/notation-go v0.11.0-alpha.4 h1:PNptLtrhW0jyw10hUWU+KNzvzeuBBZmg+/1IUaGYE10=
github.com/notaryproject/notation-go v0.11.0-alpha.4/go.mod h1:4xYTcW4wfsXkXw3piUA53uSW82RwdXyipSEtiiRVrCw=
github.com/notaryproject/notation-go v0.11.0-alpha.4.0.20221025011337-7ad4eca1a568 h1:omjj1Ssrf85KFLyFP4pC50G9xqb7jUhy6RvBjJZ0tgY=
github.com/notaryproject/notation-go v0.11.0-alpha.4.0.20221025011337-7ad4eca1a568/go.mod h1:pTGrgvkitZClSoQY31P4LgBExtRRXg1AD/9tkFmxaS0=
github.com/opencontainers/distribution-spec/specs-go v0.0.0-20220620172159-4ab4752c3b86 h1:Oumw+lPnO8qNLTY2mrqPJZMoGExLi/0h/DdikoLTXVU=
github.com/opencontainers/distribution-spec/specs-go v0.0.0-20220620172159-4ab4752c3b86/go.mod h1:aA4vdXRS8E1TG7pLZOz85InHi3BiPdErh8IpJN6E0x4=
github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U=
Expand Down
Loading

0 comments on commit a08dc9e

Please sign in to comment.