sigs.k8s.io/release-sdk@v0.11.1-0.20240417074027-8061fb5e4952/sign/impl.go (about) 1 /* 2 Copyright 2022 The Kubernetes Authors. 3 4 Licensed under the Apache License, Version 2.0 (the "License"); 5 you may not use this file except in compliance with the License. 6 You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10 Unless required by applicable law or agreed to in writing, software 11 distributed under the License is distributed on an "AS IS" BASIS, 12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 See the License for the specific language governing permissions and 14 limitations under the License. 15 */ 16 17 package sign 18 19 import ( 20 "context" 21 "fmt" 22 "net/http" 23 "os" 24 "sync" 25 26 "github.com/google/go-containerregistry/pkg/authn" 27 "github.com/google/go-containerregistry/pkg/crane" 28 "github.com/google/go-containerregistry/pkg/name" 29 "github.com/google/go-containerregistry/pkg/v1/remote/transport" 30 "github.com/sigstore/cosign/v2/cmd/cosign/cli/options" 31 "github.com/sigstore/cosign/v2/cmd/cosign/cli/rekor" 32 "github.com/sigstore/cosign/v2/cmd/cosign/cli/sign" 33 "github.com/sigstore/cosign/v2/cmd/cosign/cli/verify" 34 "github.com/sigstore/cosign/v2/pkg/blob" 35 "github.com/sigstore/cosign/v2/pkg/cosign" 36 "github.com/sigstore/cosign/v2/pkg/providers" 37 "github.com/sigstore/rekor/pkg/generated/client" 38 "github.com/sigstore/rekor/pkg/generated/models" 39 "github.com/sirupsen/logrus" 40 41 "sigs.k8s.io/release-utils/env" 42 "sigs.k8s.io/release-utils/util" 43 ) 44 45 type defaultImpl struct{} 46 47 //go:generate go run github.com/maxbrunsfeld/counterfeiter/v6 -generate 48 //counterfeiter:generate . impl 49 //go:generate /usr/bin/env bash -c "cat ../scripts/boilerplate/boilerplate.generatego.txt signfakes/fake_impl.go > signfakes/_fake_impl.go && mv signfakes/_fake_impl.go signfakes/fake_impl.go" 50 type impl interface { 51 VerifyFileInternal(ctx context.Context, ko options.KeyOpts, certOpts options.CertVerifyOptions, outputSignature, path string) error 52 VerifyImageInternal(ctx context.Context, certOpts options.CertVerifyOptions, keyPath string, images []string, ignoreTLog bool) (*SignedObject, error) 53 SignImageInternal(ro options.RootOptions, ko options.KeyOpts, signOpts options.SignOptions, imgs []string) error 54 SignFileInternal(ro options.RootOptions, ko options.KeyOpts, payloadPath string, 55 b64 bool, outputSignature string, outputCertificate string, tlogUpload bool) error 56 Setenv(string, string) error 57 EnvDefault(string, string) string 58 TokenFromProviders(context.Context, *logrus.Logger) (string, error) 59 FileExists(string) bool 60 ParseReference(string, ...name.Option) (name.Reference, error) 61 FindTlogEntry(ctx context.Context, rClient *client.Rekor, b64Sig string, blobBytes []byte, pubKey []byte) ([]models.LogEntryAnon, error) 62 Digest(ref string, opt ...crane.Option) (string, error) 63 PayloadBytes(blobRef string) ([]byte, error) 64 NewRekorClient(string) (*client.Rekor, error) 65 NewWithContext(context.Context, name.Registry, authn.Authenticator, http.RoundTripper, []string) (http.RoundTripper, error) 66 ImagesSigned(context.Context, *Signer, ...string) (*sync.Map, error) 67 } 68 69 func (*defaultImpl) VerifyFileInternal(ctx context.Context, ko options.KeyOpts, certOpts options.CertVerifyOptions, outputSignature, //nolint: gocritic 70 path string, 71 ) error { 72 verifyBlob := verify.VerifyBlobCmd{ 73 KeyOpts: ko, 74 CertVerifyOptions: certOpts, 75 CertRef: certOpts.Cert, 76 CertChain: certOpts.CertChain, 77 SigRef: outputSignature, 78 CertGithubWorkflowTrigger: certOpts.CertGithubWorkflowTrigger, 79 CertGithubWorkflowSHA: certOpts.CertGithubWorkflowSha, 80 CertGithubWorkflowName: certOpts.CertGithubWorkflowName, 81 CertGithubWorkflowRepository: certOpts.CertGithubWorkflowRepository, 82 CertGithubWorkflowRef: certOpts.CertGithubWorkflowRef, 83 IgnoreSCT: certOpts.IgnoreSCT, 84 SCTRef: "", 85 Offline: false, 86 IgnoreTlog: true, 87 } 88 89 return verifyBlob.Exec(ctx, path) 90 } 91 92 func (*defaultImpl) VerifyImageInternal(ctx context.Context, certOpts options.CertVerifyOptions, //nolint: gocritic 93 publickeyPath string, images []string, ignoreTLog bool, 94 ) (*SignedObject, error) { 95 v := verify.VerifyCommand{ 96 IgnoreTlog: ignoreTLog, 97 KeyRef: publickeyPath, 98 CertVerifyOptions: options.CertVerifyOptions{ 99 CertIdentity: certOpts.CertIdentity, 100 CertIdentityRegexp: certOpts.CertIdentityRegexp, 101 CertOidcIssuer: certOpts.CertOidcIssuer, 102 CertOidcIssuerRegexp: certOpts.CertOidcIssuerRegexp, 103 IgnoreSCT: certOpts.IgnoreSCT, 104 }, 105 IgnoreSCT: certOpts.IgnoreSCT, 106 } 107 return &SignedObject{}, v.Exec(ctx, images) 108 } 109 110 func (*defaultImpl) SignImageInternal(ro options.RootOptions, ko options.KeyOpts, //nolint: gocritic 111 signOpts options.SignOptions, imgs []string, //nolint: gocritic 112 ) error { 113 return sign.SignCmd( 114 &ro, ko, signOpts, imgs) 115 } 116 117 func (*defaultImpl) SignFileInternal(ro options.RootOptions, ko options.KeyOpts, //nolint: gocritic 118 payloadPath string, b64 bool, outputSignature string, outputCertificate string, tlogUpload bool, 119 ) error { 120 // Ignoring the signature return value for now as we are setting the outputSignature path and to keep an consistent impl API 121 // Setting timeout as 0 is acceptable here because SignBlobCmd uses the passed context 122 _, err := sign.SignBlobCmd(&ro, ko, payloadPath, b64, outputSignature, outputCertificate, tlogUpload) 123 return err 124 } 125 126 func (*defaultImpl) Setenv(key, value string) error { 127 return os.Setenv(key, value) 128 } 129 130 func (*defaultImpl) EnvDefault(key, def string) string { 131 return env.Default(key, def) 132 } 133 134 // TokenFromProviders will try the cosign OIDC providers to get an 135 // oidc token from them. 136 func (d *defaultImpl) TokenFromProviders(ctx context.Context, logger *logrus.Logger) (string, error) { 137 if !d.IdentityProvidersEnabled(ctx) { 138 logger.Warn("No OIDC provider enabled. Token cannot be obtained automatically.") 139 return "", nil 140 } 141 142 tok, err := providers.Provide(ctx, "sigstore") 143 if err != nil { 144 return "", fmt.Errorf("fetching oidc token from environment: %w", err) 145 } 146 return tok, nil 147 } 148 149 // FileExists returns true if a file exists 150 func (*defaultImpl) FileExists(path string) bool { 151 return util.Exists(path) 152 } 153 154 // IdentityProvidersEnabled returns true if any of the cosign 155 // identity providers is able to obteain an OIDC identity token 156 // suitable for keyless signing, 157 func (*defaultImpl) IdentityProvidersEnabled(ctx context.Context) bool { 158 return providers.Enabled(ctx) 159 } 160 161 func (*defaultImpl) ParseReference( 162 s string, opts ...name.Option, 163 ) (name.Reference, error) { 164 return name.ParseReference(s, opts...) 165 } 166 167 func (d *defaultImpl) FindTlogEntry( 168 ctx context.Context, rClient *client.Rekor, b64Sig string, blobBytes []byte, pubKey []byte, 169 ) ([]models.LogEntryAnon, error) { 170 return cosign.FindTlogEntry(ctx, rClient, b64Sig, blobBytes, pubKey) 171 } 172 173 func (*defaultImpl) Digest( 174 ref string, opts ...crane.Option, 175 ) (string, error) { 176 return crane.Digest(ref, opts...) 177 } 178 179 func (*defaultImpl) PayloadBytes(blobRef string) (blobBytes []byte, err error) { 180 blobBytes, err = blob.LoadFileOrURL(blobRef) 181 if err != nil { 182 return nil, fmt.Errorf("load file or url of sign payload: %w", err) 183 } 184 return blobBytes, nil 185 } 186 187 func (*defaultImpl) NewRekorClient(rekorURL string) (*client.Rekor, error) { 188 return rekor.NewClient(rekorURL) 189 } 190 191 func (*defaultImpl) NewWithContext( 192 ctx context.Context, 193 reg name.Registry, 194 auth authn.Authenticator, 195 t http.RoundTripper, 196 scopes []string, 197 ) (http.RoundTripper, error) { 198 return transport.NewWithContext(ctx, reg, auth, t, scopes) 199 } 200 201 func (d *defaultImpl) ImagesSigned(ctx context.Context, s *Signer, refs ...string) (*sync.Map, error) { 202 return s.ImagesSigned(ctx, refs...) 203 }