CVE-2023-40029 (published 2023-09-07T23:15Z)

Argo CD is a declarative continuous deployment for Kubernetes. Argo CD Cluster secrets might be managed declaratively using Argo CD / kubectl apply. As a result, the full secret body is stored in`kubectl.kubernetes.io/last-applied-configuration` annotation. pull request #7139 introduced the ability to manage cluster labels and annotations. Since clusters are stored as secrets it also exposes the `kubectl.kubernetes.io/last-applied-configuration` annotation which includes full secret body. In order to view the cluster annotations via the Argo CD API, the user must have `clusters, get` RBAC access. **Note:** In many cases, cluster secrets do not contain any actually-secret information. But sometimes, as in bearer-token auth, the contents might be very sensitive. The bug has been patched in versions 2.8.3, 2.7.14, and 2.6.15. Users are advised to upgrade. Users unable to upgrade should update/deploy cluster secret with `server-side-apply` flag which does not use or rely on `kubectl.kubernetes.io/last-applied-configuration` annotation. Note: annotation for existing secrets will require manual removal.\n\n

CVSS v3 base score: 9.6, CRITICAL (CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:C/C:H/I:H/A:N)

Weakness information:
cwe_id: CWE-532
cwe_name: Insertion of Sensitive Information into Log File
description: Information written to log files can be of a sensitive nature and give valuable guidance to an attacker or expose sensitive user information.
extended_description: 
            While logging all information may be helpful during development stages, it is important that logging levels be set appropriately before a product ships so that sensitive user data and system information are not accidentally exposed to potential attackers.
            Different log files may be produced and stored for:
            
               Server log files (e.g. server.log).  This can give information on whatever application left the file. Usually this can give full path names and system information, and sometimes usernames and passwords.
               log files that are used for debugging
            
         
         

Fix information:
repo_url: https://github.com/argoproj/argo-cd
commit_details: repo_url: https://github.com/argoproj/argo-cd
author: Alexander Matyushentsev
author_date: 2023-09-07 10:14:57-04:00
msg: Merge pull request from GHSA-fwr2-64vr-xv9m

* fix: prevent seeing/editing 'kubectl.kubernetes.io/last-applied-configuration' cluster annotation

Signed-off-by: Alexander Matyushentsev <AMatyushentsev@gmail.com>

* fix: failing unit test

Signed-off-by: iam-veeramalla <abhishek.veeramalla@gmail.com>

---------

Signed-off-by: Alexander Matyushentsev <AMatyushentsev@gmail.com>
Signed-off-by: iam-veeramalla <abhishek.veeramalla@gmail.com>
Co-authored-by: iam-veeramalla <abhishek.veeramalla@gmail.com>
repository: repo_url: https://github.com/argoproj/argo-cd
repo_name: argoproj/argo-cd
description: Declarative Continuous Deployment for Kubernetes
repo_language: Go
owner: argoproj
file_changes: filename: cluster.go
change_type: ModificationType.MODIFY
diff: @@ -345,6 +345,9 @@ func clusterToSecret(c *appv1.Cluster, secret *apiv1.Secret) error {
 	secret.Data = data
 
 	secret.Labels = c.Labels
+	if c.Annotations != nil && c.Annotations[apiv1.LastAppliedConfigAnnotation] != "" {
+		return status.Errorf(codes.InvalidArgument, "annotation %s cannot be set", apiv1.LastAppliedConfigAnnotation)
+	}
 	secret.Annotations = c.Annotations
 
 	if secret.Annotations == nil {
@@ -403,6 +406,8 @@ func SecretToCluster(s *apiv1.Secret) (*appv1.Cluster, error) {
 	annotations := map[string]string{}
 	if s.Annotations != nil {
 		annotations = collections.CopyStringMap(s.Annotations)
+		// delete system annotations
+		delete(annotations, apiv1.LastAppliedConfigAnnotation)
 		delete(annotations, common.AnnotationKeyManagedBy)
 	}
 

programming_language: Go
filename: cluster_test.go
change_type: ModificationType.MODIFY
diff: @@ -7,6 +7,8 @@ import (
 
 	"github.com/stretchr/testify/assert"
 	"github.com/stretchr/testify/require"
+	"google.golang.org/grpc/codes"
+	"google.golang.org/grpc/status"
 	v1 "k8s.io/api/core/v1"
 	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
 	"k8s.io/client-go/kubernetes/fake"
@@ -56,6 +58,24 @@ func Test_secretToCluster(t *testing.T) {
 	})
 }
 
+func Test_secretToCluster_LastAppliedConfigurationDropped(t *testing.T) {
+	secret := &v1.Secret{
+		ObjectMeta: metav1.ObjectMeta{
+			Name:        "mycluster",
+			Namespace:   fakeNamespace,
+			Annotations: map[string]string{v1.LastAppliedConfigAnnotation: "val2"},
+		},
+		Data: map[string][]byte{
+			"name":   []byte("test"),
+			"server": []byte("http://mycluster"),
+			"config": []byte("{\"username\":\"foo\"}"),
+		},
+	}
+	cluster, err := SecretToCluster(secret)
+	require.NoError(t, err)
+	assert.Len(t, cluster.Annotations, 0)
+}
+
 func TestClusterToSecret(t *testing.T) {
 	cluster := &appv1.Cluster{
 		Server:      "server",
@@ -78,6 +98,21 @@ func TestClusterToSecret(t *testing.T) {
 	assert.Equal(t, cluster.Labels, s.Labels)
 }
 
+func TestClusterToSecret_LastAppliedConfigurationRejected(t *testing.T) {
+	cluster := &appv1.Cluster{
+		Server:      "server",
+		Annotations: map[string]string{v1.LastAppliedConfigAnnotation: "val2"},
+		Name:        "test",
+		Config:      v1alpha1.ClusterConfig{},
+		Project:     "project",
+		Namespaces:  []string{"default"},
+	}
+	s := &v1.Secret{}
+	err := clusterToSecret(cluster, s)
+	require.Error(t, err)
+	require.Equal(t, codes.InvalidArgument, status.Code(err))
+}
+
 func Test_secretToCluster_NoConfig(t *testing.T) {
 	secret := &v1.Secret{
 		ObjectMeta: metav1.ObjectMeta{

programming_language: Go