admission_test.go 41 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146
  1. /*
  2. Copyright 2014 The Kubernetes Authors.
  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. http://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 serviceaccount
  14. import (
  15. "context"
  16. "reflect"
  17. "strings"
  18. "testing"
  19. "github.com/stretchr/testify/assert"
  20. corev1 "k8s.io/api/core/v1"
  21. "k8s.io/apimachinery/pkg/api/errors"
  22. metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
  23. "k8s.io/apimachinery/pkg/types"
  24. "k8s.io/apimachinery/pkg/util/diff"
  25. "k8s.io/apiserver/pkg/admission"
  26. admissiontesting "k8s.io/apiserver/pkg/admission/testing"
  27. "k8s.io/client-go/informers"
  28. "k8s.io/client-go/kubernetes/fake"
  29. corev1listers "k8s.io/client-go/listers/core/v1"
  30. "k8s.io/client-go/tools/cache"
  31. api "k8s.io/kubernetes/pkg/apis/core"
  32. "k8s.io/kubernetes/pkg/controller"
  33. kubelet "k8s.io/kubernetes/pkg/kubelet/types"
  34. )
  35. var (
  36. deprecationDisabledBoundTokenVolume = false
  37. deprecationEnabledBoundTokenVolume = true
  38. )
  39. func TestIgnoresNonCreate(t *testing.T) {
  40. for _, op := range []admission.Operation{admission.Delete, admission.Connect} {
  41. handler := NewServiceAccount()
  42. if handler.Handles(op) {
  43. t.Errorf("Expected not to handle operation %s", op)
  44. }
  45. }
  46. }
  47. func TestIgnoresNonPodResource(t *testing.T) {
  48. pod := &api.Pod{}
  49. attrs := admission.NewAttributesRecord(pod, nil, api.Kind("Pod").WithVersion("version"), "myns", "myname", api.Resource("CustomResource").WithVersion("version"), "", admission.Create, &metav1.CreateOptions{}, false, nil)
  50. handler := admissiontesting.WithReinvocationTesting(t, NewServiceAccount())
  51. err := handler.Admit(context.TODO(), attrs, nil)
  52. if err != nil {
  53. t.Errorf("Expected non-pod resource allowed, got err: %v", err)
  54. }
  55. }
  56. func TestIgnoresNilObject(t *testing.T) {
  57. attrs := admission.NewAttributesRecord(nil, nil, api.Kind("Pod").WithVersion("version"), "myns", "myname", api.Resource("pods").WithVersion("version"), "", admission.Create, &metav1.CreateOptions{}, false, nil)
  58. handler := admissiontesting.WithReinvocationTesting(t, NewServiceAccount())
  59. err := handler.Admit(context.TODO(), attrs, nil)
  60. if err != nil {
  61. t.Errorf("Expected nil object allowed allowed, got err: %v", err)
  62. }
  63. }
  64. func TestIgnoresNonPodObject(t *testing.T) {
  65. obj := &api.Namespace{}
  66. attrs := admission.NewAttributesRecord(obj, nil, api.Kind("Pod").WithVersion("version"), "myns", "myname", api.Resource("pods").WithVersion("version"), "", admission.Create, &metav1.CreateOptions{}, false, nil)
  67. handler := admissiontesting.WithReinvocationTesting(t, NewServiceAccount())
  68. err := handler.Admit(context.TODO(), attrs, nil)
  69. if err != nil {
  70. t.Errorf("Expected non pod object allowed, got err: %v", err)
  71. }
  72. }
  73. func TestIgnoresMirrorPod(t *testing.T) {
  74. pod := &api.Pod{
  75. ObjectMeta: metav1.ObjectMeta{
  76. Annotations: map[string]string{
  77. kubelet.ConfigMirrorAnnotationKey: "true",
  78. },
  79. },
  80. Spec: api.PodSpec{
  81. Volumes: []api.Volume{
  82. {VolumeSource: api.VolumeSource{}},
  83. },
  84. },
  85. }
  86. attrs := admission.NewAttributesRecord(pod, nil, api.Kind("Pod").WithVersion("version"), "myns", "myname", api.Resource("pods").WithVersion("version"), "", admission.Create, &metav1.CreateOptions{}, false, nil)
  87. err := admissiontesting.WithReinvocationTesting(t, NewServiceAccount()).Admit(context.TODO(), attrs, nil)
  88. if err != nil {
  89. t.Errorf("Expected mirror pod without service account or secrets allowed, got err: %v", err)
  90. }
  91. }
  92. func TestRejectsMirrorPodWithServiceAccount(t *testing.T) {
  93. pod := &api.Pod{
  94. ObjectMeta: metav1.ObjectMeta{
  95. Annotations: map[string]string{
  96. kubelet.ConfigMirrorAnnotationKey: "true",
  97. },
  98. },
  99. Spec: api.PodSpec{
  100. ServiceAccountName: "default",
  101. },
  102. }
  103. attrs := admission.NewAttributesRecord(pod, nil, api.Kind("Pod").WithVersion("version"), "myns", "myname", api.Resource("pods").WithVersion("version"), "", admission.Create, &metav1.CreateOptions{}, false, nil)
  104. err := admissiontesting.WithReinvocationTesting(t, NewServiceAccount()).Admit(context.TODO(), attrs, nil)
  105. if err == nil {
  106. t.Errorf("Expected a mirror pod to be prevented from referencing a service account")
  107. }
  108. }
  109. func TestRejectsMirrorPodWithSecretVolumes(t *testing.T) {
  110. pod := &api.Pod{
  111. ObjectMeta: metav1.ObjectMeta{
  112. Annotations: map[string]string{
  113. kubelet.ConfigMirrorAnnotationKey: "true",
  114. },
  115. },
  116. Spec: api.PodSpec{
  117. Volumes: []api.Volume{
  118. {VolumeSource: api.VolumeSource{Secret: &api.SecretVolumeSource{}}},
  119. },
  120. },
  121. }
  122. attrs := admission.NewAttributesRecord(pod, nil, api.Kind("Pod").WithVersion("version"), "myns", "myname", api.Resource("pods").WithVersion("version"), "", admission.Create, &metav1.CreateOptions{}, false, nil)
  123. err := admissiontesting.WithReinvocationTesting(t, NewServiceAccount()).Admit(context.TODO(), attrs, nil)
  124. if err == nil {
  125. t.Errorf("Expected a mirror pod to be prevented from referencing a secret volume")
  126. }
  127. }
  128. func TestRejectsMirrorPodWithServiceAccountTokenVolumeProjections(t *testing.T) {
  129. pod := &api.Pod{
  130. ObjectMeta: metav1.ObjectMeta{
  131. Annotations: map[string]string{
  132. kubelet.ConfigMirrorAnnotationKey: "true",
  133. },
  134. },
  135. Spec: api.PodSpec{
  136. Volumes: []api.Volume{
  137. {VolumeSource: api.VolumeSource{
  138. Projected: &api.ProjectedVolumeSource{
  139. Sources: []api.VolumeProjection{{ServiceAccountToken: &api.ServiceAccountTokenProjection{}}},
  140. },
  141. },
  142. },
  143. },
  144. },
  145. }
  146. attrs := admission.NewAttributesRecord(pod, nil, api.Kind("Pod").WithVersion("version"), "myns", "myname", api.Resource("pods").WithVersion("version"), "", admission.Create, &metav1.CreateOptions{}, false, nil)
  147. err := admissiontesting.WithReinvocationTesting(t, NewServiceAccount()).Admit(context.TODO(), attrs, nil)
  148. if err == nil {
  149. t.Errorf("Expected a mirror pod to be prevented from referencing a ServiceAccountToken volume projection")
  150. }
  151. }
  152. func TestAssignsDefaultServiceAccountAndToleratesMissingAPIToken(t *testing.T) {
  153. ns := "myns"
  154. admit := NewServiceAccount()
  155. informerFactory := informers.NewSharedInformerFactory(nil, controller.NoResyncPeriodFunc())
  156. admit.SetExternalKubeInformerFactory(informerFactory)
  157. admit.MountServiceAccountToken = true
  158. admit.RequireAPIToken = false
  159. // Add the default service account for the ns into the cache
  160. informerFactory.Core().V1().ServiceAccounts().Informer().GetStore().Add(&corev1.ServiceAccount{
  161. ObjectMeta: metav1.ObjectMeta{
  162. Name: DefaultServiceAccountName,
  163. Namespace: ns,
  164. },
  165. })
  166. pod := &api.Pod{}
  167. attrs := admission.NewAttributesRecord(pod, nil, api.Kind("Pod").WithVersion("version"), ns, "myname", api.Resource("pods").WithVersion("version"), "", admission.Create, &metav1.CreateOptions{}, false, nil)
  168. err := admissiontesting.WithReinvocationTesting(t, admit).Admit(context.TODO(), attrs, nil)
  169. if err != nil {
  170. t.Errorf("Unexpected error: %v", err)
  171. }
  172. if pod.Spec.ServiceAccountName != DefaultServiceAccountName {
  173. t.Errorf("Expected service account %s assigned, got %s", DefaultServiceAccountName, pod.Spec.ServiceAccountName)
  174. }
  175. }
  176. func TestAssignsDefaultServiceAccountAndRejectsMissingAPIToken(t *testing.T) {
  177. ns := "myns"
  178. admit := NewServiceAccount()
  179. informerFactory := informers.NewSharedInformerFactory(nil, controller.NoResyncPeriodFunc())
  180. admit.SetExternalKubeInformerFactory(informerFactory)
  181. admit.MountServiceAccountToken = true
  182. admit.RequireAPIToken = true
  183. // Add the default service account for the ns into the cache
  184. informerFactory.Core().V1().ServiceAccounts().Informer().GetStore().Add(&corev1.ServiceAccount{
  185. ObjectMeta: metav1.ObjectMeta{
  186. Name: DefaultServiceAccountName,
  187. Namespace: ns,
  188. },
  189. })
  190. pod := &api.Pod{}
  191. attrs := admission.NewAttributesRecord(pod, nil, api.Kind("Pod").WithVersion("version"), ns, "myname", api.Resource("pods").WithVersion("version"), "", admission.Create, &metav1.CreateOptions{}, false, nil)
  192. err := admissiontesting.WithReinvocationTesting(t, admit).Admit(context.TODO(), attrs, nil)
  193. if err == nil || !errors.IsServerTimeout(err) {
  194. t.Errorf("Expected server timeout error for missing API token: %v", err)
  195. }
  196. }
  197. func TestAssignsDefaultServiceAccountAndBoundTokenWithNoSecretTokens(t *testing.T) {
  198. ns := "myns"
  199. admit := NewServiceAccount()
  200. informerFactory := informers.NewSharedInformerFactory(nil, controller.NoResyncPeriodFunc())
  201. admit.SetExternalKubeInformerFactory(informerFactory)
  202. admit.MountServiceAccountToken = true
  203. admit.RequireAPIToken = true
  204. admit.boundServiceAccountTokenVolume = true
  205. // Add the default service account for the ns into the cache
  206. informerFactory.Core().V1().ServiceAccounts().Informer().GetStore().Add(&corev1.ServiceAccount{
  207. ObjectMeta: metav1.ObjectMeta{
  208. Name: DefaultServiceAccountName,
  209. Namespace: ns,
  210. },
  211. })
  212. pod := &api.Pod{
  213. Spec: api.PodSpec{
  214. Containers: []api.Container{{}},
  215. },
  216. }
  217. attrs := admission.NewAttributesRecord(pod, nil, api.Kind("Pod").WithVersion("version"), ns, "myname", api.Resource("pods").WithVersion("version"), "", admission.Create, &metav1.CreateOptions{}, false, nil)
  218. err := admissiontesting.WithReinvocationTesting(t, admit).Admit(context.TODO(), attrs, nil)
  219. if err != nil {
  220. t.Fatalf("Expected success, got: %v", err)
  221. }
  222. expectedVolumes := []api.Volume{{
  223. Name: "cleared",
  224. VolumeSource: api.VolumeSource{
  225. Projected: &api.ProjectedVolumeSource{
  226. Sources: []api.VolumeProjection{
  227. {ServiceAccountToken: &api.ServiceAccountTokenProjection{ExpirationSeconds: 3600, Path: "token"}},
  228. {ConfigMap: &api.ConfigMapProjection{LocalObjectReference: api.LocalObjectReference{Name: "kube-root-ca.crt"}, Items: []api.KeyToPath{{Key: "ca.crt", Path: "ca.crt"}}}},
  229. {DownwardAPI: &api.DownwardAPIProjection{Items: []api.DownwardAPIVolumeFile{{Path: "namespace", FieldRef: &api.ObjectFieldSelector{APIVersion: "v1", FieldPath: "metadata.namespace"}}}}},
  230. },
  231. },
  232. },
  233. }}
  234. expectedVolumeMounts := []api.VolumeMount{{
  235. Name: "cleared",
  236. ReadOnly: true,
  237. MountPath: "/var/run/secrets/kubernetes.io/serviceaccount",
  238. }}
  239. // clear generated volume names
  240. for i := range pod.Spec.Volumes {
  241. if len(pod.Spec.Volumes[i].Name) > 0 {
  242. pod.Spec.Volumes[i].Name = "cleared"
  243. }
  244. }
  245. for i := range pod.Spec.Containers[0].VolumeMounts {
  246. if len(pod.Spec.Containers[0].VolumeMounts[i].Name) > 0 {
  247. pod.Spec.Containers[0].VolumeMounts[i].Name = "cleared"
  248. }
  249. }
  250. if !reflect.DeepEqual(expectedVolumes, pod.Spec.Volumes) {
  251. t.Errorf("unexpected volumes: %s", diff.ObjectReflectDiff(expectedVolumes, pod.Spec.Volumes))
  252. }
  253. if !reflect.DeepEqual(expectedVolumeMounts, pod.Spec.Containers[0].VolumeMounts) {
  254. t.Errorf("unexpected volumes: %s", diff.ObjectReflectDiff(expectedVolumeMounts, pod.Spec.Containers[0].VolumeMounts))
  255. }
  256. }
  257. func TestFetchesUncachedServiceAccount(t *testing.T) {
  258. ns := "myns"
  259. // Build a test client that the admission plugin can use to look up the service account missing from its cache
  260. client := fake.NewSimpleClientset(&corev1.ServiceAccount{
  261. ObjectMeta: metav1.ObjectMeta{
  262. Name: DefaultServiceAccountName,
  263. Namespace: ns,
  264. },
  265. })
  266. admit := NewServiceAccount()
  267. informerFactory := informers.NewSharedInformerFactory(nil, controller.NoResyncPeriodFunc())
  268. admit.SetExternalKubeInformerFactory(informerFactory)
  269. admit.client = client
  270. admit.RequireAPIToken = false
  271. pod := &api.Pod{}
  272. attrs := admission.NewAttributesRecord(pod, nil, api.Kind("Pod").WithVersion("version"), ns, "myname", api.Resource("pods").WithVersion("version"), "", admission.Create, &metav1.CreateOptions{}, false, nil)
  273. err := admissiontesting.WithReinvocationTesting(t, admit).Admit(context.TODO(), attrs, nil)
  274. if err != nil {
  275. t.Errorf("Unexpected error: %v", err)
  276. }
  277. if pod.Spec.ServiceAccountName != DefaultServiceAccountName {
  278. t.Errorf("Expected service account %s assigned, got %s", DefaultServiceAccountName, pod.Spec.ServiceAccountName)
  279. }
  280. }
  281. func TestDeniesInvalidServiceAccount(t *testing.T) {
  282. ns := "myns"
  283. // Build a test client that the admission plugin can use to look up the service account missing from its cache
  284. client := fake.NewSimpleClientset()
  285. admit := NewServiceAccount()
  286. admit.SetExternalKubeClientSet(client)
  287. informerFactory := informers.NewSharedInformerFactory(nil, controller.NoResyncPeriodFunc())
  288. admit.SetExternalKubeInformerFactory(informerFactory)
  289. pod := &api.Pod{}
  290. attrs := admission.NewAttributesRecord(pod, nil, api.Kind("Pod").WithVersion("version"), ns, "myname", api.Resource("pods").WithVersion("version"), "", admission.Create, &metav1.CreateOptions{}, false, nil)
  291. err := admissiontesting.WithReinvocationTesting(t, admit).Admit(context.TODO(), attrs, nil)
  292. if err == nil {
  293. t.Errorf("Expected error for missing service account, got none")
  294. }
  295. }
  296. func TestAutomountsAPIToken(t *testing.T) {
  297. testBoundServiceAccountTokenVolumePhases(t, func(t *testing.T, applyFeatures func(*Plugin) *Plugin) {
  298. admit := applyFeatures(NewServiceAccount())
  299. informerFactory := informers.NewSharedInformerFactory(nil, controller.NoResyncPeriodFunc())
  300. admit.SetExternalKubeInformerFactory(informerFactory)
  301. admit.generateName = testGenerateName
  302. admit.MountServiceAccountToken = true
  303. admit.RequireAPIToken = true
  304. ns := "myns"
  305. serviceAccountName := DefaultServiceAccountName
  306. serviceAccountUID := "12345"
  307. tokenName := "token-name"
  308. if admit.boundServiceAccountTokenVolume {
  309. tokenName = generatedVolumeName
  310. }
  311. expectedVolume := admit.createVolume(tokenName, tokenName)
  312. expectedVolumeMount := api.VolumeMount{
  313. Name: tokenName,
  314. ReadOnly: true,
  315. MountPath: DefaultAPITokenMountPath,
  316. }
  317. // Add the default service account for the ns with a token into the cache
  318. informerFactory.Core().V1().ServiceAccounts().Informer().GetStore().Add(&corev1.ServiceAccount{
  319. ObjectMeta: metav1.ObjectMeta{
  320. Name: serviceAccountName,
  321. Namespace: ns,
  322. UID: types.UID(serviceAccountUID),
  323. },
  324. Secrets: []corev1.ObjectReference{
  325. {Name: tokenName},
  326. },
  327. })
  328. // Add a token for the service account into the cache
  329. informerFactory.Core().V1().Secrets().Informer().GetStore().Add(&corev1.Secret{
  330. ObjectMeta: metav1.ObjectMeta{
  331. Name: tokenName,
  332. Namespace: ns,
  333. Annotations: map[string]string{
  334. corev1.ServiceAccountNameKey: serviceAccountName,
  335. corev1.ServiceAccountUIDKey: serviceAccountUID,
  336. },
  337. },
  338. Type: corev1.SecretTypeServiceAccountToken,
  339. Data: map[string][]byte{
  340. api.ServiceAccountTokenKey: []byte("token-data"),
  341. },
  342. })
  343. pod := &api.Pod{
  344. Spec: api.PodSpec{
  345. Containers: []api.Container{
  346. {},
  347. },
  348. },
  349. }
  350. attrs := admission.NewAttributesRecord(pod, nil, api.Kind("Pod").WithVersion("version"), ns, "myname", api.Resource("pods").WithVersion("version"), "", admission.Create, &metav1.CreateOptions{}, false, nil)
  351. err := admissiontesting.WithReinvocationTesting(t, admit).Admit(context.TODO(), attrs, nil)
  352. if err != nil {
  353. t.Errorf("Unexpected error: %v", err)
  354. }
  355. if pod.Spec.ServiceAccountName != DefaultServiceAccountName {
  356. t.Errorf("Expected service account %s assigned, got %s", DefaultServiceAccountName, pod.Spec.ServiceAccountName)
  357. }
  358. if len(pod.Spec.Volumes) != 1 {
  359. t.Fatalf("Expected 1 volume, got %d", len(pod.Spec.Volumes))
  360. }
  361. if !reflect.DeepEqual(expectedVolume, pod.Spec.Volumes[0]) {
  362. t.Fatalf("Expected\n\t%#v\ngot\n\t%#v", expectedVolume, pod.Spec.Volumes[0])
  363. }
  364. if len(pod.Spec.Containers[0].VolumeMounts) != 1 {
  365. t.Fatalf("Expected 1 volume mount, got %d", len(pod.Spec.Containers[0].VolumeMounts))
  366. }
  367. if !reflect.DeepEqual(expectedVolumeMount, pod.Spec.Containers[0].VolumeMounts[0]) {
  368. t.Fatalf("Expected\n\t%#v\ngot\n\t%#v", expectedVolumeMount, pod.Spec.Containers[0].VolumeMounts[0])
  369. }
  370. // testing InitContainers
  371. pod = &api.Pod{
  372. Spec: api.PodSpec{
  373. InitContainers: []api.Container{
  374. {},
  375. },
  376. },
  377. }
  378. attrs = admission.NewAttributesRecord(pod, nil, api.Kind("Pod").WithVersion("version"), ns, "myname", api.Resource("pods").WithVersion("version"), "", admission.Create, &metav1.CreateOptions{}, false, nil)
  379. if err := admissiontesting.WithReinvocationTesting(t, admit).Admit(context.TODO(), attrs, nil); err != nil {
  380. t.Errorf("Unexpected error: %v", err)
  381. }
  382. if pod.Spec.ServiceAccountName != DefaultServiceAccountName {
  383. t.Errorf("Expected service account %s assigned, got %s", DefaultServiceAccountName, pod.Spec.ServiceAccountName)
  384. }
  385. if len(pod.Spec.Volumes) != 1 {
  386. t.Fatalf("Expected 1 volume, got %d", len(pod.Spec.Volumes))
  387. }
  388. if !reflect.DeepEqual(expectedVolume, pod.Spec.Volumes[0]) {
  389. t.Fatalf("Expected\n\t%#v\ngot\n\t%#v", expectedVolume, pod.Spec.Volumes[0])
  390. }
  391. if len(pod.Spec.InitContainers[0].VolumeMounts) != 1 {
  392. t.Fatalf("Expected 1 volume mount, got %d", len(pod.Spec.InitContainers[0].VolumeMounts))
  393. }
  394. if !reflect.DeepEqual(expectedVolumeMount, pod.Spec.InitContainers[0].VolumeMounts[0]) {
  395. t.Fatalf("Expected\n\t%#v\ngot\n\t%#v", expectedVolumeMount, pod.Spec.InitContainers[0].VolumeMounts[0])
  396. }
  397. })
  398. }
  399. func TestRespectsExistingMount(t *testing.T) {
  400. testBoundServiceAccountTokenVolumePhases(t, func(t *testing.T, applyFeatures func(*Plugin) *Plugin) {
  401. ns := "myns"
  402. tokenName := "token-name"
  403. serviceAccountName := DefaultServiceAccountName
  404. serviceAccountUID := "12345"
  405. expectedVolumeMount := api.VolumeMount{
  406. Name: "my-custom-mount",
  407. ReadOnly: false,
  408. MountPath: DefaultAPITokenMountPath,
  409. }
  410. admit := applyFeatures(NewServiceAccount())
  411. informerFactory := informers.NewSharedInformerFactory(nil, controller.NoResyncPeriodFunc())
  412. admit.SetExternalKubeInformerFactory(informerFactory)
  413. admit.MountServiceAccountToken = true
  414. admit.RequireAPIToken = true
  415. // Add the default service account for the ns with a token into the cache
  416. informerFactory.Core().V1().ServiceAccounts().Informer().GetStore().Add(&corev1.ServiceAccount{
  417. ObjectMeta: metav1.ObjectMeta{
  418. Name: serviceAccountName,
  419. Namespace: ns,
  420. UID: types.UID(serviceAccountUID),
  421. },
  422. Secrets: []corev1.ObjectReference{
  423. {Name: tokenName},
  424. },
  425. })
  426. // Add a token for the service account into the cache
  427. informerFactory.Core().V1().Secrets().Informer().GetStore().Add(&corev1.Secret{
  428. ObjectMeta: metav1.ObjectMeta{
  429. Name: tokenName,
  430. Namespace: ns,
  431. Annotations: map[string]string{
  432. corev1.ServiceAccountNameKey: serviceAccountName,
  433. corev1.ServiceAccountUIDKey: serviceAccountUID,
  434. },
  435. },
  436. Type: corev1.SecretTypeServiceAccountToken,
  437. Data: map[string][]byte{
  438. corev1.ServiceAccountTokenKey: []byte("token-data"),
  439. },
  440. })
  441. // Define a pod with a container that already mounts a volume at the API token path
  442. // Admission should respect that
  443. // Additionally, no volume should be created if no container is going to use it
  444. pod := &api.Pod{
  445. Spec: api.PodSpec{
  446. Containers: []api.Container{
  447. {
  448. VolumeMounts: []api.VolumeMount{
  449. expectedVolumeMount,
  450. },
  451. },
  452. },
  453. },
  454. }
  455. attrs := admission.NewAttributesRecord(pod, nil, api.Kind("Pod").WithVersion("version"), ns, "myname", api.Resource("pods").WithVersion("version"), "", admission.Create, &metav1.CreateOptions{}, false, nil)
  456. err := admissiontesting.WithReinvocationTesting(t, admit).Admit(context.TODO(), attrs, nil)
  457. if err != nil {
  458. t.Errorf("Unexpected error: %v", err)
  459. }
  460. if pod.Spec.ServiceAccountName != DefaultServiceAccountName {
  461. t.Errorf("Expected service account %s assigned, got %s", DefaultServiceAccountName, pod.Spec.ServiceAccountName)
  462. }
  463. if len(pod.Spec.Volumes) != 0 {
  464. t.Fatalf("Expected 0 volumes (shouldn't create a volume for a secret we don't need), got %d", len(pod.Spec.Volumes))
  465. }
  466. if len(pod.Spec.Containers[0].VolumeMounts) != 1 {
  467. t.Fatalf("Expected 1 volume mount, got %d", len(pod.Spec.Containers[0].VolumeMounts))
  468. }
  469. if !reflect.DeepEqual(expectedVolumeMount, pod.Spec.Containers[0].VolumeMounts[0]) {
  470. t.Fatalf("Expected\n\t%#v\ngot\n\t%#v", expectedVolumeMount, pod.Spec.Containers[0].VolumeMounts[0])
  471. }
  472. // check init containers
  473. pod = &api.Pod{
  474. Spec: api.PodSpec{
  475. InitContainers: []api.Container{
  476. {
  477. VolumeMounts: []api.VolumeMount{
  478. expectedVolumeMount,
  479. },
  480. },
  481. },
  482. },
  483. }
  484. attrs = admission.NewAttributesRecord(pod, nil, api.Kind("Pod").WithVersion("version"), ns, "myname", api.Resource("pods").WithVersion("version"), "", admission.Create, &metav1.CreateOptions{}, false, nil)
  485. if err := admissiontesting.WithReinvocationTesting(t, admit).Admit(context.TODO(), attrs, nil); err != nil {
  486. t.Errorf("Unexpected error: %v", err)
  487. }
  488. if pod.Spec.ServiceAccountName != DefaultServiceAccountName {
  489. t.Errorf("Expected service account %s assigned, got %s", DefaultServiceAccountName, pod.Spec.ServiceAccountName)
  490. }
  491. if len(pod.Spec.Volumes) != 0 {
  492. t.Fatalf("Expected 0 volumes (shouldn't create a volume for a secret we don't need), got %d", len(pod.Spec.Volumes))
  493. }
  494. if len(pod.Spec.InitContainers[0].VolumeMounts) != 1 {
  495. t.Fatalf("Expected 1 volume mount, got %d", len(pod.Spec.InitContainers[0].VolumeMounts))
  496. }
  497. if !reflect.DeepEqual(expectedVolumeMount, pod.Spec.InitContainers[0].VolumeMounts[0]) {
  498. t.Fatalf("Expected\n\t%#v\ngot\n\t%#v", expectedVolumeMount, pod.Spec.InitContainers[0].VolumeMounts[0])
  499. }
  500. })
  501. }
  502. func TestAllowsReferencedSecret(t *testing.T) {
  503. ns := "myns"
  504. admit := NewServiceAccount()
  505. informerFactory := informers.NewSharedInformerFactory(nil, controller.NoResyncPeriodFunc())
  506. admit.SetExternalKubeInformerFactory(informerFactory)
  507. admit.LimitSecretReferences = true
  508. admit.RequireAPIToken = false
  509. // Add the default service account for the ns with a secret reference into the cache
  510. informerFactory.Core().V1().ServiceAccounts().Informer().GetStore().Add(&corev1.ServiceAccount{
  511. ObjectMeta: metav1.ObjectMeta{
  512. Name: DefaultServiceAccountName,
  513. Namespace: ns,
  514. },
  515. Secrets: []corev1.ObjectReference{
  516. {Name: "foo"},
  517. },
  518. })
  519. pod1 := &api.Pod{
  520. Spec: api.PodSpec{
  521. Volumes: []api.Volume{
  522. {VolumeSource: api.VolumeSource{Secret: &api.SecretVolumeSource{SecretName: "foo"}}},
  523. },
  524. },
  525. }
  526. attrs := admission.NewAttributesRecord(pod1, nil, api.Kind("Pod").WithVersion("version"), ns, "myname", api.Resource("pods").WithVersion("version"), "", admission.Create, &metav1.CreateOptions{}, false, nil)
  527. if err := admissiontesting.WithReinvocationTesting(t, admit).Admit(context.TODO(), attrs, nil); err != nil {
  528. t.Errorf("Unexpected error: %v", err)
  529. }
  530. pod2 := &api.Pod{
  531. Spec: api.PodSpec{
  532. Containers: []api.Container{
  533. {
  534. Name: "container-1",
  535. Env: []api.EnvVar{
  536. {
  537. Name: "env-1",
  538. ValueFrom: &api.EnvVarSource{
  539. SecretKeyRef: &api.SecretKeySelector{
  540. LocalObjectReference: api.LocalObjectReference{Name: "foo"},
  541. },
  542. },
  543. },
  544. },
  545. },
  546. },
  547. },
  548. }
  549. attrs = admission.NewAttributesRecord(pod2, nil, api.Kind("Pod").WithVersion("version"), ns, "myname", api.Resource("pods").WithVersion("version"), "", admission.Create, &metav1.CreateOptions{}, false, nil)
  550. if err := admissiontesting.WithReinvocationTesting(t, admit).Admit(context.TODO(), attrs, nil); err != nil {
  551. t.Errorf("Unexpected error: %v", err)
  552. }
  553. pod2 = &api.Pod{
  554. Spec: api.PodSpec{
  555. InitContainers: []api.Container{
  556. {
  557. Name: "container-1",
  558. Env: []api.EnvVar{
  559. {
  560. Name: "env-1",
  561. ValueFrom: &api.EnvVarSource{
  562. SecretKeyRef: &api.SecretKeySelector{
  563. LocalObjectReference: api.LocalObjectReference{Name: "foo"},
  564. },
  565. },
  566. },
  567. },
  568. },
  569. },
  570. },
  571. }
  572. attrs = admission.NewAttributesRecord(pod2, nil, api.Kind("Pod").WithVersion("version"), ns, "myname", api.Resource("pods").WithVersion("version"), "", admission.Create, &metav1.CreateOptions{}, false, nil)
  573. if err := admissiontesting.WithReinvocationTesting(t, admit).Admit(context.TODO(), attrs, nil); err != nil {
  574. t.Errorf("Unexpected error: %v", err)
  575. }
  576. }
  577. func TestRejectsUnreferencedSecretVolumes(t *testing.T) {
  578. ns := "myns"
  579. admit := NewServiceAccount()
  580. informerFactory := informers.NewSharedInformerFactory(nil, controller.NoResyncPeriodFunc())
  581. admit.SetExternalKubeInformerFactory(informerFactory)
  582. admit.LimitSecretReferences = true
  583. admit.RequireAPIToken = false
  584. // Add the default service account for the ns into the cache
  585. informerFactory.Core().V1().ServiceAccounts().Informer().GetStore().Add(&corev1.ServiceAccount{
  586. ObjectMeta: metav1.ObjectMeta{
  587. Name: DefaultServiceAccountName,
  588. Namespace: ns,
  589. },
  590. })
  591. pod1 := &api.Pod{
  592. Spec: api.PodSpec{
  593. Volumes: []api.Volume{
  594. {VolumeSource: api.VolumeSource{Secret: &api.SecretVolumeSource{SecretName: "foo"}}},
  595. },
  596. },
  597. }
  598. attrs := admission.NewAttributesRecord(pod1, nil, api.Kind("Pod").WithVersion("version"), ns, "myname", api.Resource("pods").WithVersion("version"), "", admission.Create, &metav1.CreateOptions{}, false, nil)
  599. if err := admissiontesting.WithReinvocationTesting(t, admit).Admit(context.TODO(), attrs, nil); err == nil {
  600. t.Errorf("Expected rejection for using a secret the service account does not reference")
  601. }
  602. pod2 := &api.Pod{
  603. Spec: api.PodSpec{
  604. Containers: []api.Container{
  605. {
  606. Name: "container-1",
  607. Env: []api.EnvVar{
  608. {
  609. Name: "env-1",
  610. ValueFrom: &api.EnvVarSource{
  611. SecretKeyRef: &api.SecretKeySelector{
  612. LocalObjectReference: api.LocalObjectReference{Name: "foo"},
  613. },
  614. },
  615. },
  616. },
  617. },
  618. },
  619. },
  620. }
  621. attrs = admission.NewAttributesRecord(pod2, nil, api.Kind("Pod").WithVersion("version"), ns, "myname", api.Resource("pods").WithVersion("version"), "", admission.Create, &metav1.CreateOptions{}, false, nil)
  622. if err := admissiontesting.WithReinvocationTesting(t, admit).Admit(context.TODO(), attrs, nil); err == nil || !strings.Contains(err.Error(), "with envVar") {
  623. t.Errorf("Unexpected error: %v", err)
  624. }
  625. pod2 = &api.Pod{
  626. Spec: api.PodSpec{
  627. InitContainers: []api.Container{
  628. {
  629. Name: "container-1",
  630. Env: []api.EnvVar{
  631. {
  632. Name: "env-1",
  633. ValueFrom: &api.EnvVarSource{
  634. SecretKeyRef: &api.SecretKeySelector{
  635. LocalObjectReference: api.LocalObjectReference{Name: "foo"},
  636. },
  637. },
  638. },
  639. },
  640. },
  641. },
  642. },
  643. }
  644. attrs = admission.NewAttributesRecord(pod2, nil, api.Kind("Pod").WithVersion("version"), ns, "myname", api.Resource("pods").WithVersion("version"), "", admission.Create, &metav1.CreateOptions{}, false, nil)
  645. if err := admissiontesting.WithReinvocationTesting(t, admit).Admit(context.TODO(), attrs, nil); err == nil || !strings.Contains(err.Error(), "with envVar") {
  646. t.Errorf("Unexpected error: %v", err)
  647. }
  648. }
  649. func TestAllowUnreferencedSecretVolumesForPermissiveSAs(t *testing.T) {
  650. ns := "myns"
  651. admit := NewServiceAccount()
  652. informerFactory := informers.NewSharedInformerFactory(nil, controller.NoResyncPeriodFunc())
  653. admit.SetExternalKubeInformerFactory(informerFactory)
  654. admit.LimitSecretReferences = false
  655. admit.RequireAPIToken = false
  656. // Add the default service account for the ns into the cache
  657. informerFactory.Core().V1().ServiceAccounts().Informer().GetStore().Add(&corev1.ServiceAccount{
  658. ObjectMeta: metav1.ObjectMeta{
  659. Name: DefaultServiceAccountName,
  660. Namespace: ns,
  661. Annotations: map[string]string{EnforceMountableSecretsAnnotation: "true"},
  662. },
  663. })
  664. pod := &api.Pod{
  665. Spec: api.PodSpec{
  666. Volumes: []api.Volume{
  667. {VolumeSource: api.VolumeSource{Secret: &api.SecretVolumeSource{SecretName: "foo"}}},
  668. },
  669. },
  670. }
  671. attrs := admission.NewAttributesRecord(pod, nil, api.Kind("Pod").WithVersion("version"), ns, "myname", api.Resource("pods").WithVersion("version"), "", admission.Create, &metav1.CreateOptions{}, false, nil)
  672. err := admissiontesting.WithReinvocationTesting(t, admit).Admit(context.TODO(), attrs, nil)
  673. if err == nil {
  674. t.Errorf("Expected rejection for using a secret the service account does not reference")
  675. }
  676. }
  677. func TestAllowsReferencedImagePullSecrets(t *testing.T) {
  678. ns := "myns"
  679. admit := NewServiceAccount()
  680. informerFactory := informers.NewSharedInformerFactory(nil, controller.NoResyncPeriodFunc())
  681. admit.SetExternalKubeInformerFactory(informerFactory)
  682. admit.LimitSecretReferences = true
  683. admit.RequireAPIToken = false
  684. // Add the default service account for the ns with a secret reference into the cache
  685. informerFactory.Core().V1().ServiceAccounts().Informer().GetStore().Add(&corev1.ServiceAccount{
  686. ObjectMeta: metav1.ObjectMeta{
  687. Name: DefaultServiceAccountName,
  688. Namespace: ns,
  689. },
  690. ImagePullSecrets: []corev1.LocalObjectReference{
  691. {Name: "foo"},
  692. },
  693. })
  694. pod := &api.Pod{
  695. Spec: api.PodSpec{
  696. ImagePullSecrets: []api.LocalObjectReference{{Name: "foo"}},
  697. },
  698. }
  699. attrs := admission.NewAttributesRecord(pod, nil, api.Kind("Pod").WithVersion("version"), ns, "myname", api.Resource("pods").WithVersion("version"), "", admission.Create, &metav1.CreateOptions{}, false, nil)
  700. err := admissiontesting.WithReinvocationTesting(t, admit).Admit(context.TODO(), attrs, nil)
  701. if err != nil {
  702. t.Errorf("Unexpected error: %v", err)
  703. }
  704. }
  705. func TestRejectsUnreferencedImagePullSecrets(t *testing.T) {
  706. ns := "myns"
  707. admit := NewServiceAccount()
  708. informerFactory := informers.NewSharedInformerFactory(nil, controller.NoResyncPeriodFunc())
  709. admit.SetExternalKubeInformerFactory(informerFactory)
  710. admit.LimitSecretReferences = true
  711. admit.RequireAPIToken = false
  712. // Add the default service account for the ns into the cache
  713. informerFactory.Core().V1().ServiceAccounts().Informer().GetStore().Add(&corev1.ServiceAccount{
  714. ObjectMeta: metav1.ObjectMeta{
  715. Name: DefaultServiceAccountName,
  716. Namespace: ns,
  717. },
  718. })
  719. pod := &api.Pod{
  720. Spec: api.PodSpec{
  721. ImagePullSecrets: []api.LocalObjectReference{{Name: "foo"}},
  722. },
  723. }
  724. attrs := admission.NewAttributesRecord(pod, nil, api.Kind("Pod").WithVersion("version"), ns, "myname", api.Resource("pods").WithVersion("version"), "", admission.Create, &metav1.CreateOptions{}, false, nil)
  725. err := admissiontesting.WithReinvocationTesting(t, admit).Admit(context.TODO(), attrs, nil)
  726. if err == nil {
  727. t.Errorf("Expected rejection for using a secret the service account does not reference")
  728. }
  729. }
  730. func TestDoNotAddImagePullSecrets(t *testing.T) {
  731. ns := "myns"
  732. admit := NewServiceAccount()
  733. informerFactory := informers.NewSharedInformerFactory(nil, controller.NoResyncPeriodFunc())
  734. admit.SetExternalKubeInformerFactory(informerFactory)
  735. admit.LimitSecretReferences = true
  736. admit.RequireAPIToken = false
  737. // Add the default service account for the ns with a secret reference into the cache
  738. informerFactory.Core().V1().ServiceAccounts().Informer().GetStore().Add(&corev1.ServiceAccount{
  739. ObjectMeta: metav1.ObjectMeta{
  740. Name: DefaultServiceAccountName,
  741. Namespace: ns,
  742. },
  743. ImagePullSecrets: []corev1.LocalObjectReference{
  744. {Name: "foo"},
  745. {Name: "bar"},
  746. },
  747. })
  748. pod := &api.Pod{
  749. Spec: api.PodSpec{
  750. ImagePullSecrets: []api.LocalObjectReference{{Name: "foo"}},
  751. },
  752. }
  753. attrs := admission.NewAttributesRecord(pod, nil, api.Kind("Pod").WithVersion("version"), ns, "myname", api.Resource("pods").WithVersion("version"), "", admission.Create, &metav1.CreateOptions{}, false, nil)
  754. err := admissiontesting.WithReinvocationTesting(t, admit).Admit(context.TODO(), attrs, nil)
  755. if err != nil {
  756. t.Errorf("Unexpected error: %v", err)
  757. }
  758. if len(pod.Spec.ImagePullSecrets) != 1 || pod.Spec.ImagePullSecrets[0].Name != "foo" {
  759. t.Errorf("unexpected image pull secrets: %v", pod.Spec.ImagePullSecrets)
  760. }
  761. }
  762. func TestAddImagePullSecrets(t *testing.T) {
  763. ns := "myns"
  764. admit := NewServiceAccount()
  765. informerFactory := informers.NewSharedInformerFactory(nil, controller.NoResyncPeriodFunc())
  766. admit.SetExternalKubeInformerFactory(informerFactory)
  767. admit.LimitSecretReferences = true
  768. admit.RequireAPIToken = false
  769. sa := &corev1.ServiceAccount{
  770. ObjectMeta: metav1.ObjectMeta{
  771. Name: DefaultServiceAccountName,
  772. Namespace: ns,
  773. },
  774. ImagePullSecrets: []corev1.LocalObjectReference{
  775. {Name: "foo"},
  776. {Name: "bar"},
  777. },
  778. }
  779. // Add the default service account for the ns with a secret reference into the cache
  780. informerFactory.Core().V1().ServiceAccounts().Informer().GetStore().Add(sa)
  781. pod := &api.Pod{}
  782. attrs := admission.NewAttributesRecord(pod, nil, api.Kind("Pod").WithVersion("version"), ns, "myname", api.Resource("pods").WithVersion("version"), "", admission.Create, &metav1.CreateOptions{}, false, nil)
  783. err := admissiontesting.WithReinvocationTesting(t, admit).Admit(context.TODO(), attrs, nil)
  784. if err != nil {
  785. t.Errorf("Unexpected error: %v", err)
  786. }
  787. assert.EqualValues(t, sa.ImagePullSecrets, pod.Spec.ImagePullSecrets, "expected %v, got %v", sa.ImagePullSecrets, pod.Spec.ImagePullSecrets)
  788. pod.Spec.ImagePullSecrets[1] = api.LocalObjectReference{Name: "baz"}
  789. if reflect.DeepEqual(sa.ImagePullSecrets, pod.Spec.ImagePullSecrets) {
  790. t.Errorf("accidentally mutated the ServiceAccount.ImagePullSecrets: %v", sa.ImagePullSecrets)
  791. }
  792. }
  793. func TestMultipleReferencedSecrets(t *testing.T) {
  794. var (
  795. ns = "myns"
  796. serviceAccountName = "mysa"
  797. serviceAccountUID = "mysauid"
  798. token1 = "token1"
  799. token2 = "token2"
  800. )
  801. admit := NewServiceAccount()
  802. informerFactory := informers.NewSharedInformerFactory(nil, controller.NoResyncPeriodFunc())
  803. admit.SetExternalKubeInformerFactory(informerFactory)
  804. admit.MountServiceAccountToken = true
  805. admit.RequireAPIToken = true
  806. sa := &corev1.ServiceAccount{
  807. ObjectMeta: metav1.ObjectMeta{
  808. Name: serviceAccountName,
  809. UID: types.UID(serviceAccountUID),
  810. Namespace: ns,
  811. },
  812. Secrets: []corev1.ObjectReference{
  813. {Name: token1},
  814. {Name: token2},
  815. },
  816. }
  817. informerFactory.Core().V1().ServiceAccounts().Informer().GetStore().Add(sa)
  818. // Add two tokens for the service account into the cache.
  819. informerFactory.Core().V1().Secrets().Informer().GetStore().Add(&corev1.Secret{
  820. ObjectMeta: metav1.ObjectMeta{
  821. Name: token2,
  822. Namespace: ns,
  823. Annotations: map[string]string{
  824. api.ServiceAccountNameKey: serviceAccountName,
  825. api.ServiceAccountUIDKey: serviceAccountUID,
  826. },
  827. },
  828. Type: corev1.SecretTypeServiceAccountToken,
  829. Data: map[string][]byte{
  830. api.ServiceAccountTokenKey: []byte("token-data"),
  831. },
  832. })
  833. informerFactory.Core().V1().Secrets().Informer().GetStore().Add(&corev1.Secret{
  834. ObjectMeta: metav1.ObjectMeta{
  835. Name: token1,
  836. Namespace: ns,
  837. Annotations: map[string]string{
  838. api.ServiceAccountNameKey: serviceAccountName,
  839. api.ServiceAccountUIDKey: serviceAccountUID,
  840. },
  841. },
  842. Type: corev1.SecretTypeServiceAccountToken,
  843. Data: map[string][]byte{
  844. api.ServiceAccountTokenKey: []byte("token-data"),
  845. },
  846. })
  847. pod := &api.Pod{
  848. Spec: api.PodSpec{
  849. ServiceAccountName: serviceAccountName,
  850. Containers: []api.Container{
  851. {Name: "container-1"},
  852. },
  853. },
  854. }
  855. attrs := admission.NewAttributesRecord(pod, nil, api.Kind("Pod").WithVersion("version"), ns, "myname", api.Resource("pods").WithVersion("version"), "", admission.Create, &metav1.CreateOptions{}, false, nil)
  856. if err := admissiontesting.WithReinvocationTesting(t, admit).Admit(context.TODO(), attrs, nil); err != nil {
  857. t.Fatal(err)
  858. }
  859. if n := len(pod.Spec.Volumes); n != 1 {
  860. t.Fatalf("expected 1 volume mount, got %d", n)
  861. }
  862. if name := pod.Spec.Volumes[0].Name; name != token1 {
  863. t.Errorf("expected first referenced secret to be mounted, got %q", name)
  864. }
  865. }
  866. func newSecret(secretType corev1.SecretType, namespace, name, serviceAccountName, serviceAccountUID string) *corev1.Secret {
  867. return &corev1.Secret{
  868. ObjectMeta: metav1.ObjectMeta{
  869. Namespace: namespace,
  870. Name: name,
  871. Annotations: map[string]string{
  872. corev1.ServiceAccountNameKey: serviceAccountName,
  873. corev1.ServiceAccountUIDKey: serviceAccountUID,
  874. },
  875. },
  876. Type: secretType,
  877. }
  878. }
  879. func TestGetServiceAccountTokens(t *testing.T) {
  880. testBoundServiceAccountTokenVolumePhases(t, func(t *testing.T, applyFeatures func(*Plugin) *Plugin) {
  881. admit := applyFeatures(NewServiceAccount())
  882. indexer := cache.NewIndexer(cache.MetaNamespaceKeyFunc, cache.Indexers{})
  883. admit.secretLister = corev1listers.NewSecretLister(indexer)
  884. ns := "namespace"
  885. serviceAccountUID := "12345"
  886. sa := &corev1.ServiceAccount{
  887. ObjectMeta: metav1.ObjectMeta{
  888. Name: DefaultServiceAccountName,
  889. Namespace: ns,
  890. UID: types.UID(serviceAccountUID),
  891. },
  892. }
  893. nonSATokenSecret := newSecret(corev1.SecretTypeDockercfg, ns, "nonSATokenSecret", DefaultServiceAccountName, serviceAccountUID)
  894. indexer.Add(nonSATokenSecret)
  895. differentSAToken := newSecret(corev1.SecretTypeServiceAccountToken, ns, "differentSAToken", "someOtherSA", "someOtherUID")
  896. indexer.Add(differentSAToken)
  897. matchingSAToken := newSecret(corev1.SecretTypeServiceAccountToken, ns, "matchingSAToken", DefaultServiceAccountName, serviceAccountUID)
  898. indexer.Add(matchingSAToken)
  899. tokens, err := admit.getServiceAccountTokens(sa)
  900. if err != nil {
  901. t.Fatalf("unexpected error: %v", err)
  902. }
  903. if len(tokens) != 1 {
  904. names := make([]string, 0, len(tokens))
  905. for _, token := range tokens {
  906. names = append(names, token.Name)
  907. }
  908. t.Fatalf("expected only 1 token, got %v", names)
  909. }
  910. if e, a := matchingSAToken.Name, tokens[0].Name; e != a {
  911. t.Errorf("expected token %s, got %s", e, a)
  912. }
  913. })
  914. }
  915. func TestAutomountIsBackwardsCompatible(t *testing.T) {
  916. ns := "myns"
  917. tokenName := "token-name"
  918. serviceAccountName := DefaultServiceAccountName
  919. serviceAccountUID := "12345"
  920. defaultTokenName := "default-token-abc123"
  921. expectedVolume := api.Volume{
  922. Name: defaultTokenName,
  923. VolumeSource: api.VolumeSource{
  924. Secret: &api.SecretVolumeSource{
  925. SecretName: defaultTokenName,
  926. },
  927. },
  928. }
  929. expectedVolumeMount := api.VolumeMount{
  930. Name: defaultTokenName,
  931. ReadOnly: true,
  932. MountPath: DefaultAPITokenMountPath,
  933. }
  934. admit := NewServiceAccount()
  935. admit.generateName = testGenerateName
  936. admit.boundServiceAccountTokenVolume = deprecationEnabledBoundTokenVolume
  937. informerFactory := informers.NewSharedInformerFactory(nil, controller.NoResyncPeriodFunc())
  938. admit.SetExternalKubeInformerFactory(informerFactory)
  939. admit.MountServiceAccountToken = true
  940. admit.RequireAPIToken = true
  941. // Add the default service account for the ns with a token into the cache
  942. informerFactory.Core().V1().ServiceAccounts().Informer().GetStore().Add(&corev1.ServiceAccount{
  943. ObjectMeta: metav1.ObjectMeta{
  944. Name: serviceAccountName,
  945. Namespace: ns,
  946. UID: types.UID(serviceAccountUID),
  947. },
  948. Secrets: []corev1.ObjectReference{
  949. {Name: tokenName},
  950. },
  951. })
  952. // Add a token for the service account into the cache
  953. informerFactory.Core().V1().Secrets().Informer().GetStore().Add(&corev1.Secret{
  954. ObjectMeta: metav1.ObjectMeta{
  955. Name: tokenName,
  956. Namespace: ns,
  957. Annotations: map[string]string{
  958. corev1.ServiceAccountNameKey: serviceAccountName,
  959. corev1.ServiceAccountUIDKey: serviceAccountUID,
  960. },
  961. },
  962. Type: corev1.SecretTypeServiceAccountToken,
  963. Data: map[string][]byte{
  964. api.ServiceAccountTokenKey: []byte("token-data"),
  965. },
  966. })
  967. pod := &api.Pod{
  968. Spec: api.PodSpec{
  969. Containers: []api.Container{
  970. {
  971. Name: "c-1",
  972. VolumeMounts: []api.VolumeMount{
  973. {
  974. Name: defaultTokenName,
  975. MountPath: DefaultAPITokenMountPath,
  976. ReadOnly: true,
  977. },
  978. },
  979. },
  980. },
  981. Volumes: []api.Volume{
  982. {
  983. Name: defaultTokenName,
  984. VolumeSource: api.VolumeSource{
  985. Secret: &api.SecretVolumeSource{
  986. SecretName: defaultTokenName,
  987. },
  988. },
  989. },
  990. },
  991. },
  992. }
  993. attrs := admission.NewAttributesRecord(pod, nil, api.Kind("Pod").WithVersion("version"), ns, "myname", api.Resource("pods").WithVersion("version"), "", admission.Create, &metav1.CreateOptions{}, false, nil)
  994. err := admissiontesting.WithReinvocationTesting(t, admit).Admit(context.TODO(), attrs, nil)
  995. if err != nil {
  996. t.Errorf("Unexpected error: %v", err)
  997. }
  998. if pod.Spec.ServiceAccountName != DefaultServiceAccountName {
  999. t.Errorf("Expected service account %s assigned, got %s", DefaultServiceAccountName, pod.Spec.ServiceAccountName)
  1000. }
  1001. _ = expectedVolume
  1002. _ = expectedVolumeMount
  1003. if len(pod.Spec.Volumes) != 1 {
  1004. t.Fatalf("Expected 1 volume, got %d", len(pod.Spec.Volumes))
  1005. }
  1006. if !reflect.DeepEqual(expectedVolume, pod.Spec.Volumes[0]) {
  1007. t.Fatalf("Expected\n\t%#v\ngot\n\t%#v", expectedVolume, pod.Spec.Volumes[0])
  1008. }
  1009. if len(pod.Spec.Containers[0].VolumeMounts) != 1 {
  1010. t.Fatalf("Expected 1 volume mount, got %d", len(pod.Spec.Containers[0].VolumeMounts))
  1011. }
  1012. if !reflect.DeepEqual(expectedVolumeMount, pod.Spec.Containers[0].VolumeMounts[0]) {
  1013. t.Fatalf("Expected\n\t%#v\ngot\n\t%#v", expectedVolumeMount, pod.Spec.Containers[0].VolumeMounts[0])
  1014. }
  1015. }
  1016. func testGenerateName(n string) string {
  1017. return n + "abc123"
  1018. }
  1019. var generatedVolumeName = testGenerateName(ServiceAccountVolumeName + "-")
  1020. func testBoundServiceAccountTokenVolumePhases(t *testing.T, f func(*testing.T, func(*Plugin) *Plugin)) {
  1021. t.Run("BoundServiceAccountTokenVolume disabled", func(t *testing.T) {
  1022. f(t, func(s *Plugin) *Plugin {
  1023. s.boundServiceAccountTokenVolume = deprecationDisabledBoundTokenVolume
  1024. return s
  1025. })
  1026. })
  1027. t.Run("BoundServiceAccountTokenVolume enabled", func(t *testing.T) {
  1028. f(t, func(s *Plugin) *Plugin {
  1029. s.boundServiceAccountTokenVolume = deprecationEnabledBoundTokenVolume
  1030. return s
  1031. })
  1032. })
  1033. }