From 6e3a147367caed428bc5995a6367caebd1562aa0 Mon Sep 17 00:00:00 2001 From: Morten Linderud Date: Tue, 31 Mar 2020 12:17:26 +0200 Subject: [PATCH] Implement chart registry TLS configuration * Implements --insecure-skip-tls-verify * Implements custom cert, key and ca file Solves https://github.com/helm/helm/issues/6324 Signed-off-by: Morten Linderud --- cmd/helm/chart.go | 38 +++++++++++++++-- cmd/helm/root.go | 10 ----- internal/experimental/registry/client.go | 42 ++++++++++++++++--- internal/experimental/registry/client_opts.go | 16 +++++++ 4 files changed, 87 insertions(+), 19 deletions(-) diff --git a/cmd/helm/chart.go b/cmd/helm/chart.go index adc874cfe..feb99e048 100644 --- a/cmd/helm/chart.go +++ b/cmd/helm/chart.go @@ -20,6 +20,7 @@ import ( "github.com/spf13/cobra" + "helm.sh/helm/v3/internal/experimental/registry" "helm.sh/helm/v3/pkg/action" ) @@ -30,6 +31,11 @@ The subcommands can be used to push, pull, tag, list, or remove Helm charts. ` func newChartCmd(cfg *action.Configuration, out io.Writer) *cobra.Command { + var certFile string + var keyFile string + var caFile string + var insecureSkipTLSverify bool + cmd := &cobra.Command{ Use: "chart", Short: "push, pull, tag, or remove Helm charts", @@ -37,13 +43,39 @@ func newChartCmd(cfg *action.Configuration, out io.Writer) *cobra.Command { Hidden: !FeatureGateOCI.IsEnabled(), PersistentPreRunE: checkOCIFeatureGate(), } - cmd.AddCommand( - newChartListCmd(cfg, out), - newChartExportCmd(cfg, out), + + Cmds := []*cobra.Command{ newChartPullCmd(cfg, out), newChartPushCmd(cfg, out), newChartRemoveCmd(cfg, out), + } + + for _, subCmd := range Cmds { + f := subCmd.Flags() + f.StringVar(&certFile, "cert-file", "", "identify HTTPS client using this SSL certificate file") + f.StringVar(&keyFile, "key-file", "", "identify HTTPS client using this SSL key file") + f.StringVar(&caFile, "ca-file", "", "verify certificates of HTTPS-enabled servers using this CA bundle") + f.BoolVar(&insecureSkipTLSverify, "insecure-skip-tls-verify", false, "skip tls certificate checks for the repository") + subCmd.PreRunE = func(cmd *cobra.Command, args []string) error { + registryClient, err := registry.NewClient( + registry.ClientOptDebug(settings.Debug), + registry.ClientOptWriter(out), + registry.ClientOptInsecureClient(insecureSkipTLSverify), + registry.ClientOptTLSConfig(certFile, keyFile, caFile), + ) + if err != nil { + return err + } + cfg.RegistryClient = registryClient + return nil + } + } + // These commands does not need to have an initialized RegistryClient + cmd.AddCommand( + newChartListCmd(cfg, out), + newChartExportCmd(cfg, out), newChartSaveCmd(cfg, out), ) + cmd.AddCommand(Cmds...) return cmd } diff --git a/cmd/helm/root.go b/cmd/helm/root.go index 3ebea3bae..6292514d8 100644 --- a/cmd/helm/root.go +++ b/cmd/helm/root.go @@ -27,7 +27,6 @@ import ( "k8s.io/client-go/tools/clientcmd" "helm.sh/helm/v3/internal/completion" - "helm.sh/helm/v3/internal/experimental/registry" "helm.sh/helm/v3/pkg/action" ) @@ -172,15 +171,6 @@ func newRootCmd(actionConfig *action.Configuration, out io.Writer, args []string ) // Add *experimental* subcommands - registryClient, err := registry.NewClient( - registry.ClientOptDebug(settings.Debug), - registry.ClientOptWriter(out), - ) - if err != nil { - // TODO: don't panic here, refactor newRootCmd to return error - panic(err) - } - actionConfig.RegistryClient = registryClient cmd.AddCommand( newRegistryCmd(actionConfig, out), newChartCmd(actionConfig, out), diff --git a/internal/experimental/registry/client.go b/internal/experimental/registry/client.go index f664c9f38..390535154 100644 --- a/internal/experimental/registry/client.go +++ b/internal/experimental/registry/client.go @@ -18,6 +18,7 @@ package registry // import "helm.sh/helm/v3/internal/experimental/registry" import ( "context" + "crypto/tls" "fmt" "io" "io/ioutil" @@ -30,6 +31,7 @@ import ( ocispec "github.com/opencontainers/image-spec/specs-go/v1" "github.com/pkg/errors" + "helm.sh/helm/v3/internal/tlsutil" "helm.sh/helm/v3/pkg/chart" "helm.sh/helm/v3/pkg/helmpath" ) @@ -42,11 +44,15 @@ const ( type ( // Client works with OCI-compliant registries and local Helm chart cache Client struct { - debug bool - out io.Writer - authorizer *Authorizer - resolver *Resolver - cache *Cache + debug bool + certFile string + keyFile string + caFile string + insecureSkipTLSverify bool + out io.Writer + authorizer *Authorizer + resolver *Resolver + cache *Cache } ) @@ -69,8 +75,32 @@ func NewClient(opts ...ClientOption) (*Client, error) { Client: authClient, } } + + httpClient := http.DefaultClient + + if (client.certFile != "" && client.keyFile != "") || client.caFile != "" { + tlsConf, err := tlsutil.NewClientTLS(client.certFile, client.keyFile, client.caFile) + if err != nil { + return nil, errors.Wrap(err, "can't create TLS config for client") + } + tlsConf.BuildNameToCertificate() + + httpClient = &http.Client{ + Transport: &http.Transport{ + TLSClientConfig: tlsConf, + Proxy: http.ProxyFromEnvironment, + }, + } + } else if client.insecureSkipTLSverify { + httpClient.Transport = &http.Transport{ + TLSClientConfig: &tls.Config{ + InsecureSkipVerify: true, + }, + Proxy: http.ProxyFromEnvironment, + } + } if client.resolver == nil { - resolver, err := client.authorizer.Resolver(context.Background(), http.DefaultClient, false) + resolver, err := client.authorizer.Resolver(context.Background(), httpClient, false) if err != nil { return nil, err } diff --git a/internal/experimental/registry/client_opts.go b/internal/experimental/registry/client_opts.go index cd295813a..c08ffe301 100644 --- a/internal/experimental/registry/client_opts.go +++ b/internal/experimental/registry/client_opts.go @@ -60,3 +60,19 @@ func ClientOptCache(cache *Cache) ClientOption { client.cache = cache } } + +// ClientOptInsecure returns a function that sets the insecure tls verify setting on a client options set +func ClientOptInsecureClient(insecureSkipTLSverify bool) ClientOption { + return func(client *Client) { + client.insecureSkipTLSverify = insecureSkipTLSverify + } +} + +// ClientOptTLSConfig returns a function that sets the tls settings on a client options set +func ClientOptTLSConfig(certFile, keyFile, caFile string) ClientOption { + return func(client *Client) { + client.certFile = certFile + client.keyFile = keyFile + client.caFile = caFile + } +}