PodSecurityPolicy should respect and validate user-supplied RunAsNonRoot fields.

This commit is contained in:
Quintin Lee 2017-06-06 12:28:15 -07:00
parent 8df56da448
commit 158f17b9bb
4 changed files with 139 additions and 83 deletions

View File

@ -165,7 +165,7 @@ func (s *simpleProvider) CreateContainerSecurityContext(pod *api.Pod, container
// if we're using the non-root strategy set the marker that this container should not be // if we're using the non-root strategy set the marker that this container should not be
// run as root which will signal to the kubelet to do a final check either on the runAsUser // run as root which will signal to the kubelet to do a final check either on the runAsUser
// or, if runAsUser is not set, the image UID will be checked. // or, if runAsUser is not set, the image UID will be checked.
if s.psp.Spec.RunAsUser.Rule == extensions.RunAsUserStrategyMustRunAsNonRoot { if sc.RunAsNonRoot == nil && s.psp.Spec.RunAsUser.Rule == extensions.RunAsUserStrategyMustRunAsNonRoot {
nonRoot := true nonRoot := true
sc.RunAsNonRoot = &nonRoot sc.RunAsNonRoot = &nonRoot
} }

View File

@ -112,12 +112,21 @@ func TestCreatePodSecurityContextNonmutating(t *testing.T) {
} }
func TestCreateContainerSecurityContextNonmutating(t *testing.T) { func TestCreateContainerSecurityContextNonmutating(t *testing.T) {
untrue := false
tests := []struct {
security *api.SecurityContext
}{
{nil},
{&api.SecurityContext{RunAsNonRoot: &untrue}},
}
for _, tc := range tests {
// Create a pod with a security context that needs filling in // Create a pod with a security context that needs filling in
createPod := func() *api.Pod { createPod := func() *api.Pod {
return &api.Pod{ return &api.Pod{
Spec: api.PodSpec{ Spec: api.PodSpec{
Containers: []api.Container{{ Containers: []api.Container{{
SecurityContext: &api.SecurityContext{}, SecurityContext: tc.security,
}}, }},
}, },
} }
@ -184,6 +193,7 @@ func TestCreateContainerSecurityContextNonmutating(t *testing.T) {
t.Error("psp was mutated by CreateContainerSecurityContext") t.Error("psp was mutated by CreateContainerSecurityContext")
} }
} }
}
func TestValidatePodSecurityContextFailures(t *testing.T) { func TestValidatePodSecurityContextFailures(t *testing.T) {
failHostNetworkPod := defaultPod() failHostNetworkPod := defaultPod()

View File

@ -41,8 +41,9 @@ func (s *nonRoot) Generate(pod *api.Pod, container *api.Container) (*types.UnixU
// Validate ensures that the specified values fall within the range of the strategy. Validation // Validate ensures that the specified values fall within the range of the strategy. Validation
// of this will pass if either the UID is not set, assuming that the image will provided the UID // of this will pass if either the UID is not set, assuming that the image will provided the UID
// or if the UID is set it is not root. In order to work properly this assumes that the kubelet // or if the UID is set it is not root. Validation will fail if RunAsNonRoot is set to false.
// performs a final check on runAsUser or the image UID when runAsUser is nil. // In order to work properly this assumes that the kubelet performs a final check on runAsUser
// or the image UID when runAsUser is nil.
func (s *nonRoot) Validate(pod *api.Pod, container *api.Container) field.ErrorList { func (s *nonRoot) Validate(pod *api.Pod, container *api.Container) field.ErrorList {
allErrs := field.ErrorList{} allErrs := field.ErrorList{}
securityContextPath := field.NewPath("securityContext") securityContextPath := field.NewPath("securityContext")
@ -51,8 +52,13 @@ func (s *nonRoot) Validate(pod *api.Pod, container *api.Container) field.ErrorLi
allErrs = append(allErrs, field.Invalid(securityContextPath, container.SecurityContext, detail)) allErrs = append(allErrs, field.Invalid(securityContextPath, container.SecurityContext, detail))
return allErrs return allErrs
} }
if container.SecurityContext.RunAsNonRoot != nil && *container.SecurityContext.RunAsNonRoot == false {
detail := fmt.Sprintf("RunAsNonRoot must be true for container %s", container.Name)
allErrs = append(allErrs, field.Invalid(securityContextPath.Child("runAsNonRoot"), *container.SecurityContext.RunAsNonRoot, detail))
return allErrs
}
if container.SecurityContext.RunAsUser != nil && *container.SecurityContext.RunAsUser == 0 { if container.SecurityContext.RunAsUser != nil && *container.SecurityContext.RunAsUser == 0 {
detail := fmt.Sprintf("running with the root UID is forbidden by the pod security policy %s", container.Name) detail := fmt.Sprintf("running with the root UID is forbidden by the pod security policy for container %s", container.Name)
allErrs = append(allErrs, field.Invalid(securityContextPath.Child("runAsUser"), *container.SecurityContext.RunAsUser, detail)) allErrs = append(allErrs, field.Invalid(securityContextPath.Child("runAsUser"), *container.SecurityContext.RunAsUser, detail))
return allErrs return allErrs
} }

View File

@ -52,30 +52,70 @@ func TestNonRootGenerate(t *testing.T) {
func TestNonRootValidate(t *testing.T) { func TestNonRootValidate(t *testing.T) {
goodUID := types.UnixUserID(1) goodUID := types.UnixUserID(1)
badUID := types.UnixUserID(0) badUID := types.UnixUserID(0)
untrue := false
unfalse := true
s, err := NewRunAsNonRoot(&extensions.RunAsUserStrategyOptions{}) s, err := NewRunAsNonRoot(&extensions.RunAsUserStrategyOptions{})
if err != nil { if err != nil {
t.Fatalf("unexpected error initializing NewMustRunAs %v", err) t.Fatalf("unexpected error initializing NewMustRunAs %v", err)
} }
container := &api.Container{ tests := []struct {
container *api.Container
expectedErr bool
msg string
}{
{
container: &api.Container{
SecurityContext: &api.SecurityContext{ SecurityContext: &api.SecurityContext{
RunAsUser: &badUID, RunAsUser: &badUID,
}, },
},
expectedErr: true,
msg: "in test case %d, expected errors from root uid but got none: %v",
},
{
container: &api.Container{
SecurityContext: &api.SecurityContext{
RunAsUser: &goodUID,
},
},
expectedErr: false,
msg: "in test case %d, expected no errors from non-root uid but got %v",
},
{
container: &api.Container{
SecurityContext: &api.SecurityContext{
RunAsNonRoot: &untrue,
},
},
expectedErr: true,
msg: "in test case %d, expected errors from RunAsNonRoot but got none: %v",
},
{
container: &api.Container{
SecurityContext: &api.SecurityContext{
RunAsNonRoot: &unfalse,
RunAsUser: &goodUID,
},
},
expectedErr: false,
msg: "in test case %d, expected no errors from non-root uid but got %v",
},
{
container: &api.Container{
SecurityContext: &api.SecurityContext{
RunAsNonRoot: &unfalse,
RunAsUser: &badUID,
},
},
expectedErr: true,
msg: "in test case %d, expected errors from root uid but got %v",
},
} }
errs := s.Validate(nil, container) for i, tc := range tests {
if len(errs) == 0 { errs := s.Validate(nil, tc.container)
t.Errorf("expected errors from root uid but got none") if (len(errs) == 0) == tc.expectedErr {
} t.Errorf(tc.msg, i, errs)
}
container.SecurityContext.RunAsUser = &goodUID
errs = s.Validate(nil, container)
if len(errs) != 0 {
t.Errorf("expected no errors from non-root uid but got %v", errs)
}
container.SecurityContext.RunAsUser = nil
errs = s.Validate(nil, container)
if len(errs) != 0 {
t.Errorf("expected no errors from nil uid but got %v", errs)
} }
} }