Change:
} func TestCreateContainerSecurityContextNonmutating(t *testing.T) { <del> // Create a pod with a security context that needs filling in createPod := func() *api.Pod { return &api.Pod{ Spec: api.PodSpec{ Containers: []api.Container{{ SecurityContext: &api.SecurityContext{}, }}, }, } </del> <ins> untrue := false tests := []struct { security *api.SecurityContext }{ {nil}, {&api.SecurityContext{RunAsNonRoot: &untrue}}, </ins> } <del> // Create a PSP with strategies that will populate a blank security context createPSP := func() *extensions.PodSecurityPolicy { uid := types.UnixUserID(1) return &extensions.PodSecurityPolicy{ ObjectMeta: metav1.ObjectMeta{ Name: "psp-sa", Annotations: map[string]string{ seccomp.AllowedProfilesAnnotationKey: "*", seccomp.DefaultProfileAnnotationKey:  "foo", </del> <ins> for _, tc := range tests { // Create a pod with a security context that needs filling in createPod := func() *api.Pod { return &api.Pod{ Spec: api.PodSpec{ Containers: []api.Container{{ SecurityContext: tc.security, }}, </ins> }, <del> }, Spec: extensions.PodSecurityPolicySpec{ DefaultAddCapabilities:   []api.Capability{"foo"}, RequiredDropCapabilities: []api.Capability{"bar"}, RunAsUser: extensions.RunAsUserStrategyOptions{ Rule:   extensions.RunAsUserStrategyMustRunAs, Ranges: []extensions.UserIDRange{{Min: uid, Max: uid}}, }, SELinux: extensions.SELinuxStrategyOptions{ Rule:           extensions.SELinuxStrategyMustRunAs, SELinuxOptions: &api.SELinuxOptions{User: "you"}, }, // these are pod mutating strategies that are tested above FSGroup: extensions.FSGroupStrategyOptions{ Rule: extensions.FSGroupStrategyRunAsAny, </del> <ins> } } // Create a PSP with strategies that will populate a blank security context createPSP := func() *extensions.PodSecurityPolicy { uid := types.UnixUserID(1) return &extensions.PodSecurityPolicy{ ObjectMeta: metav1.ObjectMeta{ Name: "psp-sa", Annotations: map[string]string{ seccomp.AllowedProfilesAnnotationKey: "*", seccomp.DefaultProfileAnnotationKey:  "foo", }, </ins> }, <del> SupplementalGroups: extensions.SupplementalGroupsStrategyOptions{ Rule: extensions.SupplementalGroupsStrategyRunAsAny, </del> <ins> Spec: extensions.PodSecurityPolicySpec{ DefaultAddCapabilities:   []api.Capability{"foo"}, RequiredDropCapabilities: []api.Capability{"bar"}, RunAsUser: extensions.RunAsUserStrategyOptions{ Rule:   extensions.RunAsUserStrategyMustRunAs, Ranges: []extensions.UserIDRange{{Min: uid, Max: uid}}, }, SELinux: extensions.SELinuxStrategyOptions{ Rule:           extensions.SELinuxStrategyMustRunAs, SELinuxOptions: &api.SELinuxOptions{User: "you"}, }, // these are pod mutating strategies that are tested above FSGroup: extensions.FSGroupStrategyOptions{ Rule: extensions.FSGroupStrategyRunAsAny, }, SupplementalGroups: extensions.SupplementalGroupsStrategyOptions{ Rule: extensions.SupplementalGroupsStrategyRunAsAny, }, // mutates the container SC by defaulting to true if container sets nil ReadOnlyRootFilesystem: true, </ins> }, <del> // mutates the container SC by defaulting to true if container sets nil ReadOnlyRootFilesystem: true, }, </del> <ins> } </ins> } <del> } pod := createPod() psp := createPSP() </del> <ins> pod := createPod() psp := createPSP() </ins> <del> provider, err := NewSimpleProvider(psp, "namespace", NewSimpleStrategyFactory()) if err != nil { t.Fatalf("unable to create provider %v", err) } sc, _, err := provider.CreateContainerSecurityContext(pod, &pod.Spec.Containers[0]) if err != nil { t.Fatalf("unable to create container security context %v", err) } </del> <ins> provider, err := NewSimpleProvider(psp, "namespace", NewSimpleStrategyFactory()) if err != nil { t.Fatalf("unable to create provider %v", err) } sc, _, err := provider.CreateContainerSecurityContext(pod, &pod.Spec.Containers[0]) if err != nil { t.Fatalf("unable to create container security context %v", err) } </ins> <del> // The generated security context should have filled in missing options, so they should differ if reflect.DeepEqual(sc, &pod.Spec.Containers[0].SecurityContext) { t.Error("expected created security context to be different than container's, but they were identical") } </del> <ins> // The generated security context should have filled in missing options, so they should differ if reflect.DeepEqual(sc, &pod.Spec.Containers[0].SecurityContext) { t.Error("expected created security context to be different than container's, but they were identical") } </ins> <del> // Creating the provider or the security context should not have mutated the psp or pod if !reflect.DeepEqual(createPod(), pod) { diffs := diff.ObjectDiff(createPod(), pod) t.Errorf("pod was mutated by CreateContainerSecurityContext. diff:n%s", diffs) } if !reflect.DeepEqual(createPSP(), psp) { t.Error("psp was mutated by CreateContainerSecurityContext") </del> <ins> // Creating the provider or the security context should not have mutated the psp or pod if !reflect.DeepEqual(createPod(), pod) { diffs := diff.ObjectDiff(createPod(), pod) t.Errorf("pod was mutated by CreateContainerSecurityContext. diff:n%s", diffs) } if !reflect.DeepEqual(createPSP(), psp) { t.Error("psp was mutated by CreateContainerSecurityContext") } </ins> } }

Discussion:
What keywords did you search in Kubernetes issues before filing this one? (If you have found any duplicates, you should instead reply there.): PodSecurityPolicy RunAsNonRoot Is this a BUG REPORT or FEATURE REQUEST? (choose one): BUG REPORT <!-- If this is a BUG REPORT, please: Fill in as much of the template below as you can. If you leave out information, we can't help you as well. If this is a FEATURE REQUEST, please: Describe in detail the feature/behavior/change you'd like to see. In both cases, be ready for followup questions, and please respond in a timely manner. If we can't reproduce a bug or think a feature already exists, we might close your issue. If we're wrong, PLEASE feel free to reopen it and explain why. --Kubernetes version (use ): 1.7.0-beta, 1.8.0 (HEAD) Environment: Enable PodSecurityPolicy Cloud provider or hardware configuration: gce, probably any OS (e.g. from /etc/os-release): gci Kernel (e.g. ): Install tools: Others: What happened: The PSP admission controller ignores the specified RunAsNonRoot, and overwrites it. What you expected to happen: the admission controller is not supposed to overwrite user specified fields in the podsecuritycontext and securitycontext. How to reproduce it (as minimally and precisely as possible): enable psp, and create a pod with [only] RunAsNonRoot=false specified in a securitycontext. Anything else we need to know:
/sig auth
there are actually 2 bugs here. fails to properly restrict this field when matching against a PSP, which is a latent bug as long as we overwrite the value anyway. overwrites the user-supplied setting for the securitycontext/podsecuritycontext. I suspect the best fix will be to use the API's deep copy function. We are also doing a couple other sketchy things: assigning the "copy" built-in as a variable, and creating an "sc" shadow variable.

I have a half fix in but we should probably fix it in a separate PR, because we may want to patch 1.6.
the effect is that PSP can overly restrict a pod that wanted to run as root, but expressed that as instead of since the workaround is easy (set in your pod if you want to run as root), I don't anticipate backporting the fix.
SGTM. I'll try and get the unit tests in after the GKE summit (on friday).