github.com/argoproj/argo-cd@v1.8.7/util/clusterauth/clusterauth_test.go (about)

     1  package clusterauth
     2  
     3  import (
     4  	"context"
     5  	"io/ioutil"
     6  	"testing"
     7  
     8  	"github.com/ghodss/yaml"
     9  	"github.com/stretchr/testify/assert"
    10  	corev1 "k8s.io/api/core/v1"
    11  	apierr "k8s.io/apimachinery/pkg/api/errors"
    12  	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    13  	"k8s.io/apimachinery/pkg/runtime"
    14  	"k8s.io/client-go/kubernetes/fake"
    15  	kubetesting "k8s.io/client-go/testing"
    16  
    17  	"github.com/argoproj/argo-cd/util/errors"
    18  )
    19  
    20  const (
    21  	testToken = "eyJhbGciOiJSUzI1NiIsImtpZCI6IiJ9.eyJpc3MiOiJrdWJlcm5ldGVzL3NlcnZpY2VhY2NvdW50Iiwia3ViZXJuZXRlcy5pby9zZXJ2aWNlYWNjb3VudC9uYW1lc3BhY2UiOiJrdWJlLXN5c3RlbSIsImt1YmVybmV0ZXMuaW8vc2VydmljZWFjY291bnQvc2VjcmV0Lm5hbWUiOiJhcmdvY2QtbWFuYWdlci10b2tlbi10ajc5ciIsImt1YmVybmV0ZXMuaW8vc2VydmljZWFjY291bnQvc2VydmljZS1hY2NvdW50Lm5hbWUiOiJhcmdvY2QtbWFuYWdlciIsImt1YmVybmV0ZXMuaW8vc2VydmljZWFjY291bnQvc2VydmljZS1hY2NvdW50LnVpZCI6IjkxZGQzN2NmLThkOTItMTFlOS1hMDkxLWQ2NWYyYWU3ZmE4ZCIsInN1YiI6InN5c3RlbTpzZXJ2aWNlYWNjb3VudDprdWJlLXN5c3RlbTphcmdvY2QtbWFuYWdlciJ9.ytZjt2pDV8-A7DBMR06zQ3wt9cuVEfq262TQw7sdra-KRpDpMPnziMhc8bkwvgW-LGhTWUh5iu1y-1QhEx6mtbCt7vQArlBRxfvM5ys6ClFkplzq5c2TtZ7EzGSD0Up7tdxuG9dvR6TGXYdfFcG779yCdZo2H48sz5OSJfdEriduMEY1iL5suZd3ebOoVi1fGflmqFEkZX6SvxkoArl5mtNP6TvZ1eTcn64xh4ws152hxio42E-eSnl_CET4tpB5vgP5BVlSKW2xB7w2GJxqdETA5LJRI_OilY77dTOp8cMr_Ck3EOeda3zHfh4Okflg8rZFEeAuJYahQNeAILLkcA"
    22  )
    23  
    24  var (
    25  	testClaims = ServiceAccountClaims{
    26  		Sub:                "system:serviceaccount:kube-system:argocd-manager",
    27  		Iss:                "kubernetes/serviceaccount",
    28  		Namespace:          "kube-system",
    29  		SecretName:         "argocd-manager-token-tj79r",
    30  		ServiceAccountName: "argocd-manager",
    31  		ServiceAccountUID:  "91dd37cf-8d92-11e9-a091-d65f2ae7fa8d",
    32  	}
    33  )
    34  
    35  func newServiceAccount() *corev1.ServiceAccount {
    36  	saBytes, err := ioutil.ReadFile("./testdata/argocd-manager-sa.yaml")
    37  	errors.CheckError(err)
    38  	var sa corev1.ServiceAccount
    39  	err = yaml.Unmarshal(saBytes, &sa)
    40  	errors.CheckError(err)
    41  	return &sa
    42  }
    43  
    44  func newServiceAccountSecret() *corev1.Secret {
    45  	secretBytes, err := ioutil.ReadFile("./testdata/argocd-manager-sa-token.yaml")
    46  	errors.CheckError(err)
    47  	var secret corev1.Secret
    48  	err = yaml.Unmarshal(secretBytes, &secret)
    49  	errors.CheckError(err)
    50  	return &secret
    51  }
    52  
    53  func TestParseServiceAccountToken(t *testing.T) {
    54  	claims, err := ParseServiceAccountToken(testToken)
    55  	assert.NoError(t, err)
    56  	assert.Equal(t, testClaims, *claims)
    57  }
    58  
    59  func TestCreateServiceAccount(t *testing.T) {
    60  	ns := &corev1.Namespace{
    61  		ObjectMeta: metav1.ObjectMeta{
    62  			Name: "kube-system",
    63  		},
    64  	}
    65  	sa := &corev1.ServiceAccount{
    66  		TypeMeta: metav1.TypeMeta{
    67  			APIVersion: "v1",
    68  			Kind:       "ServiceAccount",
    69  		},
    70  		ObjectMeta: metav1.ObjectMeta{
    71  			Name:      "argocd-manager",
    72  			Namespace: "kube-system",
    73  		},
    74  	}
    75  
    76  	t.Run("New SA", func(t *testing.T) {
    77  		cs := fake.NewSimpleClientset(ns)
    78  		err := CreateServiceAccount(cs, "argocd-manager", "kube-system")
    79  		assert.NoError(t, err)
    80  		rsa, err := cs.CoreV1().ServiceAccounts("kube-system").Get(context.Background(), "argocd-manager", metav1.GetOptions{})
    81  		assert.NoError(t, err)
    82  		assert.NotNil(t, rsa)
    83  	})
    84  
    85  	t.Run("SA exists already", func(t *testing.T) {
    86  		cs := fake.NewSimpleClientset(ns, sa)
    87  		err := CreateServiceAccount(cs, "argocd-manager", "kube-system")
    88  		assert.NoError(t, err)
    89  		rsa, err := cs.CoreV1().ServiceAccounts("kube-system").Get(context.Background(), "argocd-manager", metav1.GetOptions{})
    90  		assert.NoError(t, err)
    91  		assert.NotNil(t, rsa)
    92  	})
    93  
    94  	t.Run("Invalid name", func(t *testing.T) {
    95  		cs := fake.NewSimpleClientset(ns)
    96  		err := CreateServiceAccount(cs, "", "kube-system")
    97  		assert.NoError(t, err)
    98  		rsa, err := cs.CoreV1().ServiceAccounts("kube-system").Get(context.Background(), "argocd-manager", metav1.GetOptions{})
    99  		assert.Error(t, err)
   100  		assert.Nil(t, rsa)
   101  	})
   102  
   103  	t.Run("Invalid namespace", func(t *testing.T) {
   104  		cs := fake.NewSimpleClientset()
   105  		err := CreateServiceAccount(cs, "argocd-manager", "invalid")
   106  		assert.NoError(t, err)
   107  		rsa, err := cs.CoreV1().ServiceAccounts("invalid").Get(context.Background(), "argocd-manager", metav1.GetOptions{})
   108  		assert.NoError(t, err)
   109  		assert.NotNil(t, rsa)
   110  	})
   111  }
   112  
   113  func TestInstallClusterManagerRBAC(t *testing.T) {
   114  	ns := &corev1.Namespace{
   115  		ObjectMeta: metav1.ObjectMeta{
   116  			Name: "test",
   117  		},
   118  	}
   119  	secret := &corev1.Secret{
   120  		ObjectMeta: metav1.ObjectMeta{
   121  			Name:      "sa-secret",
   122  			Namespace: "test",
   123  		},
   124  		Type: corev1.SecretTypeServiceAccountToken,
   125  		Data: map[string][]byte{
   126  			"token": []byte("foobar"),
   127  		},
   128  	}
   129  	sa := &corev1.ServiceAccount{
   130  		ObjectMeta: metav1.ObjectMeta{
   131  			Name:      ArgoCDManagerServiceAccount,
   132  			Namespace: "test",
   133  		},
   134  		Secrets: []corev1.ObjectReference{
   135  			corev1.ObjectReference{
   136  				Kind:            secret.GetObjectKind().GroupVersionKind().Kind,
   137  				APIVersion:      secret.APIVersion,
   138  				Name:            secret.GetName(),
   139  				Namespace:       secret.GetNamespace(),
   140  				UID:             secret.GetUID(),
   141  				ResourceVersion: secret.GetResourceVersion(),
   142  			},
   143  		},
   144  	}
   145  
   146  	t.Run("Cluster Scope - Success", func(t *testing.T) {
   147  		cs := fake.NewSimpleClientset(ns, secret, sa)
   148  		token, err := InstallClusterManagerRBAC(cs, "test", nil)
   149  		assert.NoError(t, err)
   150  		assert.Equal(t, "foobar", token)
   151  	})
   152  
   153  	t.Run("Cluster Scope - Missing data in secret", func(t *testing.T) {
   154  		nsecret := secret.DeepCopy()
   155  		nsecret.Data = make(map[string][]byte)
   156  		cs := fake.NewSimpleClientset(ns, nsecret, sa)
   157  		token, err := InstallClusterManagerRBAC(cs, "test", nil)
   158  		assert.Error(t, err)
   159  		assert.Empty(t, token)
   160  	})
   161  
   162  	t.Run("Namespace Scope - Success", func(t *testing.T) {
   163  		cs := fake.NewSimpleClientset(ns, secret, sa)
   164  		token, err := InstallClusterManagerRBAC(cs, "test", []string{"nsa"})
   165  		assert.NoError(t, err)
   166  		assert.Equal(t, "foobar", token)
   167  	})
   168  
   169  	t.Run("Namespace Scope - Missing data in secret", func(t *testing.T) {
   170  		nsecret := secret.DeepCopy()
   171  		nsecret.Data = make(map[string][]byte)
   172  		cs := fake.NewSimpleClientset(ns, nsecret, sa)
   173  		token, err := InstallClusterManagerRBAC(cs, "test", []string{"nsa"})
   174  		assert.Error(t, err)
   175  		assert.Empty(t, token)
   176  	})
   177  
   178  }
   179  
   180  func TestUninstallClusterManagerRBAC(t *testing.T) {
   181  	t.Run("Success", func(t *testing.T) {
   182  		cs := fake.NewSimpleClientset(newServiceAccountSecret())
   183  		err := UninstallClusterManagerRBAC(cs)
   184  		assert.NoError(t, err)
   185  	})
   186  }
   187  
   188  func TestGenerateNewClusterManagerSecret(t *testing.T) {
   189  	kubeclientset := fake.NewSimpleClientset(newServiceAccountSecret())
   190  	kubeclientset.ReactionChain = nil
   191  
   192  	generatedSecret := newServiceAccountSecret()
   193  	generatedSecret.Name = "argocd-manager-token-abc123"
   194  	generatedSecret.Data = map[string][]byte{
   195  		"token": []byte("fake-token"),
   196  	}
   197  
   198  	kubeclientset.AddReactor("*", "secrets", func(action kubetesting.Action) (handled bool, ret runtime.Object, err error) {
   199  		return true, generatedSecret, nil
   200  	})
   201  
   202  	created, err := GenerateNewClusterManagerSecret(kubeclientset, &testClaims)
   203  	assert.NoError(t, err)
   204  	assert.Equal(t, "argocd-manager-token-abc123", created.Name)
   205  	assert.Equal(t, "fake-token", string(created.Data["token"]))
   206  }
   207  
   208  func TestRotateServiceAccountSecrets(t *testing.T) {
   209  	generatedSecret := newServiceAccountSecret()
   210  	generatedSecret.Name = "argocd-manager-token-abc123"
   211  	generatedSecret.Data = map[string][]byte{
   212  		"token": []byte("fake-token"),
   213  	}
   214  
   215  	kubeclientset := fake.NewSimpleClientset(newServiceAccount(), newServiceAccountSecret(), generatedSecret)
   216  
   217  	err := RotateServiceAccountSecrets(kubeclientset, &testClaims, generatedSecret)
   218  	assert.NoError(t, err)
   219  
   220  	// Verify service account references new secret and old secret is deleted
   221  	saClient := kubeclientset.CoreV1().ServiceAccounts(testClaims.Namespace)
   222  	sa, err := saClient.Get(context.Background(), testClaims.ServiceAccountName, metav1.GetOptions{})
   223  	assert.NoError(t, err)
   224  	assert.Equal(t, sa.Secrets, []corev1.ObjectReference{
   225  		{
   226  			Name: "argocd-manager-token-abc123",
   227  		},
   228  	})
   229  	secretsClient := kubeclientset.CoreV1().Secrets(testClaims.Namespace)
   230  	_, err = secretsClient.Get(context.Background(), testClaims.SecretName, metav1.GetOptions{})
   231  	assert.True(t, apierr.IsNotFound(err))
   232  }
   233  
   234  func TestGetServiceAccountBearerToken(t *testing.T) {
   235  	sa := newServiceAccount()
   236  	tokenSecret := newServiceAccountSecret()
   237  	dockercfgSecret := &corev1.Secret{
   238  		ObjectMeta: metav1.ObjectMeta{
   239  			Name:      "argocd-manager-dockercfg-d8j66",
   240  			Namespace: "kube-system",
   241  		},
   242  		Type: corev1.SecretTypeDockercfg,
   243  		// Skipping data, doesn't really matter.
   244  	}
   245  	sa.Secrets = []corev1.ObjectReference{
   246  		{
   247  			Name:      dockercfgSecret.Name,
   248  			Namespace: dockercfgSecret.Namespace,
   249  		},
   250  		{
   251  			Name:      tokenSecret.Name,
   252  			Namespace: tokenSecret.Namespace,
   253  		},
   254  	}
   255  	kubeclientset := fake.NewSimpleClientset(sa, dockercfgSecret, tokenSecret)
   256  
   257  	token, err := GetServiceAccountBearerToken(kubeclientset, "kube-system", sa.Name)
   258  	assert.NoError(t, err)
   259  	assert.Equal(t, testToken, token)
   260  }