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