diff --git a/SPECS/helm/CVE-2021-32690.patch b/SPECS/helm/CVE-2021-32690.patch new file mode 100644 index 0000000000..5e33c0ece7 --- /dev/null +++ b/SPECS/helm/CVE-2021-32690.patch @@ -0,0 +1,697 @@ +From 61d8e8c4a6f95540c15c6a65f36a6dd0a45e7a2f Mon Sep 17 00:00:00 2001 +From: Matt Farina +Date: Fri, 11 Jun 2021 14:36:55 -0400 +Subject: [PATCH] tweak basic handling + +Backported to helm 3.4.1 + +Signed-off-by: Matt Farina +Signed-off-by: Henry Beberman + +(cherry picked from commit 17ed9c4cd3c61290587a22953e5359af5ecccaa2) + +diff -Naur a/cmd/helm/flags.go b/cmd/helm/flags.go +--- a/cmd/helm/flags.go 2020-11-11 11:44:01.000000000 -0800 ++++ b/cmd/helm/flags.go 2021-09-20 15:58:38.405158596 -0700 +@@ -56,6 +56,7 @@ + f.StringVar(&c.KeyFile, "key-file", "", "identify HTTPS client using this SSL key file") + f.BoolVar(&c.InsecureSkipTLSverify, "insecure-skip-tls-verify", false, "skip tls certificate checks for the chart download") + f.StringVar(&c.CaFile, "ca-file", "", "verify certificates of HTTPS-enabled servers using this CA bundle") ++ f.BoolVar(&c.PassCredentialsAll, "pass-credentials", false, "pass credentials to all domains") + } + + // bindOutputFlag will add the output flag to the given command and bind the +diff -Naur a/cmd/helm/install_test.go b/cmd/helm/install_test.go +--- a/cmd/helm/install_test.go 2020-11-11 11:44:01.000000000 -0800 ++++ b/cmd/helm/install_test.go 2021-09-20 15:58:38.405158596 -0700 +@@ -18,10 +18,36 @@ + + import ( + "fmt" ++ "net/http" ++ "net/http/httptest" + "testing" ++ ++ "helm.sh/helm/v3/pkg/repo/repotest" + ) + + func TestInstall(t *testing.T) { ++ srv, err := repotest.NewTempServerWithCleanup(t, "testdata/testcharts/*.tgz*") ++ if err != nil { ++ t.Fatal(err) ++ } ++ defer srv.Stop() ++ ++ srv.WithMiddleware(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { ++ username, password, ok := r.BasicAuth() ++ if !ok || username != "username" || password != "password" { ++ t.Errorf("Expected request to use basic auth and for username == 'username' and password == 'password', got '%v', '%s', '%s'", ok, username, password) ++ } ++ })) ++ ++ srv2 := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { ++ http.FileServer(http.Dir(srv.Root())).ServeHTTP(w, r) ++ })) ++ defer srv2.Close() ++ ++ if err := srv.LinkIndices(); err != nil { ++ t.Fatal(err) ++ } ++ + tests := []cmdTestCase{ + // Install, base case + { +@@ -201,6 +227,17 @@ + name: "install chart with only crds", + cmd: "install crd-test testdata/testcharts/chart-with-only-crds --namespace default", + }, ++ // Verify the user/pass works ++ { ++ name: "basic install with credentials", ++ cmd: "install aeneas reqtest --namespace default --repo " + srv.URL() + " --username username --password password", ++ golden: "output/install.txt", ++ }, ++ { ++ name: "basic install with credentials", ++ cmd: "install aeneas reqtest --namespace default --repo " + srv2.URL + " --username username --password password --pass-credentials", ++ golden: "output/install.txt", ++ }, + } + + runTestActionCmd(t, tests) +diff -Naur a/cmd/helm/pull_test.go b/cmd/helm/pull_test.go +--- a/cmd/helm/pull_test.go 2020-11-11 11:44:01.000000000 -0800 ++++ b/cmd/helm/pull_test.go 2021-09-20 15:58:38.405158596 -0700 +@@ -18,6 +18,8 @@ + + import ( + "fmt" ++ "net/http" ++ "net/http/httptest" + "os" + "path/filepath" + "testing" +@@ -185,6 +187,115 @@ + } + + ef := filepath.Join(outdir, tt.expectFile) ++ fi, err := os.Stat(ef) ++ if err != nil { ++ t.Errorf("%q: expected a file at %s. %s", tt.name, ef, err) ++ } ++ if fi.IsDir() != tt.expectDir { ++ t.Errorf("%q: expected directory=%t, but it's not.", tt.name, tt.expectDir) ++ } ++ }) ++ } ++} ++ ++func TestPullWithCredentialsCmd(t *testing.T) { ++ srv, err := repotest.NewTempServerWithCleanup(t, "testdata/testcharts/*.tgz*") ++ if err != nil { ++ t.Fatal(err) ++ } ++ defer srv.Stop() ++ ++ srv.WithMiddleware(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { ++ username, password, ok := r.BasicAuth() ++ if !ok || username != "username" || password != "password" { ++ t.Errorf("Expected request to use basic auth and for username == 'username' and password == 'password', got '%v', '%s', '%s'", ok, username, password) ++ } ++ })) ++ ++ srv2 := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { ++ http.FileServer(http.Dir(srv.Root())).ServeHTTP(w, r) ++ })) ++ defer srv2.Close() ++ ++ if err := srv.LinkIndices(); err != nil { ++ t.Fatal(err) ++ } ++ ++ // all flags will get "-d outdir" appended. ++ tests := []struct { ++ name string ++ args string ++ existFile string ++ existDir string ++ wantError bool ++ wantErrorMsg string ++ expectFile string ++ expectDir bool ++ }{ ++ { ++ name: "Chart fetch using repo URL", ++ expectFile: "./signtest-0.1.0.tgz", ++ args: "signtest --repo " + srv.URL() + " --username username --password password", ++ }, ++ { ++ name: "Fail fetching non-existent chart on repo URL", ++ args: "someChart --repo " + srv.URL() + " --username username --password password", ++ wantError: true, ++ }, ++ { ++ name: "Specific version chart fetch using repo URL", ++ expectFile: "./signtest-0.1.0.tgz", ++ args: "signtest --version=0.1.0 --repo " + srv.URL() + " --username username --password password", ++ }, ++ { ++ name: "Specific version chart fetch using repo URL", ++ args: "signtest --version=0.2.0 --repo " + srv.URL() + " --username username --password password", ++ wantError: true, ++ }, ++ { ++ name: "Chart located on different domain with credentials passed", ++ args: "reqtest --repo " + srv2.URL + " --username username --password password --pass-credentials", ++ expectFile: "./reqtest-0.1.0.tgz", ++ }, ++ } ++ ++ for _, tt := range tests { ++ t.Run(tt.name, func(t *testing.T) { ++ outdir := srv.Root() ++ cmd := fmt.Sprintf("pull %s -d '%s' --repository-config %s --repository-cache %s --registry-config %s", ++ tt.args, ++ outdir, ++ filepath.Join(outdir, "repositories.yaml"), ++ outdir, ++ filepath.Join(outdir, "config.json"), ++ ) ++ // Create file or Dir before helm pull --untar, see: https://github.com/helm/helm/issues/7182 ++ if tt.existFile != "" { ++ file := filepath.Join(outdir, tt.existFile) ++ _, err := os.Create(file) ++ if err != nil { ++ t.Fatal(err) ++ } ++ } ++ if tt.existDir != "" { ++ file := filepath.Join(outdir, tt.existDir) ++ err := os.Mkdir(file, 0755) ++ if err != nil { ++ t.Fatal(err) ++ } ++ } ++ _, _, err := executeActionCommand(cmd) ++ if err != nil { ++ if tt.wantError { ++ if tt.wantErrorMsg != "" && tt.wantErrorMsg == err.Error() { ++ t.Fatalf("Actual error %s, not equal to expected error %s", err, tt.wantErrorMsg) ++ } ++ return ++ } ++ t.Fatalf("%q reported error: %s", tt.name, err) ++ } ++ ++ ef := filepath.Join(outdir, tt.expectFile) + fi, err := os.Stat(ef) + if err != nil { + t.Errorf("%q: expected a file at %s. %s", tt.name, ef, err) +diff -Naur a/cmd/helm/repo_add.go b/cmd/helm/repo_add.go +--- a/cmd/helm/repo_add.go 2020-11-11 11:44:01.000000000 -0800 ++++ b/cmd/helm/repo_add.go 2021-09-20 15:58:38.405158596 -0700 +@@ -48,6 +48,7 @@ + url string + username string + password string ++ passCredentialsAll bool + forceUpdate bool + allowDeprecatedRepos bool + +@@ -91,6 +92,7 @@ + f.StringVar(&o.caFile, "ca-file", "", "verify certificates of HTTPS-enabled servers using this CA bundle") + f.BoolVar(&o.insecureSkipTLSverify, "insecure-skip-tls-verify", false, "skip tls certificate checks for the repository") + f.BoolVar(&o.allowDeprecatedRepos, "allow-deprecated-repos", false, "by default, this command will not allow adding official repos that have been permanently deleted. This disables that behavior") ++ f.BoolVar(&o.passCredentialsAll, "pass-credentials", false, "pass credentials to all domains") + + return cmd + } +@@ -149,6 +151,7 @@ + URL: o.url, + Username: o.username, + Password: o.password, ++ PassCredentialsAll: o.passCredentialsAll, + CertFile: o.certFile, + KeyFile: o.keyFile, + CAFile: o.caFile, +diff -Naur a/pkg/action/install.go b/pkg/action/install.go +--- a/pkg/action/install.go 2020-11-11 11:44:01.000000000 -0800 ++++ b/pkg/action/install.go 2021-09-20 15:58:38.405158596 -0700 +@@ -20,6 +20,7 @@ + "bytes" + "fmt" + "io/ioutil" ++ "net/url" + "os" + "path" + "path/filepath" +@@ -110,6 +111,7 @@ + InsecureSkipTLSverify bool // --insecure-skip-verify + Keyring string // --keyring + Password string // --password ++ PassCredentialsAll bool // --pass-credentials + RepoURL string // --repo + Username string // --username + Verify bool // --verify +@@ -643,7 +645,7 @@ + Keyring: c.Keyring, + Getters: getter.All(settings), + Options: []getter.Option{ +- getter.WithBasicAuth(c.Username, c.Password), ++ getter.WithPassCredentialsAll(c.PassCredentialsAll), + getter.WithTLSClientConfig(c.CertFile, c.KeyFile, c.CaFile), + getter.WithInsecureSkipVerifyTLS(c.InsecureSkipTLSverify), + }, +@@ -654,12 +656,34 @@ + dl.Verify = downloader.VerifyAlways + } + if c.RepoURL != "" { +- chartURL, err := repo.FindChartInAuthAndTLSRepoURL(c.RepoURL, c.Username, c.Password, name, version, +- c.CertFile, c.KeyFile, c.CaFile, c.InsecureSkipTLSverify, getter.All(settings)) ++ chartURL, err := repo.FindChartInAuthAndTLSAndPassRepoURL(c.RepoURL, c.Username, c.Password, name, version, ++ c.CertFile, c.KeyFile, c.CaFile, c.InsecureSkipTLSverify, c.PassCredentialsAll, getter.All(settings)) + if err != nil { + return "", err + } + name = chartURL ++ ++ // Only pass the user/pass on when the user has said to or when the ++ // location of the chart repo and the chart are the same domain. ++ u1, err := url.Parse(c.RepoURL) ++ if err != nil { ++ return "", err ++ } ++ u2, err := url.Parse(chartURL) ++ if err != nil { ++ return "", err ++ } ++ ++ // Host on URL (returned from url.Parse) contains the port if present. ++ // This check ensures credentials are not passed between different ++ // services on different ports. ++ if c.PassCredentialsAll || (u1.Scheme == u2.Scheme && u1.Host == u2.Host) { ++ dl.Options = append(dl.Options, getter.WithBasicAuth(c.Username, c.Password)) ++ } else { ++ dl.Options = append(dl.Options, getter.WithBasicAuth("", "")) ++ } ++ } else { ++ dl.Options = append(dl.Options, getter.WithBasicAuth(c.Username, c.Password)) + } + + if err := os.MkdirAll(settings.RepositoryCache, 0755); err != nil { +diff -Naur a/pkg/action/pull.go b/pkg/action/pull.go +--- a/pkg/action/pull.go 2020-11-11 11:44:01.000000000 -0800 ++++ b/pkg/action/pull.go 2021-09-20 15:58:38.405158596 -0700 +@@ -63,6 +63,7 @@ + Getters: getter.All(p.Settings), + Options: []getter.Option{ + getter.WithBasicAuth(p.Username, p.Password), ++ getter.WithPassCredentialsAll(p.PassCredentialsAll), + getter.WithTLSClientConfig(p.CertFile, p.KeyFile, p.CaFile), + getter.WithInsecureSkipVerifyTLS(p.InsecureSkipTLSverify), + }, +@@ -89,7 +90,7 @@ + } + + if p.RepoURL != "" { +- chartURL, err := repo.FindChartInAuthAndTLSRepoURL(p.RepoURL, p.Username, p.Password, chartRef, p.Version, p.CertFile, p.KeyFile, p.CaFile, p.InsecureSkipTLSverify, getter.All(p.Settings)) ++ chartURL, err := repo.FindChartInAuthAndTLSAndPassRepoURL(p.RepoURL, p.Username, p.Password, chartRef, p.Version, p.CertFile, p.KeyFile, p.CaFile, p.InsecureSkipTLSverify, p.PassCredentialsAll, getter.All(p.Settings)) + if err != nil { + return out.String(), err + } +diff -Naur a/pkg/downloader/chart_downloader.go b/pkg/downloader/chart_downloader.go +--- a/pkg/downloader/chart_downloader.go 2020-11-11 11:44:01.000000000 -0800 ++++ b/pkg/downloader/chart_downloader.go 2021-09-20 15:58:38.405158596 -0700 +@@ -189,6 +189,7 @@ + c.Options = append( + c.Options, + getter.WithBasicAuth(rc.Username, rc.Password), ++ getter.WithPassCredentialsAll(rc.PassCredentialsAll), + ) + } + return u, nil +@@ -218,7 +219,10 @@ + c.Options = append(c.Options, getter.WithTLSClientConfig(r.Config.CertFile, r.Config.KeyFile, r.Config.CAFile)) + } + if r.Config.Username != "" && r.Config.Password != "" { +- c.Options = append(c.Options, getter.WithBasicAuth(r.Config.Username, r.Config.Password)) ++ c.Options = append(c.Options, ++ getter.WithBasicAuth(r.Config.Username, r.Config.Password), ++ getter.WithPassCredentialsAll(r.Config.PassCredentialsAll), ++ ) + } + } + +diff -Naur a/pkg/downloader/chart_downloader_test.go b/pkg/downloader/chart_downloader_test.go +--- a/pkg/downloader/chart_downloader_test.go 2020-11-11 11:44:01.000000000 -0800 ++++ b/pkg/downloader/chart_downloader_test.go 2021-09-20 15:58:38.405158596 -0700 +@@ -205,6 +205,7 @@ + }), + Options: []getter.Option{ + getter.WithBasicAuth("username", "password"), ++ getter.WithPassCredentialsAll(false), + }, + } + cname := "/signtest-0.1.0.tgz" +diff -Naur a/pkg/downloader/manager.go b/pkg/downloader/manager.go +--- a/pkg/downloader/manager.go 2020-11-11 11:44:01.000000000 -0800 ++++ b/pkg/downloader/manager.go 2021-09-20 16:04:28.201715831 -0700 +@@ -307,7 +307,7 @@ + + // Any failure to resolve/download a chart should fail: + // https://github.com/helm/helm/issues/1439 +- churl, username, password, err := m.findChartURL(dep.Name, dep.Version, dep.Repository, repos) ++ churl, username, password, passcredentialsall, err := m.findChartURL(dep.Name, dep.Version, dep.Repository, repos) + if err != nil { + saveError = errors.Wrapf(err, "could not find %s", churl) + break +@@ -329,6 +329,7 @@ + Getters: m.Getters, + Options: []getter.Option{ + getter.WithBasicAuth(username, password), ++ getter.WithPassCredentialsAll(passcredentialsall), + }, + } + +@@ -647,7 +648,8 @@ + // repoURL is the repository to search + // + // If it finds a URL that is "relative", it will prepend the repoURL. +-func (m *Manager) findChartURL(name, version, repoURL string, repos map[string]*repo.ChartRepository) (url, username, password string, err error) { ++func (m *Manager) findChartURL(name, version, repoURL string, repos map[string]*repo.ChartRepository) (url, username, password string, passcredentialsall bool, err error) { ++ + for _, cr := range repos { + if urlutil.Equal(repoURL, cr.Config.URL) { + var entry repo.ChartVersions +@@ -666,15 +668,16 @@ + } + username = cr.Config.Username + password = cr.Config.Password ++ passcredentialsall = cr.Config.PassCredentialsAll + return + } + } + url, err = repo.FindChartInRepoURL(repoURL, name, version, "", "", "", m.Getters) + if err == nil { +- return ++ return url, username, password, false, err + } + err = errors.Errorf("chart %s not found in %s: %s", name, repoURL, err) +- return ++ return url, username, password, false, err + } + + // findEntryByName finds an entry in the chart repository whose name matches the given name. +diff -Naur a/pkg/downloader/manager_test.go b/pkg/downloader/manager_test.go +--- a/pkg/downloader/manager_test.go 2020-11-11 11:44:01.000000000 -0800 ++++ b/pkg/downloader/manager_test.go 2021-09-20 15:58:38.405158596 -0700 +@@ -81,7 +81,7 @@ + version := "0.1.0" + repoURL := "http://example.com/charts" + +- churl, username, password, err := m.findChartURL(name, version, repoURL, repos) ++ churl, username, password, passcredentialsall, err := m.findChartURL(name, version, repoURL, repos) + if err != nil { + t.Fatal(err) + } +@@ -94,6 +94,9 @@ + if password != "" { + t.Errorf("Unexpected password %q", password) + } ++ if passcredentialsall != false { ++ t.Errorf("Unexpected passcredentialsall %t", passcredentialsall) ++ } + } + + func TestGetRepoNames(t *testing.T) { +diff -Naur a/pkg/getter/getter.go b/pkg/getter/getter.go +--- a/pkg/getter/getter.go 2020-11-11 11:44:01.000000000 -0800 ++++ b/pkg/getter/getter.go 2021-09-20 15:58:38.405158596 -0700 +@@ -36,6 +36,7 @@ + insecureSkipVerifyTLS bool + username string + password string ++ passCredentialsAll bool + userAgent string + timeout time.Duration + } +@@ -60,6 +61,12 @@ + } + } + ++func WithPassCredentialsAll(pass bool) Option { ++ return func(opts *options) { ++ opts.passCredentialsAll = pass ++ } ++} ++ + // WithUserAgent sets the request's User-Agent header to use the provided agent name. + func WithUserAgent(userAgent string) Option { + return func(opts *options) { +diff -Naur a/pkg/getter/httpgetter.go b/pkg/getter/httpgetter.go +--- a/pkg/getter/httpgetter.go 2020-11-11 11:44:01.000000000 -0800 ++++ b/pkg/getter/httpgetter.go 2021-09-20 15:58:38.405158596 -0700 +@@ -20,6 +20,7 @@ + "crypto/tls" + "io" + "net/http" ++ "net/url" + + "github.com/pkg/errors" + +@@ -56,8 +57,24 @@ + req.Header.Set("User-Agent", g.opts.userAgent) + } + +- if g.opts.username != "" && g.opts.password != "" { +- req.SetBasicAuth(g.opts.username, g.opts.password) ++ // Before setting the basic auth credentials, make sure the URL associated ++ // with the basic auth is the one being fetched. ++ u1, err := url.Parse(g.opts.url) ++ if err != nil { ++ return buf, errors.Wrap(err, "Unable to parse getter URL") ++ } ++ u2, err := url.Parse(href) ++ if err != nil { ++ return buf, errors.Wrap(err, "Unable to parse URL getting from") ++ } ++ ++ // Host on URL (returned from url.Parse) contains the port if present. ++ // This check ensures credentials are not passed between different ++ // services on different ports. ++ if g.opts.passCredentialsAll || (u1.Scheme == u2.Scheme && u1.Host == u2.Host) { ++ if g.opts.username != "" && g.opts.password != "" { ++ req.SetBasicAuth(g.opts.username, g.opts.password) ++ } + } + + client, err := g.httpClient() +diff -Naur a/pkg/getter/httpgetter_test.go b/pkg/getter/httpgetter_test.go +--- a/pkg/getter/httpgetter_test.go 2020-11-11 11:44:01.000000000 -0800 ++++ b/pkg/getter/httpgetter_test.go 2021-09-20 15:58:38.405158596 -0700 +@@ -54,6 +54,7 @@ + // Test with options + g, err = NewHTTPGetter( + WithBasicAuth("I", "Am"), ++ WithPassCredentialsAll(false), + WithUserAgent("Groot"), + WithTLSClientConfig(pub, priv, ca), + WithInsecureSkipVerifyTLS(insecure), +@@ -76,6 +77,10 @@ + t.Errorf("Expected NewHTTPGetter to contain %q as the password, got %q", "Am", hg.opts.password) + } + ++ if hg.opts.passCredentialsAll != false { ++ t.Errorf("Expected NewHTTPGetter to contain %t as PassCredentialsAll, got %t", false, hg.opts.passCredentialsAll) ++ } ++ + if hg.opts.userAgent != "Groot" { + t.Errorf("Expected NewHTTPGetter to contain %q as the user agent, got %q", "Groot", hg.opts.userAgent) + } +@@ -118,6 +123,28 @@ + if hg.opts.insecureSkipVerifyTLS != insecure { + t.Errorf("Expected NewHTTPGetter to contain %t as InsecureSkipVerifyTLs flag, got %t", insecure, hg.opts.insecureSkipVerifyTLS) + } ++ ++ // Checking false by default ++ if hg.opts.passCredentialsAll != false { ++ t.Errorf("Expected NewHTTPGetter to contain %t as PassCredentialsAll, got %t", false, hg.opts.passCredentialsAll) ++ } ++ ++ // Test setting PassCredentialsAll ++ g, err = NewHTTPGetter( ++ WithBasicAuth("I", "Am"), ++ WithPassCredentialsAll(true), ++ ) ++ if err != nil { ++ t.Fatal(err) ++ } ++ ++ hg, ok = g.(*HTTPGetter) ++ if !ok { ++ t.Fatal("expected NewHTTPGetter to produce an *HTTPGetter") ++ } ++ if hg.opts.passCredentialsAll != true { ++ t.Errorf("Expected NewHTTPGetter to contain %t as PassCredentialsAll, got %t", true, hg.opts.passCredentialsAll) ++ } + } + + func TestDownload(t *testing.T) { +@@ -163,10 +190,81 @@ + httpgetter, err := NewHTTPGetter( + WithURL(u.String()), + WithBasicAuth("username", "password"), ++ WithPassCredentialsAll(false), + WithUserAgent(expectedUserAgent), + ) + if err != nil { + t.Fatal(err) ++ } ++ got, err = httpgetter.Get(u.String()) ++ if err != nil { ++ t.Fatal(err) ++ } ++ ++ if got.String() != expect { ++ t.Errorf("Expected %q, got %q", expect, got.String()) ++ } ++ ++ // test with Get URL differing from withURL ++ crossAuthSrv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { ++ username, password, ok := r.BasicAuth() ++ if ok || username == "username" || password == "password" { ++ t.Errorf("Expected request to not include but got '%v', '%s', '%s'", ok, username, password) ++ } ++ fmt.Fprint(w, expect) ++ })) ++ ++ defer crossAuthSrv.Close() ++ ++ u, _ = url.ParseRequestURI(crossAuthSrv.URL) ++ ++ // A different host is provided for the WithURL from the one used for Get ++ u2, _ := url.ParseRequestURI(crossAuthSrv.URL) ++ host := strings.Split(u2.Host, ":") ++ host[0] = host[0] + "a" ++ u2.Host = strings.Join(host, ":") ++ httpgetter, err = NewHTTPGetter( ++ WithURL(u2.String()), ++ WithBasicAuth("username", "password"), ++ WithPassCredentialsAll(false), ++ ) ++ if err != nil { ++ t.Fatal(err) ++ } ++ got, err = httpgetter.Get(u.String()) ++ if err != nil { ++ t.Fatal(err) ++ } ++ ++ if got.String() != expect { ++ t.Errorf("Expected %q, got %q", expect, got.String()) ++ } ++ ++ // test with Get URL differing from withURL and should pass creds ++ crossAuthSrv = httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { ++ username, password, ok := r.BasicAuth() ++ if !ok || username != "username" || password != "password" { ++ t.Errorf("Expected request to use basic auth and for username == 'username' and password == 'password', got '%v', '%s', '%s'", ok, username, password) ++ } ++ fmt.Fprint(w, expect) ++ })) ++ ++ defer crossAuthSrv.Close() ++ ++ u, _ = url.ParseRequestURI(crossAuthSrv.URL) ++ ++ // A different host is provided for the WithURL from the one used for Get ++ u2, _ = url.ParseRequestURI(crossAuthSrv.URL) ++ host = strings.Split(u2.Host, ":") ++ host[0] = host[0] + "a" ++ u2.Host = strings.Join(host, ":") ++ httpgetter, err = NewHTTPGetter( ++ WithURL(u2.String()), ++ WithBasicAuth("username", "password"), ++ WithPassCredentialsAll(true), ++ ) ++ if err != nil { ++ t.Fatal(err) + } + got, err = httpgetter.Get(u.String()) + if err != nil { +diff -Naur a/pkg/repo/chartrepo.go b/pkg/repo/chartrepo.go +--- a/pkg/repo/chartrepo.go 2020-11-11 11:44:01.000000000 -0800 ++++ b/pkg/repo/chartrepo.go 2021-09-20 15:58:38.405158596 -0700 +@@ -48,6 +48,7 @@ + KeyFile string `json:"keyFile"` + CAFile string `json:"caFile"` + InsecureSkipTLSverify bool `json:"insecure_skip_tls_verify"` ++ PassCredentialsAll bool `json:"pass_credentials_all"` + } + + // ChartRepository represents a chart repository +@@ -127,6 +128,7 @@ + getter.WithInsecureSkipVerifyTLS(r.Config.InsecureSkipTLSverify), + getter.WithTLSClientConfig(r.Config.CertFile, r.Config.KeyFile, r.Config.CAFile), + getter.WithBasicAuth(r.Config.Username, r.Config.Password), ++ getter.WithPassCredentialsAll(r.Config.PassCredentialsAll), + ) + if err != nil { + return "", err +@@ -213,6 +215,15 @@ + // but it also receives credentials and TLS verify flag for the chart repository. + // TODO Helm 4, FindChartInAuthAndTLSRepoURL should be integrated into FindChartInAuthRepoURL. + func FindChartInAuthAndTLSRepoURL(repoURL, username, password, chartName, chartVersion, certFile, keyFile, caFile string, insecureSkipTLSverify bool, getters getter.Providers) (string, error) { ++ return FindChartInAuthAndTLSAndPassRepoURL(repoURL, username, password, chartName, chartVersion, certFile, keyFile, caFile, false, false, getters) ++} ++ ++// FindChartInAuthAndTLSAndPassRepoURL finds chart in chart repository pointed by repoURL ++// without adding repo to repositories, like FindChartInRepoURL, ++// but it also receives credentials, TLS verify flag, and if credentials should ++// be passed on to other domains. ++// TODO Helm 4, FindChartInAuthAndTLSAndPassRepoURL should be integrated into FindChartInAuthRepoURL. ++func FindChartInAuthAndTLSAndPassRepoURL(repoURL, username, password, chartName, chartVersion, certFile, keyFile, caFile string, insecureSkipTLSverify, passCredentialsAll bool, getters getter.Providers) (string, error) { + + // Download and write the index file to a temporary location + buf := make([]byte, 20) +@@ -223,6 +234,7 @@ + URL: repoURL, + Username: username, + Password: password, ++ PassCredentialsAll: passCredentialsAll, + CertFile: certFile, + KeyFile: keyFile, + CAFile: caFile, +diff -Naur a/pkg/repo/chartrepo_test.go b/pkg/repo/chartrepo_test.go +--- a/pkg/repo/chartrepo_test.go 2020-11-11 11:44:01.000000000 -0800 ++++ b/pkg/repo/chartrepo_test.go 2021-09-20 15:58:38.405158596 -0700 +@@ -291,14 +291,14 @@ + return httptest.NewTLSServer(handler), nil + } + +-func TestFindChartInAuthAndTLSRepoURL(t *testing.T) { ++func TestFindChartInAuthAndTLSAndPassRepoURL(t *testing.T) { + srv, err := startLocalTLSServerForTests(nil) + if err != nil { + t.Fatal(err) + } + defer srv.Close() + +- chartURL, err := FindChartInAuthAndTLSRepoURL(srv.URL, "", "", "nginx", "", "", "", "", true, getter.All(&cli.EnvSettings{})) ++ chartURL, err := FindChartInAuthAndTLSAndPassRepoURL(srv.URL, "", "", "nginx", "", "", "", "", true, false, getter.All(&cli.EnvSettings{})) + if err != nil { + t.Fatalf("%v", err) + } +@@ -307,10 +307,10 @@ + } + + // If the insecureSkipTLsverify is false, it will return an error that contains "x509: certificate signed by unknown authority". +- _, err = FindChartInAuthAndTLSRepoURL(srv.URL, "", "", "nginx", "0.1.0", "", "", "", false, getter.All(&cli.EnvSettings{})) ++ _, err = FindChartInAuthAndTLSAndPassRepoURL(srv.URL, "", "", "nginx", "0.1.0", "", "", "", false, false, getter.All(&cli.EnvSettings{})) + + if !strings.Contains(err.Error(), "x509: certificate signed by unknown authority") { +- t.Errorf("Expected TLS error for function FindChartInAuthAndTLSRepoURL not found, but got a different error (%v)", err) ++ t.Errorf("Expected TLS error for function FindChartInAuthAndTLSAndPassRepoURL not found, but got a different error (%v)", err) + } + } + diff --git a/SPECS/helm/helm.spec b/SPECS/helm/helm.spec index 9eabb04883..d0f331fd3b 100644 --- a/SPECS/helm/helm.spec +++ b/SPECS/helm/helm.spec @@ -2,7 +2,7 @@ Name: helm Version: 3.4.1 -Release: 3%{?dist} +Release: 4%{?dist} Summary: The Kubernetes Package Manager Group: Applications/Networking License: Apache 2.0 @@ -26,6 +26,7 @@ Source0: %{name}-%{version}.tar.gz # Source1: %{name}-%{version}-vendor.tar.gz Patch0: CVE-2021-21303.patch +Patch1: CVE-2021-32690.patch BuildRequires: golang >= 1.15.5 %description @@ -53,6 +54,9 @@ install -m 755 ./helm %{buildroot}%{_bindir} %changelog +* Mon Sep 20 2021 Henry Beberman - 3.4.1-4 +- Patch CVE-2021-32690 + * Mon Sep 20 2021 Henry Beberman - 3.4.1-3 - Patch CVE-2021-21303