From ef9683a40938c332e9667dad4e9270b883af1269 Mon Sep 17 00:00:00 2001 From: Scott Nichols Date: Thu, 23 Sep 2021 14:05:53 -0700 Subject: [PATCH] Migrate cosign public-key to cobra. (#771) Signed-off-by: Scott Nichols --- cmd/cosign/cli/commands.go | 5 +- cmd/cosign/cli/options/fulcio.go | 1 + cmd/cosign/cli/options/oidc.go | 1 + cmd/cosign/cli/options/public_key.go | 38 ++++++++++++ cmd/cosign/cli/options/rekor.go | 1 + .../{securitykey.go => security_key.go} | 1 + cmd/cosign/cli/options/sign.go | 1 + cmd/cosign/cli/options/signblob.go | 1 + cmd/cosign/cli/public_key.go | 58 ++++++++++++++++--- cmd/cosign/cli/sign.go | 32 +++++----- cmd/cosign/cli/signblob.go | 36 ++++++------ cmd/cosign/main.go | 1 + 12 files changed, 132 insertions(+), 44 deletions(-) create mode 100644 cmd/cosign/cli/options/public_key.go rename cmd/cosign/cli/options/{securitykey.go => security_key.go} (94%) diff --git a/cmd/cosign/cli/commands.go b/cmd/cosign/cli/commands.go index cc9f7fe7da6..cffe67715e9 100644 --- a/cmd/cosign/cli/commands.go +++ b/cmd/cosign/cli/commands.go @@ -53,11 +53,11 @@ func New() *cobra.Command { PublicKey(), generate.GenerateKeyPair(), // Signing - verify.Verify(), sign.Sign(), + sign.SignBlob(), Attest(), generate.Generate(), - sign.SignBlob(), + verify.Verify(), verify.VerifyAttestation(), verify.VerifyBlob(), // Manifest sub-tree @@ -116,6 +116,7 @@ func New() *cobra.Command { options.AddRootOptions(cmd, ro) // Add sub-commands. + addPublicKey(cmd) addSign(cmd) addSignBlob(cmd) diff --git a/cmd/cosign/cli/options/fulcio.go b/cmd/cosign/cli/options/fulcio.go index 4b909cc3c2b..c0b6cc69189 100644 --- a/cmd/cosign/cli/options/fulcio.go +++ b/cmd/cosign/cli/options/fulcio.go @@ -26,6 +26,7 @@ type FulcioOptions struct { IdentityToken string } +// AddFulcioOptions adds the Fulcio related options to cmd. func AddFulcioOptions(cmd *cobra.Command, o *FulcioOptions) { cmd.Flags().StringVar(&o.URL, "fulcio-url", fulcioclient.SigstorePublicServerURL, "[EXPERIMENTAL] address of sigstore PKI server") diff --git a/cmd/cosign/cli/options/oidc.go b/cmd/cosign/cli/options/oidc.go index 2832399e4a5..0f1b4f80324 100644 --- a/cmd/cosign/cli/options/oidc.go +++ b/cmd/cosign/cli/options/oidc.go @@ -26,6 +26,7 @@ type OIDCOptions struct { ClientSecret string } +// AddOIDCOptions adds the OIDC related options to cmd. func AddOIDCOptions(cmd *cobra.Command, o *OIDCOptions) { cmd.Flags().StringVar(&o.Issuer, "oidc-issuer", "https://oauth2.sigstore.dev/auth", "[EXPERIMENTAL] OIDC provider to be used to issue ID token") diff --git a/cmd/cosign/cli/options/public_key.go b/cmd/cosign/cli/options/public_key.go new file mode 100644 index 00000000000..fdce27ca4db --- /dev/null +++ b/cmd/cosign/cli/options/public_key.go @@ -0,0 +1,38 @@ +// +// Copyright 2021 The Sigstore Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package options + +import ( + "github.com/spf13/cobra" +) + +// PublicKeyOptions is the top level wrapper for the public-key command. +type PublicKeyOptions struct { + Key string + SecurityKey SecurityKeyOptions + OutFile string +} + +// AddPublicKeyOptions adds the public-key command options to cmd. +func AddPublicKeyOptions(cmd *cobra.Command, o *PublicKeyOptions) { + cmd.Flags().StringVar(&o.Key, "key", "", + "path to the private key file, KMS URI or Kubernetes Secret") + + AddSecurityKeyOptions(cmd, &o.SecurityKey) + + cmd.Flags().StringVar(&o.OutFile, "outfile", "", + "path to a payload file to use rather than generating one") +} diff --git a/cmd/cosign/cli/options/rekor.go b/cmd/cosign/cli/options/rekor.go index 5841544911f..570744038bd 100644 --- a/cmd/cosign/cli/options/rekor.go +++ b/cmd/cosign/cli/options/rekor.go @@ -24,6 +24,7 @@ type RekorOptions struct { URL string } +// AddRekorOptions adds the Rektor related options to cmd. func AddRekorOptions(cmd *cobra.Command, o *RekorOptions) { cmd.Flags().StringVar(&o.URL, "rekor-url", "https://rekor.sigstore.dev", "[EXPERIMENTAL] address of rekor STL server") diff --git a/cmd/cosign/cli/options/securitykey.go b/cmd/cosign/cli/options/security_key.go similarity index 94% rename from cmd/cosign/cli/options/securitykey.go rename to cmd/cosign/cli/options/security_key.go index 33e20f68aaf..8130b8024e3 100644 --- a/cmd/cosign/cli/options/securitykey.go +++ b/cmd/cosign/cli/options/security_key.go @@ -25,6 +25,7 @@ type SecurityKeyOptions struct { Slot string } +// AddSecurityKeyOptions adds the security key related options to cmd. func AddSecurityKeyOptions(cmd *cobra.Command, o *SecurityKeyOptions) { cmd.Flags().BoolVar(&o.Use, "sk", false, "whether to use a hardware security key") diff --git a/cmd/cosign/cli/options/sign.go b/cmd/cosign/cli/options/sign.go index 6eef071f928..c600a21fadf 100644 --- a/cmd/cosign/cli/options/sign.go +++ b/cmd/cosign/cli/options/sign.go @@ -56,6 +56,7 @@ func (s *SignOptions) AnnotationsMap() (sigs.AnnotationsMap, error) { return ann, nil } +// AddSignOptions adds the sign command options to cmd. func AddSignOptions(cmd *cobra.Command, o *SignOptions) { cmd.Flags().StringVar(&o.Key, "key", "", "path to the private key file, KMS URI or Kubernetes Secret") diff --git a/cmd/cosign/cli/options/signblob.go b/cmd/cosign/cli/options/signblob.go index 47d92d4fb09..78c2d9b2fac 100644 --- a/cmd/cosign/cli/options/signblob.go +++ b/cmd/cosign/cli/options/signblob.go @@ -31,6 +31,7 @@ type SignBlobOptions struct { RegistryOpts RegistryOpts } +// AddSignBlobOptions adds the sign-blob command options to cmd. func AddSignBlobOptions(cmd *cobra.Command, o *SignBlobOptions) { cmd.Flags().StringVar(&o.Key, "key", "", "path to the private key file, KMS URI or Kubernetes Secret") diff --git a/cmd/cosign/cli/public_key.go b/cmd/cosign/cli/public_key.go index 25dc80647d0..135c137c4f4 100644 --- a/cmd/cosign/cli/public_key.go +++ b/cmd/cosign/cli/public_key.go @@ -24,6 +24,7 @@ import ( "github.com/peterbourgon/ff/v3/ffcli" "github.com/pkg/errors" + "github.com/spf13/cobra" "github.com/sigstore/cosign/cmd/cosign/cli/generate" "github.com/sigstore/cosign/cmd/cosign/cli/options" @@ -39,6 +40,8 @@ type NamedWriter struct { io.Writer } +// PublicKey subcommand for ffcli. +// Deprecated: this will be deleted when the migration from ffcli to cobra is done. func PublicKey() *ffcli.Command { var ( flagset = flag.NewFlagSet("cosign public-key", flag.ExitOnError) @@ -75,17 +78,53 @@ EXAMPLES cosign public-key -key hashivault://[KEY]`, FlagSet: flagset, Exec: func(ctx context.Context, args []string) error { - if !options.OneOf(*key, *sk) { + _ = key + _ = sk + _ = slot + _ = outFile + panic("this command is now implemented in cobra.") + }, + } +} + +func addPublicKey(topLevel *cobra.Command) { + o := &options.PublicKeyOptions{} + + cmd := &cobra.Command{ + Use: "public-key", + Short: "Gets a public key from the key-pair.", + Long: "Gets a public key from the key-pair and\nwrites to a specified file. By default, it will write to standard out.", + Example: ` + # extract public key from private key to a specified out file. + cosign public-key --key --outfile + + # extract public key from URL. + cosign public-key --key https://host.for/ --outfile + + # extract public key from Azure Key Vault + cosign public-key --key azurekms://[VAULT_NAME][VAULT_URI]/[KEY] + + # extract public key from AWS KMS + cosign public-key --key awskms://[ENDPOINT]/[ID/ALIAS/ARN] + + # extract public key from Google Cloud KMS + cosign public-key --key gcpkms://projects/[PROJECT]/locations/global/keyRings/[KEYRING]/cryptoKeys/[KEY] + + # extract public key from Hashicorp Vault KMS + cosign public-key --key hashivault://[KEY]`, + + RunE: func(cmd *cobra.Command, args []string) error { + if !options.OneOf(o.Key, o.SecurityKey.Use) { return &options.KeyParseError{} } writer := NamedWriter{Name: "", Writer: nil} var f *os.File // Open output file for public key if specified. - if *outFile != "" { - writer.Name = *outFile + if o.OutFile != "" { + writer.Name = o.OutFile var err error - f, err = os.OpenFile(*outFile, os.O_WRONLY|os.O_CREATE, 0600) + f, err = os.OpenFile(o.OutFile, os.O_WRONLY|os.O_CREATE, 0600) if err != nil { return err } @@ -95,13 +134,16 @@ EXAMPLES writer.Writer = os.Stdout } pk := Pkopts{ - KeyRef: *key, - Sk: *sk, - Slot: *slot, + KeyRef: o.Key, + Sk: o.SecurityKey.Use, + Slot: o.SecurityKey.Slot, } - return GetPublicKey(ctx, pk, writer, generate.GetPass) + return GetPublicKey(context.Background(), pk, writer, generate.GetPass) }, } + + options.AddPublicKeyOptions(cmd, o) + topLevel.AddCommand(cmd) } type Pkopts struct { diff --git a/cmd/cosign/cli/sign.go b/cmd/cosign/cli/sign.go index 9d019ce2d49..2ba9a3cdb58 100644 --- a/cmd/cosign/cli/sign.go +++ b/cmd/cosign/cli/sign.go @@ -28,7 +28,7 @@ import ( ) func addSign(topLevel *cobra.Command) { - so := &options.SignOptions{} + o := &options.SignOptions{} cmd := &cobra.Command{ Use: "sign", @@ -69,38 +69,38 @@ func addSign(topLevel *cobra.Command) { if len(args) == 0 { return flag.ErrHelp } - switch so.Attachment { + switch o.Attachment { case "sbom", "": break default: return flag.ErrHelp } ko := sign.KeyOpts{ - KeyRef: so.Key, + KeyRef: o.Key, PassFunc: generate.GetPass, - Sk: so.SecurityKey.Use, - Slot: so.SecurityKey.Slot, - FulcioURL: so.Fulcio.URL, - IDToken: so.Fulcio.IdentityToken, - RekorURL: so.Rektor.URL, - OIDCIssuer: so.OIDC.Issuer, - OIDCClientID: so.OIDC.ClientID, - OIDCClientSecret: so.OIDC.ClientSecret, + Sk: o.SecurityKey.Use, + Slot: o.SecurityKey.Slot, + FulcioURL: o.Fulcio.URL, + IDToken: o.Fulcio.IdentityToken, + RekorURL: o.Rektor.URL, + OIDCIssuer: o.OIDC.Issuer, + OIDCClientID: o.OIDC.ClientID, + OIDCClientSecret: o.OIDC.ClientSecret, } - annotationsMap, err := so.AnnotationsMap() + annotationsMap, err := o.AnnotationsMap() if err != nil { return err } - if err := sign.SignCmd(context.Background(), ko, so.RegistryOpts, annotationsMap.Annotations, args, so.Cert, so.Upload, so.PayloadPath, so.Force, so.Recursive, so.Attachment); err != nil { - if so.Attachment == "" { + if err := sign.SignCmd(context.Background(), ko, o.RegistryOpts, annotationsMap.Annotations, args, o.Cert, o.Upload, o.PayloadPath, o.Force, o.Recursive, o.Attachment); err != nil { + if o.Attachment == "" { return errors.Wrapf(err, "signing %v", args) } - return errors.Wrapf(err, "signing attachement %s for image %v", so.Attachment, args) + return errors.Wrapf(err, "signing attachement %s for image %v", o.Attachment, args) } return nil }, } - options.AddSignOptions(cmd, so) + options.AddSignOptions(cmd, o) topLevel.AddCommand(cmd) } diff --git a/cmd/cosign/cli/signblob.go b/cmd/cosign/cli/signblob.go index c025b273007..8c44d19ff14 100644 --- a/cmd/cosign/cli/signblob.go +++ b/cmd/cosign/cli/signblob.go @@ -28,7 +28,7 @@ import ( ) func addSignBlob(topLevel *cobra.Command) { - so := &options.SignBlobOptions{} + o := &options.SignBlobOptions{} cmd := &cobra.Command{ Use: "sign-blob", @@ -39,24 +39,24 @@ func addSignBlob(topLevel *cobra.Command) { COSIGN_EXPERIMENTAL=1 cosign sign-blob # sign a blob with a local key pair file - cosign sign-blob -key cosign.key + cosign sign-blob --key cosign.key # sign a blob with a key pair stored in Azure Key Vault - cosign sign-blob -key azurekms://[VAULT_NAME][VAULT_URI]/[KEY] + cosign sign-blob --key azurekms://[VAULT_NAME][VAULT_URI]/[KEY] # sign a blob with a key pair stored in AWS KMS - cosign sign-blob -key awskms://[ENDPOINT]/[ID/ALIAS/ARN] + cosign sign-blob --key awskms://[ENDPOINT]/[ID/ALIAS/ARN] # sign a blob with a key pair stored in Google Cloud KMS - cosign sign-blob -key gcpkms://projects/[PROJECT]/locations/global/keyRings/[KEYRING]/cryptoKeys/[KEY] + cosign sign-blob --key gcpkms://projects/[PROJECT]/locations/global/keyRings/[KEYRING]/cryptoKeys/[KEY] # sign a blob with a key pair stored in Hashicorp Vault - cosign sign-blob -key hashivault://[KEY] `, + cosign sign-blob --key hashivault://[KEY] `, RunE: func(cmd *cobra.Command, args []string) error { // A key file is required unless we're in experimental mode! if !options.EnableExperimental() { - if !options.OneOf(so.Key, so.SecurityKey.Use) { + if !options.OneOf(o.Key, o.SecurityKey.Use) { return &options.KeyParseError{} } } @@ -65,19 +65,19 @@ func addSignBlob(topLevel *cobra.Command) { return flag.ErrHelp } ko := sign.KeyOpts{ - KeyRef: so.Key, + KeyRef: o.Key, PassFunc: generate.GetPass, - Sk: so.SecurityKey.Use, - Slot: so.SecurityKey.Slot, - FulcioURL: so.Fulcio.URL, - IDToken: so.Fulcio.IdentityToken, - RekorURL: so.Rektor.URL, - OIDCIssuer: so.OIDC.Issuer, - OIDCClientID: so.OIDC.ClientID, - OIDCClientSecret: so.OIDC.ClientSecret, + Sk: o.SecurityKey.Use, + Slot: o.SecurityKey.Slot, + FulcioURL: o.Fulcio.URL, + IDToken: o.Fulcio.IdentityToken, + RekorURL: o.Rektor.URL, + OIDCIssuer: o.OIDC.Issuer, + OIDCClientID: o.OIDC.ClientID, + OIDCClientSecret: o.OIDC.ClientSecret, } for _, blob := range args { - if _, err := sign.SignBlobCmd(context.Background(), ko, so.RegistryOpts, blob, so.Base64Output, so.Output); err != nil { + if _, err := sign.SignBlobCmd(context.Background(), ko, o.RegistryOpts, blob, o.Base64Output, o.Output); err != nil { return errors.Wrapf(err, "signing %s", blob) } } @@ -85,6 +85,6 @@ func addSignBlob(topLevel *cobra.Command) { }, } - options.AddSignBlobOptions(cmd, so) + options.AddSignBlobOptions(cmd, o) topLevel.AddCommand(cmd) } diff --git a/cmd/cosign/main.go b/cmd/cosign/main.go index bd4c24445b4..8bcebbd2b71 100644 --- a/cmd/cosign/main.go +++ b/cmd/cosign/main.go @@ -49,6 +49,7 @@ func main() { // escape the remaining args to let them be passed to cobra. if len(os.Args) > 1 { switch os.Args[1] { + case "public-key": case "sign", "sign-blob": // cobra. default: