From e2afc97587a6c57d77c5837437cbc005dcbab35e Mon Sep 17 00:00:00 2001 From: Clayton Coleman Date: Wed, 18 May 2016 16:24:44 -0400 Subject: [PATCH] Add init containers to PSP admission Treat them just like regular containers. --- .../security/podsecuritypolicy/admission.go | 24 +++++++++++++ .../podsecuritypolicy/admission_test.go | 34 +++++++++++++++++++ 2 files changed, 58 insertions(+) diff --git a/plugin/pkg/admission/security/podsecuritypolicy/admission.go b/plugin/pkg/admission/security/podsecuritypolicy/admission.go index acf8602a200..e3bc36805bb 100644 --- a/plugin/pkg/admission/security/podsecuritypolicy/admission.go +++ b/plugin/pkg/admission/security/podsecuritypolicy/admission.go @@ -186,6 +186,7 @@ func (c *podSecurityPolicyPlugin) Admit(a admission.Attributes) error { // the same psp or is not considered valid. func assignSecurityContext(provider psp.Provider, pod *api.Pod, fldPath *field.Path) field.ErrorList { generatedSCs := make([]*api.SecurityContext, len(pod.Spec.Containers)) + var generatedInitSCs []*api.SecurityContext errs := field.ErrorList{} @@ -201,6 +202,26 @@ func assignSecurityContext(provider psp.Provider, pod *api.Pod, fldPath *field.P pod.Spec.SecurityContext = psc errs = append(errs, provider.ValidatePodSecurityContext(pod, field.NewPath("spec", "securityContext"))...) + // Note: this is not changing the original container, we will set container SCs later so long + // as all containers validated under the same PSP. + for i, containerCopy := range pod.Spec.InitContainers { + // We will determine the effective security context for the container and validate against that + // since that is how the sc provider will eventually apply settings in the runtime. + // This results in an SC that is based on the Pod's PSC with the set fields from the container + // overriding pod level settings. + containerCopy.SecurityContext = sc.DetermineEffectiveSecurityContext(pod, &containerCopy) + + sc, err := provider.CreateContainerSecurityContext(pod, &containerCopy) + if err != nil { + errs = append(errs, field.Invalid(field.NewPath("spec", "initContainers").Index(i).Child("securityContext"), "", err.Error())) + continue + } + generatedInitSCs = append(generatedInitSCs, sc) + + containerCopy.SecurityContext = sc + errs = append(errs, provider.ValidateContainerSecurityContext(pod, &containerCopy, field.NewPath("spec", "initContainers").Index(i).Child("securityContext"))...) + } + // Note: this is not changing the original container, we will set container SCs later so long // as all containers validated under the same PSP. for i, containerCopy := range pod.Spec.Containers { @@ -229,6 +250,9 @@ func assignSecurityContext(provider psp.Provider, pod *api.Pod, fldPath *field.P // if we've reached this code then we've generated and validated an SC for every container in the // pod so let's apply what we generated. Note: the psc is already applied. + for i, sc := range generatedInitSCs { + pod.Spec.InitContainers[i].SecurityContext = sc + } for i, sc := range generatedSCs { pod.Spec.Containers[i].SecurityContext = sc } diff --git a/plugin/pkg/admission/security/podsecuritypolicy/admission_test.go b/plugin/pkg/admission/security/podsecuritypolicy/admission_test.go index 8292f24bae6..04c9bbb1904 100644 --- a/plugin/pkg/admission/security/podsecuritypolicy/admission_test.go +++ b/plugin/pkg/admission/security/podsecuritypolicy/admission_test.go @@ -44,6 +44,12 @@ func NewTestAdmission(store cache.Store, kclient clientset.Interface) kadmission } } +func useInitContainers(pod *kapi.Pod) *kapi.Pod { + pod.Spec.InitContainers = pod.Spec.Containers + pod.Spec.Containers = []kapi.Container{} + return pod +} + func TestAdmitPrivileged(t *testing.T) { createPodWithPriv := func(priv bool) *kapi.Pod { pod := goodPod() @@ -203,6 +209,18 @@ func TestAdmitCaps(t *testing.T) { } } } + + for k, v := range tc { + useInitContainers(v.pod) + testPSPAdmit(k, v.psps, v.pod, v.shouldPass, v.expectedPSP, t) + + if v.expectedCapabilities != nil { + if !reflect.DeepEqual(v.expectedCapabilities, v.pod.Spec.InitContainers[0].SecurityContext.Capabilities) { + t.Errorf("%s resulted in caps that were not expected - expected: %v, received: %v", k, v.expectedCapabilities, v.pod.Spec.InitContainers[0].SecurityContext.Capabilities) + } + } + } + } func TestAdmitVolumes(t *testing.T) { @@ -235,6 +253,10 @@ func TestAdmitVolumes(t *testing.T) { // expect a denial for this PSP testPSPAdmit(fmt.Sprintf("%s denial", string(fsType)), []*extensions.PodSecurityPolicy{psp}, pod, false, "", t) + // also expect a denial for this PSP if it's an init container + useInitContainers(pod) + testPSPAdmit(fmt.Sprintf("%s denial", string(fsType)), []*extensions.PodSecurityPolicy{psp}, pod, false, "", t) + // now add the fstype directly to the psp and it should validate psp.Spec.Volumes = []extensions.FSType{fsType} testPSPAdmit(fmt.Sprintf("%s direct accept", string(fsType)), []*extensions.PodSecurityPolicy{psp}, pod, true, psp.Name, t) @@ -304,6 +326,18 @@ func TestAdmitHostNetwork(t *testing.T) { } } } + + // test again with init containers + for k, v := range tests { + useInitContainers(v.pod) + testPSPAdmit(k, v.psps, v.pod, v.shouldPass, v.expectedPSP, t) + + if v.shouldPass { + if v.pod.Spec.SecurityContext.HostNetwork != v.expectedHostNetwork { + t.Errorf("%s expected hostNetwork to be %t", k, v.expectedHostNetwork) + } + } + } } func TestAdmitHostPorts(t *testing.T) {