From ca91dd9d6b9cce8f9c8230aa11c1e9353cc2558e Mon Sep 17 00:00:00 2001 From: Alexandre Gaudreault Date: Thu, 26 Sep 2024 15:12:21 -0400 Subject: [PATCH 1/5] fix(extension): add header to support apps-in-any-namespace (#20123) Signed-off-by: Alexandre Gaudreault --- server/extension/extension.go | 16 +++++++++++++--- server/extension/extension_test.go | 6 ++++-- server/server.go | 2 +- 3 files changed, 18 insertions(+), 6 deletions(-) diff --git a/server/extension/extension.go b/server/extension/extension.go index 706dfbbb31abd1..57e493b2210520 100644 --- a/server/extension/extension.go +++ b/server/extension/extension.go @@ -33,6 +33,12 @@ const ( DefaultIdleConnectionTimeout = 60 * time.Second DefaultMaxIdleConnections = 30 + // HeaderArgoCDNamespace defines the namespace of the + // argo control plane to be passed to the extension handler. + // Example: + // Argocd-Namespace: "namespace" + HeaderArgoCDNamespace = "Argocd-Namespace" + // HeaderArgoCDApplicationName defines the name of the // expected application header to be passed to the extension // handler. The header value must follow the format: @@ -333,6 +339,7 @@ type RbacEnforcer interface { // and handling proxy extensions. type Manager struct { log *log.Entry + namespace string settings SettingsGetter application ApplicationGetter project ProjectGetter @@ -355,9 +362,10 @@ type ExtensionMetricsRegistry interface { } // NewManager will initialize a new manager. -func NewManager(log *log.Entry, sg SettingsGetter, ag ApplicationGetter, pg ProjectGetter, rbac RbacEnforcer, ug UserGetter) *Manager { +func NewManager(log *log.Entry, namespace string, sg SettingsGetter, ag ApplicationGetter, pg ProjectGetter, rbac RbacEnforcer, ug UserGetter) *Manager { return &Manager{ log: log, + namespace: namespace, settings: sg, application: ag, project: pg, @@ -740,7 +748,7 @@ func (m *Manager) CallExtension() func(http.ResponseWriter, *http.Request) { user := m.userGetter.GetUser(r.Context()) groups := m.userGetter.GetGroups(r.Context()) - prepareRequest(r, extName, app, user, groups) + prepareRequest(r, m.namespace, extName, app, user, groups) m.log.Debugf("proxing request for extension %q", extName) // httpsnoop package is used to properly wrap the responseWriter // and avoid optional intefaces issue: @@ -763,11 +771,13 @@ func registerMetrics(extName string, metrics httpsnoop.Metrics, extensionMetrics // the Argo CD extension API section from it. It provides additional information to // the backend service appending them in the outgoing request headers. The appended // headers are: +// - Control plane namespace // - Cluster destination name // - Cluster destination server // - Argo CD authenticated username -func prepareRequest(r *http.Request, extName string, app *v1alpha1.Application, username string, groups []string) { +func prepareRequest(r *http.Request, namespace string, extName string, app *v1alpha1.Application, username string, groups []string) { r.URL.Path = strings.TrimPrefix(r.URL.Path, fmt.Sprintf("%s/%s", URLPrefix, extName)) + r.Header.Set(HeaderArgoCDNamespace, namespace) if app.Spec.Destination.Name != "" { r.Header.Set(HeaderArgoCDTargetClusterName, app.Spec.Destination.Name) } diff --git a/server/extension/extension_test.go b/server/extension/extension_test.go index 300e1e89a490dd..e412537eea0cdb 100644 --- a/server/extension/extension_test.go +++ b/server/extension/extension_test.go @@ -150,7 +150,7 @@ func TestRegisterExtensions(t *testing.T) { logger, _ := test.NewNullLogger() logEntry := logger.WithContext(context.Background()) - m := extension.NewManager(logEntry, settMock, nil, nil, nil, nil) + m := extension.NewManager(logEntry, "", settMock, nil, nil, nil, nil) return &fixture{ settingsGetterMock: settMock, @@ -248,6 +248,7 @@ func TestCallExtension(t *testing.T) { userMock *mocks.UserGetter manager *extension.Manager } + defaultServerNamespace := "control-plane-ns" defaultProjectName := "project-name" setup := func() *fixture { @@ -260,7 +261,7 @@ func TestCallExtension(t *testing.T) { logger, _ := test.NewNullLogger() logEntry := logger.WithContext(context.Background()) - m := extension.NewManager(logEntry, settMock, appMock, projMock, rbacMock, userMock) + m := extension.NewManager(logEntry, defaultServerNamespace, settMock, appMock, projMock, rbacMock, userMock) m.AddMetricsRegistry(metricsMock) mux := http.NewServeMux() @@ -444,6 +445,7 @@ func TestCallExtension(t *testing.T) { require.NoError(t, err) actual := strings.TrimSuffix(string(body), "\n") assert.Equal(t, backendResponse, actual) + assert.Equal(t, defaultServerNamespace, resp.Header.Get(extension.HeaderArgoCDNamespace)) assert.Equal(t, clusterURL, resp.Header.Get(extension.HeaderArgoCDTargetClusterURL)) assert.Equal(t, "Bearer some-bearer-token", resp.Header.Get("Authorization")) assert.Equal(t, "some-user", resp.Header.Get(extension.HeaderArgoCDUsername)) diff --git a/server/server.go b/server/server.go index 7ded3951a37c3d..400ad99ad47a37 100644 --- a/server/server.go +++ b/server/server.go @@ -328,7 +328,7 @@ func NewServer(ctx context.Context, opts ArgoCDServerOpts, appsetOpts Applicatio ag := extension.NewDefaultApplicationGetter(appLister) pg := extension.NewDefaultProjectGetter(projLister, dbInstance) ug := extension.NewDefaultUserGetter(policyEnf) - em := extension.NewManager(logger, sg, ag, pg, enf, ug) + em := extension.NewManager(logger, opts.Namespace, sg, ag, pg, enf, ug) a := &ArgoCDServer{ ArgoCDServerOpts: opts, From bc15ae89d868f6f30134575ba64563468ebeff9e Mon Sep 17 00:00:00 2001 From: Symeon Meichanetzoglou Date: Thu, 26 Sep 2024 23:14:50 +0200 Subject: [PATCH 2/5] Fix typo (#20127) Remove a redundant "is". Signed-off-by: Symeon Meichanetzoglou --- docs/user-guide/diff-strategies.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/user-guide/diff-strategies.md b/docs/user-guide/diff-strategies.md index ffd09660696acb..505be30cd6a8bd 100644 --- a/docs/user-guide/diff-strategies.md +++ b/docs/user-guide/diff-strategies.md @@ -18,7 +18,7 @@ Argo CD currently has 3 different strategies to calculate diffs: ## Structured-Merge Diff *Current Status: [Beta][1] (Since v2.5.0)* -This is diff strategy is automatically used when Server-Side Apply +This diff strategy is automatically used when Server-Side Apply sync option is enabled. It uses the [structured-merge-diff][2] library used by Kubernetes to calculate diffs based on fields ownership. There are some challenges using this strategy to calculate diffs for CRDs From 159eeecd17cb6439c7888189eda7f431e504674d Mon Sep 17 00:00:00 2001 From: Linghao Su Date: Fri, 27 Sep 2024 21:15:49 +0800 Subject: [PATCH 3/5] fix(ui): add optional check to avoid undefined reference in project detail (#20044) Signed-off-by: linghaoSu --- .../app/settings/components/project-details/project-details.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ui/src/app/settings/components/project-details/project-details.tsx b/ui/src/app/settings/components/project-details/project-details.tsx index e77c501b70bbcf..96110c2ff9cee0 100644 --- a/ui/src/app/settings/components/project-details/project-details.tsx +++ b/ui/src/app/settings/components/project-details/project-details.tsx @@ -671,7 +671,7 @@ export class ProjectDetails extends React.Component {authCtx => - authCtx.appsInAnyNamespaceEnabled && ( + authCtx?.appsInAnyNamespaceEnabled && ( this.saveProject(item)} values={proj} From ea71067059b802e1e72da0892da329ef32ee507c Mon Sep 17 00:00:00 2001 From: pasha-codefresh Date: Fri, 27 Sep 2024 16:45:58 +0300 Subject: [PATCH 4/5] chore(deps): bump Helm from 3.15.2 to 3.15.4 (#20135) * sec: upgrade helm version in order to fix critical vulnerability Signed-off-by: pashakostohrys * sec: upgrade helm version in order to fix critical vulnerability Signed-off-by: pashakostohrys --------- Signed-off-by: pashakostohrys --- docs/operator-manual/upgrading/2.12-2.13.md | 4 ++++ hack/installers/checksums/helm-v-linux-amd64.tar.gz.sha256 | 0 .../checksums/helm-v3.15.4-darwin-amd64.tar.gz.sha256 | 1 + .../checksums/helm-v3.15.4-darwin-arm64.tar.gz.sha256 | 1 + .../checksums/helm-v3.15.4-linux-amd64.tar.gz.sha256 | 1 + .../checksums/helm-v3.15.4-linux-arm64.tar.gz.sha256 | 1 + .../checksums/helm-v3.15.4-linux-ppc64le.tar.gz.sha256 | 1 + .../checksums/helm-v3.15.4-linux-s390x.tar.gz.sha256 | 1 + hack/tool-versions.sh | 2 +- 9 files changed, 11 insertions(+), 1 deletion(-) create mode 100644 hack/installers/checksums/helm-v-linux-amd64.tar.gz.sha256 create mode 100644 hack/installers/checksums/helm-v3.15.4-darwin-amd64.tar.gz.sha256 create mode 100644 hack/installers/checksums/helm-v3.15.4-darwin-arm64.tar.gz.sha256 create mode 100644 hack/installers/checksums/helm-v3.15.4-linux-amd64.tar.gz.sha256 create mode 100644 hack/installers/checksums/helm-v3.15.4-linux-arm64.tar.gz.sha256 create mode 100644 hack/installers/checksums/helm-v3.15.4-linux-ppc64le.tar.gz.sha256 create mode 100644 hack/installers/checksums/helm-v3.15.4-linux-s390x.tar.gz.sha256 diff --git a/docs/operator-manual/upgrading/2.12-2.13.md b/docs/operator-manual/upgrading/2.12-2.13.md index 14b26f22a2d700..9e918cc3deb876 100644 --- a/docs/operator-manual/upgrading/2.12-2.13.md +++ b/docs/operator-manual/upgrading/2.12-2.13.md @@ -1,5 +1,9 @@ # v2.12 to 2.13 +## Upgraded Helm Version + +Note that bundled Helm version has been upgraded from 3.15.2 to 3.15.4. + ## Custom Resource Actions for Flux Resources [`Custom Resource Actions`](../resource_actions.md#Custom-Resource-Actions) have been added for Flux Resources. diff --git a/hack/installers/checksums/helm-v-linux-amd64.tar.gz.sha256 b/hack/installers/checksums/helm-v-linux-amd64.tar.gz.sha256 new file mode 100644 index 00000000000000..e69de29bb2d1d6 diff --git a/hack/installers/checksums/helm-v3.15.4-darwin-amd64.tar.gz.sha256 b/hack/installers/checksums/helm-v3.15.4-darwin-amd64.tar.gz.sha256 new file mode 100644 index 00000000000000..8857c0720c0c7a --- /dev/null +++ b/hack/installers/checksums/helm-v3.15.4-darwin-amd64.tar.gz.sha256 @@ -0,0 +1 @@ +1bc3f354f7ce4d7fd9cfa5bcc701c1f32c88d27076d96c2792d5b5226062aee5 helm-v3.15.4-darwin-amd64.tar.gz diff --git a/hack/installers/checksums/helm-v3.15.4-darwin-arm64.tar.gz.sha256 b/hack/installers/checksums/helm-v3.15.4-darwin-arm64.tar.gz.sha256 new file mode 100644 index 00000000000000..4ee2beac2ea3f8 --- /dev/null +++ b/hack/installers/checksums/helm-v3.15.4-darwin-arm64.tar.gz.sha256 @@ -0,0 +1 @@ +88115846a1fb58f8eb8f64fec5c343d95ca394f1be811602fa54a887c98730ac helm-v3.15.4-darwin-arm64.tar.gz diff --git a/hack/installers/checksums/helm-v3.15.4-linux-amd64.tar.gz.sha256 b/hack/installers/checksums/helm-v3.15.4-linux-amd64.tar.gz.sha256 new file mode 100644 index 00000000000000..5996c0d82cf758 --- /dev/null +++ b/hack/installers/checksums/helm-v3.15.4-linux-amd64.tar.gz.sha256 @@ -0,0 +1 @@ +11400fecfc07fd6f034863e4e0c4c4445594673fd2a129e701fe41f31170cfa9 helm-v3.15.4-linux-amd64.tar.gz diff --git a/hack/installers/checksums/helm-v3.15.4-linux-arm64.tar.gz.sha256 b/hack/installers/checksums/helm-v3.15.4-linux-arm64.tar.gz.sha256 new file mode 100644 index 00000000000000..a6954894d20c05 --- /dev/null +++ b/hack/installers/checksums/helm-v3.15.4-linux-arm64.tar.gz.sha256 @@ -0,0 +1 @@ +fa419ecb139442e8a594c242343fafb7a46af3af34041c4eac1efcc49d74e626 helm-v3.15.4-linux-arm64.tar.gz diff --git a/hack/installers/checksums/helm-v3.15.4-linux-ppc64le.tar.gz.sha256 b/hack/installers/checksums/helm-v3.15.4-linux-ppc64le.tar.gz.sha256 new file mode 100644 index 00000000000000..9292b9dbe2c710 --- /dev/null +++ b/hack/installers/checksums/helm-v3.15.4-linux-ppc64le.tar.gz.sha256 @@ -0,0 +1 @@ +e4efce93723f52dd858e9046ea836c9c75f346facce1b87b8cf78c817b97e6ac helm-v3.15.4-linux-ppc64le.tar.gz diff --git a/hack/installers/checksums/helm-v3.15.4-linux-s390x.tar.gz.sha256 b/hack/installers/checksums/helm-v3.15.4-linux-s390x.tar.gz.sha256 new file mode 100644 index 00000000000000..040a3b21534c8f --- /dev/null +++ b/hack/installers/checksums/helm-v3.15.4-linux-s390x.tar.gz.sha256 @@ -0,0 +1 @@ +c6e0cdea598196895ac7b627ce972699ef9f06b0eba51dc4db7cc21b3369f24a helm-v3.15.4-linux-s390x.tar.gz diff --git a/hack/tool-versions.sh b/hack/tool-versions.sh index 28ca1cda431daf..407a6dd14b88b9 100644 --- a/hack/tool-versions.sh +++ b/hack/tool-versions.sh @@ -11,7 +11,7 @@ # Use ./hack/installers/checksums/add-helm-checksums.sh and # add-kustomize-checksums.sh to help download checksums. ############################################################################### -helm3_version=3.15.2 +helm3_version=3.15.4 kubectl_version=1.17.8 kubectx_version=0.6.3 kustomize5_version=5.4.3 From fa54ce221e62e881c5c7920e9b053fe80fd34792 Mon Sep 17 00:00:00 2001 From: Tony Au-Yeung Date: Fri, 27 Sep 2024 11:03:42 -0500 Subject: [PATCH 5/5] fix: oras-go client should fallback to docker config if no credentials specified (#18133) * oras-go client should fallback to docker config if no credentials specified Signed-off-by: Tony Au-Yeung * Fix tests Signed-off-by: Tony Au-Yeung * Fix lint Signed-off-by: Tony Au-Yeung * gofumpt Signed-off-by: Tony Au-Yeung * Validate auth header Signed-off-by: Tony Au-Yeung --------- Signed-off-by: Tony Au-Yeung --- util/helm/client.go | 23 +++++++--- util/helm/client_test.go | 97 ++++++++++++++++++++++++++++++++++++++-- 2 files changed, 111 insertions(+), 9 deletions(-) diff --git a/util/helm/client.go b/util/helm/client.go index 3ddbcec4333a67..d9972adb04968d 100644 --- a/util/helm/client.go +++ b/util/helm/client.go @@ -25,6 +25,7 @@ import ( "gopkg.in/yaml.v2" "oras.land/oras-go/v2/registry/remote" "oras.land/oras-go/v2/registry/remote/auth" + "oras.land/oras-go/v2/registry/remote/credentials" "github.com/argoproj/argo-cd/v2/util/cache" argoio "github.com/argoproj/argo-cd/v2/util/io" @@ -447,13 +448,23 @@ func (c *nativeHelmChart) GetTags(chart string, noCache bool) (*TagsList, error) }} repoHost, _, _ := strings.Cut(tagsURL, "/") + credential := auth.StaticCredential(repoHost, auth.Credential{ + Username: c.creds.Username, + Password: c.creds.Password, + }) + + // Try to fallback to the environment config, but we shouldn't error if the file is not set + if c.creds.Username == "" && c.creds.Password == "" { + store, _ := credentials.NewStoreFromDocker(credentials.StoreOptions{}) + if store != nil { + credential = credentials.Credential(store) + } + } + repo.Client = &auth.Client{ - Client: client, - Cache: nil, - Credential: auth.StaticCredential(repoHost, auth.Credential{ - Username: c.creds.Username, - Password: c.creds.Password, - }), + Client: client, + Cache: nil, + Credential: credential, } ctx := context.Background() diff --git a/util/helm/client_test.go b/util/helm/client_test.go index f03bd15bf096d6..cae4574e2e86be 100644 --- a/util/helm/client_test.go +++ b/util/helm/client_test.go @@ -9,6 +9,7 @@ import ( "net/http/httptest" "net/url" "os" + "path/filepath" "strings" "testing" @@ -214,6 +215,91 @@ func TestGetTagsFromUrl(t *testing.T) { } func TestGetTagsFromURLPrivateRepoAuthentication(t *testing.T) { + username := "my-username" + password := "my-password" + expectedAuthorization := "Basic bXktdXNlcm5hbWU6bXktcGFzc3dvcmQ=" // base64(user:password) + server := httptest.NewTLSServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + t.Logf("called %s", r.URL.Path) + + authorization := r.Header.Get("Authorization") + + if authorization == "" { + w.Header().Set("WWW-Authenticate", `Basic realm="helm repo to get tags"`) + w.WriteHeader(http.StatusUnauthorized) + return + } + + assert.Equal(t, expectedAuthorization, authorization) + + responseTags := TagsList{ + Tags: []string{ + "2.8.0", + "2.8.0-prerelease", + "2.8.0_build", + "2.8.0-prerelease_build", + "2.8.0-prerelease.1_build.1234", + }, + } + + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(http.StatusOK) + err := json.NewEncoder(w).Encode(responseTags) + if err != nil { + t.Fatal(err) + } + })) + t.Cleanup(server.Close) + + serverURL, err := url.Parse(server.URL) + require.NoError(t, err) + + testCases := []struct { + name string + repoURL string + }{ + { + name: "should login correctly when the repo path is in the server root with http scheme", + repoURL: server.URL, + }, + { + name: "should login correctly when the repo path is not in the server root with http scheme", + repoURL: fmt.Sprintf("%s/my-repo", server.URL), + }, + { + name: "should login correctly when the repo path is in the server root without http scheme", + repoURL: serverURL.Host, + }, + { + name: "should login correctly when the repo path is not in the server root without http scheme", + repoURL: fmt.Sprintf("%s/my-repo", serverURL.Host), + }, + } + + for _, testCase := range testCases { + t.Run(testCase.name, func(t *testing.T) { + client := NewClient(testCase.repoURL, Creds{ + InsecureSkipVerify: true, + Username: username, + Password: password, + }, true, "", "") + + tags, err := client.GetTags("mychart", true) + + require.NoError(t, err) + assert.ElementsMatch(t, tags.Tags, []string{ + "2.8.0", + "2.8.0-prerelease", + "2.8.0+build", + "2.8.0-prerelease+build", + "2.8.0-prerelease.1+build.1234", + }) + }) + } +} + +func TestGetTagsFromURLEnvironmentAuthentication(t *testing.T) { + bearerToken := "Zm9vOmJhcg==" + expectedAuthorization := "Basic " + bearerToken server := httptest.NewTLSServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { t.Logf("called %s", r.URL.Path) @@ -224,7 +310,7 @@ func TestGetTagsFromURLPrivateRepoAuthentication(t *testing.T) { return } - t.Logf("authorization received %s", authorization) + assert.Equal(t, expectedAuthorization, authorization) responseTags := TagsList{ Tags: []string{ @@ -248,6 +334,13 @@ func TestGetTagsFromURLPrivateRepoAuthentication(t *testing.T) { serverURL, err := url.Parse(server.URL) require.NoError(t, err) + tempDir := t.TempDir() + configPath := filepath.Join(tempDir, "config.json") + t.Setenv("DOCKER_CONFIG", tempDir) + + config := fmt.Sprintf(`{"auths":{"%s":{"auth":"%s"}}}`, server.URL, bearerToken) + require.NoError(t, os.WriteFile(configPath, []byte(config), 0o666)) + testCases := []struct { name string repoURL string @@ -274,8 +367,6 @@ func TestGetTagsFromURLPrivateRepoAuthentication(t *testing.T) { t.Run(testCase.name, func(t *testing.T) { client := NewClient(testCase.repoURL, Creds{ InsecureSkipVerify: true, - Username: "my-username", - Password: "my-password", }, true, "", "") tags, err := client.GetTags("mychart", true)