diff --git a/cmd/cosign/cli/attach/sbom.go b/cmd/cosign/cli/attach/sbom.go index 1c3b257660e..2d7d68d9ce9 100644 --- a/cmd/cosign/cli/attach/sbom.go +++ b/cmd/cosign/cli/attach/sbom.go @@ -44,7 +44,9 @@ func SBOM() *ffcli.Command { flagset = flag.NewFlagSet("cosign attach sbom", flag.ExitOnError) sbom = flagset.String("sbom", "", "path to the sbom, or {-} for stdin") sbomType = flagset.String("type", "spdx", "type of sbom (spdx|cyclonedx), default spdx") + regOpts cli.RegistryOpts ) + cli.ApplyRegistryFlags(®Opts, flagset) return &ffcli.Command{ Name: "sbom", ShortUsage: "cosign attach sbom ", @@ -60,12 +62,12 @@ func SBOM() *ffcli.Command { return flag.ErrHelp } - return SBOMCmd(ctx, *sbom, mt, args[0]) + return SBOMCmd(ctx, regOpts, *sbom, mt, args[0]) }, } } -func SBOMCmd(ctx context.Context, sbomRef, sbomType, imageRef string) error { +func SBOMCmd(ctx context.Context, regOpts cli.RegistryOpts, sbomRef, sbomType, imageRef string) error { ref, err := name.ParseReference(imageRef) if err != nil { return err @@ -76,13 +78,15 @@ func SBOMCmd(ctx context.Context, sbomRef, sbomType, imageRef string) error { return err } - dstRef, err := cli.AttachedImageTag(ctx, ref, cosign.SBOMTagSuffix) + remoteOpts := regOpts.GetRegistryClientOpts(ctx) + + dstRef, err := cli.AttachedImageTag(ref, cosign.SBOMTagSuffix, remoteOpts...) if err != nil { return err } fmt.Fprintf(os.Stderr, "Uploading SBOM file for [%s] to [%s] with mediaType [%s].\n", ref.Name(), dstRef.Name(), sbomType) - if _, err := cremote.UploadFile(b, dstRef, types.MediaType(sbomType), types.OCIConfigJSON, cli.DefaultRegistryClientOpts(ctx)...); err != nil { + if _, err := cremote.UploadFile(b, dstRef, types.MediaType(sbomType), types.OCIConfigJSON, remoteOpts...); err != nil { return err } diff --git a/cmd/cosign/cli/attach/sig.go b/cmd/cosign/cli/attach/sig.go index e1eb0f62c5d..83257250164 100644 --- a/cmd/cosign/cli/attach/sig.go +++ b/cmd/cosign/cli/attach/sig.go @@ -38,7 +38,9 @@ func Signature() *ffcli.Command { flagset = flag.NewFlagSet("cosign attach signature", flag.ExitOnError) signature = flagset.String("signature", "", "the signature, path to the signature, or {-} for stdin") payload = flagset.String("payload", "", "path to the payload covered by the signature (if using another format)") + regOpts cli.RegistryOpts ) + cli.ApplyRegistryFlags(®Opts, flagset) return &ffcli.Command{ Name: "signature", ShortUsage: "cosign attach signature ", @@ -49,12 +51,12 @@ func Signature() *ffcli.Command { return flag.ErrHelp } - return SignatureCmd(ctx, *signature, *payload, args[0]) + return SignatureCmd(ctx, regOpts, *signature, *payload, args[0]) }, } } -func SignatureCmd(ctx context.Context, sigRef, payloadRef, imageRef string) error { +func SignatureCmd(ctx context.Context, regOpts cli.RegistryOpts, sigRef, payloadRef, imageRef string) error { b64SigBytes, err := signatureBytes(sigRef) if err != nil { return err @@ -67,12 +69,14 @@ func SignatureCmd(ctx context.Context, sigRef, payloadRef, imageRef string) erro return err } - h, err := cli.Digest(ctx, ref) + remoteOpts := regOpts.GetRegistryClientOpts(ctx) + + h, err := cli.Digest(ref, remoteOpts...) if err != nil { return err } - dstRef, err := cli.AttachedImageTag(ctx, ref, cosign.SignatureTagSuffix) + dstRef, err := cli.AttachedImageTag(ref, cosign.SignatureTagSuffix, remoteOpts...) if err != nil { return err } @@ -93,8 +97,7 @@ func SignatureCmd(ctx context.Context, sigRef, payloadRef, imageRef string) erro if err != nil { return err } - regClientOpts := cli.DefaultRegistryClientOpts(ctx) - if _, err := cremote.UploadSignature(sigBytes, payload, dstRef, cremote.UploadOpts{RemoteOpts: regClientOpts}); err != nil { + if _, err := cremote.UploadSignature(sigBytes, payload, dstRef, cremote.UploadOpts{RemoteOpts: remoteOpts}); err != nil { return err } return nil diff --git a/cmd/cosign/cli/attest.go b/cmd/cosign/cli/attest.go index 024385fd4c1..7c155f54ca5 100644 --- a/cmd/cosign/cli/attest.go +++ b/cmd/cosign/cli/attest.go @@ -51,7 +51,9 @@ func Attest() *ffcli.Command { force = flagset.Bool("f", false, "skip warnings and confirmations") idToken = flagset.String("identity-token", "", "[EXPERIMENTAL] identity token to use for certificate from fulcio") predicateType = flagset.String("type", "custom", "specify predicate type (default: custom) (slsaprovenance|link|spdx)") + regOpts RegistryOpts ) + ApplyRegistryFlags(®Opts, flagset) return &ffcli.Command{ Name: "attest", ShortUsage: "cosign attest -key | [-predicate ] [-a key=value] [-upload=true|false] [-f] [-r] ", @@ -94,7 +96,7 @@ EXAMPLES IDToken: *idToken, } for _, img := range args { - if err := AttestCmd(ctx, ko, img, *cert, *upload, *predicatePath, *force, *predicateType); err != nil { + if err := AttestCmd(ctx, ko, regOpts, img, *cert, *upload, *predicatePath, *force, *predicateType); err != nil { return errors.Wrapf(err, "signing %s", img) } } @@ -117,7 +119,7 @@ var predicateTypeMap = map[string]string{ predicateLink: in_toto.PredicateLinkV1, } -func AttestCmd(ctx context.Context, ko KeyOpts, imageRef string, certPath string, +func AttestCmd(ctx context.Context, ko KeyOpts, regOpts RegistryOpts, imageRef string, certPath string, upload bool, predicatePath string, force bool, predicateType string) error { // A key file or token is required unless we're in experimental mode! if EnableExperimental() { @@ -135,13 +137,13 @@ func AttestCmd(ctx context.Context, ko KeyOpts, imageRef string, certPath string return fmt.Errorf("invalid predicate type: %s", predicateType) } - remoteOpts := DefaultRegistryClientOpts(ctx) + remoteOpts := regOpts.GetRegistryClientOpts(ctx) ref, err := name.ParseReference(imageRef) if err != nil { return errors.Wrap(err, "parsing reference") } - h, err := Digest(ctx, ref) + h, err := Digest(ref, remoteOpts...) if err != nil { return err } @@ -178,7 +180,7 @@ func AttestCmd(ctx context.Context, ko KeyOpts, imageRef string, certPath string return nil } - attRef, err := AttachedImageTag(ctx, ref, cosign.AttestationTagSuffix) + attRef, err := AttachedImageTag(ref, cosign.AttestationTagSuffix, remoteOpts...) if err != nil { return err } diff --git a/cmd/cosign/cli/clean.go b/cmd/cosign/cli/clean.go index 41f4f04e133..e9cae2103bf 100644 --- a/cmd/cosign/cli/clean.go +++ b/cmd/cosign/cli/clean.go @@ -31,8 +31,9 @@ import ( func Clean() *ffcli.Command { var ( flagset = flag.NewFlagSet("cosign clean", flag.ExitOnError) + regOpts RegistryOpts ) - + ApplyRegistryFlags(®Opts, flagset) return &ffcli.Command{ Name: "clean", ShortUsage: "cosign clean ", @@ -43,18 +44,19 @@ func Clean() *ffcli.Command { return flag.ErrHelp } - return CleanCmd(ctx, args[0]) + return CleanCmd(ctx, regOpts, args[0]) }, } } -func CleanCmd(ctx context.Context, imageRef string) error { +func CleanCmd(ctx context.Context, regOpts RegistryOpts, imageRef string) error { ref, err := name.ParseReference(imageRef) if err != nil { return err } - sigRef, err := AttachedImageTag(ctx, ref, cosign.SignatureTagSuffix) + remoteOpts := regOpts.GetRegistryClientOpts(ctx) + sigRef, err := AttachedImageTag(ref, cosign.SignatureTagSuffix, remoteOpts...) if err != nil { return err } @@ -62,7 +64,7 @@ func CleanCmd(ctx context.Context, imageRef string) error { fmt.Fprintln(os.Stderr, "Deleting signature metadata...") - err = remote.Delete(sigRef, DefaultRegistryClientOpts(ctx)...) + err = remote.Delete(sigRef, remoteOpts...) if err != nil { return err } diff --git a/cmd/cosign/cli/copy.go b/cmd/cosign/cli/copy.go index 44d4a9a0ed7..490d5c9ccf7 100644 --- a/cmd/cosign/cli/copy.go +++ b/cmd/cosign/cli/copy.go @@ -32,7 +32,9 @@ func Copy() *ffcli.Command { flagset = flag.NewFlagSet("cosign copy", flag.ExitOnError) sigOnlyFlag = flagset.Bool("sig-only", false, "only copy the image signature") forceFlag = flagset.Bool("f", false, "overwrite destination image(s), if necessary") + regOpts RegistryOpts ) + ApplyRegistryFlags(®Opts, flagset) return &ffcli.Command{ Name: "copy", ShortUsage: "cosign copy ", @@ -54,12 +56,12 @@ EXAMPLES if len(args) != 2 { return flag.ErrHelp } - return CopyCmd(ctx, args[0], args[1], *sigOnlyFlag, *forceFlag) + return CopyCmd(ctx, regOpts, args[0], args[1], *sigOnlyFlag, *forceFlag) }, } } -func CopyCmd(ctx context.Context, srcImg, dstImg string, sigOnly, force bool) error { +func CopyCmd(ctx context.Context, regOpts RegistryOpts, srcImg, dstImg string, sigOnly, force bool) error { srcRef, err := name.ParseReference(srcImg) if err != nil { return err @@ -69,21 +71,20 @@ func CopyCmd(ctx context.Context, srcImg, dstImg string, sigOnly, force bool) er return err } - sigSrcRef, err := AttachedImageTag(ctx, srcRef, cosign.SignatureTagSuffix) + remoteOpts := regOpts.GetRegistryClientOpts(ctx) + sigSrcRef, err := AttachedImageTag(srcRef, cosign.SignatureTagSuffix, remoteOpts...) if err != nil { return err } dstRepoRef := dstRef.Context() sigDstRef := dstRepoRef.Tag(sigSrcRef.Identifier()) - - regClientOpts := DefaultRegistryClientOpts(ctx) - if err := copyImage(sigSrcRef, sigDstRef, force, regClientOpts...); err != nil { + if err := copyImage(sigSrcRef, sigDstRef, force, remoteOpts...); err != nil { return err } if !sigOnly { - if err := copyImage(srcRef, dstRef, force, regClientOpts...); err != nil { + if err := copyImage(srcRef, dstRef, force, remoteOpts...); err != nil { return err } } diff --git a/cmd/cosign/cli/digest.go b/cmd/cosign/cli/digest.go index 4208205a54f..58cc34017ff 100644 --- a/cmd/cosign/cli/digest.go +++ b/cmd/cosign/cli/digest.go @@ -15,8 +15,6 @@ package cli import ( - "context" - "github.com/google/go-containerregistry/pkg/name" v1 "github.com/google/go-containerregistry/pkg/v1" "github.com/google/go-containerregistry/pkg/v1/remote" @@ -26,11 +24,11 @@ import ( // // If the reference is by digest already, it simply extracts the digest. // Otherwise, it looks up the digest from the registry. -func Digest(ctx context.Context, ref name.Reference) (v1.Hash, error) { +func Digest(ref name.Reference, remoteOpts ...remote.Option) (v1.Hash, error) { if d, ok := ref.(name.Digest); ok { return v1.NewHash(d.DigestStr()) } - desc, err := remote.Get(ref, DefaultRegistryClientOpts(ctx)...) + desc, err := remote.Get(ref, remoteOpts...) if err != nil { return v1.Hash{}, err } diff --git a/cmd/cosign/cli/download/sbom.go b/cmd/cosign/cli/download/sbom.go index 6a5dc027bf3..4b143d1dc2b 100644 --- a/cmd/cosign/cli/download/sbom.go +++ b/cmd/cosign/cli/download/sbom.go @@ -34,7 +34,9 @@ import ( func SBOM() *ffcli.Command { var ( flagset = flag.NewFlagSet("cosign download sbom", flag.ExitOnError) + regOpts cli.RegistryOpts ) + cli.ApplyRegistryFlags(®Opts, flagset) return &ffcli.Command{ Name: "sbom", ShortUsage: "cosign download sbom ", @@ -44,23 +46,25 @@ func SBOM() *ffcli.Command { if len(args) != 1 { return flag.ErrHelp } - _, err := SBOMCmd(ctx, args[0], os.Stdout) + _, err := SBOMCmd(ctx, regOpts, args[0], os.Stdout) return err }, } } -func SBOMCmd(ctx context.Context, imageRef string, out io.Writer) ([]string, error) { +func SBOMCmd(ctx context.Context, regOpts cli.RegistryOpts, imageRef string, out io.Writer) ([]string, error) { ref, err := name.ParseReference(imageRef) if err != nil { return nil, err } - dstRef, err := cli.AttachedImageTag(ctx, ref, cosign.SBOMTagSuffix) + remoteOpts := regOpts.GetRegistryClientOpts(ctx) + + dstRef, err := cli.AttachedImageTag(ref, cosign.SBOMTagSuffix, remoteOpts...) if err != nil { return nil, err } - img, err := remote.Image(dstRef, cli.DefaultRegistryClientOpts(ctx)...) + img, err := remote.Image(dstRef, remoteOpts...) if err != nil { return nil, err } diff --git a/cmd/cosign/cli/download/signature.go b/cmd/cosign/cli/download/signature.go index a026030367c..d911bd0e6eb 100644 --- a/cmd/cosign/cli/download/signature.go +++ b/cmd/cosign/cli/download/signature.go @@ -31,7 +31,9 @@ import ( func Signature() *ffcli.Command { var ( flagset = flag.NewFlagSet("cosign download signature", flag.ExitOnError) + regOpts cli.RegistryOpts ) + cli.ApplyRegistryFlags(®Opts, flagset) return &ffcli.Command{ Name: "signature", ShortUsage: "cosign download signature ", @@ -41,12 +43,12 @@ func Signature() *ffcli.Command { if len(args) != 1 { return flag.ErrHelp } - return SignatureCmd(ctx, args[0]) + return SignatureCmd(ctx, regOpts, args[0]) }, } } -func SignatureCmd(ctx context.Context, imageRef string) error { +func SignatureCmd(ctx context.Context, regOpts cli.RegistryOpts, imageRef string) error { ref, err := name.ParseReference(imageRef) if err != nil { return err @@ -55,7 +57,7 @@ func SignatureCmd(ctx context.Context, imageRef string) error { if err != nil { return err } - regClientOpts := cli.DefaultRegistryClientOpts(ctx) + regClientOpts := regOpts.GetRegistryClientOpts(ctx) signatures, err := cosign.FetchSignaturesForImage(ctx, ref, sigRepo, cosign.SignatureTagSuffix, regClientOpts...) if err != nil { return err diff --git a/cmd/cosign/cli/flags.go b/cmd/cosign/cli/flags.go index acec64c79bc..6fd9f4bf91a 100644 --- a/cmd/cosign/cli/flags.go +++ b/cmd/cosign/cli/flags.go @@ -15,7 +15,15 @@ package cli -import "reflect" +import ( + "context" + "crypto/tls" + "flag" + "net/http" + "reflect" + + "github.com/google/go-containerregistry/pkg/v1/remote" +) // oneOf ensures that only one of the supplied interfaces is set to a non-zero value. func oneOf(args ...interface{}) bool { @@ -32,3 +40,19 @@ func nOf(args ...interface{}) int { } return n } + +type RegistryOpts struct { + AllowInsecure bool +} + +func (co *RegistryOpts) GetRegistryClientOpts(ctx context.Context) []remote.Option { + opts := defaultRegistryClientOpts(ctx) + if co != nil && co.AllowInsecure { + opts = append(opts, remote.WithTransport(&http.Transport{TLSClientConfig: &tls.Config{InsecureSkipVerify: true}})) // #nosec G402 + } + return opts +} + +func ApplyRegistryFlags(regOpts *RegistryOpts, fs *flag.FlagSet) { + fs.BoolVar(®Opts.AllowInsecure, "allow-insecure-registry", false, "whether to allow insecure connections to registries. Don't use this for anything but testing") +} diff --git a/cmd/cosign/cli/generate.go b/cmd/cosign/cli/generate.go index c5efbbda687..f74c5405040 100644 --- a/cmd/cosign/cli/generate.go +++ b/cmd/cosign/cli/generate.go @@ -22,7 +22,6 @@ import ( "io" "os" - "github.com/google/go-containerregistry/pkg/authn" "github.com/google/go-containerregistry/pkg/name" "github.com/google/go-containerregistry/pkg/v1/remote" "github.com/peterbourgon/ff/v3/ffcli" @@ -33,8 +32,10 @@ import ( func Generate() *ffcli.Command { var ( flagset = flag.NewFlagSet("cosign generate", flag.ExitOnError) + regOpts RegistryOpts annotations = annotationsMap{} ) + ApplyRegistryFlags(®Opts, flagset) flagset.Var(&annotations, "a", "extra key=value pairs to sign") return &ffcli.Command{ @@ -59,18 +60,18 @@ EXAMPLES if len(args) != 1 { return flag.ErrHelp } - return GenerateCmd(ctx, args[0], annotations.annotations, os.Stdout) + return GenerateCmd(ctx, regOpts, args[0], annotations.annotations, os.Stdout) }, } } -func GenerateCmd(_ context.Context, imageRef string, annotations map[string]interface{}, w io.Writer) error { +func GenerateCmd(ctx context.Context, regOpts RegistryOpts, imageRef string, annotations map[string]interface{}, w io.Writer) error { ref, err := name.ParseReference(imageRef) if err != nil { return err } - get, err := remote.Get(ref, remote.WithAuthFromKeychain(authn.DefaultKeychain)) + get, err := remote.Get(ref, regOpts.GetRegistryClientOpts(ctx)...) if err != nil { return err } diff --git a/cmd/cosign/cli/sign.go b/cmd/cosign/cli/sign.go index 20817bd0893..e8cb2052e07 100644 --- a/cmd/cosign/cli/sign.go +++ b/cmd/cosign/cli/sign.go @@ -122,7 +122,9 @@ func Sign() *ffcli.Command { oidcClientSecret = flagset.String("oidc-client-secret", "", "[EXPERIMENTAL] OIDC client secret for application") attachment = flagset.String("attachment", "", "related image attachment to sign (sbom), default none") annotations = annotationsMap{} + regOpts RegistryOpts ) + ApplyRegistryFlags(®Opts, flagset) flagset.Var(&annotations, "a", "extra key=value pairs to sign") return &ffcli.Command{ Name: "sign", @@ -184,7 +186,7 @@ EXAMPLES OIDCClientID: *oidcClientID, OIDCClientSecret: *oidcClientSecret, } - if err := SignCmd(ctx, ko, annotations.annotations, args, *cert, *upload, *payloadPath, *force, *recursive, *attachment); err != nil { + 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) } @@ -195,7 +197,7 @@ EXAMPLES } } -func getAttachedImageRef(ctx context.Context, imageRef string, attachment string) (name.Reference, error) { +func getAttachedImageRef(imageRef string, attachment string, remoteOpts ...remote.Option) (name.Reference, error) { ref, err := name.ParseReference(imageRef) if err != nil { return nil, errors.Wrap(err, "parsing reference") @@ -204,7 +206,7 @@ func getAttachedImageRef(ctx context.Context, imageRef string, attachment string return ref, nil } if attachment == "sbom" { - return AttachedImageTag(ctx, ref, cosign.SBOMTagSuffix) + return AttachedImageTag(ref, cosign.SBOMTagSuffix, remoteOpts...) } return nil, fmt.Errorf("unknown attachment type %s", attachment) } @@ -243,7 +245,7 @@ func getTransitiveImages(rootIndex *remote.Descriptor, repo name.Repository, opt return imgs, nil } -func SignCmd(ctx context.Context, ko KeyOpts, annotations map[string]interface{}, +func SignCmd(ctx context.Context, ko KeyOpts, regOpts RegistryOpts, annotations map[string]interface{}, imgs []string, certPath string, upload bool, payloadPath string, force bool, recursive bool, attachment string) error { if EnableExperimental() { if nOf(ko.KeyRef, ko.Sk) > 1 { @@ -255,17 +257,17 @@ func SignCmd(ctx context.Context, ko KeyOpts, annotations map[string]interface{} } } - remoteOpts := DefaultRegistryClientOpts(ctx) + remoteOpts := regOpts.GetRegistryClientOpts(ctx) toSign := make([]name.Digest, 0, len(imgs)) for _, inputImg := range imgs { // A key file or token is required unless we're in experimental mode! - ref, err := getAttachedImageRef(ctx, inputImg, attachment) + ref, err := getAttachedImageRef(inputImg, attachment, remoteOpts...) if err != nil { return fmt.Errorf("unable to resolve attachment %s for image %s", attachment, inputImg) } - h, err := Digest(ctx, ref) + h, err := Digest(ref, remoteOpts...) if err != nil { return errors.Wrap(err, "resolving digest") } @@ -323,7 +325,7 @@ func SignCmd(ctx context.Context, ko KeyOpts, annotations map[string]interface{} continue } - sigRef, err := AttachedImageTag(ctx, img, cosign.SignatureTagSuffix) + sigRef, err := AttachedImageTag(img, cosign.SignatureTagSuffix, remoteOpts...) if err != nil { return err } diff --git a/cmd/cosign/cli/sign_blob.go b/cmd/cosign/cli/sign_blob.go index 5e0a4c8ae37..98a3ef20780 100644 --- a/cmd/cosign/cli/sign_blob.go +++ b/cmd/cosign/cli/sign_blob.go @@ -47,7 +47,9 @@ func SignBlob() *ffcli.Command { oidcClientID = flagset.String("oidc-client-id", "sigstore", "[EXPERIMENTAL] OIDC client ID for application") oidcClientSecret = flagset.String("oidc-client-secret", "", "[EXPERIMENTAL] OIDC client secret for application") output = flagset.String("output", "", "write the signature to FILE") + regOpts RegistryOpts ) + ApplyRegistryFlags(®Opts, flagset) return &ffcli.Command{ Name: "sign-blob", ShortUsage: "cosign sign-blob -key | [-sig ] ", @@ -97,7 +99,7 @@ EXAMPLES OIDCClientSecret: *oidcClientSecret, } for _, blob := range args { - if _, err := SignBlobCmd(ctx, ko, blob, *b64, *output); err != nil { + if _, err := SignBlobCmd(ctx, ko, regOpts, blob, *b64, *output); err != nil { return errors.Wrapf(err, "signing %s", blob) } } @@ -119,7 +121,7 @@ type KeyOpts struct { OIDCClientSecret string } -func SignBlobCmd(ctx context.Context, ko KeyOpts, payloadPath string, b64 bool, output string) ([]byte, error) { +func SignBlobCmd(ctx context.Context, ko KeyOpts, regOpts RegistryOpts, payloadPath string, b64 bool, output string) ([]byte, error) { var payload []byte var err error diff --git a/cmd/cosign/cli/sign_test.go b/cmd/cosign/cli/sign_test.go index fbe10140c34..192e3e3796e 100644 --- a/cmd/cosign/cli/sign_test.go +++ b/cmd/cosign/cli/sign_test.go @@ -34,7 +34,7 @@ func TestSignCmdLocalKeyAndSk(t *testing.T) { Sk: true, }, } { - err := SignCmd(ctx, ko, nil, nil, "", false, "", false, false, "") + err := SignCmd(ctx, ko, RegistryOpts{}, nil, nil, "", false, "", false, false, "") if (errors.Is(err, &KeyParseError{}) == false) { t.Fatal("expected KeyParseError") } diff --git a/cmd/cosign/cli/triangulate.go b/cmd/cosign/cli/triangulate.go index 7601eb1e8bd..52bbbd40acb 100644 --- a/cmd/cosign/cli/triangulate.go +++ b/cmd/cosign/cli/triangulate.go @@ -30,7 +30,9 @@ func Triangulate() *ffcli.Command { var ( flagset = flag.NewFlagSet("cosign triangulate", flag.ExitOnError) t = flagset.String("type", "signature", "related attachment to triangulate (attestation|sbom|signature), default signature") + regOpts RegistryOpts ) + ApplyRegistryFlags(®Opts, flagset) return &ffcli.Command{ Name: "triangulate", ShortUsage: "cosign triangulate ", @@ -40,25 +42,26 @@ func Triangulate() *ffcli.Command { if len(args) != 1 { return flag.ErrHelp } - return MungeCmd(ctx, args[0], *t) + return MungeCmd(ctx, regOpts, args[0], *t) }, } } -func MungeCmd(ctx context.Context, imageRef string, attachmentType string) error { +func MungeCmd(ctx context.Context, regOpts RegistryOpts, imageRef string, attachmentType string) error { ref, err := name.ParseReference(imageRef) if err != nil { return err } + remoteOpts := regOpts.GetRegistryClientOpts(ctx) var dstRef name.Tag switch attachmentType { case cosign.Signature: - dstRef, err = AttachedImageTag(ctx, ref, cosign.SignatureTagSuffix) + dstRef, err = AttachedImageTag(ref, cosign.SignatureTagSuffix, remoteOpts...) case cosign.SBOM: - dstRef, err = AttachedImageTag(ctx, ref, cosign.SBOMTagSuffix) + dstRef, err = AttachedImageTag(ref, cosign.SBOMTagSuffix, remoteOpts...) case cosign.Attestation: - dstRef, err = AttachedImageTag(ctx, ref, cosign.AttestationTagSuffix) + dstRef, err = AttachedImageTag(ref, cosign.AttestationTagSuffix, remoteOpts...) default: err = fmt.Errorf("unknown attachment type %s", attachmentType) } diff --git a/cmd/cosign/cli/upload/blob.go b/cmd/cosign/cli/upload/blob.go index 78b783f0ce9..507a6edd647 100644 --- a/cmd/cosign/cli/upload/blob.go +++ b/cmd/cosign/cli/upload/blob.go @@ -61,7 +61,9 @@ func Blob() *ffcli.Command { var ( flagset = flag.NewFlagSet("cosign upload blob", flag.ExitOnError) ct = flagset.String("ct", "", "content type to set") + regOpts cli.RegistryOpts ) + cli.ApplyRegistryFlags(®Opts, flagset) fmap := Files{} flagset.Var(&fmap, "f", ":[platform/arch]") return &ffcli.Command{ @@ -89,18 +91,18 @@ EXAMPLES return flag.ErrHelp } - return BlobCmd(ctx, fmap.Files, *ct, args[0]) + return BlobCmd(ctx, regOpts, fmap.Files, *ct, args[0]) }, } } -func BlobCmd(ctx context.Context, files []cremote.File, contentType, imageRef string) error { +func BlobCmd(ctx context.Context, regOpts cli.RegistryOpts, files []cremote.File, contentType, imageRef string) error { ref, err := name.ParseReference(imageRef) if err != nil { return err } - dgster, err := cremote.UploadFiles(ref, files, cremote.DefaultMediaTypeGetter, cli.DefaultRegistryClientOpts(ctx)...) + dgster, err := cremote.UploadFiles(ref, files, cremote.DefaultMediaTypeGetter, regOpts.GetRegistryClientOpts(ctx)...) if err != nil { return err } diff --git a/cmd/cosign/cli/upload/wasm.go b/cmd/cosign/cli/upload/wasm.go index 7101a9d7821..81115cfa966 100644 --- a/cmd/cosign/cli/upload/wasm.go +++ b/cmd/cosign/cli/upload/wasm.go @@ -34,7 +34,9 @@ func Wasm() *ffcli.Command { var ( flagset = flag.NewFlagSet("cosign upload wasm", flag.ExitOnError) f = flagset.String("f", "", "path to the wasm file to upload") + regOpts cli.RegistryOpts ) + cli.ApplyRegistryFlags(®Opts, flagset) return &ffcli.Command{ Name: "wasm", ShortUsage: "cosign upload wasm -f foo.wasm ", @@ -45,12 +47,12 @@ func Wasm() *ffcli.Command { return flag.ErrHelp } - return WasmCmd(ctx, *f, args[0]) + return WasmCmd(ctx, regOpts, *f, args[0]) }, } } -func WasmCmd(ctx context.Context, wasmPath, imageRef string) error { +func WasmCmd(ctx context.Context, regOpts cli.RegistryOpts, wasmPath, imageRef string) error { b, err := ioutil.ReadFile(wasmPath) if err != nil { return err @@ -60,9 +62,9 @@ func WasmCmd(ctx context.Context, wasmPath, imageRef string) error { if err != nil { return err } - + regOpts.GetRegistryClientOpts(ctx) fmt.Fprintf(os.Stderr, "Uploading wasm file from [%s] to [%s].\n", wasmPath, ref.Name()) - if _, err := cremote.UploadFile(b, ref, types.WasmLayerMediaType, types.WasmConfigMediaType, cli.DefaultRegistryClientOpts(ctx)...); err != nil { + if _, err := cremote.UploadFile(b, ref, types.WasmLayerMediaType, types.WasmConfigMediaType, regOpts.GetRegistryClientOpts(ctx)...); err != nil { return err } diff --git a/cmd/cosign/cli/util.go b/cmd/cosign/cli/util.go index a7fb77db532..29d3041927e 100644 --- a/cmd/cosign/cli/util.go +++ b/cmd/cosign/cli/util.go @@ -54,8 +54,8 @@ func TargetRepositoryForImage(img name.Reference) (name.Repository, error) { return name.NewRepository(wantRepo) } -func AttachedImageTag(ctx context.Context, ref name.Reference, suffix string) (name.Tag, error) { - h, err := Digest(ctx, ref) +func AttachedImageTag(ref name.Reference, suffix string, remoteOpts ...remote.Option) (name.Tag, error) { + h, err := Digest(ref, remoteOpts...) if err != nil { return name.Tag{}, err } @@ -111,7 +111,7 @@ func LoadPublicKey(ctx context.Context, keyRef string) (verifier signature.Verif return signature.LoadECDSAVerifier(ed, crypto.SHA256) } -func DefaultRegistryClientOpts(ctx context.Context) []remote.Option { +func defaultRegistryClientOpts(ctx context.Context) []remote.Option { return []remote.Option{ remote.WithAuthFromKeychain(authn.DefaultKeychain), remote.WithContext(ctx), diff --git a/cmd/cosign/cli/verify.go b/cmd/cosign/cli/verify.go index d514a36f692..11dbf55644e 100644 --- a/cmd/cosign/cli/verify.go +++ b/cmd/cosign/cli/verify.go @@ -34,6 +34,7 @@ import ( // VerifyCommand verifies a signature on a supplied container image type VerifyCommand struct { + RegistryOpts CheckClaims bool KeyRef string CertEmail string @@ -55,6 +56,7 @@ func ApplyVerifyFlags(cmd *VerifyCommand, flagset *flag.FlagSet) { flagset.BoolVar(&cmd.CheckClaims, "check-claims", true, "whether to check the claims found") flagset.StringVar(&cmd.Output, "output", "json", "output format for the signing image information (default JSON) (json|text)") flagset.StringVar(&cmd.Attachment, "attachment", "", "related image attachment to sign (none|sbom), default none") + ApplyRegistryFlags(&cmd.RegistryOpts, flagset) // parse annotations flagset.Var(&annotations, "a", "extra key=value pairs to sign") @@ -124,9 +126,11 @@ func (c *VerifyCommand) Exec(ctx context.Context, args []string) (err error) { return &KeyParseError{} } + remoteOpts := c.RegistryOpts.GetRegistryClientOpts(ctx) + co := &cosign.CheckOpts{ Annotations: *c.Annotations, - RegistryClientOpts: DefaultRegistryClientOpts(ctx), + RegistryClientOpts: remoteOpts, } if c.CheckClaims { co.ClaimVerifier = cosign.SimpleClaimVerifier @@ -158,7 +162,7 @@ func (c *VerifyCommand) Exec(ctx context.Context, args []string) (err error) { co.SigVerifier = pubKey for _, img := range args { - ref, err := getAttachedImageRef(ctx, img, c.Attachment) + ref, err := getAttachedImageRef(img, c.Attachment, remoteOpts...) if err != nil { return errors.Wrapf(err, "resolving attachment type %s for image %s", c.Attachment, img) } diff --git a/cmd/cosign/cli/verify_attestation.go b/cmd/cosign/cli/verify_attestation.go index 24772bff4c2..a2a39b8687a 100644 --- a/cmd/cosign/cli/verify_attestation.go +++ b/cmd/cosign/cli/verify_attestation.go @@ -33,6 +33,7 @@ import ( // VerifyAttestationCommand verifies a signature on a supplied container image type VerifyAttestationCommand struct { + RegistryOpts CheckClaims bool KeyRef string Sk bool @@ -56,6 +57,7 @@ func VerifyAttestation() *ffcli.Command { cmd := VerifyAttestationCommand{} flagset := flag.NewFlagSet("cosign verify-attestation", flag.ExitOnError) applyVerifyAttestationFlags(&cmd, flagset) + ApplyRegistryFlags(&cmd.RegistryOpts, flagset) return &ffcli.Command{ Name: "verify-attestation", @@ -116,7 +118,7 @@ func (c *VerifyAttestationCommand) Exec(ctx context.Context, args []string) (err } co := &cosign.CheckOpts{ - RegistryClientOpts: DefaultRegistryClientOpts(ctx), + RegistryClientOpts: c.GetRegistryClientOpts(ctx), SigTagSuffixOverride: cosign.AttestationTagSuffix, } if c.CheckClaims { diff --git a/cmd/cosign/cli/verify_blob.go b/cmd/cosign/cli/verify_blob.go index d64dc7bd89f..2109f451f36 100644 --- a/cmd/cosign/cli/verify_blob.go +++ b/cmd/cosign/cli/verify_blob.go @@ -49,7 +49,9 @@ func VerifyBlob() *ffcli.Command { rekorURL = flagset.String("rekor-url", "https://rekor.sigstore.dev", "[EXPERIMENTAL] address of rekor STL server") cert = flagset.String("cert", "", "path to the public certificate") signature = flagset.String("signature", "", "signature content or path or remote URL") + regOpts RegistryOpts ) + ApplyRegistryFlags(®Opts, flagset) return &ffcli.Command{ Name: "verify-blob", ShortUsage: "cosign verify-blob (-key ||)|(-cert ) -signature ", diff --git a/test/e2e_test.go b/test/e2e_test.go index 228fe32e4a1..60bce98aa9f 100644 --- a/test/e2e_test.go +++ b/test/e2e_test.go @@ -92,22 +92,22 @@ func TestSignVerify(t *testing.T) { // Verify should fail at first mustErr(verify(pubKeyPath, imgName, true, nil, ""), t) // So should download - mustErr(download.SignatureCmd(ctx, imgName), t) + mustErr(download.SignatureCmd(ctx, cli.RegistryOpts{}, imgName), t) // Now sign the image ko := cli.KeyOpts{KeyRef: privKeyPath, PassFunc: passFunc} - must(cli.SignCmd(ctx, ko, nil, []string{imgName}, "", true, "", false, false, ""), t) + must(cli.SignCmd(ctx, ko, cli.RegistryOpts{}, nil, []string{imgName}, "", true, "", false, false, ""), t) // Now verify and download should work! must(verify(pubKeyPath, imgName, true, nil, ""), t) - must(download.SignatureCmd(ctx, imgName), t) + must(download.SignatureCmd(ctx, cli.RegistryOpts{}, imgName), t) // Look for a specific annotation mustErr(verify(pubKeyPath, imgName, true, map[string]interface{}{"foo": "bar"}, ""), t) // Sign the image with an annotation annotations := map[string]interface{}{"foo": "bar"} - must(cli.SignCmd(ctx, ko, annotations, []string{imgName}, "", true, "", false, false, ""), t) + must(cli.SignCmd(ctx, ko, cli.RegistryOpts{}, annotations, []string{imgName}, "", true, "", false, false, ""), t) // It should match this time. must(verify(pubKeyPath, imgName, true, map[string]interface{}{"foo": "bar"}, ""), t) @@ -131,14 +131,14 @@ func TestSignVerifyClean(t *testing.T) { // Now sign the image ko := cli.KeyOpts{KeyRef: privKeyPath, PassFunc: passFunc} - must(cli.SignCmd(ctx, ko, nil, []string{imgName}, "", true, "", false, false, ""), t) + must(cli.SignCmd(ctx, ko, cli.RegistryOpts{}, nil, []string{imgName}, "", true, "", false, false, ""), t) // Now verify and download should work! must(verify(pubKeyPath, imgName, true, nil, ""), t) - must(download.SignatureCmd(ctx, imgName), t) + must(download.SignatureCmd(ctx, cli.RegistryOpts{}, imgName), t) // Now clean signature from the given image - must(cli.CleanCmd(ctx, imgName), t) + must(cli.CleanCmd(ctx, cli.RegistryOpts{}, imgName), t) // It doesn't work mustErr(verify(pubKeyPath, imgName, true, nil, ""), t) @@ -173,7 +173,7 @@ func TestAttestVerify(t *testing.T) { // Now attest the image ko := cli.KeyOpts{KeyRef: privKeyPath, PassFunc: passFunc} - must(cli.AttestCmd(ctx, ko, imgName, "", true, ap, false, "custom"), t) + must(cli.AttestCmd(ctx, ko, cli.RegistryOpts{}, imgName, "", true, ap, false, "custom"), t) // Now verify and download should work! must(verifyAttestation.Exec(ctx, []string{imgName}), t) @@ -205,7 +205,7 @@ func TestBundle(t *testing.T) { } // Sign the image - must(cli.SignCmd(ctx, ko, nil, []string{imgName}, "", true, "", false, false, ""), t) + must(cli.SignCmd(ctx, ko, cli.RegistryOpts{}, nil, []string{imgName}, "", true, "", false, false, ""), t) // Make sure verify works must(verify(pubKeyPath, imgName, true, nil, ""), t) @@ -231,18 +231,18 @@ func TestDuplicateSign(t *testing.T) { // Verify should fail at first mustErr(verify(pubKeyPath, imgName, true, nil, ""), t) // So should download - mustErr(download.SignatureCmd(ctx, imgName), t) + mustErr(download.SignatureCmd(ctx, cli.RegistryOpts{}, imgName), t) // Now sign the image ko := cli.KeyOpts{KeyRef: privKeyPath, PassFunc: passFunc} - must(cli.SignCmd(ctx, ko, nil, []string{imgName}, "", true, "", false, false, ""), t) + must(cli.SignCmd(ctx, ko, cli.RegistryOpts{}, nil, []string{imgName}, "", true, "", false, false, ""), t) // Now verify and download should work! must(verify(pubKeyPath, imgName, true, nil, ""), t) - must(download.SignatureCmd(ctx, imgName), t) + must(download.SignatureCmd(ctx, cli.RegistryOpts{}, imgName), t) // Signing again should work just fine... - must(cli.SignCmd(ctx, ko, nil, []string{imgName}, "", true, "", false, false, ""), t) + must(cli.SignCmd(ctx, ko, cli.RegistryOpts{}, nil, []string{imgName}, "", true, "", false, false, ""), t) // but a duplicate signature should not be a uploaded sigRepo, err := cli.TargetRepositoryForImage(ref) if err != nil { @@ -336,14 +336,14 @@ func TestMultipleSignatures(t *testing.T) { // Now sign the image with one key ko := cli.KeyOpts{KeyRef: priv1, PassFunc: passFunc} - must(cli.SignCmd(ctx, ko, nil, []string{imgName}, "", true, "", false, false, ""), t) + must(cli.SignCmd(ctx, ko, cli.RegistryOpts{}, nil, []string{imgName}, "", true, "", false, false, ""), t) // Now verify should work with that one, but not the other must(verify(pub1, imgName, true, nil, ""), t) mustErr(verify(pub2, imgName, true, nil, ""), t) // Now sign with the other key too ko.KeyRef = priv2 - must(cli.SignCmd(ctx, ko, nil, []string{imgName}, "", true, "", false, false, ""), t) + must(cli.SignCmd(ctx, ko, cli.RegistryOpts{}, nil, []string{imgName}, "", true, "", false, false, ""), t) // Now verify should work with both must(verify(pub1, imgName, true, nil, ""), t) @@ -385,7 +385,7 @@ func TestSignBlob(t *testing.T) { KeyRef: privKeyPath1, PassFunc: passFunc, } - sig, err := cli.SignBlobCmd(ctx, ko, bp, true, "") + sig, err := cli.SignBlobCmd(ctx, ko, cli.RegistryOpts{}, bp, true, "") if err != nil { t.Fatal(err) } @@ -404,7 +404,7 @@ func TestGenerate(t *testing.T) { // Generate the payload for the image, and check the digest. b := bytes.Buffer{} - must(cli.GenerateCmd(context.Background(), imgName, nil, &b), t) + must(cli.GenerateCmd(context.Background(), cli.RegistryOpts{}, imgName, nil, &b), t) ss := payload.SimpleContainerImage{} must(json.Unmarshal(b.Bytes(), &ss), t) @@ -413,7 +413,7 @@ func TestGenerate(t *testing.T) { // Now try with some annotations. b.Reset() a := map[string]interface{}{"foo": "bar"} - must(cli.GenerateCmd(context.Background(), imgName, a, &b), t) + must(cli.GenerateCmd(context.Background(), cli.RegistryOpts{}, imgName, a, &b), t) must(json.Unmarshal(b.Bytes(), &ss), t) equals(desc.Digest.String(), ss.Critical.Image.DockerManifestDigest, t) @@ -492,7 +492,7 @@ func TestUploadDownload(t *testing.T) { } // Upload it! - err := attach.SignatureCmd(ctx, sigRef, payloadPath, imgName) + err := attach.SignatureCmd(ctx, cli.RegistryOpts{}, sigRef, payloadPath, imgName) if testCase.expectedErr { mustErr(err, t) } else { @@ -541,7 +541,7 @@ func TestUploadBlob(t *testing.T) { // Upload it! files := []cremote.File{cremote.FileFromFlag(payloadPath)} - must(upload.BlobCmd(ctx, files, "", imgName), t) + must(upload.BlobCmd(ctx, cli.RegistryOpts{}, files, "", imgName), t) // Check it ref, err := name.ParseReference(imgName) @@ -587,7 +587,7 @@ func TestAttachSBOM(t *testing.T) { defer cleanup() out := bytes.Buffer{} - _, err := download.SBOMCmd(ctx, img.Name(), &out) + _, err := download.SBOMCmd(ctx, cli.RegistryOpts{}, img.Name(), &out) if err == nil { t.Fatal("Expected error") } @@ -595,9 +595,9 @@ func TestAttachSBOM(t *testing.T) { out.Reset() // Upload it! - must(attach.SBOMCmd(ctx, "./testdata/bom-go-mod.spdx", "spdx", imgName), t) + must(attach.SBOMCmd(ctx, cli.RegistryOpts{}, "./testdata/bom-go-mod.spdx", "spdx", imgName), t) - sboms, err := download.SBOMCmd(ctx, imgName, &out) + sboms, err := download.SBOMCmd(ctx, cli.RegistryOpts{}, imgName, &out) if err != nil { t.Fatal(err) } @@ -625,7 +625,7 @@ func TestAttachSBOM(t *testing.T) { // Now sign the sbom with one key ko1 := cli.KeyOpts{KeyRef: privKeyPath1, PassFunc: passFunc} - must(cli.SignCmd(ctx, ko1, nil, []string{imgName}, "", true, "", false, false, "sbom"), t) + must(cli.SignCmd(ctx, ko1, cli.RegistryOpts{}, nil, []string{imgName}, "", true, "", false, false, "sbom"), t) // Now verify should work with that one, but not the other must(verify(pubKeyPath1, imgName, true, nil, "sbom"), t) @@ -663,7 +663,7 @@ func TestTlog(t *testing.T) { PassFunc: passFunc, RekorURL: rekorURL, } - must(cli.SignCmd(ctx, ko, nil, []string{imgName}, "", true, "", false, false, ""), t) + must(cli.SignCmd(ctx, ko, cli.RegistryOpts{}, nil, []string{imgName}, "", true, "", false, false, ""), t) // Now verify should work! must(verify(pubKeyPath, imgName, true, nil, ""), t) @@ -675,7 +675,7 @@ func TestTlog(t *testing.T) { mustErr(verify(pubKeyPath, imgName, true, nil, ""), t) // Sign again with the tlog env var on - must(cli.SignCmd(ctx, ko, nil, []string{imgName}, "", true, "", false, false, ""), t) + must(cli.SignCmd(ctx, ko, cli.RegistryOpts{}, nil, []string{imgName}, "", true, "", false, false, ""), t) // And now verify works! must(verify(pubKeyPath, imgName, true, nil, ""), t) }