123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939 |
- /*
- Copyright 2016 The Kubernetes Authors.
- 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 validation
- import (
- "fmt"
- "testing"
- "github.com/stretchr/testify/assert"
- metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
- "k8s.io/apimachinery/pkg/util/intstr"
- "k8s.io/apimachinery/pkg/util/validation/field"
- api "k8s.io/kubernetes/pkg/apis/core"
- "k8s.io/kubernetes/pkg/apis/policy"
- "k8s.io/kubernetes/pkg/security/apparmor"
- "k8s.io/kubernetes/pkg/security/podsecuritypolicy/seccomp"
- psputil "k8s.io/kubernetes/pkg/security/podsecuritypolicy/util"
- "k8s.io/utils/pointer"
- )
- func TestValidatePodDisruptionBudgetSpec(t *testing.T) {
- minAvailable := intstr.FromString("0%")
- maxUnavailable := intstr.FromString("10%")
- spec := policy.PodDisruptionBudgetSpec{
- MinAvailable: &minAvailable,
- MaxUnavailable: &maxUnavailable,
- }
- errs := ValidatePodDisruptionBudgetSpec(spec, field.NewPath("foo"))
- if len(errs) == 0 {
- t.Errorf("unexpected success for %v", spec)
- }
- }
- func TestValidateMinAvailablePodDisruptionBudgetSpec(t *testing.T) {
- successCases := []intstr.IntOrString{
- intstr.FromString("0%"),
- intstr.FromString("1%"),
- intstr.FromString("100%"),
- intstr.FromInt(0),
- intstr.FromInt(1),
- intstr.FromInt(100),
- }
- for _, c := range successCases {
- spec := policy.PodDisruptionBudgetSpec{
- MinAvailable: &c,
- }
- errs := ValidatePodDisruptionBudgetSpec(spec, field.NewPath("foo"))
- if len(errs) != 0 {
- t.Errorf("unexpected failure %v for %v", errs, spec)
- }
- }
- failureCases := []intstr.IntOrString{
- intstr.FromString("1.1%"),
- intstr.FromString("nope"),
- intstr.FromString("-1%"),
- intstr.FromString("101%"),
- intstr.FromInt(-1),
- }
- for _, c := range failureCases {
- spec := policy.PodDisruptionBudgetSpec{
- MinAvailable: &c,
- }
- errs := ValidatePodDisruptionBudgetSpec(spec, field.NewPath("foo"))
- if len(errs) == 0 {
- t.Errorf("unexpected success for %v", spec)
- }
- }
- }
- func TestValidateMinAvailablePodAndMaxUnavailableDisruptionBudgetSpec(t *testing.T) {
- c1 := intstr.FromString("10%")
- c2 := intstr.FromInt(1)
- spec := policy.PodDisruptionBudgetSpec{
- MinAvailable: &c1,
- MaxUnavailable: &c2,
- }
- errs := ValidatePodDisruptionBudgetSpec(spec, field.NewPath("foo"))
- if len(errs) == 0 {
- t.Errorf("unexpected success for %v", spec)
- }
- }
- func TestValidatePodDisruptionBudgetStatus(t *testing.T) {
- successCases := []policy.PodDisruptionBudgetStatus{
- {PodDisruptionsAllowed: 10},
- {CurrentHealthy: 5},
- {DesiredHealthy: 3},
- {ExpectedPods: 2}}
- for _, c := range successCases {
- errors := ValidatePodDisruptionBudgetStatus(c, field.NewPath("status"))
- if len(errors) > 0 {
- t.Errorf("unexpected failure %v for %v", errors, c)
- }
- }
- failureCases := []policy.PodDisruptionBudgetStatus{
- {PodDisruptionsAllowed: -10},
- {CurrentHealthy: -5},
- {DesiredHealthy: -3},
- {ExpectedPods: -2}}
- for _, c := range failureCases {
- errors := ValidatePodDisruptionBudgetStatus(c, field.NewPath("status"))
- if len(errors) == 0 {
- t.Errorf("unexpected success for %v", c)
- }
- }
- }
- func TestValidatePodSecurityPolicy(t *testing.T) {
- validPSP := func() *policy.PodSecurityPolicy {
- return &policy.PodSecurityPolicy{
- ObjectMeta: metav1.ObjectMeta{
- Name: "foo",
- Annotations: map[string]string{},
- },
- Spec: policy.PodSecurityPolicySpec{
- SELinux: policy.SELinuxStrategyOptions{
- Rule: policy.SELinuxStrategyRunAsAny,
- },
- RunAsUser: policy.RunAsUserStrategyOptions{
- Rule: policy.RunAsUserStrategyRunAsAny,
- },
- RunAsGroup: &policy.RunAsGroupStrategyOptions{
- Rule: policy.RunAsGroupStrategyRunAsAny,
- },
- FSGroup: policy.FSGroupStrategyOptions{
- Rule: policy.FSGroupStrategyRunAsAny,
- },
- SupplementalGroups: policy.SupplementalGroupsStrategyOptions{
- Rule: policy.SupplementalGroupsStrategyRunAsAny,
- },
- AllowedHostPaths: []policy.AllowedHostPath{
- {PathPrefix: "/foo/bar"},
- {PathPrefix: "/baz/"},
- },
- },
- }
- }
- noUserOptions := validPSP()
- noUserOptions.Spec.RunAsUser.Rule = ""
- noGroupOptions := validPSP()
- noGroupOptions.Spec.RunAsGroup.Rule = ""
- noSELinuxOptions := validPSP()
- noSELinuxOptions.Spec.SELinux.Rule = ""
- invalidUserStratType := validPSP()
- invalidUserStratType.Spec.RunAsUser.Rule = "invalid"
- invalidGroupStratType := validPSP()
- invalidGroupStratType.Spec.RunAsGroup.Rule = "invalid"
- invalidSELinuxStratType := validPSP()
- invalidSELinuxStratType.Spec.SELinux.Rule = "invalid"
- invalidUIDPSP := validPSP()
- invalidUIDPSP.Spec.RunAsUser.Rule = policy.RunAsUserStrategyMustRunAs
- invalidUIDPSP.Spec.RunAsUser.Ranges = []policy.IDRange{{Min: -1, Max: 1}}
- invalidGIDPSP := validPSP()
- invalidGIDPSP.Spec.RunAsGroup.Rule = policy.RunAsGroupStrategyMustRunAs
- invalidGIDPSP.Spec.RunAsGroup.Ranges = []policy.IDRange{{Min: -1, Max: 1}}
- missingObjectMetaName := validPSP()
- missingObjectMetaName.ObjectMeta.Name = ""
- noFSGroupOptions := validPSP()
- noFSGroupOptions.Spec.FSGroup.Rule = ""
- invalidFSGroupStratType := validPSP()
- invalidFSGroupStratType.Spec.FSGroup.Rule = "invalid"
- noSupplementalGroupsOptions := validPSP()
- noSupplementalGroupsOptions.Spec.SupplementalGroups.Rule = ""
- invalidSupGroupStratType := validPSP()
- invalidSupGroupStratType.Spec.SupplementalGroups.Rule = "invalid"
- invalidRangeMinGreaterThanMax := validPSP()
- invalidRangeMinGreaterThanMax.Spec.FSGroup.Ranges = []policy.IDRange{
- {Min: 2, Max: 1},
- }
- invalidRangeNegativeMin := validPSP()
- invalidRangeNegativeMin.Spec.FSGroup.Ranges = []policy.IDRange{
- {Min: -1, Max: 10},
- }
- invalidRangeNegativeMax := validPSP()
- invalidRangeNegativeMax.Spec.FSGroup.Ranges = []policy.IDRange{
- {Min: 1, Max: -10},
- }
- wildcardAllowedCapAndRequiredDrop := validPSP()
- wildcardAllowedCapAndRequiredDrop.Spec.RequiredDropCapabilities = []api.Capability{"foo"}
- wildcardAllowedCapAndRequiredDrop.Spec.AllowedCapabilities = []api.Capability{policy.AllowAllCapabilities}
- requiredCapAddAndDrop := validPSP()
- requiredCapAddAndDrop.Spec.DefaultAddCapabilities = []api.Capability{"foo"}
- requiredCapAddAndDrop.Spec.RequiredDropCapabilities = []api.Capability{"foo"}
- allowedCapListedInRequiredDrop := validPSP()
- allowedCapListedInRequiredDrop.Spec.RequiredDropCapabilities = []api.Capability{"foo"}
- allowedCapListedInRequiredDrop.Spec.AllowedCapabilities = []api.Capability{"foo"}
- invalidAppArmorDefault := validPSP()
- invalidAppArmorDefault.Annotations = map[string]string{
- apparmor.DefaultProfileAnnotationKey: "not-good",
- }
- invalidAppArmorAllowed := validPSP()
- invalidAppArmorAllowed.Annotations = map[string]string{
- apparmor.AllowedProfilesAnnotationKey: apparmor.ProfileRuntimeDefault + ",not-good",
- }
- invalidAllowedUnsafeSysctlPattern := validPSP()
- invalidAllowedUnsafeSysctlPattern.Spec.AllowedUnsafeSysctls = []string{"a.*.b"}
- invalidForbiddenSysctlPattern := validPSP()
- invalidForbiddenSysctlPattern.Spec.ForbiddenSysctls = []string{"a.*.b"}
- invalidOverlappingSysctls := validPSP()
- invalidOverlappingSysctls.Spec.ForbiddenSysctls = []string{"kernel.*", "net.ipv4.ip_local_port_range"}
- invalidOverlappingSysctls.Spec.AllowedUnsafeSysctls = []string{"kernel.shmmax", "net.ipv4.ip_local_port_range"}
- invalidDuplicatedSysctls := validPSP()
- invalidDuplicatedSysctls.Spec.ForbiddenSysctls = []string{"net.ipv4.ip_local_port_range"}
- invalidDuplicatedSysctls.Spec.AllowedUnsafeSysctls = []string{"net.ipv4.ip_local_port_range"}
- invalidSeccompDefault := validPSP()
- invalidSeccompDefault.Annotations = map[string]string{
- seccomp.DefaultProfileAnnotationKey: "not-good",
- }
- invalidSeccompAllowAnyDefault := validPSP()
- invalidSeccompAllowAnyDefault.Annotations = map[string]string{
- seccomp.DefaultProfileAnnotationKey: "*",
- }
- invalidSeccompAllowed := validPSP()
- invalidSeccompAllowed.Annotations = map[string]string{
- seccomp.AllowedProfilesAnnotationKey: api.SeccompProfileRuntimeDefault + ",not-good",
- }
- invalidAllowedHostPathMissingPath := validPSP()
- invalidAllowedHostPathMissingPath.Spec.AllowedHostPaths = []policy.AllowedHostPath{
- {PathPrefix: ""},
- }
- invalidAllowedHostPathBacksteps := validPSP()
- invalidAllowedHostPathBacksteps.Spec.AllowedHostPaths = []policy.AllowedHostPath{
- {PathPrefix: "/dont/allow/backsteps/.."},
- }
- invalidDefaultAllowPrivilegeEscalation := validPSP()
- pe := true
- invalidDefaultAllowPrivilegeEscalation.Spec.DefaultAllowPrivilegeEscalation = &pe
- emptyFlexDriver := validPSP()
- emptyFlexDriver.Spec.Volumes = []policy.FSType{policy.FlexVolume}
- emptyFlexDriver.Spec.AllowedFlexVolumes = []policy.AllowedFlexVolume{{}}
- nonEmptyFlexVolumes := validPSP()
- nonEmptyFlexVolumes.Spec.AllowedFlexVolumes = []policy.AllowedFlexVolume{{Driver: "example/driver"}}
- invalidProcMount := validPSP()
- invalidProcMount.Spec.AllowedProcMountTypes = []api.ProcMountType{api.ProcMountType("bogus")}
- allowedCSIDriverPSP := validPSP()
- allowedCSIDriverPSP.Spec.Volumes = []policy.FSType{policy.CSI}
- allowedCSIDriverPSP.Spec.AllowedCSIDrivers = []policy.AllowedCSIDriver{{}}
- type testCase struct {
- psp *policy.PodSecurityPolicy
- errorType field.ErrorType
- errorDetail string
- }
- errorCases := map[string]testCase{
- "no user options": {
- psp: noUserOptions,
- errorType: field.ErrorTypeNotSupported,
- errorDetail: `supported values: "MustRunAs", "MustRunAsNonRoot", "RunAsAny"`,
- },
- "no group options": {
- psp: noGroupOptions,
- errorType: field.ErrorTypeNotSupported,
- errorDetail: `supported values: "MustRunAs", "RunAsAny", "MayRunAs"`,
- },
- "no selinux options": {
- psp: noSELinuxOptions,
- errorType: field.ErrorTypeNotSupported,
- errorDetail: `supported values: "MustRunAs", "RunAsAny"`,
- },
- "no fsgroup options": {
- psp: noFSGroupOptions,
- errorType: field.ErrorTypeNotSupported,
- errorDetail: `supported values: "MayRunAs", "MustRunAs", "RunAsAny"`,
- },
- "no sup group options": {
- psp: noSupplementalGroupsOptions,
- errorType: field.ErrorTypeNotSupported,
- errorDetail: `supported values: "MayRunAs", "MustRunAs", "RunAsAny"`,
- },
- "invalid user strategy type": {
- psp: invalidUserStratType,
- errorType: field.ErrorTypeNotSupported,
- errorDetail: `supported values: "MustRunAs", "MustRunAsNonRoot", "RunAsAny"`,
- },
- "invalid group strategy type": {
- psp: invalidGroupStratType,
- errorType: field.ErrorTypeNotSupported,
- errorDetail: `supported values: "MustRunAs", "RunAsAny", "MayRunAs"`,
- },
- "invalid selinux strategy type": {
- psp: invalidSELinuxStratType,
- errorType: field.ErrorTypeNotSupported,
- errorDetail: `supported values: "MustRunAs", "RunAsAny"`,
- },
- "invalid sup group strategy type": {
- psp: invalidSupGroupStratType,
- errorType: field.ErrorTypeNotSupported,
- errorDetail: `supported values: "MayRunAs", "MustRunAs", "RunAsAny"`,
- },
- "invalid fs group strategy type": {
- psp: invalidFSGroupStratType,
- errorType: field.ErrorTypeNotSupported,
- errorDetail: `supported values: "MayRunAs", "MustRunAs", "RunAsAny"`,
- },
- "invalid uid": {
- psp: invalidUIDPSP,
- errorType: field.ErrorTypeInvalid,
- errorDetail: "min cannot be negative",
- },
- "invalid gid": {
- psp: invalidGIDPSP,
- errorType: field.ErrorTypeInvalid,
- errorDetail: "min cannot be negative",
- },
- "missing object meta name": {
- psp: missingObjectMetaName,
- errorType: field.ErrorTypeRequired,
- errorDetail: "name or generateName is required",
- },
- "invalid range min greater than max": {
- psp: invalidRangeMinGreaterThanMax,
- errorType: field.ErrorTypeInvalid,
- errorDetail: "min cannot be greater than max",
- },
- "invalid range negative min": {
- psp: invalidRangeNegativeMin,
- errorType: field.ErrorTypeInvalid,
- errorDetail: "min cannot be negative",
- },
- "invalid range negative max": {
- psp: invalidRangeNegativeMax,
- errorType: field.ErrorTypeInvalid,
- errorDetail: "max cannot be negative",
- },
- "non-empty required drops and all caps are allowed by a wildcard": {
- psp: wildcardAllowedCapAndRequiredDrop,
- errorType: field.ErrorTypeInvalid,
- errorDetail: "must be empty when all capabilities are allowed by a wildcard",
- },
- "invalid required caps": {
- psp: requiredCapAddAndDrop,
- errorType: field.ErrorTypeInvalid,
- errorDetail: "capability is listed in defaultAddCapabilities and requiredDropCapabilities",
- },
- "allowed cap listed in required drops": {
- psp: allowedCapListedInRequiredDrop,
- errorType: field.ErrorTypeInvalid,
- errorDetail: "capability is listed in allowedCapabilities and requiredDropCapabilities",
- },
- "invalid AppArmor default profile": {
- psp: invalidAppArmorDefault,
- errorType: field.ErrorTypeInvalid,
- errorDetail: "invalid AppArmor profile name: \"not-good\"",
- },
- "invalid AppArmor allowed profile": {
- psp: invalidAppArmorAllowed,
- errorType: field.ErrorTypeInvalid,
- errorDetail: "invalid AppArmor profile name: \"not-good\"",
- },
- "invalid allowed unsafe sysctl pattern": {
- psp: invalidAllowedUnsafeSysctlPattern,
- errorType: field.ErrorTypeInvalid,
- errorDetail: fmt.Sprintf("must have at most 253 characters and match regex %s", SysctlPatternFmt),
- },
- "invalid forbidden sysctl pattern": {
- psp: invalidForbiddenSysctlPattern,
- errorType: field.ErrorTypeInvalid,
- errorDetail: fmt.Sprintf("must have at most 253 characters and match regex %s", SysctlPatternFmt),
- },
- "invalid overlapping sysctl pattern": {
- psp: invalidOverlappingSysctls,
- errorType: field.ErrorTypeInvalid,
- errorDetail: fmt.Sprintf("sysctl overlaps with %s", invalidOverlappingSysctls.Spec.ForbiddenSysctls[0]),
- },
- "invalid duplicated sysctls": {
- psp: invalidDuplicatedSysctls,
- errorType: field.ErrorTypeInvalid,
- errorDetail: fmt.Sprintf("sysctl overlaps with %s", invalidDuplicatedSysctls.Spec.AllowedUnsafeSysctls[0]),
- },
- "invalid seccomp default profile": {
- psp: invalidSeccompDefault,
- errorType: field.ErrorTypeInvalid,
- errorDetail: "must be a valid seccomp profile",
- },
- "invalid seccomp allow any default profile": {
- psp: invalidSeccompAllowAnyDefault,
- errorType: field.ErrorTypeInvalid,
- errorDetail: "must be a valid seccomp profile",
- },
- "invalid seccomp allowed profile": {
- psp: invalidSeccompAllowed,
- errorType: field.ErrorTypeInvalid,
- errorDetail: "must be a valid seccomp profile",
- },
- "invalid defaultAllowPrivilegeEscalation": {
- psp: invalidDefaultAllowPrivilegeEscalation,
- errorType: field.ErrorTypeInvalid,
- errorDetail: "Cannot set DefaultAllowPrivilegeEscalation to true without also setting AllowPrivilegeEscalation to true",
- },
- "invalid allowed host path empty path": {
- psp: invalidAllowedHostPathMissingPath,
- errorType: field.ErrorTypeRequired,
- errorDetail: "is required",
- },
- "invalid allowed host path with backsteps": {
- psp: invalidAllowedHostPathBacksteps,
- errorType: field.ErrorTypeInvalid,
- errorDetail: "must not contain '..'",
- },
- "empty flex volume driver": {
- psp: emptyFlexDriver,
- errorType: field.ErrorTypeRequired,
- errorDetail: "must specify a driver",
- },
- "CSI policy with empty allowed driver list": {
- psp: allowedCSIDriverPSP,
- errorType: field.ErrorTypeRequired,
- },
- "invalid allowedProcMountTypes": {
- psp: invalidProcMount,
- errorType: field.ErrorTypeNotSupported,
- errorDetail: `supported values: "Default", "Unmasked"`,
- },
- }
- for k, v := range errorCases {
- errs := ValidatePodSecurityPolicy(v.psp)
- if len(errs) == 0 {
- t.Errorf("%s expected errors but got none", k)
- continue
- }
- if errs[0].Type != v.errorType {
- t.Errorf("[%s] received an unexpected error type. Expected: '%s' got: '%s'", k, v.errorType, errs[0].Type)
- }
- if errs[0].Detail != v.errorDetail {
- t.Errorf("[%s] received an unexpected error detail. Expected '%s' got: '%s'", k, v.errorDetail, errs[0].Detail)
- }
- }
- // Update error is different for 'missing object meta name'.
- errorCases["missing object meta name"] = testCase{
- psp: errorCases["missing object meta name"].psp,
- errorType: field.ErrorTypeInvalid,
- errorDetail: "field is immutable",
- }
- // Should not be able to update to an invalid policy.
- for k, v := range errorCases {
- v.psp.ResourceVersion = "444" // Required for updates.
- errs := ValidatePodSecurityPolicyUpdate(validPSP(), v.psp)
- if len(errs) == 0 {
- t.Errorf("[%s] expected update errors but got none", k)
- continue
- }
- if errs[0].Type != v.errorType {
- t.Errorf("[%s] received an unexpected error type. Expected: '%s' got: '%s'", k, v.errorType, errs[0].Type)
- }
- if errs[0].Detail != v.errorDetail {
- t.Errorf("[%s] received an unexpected error detail. Expected '%s' got: '%s'", k, v.errorDetail, errs[0].Detail)
- }
- }
- mustRunAs := validPSP()
- mustRunAs.Spec.FSGroup.Rule = policy.FSGroupStrategyMustRunAs
- mustRunAs.Spec.SupplementalGroups.Rule = policy.SupplementalGroupsStrategyMustRunAs
- mustRunAs.Spec.RunAsUser.Rule = policy.RunAsUserStrategyMustRunAs
- mustRunAs.Spec.RunAsUser.Ranges = []policy.IDRange{
- {Min: 1, Max: 1},
- }
- mustRunAs.Spec.SELinux.Rule = policy.SELinuxStrategyMustRunAs
- runAsNonRoot := validPSP()
- runAsNonRoot.Spec.RunAsUser.Rule = policy.RunAsUserStrategyMustRunAsNonRoot
- caseInsensitiveAddDrop := validPSP()
- caseInsensitiveAddDrop.Spec.DefaultAddCapabilities = []api.Capability{"foo"}
- caseInsensitiveAddDrop.Spec.RequiredDropCapabilities = []api.Capability{"FOO"}
- caseInsensitiveAllowedDrop := validPSP()
- caseInsensitiveAllowedDrop.Spec.RequiredDropCapabilities = []api.Capability{"FOO"}
- caseInsensitiveAllowedDrop.Spec.AllowedCapabilities = []api.Capability{"foo"}
- validAppArmor := validPSP()
- validAppArmor.Annotations = map[string]string{
- apparmor.DefaultProfileAnnotationKey: apparmor.ProfileRuntimeDefault,
- apparmor.AllowedProfilesAnnotationKey: apparmor.ProfileRuntimeDefault + "," + apparmor.ProfileNamePrefix + "foo",
- }
- withForbiddenSysctl := validPSP()
- withForbiddenSysctl.Spec.ForbiddenSysctls = []string{"net.*"}
- withAllowedUnsafeSysctl := validPSP()
- withAllowedUnsafeSysctl.Spec.AllowedUnsafeSysctls = []string{"net.ipv4.tcp_max_syn_backlog"}
- validSeccomp := validPSP()
- validSeccomp.Annotations = map[string]string{
- seccomp.DefaultProfileAnnotationKey: api.SeccompProfileRuntimeDefault,
- seccomp.AllowedProfilesAnnotationKey: api.SeccompProfileRuntimeDefault + ",unconfined,localhost/foo,*",
- }
- validDefaultAllowPrivilegeEscalation := validPSP()
- pe = true
- validDefaultAllowPrivilegeEscalation.Spec.DefaultAllowPrivilegeEscalation = &pe
- validDefaultAllowPrivilegeEscalation.Spec.AllowPrivilegeEscalation = true
- flexvolumeWhenFlexVolumesAllowed := validPSP()
- flexvolumeWhenFlexVolumesAllowed.Spec.Volumes = []policy.FSType{policy.FlexVolume}
- flexvolumeWhenFlexVolumesAllowed.Spec.AllowedFlexVolumes = []policy.AllowedFlexVolume{
- {Driver: "example/driver1"},
- }
- flexvolumeWhenAllVolumesAllowed := validPSP()
- flexvolumeWhenAllVolumesAllowed.Spec.Volumes = []policy.FSType{policy.All}
- flexvolumeWhenAllVolumesAllowed.Spec.AllowedFlexVolumes = []policy.AllowedFlexVolume{
- {Driver: "example/driver2"},
- }
- validProcMount := validPSP()
- validProcMount.Spec.AllowedProcMountTypes = []api.ProcMountType{api.DefaultProcMount, api.UnmaskedProcMount}
- allowedCSIDriversWithCSIFsType := validPSP()
- allowedCSIDriversWithCSIFsType.Spec.Volumes = []policy.FSType{policy.CSI}
- allowedCSIDriversWithCSIFsType.Spec.AllowedCSIDrivers = []policy.AllowedCSIDriver{{Name: "foo"}}
- allowedCSIDriversWithAllFsTypes := validPSP()
- allowedCSIDriversWithAllFsTypes.Spec.Volumes = []policy.FSType{policy.All}
- allowedCSIDriversWithAllFsTypes.Spec.AllowedCSIDrivers = []policy.AllowedCSIDriver{{Name: "bar"}}
- successCases := map[string]struct {
- psp *policy.PodSecurityPolicy
- }{
- "must run as": {
- psp: mustRunAs,
- },
- "run as any": {
- psp: validPSP(),
- },
- "run as non-root (user only)": {
- psp: runAsNonRoot,
- },
- "comparison for add -> drop is case sensitive": {
- psp: caseInsensitiveAddDrop,
- },
- "comparison for allowed -> drop is case sensitive": {
- psp: caseInsensitiveAllowedDrop,
- },
- "valid AppArmor annotations": {
- psp: validAppArmor,
- },
- "with network sysctls forbidden": {
- psp: withForbiddenSysctl,
- },
- "with unsafe net.ipv4.tcp_max_syn_backlog sysctl allowed": {
- psp: withAllowedUnsafeSysctl,
- },
- "valid seccomp annotations": {
- psp: validSeccomp,
- },
- "valid defaultAllowPrivilegeEscalation as true": {
- psp: validDefaultAllowPrivilegeEscalation,
- },
- "allow white-listed flexVolume when flex volumes are allowed": {
- psp: flexvolumeWhenFlexVolumesAllowed,
- },
- "allow white-listed flexVolume when all volumes are allowed": {
- psp: flexvolumeWhenAllVolumesAllowed,
- },
- "valid allowedProcMountTypes": {
- psp: validProcMount,
- },
- "allowed CSI drivers when FSType policy is set to CSI": {
- psp: allowedCSIDriversWithCSIFsType,
- },
- "allowed CSI drivers when FSType policy is set to All": {
- psp: allowedCSIDriversWithAllFsTypes,
- },
- }
- for k, v := range successCases {
- if errs := ValidatePodSecurityPolicy(v.psp); len(errs) != 0 {
- t.Errorf("Expected success for %s, got %v", k, errs)
- }
- // Should be able to update to a valid PSP.
- v.psp.ResourceVersion = "444" // Required for updates.
- if errs := ValidatePodSecurityPolicyUpdate(validPSP(), v.psp); len(errs) != 0 {
- t.Errorf("Expected success for %s update, got %v", k, errs)
- }
- }
- }
- func TestValidatePSPVolumes(t *testing.T) {
- validPSP := func() *policy.PodSecurityPolicy {
- return &policy.PodSecurityPolicy{
- ObjectMeta: metav1.ObjectMeta{Name: "foo"},
- Spec: policy.PodSecurityPolicySpec{
- SELinux: policy.SELinuxStrategyOptions{
- Rule: policy.SELinuxStrategyRunAsAny,
- },
- RunAsUser: policy.RunAsUserStrategyOptions{
- Rule: policy.RunAsUserStrategyRunAsAny,
- },
- RunAsGroup: &policy.RunAsGroupStrategyOptions{
- Rule: policy.RunAsGroupStrategyRunAsAny,
- },
- FSGroup: policy.FSGroupStrategyOptions{
- Rule: policy.FSGroupStrategyRunAsAny,
- },
- SupplementalGroups: policy.SupplementalGroupsStrategyOptions{
- Rule: policy.SupplementalGroupsStrategyRunAsAny,
- },
- },
- }
- }
- volumes := psputil.GetAllFSTypesAsSet()
- // add in the * value since that is a pseudo type that is not included by default
- volumes.Insert(string(policy.All))
- for _, strVolume := range volumes.List() {
- psp := validPSP()
- psp.Spec.Volumes = []policy.FSType{policy.FSType(strVolume)}
- errs := ValidatePodSecurityPolicy(psp)
- if len(errs) != 0 {
- t.Errorf("%s validation expected no errors but received %v", strVolume, errs)
- }
- }
- }
- func TestIsValidSysctlPattern(t *testing.T) {
- valid := []string{
- "a.b.c.d",
- "a",
- "a_b",
- "a-b",
- "abc",
- "abc.def",
- "*",
- "a.*",
- "*",
- "abc*",
- "a.abc*",
- "a.b.*",
- }
- invalid := []string{
- "",
- "ä",
- "a_",
- "_",
- "_a",
- "_a._b",
- "__",
- "-",
- ".",
- "a.",
- ".a",
- "a.b.",
- "a*.b",
- "a*b",
- "*a",
- "Abc",
- func(n int) string {
- x := make([]byte, n)
- for i := range x {
- x[i] = byte('a')
- }
- return string(x)
- }(256),
- }
- for _, s := range valid {
- if !IsValidSysctlPattern(s) {
- t.Errorf("%q expected to be a valid sysctl pattern", s)
- }
- }
- for _, s := range invalid {
- if IsValidSysctlPattern(s) {
- t.Errorf("%q expected to be an invalid sysctl pattern", s)
- }
- }
- }
- func TestValidatePSPRunAsUser(t *testing.T) {
- var testCases = []struct {
- name string
- runAsUserStrategy policy.RunAsUserStrategyOptions
- fail bool
- }{
- {"Invalid RunAsUserStrategy", policy.RunAsUserStrategyOptions{Rule: policy.RunAsUserStrategy("someInvalidStrategy")}, true},
- {"RunAsUserStrategyMustRunAs", policy.RunAsUserStrategyOptions{Rule: policy.RunAsUserStrategyMustRunAs}, false},
- {"RunAsUserStrategyMustRunAsNonRoot", policy.RunAsUserStrategyOptions{Rule: policy.RunAsUserStrategyMustRunAsNonRoot}, false},
- {"RunAsUserStrategyMustRunAsNonRoot With Valid Range", policy.RunAsUserStrategyOptions{Rule: policy.RunAsUserStrategyMustRunAs, Ranges: []policy.IDRange{{Min: 2, Max: 3}, {Min: 4, Max: 5}}}, false},
- {"RunAsUserStrategyMustRunAsNonRoot With Invalid Range", policy.RunAsUserStrategyOptions{Rule: policy.RunAsUserStrategyMustRunAs, Ranges: []policy.IDRange{{Min: 2, Max: 3}, {Min: 5, Max: 4}}}, true},
- }
- for _, testCase := range testCases {
- t.Run(testCase.name, func(t *testing.T) {
- errList := validatePSPRunAsUser(field.NewPath("status"), &testCase.runAsUserStrategy)
- actualErrors := len(errList)
- expectedErrors := 1
- if !testCase.fail {
- expectedErrors = 0
- }
- if actualErrors != expectedErrors {
- t.Errorf("In testCase %v, expected %v errors, got %v errors", testCase.name, expectedErrors, actualErrors)
- }
- })
- }
- }
- func TestValidatePSPFSGroup(t *testing.T) {
- var testCases = []struct {
- name string
- fsGroupStrategy policy.FSGroupStrategyOptions
- fail bool
- }{
- {"Invalid FSGroupStrategy", policy.FSGroupStrategyOptions{Rule: policy.FSGroupStrategyType("someInvalidStrategy")}, true},
- {"FSGroupStrategyMustRunAs", policy.FSGroupStrategyOptions{Rule: policy.FSGroupStrategyMustRunAs}, false},
- {"FSGroupStrategyMayRunAs", policy.FSGroupStrategyOptions{Rule: policy.FSGroupStrategyMayRunAs, Ranges: []policy.IDRange{{Min: 1, Max: 5}}}, false},
- {"FSGroupStrategyRunAsAny", policy.FSGroupStrategyOptions{Rule: policy.FSGroupStrategyRunAsAny}, false},
- }
- for _, testCase := range testCases {
- t.Run(testCase.name, func(t *testing.T) {
- errList := validatePSPFSGroup(field.NewPath("Status"), &testCase.fsGroupStrategy)
- actualErrors := len(errList)
- expectedErrors := 1
- if !testCase.fail {
- expectedErrors = 0
- }
- if actualErrors != expectedErrors {
- t.Errorf("In testCase %v, expected %v errors, got %v errors", testCase.name, expectedErrors, actualErrors)
- }
- })
- }
- }
- func TestValidatePSPSupplementalGroup(t *testing.T) {
- var testCases = []struct {
- name string
- supplementalGroupStrategy policy.SupplementalGroupsStrategyOptions
- fail bool
- }{
- {"Invalid SupplementalGroupStrategy", policy.SupplementalGroupsStrategyOptions{Rule: policy.SupplementalGroupsStrategyType("someInvalidStrategy")}, true},
- {"SupplementalGroupsStrategyMustRunAs", policy.SupplementalGroupsStrategyOptions{Rule: policy.SupplementalGroupsStrategyMustRunAs}, false},
- {"SupplementalGroupsStrategyMayRunAs", policy.SupplementalGroupsStrategyOptions{Rule: policy.SupplementalGroupsStrategyMayRunAs, Ranges: []policy.IDRange{{Min: 1, Max: 5}}}, false},
- {"SupplementalGroupsStrategyRunAsAny", policy.SupplementalGroupsStrategyOptions{Rule: policy.SupplementalGroupsStrategyRunAsAny}, false},
- }
- for _, testCase := range testCases {
- t.Run(testCase.name, func(t *testing.T) {
- errList := validatePSPSupplementalGroup(field.NewPath("Status"), &testCase.supplementalGroupStrategy)
- actualErrors := len(errList)
- expectedErrors := 1
- if !testCase.fail {
- expectedErrors = 0
- }
- if actualErrors != expectedErrors {
- t.Errorf("In testCase %v, expected %v errors, got %v errors", testCase.name, expectedErrors, actualErrors)
- }
- })
- }
- }
- func TestValidatePSPRunAsGroup(t *testing.T) {
- var testCases = []struct {
- name string
- runAsGroup policy.RunAsGroupStrategyOptions
- fail bool
- }{
- {"RunAsGroupStrategyMayRunAs", policy.RunAsGroupStrategyOptions{Rule: policy.RunAsGroupStrategyMayRunAs, Ranges: []policy.IDRange{{Min: 1, Max: 5}}}, false},
- {"RunAsGroupStrategyMustRunAs", policy.RunAsGroupStrategyOptions{Rule: policy.RunAsGroupStrategyMustRunAs, Ranges: []policy.IDRange{{Min: 1, Max: 5}}}, false},
- {"RunAsGroupStrategyRunAsAny", policy.RunAsGroupStrategyOptions{Rule: policy.RunAsGroupStrategyRunAsAny}, false},
- }
- for _, testCase := range testCases {
- t.Run(testCase.name, func(t *testing.T) {
- errList := validatePSPRunAsGroup(field.NewPath("Status"), &testCase.runAsGroup)
- actualErrors := len(errList)
- expectedErrors := 1
- if !testCase.fail {
- expectedErrors = 0
- }
- if actualErrors != expectedErrors {
- t.Errorf("In testCase %v, expected %v errors, got %v errors", testCase.name, expectedErrors, actualErrors)
- }
- })
- }
- }
- func TestValidatePSPSELinux(t *testing.T) {
- var testCases = []struct {
- name string
- selinux policy.SELinuxStrategyOptions
- fail bool
- }{
- {"SELinuxStrategyMustRunAs",
- policy.SELinuxStrategyOptions{
- Rule: policy.SELinuxStrategyMustRunAs,
- SELinuxOptions: &api.SELinuxOptions{Level: "s9:z0,z1"}}, false},
- {"SELinuxStrategyMustRunAs",
- policy.SELinuxStrategyOptions{
- Rule: policy.SELinuxStrategyMustRunAs,
- SELinuxOptions: &api.SELinuxOptions{Level: "s0"}}, false},
- }
- for _, testCase := range testCases {
- t.Run(testCase.name, func(t *testing.T) {
- errList := validatePSPSELinux(field.NewPath("Status"), &testCase.selinux)
- actualErrors := len(errList)
- expectedErrors := 1
- if !testCase.fail {
- expectedErrors = 0
- }
- if actualErrors != expectedErrors {
- t.Errorf("In testCase %v, expected %v errors, got %v errors", testCase.name, expectedErrors, actualErrors)
- }
- })
- }
- }
- func TestValidateRuntimeClassStrategy(t *testing.T) {
- var testCases = []struct {
- name string
- strategy *policy.RuntimeClassStrategyOptions
- expectErrors bool
- }{{
- name: "nil strategy",
- strategy: nil,
- }, {
- name: "empty strategy",
- strategy: &policy.RuntimeClassStrategyOptions{},
- }, {
- name: "allow all strategy",
- strategy: &policy.RuntimeClassStrategyOptions{
- AllowedRuntimeClassNames: []string{"*"},
- },
- }, {
- name: "valid defaulting & allow all",
- strategy: &policy.RuntimeClassStrategyOptions{
- DefaultRuntimeClassName: pointer.StringPtr("native"),
- AllowedRuntimeClassNames: []string{"*"},
- },
- }, {
- name: "valid defaulting & allow explicit",
- strategy: &policy.RuntimeClassStrategyOptions{
- DefaultRuntimeClassName: pointer.StringPtr("native"),
- AllowedRuntimeClassNames: []string{"foo", "native", "sandboxed"},
- },
- }, {
- name: "valid whitelisting",
- strategy: &policy.RuntimeClassStrategyOptions{
- AllowedRuntimeClassNames: []string{"foo", "native", "sandboxed"},
- },
- }, {
- name: "invalid default name",
- strategy: &policy.RuntimeClassStrategyOptions{
- DefaultRuntimeClassName: pointer.StringPtr("foo bar"),
- },
- expectErrors: true,
- }, {
- name: "disallowed default",
- strategy: &policy.RuntimeClassStrategyOptions{
- DefaultRuntimeClassName: pointer.StringPtr("foo"),
- AllowedRuntimeClassNames: []string{"native", "sandboxed"},
- },
- expectErrors: true,
- }, {
- name: "nothing allowed default",
- strategy: &policy.RuntimeClassStrategyOptions{
- DefaultRuntimeClassName: pointer.StringPtr("foo"),
- },
- expectErrors: true,
- }, {
- name: "invalid whitelist name",
- strategy: &policy.RuntimeClassStrategyOptions{
- AllowedRuntimeClassNames: []string{"native", "sandboxed", "foo*"},
- },
- expectErrors: true,
- }, {
- name: "duplicate whitelist names",
- strategy: &policy.RuntimeClassStrategyOptions{
- AllowedRuntimeClassNames: []string{"native", "sandboxed", "native"},
- },
- expectErrors: true,
- }, {
- name: "allow all redundant whitelist",
- strategy: &policy.RuntimeClassStrategyOptions{
- AllowedRuntimeClassNames: []string{"*", "sandboxed", "native"},
- },
- expectErrors: true,
- }}
- for _, test := range testCases {
- t.Run(test.name, func(t *testing.T) {
- errs := validateRuntimeClassStrategy(field.NewPath(""), test.strategy)
- if test.expectErrors {
- assert.NotEmpty(t, errs)
- } else {
- assert.Empty(t, errs)
- }
- })
- }
- }
|