Skip to content

Commit

Permalink
Merge pull request from GHSA-56hp-xqp3-w2jf
Browse files Browse the repository at this point in the history
tweak basic handling
  • Loading branch information
mattfarina committed Jun 16, 2021
2 parents 3e1da5f + 17ed9c4 commit 179f901
Show file tree
Hide file tree
Showing 15 changed files with 342 additions and 18 deletions.
1 change: 1 addition & 0 deletions cmd/helm/flags.go
Expand Up @@ -57,6 +57,7 @@ func addChartPathOptionsFlags(f *pflag.FlagSet, c *action.ChartPathOptions) {
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
Expand Down
37 changes: 37 additions & 0 deletions cmd/helm/install_test.go
Expand Up @@ -18,10 +18,36 @@ package main

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
{
Expand Down Expand Up @@ -207,6 +233,17 @@ func TestInstall(t *testing.T) {
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)
Expand Down
111 changes: 111 additions & 0 deletions cmd/helm/pull_test.go
Expand Up @@ -18,6 +18,8 @@ package main

import (
"fmt"
"net/http"
"net/http/httptest"
"os"
"path/filepath"
"testing"
Expand Down Expand Up @@ -250,6 +252,115 @@ func TestPullCmd(t *testing.T) {
}
}

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)
}
if fi.IsDir() != tt.expectDir {
t.Errorf("%q: expected directory=%t, but it's not.", tt.name, tt.expectDir)
}
})
}
}

func TestPullVersionCompletion(t *testing.T) {
repoFile := "testdata/helmhome/helm/repositories.yaml"
repoCache := "testdata/helmhome/helm/repository"
Expand Down
3 changes: 3 additions & 0 deletions cmd/helm/repo_add.go
Expand Up @@ -48,6 +48,7 @@ type repoAddOptions struct {
url string
username string
password string
passCredentialsAll bool
forceUpdate bool
allowDeprecatedRepos bool

Expand Down Expand Up @@ -91,6 +92,7 @@ func newRepoAddCmd(out io.Writer) *cobra.Command {
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
}
Expand Down Expand Up @@ -156,6 +158,7 @@ func (o *repoAddOptions) run(out io.Writer) error {
URL: o.url,
Username: o.username,
Password: o.password,
PassCredentialsAll: o.passCredentialsAll,
CertFile: o.certFile,
KeyFile: o.keyFile,
CAFile: o.caFile,
Expand Down
30 changes: 27 additions & 3 deletions pkg/action/install.go
Expand Up @@ -20,6 +20,7 @@ import (
"bytes"
"fmt"
"io/ioutil"
"net/url"
"os"
"path"
"path/filepath"
Expand Down Expand Up @@ -113,6 +114,7 @@ type ChartPathOptions struct {
InsecureSkipTLSverify bool // --insecure-skip-verify
Keyring string // --keyring
Password string // --password
PassCredentialsAll bool // --pass-credentials
RepoURL string // --repo
Username string // --username
Verify bool // --verify
Expand Down Expand Up @@ -654,7 +656,7 @@ func (c *ChartPathOptions) LocateChart(name string, settings *cli.EnvSettings) (
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),
},
Expand All @@ -665,12 +667,34 @@ func (c *ChartPathOptions) LocateChart(name string, settings *cli.EnvSettings) (
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 {
Expand Down
3 changes: 2 additions & 1 deletion pkg/action/pull.go
Expand Up @@ -82,6 +82,7 @@ func (p *Pull) Run(chartRef string) (string, error) {
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),
},
Expand Down Expand Up @@ -118,7 +119,7 @@ func (p *Pull) Run(chartRef string) (string, error) {
}

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
}
Expand Down
6 changes: 5 additions & 1 deletion pkg/downloader/chart_downloader.go
Expand Up @@ -195,6 +195,7 @@ func (c *ChartDownloader) ResolveChartVersion(ref, version string) (*url.URL, er
c.Options = append(
c.Options,
getter.WithBasicAuth(rc.Username, rc.Password),
getter.WithPassCredentialsAll(rc.PassCredentialsAll),
)
}
return u, nil
Expand Down Expand Up @@ -224,7 +225,10 @@ func (c *ChartDownloader) ResolveChartVersion(ref, version string) (*url.URL, er
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),
)
}
}

Expand Down
1 change: 1 addition & 0 deletions pkg/downloader/chart_downloader_test.go
Expand Up @@ -205,6 +205,7 @@ func TestDownloadTo(t *testing.T) {
}),
Options: []getter.Option{
getter.WithBasicAuth("username", "password"),
getter.WithPassCredentialsAll(false),
},
}
cname := "/signtest-0.1.0.tgz"
Expand Down
12 changes: 7 additions & 5 deletions pkg/downloader/manager.go
Expand Up @@ -310,7 +310,7 @@ func (m *Manager) downloadAll(deps []*chart.Dependency) error {

// Any failure to resolve/download a chart should fail:
// https://github.com/helm/helm/issues/1439
churl, username, password, insecureskiptlsverify, err := m.findChartURL(dep.Name, dep.Version, dep.Repository, repos)
churl, username, password, insecureskiptlsverify, passcredentialsall, err := m.findChartURL(dep.Name, dep.Version, dep.Repository, repos)
if err != nil {
saveError = errors.Wrapf(err, "could not find %s", churl)
break
Expand All @@ -332,6 +332,7 @@ func (m *Manager) downloadAll(deps []*chart.Dependency) error {
Getters: m.Getters,
Options: []getter.Option{
getter.WithBasicAuth(username, password),
getter.WithPassCredentialsAll(passcredentialsall),
getter.WithInsecureSkipVerifyTLS(insecureskiptlsverify),
},
}
Expand Down Expand Up @@ -686,9 +687,9 @@ func (m *Manager) parallelRepoUpdate(repos []*repo.Entry) error {
// 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, insecureskiptlsverify bool, err error) {
func (m *Manager) findChartURL(name, version, repoURL string, repos map[string]*repo.ChartRepository) (url, username, password string, insecureskiptlsverify, passcredentialsall bool, err error) {
if strings.HasPrefix(repoURL, "oci://") {
return fmt.Sprintf("%s/%s:%s", repoURL, name, version), "", "", false, nil
return fmt.Sprintf("%s/%s:%s", repoURL, name, version), "", "", false, false, nil
}

for _, cr := range repos {
Expand All @@ -710,16 +711,17 @@ func (m *Manager) findChartURL(name, version, repoURL string, repos map[string]*
}
username = cr.Config.Username
password = cr.Config.Password
passcredentialsall = cr.Config.PassCredentialsAll
insecureskiptlsverify = cr.Config.InsecureSkipTLSverify
return
}
}
url, err = repo.FindChartInRepoURL(repoURL, name, version, "", "", "", m.Getters)
if err == nil {
return url, username, password, false, err
return url, username, password, false, false, err
}
err = errors.Errorf("chart %s not found in %s: %s", name, repoURL, err)
return url, username, password, false, err
return url, username, password, false, false, err
}

// findEntryByName finds an entry in the chart repository whose name matches the given name.
Expand Down

0 comments on commit 179f901

Please sign in to comment.