From fc30204cf3a612240292f82269dcfcdf3eb8583b Mon Sep 17 00:00:00 2001 From: suryatech27-cloud Date: Mon, 21 Mar 2022 15:29:41 +0530 Subject: [PATCH] handshake failure(2-way ssl support for oci pull) Signed-off-by: suryatech27-cloud --- internal/tlsutil/cfg.go | 90 ++++++++++++++++++++++++++++++++++++++++ pkg/action/pull.go | 18 +++++++- pkg/registry/client.go | 92 +++++++++++++++++++++++++++++++++++++++++ 3 files changed, 199 insertions(+), 1 deletion(-) diff --git a/internal/tlsutil/cfg.go b/internal/tlsutil/cfg.go index 8b9d4329f..ef0b8e18e 100644 --- a/internal/tlsutil/cfg.go +++ b/internal/tlsutil/cfg.go @@ -19,7 +19,11 @@ package tlsutil import ( "crypto/tls" "crypto/x509" + "fmt" + "io/ioutil" "os" + "path/filepath" + "runtime" "github.com/pkg/errors" ) @@ -56,3 +60,89 @@ func ClientConfig(opts Options) (cfg *tls.Config, err error) { cfg = &tls.Config{InsecureSkipVerify: opts.InsecureSkipVerify, Certificates: []tls.Certificate{*cert}, RootCAs: pool} return cfg, nil } + +func ReadCertFromSecDir(cfgFileBaseName string, host string) (opts Options, err error) { + if runtime.GOOS == "windows" || runtime.GOOS == "unix" { + fmt.Printf("%v OS not supported for this oci pull. Contact your administrator for more information !!!", runtime.GOOS) + } else { + var clientCertDir = "/etc/docker/certs.d/" + /* var fileName = helmpath.ConfigPath(cfgFileBaseName) + data, err := ioutil.ReadFile(fileName) + + if err != nil { + fmt.Printf("Config file not exist %v\n", err) + } + + var configDataMap map[string]map[string]interface{} + err = json.Unmarshal(data, &configDataMap) + + if err != nil { + fmt.Printf("Please do login before initiating the pull : %v", err) + } + + keys := reflect.ValueOf(configDataMap["auths"]).MapKeys() + strkeys := make([]string, len(keys)) + for i := 0; i < len(keys); i++ { + strkeys[i] = keys[i].String() + } + //fmt.Print(strings.Join(strkeys, ",")) + strkey := strings.Join(strkeys, "") */ + clientCertDir = clientCertDir + host + + if _, err := os.Stat(clientCertDir); err != nil { + if os.IsNotExist(err) { + os.MkdirAll(clientCertDir, os.ModePerm) + return opts, errors.Wrapf(err, "Client Certificate Directory Not Exist !! \n %v Directory created.", clientCertDir) + } + } else { + + if files, err := ioutil.ReadDir(clientCertDir); err == nil { + for _, file := range files { + if filepath.Ext(file.Name()) == ".crt" { + opts.CaCertFile = clientCertDir + "/" + file.Name() + //fmt.Printf("cafile: %v\n", opts.CaCertFile) + } else if filepath.Ext(file.Name()) == ".pem" { + opts.CertFile = clientCertDir + "/" + file.Name() + //fmt.Printf("certFile: %v\n", opts.CertFile) + } else if filepath.Ext(file.Name()) == ".key" { + opts.KeyFile = clientCertDir + "/" + file.Name() + //fmt.Printf("keyFile: %v\n", opts.KeyFile) + } + } + } else { + fmt.Printf(" Certificate not found in current directory - %v\n ", err) + os.Exit(1) + } + if opts.CaCertFile == "" && opts.CertFile == "" && opts.KeyFile == "" { + fmt.Printf("Error Certificate (cacerts.crt,client.pem,client.key) required : Client authentication failed due to certificate not present in cert directory !! \n") + os.Exit(1) + } + + if opts.CaCertFile == "" && opts.CertFile == "" { + fmt.Printf("Error Certificate Required : Root-CA and client certificate (cacerts.crt,client.pem) not found.\n") + os.Exit(1) + } + + if opts.CaCertFile == "" && opts.KeyFile == "" { + fmt.Printf("Error Certificate Required : Root-CA and and client keyfie (cacerts.crt,client.key) not found.\n") + os.Exit(1) + } + + if opts.CertFile == "" && opts.KeyFile == "" { + fmt.Printf("Error Certificate Required : Client certificate and client keyfile (client.pem,client.key) not found.\n") + os.Exit(1) + } + if opts.CaCertFile == "" { + fmt.Printf("Error Certificate Required : Client Root-CA (cacerts.crt) not found.\n") + os.Exit(1) + } else if opts.CertFile == "" { + fmt.Printf("Error Certificate Required : Client certificate(client.pem) not found.\n") + os.Exit(1) + } else if opts.KeyFile == "" { + fmt.Printf("Error Certificate Required : Client keyfile (client.key) not found.\n") + os.Exit(1) + } + } + } + return opts, nil +} diff --git a/pkg/action/pull.go b/pkg/action/pull.go index b4018869e..cf0a6ab40 100644 --- a/pkg/action/pull.go +++ b/pkg/action/pull.go @@ -125,7 +125,23 @@ func (p *Pull) Run(chartRef string) (string, error) { saved, v, err := c.DownloadTo(chartRef, p.Version, dest) if err != nil { - return out.String(), err + registryClient, err := registry.NewCrosClient(chartRef, + registry.ClientOptDebug(p.Settings.Debug), + registry.ClientOptCredentialsFile(p.Settings.RegistryConfig), + registry.ClientOptWriter(&out), + ) + if err != nil { + return out.String(), err + } + c.Options = append(c.Options, + getter.WithRegistryClient(registryClient), + getter.WithTagName(p.Version)) + + saved, v, err = c.DownloadTo(chartRef, p.Version, dest) + if err != nil { + return out.String(), err + } + } if p.Verify { diff --git a/pkg/registry/client.go b/pkg/registry/client.go index 0107136d3..89401a978 100644 --- a/pkg/registry/client.go +++ b/pkg/registry/client.go @@ -22,9 +22,11 @@ import ( "fmt" "io" "io/ioutil" + "net" "net/http" "sort" "strings" + "time" "github.com/Masterminds/semver/v3" "github.com/containerd/containerd/remotes" @@ -38,6 +40,8 @@ import ( registryremote "oras.land/oras-go/pkg/registry/remote" registryauth "oras.land/oras-go/pkg/registry/remote/auth" + "helm.sh/helm/v3/internal/tlsutil" + "helm.sh/helm/v3/internal/urlutil" "helm.sh/helm/v3/internal/version" "helm.sh/helm/v3/pkg/chart" "helm.sh/helm/v3/pkg/helmpath" @@ -131,6 +135,94 @@ func NewClient(options ...ClientOption) (*Client, error) { return client, nil } +func NewCrosClient(chartref string, options ...ClientOption) (*Client, error) { + client := &Client{ + out: ioutil.Discard, + } + for _, option := range options { + option(client) + } + if client.credentialsFile == "" { + client.credentialsFile = helmpath.ConfigPath(CredentialsFileBasename) + } + if client.authorizer == nil { + authClient, err := dockerauth.NewClientWithDockerFallback(client.credentialsFile) + if err != nil { + return nil, err + } + client.authorizer = authClient + } + if client.resolver == nil { + host, err := urlutil.ExtractHostname(chartref) + if err != nil { + + } + clientOpts, err := tlsutil.ReadCertFromSecDir(CredentialsFileBasename, host) + if err != nil { + return client, errors.Wrapf(err, "Client certificate/directory Not Exist !!") + } + cfgtls, err := tlsutil.ClientConfig(clientOpts) + if err != nil { + fmt.Printf("error :%v\n", err) + + } + var rt http.RoundTripper = &http.Transport{ + Dial: (&net.Dialer{ + Timeout: 30 * time.Second, + KeepAlive: 30 * time.Second, + }).Dial, + TLSHandshakeTimeout: 30 * time.Second, + TLSClientConfig: cfgtls, + ResponseHeaderTimeout: time.Duration(3 * time.Second), + DisableKeepAlives: true, + } + crosclient := http.Client{Transport: rt, Timeout: 30 * time.Second} + headers := http.Header{} + headers.Set("User-Agent", version.GetUserAgent()) + opts := []auth.ResolverOption{auth.WithResolverHeaders(headers), auth.WithResolverClient(&crosclient)} + resolver, err := client.authorizer.ResolverWithOpts(opts...) + if err != nil { + return nil, err + } + client.resolver = resolver + } + + if client.registryAuthorizer == nil { + client.registryAuthorizer = ®istryauth.Client{ + Header: http.Header{ + "User-Agent": {version.GetUserAgent()}, + }, + Cache: registryauth.DefaultCache, + Credential: func(ctx context.Context, reg string) (registryauth.Credential, error) { + dockerClient, ok := client.authorizer.(*dockerauth.Client) + if !ok { + return registryauth.EmptyCredential, errors.New("unable to obtain docker client") + } + + username, password, err := dockerClient.Credential(reg) + if err != nil { + return registryauth.EmptyCredential, errors.New("unable to retrieve credentials") + } + + // A blank returned username and password value is a bearer token + if username == "" && password != "" { + return registryauth.Credential{ + RefreshToken: password, + }, nil + } + + return registryauth.Credential{ + Username: username, + Password: password, + }, nil + + }, + } + + } + return client, nil +} + // ClientOptDebug returns a function that sets the debug setting on client options set func ClientOptDebug(debug bool) ClientOption { return func(client *Client) {