Repository: helm/helm (go)

Issue:
helm 3.0.0-beta4: ca-file does not work for helm repo add/update
We are using an internal helm repository with a certificate signed by our internal company CA.
When using this repository in helm 3.0.0-beta4 I get this x509 error:

```
$ helm repo add name https://repourl --ca-file /path/to/ca.pem
Error: looks like "https://repourl" is not a valid chart repository or cannot be reached: Get https://repourl/index.yaml: x509: certificate signed by unknown authority
```

The same happens when I migrate my existing helm v2 repository config and call `helm repo update`.

Executing the exact same commands in helm v2.14.3 with the same repository configuration and same ca.pem file is working fine.

Output of `helm version`:
```
version.BuildInfo{Version:"v3.0.0-beta.4", GitCommit:"7ffc879f137bd3a69eea53349b01f05e3d1d2385", GitTreeState:"dirty", GoVersion:"go1.13.1"}
```

Pull request:
fix(tlsutil): accept only a CA certificate for validation
closes #6635.

Signed-off-by: Matthew Fisher <matt.fisher@microsoft.com>

Patch:
diff --git a/internal/tlsutil/tls.go b/internal/tlsutil/tls.go
index dc123e1e5..ed7795dbe 100644
--- a/internal/tlsutil/tls.go
+++ b/internal/tlsutil/tls.go
@@ -26,13 +26,16 @@ import (
 
 // NewClientTLS returns tls.Config appropriate for client auth.
 func NewClientTLS(certFile, keyFile, caFile string) (*tls.Config, error) {
-	cert, err := CertFromFilePair(certFile, keyFile)
-	if err != nil {
-		return nil, err
-	}
-	config := tls.Config{
-		Certificates: []tls.Certificate{*cert},
+	config := tls.Config{}
+
+	if certFile != "" && keyFile != "" {
+		cert, err := CertFromFilePair(certFile, keyFile)
+		if err != nil {
+			return nil, err
+		}
+		config.Certificates = []tls.Certificate{*cert}
 	}
+
 	if caFile != "" {
 		cp, err := CertPoolFromFile(caFile)
 		if err != nil {
@@ -40,6 +43,7 @@ func NewClientTLS(certFile, keyFile, caFile string) (*tls.Config, error) {
 		}
 		config.RootCAs = cp
 	}
+
 	return &config, nil
 }
 
diff --git a/pkg/getter/httpgetter.go b/pkg/getter/httpgetter.go
index a36ab0321..89abfb1cf 100644
--- a/pkg/getter/httpgetter.go
+++ b/pkg/getter/httpgetter.go
@@ -89,7 +89,7 @@ func NewHTTPGetter(options ...Option) (Getter, error) {
 }
 
 func (g *HTTPGetter) httpClient() (*http.Client, error) {
-	if g.opts.certFile != "" && g.opts.keyFile != "" {
+	if (g.opts.certFile != "" && g.opts.keyFile != "") || g.opts.caFile != "" {
 		tlsConf, err := tlsutil.NewClientTLS(g.opts.certFile, g.opts.keyFile, g.opts.caFile)
 		if err != nil {
 			return nil, errors.Wrap(err, "can't create TLS config for client")


Test patch:
diff --git a/internal/tlsutil/tlsutil_test.go b/internal/tlsutil/tlsutil_test.go
index c2a477272..a737226fd 100644
--- a/internal/tlsutil/tlsutil_test.go
+++ b/internal/tlsutil/tlsutil_test.go
@@ -81,3 +81,54 @@ func testfile(t *testing.T, file string) (path string) {
 	}
 	return path
 }
+
+func TestNewClientTLS(t *testing.T) {
+	certFile := testfile(t, testCertFile)
+	keyFile := testfile(t, testKeyFile)
+	caCertFile := testfile(t, testCaCertFile)
+
+	cfg, err := NewClientTLS(certFile, keyFile, caCertFile)
+	if err != nil {
+		t.Error(err)
+	}
+
+	if got := len(cfg.Certificates); got != 1 {
+		t.Fatalf("expecting 1 client certificates, got %d", got)
+	}
+	if cfg.InsecureSkipVerify {
+		t.Fatalf("insecure skip verify mistmatch, expecting false")
+	}
+	if cfg.RootCAs == nil {
+		t.Fatalf("mismatch tls RootCAs, expecting non-nil")
+	}
+
+	cfg, err = NewClientTLS("", "", caCertFile)
+	if err != nil {
+		t.Error(err)
+	}
+
+	if got := len(cfg.Certificates); got != 0 {
+		t.Fatalf("expecting 0 client certificates, got %d", got)
+	}
+	if cfg.InsecureSkipVerify {
+		t.Fatalf("insecure skip verify mistmatch, expecting false")
+	}
+	if cfg.RootCAs == nil {
+		t.Fatalf("mismatch tls RootCAs, expecting non-nil")
+	}
+
+	cfg, err = NewClientTLS(certFile, keyFile, "")
+	if err != nil {
+		t.Error(err)
+	}
+
+	if got := len(cfg.Certificates); got != 1 {
+		t.Fatalf("expecting 1 client certificates, got %d", got)
+	}
+	if cfg.InsecureSkipVerify {
+		t.Fatalf("insecure skip verify mistmatch, expecting false")
+	}
+	if cfg.RootCAs != nil {
+		t.Fatalf("mismatch tls RootCAs, expecting nil")
+	}
+}
diff --git a/pkg/getter/httpgetter_test.go b/pkg/getter/httpgetter_test.go
index 0c2c55ea3..b20085574 100644
--- a/pkg/getter/httpgetter_test.go
+++ b/pkg/getter/httpgetter_test.go
@@ -180,4 +180,14 @@ func TestDownloadTLS(t *testing.T) {
 	if _, err := g.Get(u.String(), WithURL(u.String()), WithTLSClientConfig(pub, priv, ca)); err != nil {
 		t.Error(err)
 	}
+
+	// test with only the CA file (see also #6635)
+	g, err = NewHTTPGetter()
+	if err != nil {
+		t.Fatal(err)
+	}
+
+	if _, err := g.Get(u.String(), WithURL(u.String()), WithTLSClientConfig("", "", ca)); err != nil {
+		t.Error(err)
+	}
 }