2016-05-05 19:43:54 +00:00
|
|
|
/*
|
2016-06-03 00:25:58 +00:00
|
|
|
Copyright 2016 The Kubernetes Authors.
|
2016-05-05 19:43:54 +00:00
|
|
|
|
|
|
|
Licensed under the Apache License, Version 2.0 (the "License");
|
|
|
|
you may not use this file except in compliance with the License.
|
|
|
|
You may obtain a copy of the License at
|
|
|
|
|
|
|
|
http://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
|
|
|
|
Unless required by applicable law or agreed to in writing, software
|
|
|
|
distributed under the License is distributed on an "AS IS" BASIS,
|
|
|
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
|
|
See the License for the specific language governing permissions and
|
|
|
|
limitations under the License.
|
|
|
|
*/
|
|
|
|
|
|
|
|
package podsecuritypolicy
|
|
|
|
|
|
|
|
import (
|
|
|
|
"fmt"
|
|
|
|
"reflect"
|
|
|
|
"strings"
|
|
|
|
"testing"
|
|
|
|
|
2016-08-18 00:24:47 +00:00
|
|
|
"github.com/davecgh/go-spew/spew"
|
|
|
|
|
2017-06-22 17:25:57 +00:00
|
|
|
"k8s.io/api/core/v1"
|
2017-01-17 03:38:19 +00:00
|
|
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
2017-01-11 14:09:48 +00:00
|
|
|
"k8s.io/apimachinery/pkg/util/diff"
|
|
|
|
"k8s.io/apimachinery/pkg/util/validation/field"
|
2017-11-08 22:34:54 +00:00
|
|
|
api "k8s.io/kubernetes/pkg/apis/core"
|
|
|
|
k8s_api_v1 "k8s.io/kubernetes/pkg/apis/core/v1"
|
2016-05-05 19:43:54 +00:00
|
|
|
"k8s.io/kubernetes/pkg/apis/extensions"
|
2016-08-18 00:24:47 +00:00
|
|
|
"k8s.io/kubernetes/pkg/security/apparmor"
|
2016-08-23 16:52:27 +00:00
|
|
|
"k8s.io/kubernetes/pkg/security/podsecuritypolicy/seccomp"
|
2016-05-05 19:43:54 +00:00
|
|
|
psputil "k8s.io/kubernetes/pkg/security/podsecuritypolicy/util"
|
|
|
|
)
|
|
|
|
|
2016-08-18 00:24:47 +00:00
|
|
|
const defaultContainerName = "test-c"
|
|
|
|
|
2017-11-10 17:22:34 +00:00
|
|
|
func TestDefaultPodSecurityContextNonmutating(t *testing.T) {
|
2016-05-05 19:43:54 +00:00
|
|
|
// Create a pod with a security context that needs filling in
|
|
|
|
createPod := func() *api.Pod {
|
|
|
|
return &api.Pod{
|
|
|
|
Spec: api.PodSpec{
|
|
|
|
SecurityContext: &api.PodSecurityContext{},
|
|
|
|
},
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Create a PSP with strategies that will populate a blank psc
|
|
|
|
createPSP := func() *extensions.PodSecurityPolicy {
|
|
|
|
return &extensions.PodSecurityPolicy{
|
2017-01-17 03:38:19 +00:00
|
|
|
ObjectMeta: metav1.ObjectMeta{
|
2016-05-05 19:43:54 +00:00
|
|
|
Name: "psp-sa",
|
2016-08-23 16:52:27 +00:00
|
|
|
Annotations: map[string]string{
|
|
|
|
seccomp.AllowedProfilesAnnotationKey: "*",
|
|
|
|
},
|
2016-05-05 19:43:54 +00:00
|
|
|
},
|
|
|
|
Spec: extensions.PodSecurityPolicySpec{
|
2017-10-14 19:07:09 +00:00
|
|
|
AllowPrivilegeEscalation: true,
|
2016-05-05 19:43:54 +00:00
|
|
|
RunAsUser: extensions.RunAsUserStrategyOptions{
|
|
|
|
Rule: extensions.RunAsUserStrategyRunAsAny,
|
|
|
|
},
|
|
|
|
SELinux: extensions.SELinuxStrategyOptions{
|
|
|
|
Rule: extensions.SELinuxStrategyRunAsAny,
|
|
|
|
},
|
|
|
|
FSGroup: extensions.FSGroupStrategyOptions{
|
2017-10-14 19:07:09 +00:00
|
|
|
Rule: extensions.FSGroupStrategyRunAsAny,
|
2016-05-05 19:43:54 +00:00
|
|
|
},
|
|
|
|
SupplementalGroups: extensions.SupplementalGroupsStrategyOptions{
|
2017-10-14 19:07:09 +00:00
|
|
|
Rule: extensions.SupplementalGroupsStrategyRunAsAny,
|
2016-05-05 19:43:54 +00:00
|
|
|
},
|
|
|
|
},
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pod := createPod()
|
|
|
|
psp := createPSP()
|
|
|
|
|
|
|
|
provider, err := NewSimpleProvider(psp, "namespace", NewSimpleStrategyFactory())
|
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("unable to create provider %v", err)
|
|
|
|
}
|
2017-11-10 17:22:34 +00:00
|
|
|
err = provider.DefaultPodSecurityContext(pod)
|
2016-05-05 19:43:54 +00:00
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("unable to create psc %v", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Creating the provider or the security context should not have mutated the psp or pod
|
2017-10-14 19:07:09 +00:00
|
|
|
// since all the strategies were permissive
|
2016-05-05 19:43:54 +00:00
|
|
|
if !reflect.DeepEqual(createPod(), pod) {
|
|
|
|
diffs := diff.ObjectDiff(createPod(), pod)
|
2017-11-10 17:22:34 +00:00
|
|
|
t.Errorf("pod was mutated by DefaultPodSecurityContext. diff:\n%s", diffs)
|
2016-05-05 19:43:54 +00:00
|
|
|
}
|
|
|
|
if !reflect.DeepEqual(createPSP(), psp) {
|
2017-11-10 17:22:34 +00:00
|
|
|
t.Error("psp was mutated by DefaultPodSecurityContext")
|
2016-05-05 19:43:54 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-11-10 17:23:26 +00:00
|
|
|
func TestDefaultContainerSecurityContextNonmutating(t *testing.T) {
|
2017-06-06 19:28:15 +00:00
|
|
|
untrue := false
|
|
|
|
tests := []struct {
|
|
|
|
security *api.SecurityContext
|
|
|
|
}{
|
|
|
|
{nil},
|
|
|
|
{&api.SecurityContext{RunAsNonRoot: &untrue}},
|
2016-05-05 19:43:54 +00:00
|
|
|
}
|
|
|
|
|
2017-06-06 19:28:15 +00:00
|
|
|
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,
|
|
|
|
}},
|
2016-08-23 16:52:27 +00:00
|
|
|
},
|
2017-06-06 19:28:15 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Create a PSP with strategies that will populate a blank security context
|
|
|
|
createPSP := func() *extensions.PodSecurityPolicy {
|
|
|
|
return &extensions.PodSecurityPolicy{
|
|
|
|
ObjectMeta: metav1.ObjectMeta{
|
|
|
|
Name: "psp-sa",
|
|
|
|
Annotations: map[string]string{
|
|
|
|
seccomp.AllowedProfilesAnnotationKey: "*",
|
|
|
|
seccomp.DefaultProfileAnnotationKey: "foo",
|
|
|
|
},
|
2016-05-05 19:43:54 +00:00
|
|
|
},
|
2017-06-06 19:28:15 +00:00
|
|
|
Spec: extensions.PodSecurityPolicySpec{
|
2017-10-14 19:07:09 +00:00
|
|
|
AllowPrivilegeEscalation: true,
|
2017-06-06 19:28:15 +00:00
|
|
|
RunAsUser: extensions.RunAsUserStrategyOptions{
|
2017-10-14 19:07:09 +00:00
|
|
|
Rule: extensions.RunAsUserStrategyRunAsAny,
|
2017-06-06 19:28:15 +00:00
|
|
|
},
|
|
|
|
SELinux: extensions.SELinuxStrategyOptions{
|
2017-10-14 19:07:09 +00:00
|
|
|
Rule: extensions.SELinuxStrategyRunAsAny,
|
2017-06-06 19:28:15 +00:00
|
|
|
},
|
|
|
|
FSGroup: extensions.FSGroupStrategyOptions{
|
|
|
|
Rule: extensions.FSGroupStrategyRunAsAny,
|
|
|
|
},
|
|
|
|
SupplementalGroups: extensions.SupplementalGroupsStrategyOptions{
|
|
|
|
Rule: extensions.SupplementalGroupsStrategyRunAsAny,
|
|
|
|
},
|
2016-05-05 19:43:54 +00:00
|
|
|
},
|
2017-06-06 19:28:15 +00:00
|
|
|
}
|
2016-05-05 19:43:54 +00:00
|
|
|
}
|
|
|
|
|
2017-06-06 19:28:15 +00:00
|
|
|
pod := createPod()
|
|
|
|
psp := createPSP()
|
2016-05-05 19:43:54 +00:00
|
|
|
|
2017-06-06 19:28:15 +00:00
|
|
|
provider, err := NewSimpleProvider(psp, "namespace", NewSimpleStrategyFactory())
|
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("unable to create provider %v", err)
|
|
|
|
}
|
2017-11-10 17:23:26 +00:00
|
|
|
err = provider.DefaultContainerSecurityContext(pod, &pod.Spec.Containers[0])
|
2017-06-06 19:28:15 +00:00
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("unable to create container security context %v", err)
|
|
|
|
}
|
2016-05-05 19:43:54 +00:00
|
|
|
|
2017-06-06 19:28:15 +00:00
|
|
|
// Creating the provider or the security context should not have mutated the psp or pod
|
2017-10-14 19:07:09 +00:00
|
|
|
// since all the strategies were permissive
|
2017-06-06 19:28:15 +00:00
|
|
|
if !reflect.DeepEqual(createPod(), pod) {
|
|
|
|
diffs := diff.ObjectDiff(createPod(), pod)
|
2017-11-10 17:23:26 +00:00
|
|
|
t.Errorf("pod was mutated by DefaultContainerSecurityContext. diff:\n%s", diffs)
|
2017-06-06 19:28:15 +00:00
|
|
|
}
|
|
|
|
if !reflect.DeepEqual(createPSP(), psp) {
|
2017-11-10 17:23:26 +00:00
|
|
|
t.Error("psp was mutated by DefaultContainerSecurityContext")
|
2017-06-06 19:28:15 +00:00
|
|
|
}
|
2016-05-05 19:43:54 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestValidatePodSecurityContextFailures(t *testing.T) {
|
|
|
|
failHostNetworkPod := defaultPod()
|
|
|
|
failHostNetworkPod.Spec.SecurityContext.HostNetwork = true
|
|
|
|
|
|
|
|
failHostPIDPod := defaultPod()
|
|
|
|
failHostPIDPod.Spec.SecurityContext.HostPID = true
|
|
|
|
|
|
|
|
failHostIPCPod := defaultPod()
|
|
|
|
failHostIPCPod.Spec.SecurityContext.HostIPC = true
|
|
|
|
|
|
|
|
failSupplementalGroupPod := defaultPod()
|
2017-06-21 07:13:36 +00:00
|
|
|
failSupplementalGroupPod.Spec.SecurityContext.SupplementalGroups = []int64{999}
|
2016-05-05 19:43:54 +00:00
|
|
|
failSupplementalGroupPSP := defaultPSP()
|
|
|
|
failSupplementalGroupPSP.Spec.SupplementalGroups = extensions.SupplementalGroupsStrategyOptions{
|
|
|
|
Rule: extensions.SupplementalGroupsStrategyMustRunAs,
|
2017-04-20 10:57:07 +00:00
|
|
|
Ranges: []extensions.GroupIDRange{
|
2016-05-05 19:43:54 +00:00
|
|
|
{Min: 1, Max: 1},
|
|
|
|
},
|
|
|
|
}
|
|
|
|
|
|
|
|
failFSGroupPod := defaultPod()
|
2017-06-21 07:13:36 +00:00
|
|
|
fsGroup := int64(999)
|
2016-05-05 19:43:54 +00:00
|
|
|
failFSGroupPod.Spec.SecurityContext.FSGroup = &fsGroup
|
|
|
|
failFSGroupPSP := defaultPSP()
|
|
|
|
failFSGroupPSP.Spec.FSGroup = extensions.FSGroupStrategyOptions{
|
|
|
|
Rule: extensions.FSGroupStrategyMustRunAs,
|
2017-04-20 10:57:07 +00:00
|
|
|
Ranges: []extensions.GroupIDRange{
|
2016-05-05 19:43:54 +00:00
|
|
|
{Min: 1, Max: 1},
|
|
|
|
},
|
|
|
|
}
|
|
|
|
|
|
|
|
failNilSELinuxPod := defaultPod()
|
|
|
|
failSELinuxPSP := defaultPSP()
|
|
|
|
failSELinuxPSP.Spec.SELinux.Rule = extensions.SELinuxStrategyMustRunAs
|
|
|
|
failSELinuxPSP.Spec.SELinux.SELinuxOptions = &api.SELinuxOptions{
|
|
|
|
Level: "foo",
|
|
|
|
}
|
|
|
|
|
|
|
|
failInvalidSELinuxPod := defaultPod()
|
|
|
|
failInvalidSELinuxPod.Spec.SecurityContext.SELinuxOptions = &api.SELinuxOptions{
|
|
|
|
Level: "bar",
|
|
|
|
}
|
|
|
|
|
2016-09-01 22:55:34 +00:00
|
|
|
failHostDirPod := defaultPod()
|
|
|
|
failHostDirPod.Spec.Volumes = []api.Volume{
|
|
|
|
{
|
|
|
|
Name: "bad volume",
|
|
|
|
VolumeSource: api.VolumeSource{
|
|
|
|
HostPath: &api.HostPathVolumeSource{},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
}
|
|
|
|
|
2017-08-06 19:40:18 +00:00
|
|
|
failHostPathDirPod := defaultPod()
|
|
|
|
failHostPathDirPod.Spec.Volumes = []api.Volume{
|
|
|
|
{
|
|
|
|
Name: "bad volume",
|
|
|
|
VolumeSource: api.VolumeSource{
|
|
|
|
HostPath: &api.HostPathVolumeSource{
|
|
|
|
Path: "/fail",
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
}
|
|
|
|
failHostPathDirPSP := defaultPSP()
|
|
|
|
failHostPathDirPSP.Spec.Volumes = []extensions.FSType{extensions.HostPath}
|
|
|
|
failHostPathDirPSP.Spec.AllowedHostPaths = []extensions.AllowedHostPath{
|
|
|
|
{PathPrefix: "/foo/bar"},
|
|
|
|
}
|
|
|
|
|
2016-09-30 07:35:54 +00:00
|
|
|
failOtherSysctlsAllowedPSP := defaultPSP()
|
|
|
|
failOtherSysctlsAllowedPSP.Annotations[extensions.SysctlsPodSecurityPolicyAnnotationKey] = "bar,abc"
|
|
|
|
|
|
|
|
failNoSysctlAllowedPSP := defaultPSP()
|
|
|
|
failNoSysctlAllowedPSP.Annotations[extensions.SysctlsPodSecurityPolicyAnnotationKey] = ""
|
|
|
|
|
|
|
|
failSafeSysctlFooPod := defaultPod()
|
|
|
|
failSafeSysctlFooPod.Annotations[api.SysctlsPodAnnotationKey] = "foo=1"
|
|
|
|
|
|
|
|
failUnsafeSysctlFooPod := defaultPod()
|
|
|
|
failUnsafeSysctlFooPod.Annotations[api.UnsafeSysctlsPodAnnotationKey] = "foo=1"
|
|
|
|
|
2016-08-23 16:52:27 +00:00
|
|
|
failSeccompProfilePod := defaultPod()
|
|
|
|
failSeccompProfilePod.Annotations = map[string]string{api.SeccompPodAnnotationKey: "foo"}
|
|
|
|
|
2017-09-27 09:00:27 +00:00
|
|
|
podWithInvalidFlexVolumeDriver := defaultPod()
|
|
|
|
podWithInvalidFlexVolumeDriver.Spec.Volumes = []api.Volume{
|
|
|
|
{
|
|
|
|
Name: "flex-volume",
|
|
|
|
VolumeSource: api.VolumeSource{
|
|
|
|
FlexVolume: &api.FlexVolumeSource{
|
|
|
|
Driver: "example/unknown",
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
}
|
|
|
|
|
2016-05-05 19:43:54 +00:00
|
|
|
errorCases := map[string]struct {
|
|
|
|
pod *api.Pod
|
|
|
|
psp *extensions.PodSecurityPolicy
|
|
|
|
expectedError string
|
|
|
|
}{
|
|
|
|
"failHostNetwork": {
|
|
|
|
pod: failHostNetworkPod,
|
|
|
|
psp: defaultPSP(),
|
|
|
|
expectedError: "Host network is not allowed to be used",
|
|
|
|
},
|
|
|
|
"failHostPID": {
|
|
|
|
pod: failHostPIDPod,
|
|
|
|
psp: defaultPSP(),
|
|
|
|
expectedError: "Host PID is not allowed to be used",
|
|
|
|
},
|
|
|
|
"failHostIPC": {
|
|
|
|
pod: failHostIPCPod,
|
|
|
|
psp: defaultPSP(),
|
|
|
|
expectedError: "Host IPC is not allowed to be used",
|
|
|
|
},
|
|
|
|
"failSupplementalGroupOutOfRange": {
|
|
|
|
pod: failSupplementalGroupPod,
|
|
|
|
psp: failSupplementalGroupPSP,
|
|
|
|
expectedError: "999 is not an allowed group",
|
|
|
|
},
|
|
|
|
"failSupplementalGroupEmpty": {
|
|
|
|
pod: defaultPod(),
|
|
|
|
psp: failSupplementalGroupPSP,
|
|
|
|
expectedError: "unable to validate empty groups against required ranges",
|
|
|
|
},
|
|
|
|
"failFSGroupOutOfRange": {
|
|
|
|
pod: failFSGroupPod,
|
|
|
|
psp: failFSGroupPSP,
|
|
|
|
expectedError: "999 is not an allowed group",
|
|
|
|
},
|
|
|
|
"failFSGroupEmpty": {
|
|
|
|
pod: defaultPod(),
|
|
|
|
psp: failFSGroupPSP,
|
|
|
|
expectedError: "unable to validate empty groups against required ranges",
|
|
|
|
},
|
|
|
|
"failNilSELinux": {
|
|
|
|
pod: failNilSELinuxPod,
|
|
|
|
psp: failSELinuxPSP,
|
2017-10-06 20:31:59 +00:00
|
|
|
expectedError: "seLinuxOptions: Required",
|
2016-05-05 19:43:54 +00:00
|
|
|
},
|
|
|
|
"failInvalidSELinux": {
|
|
|
|
pod: failInvalidSELinuxPod,
|
|
|
|
psp: failSELinuxPSP,
|
2017-10-06 20:31:59 +00:00
|
|
|
expectedError: "seLinuxOptions.level: Invalid value",
|
2016-05-05 19:43:54 +00:00
|
|
|
},
|
2016-09-01 22:55:34 +00:00
|
|
|
"failHostDirPSP": {
|
|
|
|
pod: failHostDirPod,
|
|
|
|
psp: defaultPSP(),
|
|
|
|
expectedError: "hostPath volumes are not allowed to be used",
|
|
|
|
},
|
2017-08-06 19:40:18 +00:00
|
|
|
"failHostPathDirPSP": {
|
|
|
|
pod: failHostPathDirPod,
|
|
|
|
psp: failHostPathDirPSP,
|
|
|
|
expectedError: "is not allowed to be used",
|
|
|
|
},
|
2016-09-30 07:35:54 +00:00
|
|
|
"failSafeSysctlFooPod with failNoSysctlAllowedSCC": {
|
|
|
|
pod: failSafeSysctlFooPod,
|
|
|
|
psp: failNoSysctlAllowedPSP,
|
|
|
|
expectedError: "sysctls are not allowed",
|
|
|
|
},
|
|
|
|
"failUnsafeSysctlFooPod with failNoSysctlAllowedSCC": {
|
|
|
|
pod: failUnsafeSysctlFooPod,
|
|
|
|
psp: failNoSysctlAllowedPSP,
|
|
|
|
expectedError: "sysctls are not allowed",
|
|
|
|
},
|
|
|
|
"failSafeSysctlFooPod with failOtherSysctlsAllowedSCC": {
|
|
|
|
pod: failSafeSysctlFooPod,
|
|
|
|
psp: failOtherSysctlsAllowedPSP,
|
|
|
|
expectedError: "sysctl \"foo\" is not allowed",
|
|
|
|
},
|
|
|
|
"failUnsafeSysctlFooPod with failOtherSysctlsAllowedSCC": {
|
|
|
|
pod: failUnsafeSysctlFooPod,
|
|
|
|
psp: failOtherSysctlsAllowedPSP,
|
|
|
|
expectedError: "sysctl \"foo\" is not allowed",
|
|
|
|
},
|
2016-08-23 16:52:27 +00:00
|
|
|
"failInvalidSeccomp": {
|
|
|
|
pod: failSeccompProfilePod,
|
|
|
|
psp: defaultPSP(),
|
|
|
|
expectedError: "Forbidden: seccomp may not be set",
|
|
|
|
},
|
2017-09-27 09:00:27 +00:00
|
|
|
"fail pod with disallowed flexVolume when flex volumes are allowed": {
|
|
|
|
pod: podWithInvalidFlexVolumeDriver,
|
|
|
|
psp: allowFlexVolumesPSP(false, false),
|
|
|
|
expectedError: "Flexvolume driver is not allowed to be used",
|
|
|
|
},
|
|
|
|
"fail pod with disallowed flexVolume when all volumes are allowed": {
|
|
|
|
pod: podWithInvalidFlexVolumeDriver,
|
|
|
|
psp: allowFlexVolumesPSP(false, true),
|
|
|
|
expectedError: "Flexvolume driver is not allowed to be used",
|
|
|
|
},
|
2016-05-05 19:43:54 +00:00
|
|
|
}
|
|
|
|
for k, v := range errorCases {
|
|
|
|
provider, err := NewSimpleProvider(v.psp, "namespace", NewSimpleStrategyFactory())
|
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("unable to create provider %v", err)
|
|
|
|
}
|
2018-02-14 02:55:50 +00:00
|
|
|
errs := provider.ValidatePod(v.pod, field.NewPath(""))
|
2016-05-05 19:43:54 +00:00
|
|
|
if len(errs) == 0 {
|
|
|
|
t.Errorf("%s expected validation failure but did not receive errors", k)
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
if !strings.Contains(errs[0].Error(), v.expectedError) {
|
|
|
|
t.Errorf("%s received unexpected error %v", k, errs)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-09-27 09:00:27 +00:00
|
|
|
func allowFlexVolumesPSP(allowAllFlexVolumes, allowAllVolumes bool) *extensions.PodSecurityPolicy {
|
|
|
|
psp := defaultPSP()
|
|
|
|
|
|
|
|
allowedVolumes := []extensions.AllowedFlexVolume{
|
|
|
|
{Driver: "example/foo"},
|
|
|
|
{Driver: "example/bar"},
|
|
|
|
}
|
|
|
|
if allowAllFlexVolumes {
|
|
|
|
allowedVolumes = []extensions.AllowedFlexVolume{}
|
|
|
|
}
|
|
|
|
|
|
|
|
allowedVolumeType := extensions.FlexVolume
|
|
|
|
if allowAllVolumes {
|
|
|
|
allowedVolumeType = extensions.All
|
|
|
|
}
|
|
|
|
|
|
|
|
psp.Spec.AllowedFlexVolumes = allowedVolumes
|
|
|
|
psp.Spec.Volumes = []extensions.FSType{allowedVolumeType}
|
|
|
|
|
|
|
|
return psp
|
|
|
|
}
|
|
|
|
|
2016-05-05 19:43:54 +00:00
|
|
|
func TestValidateContainerSecurityContextFailures(t *testing.T) {
|
2018-02-09 06:53:53 +00:00
|
|
|
// fail user strategy
|
2016-05-05 19:43:54 +00:00
|
|
|
failUserPSP := defaultPSP()
|
2017-06-21 07:13:36 +00:00
|
|
|
uid := int64(999)
|
|
|
|
badUID := int64(1)
|
2016-05-05 19:43:54 +00:00
|
|
|
failUserPSP.Spec.RunAsUser = extensions.RunAsUserStrategyOptions{
|
|
|
|
Rule: extensions.RunAsUserStrategyMustRunAs,
|
2017-04-20 10:57:07 +00:00
|
|
|
Ranges: []extensions.UserIDRange{{Min: uid, Max: uid}},
|
2016-05-05 19:43:54 +00:00
|
|
|
}
|
|
|
|
failUserPod := defaultPod()
|
|
|
|
failUserPod.Spec.Containers[0].SecurityContext.RunAsUser = &badUID
|
|
|
|
|
2018-02-09 06:53:53 +00:00
|
|
|
// fail selinux strategy
|
2016-05-05 19:43:54 +00:00
|
|
|
failSELinuxPSP := defaultPSP()
|
|
|
|
failSELinuxPSP.Spec.SELinux = extensions.SELinuxStrategyOptions{
|
|
|
|
Rule: extensions.SELinuxStrategyMustRunAs,
|
|
|
|
SELinuxOptions: &api.SELinuxOptions{
|
|
|
|
Level: "foo",
|
|
|
|
},
|
|
|
|
}
|
|
|
|
failSELinuxPod := defaultPod()
|
|
|
|
failSELinuxPod.Spec.Containers[0].SecurityContext.SELinuxOptions = &api.SELinuxOptions{
|
|
|
|
Level: "bar",
|
|
|
|
}
|
|
|
|
|
2016-08-18 00:24:47 +00:00
|
|
|
failNilAppArmorPod := defaultPod()
|
2016-11-18 21:26:53 +00:00
|
|
|
v1FailInvalidAppArmorPod := defaultV1Pod()
|
|
|
|
apparmor.SetProfileName(v1FailInvalidAppArmorPod, defaultContainerName, apparmor.ProfileNamePrefix+"foo")
|
|
|
|
failInvalidAppArmorPod := &api.Pod{}
|
2017-10-09 17:13:46 +00:00
|
|
|
k8s_api_v1.Convert_v1_Pod_To_core_Pod(v1FailInvalidAppArmorPod, failInvalidAppArmorPod, nil)
|
2016-11-18 21:26:53 +00:00
|
|
|
|
2016-08-18 00:24:47 +00:00
|
|
|
failAppArmorPSP := defaultPSP()
|
|
|
|
failAppArmorPSP.Annotations = map[string]string{
|
|
|
|
apparmor.AllowedProfilesAnnotationKey: apparmor.ProfileRuntimeDefault,
|
|
|
|
}
|
|
|
|
|
2016-05-05 19:43:54 +00:00
|
|
|
failPrivPod := defaultPod()
|
|
|
|
var priv bool = true
|
|
|
|
failPrivPod.Spec.Containers[0].SecurityContext.Privileged = &priv
|
|
|
|
|
|
|
|
failCapsPod := defaultPod()
|
|
|
|
failCapsPod.Spec.Containers[0].SecurityContext.Capabilities = &api.Capabilities{
|
|
|
|
Add: []api.Capability{"foo"},
|
|
|
|
}
|
|
|
|
|
|
|
|
failHostPortPod := defaultPod()
|
|
|
|
failHostPortPod.Spec.Containers[0].Ports = []api.ContainerPort{{HostPort: 1}}
|
|
|
|
|
|
|
|
readOnlyRootFSPSP := defaultPSP()
|
|
|
|
readOnlyRootFSPSP.Spec.ReadOnlyRootFilesystem = true
|
|
|
|
|
|
|
|
readOnlyRootFSPodFalse := defaultPod()
|
|
|
|
readOnlyRootFS := false
|
|
|
|
readOnlyRootFSPodFalse.Spec.Containers[0].SecurityContext.ReadOnlyRootFilesystem = &readOnlyRootFS
|
|
|
|
|
2016-08-23 16:52:27 +00:00
|
|
|
failSeccompPod := defaultPod()
|
|
|
|
failSeccompPod.Annotations = map[string]string{
|
|
|
|
api.SeccompContainerAnnotationKeyPrefix + failSeccompPod.Spec.Containers[0].Name: "foo",
|
|
|
|
}
|
|
|
|
|
|
|
|
failSeccompPodInheritPodAnnotation := defaultPod()
|
|
|
|
failSeccompPodInheritPodAnnotation.Annotations = map[string]string{
|
|
|
|
api.SeccompPodAnnotationKey: "foo",
|
|
|
|
}
|
|
|
|
|
2016-05-05 19:43:54 +00:00
|
|
|
errorCases := map[string]struct {
|
|
|
|
pod *api.Pod
|
|
|
|
psp *extensions.PodSecurityPolicy
|
|
|
|
expectedError string
|
|
|
|
}{
|
|
|
|
"failUserPSP": {
|
|
|
|
pod: failUserPod,
|
|
|
|
psp: failUserPSP,
|
2017-10-06 20:40:49 +00:00
|
|
|
expectedError: "runAsUser: Invalid value",
|
2016-05-05 19:43:54 +00:00
|
|
|
},
|
|
|
|
"failSELinuxPSP": {
|
|
|
|
pod: failSELinuxPod,
|
|
|
|
psp: failSELinuxPSP,
|
2017-10-06 20:31:59 +00:00
|
|
|
expectedError: "seLinuxOptions.level: Invalid value",
|
2016-05-05 19:43:54 +00:00
|
|
|
},
|
2016-08-18 00:24:47 +00:00
|
|
|
"failNilAppArmor": {
|
|
|
|
pod: failNilAppArmorPod,
|
|
|
|
psp: failAppArmorPSP,
|
|
|
|
expectedError: "AppArmor profile must be set",
|
|
|
|
},
|
|
|
|
"failInvalidAppArmor": {
|
|
|
|
pod: failInvalidAppArmorPod,
|
|
|
|
psp: failAppArmorPSP,
|
|
|
|
expectedError: "localhost/foo is not an allowed profile. Allowed values: \"runtime/default\"",
|
|
|
|
},
|
2016-05-05 19:43:54 +00:00
|
|
|
"failPrivPSP": {
|
|
|
|
pod: failPrivPod,
|
|
|
|
psp: defaultPSP(),
|
|
|
|
expectedError: "Privileged containers are not allowed",
|
|
|
|
},
|
|
|
|
"failCapsPSP": {
|
|
|
|
pod: failCapsPod,
|
|
|
|
psp: defaultPSP(),
|
|
|
|
expectedError: "capability may not be added",
|
|
|
|
},
|
|
|
|
"failHostPortPSP": {
|
|
|
|
pod: failHostPortPod,
|
|
|
|
psp: defaultPSP(),
|
2017-06-16 08:22:50 +00:00
|
|
|
expectedError: "Host port 1 is not allowed to be used. Allowed ports: []",
|
2016-05-05 19:43:54 +00:00
|
|
|
},
|
|
|
|
"failReadOnlyRootFS - nil": {
|
|
|
|
pod: defaultPod(),
|
|
|
|
psp: readOnlyRootFSPSP,
|
|
|
|
expectedError: "ReadOnlyRootFilesystem may not be nil and must be set to true",
|
|
|
|
},
|
|
|
|
"failReadOnlyRootFS - false": {
|
|
|
|
pod: readOnlyRootFSPodFalse,
|
|
|
|
psp: readOnlyRootFSPSP,
|
|
|
|
expectedError: "ReadOnlyRootFilesystem must be set to true",
|
|
|
|
},
|
2016-08-23 16:52:27 +00:00
|
|
|
"failSeccompContainerAnnotation": {
|
|
|
|
pod: failSeccompPod,
|
|
|
|
psp: defaultPSP(),
|
|
|
|
expectedError: "Forbidden: seccomp may not be set",
|
|
|
|
},
|
|
|
|
"failSeccompContainerPodAnnotation": {
|
|
|
|
pod: failSeccompPodInheritPodAnnotation,
|
|
|
|
psp: defaultPSP(),
|
|
|
|
expectedError: "Forbidden: seccomp may not be set",
|
|
|
|
},
|
2016-05-05 19:43:54 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
for k, v := range errorCases {
|
|
|
|
provider, err := NewSimpleProvider(v.psp, "namespace", NewSimpleStrategyFactory())
|
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("unable to create provider %v", err)
|
|
|
|
}
|
|
|
|
errs := provider.ValidateContainerSecurityContext(v.pod, &v.pod.Spec.Containers[0], field.NewPath(""))
|
|
|
|
if len(errs) == 0 {
|
|
|
|
t.Errorf("%s expected validation failure but did not receive errors", k)
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
if !strings.Contains(errs[0].Error(), v.expectedError) {
|
2017-06-16 08:22:50 +00:00
|
|
|
t.Errorf("%s received unexpected error %v\nexpected: %s", k, errs, v.expectedError)
|
2016-05-05 19:43:54 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestValidatePodSecurityContextSuccess(t *testing.T) {
|
|
|
|
hostNetworkPSP := defaultPSP()
|
|
|
|
hostNetworkPSP.Spec.HostNetwork = true
|
|
|
|
hostNetworkPod := defaultPod()
|
|
|
|
hostNetworkPod.Spec.SecurityContext.HostNetwork = true
|
|
|
|
|
|
|
|
hostPIDPSP := defaultPSP()
|
|
|
|
hostPIDPSP.Spec.HostPID = true
|
|
|
|
hostPIDPod := defaultPod()
|
|
|
|
hostPIDPod.Spec.SecurityContext.HostPID = true
|
|
|
|
|
|
|
|
hostIPCPSP := defaultPSP()
|
|
|
|
hostIPCPSP.Spec.HostIPC = true
|
|
|
|
hostIPCPod := defaultPod()
|
|
|
|
hostIPCPod.Spec.SecurityContext.HostIPC = true
|
|
|
|
|
|
|
|
supGroupPSP := defaultPSP()
|
|
|
|
supGroupPSP.Spec.SupplementalGroups = extensions.SupplementalGroupsStrategyOptions{
|
|
|
|
Rule: extensions.SupplementalGroupsStrategyMustRunAs,
|
2017-04-20 10:57:07 +00:00
|
|
|
Ranges: []extensions.GroupIDRange{
|
2016-05-05 19:43:54 +00:00
|
|
|
{Min: 1, Max: 5},
|
|
|
|
},
|
|
|
|
}
|
|
|
|
supGroupPod := defaultPod()
|
2017-06-21 07:13:36 +00:00
|
|
|
supGroupPod.Spec.SecurityContext.SupplementalGroups = []int64{3}
|
2016-05-05 19:43:54 +00:00
|
|
|
|
|
|
|
fsGroupPSP := defaultPSP()
|
|
|
|
fsGroupPSP.Spec.FSGroup = extensions.FSGroupStrategyOptions{
|
|
|
|
Rule: extensions.FSGroupStrategyMustRunAs,
|
2017-04-20 10:57:07 +00:00
|
|
|
Ranges: []extensions.GroupIDRange{
|
2016-05-05 19:43:54 +00:00
|
|
|
{Min: 1, Max: 5},
|
|
|
|
},
|
|
|
|
}
|
|
|
|
fsGroupPod := defaultPod()
|
2017-06-21 07:13:36 +00:00
|
|
|
fsGroup := int64(3)
|
2016-05-05 19:43:54 +00:00
|
|
|
fsGroupPod.Spec.SecurityContext.FSGroup = &fsGroup
|
|
|
|
|
|
|
|
seLinuxPod := defaultPod()
|
|
|
|
seLinuxPod.Spec.SecurityContext.SELinuxOptions = &api.SELinuxOptions{
|
|
|
|
User: "user",
|
|
|
|
Role: "role",
|
|
|
|
Type: "type",
|
|
|
|
Level: "level",
|
|
|
|
}
|
|
|
|
seLinuxPSP := defaultPSP()
|
|
|
|
seLinuxPSP.Spec.SELinux.Rule = extensions.SELinuxStrategyMustRunAs
|
|
|
|
seLinuxPSP.Spec.SELinux.SELinuxOptions = &api.SELinuxOptions{
|
|
|
|
User: "user",
|
|
|
|
Role: "role",
|
|
|
|
Type: "type",
|
|
|
|
Level: "level",
|
|
|
|
}
|
|
|
|
|
2017-08-06 19:40:18 +00:00
|
|
|
hostPathDirPod := defaultPod()
|
|
|
|
hostPathDirPod.Spec.Volumes = []api.Volume{
|
|
|
|
{
|
|
|
|
Name: "good volume",
|
|
|
|
VolumeSource: api.VolumeSource{
|
|
|
|
HostPath: &api.HostPathVolumeSource{
|
|
|
|
Path: "/foo/bar/baz",
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
}
|
|
|
|
|
|
|
|
hostPathDirPSP := defaultPSP()
|
|
|
|
hostPathDirPSP.Spec.Volumes = []extensions.FSType{extensions.HostPath}
|
|
|
|
hostPathDirPSP.Spec.AllowedHostPaths = []extensions.AllowedHostPath{
|
|
|
|
{PathPrefix: "/foo/bar"},
|
|
|
|
}
|
|
|
|
|
|
|
|
hostPathDirAsterisksPSP := defaultPSP()
|
|
|
|
hostPathDirAsterisksPSP.Spec.Volumes = []extensions.FSType{extensions.All}
|
|
|
|
hostPathDirAsterisksPSP.Spec.AllowedHostPaths = []extensions.AllowedHostPath{
|
|
|
|
{PathPrefix: "/foo/bar"},
|
|
|
|
}
|
|
|
|
|
2016-09-30 07:35:54 +00:00
|
|
|
sysctlAllowFooPSP := defaultPSP()
|
|
|
|
sysctlAllowFooPSP.Annotations[extensions.SysctlsPodSecurityPolicyAnnotationKey] = "foo"
|
|
|
|
|
|
|
|
safeSysctlFooPod := defaultPod()
|
|
|
|
safeSysctlFooPod.Annotations[api.SysctlsPodAnnotationKey] = "foo=1"
|
|
|
|
|
|
|
|
unsafeSysctlFooPod := defaultPod()
|
|
|
|
unsafeSysctlFooPod.Annotations[api.UnsafeSysctlsPodAnnotationKey] = "foo=1"
|
|
|
|
|
2016-08-23 16:52:27 +00:00
|
|
|
seccompPSP := defaultPSP()
|
|
|
|
seccompPSP.Annotations = map[string]string{
|
|
|
|
seccomp.AllowedProfilesAnnotationKey: "foo",
|
|
|
|
}
|
|
|
|
|
|
|
|
seccompPod := defaultPod()
|
|
|
|
seccompPod.Annotations = map[string]string{
|
|
|
|
api.SeccompPodAnnotationKey: "foo",
|
|
|
|
}
|
|
|
|
|
2017-09-27 09:00:27 +00:00
|
|
|
flexVolumePod := defaultPod()
|
|
|
|
flexVolumePod.Spec.Volumes = []api.Volume{
|
|
|
|
{
|
|
|
|
Name: "flex-volume",
|
|
|
|
VolumeSource: api.VolumeSource{
|
|
|
|
FlexVolume: &api.FlexVolumeSource{
|
|
|
|
Driver: "example/bar",
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
}
|
|
|
|
|
2017-08-09 15:50:04 +00:00
|
|
|
successCases := map[string]struct {
|
2016-05-05 19:43:54 +00:00
|
|
|
pod *api.Pod
|
|
|
|
psp *extensions.PodSecurityPolicy
|
|
|
|
}{
|
|
|
|
"pass hostNetwork validating PSP": {
|
|
|
|
pod: hostNetworkPod,
|
|
|
|
psp: hostNetworkPSP,
|
|
|
|
},
|
|
|
|
"pass hostPID validating PSP": {
|
|
|
|
pod: hostPIDPod,
|
|
|
|
psp: hostPIDPSP,
|
|
|
|
},
|
|
|
|
"pass hostIPC validating PSP": {
|
|
|
|
pod: hostIPCPod,
|
|
|
|
psp: hostIPCPSP,
|
|
|
|
},
|
|
|
|
"pass supplemental group validating PSP": {
|
|
|
|
pod: supGroupPod,
|
|
|
|
psp: supGroupPSP,
|
|
|
|
},
|
|
|
|
"pass fs group validating PSP": {
|
|
|
|
pod: fsGroupPod,
|
|
|
|
psp: fsGroupPSP,
|
|
|
|
},
|
|
|
|
"pass selinux validating PSP": {
|
|
|
|
pod: seLinuxPod,
|
|
|
|
psp: seLinuxPSP,
|
|
|
|
},
|
2016-09-30 07:35:54 +00:00
|
|
|
"pass sysctl specific profile with safe sysctl": {
|
|
|
|
pod: safeSysctlFooPod,
|
|
|
|
psp: sysctlAllowFooPSP,
|
|
|
|
},
|
|
|
|
"pass sysctl specific profile with unsafe sysctl": {
|
|
|
|
pod: unsafeSysctlFooPod,
|
|
|
|
psp: sysctlAllowFooPSP,
|
|
|
|
},
|
|
|
|
"pass empty profile with safe sysctl": {
|
|
|
|
pod: safeSysctlFooPod,
|
|
|
|
psp: defaultPSP(),
|
|
|
|
},
|
|
|
|
"pass empty profile with unsafe sysctl": {
|
|
|
|
pod: unsafeSysctlFooPod,
|
|
|
|
psp: defaultPSP(),
|
|
|
|
},
|
2017-08-06 19:40:18 +00:00
|
|
|
"pass hostDir allowed directory validating PSP": {
|
|
|
|
pod: hostPathDirPod,
|
|
|
|
psp: hostPathDirPSP,
|
|
|
|
},
|
|
|
|
"pass hostDir all volumes allowed validating PSP": {
|
|
|
|
pod: hostPathDirPod,
|
|
|
|
psp: hostPathDirAsterisksPSP,
|
|
|
|
},
|
2016-08-23 16:52:27 +00:00
|
|
|
"pass seccomp validating PSP": {
|
|
|
|
pod: seccompPod,
|
|
|
|
psp: seccompPSP,
|
|
|
|
},
|
2017-09-27 09:00:27 +00:00
|
|
|
"flex volume driver in a whitelist (all volumes are allowed)": {
|
|
|
|
pod: flexVolumePod,
|
|
|
|
psp: allowFlexVolumesPSP(false, true),
|
|
|
|
},
|
|
|
|
"flex volume driver with empty whitelist (all volumes are allowed)": {
|
|
|
|
pod: flexVolumePod,
|
|
|
|
psp: allowFlexVolumesPSP(true, true),
|
|
|
|
},
|
|
|
|
"flex volume driver in a whitelist (only flex volumes are allowed)": {
|
|
|
|
pod: flexVolumePod,
|
|
|
|
psp: allowFlexVolumesPSP(false, false),
|
|
|
|
},
|
|
|
|
"flex volume driver with empty whitelist (only flex volumes volumes are allowed)": {
|
|
|
|
pod: flexVolumePod,
|
|
|
|
psp: allowFlexVolumesPSP(true, false),
|
|
|
|
},
|
2016-05-05 19:43:54 +00:00
|
|
|
}
|
|
|
|
|
2017-08-09 15:50:04 +00:00
|
|
|
for k, v := range successCases {
|
2016-05-05 19:43:54 +00:00
|
|
|
provider, err := NewSimpleProvider(v.psp, "namespace", NewSimpleStrategyFactory())
|
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("unable to create provider %v", err)
|
|
|
|
}
|
2018-02-14 02:55:50 +00:00
|
|
|
errs := provider.ValidatePod(v.pod, field.NewPath(""))
|
2016-05-05 19:43:54 +00:00
|
|
|
if len(errs) != 0 {
|
|
|
|
t.Errorf("%s expected validation pass but received errors %v", k, errs)
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestValidateContainerSecurityContextSuccess(t *testing.T) {
|
2018-02-09 06:53:53 +00:00
|
|
|
// success user strategy
|
2016-05-05 19:43:54 +00:00
|
|
|
userPSP := defaultPSP()
|
2017-06-21 07:13:36 +00:00
|
|
|
uid := int64(999)
|
2016-05-05 19:43:54 +00:00
|
|
|
userPSP.Spec.RunAsUser = extensions.RunAsUserStrategyOptions{
|
|
|
|
Rule: extensions.RunAsUserStrategyMustRunAs,
|
2017-04-20 10:57:07 +00:00
|
|
|
Ranges: []extensions.UserIDRange{{Min: uid, Max: uid}},
|
2016-05-05 19:43:54 +00:00
|
|
|
}
|
|
|
|
userPod := defaultPod()
|
|
|
|
userPod.Spec.Containers[0].SecurityContext.RunAsUser = &uid
|
|
|
|
|
2018-02-09 06:53:53 +00:00
|
|
|
// success selinux strategy
|
2016-05-05 19:43:54 +00:00
|
|
|
seLinuxPSP := defaultPSP()
|
|
|
|
seLinuxPSP.Spec.SELinux = extensions.SELinuxStrategyOptions{
|
|
|
|
Rule: extensions.SELinuxStrategyMustRunAs,
|
|
|
|
SELinuxOptions: &api.SELinuxOptions{
|
|
|
|
Level: "foo",
|
|
|
|
},
|
|
|
|
}
|
|
|
|
seLinuxPod := defaultPod()
|
|
|
|
seLinuxPod.Spec.Containers[0].SecurityContext.SELinuxOptions = &api.SELinuxOptions{
|
|
|
|
Level: "foo",
|
|
|
|
}
|
|
|
|
|
2016-08-18 00:24:47 +00:00
|
|
|
appArmorPSP := defaultPSP()
|
|
|
|
appArmorPSP.Annotations = map[string]string{
|
|
|
|
apparmor.AllowedProfilesAnnotationKey: apparmor.ProfileRuntimeDefault,
|
|
|
|
}
|
2016-11-18 21:26:53 +00:00
|
|
|
v1AppArmorPod := defaultV1Pod()
|
|
|
|
apparmor.SetProfileName(v1AppArmorPod, defaultContainerName, apparmor.ProfileRuntimeDefault)
|
|
|
|
appArmorPod := &api.Pod{}
|
2017-10-09 17:13:46 +00:00
|
|
|
k8s_api_v1.Convert_v1_Pod_To_core_Pod(v1AppArmorPod, appArmorPod, nil)
|
2016-08-18 00:24:47 +00:00
|
|
|
|
2016-05-05 19:43:54 +00:00
|
|
|
privPSP := defaultPSP()
|
|
|
|
privPSP.Spec.Privileged = true
|
|
|
|
privPod := defaultPod()
|
|
|
|
var priv bool = true
|
|
|
|
privPod.Spec.Containers[0].SecurityContext.Privileged = &priv
|
|
|
|
|
|
|
|
capsPSP := defaultPSP()
|
|
|
|
capsPSP.Spec.AllowedCapabilities = []api.Capability{"foo"}
|
|
|
|
capsPod := defaultPod()
|
|
|
|
capsPod.Spec.Containers[0].SecurityContext.Capabilities = &api.Capabilities{
|
|
|
|
Add: []api.Capability{"foo"},
|
|
|
|
}
|
|
|
|
|
|
|
|
// pod should be able to request caps that are in the required set even if not specified in the allowed set
|
|
|
|
requiredCapsPSP := defaultPSP()
|
|
|
|
requiredCapsPSP.Spec.DefaultAddCapabilities = []api.Capability{"foo"}
|
|
|
|
requiredCapsPod := defaultPod()
|
|
|
|
requiredCapsPod.Spec.Containers[0].SecurityContext.Capabilities = &api.Capabilities{
|
|
|
|
Add: []api.Capability{"foo"},
|
|
|
|
}
|
|
|
|
|
|
|
|
hostDirPSP := defaultPSP()
|
|
|
|
hostDirPSP.Spec.Volumes = []extensions.FSType{extensions.HostPath}
|
|
|
|
hostDirPod := defaultPod()
|
|
|
|
hostDirPod.Spec.Volumes = []api.Volume{
|
|
|
|
{
|
2017-06-21 15:54:20 +00:00
|
|
|
Name: "bad volume",
|
2016-05-05 19:43:54 +00:00
|
|
|
VolumeSource: api.VolumeSource{
|
|
|
|
HostPath: &api.HostPathVolumeSource{},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
}
|
|
|
|
|
|
|
|
hostPortPSP := defaultPSP()
|
|
|
|
hostPortPSP.Spec.HostPorts = []extensions.HostPortRange{{Min: 1, Max: 1}}
|
|
|
|
hostPortPod := defaultPod()
|
|
|
|
hostPortPod.Spec.Containers[0].Ports = []api.ContainerPort{{HostPort: 1}}
|
|
|
|
|
|
|
|
readOnlyRootFSPodFalse := defaultPod()
|
|
|
|
readOnlyRootFSFalse := false
|
|
|
|
readOnlyRootFSPodFalse.Spec.Containers[0].SecurityContext.ReadOnlyRootFilesystem = &readOnlyRootFSFalse
|
|
|
|
|
|
|
|
readOnlyRootFSPodTrue := defaultPod()
|
|
|
|
readOnlyRootFSTrue := true
|
|
|
|
readOnlyRootFSPodTrue.Spec.Containers[0].SecurityContext.ReadOnlyRootFilesystem = &readOnlyRootFSTrue
|
|
|
|
|
2016-08-23 16:52:27 +00:00
|
|
|
seccompPSP := defaultPSP()
|
|
|
|
seccompPSP.Annotations = map[string]string{
|
|
|
|
seccomp.AllowedProfilesAnnotationKey: "foo",
|
|
|
|
}
|
|
|
|
|
|
|
|
seccompPod := defaultPod()
|
|
|
|
seccompPod.Annotations = map[string]string{
|
|
|
|
api.SeccompContainerAnnotationKeyPrefix + seccompPod.Spec.Containers[0].Name: "foo",
|
|
|
|
}
|
|
|
|
|
|
|
|
seccompPodInherit := defaultPod()
|
|
|
|
seccompPodInherit.Annotations = map[string]string{
|
|
|
|
api.SeccompPodAnnotationKey: "foo",
|
|
|
|
}
|
|
|
|
|
2017-08-09 15:50:04 +00:00
|
|
|
successCases := map[string]struct {
|
2016-05-05 19:43:54 +00:00
|
|
|
pod *api.Pod
|
|
|
|
psp *extensions.PodSecurityPolicy
|
|
|
|
}{
|
|
|
|
"pass user must run as PSP": {
|
|
|
|
pod: userPod,
|
|
|
|
psp: userPSP,
|
|
|
|
},
|
|
|
|
"pass seLinux must run as PSP": {
|
|
|
|
pod: seLinuxPod,
|
|
|
|
psp: seLinuxPSP,
|
|
|
|
},
|
2016-08-18 00:24:47 +00:00
|
|
|
"pass AppArmor allowed profiles": {
|
|
|
|
pod: appArmorPod,
|
|
|
|
psp: appArmorPSP,
|
|
|
|
},
|
2016-05-05 19:43:54 +00:00
|
|
|
"pass priv validating PSP": {
|
|
|
|
pod: privPod,
|
|
|
|
psp: privPSP,
|
|
|
|
},
|
|
|
|
"pass allowed caps validating PSP": {
|
|
|
|
pod: capsPod,
|
|
|
|
psp: capsPSP,
|
|
|
|
},
|
|
|
|
"pass required caps validating PSP": {
|
|
|
|
pod: requiredCapsPod,
|
|
|
|
psp: requiredCapsPSP,
|
|
|
|
},
|
|
|
|
"pass hostDir validating PSP": {
|
|
|
|
pod: hostDirPod,
|
|
|
|
psp: hostDirPSP,
|
|
|
|
},
|
|
|
|
"pass hostPort validating PSP": {
|
|
|
|
pod: hostPortPod,
|
|
|
|
psp: hostPortPSP,
|
|
|
|
},
|
|
|
|
"pass read only root fs - nil": {
|
|
|
|
pod: defaultPod(),
|
|
|
|
psp: defaultPSP(),
|
|
|
|
},
|
|
|
|
"pass read only root fs - false": {
|
|
|
|
pod: readOnlyRootFSPodFalse,
|
|
|
|
psp: defaultPSP(),
|
|
|
|
},
|
|
|
|
"pass read only root fs - true": {
|
|
|
|
pod: readOnlyRootFSPodTrue,
|
|
|
|
psp: defaultPSP(),
|
|
|
|
},
|
2016-08-23 16:52:27 +00:00
|
|
|
"pass seccomp container annotation": {
|
|
|
|
pod: seccompPod,
|
|
|
|
psp: seccompPSP,
|
|
|
|
},
|
|
|
|
"pass seccomp inherit pod annotation": {
|
|
|
|
pod: seccompPodInherit,
|
|
|
|
psp: seccompPSP,
|
|
|
|
},
|
2016-05-05 19:43:54 +00:00
|
|
|
}
|
|
|
|
|
2017-08-09 15:50:04 +00:00
|
|
|
for k, v := range successCases {
|
2016-05-05 19:43:54 +00:00
|
|
|
provider, err := NewSimpleProvider(v.psp, "namespace", NewSimpleStrategyFactory())
|
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("unable to create provider %v", err)
|
|
|
|
}
|
|
|
|
errs := provider.ValidateContainerSecurityContext(v.pod, &v.pod.Spec.Containers[0], field.NewPath(""))
|
|
|
|
if len(errs) != 0 {
|
2016-08-18 00:24:47 +00:00
|
|
|
t.Errorf("%s expected validation pass but received errors %v\n%s", k, errs, spew.Sdump(v.pod.ObjectMeta))
|
2016-05-05 19:43:54 +00:00
|
|
|
continue
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestGenerateContainerSecurityContextReadOnlyRootFS(t *testing.T) {
|
|
|
|
truePSP := defaultPSP()
|
|
|
|
truePSP.Spec.ReadOnlyRootFilesystem = true
|
|
|
|
|
|
|
|
trueVal := true
|
|
|
|
expectTrue := &trueVal
|
|
|
|
falseVal := false
|
|
|
|
expectFalse := &falseVal
|
|
|
|
|
|
|
|
falsePod := defaultPod()
|
|
|
|
falsePod.Spec.Containers[0].SecurityContext.ReadOnlyRootFilesystem = expectFalse
|
|
|
|
|
|
|
|
truePod := defaultPod()
|
|
|
|
truePod.Spec.Containers[0].SecurityContext.ReadOnlyRootFilesystem = expectTrue
|
|
|
|
|
|
|
|
tests := map[string]struct {
|
|
|
|
pod *api.Pod
|
|
|
|
psp *extensions.PodSecurityPolicy
|
|
|
|
expected *bool
|
|
|
|
}{
|
|
|
|
"false psp, nil sc": {
|
|
|
|
psp: defaultPSP(),
|
|
|
|
pod: defaultPod(),
|
|
|
|
expected: nil,
|
|
|
|
},
|
|
|
|
"false psp, false sc": {
|
|
|
|
psp: defaultPSP(),
|
|
|
|
pod: falsePod,
|
|
|
|
expected: expectFalse,
|
|
|
|
},
|
|
|
|
"false psp, true sc": {
|
|
|
|
psp: defaultPSP(),
|
|
|
|
pod: truePod,
|
|
|
|
expected: expectTrue,
|
|
|
|
},
|
|
|
|
"true psp, nil sc": {
|
|
|
|
psp: truePSP,
|
|
|
|
pod: defaultPod(),
|
|
|
|
expected: expectTrue,
|
|
|
|
},
|
|
|
|
"true psp, false sc": {
|
|
|
|
psp: truePSP,
|
|
|
|
pod: falsePod,
|
|
|
|
// expect false even though it defaults to true to ensure it doesn't change set values
|
|
|
|
// validation catches the mismatch, not generation
|
|
|
|
expected: expectFalse,
|
|
|
|
},
|
|
|
|
"true psp, true sc": {
|
|
|
|
psp: truePSP,
|
|
|
|
pod: truePod,
|
|
|
|
expected: expectTrue,
|
|
|
|
},
|
|
|
|
}
|
|
|
|
|
|
|
|
for k, v := range tests {
|
|
|
|
provider, err := NewSimpleProvider(v.psp, "namespace", NewSimpleStrategyFactory())
|
|
|
|
if err != nil {
|
|
|
|
t.Errorf("%s unable to create provider %v", k, err)
|
|
|
|
continue
|
|
|
|
}
|
2017-11-10 17:23:26 +00:00
|
|
|
err = provider.DefaultContainerSecurityContext(v.pod, &v.pod.Spec.Containers[0])
|
2016-05-05 19:43:54 +00:00
|
|
|
if err != nil {
|
|
|
|
t.Errorf("%s unable to create container security context %v", k, err)
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
2017-11-10 17:23:26 +00:00
|
|
|
sc := v.pod.Spec.Containers[0].SecurityContext
|
2016-05-05 19:43:54 +00:00
|
|
|
if v.expected == nil && sc.ReadOnlyRootFilesystem != nil {
|
|
|
|
t.Errorf("%s expected a nil ReadOnlyRootFilesystem but got %t", k, *sc.ReadOnlyRootFilesystem)
|
|
|
|
}
|
|
|
|
if v.expected != nil && sc.ReadOnlyRootFilesystem == nil {
|
2016-10-13 19:39:23 +00:00
|
|
|
t.Errorf("%s expected a non nil ReadOnlyRootFilesystem but received nil", k)
|
2016-05-05 19:43:54 +00:00
|
|
|
}
|
|
|
|
if v.expected != nil && sc.ReadOnlyRootFilesystem != nil && (*v.expected != *sc.ReadOnlyRootFilesystem) {
|
|
|
|
t.Errorf("%s expected a non nil ReadOnlyRootFilesystem set to %t but got %t", k, *v.expected, *sc.ReadOnlyRootFilesystem)
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func defaultPSP() *extensions.PodSecurityPolicy {
|
|
|
|
return &extensions.PodSecurityPolicy{
|
2017-01-17 03:38:19 +00:00
|
|
|
ObjectMeta: metav1.ObjectMeta{
|
2016-09-30 07:35:54 +00:00
|
|
|
Name: "psp-sa",
|
|
|
|
Annotations: map[string]string{},
|
2016-05-05 19:43:54 +00:00
|
|
|
},
|
|
|
|
Spec: extensions.PodSecurityPolicySpec{
|
|
|
|
RunAsUser: extensions.RunAsUserStrategyOptions{
|
|
|
|
Rule: extensions.RunAsUserStrategyRunAsAny,
|
|
|
|
},
|
|
|
|
SELinux: extensions.SELinuxStrategyOptions{
|
|
|
|
Rule: extensions.SELinuxStrategyRunAsAny,
|
|
|
|
},
|
|
|
|
FSGroup: extensions.FSGroupStrategyOptions{
|
|
|
|
Rule: extensions.FSGroupStrategyRunAsAny,
|
|
|
|
},
|
|
|
|
SupplementalGroups: extensions.SupplementalGroupsStrategyOptions{
|
|
|
|
Rule: extensions.SupplementalGroupsStrategyRunAsAny,
|
|
|
|
},
|
2017-06-26 19:13:28 +00:00
|
|
|
AllowPrivilegeEscalation: true,
|
2016-05-05 19:43:54 +00:00
|
|
|
},
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func defaultPod() *api.Pod {
|
|
|
|
var notPriv bool = false
|
|
|
|
return &api.Pod{
|
2017-01-17 03:38:19 +00:00
|
|
|
ObjectMeta: metav1.ObjectMeta{
|
2016-09-30 07:35:54 +00:00
|
|
|
Annotations: map[string]string{},
|
|
|
|
},
|
2016-05-05 19:43:54 +00:00
|
|
|
Spec: api.PodSpec{
|
|
|
|
SecurityContext: &api.PodSecurityContext{
|
|
|
|
// fill in for test cases
|
|
|
|
},
|
|
|
|
Containers: []api.Container{
|
|
|
|
{
|
2016-08-18 00:24:47 +00:00
|
|
|
Name: defaultContainerName,
|
2016-05-05 19:43:54 +00:00
|
|
|
SecurityContext: &api.SecurityContext{
|
|
|
|
// expected to be set by defaulting mechanisms
|
|
|
|
Privileged: ¬Priv,
|
|
|
|
// fill in the rest for test cases
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-11-18 21:26:53 +00:00
|
|
|
func defaultV1Pod() *v1.Pod {
|
|
|
|
var notPriv bool = false
|
|
|
|
return &v1.Pod{
|
2017-01-17 03:38:19 +00:00
|
|
|
ObjectMeta: metav1.ObjectMeta{
|
2016-11-18 21:26:53 +00:00
|
|
|
Annotations: map[string]string{},
|
|
|
|
},
|
|
|
|
Spec: v1.PodSpec{
|
|
|
|
SecurityContext: &v1.PodSecurityContext{
|
|
|
|
// fill in for test cases
|
|
|
|
},
|
|
|
|
Containers: []v1.Container{
|
|
|
|
{
|
|
|
|
Name: defaultContainerName,
|
|
|
|
SecurityContext: &v1.SecurityContext{
|
|
|
|
// expected to be set by defaulting mechanisms
|
|
|
|
Privileged: ¬Priv,
|
|
|
|
// fill in the rest for test cases
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-05-05 19:43:54 +00:00
|
|
|
// TestValidateAllowedVolumes will test that for every field of VolumeSource we can create
|
|
|
|
// a pod with that type of volume and deny it, accept it explicitly, or accept it with
|
|
|
|
// the FSTypeAll wildcard.
|
|
|
|
func TestValidateAllowedVolumes(t *testing.T) {
|
|
|
|
val := reflect.ValueOf(api.VolumeSource{})
|
|
|
|
|
|
|
|
for i := 0; i < val.NumField(); i++ {
|
|
|
|
// reflectively create the volume source
|
|
|
|
fieldVal := val.Type().Field(i)
|
|
|
|
|
|
|
|
volumeSource := api.VolumeSource{}
|
|
|
|
volumeSourceVolume := reflect.New(fieldVal.Type.Elem())
|
|
|
|
|
|
|
|
reflect.ValueOf(&volumeSource).Elem().FieldByName(fieldVal.Name).Set(volumeSourceVolume)
|
|
|
|
volume := api.Volume{VolumeSource: volumeSource}
|
|
|
|
|
|
|
|
// sanity check before moving on
|
|
|
|
fsType, err := psputil.GetVolumeFSType(volume)
|
|
|
|
if err != nil {
|
|
|
|
t.Errorf("error getting FSType for %s: %s", fieldVal.Name, err.Error())
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
|
|
|
// add the volume to the pod
|
|
|
|
pod := defaultPod()
|
|
|
|
pod.Spec.Volumes = []api.Volume{volume}
|
|
|
|
|
|
|
|
// create a PSP that allows no volumes
|
|
|
|
psp := defaultPSP()
|
|
|
|
|
|
|
|
provider, err := NewSimpleProvider(psp, "namespace", NewSimpleStrategyFactory())
|
|
|
|
if err != nil {
|
|
|
|
t.Errorf("error creating provider for %s: %s", fieldVal.Name, err.Error())
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
|
|
|
// expect a denial for this PSP and test the error message to ensure it's related to the volumesource
|
2018-02-14 02:55:50 +00:00
|
|
|
errs := provider.ValidatePod(pod, field.NewPath(""))
|
2016-05-05 19:43:54 +00:00
|
|
|
if len(errs) != 1 {
|
|
|
|
t.Errorf("expected exactly 1 error for %s but got %v", fieldVal.Name, errs)
|
|
|
|
} else {
|
|
|
|
if !strings.Contains(errs.ToAggregate().Error(), fmt.Sprintf("%s volumes are not allowed to be used", fsType)) {
|
|
|
|
t.Errorf("did not find the expected error, received: %v", errs)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// now add the fstype directly to the psp and it should validate
|
|
|
|
psp.Spec.Volumes = []extensions.FSType{fsType}
|
2018-02-14 02:55:50 +00:00
|
|
|
errs = provider.ValidatePod(pod, field.NewPath(""))
|
2016-05-05 19:43:54 +00:00
|
|
|
if len(errs) != 0 {
|
|
|
|
t.Errorf("directly allowing volume expected no errors for %s but got %v", fieldVal.Name, errs)
|
|
|
|
}
|
|
|
|
|
|
|
|
// now change the psp to allow any volumes and the pod should still validate
|
|
|
|
psp.Spec.Volumes = []extensions.FSType{extensions.All}
|
2018-02-14 02:55:50 +00:00
|
|
|
errs = provider.ValidatePod(pod, field.NewPath(""))
|
2016-05-05 19:43:54 +00:00
|
|
|
if len(errs) != 0 {
|
|
|
|
t.Errorf("wildcard volume expected no errors for %s but got %v", fieldVal.Name, errs)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2017-06-26 19:13:28 +00:00
|
|
|
|
|
|
|
// TestValidateAllowPrivilegeEscalation will test that when the podSecurityPolicy
|
|
|
|
// AllowPrivilegeEscalation is false we cannot set a container's securityContext
|
|
|
|
// to allowPrivilegeEscalation, but when it is true we can.
|
|
|
|
func TestValidateAllowPrivilegeEscalation(t *testing.T) {
|
|
|
|
pod := defaultPod()
|
|
|
|
pe := true
|
|
|
|
pod.Spec.Containers[0].SecurityContext.AllowPrivilegeEscalation = &pe
|
|
|
|
|
|
|
|
// create a PSP that does not allow privilege escalation
|
|
|
|
psp := defaultPSP()
|
|
|
|
psp.Spec.AllowPrivilegeEscalation = false
|
|
|
|
|
|
|
|
provider, err := NewSimpleProvider(psp, "namespace", NewSimpleStrategyFactory())
|
|
|
|
if err != nil {
|
|
|
|
t.Errorf("error creating provider: %v", err.Error())
|
|
|
|
}
|
|
|
|
|
|
|
|
// expect a denial for this PSP and test the error message to ensure it's related to allowPrivilegeEscalation
|
|
|
|
errs := provider.ValidateContainerSecurityContext(pod, &pod.Spec.Containers[0], field.NewPath(""))
|
|
|
|
if len(errs) != 1 {
|
|
|
|
t.Errorf("expected exactly 1 error but got %v", errs)
|
|
|
|
} else {
|
|
|
|
if !strings.Contains(errs.ToAggregate().Error(), "Allowing privilege escalation for containers is not allowed") {
|
|
|
|
t.Errorf("did not find the expected error, received: %v", errs)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// now add allowPrivilegeEscalation to the podSecurityPolicy
|
|
|
|
psp.Spec.AllowPrivilegeEscalation = true
|
|
|
|
errs = provider.ValidateContainerSecurityContext(pod, &pod.Spec.Containers[0], field.NewPath(""))
|
|
|
|
if len(errs) != 0 {
|
|
|
|
t.Errorf("directly allowing privilege escalation expected no errors but got %v", errs)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// TestValidateDefaultAllowPrivilegeEscalation will test that when the podSecurityPolicy
|
|
|
|
// DefaultAllowPrivilegeEscalation is false we cannot set a container's
|
|
|
|
// securityContext to allowPrivilegeEscalation but when it is true we can.
|
|
|
|
func TestValidateDefaultAllowPrivilegeEscalation(t *testing.T) {
|
|
|
|
pod := defaultPod()
|
|
|
|
pe := true
|
|
|
|
pod.Spec.Containers[0].SecurityContext.AllowPrivilegeEscalation = &pe
|
|
|
|
|
|
|
|
// create a PSP that does not allow privilege escalation
|
|
|
|
psp := defaultPSP()
|
|
|
|
dpe := false
|
|
|
|
psp.Spec.DefaultAllowPrivilegeEscalation = &dpe
|
|
|
|
psp.Spec.AllowPrivilegeEscalation = false
|
|
|
|
|
|
|
|
provider, err := NewSimpleProvider(psp, "namespace", NewSimpleStrategyFactory())
|
|
|
|
if err != nil {
|
|
|
|
t.Errorf("error creating provider: %v", err.Error())
|
|
|
|
}
|
|
|
|
|
|
|
|
// expect a denial for this PSP and test the error message to ensure it's related to allowPrivilegeEscalation
|
|
|
|
errs := provider.ValidateContainerSecurityContext(pod, &pod.Spec.Containers[0], field.NewPath(""))
|
|
|
|
if len(errs) != 1 {
|
|
|
|
t.Errorf("expected exactly 1 error but got %v", errs)
|
|
|
|
} else {
|
|
|
|
if !strings.Contains(errs.ToAggregate().Error(), "Allowing privilege escalation for containers is not allowed") {
|
|
|
|
t.Errorf("did not find the expected error, received: %v", errs)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// now add DefaultAllowPrivilegeEscalation to the podSecurityPolicy
|
|
|
|
dpe = true
|
|
|
|
psp.Spec.DefaultAllowPrivilegeEscalation = &dpe
|
|
|
|
psp.Spec.AllowPrivilegeEscalation = false
|
|
|
|
|
|
|
|
// expect a denial for this PSP because we did not allowPrivilege Escalation via the PodSecurityPolicy
|
|
|
|
// and test the error message to ensure it's related to allowPrivilegeEscalation
|
|
|
|
errs = provider.ValidateContainerSecurityContext(pod, &pod.Spec.Containers[0], field.NewPath(""))
|
|
|
|
if len(errs) != 1 {
|
|
|
|
t.Errorf("expected exactly 1 error but got %v", errs)
|
|
|
|
} else {
|
|
|
|
if !strings.Contains(errs.ToAggregate().Error(), "Allowing privilege escalation for containers is not allowed") {
|
|
|
|
t.Errorf("did not find the expected error, received: %v", errs)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Now set AllowPrivilegeEscalation
|
|
|
|
psp.Spec.AllowPrivilegeEscalation = true
|
|
|
|
errs = provider.ValidateContainerSecurityContext(pod, &pod.Spec.Containers[0], field.NewPath(""))
|
|
|
|
if len(errs) != 0 {
|
|
|
|
t.Errorf("directly allowing privilege escalation expected no errors but got %v", errs)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Now set the psp spec to false and reset AllowPrivilegeEscalation
|
|
|
|
psp.Spec.AllowPrivilegeEscalation = false
|
|
|
|
pod.Spec.Containers[0].SecurityContext.AllowPrivilegeEscalation = nil
|
|
|
|
errs = provider.ValidateContainerSecurityContext(pod, &pod.Spec.Containers[0], field.NewPath(""))
|
|
|
|
if len(errs) != 1 {
|
|
|
|
t.Errorf("expected exactly 1 error but got %v", errs)
|
|
|
|
} else {
|
|
|
|
if !strings.Contains(errs.ToAggregate().Error(), "Allowing privilege escalation for containers is not allowed") {
|
|
|
|
t.Errorf("did not find the expected error, received: %v", errs)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Now unset both AllowPrivilegeEscalation
|
|
|
|
psp.Spec.AllowPrivilegeEscalation = true
|
|
|
|
pod.Spec.Containers[0].SecurityContext.AllowPrivilegeEscalation = nil
|
|
|
|
errs = provider.ValidateContainerSecurityContext(pod, &pod.Spec.Containers[0], field.NewPath(""))
|
|
|
|
if len(errs) != 0 {
|
|
|
|
t.Errorf("resetting allowing privilege escalation expected no errors but got %v", errs)
|
|
|
|
}
|
|
|
|
}
|