auth_test.go 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392
  1. /*
  2. Copyright © 2025 ESO Maintainer Team
  3. Licensed under the Apache License, Version 2.0 (the "License");
  4. you may not use this file except in compliance with the License.
  5. You may obtain a copy of the License at
  6. https://www.apache.org/licenses/LICENSE-2.0
  7. Unless required by applicable law or agreed to in writing, software
  8. distributed under the License is distributed on an "AS IS" BASIS,
  9. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  10. See the License for the specific language governing permissions and
  11. limitations under the License.
  12. */
  13. package kubernetes
  14. import (
  15. "context"
  16. "testing"
  17. "github.com/stretchr/testify/assert"
  18. corev1 "k8s.io/api/core/v1"
  19. metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
  20. typedcorev1 "k8s.io/client-go/kubernetes/typed/core/v1"
  21. "k8s.io/client-go/rest"
  22. pointer "k8s.io/utils/ptr"
  23. kclient "sigs.k8s.io/controller-runtime/pkg/client"
  24. fclient "sigs.k8s.io/controller-runtime/pkg/client/fake"
  25. esv1 "github.com/external-secrets/external-secrets/apis/externalsecrets/v1"
  26. v1 "github.com/external-secrets/external-secrets/apis/meta/v1"
  27. utilfake "github.com/external-secrets/external-secrets/runtime/util/fake"
  28. )
  29. const (
  30. caCert = `-----BEGIN CERTIFICATE-----
  31. MIICGTCCAZ+gAwIBAgIQCeCTZaz32ci5PhwLBCou8zAKBggqhkjOPQQDAzBOMQsw
  32. CQYDVQQGEwJVUzEXMBUGA1UEChMORGlnaUNlcnQsIEluYy4xJjAkBgNVBAMTHURp
  33. Z2lDZXJ0IFRMUyBFQ0MgUDM4NCBSb290IEc1MB4XDTIxMDExNTAwMDAwMFoXDTQ2
  34. MDExNDIzNTk1OVowTjELMAkGA1UEBhMCVVMxFzAVBgNVBAoTDkRpZ2lDZXJ0LCBJ
  35. bmMuMSYwJAYDVQQDEx1EaWdpQ2VydCBUTFMgRUNDIFAzODQgUm9vdCBHNTB2MBAG
  36. ByqGSM49AgEGBSuBBAAiA2IABMFEoc8Rl1Ca3iOCNQfN0MsYndLxf3c1TzvdlHJS
  37. 7cI7+Oz6e2tYIOyZrsn8aLN1udsJ7MgT9U7GCh1mMEy7H0cKPGEQQil8pQgO4CLp
  38. 0zVozptjn4S1mU1YoI71VOeVyaNCMEAwHQYDVR0OBBYEFMFRRVBZqz7nLFr6ICIS
  39. B4CIfBFqMA4GA1UdDwEB/wQEAwIBhjAPBgNVHRMBAf8EBTADAQH/MAoGCCqGSM49
  40. BAMDA2gAMGUCMQCJao1H5+z8blUD2WdsJk6Dxv3J+ysTvLd6jLRl0mlpYxNjOyZQ
  41. LgGheQaRnUi/wr4CMEfDFXuxoJGZSZOoPHzoRgaLLPIxAJSdYsiJvRmEFOml+wG4
  42. DXZDjC5Ty3zfDBeWUA==
  43. -----END CERTIFICATE-----
  44. `
  45. authTestKubeConfig = `apiVersion: v1
  46. clusters:
  47. - cluster:
  48. server: https://api.my-domain.tld
  49. certificate-authority-data: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUNHVENDQVorZ0F3SUJBZ0lRQ2VDVFphejMyY2k1UGh3TEJDb3U4ekFLQmdncWhrak9QUVFEQXpCT01Rc3cKQ1FZRFZRUUdFd0pWVXpFWE1CVUdBMVVFQ2hNT1JHbG5hVU5sY25Rc0lFbHVZeTR4SmpBa0JnTlZCQU1USFVScApaMmxEWlhKMElGUk1VeUJGUTBNZ1VETTROQ0JTYjI5MElFYzFNQjRYRFRJeE1ERXhOVEF3TURBd01Gb1hEVFEyCk1ERXhOREl6TlRrMU9Wb3dUakVMTUFrR0ExVUVCaE1DVlZNeEZ6QVZCZ05WQkFvVERrUnBaMmxEWlhKMExDQkoKYm1NdU1TWXdKQVlEVlFRREV4MUVhV2RwUTJWeWRDQlVURk1nUlVORElGQXpPRFFnVW05dmRDQkhOVEIyTUJBRwpCeXFHU000OUFnRUdCU3VCQkFBaUEySUFCTUZFb2M4UmwxQ2EzaU9DTlFmTjBNc1luZEx4ZjNjMVR6dmRsSEpTCjdjSTcrT3o2ZTJ0WUlPeVpyc244YUxOMXVkc0o3TWdUOVU3R0NoMW1NRXk3SDBjS1BHRVFRaWw4cFFnTzRDTHAKMHpWb3pwdGpuNFMxbVUxWW9JNzFWT2VWeWFOQ01FQXdIUVlEVlIwT0JCWUVGTUZSUlZCWnF6N25MRnI2SUNJUwpCNENJZkJGcU1BNEdBMVVkRHdFQi93UUVBd0lCaGpBUEJnTlZIUk1CQWY4RUJUQURBUUgvTUFvR0NDcUdTTTQ5CkJBTURBMmdBTUdVQ01RQ0phbzFINSt6OGJsVUQyV2RzSms2RHh2M0oreXNUdkxkNmpMUmwwbWxwWXhOak95WlEKTGdHaGVRYVJuVWkvd3I0Q01FZkRGWHV4b0pHWlNaT29QSHpvUmdhTExQSXhBSlNkWXNpSnZSbUVGT21sK3dHNApEWFpEakM1VHkzemZEQmVXVUE9PQotLS0tLUVORCBDRVJUSUZJQ0FURS0tLS0tCg==
  50. name: mycluster
  51. contexts:
  52. - context:
  53. cluster: mycluster
  54. user: myuser
  55. name: mycontext
  56. current-context: mycontext
  57. kind: Config
  58. preferences: {}
  59. users:
  60. - name: myuser
  61. user:
  62. token: eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJPbmxpbmUgSldUIEJ1aWxkZXIiLCJpYXQiOjE3MTkzOTY4OTksImV4cCI6MTc1MDkzMjg4NywiYXVkIjoid3d3LmV4YW1wbGUuY29tIiwic3ViIjoianJvY2tldEBleGFtcGxlLmNvbSIsIkdpdmVuTmFtZSI6IkpvaG5ueSIsIlN1cm5hbWUiOiJSb2NrZXQiLCJFbWFpbCI6Impyb2NrZXRAZXhhbXBsZS5jb20iLCJSb2xlIjpbIk1hbmFnZXIiLCJQcm9qZWN0IEFkbWluaXN0cmF0b3IiXX0.xXrfIl0akhfjWU_BDl7Ad54SXje0YlJdnugzwh96VmM
  63. `
  64. serverURL = "https://my.test.tld"
  65. )
  66. func TestSetAuth(t *testing.T) {
  67. type fields struct {
  68. kube kclient.Client
  69. kubeclientset typedcorev1.CoreV1Interface
  70. store *esv1.KubernetesProvider
  71. namespace string
  72. storeKind string
  73. }
  74. type want = rest.Config
  75. tests := []struct {
  76. name string
  77. fields fields
  78. want *want
  79. wantErr bool
  80. }{
  81. {
  82. name: "should return err if no ca provided",
  83. fields: fields{
  84. store: &esv1.KubernetesProvider{
  85. Server: esv1.KubernetesServer{},
  86. },
  87. },
  88. want: nil,
  89. wantErr: true,
  90. },
  91. {
  92. name: "should return err if no auth provided",
  93. fields: fields{
  94. store: &esv1.KubernetesProvider{
  95. Server: esv1.KubernetesServer{
  96. CABundle: []byte(caCert),
  97. },
  98. },
  99. },
  100. want: nil,
  101. wantErr: true,
  102. },
  103. {
  104. name: "should fetch ca from Secret",
  105. fields: fields{
  106. namespace: "default",
  107. kube: fclient.NewClientBuilder().WithObjects(&corev1.Secret{
  108. ObjectMeta: metav1.ObjectMeta{
  109. Name: "foobar",
  110. Namespace: "default",
  111. },
  112. Data: map[string][]byte{
  113. "cert": []byte(caCert),
  114. "token": []byte("mytoken"),
  115. },
  116. }).Build(),
  117. store: &esv1.KubernetesProvider{
  118. Server: esv1.KubernetesServer{
  119. URL: serverURL,
  120. CAProvider: &esv1.CAProvider{
  121. Type: esv1.CAProviderTypeSecret,
  122. Name: "foobar",
  123. Key: "cert",
  124. },
  125. },
  126. Auth: &esv1.KubernetesAuth{
  127. Token: &esv1.TokenAuth{
  128. BearerToken: v1.SecretKeySelector{
  129. Name: "foobar",
  130. Namespace: pointer.To("shouldnotberelevant"),
  131. Key: "token",
  132. },
  133. },
  134. },
  135. },
  136. },
  137. want: &want{
  138. Host: serverURL,
  139. BearerToken: "mytoken",
  140. TLSClientConfig: rest.TLSClientConfig{
  141. CAData: []byte(caCert),
  142. },
  143. },
  144. wantErr: false,
  145. },
  146. {
  147. name: "should fetch ca from ConfigMap",
  148. fields: fields{
  149. namespace: "default",
  150. kube: fclient.NewClientBuilder().WithObjects(&corev1.Secret{
  151. ObjectMeta: metav1.ObjectMeta{
  152. Name: "foobar",
  153. Namespace: "default",
  154. },
  155. Data: map[string][]byte{
  156. "token": []byte("mytoken"),
  157. },
  158. }, &corev1.ConfigMap{
  159. ObjectMeta: metav1.ObjectMeta{
  160. Name: "foobar",
  161. Namespace: "default",
  162. },
  163. Data: map[string]string{
  164. "cert": "1234",
  165. },
  166. }).Build(),
  167. store: &esv1.KubernetesProvider{
  168. Server: esv1.KubernetesServer{
  169. URL: serverURL,
  170. CAProvider: &esv1.CAProvider{
  171. Type: esv1.CAProviderTypeConfigMap,
  172. Name: "foobar",
  173. Key: "cert",
  174. },
  175. },
  176. Auth: &esv1.KubernetesAuth{
  177. Token: &esv1.TokenAuth{
  178. BearerToken: v1.SecretKeySelector{
  179. Name: "foobar",
  180. Namespace: pointer.To("shouldnotberelevant"),
  181. Key: "token",
  182. },
  183. },
  184. },
  185. },
  186. },
  187. want: &want{
  188. Host: serverURL,
  189. BearerToken: "mytoken",
  190. TLSClientConfig: rest.TLSClientConfig{
  191. CAData: []byte("1234"),
  192. },
  193. },
  194. wantErr: false,
  195. },
  196. {
  197. name: "should set token from secret",
  198. fields: fields{
  199. namespace: "default",
  200. kube: fclient.NewClientBuilder().WithObjects(&corev1.Secret{
  201. ObjectMeta: metav1.ObjectMeta{
  202. Name: "foobar",
  203. Namespace: "default",
  204. },
  205. Data: map[string][]byte{
  206. "token": []byte("mytoken"),
  207. },
  208. }).Build(),
  209. store: &esv1.KubernetesProvider{
  210. Server: esv1.KubernetesServer{
  211. URL: serverURL,
  212. CABundle: []byte(caCert),
  213. },
  214. Auth: &esv1.KubernetesAuth{
  215. Token: &esv1.TokenAuth{
  216. BearerToken: v1.SecretKeySelector{
  217. Name: "foobar",
  218. Namespace: pointer.To("shouldnotberelevant"),
  219. Key: "token",
  220. },
  221. },
  222. },
  223. },
  224. },
  225. want: &want{
  226. Host: serverURL,
  227. BearerToken: "mytoken",
  228. TLSClientConfig: rest.TLSClientConfig{
  229. CAData: []byte(caCert),
  230. },
  231. },
  232. wantErr: false,
  233. },
  234. {
  235. name: "should set client cert from secret",
  236. fields: fields{
  237. namespace: "default",
  238. kube: fclient.NewClientBuilder().WithObjects(&corev1.Secret{
  239. ObjectMeta: metav1.ObjectMeta{
  240. Name: "mycert",
  241. Namespace: "default",
  242. },
  243. Data: map[string][]byte{
  244. "cert": []byte("my-cert"),
  245. "key": []byte("my-key"),
  246. },
  247. }).Build(),
  248. store: &esv1.KubernetesProvider{
  249. Server: esv1.KubernetesServer{
  250. URL: serverURL,
  251. CABundle: []byte(caCert),
  252. },
  253. Auth: &esv1.KubernetesAuth{
  254. Cert: &esv1.CertAuth{
  255. ClientCert: v1.SecretKeySelector{
  256. Name: "mycert",
  257. Key: "cert",
  258. },
  259. ClientKey: v1.SecretKeySelector{
  260. Name: "mycert",
  261. Key: "key",
  262. },
  263. },
  264. },
  265. },
  266. },
  267. want: &want{
  268. Host: serverURL,
  269. TLSClientConfig: rest.TLSClientConfig{
  270. CAData: []byte(caCert),
  271. CertData: []byte("my-cert"),
  272. KeyData: []byte("my-key"),
  273. },
  274. },
  275. wantErr: false,
  276. },
  277. {
  278. name: "should set token from service account",
  279. fields: fields{
  280. namespace: "default",
  281. kube: fclient.NewClientBuilder().WithObjects(&corev1.ServiceAccount{
  282. ObjectMeta: metav1.ObjectMeta{
  283. Name: "my-sa",
  284. Namespace: "default",
  285. },
  286. }).Build(),
  287. kubeclientset: utilfake.NewCreateTokenMock().WithToken("my-sa-token"),
  288. store: &esv1.KubernetesProvider{
  289. Server: esv1.KubernetesServer{
  290. URL: serverURL,
  291. CABundle: []byte(caCert),
  292. },
  293. Auth: &esv1.KubernetesAuth{
  294. ServiceAccount: &v1.ServiceAccountSelector{
  295. Name: "my-sa",
  296. Namespace: pointer.To("shouldnotberelevant"),
  297. },
  298. },
  299. },
  300. },
  301. want: &want{
  302. Host: serverURL,
  303. BearerToken: "my-sa-token",
  304. TLSClientConfig: rest.TLSClientConfig{
  305. CAData: []byte(caCert),
  306. },
  307. },
  308. wantErr: false,
  309. },
  310. {
  311. name: "should fail with missing URL",
  312. fields: fields{
  313. namespace: "default",
  314. kube: fclient.NewClientBuilder().WithObjects(&corev1.ServiceAccount{
  315. ObjectMeta: metav1.ObjectMeta{
  316. Name: "my-sa",
  317. Namespace: "default",
  318. },
  319. }).Build(),
  320. kubeclientset: utilfake.NewCreateTokenMock().WithToken("my-sa-token"),
  321. store: &esv1.KubernetesProvider{
  322. Server: esv1.KubernetesServer{
  323. CABundle: []byte(caCert),
  324. },
  325. Auth: &esv1.KubernetesAuth{
  326. ServiceAccount: &v1.ServiceAccountSelector{
  327. Name: "my-sa",
  328. Namespace: pointer.To("shouldnotberelevant"),
  329. },
  330. },
  331. },
  332. },
  333. want: nil,
  334. wantErr: true,
  335. },
  336. {
  337. name: "should read config from secret",
  338. fields: fields{
  339. namespace: "default",
  340. kube: fclient.NewClientBuilder().WithObjects(&corev1.Secret{
  341. ObjectMeta: metav1.ObjectMeta{
  342. Name: "foobar",
  343. Namespace: "default",
  344. },
  345. Data: map[string][]byte{
  346. "config": []byte(authTestKubeConfig),
  347. },
  348. }).Build(),
  349. store: &esv1.KubernetesProvider{
  350. AuthRef: &v1.SecretKeySelector{
  351. Name: "foobar",
  352. Namespace: pointer.To("default"),
  353. Key: "config",
  354. },
  355. },
  356. },
  357. want: &want{
  358. Host: "https://api.my-domain.tld",
  359. BearerToken: "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJPbmxpbmUgSldUIEJ1aWxkZXIiLCJpYXQiOjE3MTkzOTY4OTksImV4cCI6MTc1MDkzMjg4NywiYXVkIjoid3d3LmV4YW1wbGUuY29tIiwic3ViIjoianJvY2tldEBleGFtcGxlLmNvbSIsIkdpdmVuTmFtZSI6IkpvaG5ueSIsIlN1cm5hbWUiOiJSb2NrZXQiLCJFbWFpbCI6Impyb2NrZXRAZXhhbXBsZS5jb20iLCJSb2xlIjpbIk1hbmFnZXIiLCJQcm9qZWN0IEFkbWluaXN0cmF0b3IiXX0.xXrfIl0akhfjWU_BDl7Ad54SXje0YlJdnugzwh96VmM",
  360. TLSClientConfig: rest.TLSClientConfig{
  361. CAData: []byte(caCert),
  362. },
  363. },
  364. wantErr: false,
  365. },
  366. }
  367. for _, tt := range tests {
  368. t.Run(tt.name, func(t *testing.T) {
  369. k := &Client{
  370. ctrlClientset: tt.fields.kubeclientset,
  371. ctrlClient: tt.fields.kube,
  372. store: tt.fields.store,
  373. namespace: tt.fields.namespace,
  374. storeKind: tt.fields.storeKind,
  375. }
  376. cfg, err := k.getAuth(context.Background())
  377. if (err != nil) != tt.wantErr {
  378. t.Errorf("BaseClient.setAuth() error = %v, wantErr %v", err, tt.wantErr)
  379. }
  380. assert.Equal(t, tt.want, cfg)
  381. })
  382. }
  383. }