Skip to content

Commit

Permalink
Migrate cosign sign to cobra
Browse files Browse the repository at this point in the history
Signed-off-by: Scott Nichols <[email protected]>
  • Loading branch information
n3wscott committed Sep 23, 2021
1 parent 2474b54 commit 089be4f
Show file tree
Hide file tree
Showing 6 changed files with 245 additions and 34 deletions.
5 changes: 4 additions & 1 deletion cmd/cosign/cli/commands.go
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,10 @@ func New() *cobra.Command {
return nil // TODO: use cobra to output help.
},
}
options.AddRootArgs(cmd, ro)
options.AddRootOptions(cmd, ro)

// Add sub-commands.
addSign(cmd)

return cmd
}
Expand Down
2 changes: 1 addition & 1 deletion cmd/cosign/cli/options/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ type RootOptions struct {
Verbose bool
}

func AddRootArgs(cmd *cobra.Command, o *RootOptions) {
func AddRootOptions(cmd *cobra.Command, o *RootOptions) {
cmd.PersistentFlags().StringVar(&o.OutputFile, "output-file", "",
"log output to a file")

Expand Down
117 changes: 117 additions & 0 deletions cmd/cosign/cli/options/sign.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
//
// 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 (
"fmt"
"strings"

"github.com/spf13/cobra"

sigs "github.com/sigstore/cosign/pkg/signature"
fulcioclient "github.com/sigstore/fulcio/pkg/client"
)

// SignOptions is the top level wrapper for the sign command.
type SignOptions struct {
Key string
Cert string
Upload bool
SecurityKey bool
SecurityKeySlot string
PayloadPath string
Force bool
Recursive bool
FulcioURL string
RektorURL string
IdentityToken string
OIDCIssuer string
OIDCClientID string
OIDCClientSecret string
Attachment string

Annotations []string
RegistryOpts RegistryOpts
}

func (s *SignOptions) AnnotationsMap() (sigs.AnnotationsMap, error) {
ann := sigs.AnnotationsMap{}
for _, a := range s.Annotations {
kv := strings.Split(a, "=")
if len(kv) != 2 {
return ann, fmt.Errorf("unable to parse annotation: %s", a)
}
ann.Annotations[kv[0]] = kv[1]
}
return ann, nil
}

func AddSignOptions(cmd *cobra.Command, o *SignOptions) {
cmd.Flags().StringVar(&o.Key, "key", "",
"path to the private key file, KMS URI or Kubernetes Secret")

cmd.Flags().StringVar(&o.Cert, "cert", "",
"path to the x509 certificate to include in the Signature")

cmd.Flags().BoolVar(&o.Upload, "upload", true,
"whether to upload the signature")

cmd.Flags().BoolVar(&o.SecurityKey, "sk", false,
"whether to use a hardware security key")

cmd.Flags().StringVar(&o.SecurityKeySlot, "slot", "",
"security key slot to use for generated key (default: signature) (authentication|signature|card-authentication|key-management)")

cmd.Flags().StringVar(&o.PayloadPath, "payload", "",
"path to a payload file to use rather than generating one")

cmd.Flags().BoolVarP(&o.Force, "force", "f", false,
"skip warnings and confirmations")

cmd.Flags().BoolVarP(&o.Recursive, "recursive", "r", false,
"if a multi-arch image is specified, additionally sign each discrete image")

cmd.Flags().StringVar(&o.OIDCIssuer, "attachment", "",
"related image attachment to sign (sbom), default none")

cmd.Flags().StringSliceVarP(&o.Annotations, "annotations", "a", nil,
"extra key=value pairs to sign")

cmd.Flags().BoolVar(&o.RegistryOpts.AllowInsecure, "allow-insecure-registry", false,
"whether to allow insecure connections to registries. Don't use this for anything but testing")

// TODO: an interesting idea? This hides the flags that are experimental
// unless experimental is enabled.
if EnableExperimental() {
cmd.Flags().StringVar(&o.FulcioURL, "fulcio-url", fulcioclient.SigstorePublicServerURL,
"[EXPERIMENTAL] address of sigstore PKI server")

cmd.Flags().StringVar(&o.RektorURL, "rekor-url", "https://rekor.sigstore.dev",
"[EXPERIMENTAL] address of rekor STL server")

cmd.Flags().StringVar(&o.IdentityToken, "identity-token", "",
"[EXPERIMENTAL] identity token to use for certificate from fulcio")

cmd.Flags().StringVar(&o.OIDCIssuer, "oidc-issuer", "https://oauth2.sigstore.dev/auth",
"[EXPERIMENTAL] OIDC provider to be used to issue ID token")

cmd.Flags().StringVar(&o.OIDCClientID, "oidc-client-id", "sigstore",
"[EXPERIMENTAL] OIDC client ID for application")

cmd.Flags().StringVar(&o.OIDCClientSecret, "oidc-client-secret", "",
"[EXPERIMENTAL] OIDC client secret for application")
}
}
91 changes: 91 additions & 0 deletions cmd/cosign/cli/sign.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
package cli

import (
"context"
"flag"

"github.com/pkg/errors"
"github.com/spf13/cobra"

"github.com/sigstore/cosign/cmd/cosign/cli/generate"
"github.com/sigstore/cosign/cmd/cosign/cli/options"
"github.com/sigstore/cosign/cmd/cosign/cli/sign"
)

func addSign(topLevel *cobra.Command) {
so := &options.SignOptions{}

cmd := &cobra.Command{
Use: "sign",
Short: "Sign the supplied container image.\ncosign sign --key <key path>|<kms uri> [--payload <path>] [-a key=value] [--upload=true|false] [-f] [-r] <image uri>",
Long: "Sign the supplied container image.",
Example: `
# sign a container image with Google sign-in (experimental)
COSIGN_EXPERIMENTAL=1 cosign sign <IMAGE>
# sign a container image with a local key pair file
cosign sign --key cosign.key <IMAGE>
# sign a multi-arch container image AND all referenced, discrete images
cosign sign --key cosign.key --r <MULTI-ARCH IMAGE>
# sign a container image and add annotations
cosign sign --key cosign.key -a key1=value1 -a key2=value2 <IMAGE>
# sign a container image with a key pair stored in Azure Key Vault
cosign sign --key azurekms://[VAULT_NAME][VAULT_URI]/[KEY] <IMAGE>
# sign a container image with a key pair stored in AWS KMS
cosign sign --key awskms://[ENDPOINT]/[ID/ALIAS/ARN] <IMAGE>
# sign a container image with a key pair stored in Google Cloud KMS
cosign sign --key gcpkms://projects/[PROJECT]/locations/global/keyRings/[KEYRING]/cryptoKeys/[KEY]/versions/[VERSION] <IMAGE>
# sign a container image with a key pair stored in Hashicorp Vault
cosign sign --key hashivault://[KEY] <IMAGE>
# sign a container image with a key pair stored in a Kubernetes secret
cosign sign --key k8s://[NAMESPACE]/[KEY] <IMAGE>
# sign a container in a registry which does not fully support OCI media types
COSIGN_DOCKER_MEDIA_TYPES=1 cosign sign --key cosign.key legacy-registry.example.com/my/image
`,
RunE: func(cmd *cobra.Command, args []string) error {
if len(args) == 0 {
return flag.ErrHelp
}
switch so.Attachment {
case "sbom", "":
break
default:
return flag.ErrHelp
}
ko := sign.KeyOpts{
KeyRef: so.Key,
PassFunc: generate.GetPass,
Sk: so.SecurityKey,
Slot: so.SecurityKeySlot,
FulcioURL: so.FulcioURL,
RekorURL: so.RektorURL,
IDToken: so.IdentityToken,
OIDCIssuer: so.OIDCIssuer,
OIDCClientID: so.OIDCClientID,
OIDCClientSecret: so.OIDCClientSecret,
}
annotationsMap, err := so.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 == "" {
return errors.Wrapf(err, "signing %v", args)
}
return errors.Wrapf(err, "signing attachement %s for image %v", so.Attachment, args)
}
return nil
},
}

options.AddSignOptions(cmd, so)
topLevel.AddCommand(cmd)
}
54 changes: 23 additions & 31 deletions cmd/cosign/cli/sign/sign.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,6 @@ import (
"github.com/pkg/errors"

"github.com/sigstore/cosign/cmd/cosign/cli/fulcio/fulcioverifier"
"github.com/sigstore/cosign/cmd/cosign/cli/generate"
"github.com/sigstore/cosign/cmd/cosign/cli/options"
"github.com/sigstore/cosign/internal/oci"
ociremote "github.com/sigstore/cosign/internal/oci/remote"
Expand All @@ -48,7 +47,7 @@ import (
providers "github.com/sigstore/cosign/pkg/providers/all"
sigs "github.com/sigstore/cosign/pkg/signature"
fulcioClient "github.com/sigstore/fulcio/pkg/client"
rekorClient "github.com/sigstore/rekor/pkg/client"
rekorclient "github.com/sigstore/rekor/pkg/client"
"github.com/sigstore/rekor/pkg/generated/client"
"github.com/sigstore/rekor/pkg/generated/models"
"github.com/sigstore/sigstore/pkg/cryptoutils"
Expand Down Expand Up @@ -97,7 +96,7 @@ func UploadToTlog(ctx context.Context, sv *CertSignVerifier, rekorURL string, up
}
rekorBytes = pemBytes
}
rekorClient, err := rekorClient.GetRekorClient(rekorURL)
rekorClient, err := rekorclient.GetRekorClient(rekorURL)
if err != nil {
return nil, err
}
Expand All @@ -109,6 +108,8 @@ func UploadToTlog(ctx context.Context, sv *CertSignVerifier, rekorURL string, up
return Bundle(entry), nil
}

// Sign subcommand for ffcli.
// Deprecated, this will be deleted when the migration from ffcli to cobra is done.
func Sign() *ffcli.Command {
var (
flagset = flag.NewFlagSet("cosign sign", flag.ExitOnError)
Expand Down Expand Up @@ -171,34 +172,25 @@ EXAMPLES
`,
FlagSet: flagset,
Exec: func(ctx context.Context, args []string) error {
if len(args) == 0 {
return flag.ErrHelp
}
switch *attachment {
case "sbom", "":
break
default:
return flag.ErrHelp
}
ko := KeyOpts{
KeyRef: *key,
PassFunc: generate.GetPass,
Sk: *sk,
Slot: *slot,
FulcioURL: *fulcioURL,
RekorURL: *rekorURL,
IDToken: *idToken,
OIDCIssuer: *oidcIssuer,
OIDCClientID: *oidcClientID,
OIDCClientSecret: *oidcClientSecret,
}
if err := SignCmd(ctx, ko, regOpts, annotations.Annotations, args, *cert, *upload, *payloadPath, *force, *recursive, *attachment); err != nil {
if *attachment == "" {
return errors.Wrapf(err, "signing %v", args)
}
return errors.Wrapf(err, "signing attachement %s for image %v", *attachment, args)
}
return nil
_ = flagset
_ = key
_ = cert
_ = upload
_ = sk
_ = slot
_ = payloadPath
_ = force
_ = recursive
_ = fulcioURL
_ = rekorURL
_ = idToken
_ = oidcIssuer
_ = oidcClientID
_ = oidcClientSecret
_ = attachment
_ = annotations
_ = regOpts
panic("this command is now implemented in cobra.")
},
}
}
Expand Down
10 changes: 9 additions & 1 deletion cmd/cosign/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,15 @@ func main() {

// Extra migration hacks, while we still use ffcli, we will add a -- to
// escape the remaining args to let them be passed to cobra.
os.Args = append([]string{os.Args[0], "--"}, os.Args[1:]...)
if len(os.Args) > 1 {
switch os.Args[1] {
case "sign":
// cobra.
default:
// ffcli
os.Args = append([]string{os.Args[0], "--"}, os.Args[1:]...)
}
}

if err := cli.New().Execute(); err != nil {
log.Fatalf("error during command execution: %v", err)
Expand Down

0 comments on commit 089be4f

Please sign in to comment.