diff --git a/cmd/cosign/cli/options/registry.go b/cmd/cosign/cli/options/registry.go index 61edb47d48b..27ed3be041e 100644 --- a/cmd/cosign/cli/options/registry.go +++ b/cmd/cosign/cli/options/registry.go @@ -17,10 +17,12 @@ package options import ( "context" "crypto/tls" + "crypto/x509" "errors" "fmt" "io" "net/http" + "os" ecr "github.com/awslabs/amazon-ecr-credential-helper/ecr-login" "github.com/chrismellard/docker-credential-acr-env/pkg/credhelper" @@ -45,6 +47,10 @@ type RegistryOptions struct { RefOpts ReferenceOptions Keychain Keychain AuthConfig authn.AuthConfig + RegistryCACert string + RegistryClientCert string + RegistryClientKey string + RegistryServerName string // RegistryClientOpts allows overriding the result of GetRegistryClientOpts. RegistryClientOpts []remote.Option @@ -72,6 +78,18 @@ func (o *RegistryOptions) AddFlags(cmd *cobra.Command) { cmd.Flags().StringVar(&o.AuthConfig.RegistryToken, "registry-token", "", "registry bearer auth token") + cmd.Flags().StringVar(&o.RegistryCACert, "registry-cacert", "", + "path to the X.509 CA certificate file in PEM format to be used for the connection to the registry") + + cmd.Flags().StringVar(&o.RegistryClientCert, "registry-client-cert", "", + "path to the X.509 certificate file in PEM format to be used for the connection to the registry") + + cmd.Flags().StringVar(&o.RegistryClientKey, "registry-client-key", "", + "path to the X.509 private key file in PEM format to be used, together with the 'registry-client-cert' value, for the connection to the registry") + + cmd.Flags().StringVar(&o.RegistryServerName, "registry-server-name", "", + "SAN name to use as the 'ServerName' tls.Config field to verify the mTLS connection to the registry") + o.RefOpts.AddFlags(cmd) } @@ -131,8 +149,9 @@ func (o *RegistryOptions) GetRegistryClientOpts(ctx context.Context) []remote.Op opts = append(opts, remote.WithAuthFromKeychain(authn.DefaultKeychain)) } - if o.AllowInsecure { - opts = append(opts, remote.WithTransport(&http.Transport{TLSClientConfig: &tls.Config{InsecureSkipVerify: true}})) // #nosec G402 + tlsConfig, err := o.getTLSConfig() + if err == nil { + opts = append(opts, remote.WithTransport(&http.Transport{TLSClientConfig: tlsConfig})) } // Reuse a remote.Pusher and a remote.Puller for all operations that use these opts. @@ -193,3 +212,41 @@ func (o *RegistryExperimentalOptions) AddFlags(cmd *cobra.Command) { cmd.Flags().Var(&o.RegistryReferrersMode, "registry-referrers-mode", "mode for fetching references from the registry. allowed: legacy, oci-1-1") } + +func (o *RegistryOptions) getTLSConfig() (*tls.Config, error) { + var tlsConfig tls.Config + + if o.RegistryCACert != "" { + f, err := os.Open(o.RegistryCACert) + if err != nil { + return nil, err + } + defer f.Close() + caCertBytes, err := io.ReadAll(f) + if err != nil { + return nil, fmt.Errorf("unable to read CA certs from %s: %w", o.RegistryCACert, err) + } + pool := x509.NewCertPool() + if !pool.AppendCertsFromPEM(caCertBytes) { + return nil, fmt.Errorf("no valid CA certs found in %s", o.RegistryCACert) + } + tlsConfig.RootCAs = pool + } + + if o.RegistryClientCert != "" && o.RegistryClientKey != "" { + cert, err := tls.LoadX509KeyPair(o.RegistryClientCert, o.RegistryClientKey) + if err != nil { + return nil, fmt.Errorf("unable to read CA certs from cert %s, key %s: %w", + o.RegistryClientCert, o.RegistryClientKey, err) + } + tlsConfig.Certificates = []tls.Certificate{cert} + } + + if o.RegistryServerName != "" { + tlsConfig.ServerName = o.RegistryServerName + } + + tlsConfig.InsecureSkipVerify = o.AllowInsecure // #nosec G402 + + return &tlsConfig, nil +}