1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440244124422443244424452446244724482449245024512452 |
- /*
- 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 podsecuritypolicy
- import (
- "context"
- "fmt"
- "reflect"
- "strings"
- "testing"
- "github.com/stretchr/testify/assert"
- v1 "k8s.io/api/core/v1"
- policy "k8s.io/api/policy/v1beta1"
- apiequality "k8s.io/apimachinery/pkg/api/equality"
- metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
- "k8s.io/apimachinery/pkg/util/diff"
- kadmission "k8s.io/apiserver/pkg/admission"
- admissiontesting "k8s.io/apiserver/pkg/admission/testing"
- "k8s.io/apiserver/pkg/authentication/serviceaccount"
- "k8s.io/apiserver/pkg/authentication/user"
- "k8s.io/apiserver/pkg/authorization/authorizer"
- "k8s.io/apiserver/pkg/authorization/authorizerfactory"
- utilfeature "k8s.io/apiserver/pkg/util/feature"
- "k8s.io/client-go/informers"
- featuregatetesting "k8s.io/component-base/featuregate/testing"
- "k8s.io/kubernetes/pkg/api/legacyscheme"
- kapi "k8s.io/kubernetes/pkg/apis/core"
- k8s_api_v1 "k8s.io/kubernetes/pkg/apis/core/v1"
- "k8s.io/kubernetes/pkg/controller"
- "k8s.io/kubernetes/pkg/features"
- "k8s.io/kubernetes/pkg/security/apparmor"
- kpsp "k8s.io/kubernetes/pkg/security/podsecuritypolicy"
- "k8s.io/kubernetes/pkg/security/podsecuritypolicy/seccomp"
- psputil "k8s.io/kubernetes/pkg/security/podsecuritypolicy/util"
- utilpointer "k8s.io/utils/pointer"
- )
- const defaultContainerName = "test-c"
- // NewTestAdmission provides an admission plugin with test implementations of internal structs.
- func NewTestAdmission(psps []*policy.PodSecurityPolicy, authz authorizer.Authorizer) *Plugin {
- informerFactory := informers.NewSharedInformerFactory(nil, controller.NoResyncPeriodFunc())
- store := informerFactory.Policy().V1beta1().PodSecurityPolicies().Informer().GetStore()
- for _, psp := range psps {
- store.Add(psp)
- }
- lister := informerFactory.Policy().V1beta1().PodSecurityPolicies().Lister()
- if authz == nil {
- authz = &TestAuthorizer{}
- }
- return &Plugin{
- Handler: kadmission.NewHandler(kadmission.Create, kadmission.Update),
- strategyFactory: kpsp.NewSimpleStrategyFactory(),
- authz: authz,
- lister: lister,
- }
- }
- // TestAuthorizer is a testing struct for testing that fulfills the authorizer interface.
- type TestAuthorizer struct {
- // usernameToNamespaceToAllowedPSPs contains the map of allowed PSPs.
- // if nil, all PSPs are allowed.
- usernameToNamespaceToAllowedPSPs map[string]map[string]map[string]bool
- // allowedAPIGroupName specifies an API Group name that contains PSP resources.
- // In order to be authorized, AttributesRecord must have this group name.
- // When empty, API Group name isn't taken into account.
- // TODO: remove this when PSP will be completely moved out of the extensions and we'll lookup only in "policy" group.
- allowedAPIGroupName string
- }
- func (t *TestAuthorizer) Authorize(ctx context.Context, a authorizer.Attributes) (authorized authorizer.Decision, reason string, err error) {
- if t.usernameToNamespaceToAllowedPSPs == nil {
- return authorizer.DecisionAllow, "", nil
- }
- allowedInNamespace := t.usernameToNamespaceToAllowedPSPs[a.GetUser().GetName()][a.GetNamespace()][a.GetName()]
- allowedClusterWide := t.usernameToNamespaceToAllowedPSPs[a.GetUser().GetName()][""][a.GetName()]
- allowedAPIGroup := len(t.allowedAPIGroupName) == 0 || a.GetAPIGroup() == t.allowedAPIGroupName
- if allowedAPIGroup && (allowedInNamespace || allowedClusterWide) {
- return authorizer.DecisionAllow, "", nil
- }
- return authorizer.DecisionNoOpinion, "", nil
- }
- var _ authorizer.Authorizer = &TestAuthorizer{}
- func useInitContainers(pod *kapi.Pod) *kapi.Pod {
- pod.Spec.InitContainers = pod.Spec.Containers
- pod.Spec.Containers = []kapi.Container{}
- return pod
- }
- func TestAdmitSeccomp(t *testing.T) {
- containerName := "container"
- tests := map[string]struct {
- pspAnnotations map[string]string
- podAnnotations map[string]string
- shouldPassAdmit bool
- shouldPassValidate bool
- }{
- "no seccomp, no pod annotations": {
- pspAnnotations: nil,
- podAnnotations: nil,
- shouldPassAdmit: true,
- shouldPassValidate: true,
- },
- "no seccomp, pod annotations": {
- pspAnnotations: nil,
- podAnnotations: map[string]string{
- kapi.SeccompPodAnnotationKey: "foo",
- },
- shouldPassAdmit: false,
- shouldPassValidate: false,
- },
- "no seccomp, container annotations": {
- pspAnnotations: nil,
- podAnnotations: map[string]string{
- kapi.SeccompContainerAnnotationKeyPrefix + containerName: "foo",
- },
- shouldPassAdmit: false,
- shouldPassValidate: false,
- },
- "seccomp, allow any no pod annotation": {
- pspAnnotations: map[string]string{
- seccomp.AllowedProfilesAnnotationKey: seccomp.AllowAny,
- },
- podAnnotations: nil,
- shouldPassAdmit: true,
- shouldPassValidate: true,
- },
- "seccomp, allow any pod annotation": {
- pspAnnotations: map[string]string{
- seccomp.AllowedProfilesAnnotationKey: seccomp.AllowAny,
- },
- podAnnotations: map[string]string{
- kapi.SeccompPodAnnotationKey: "foo",
- },
- shouldPassAdmit: true,
- shouldPassValidate: true,
- },
- "seccomp, allow any container annotation": {
- pspAnnotations: map[string]string{
- seccomp.AllowedProfilesAnnotationKey: seccomp.AllowAny,
- },
- podAnnotations: map[string]string{
- kapi.SeccompContainerAnnotationKeyPrefix + containerName: "foo",
- },
- shouldPassAdmit: true,
- shouldPassValidate: true,
- },
- "seccomp, allow specific pod annotation failure": {
- pspAnnotations: map[string]string{
- seccomp.AllowedProfilesAnnotationKey: "foo",
- },
- podAnnotations: map[string]string{
- kapi.SeccompPodAnnotationKey: "bar",
- },
- shouldPassAdmit: false,
- shouldPassValidate: false,
- },
- "seccomp, allow specific container annotation failure": {
- pspAnnotations: map[string]string{
- // provide a default so we don't have to give the pod annotation
- seccomp.DefaultProfileAnnotationKey: "foo",
- seccomp.AllowedProfilesAnnotationKey: "foo",
- },
- podAnnotations: map[string]string{
- kapi.SeccompContainerAnnotationKeyPrefix + containerName: "bar",
- },
- shouldPassAdmit: false,
- shouldPassValidate: false,
- },
- "seccomp, allow specific pod annotation pass": {
- pspAnnotations: map[string]string{
- seccomp.AllowedProfilesAnnotationKey: "foo",
- },
- podAnnotations: map[string]string{
- kapi.SeccompPodAnnotationKey: "foo",
- },
- shouldPassAdmit: true,
- shouldPassValidate: true,
- },
- "seccomp, allow specific container annotation pass": {
- pspAnnotations: map[string]string{
- // provide a default so we don't have to give the pod annotation
- seccomp.DefaultProfileAnnotationKey: "foo",
- seccomp.AllowedProfilesAnnotationKey: "foo,bar",
- },
- podAnnotations: map[string]string{
- kapi.SeccompContainerAnnotationKeyPrefix + containerName: "bar",
- },
- shouldPassAdmit: true,
- shouldPassValidate: true,
- },
- }
- for k, v := range tests {
- psp := restrictivePSP()
- psp.Annotations = v.pspAnnotations
- pod := &kapi.Pod{
- ObjectMeta: metav1.ObjectMeta{
- Annotations: v.podAnnotations,
- },
- Spec: kapi.PodSpec{
- Containers: []kapi.Container{
- {Name: containerName},
- },
- },
- }
- testPSPAdmit(k, []*policy.PodSecurityPolicy{psp}, pod, v.shouldPassAdmit, v.shouldPassValidate, psp.Name, t)
- }
- }
- func TestAdmitPrivileged(t *testing.T) {
- createPodWithPriv := func(priv bool) *kapi.Pod {
- pod := goodPod()
- pod.Spec.Containers[0].SecurityContext.Privileged = &priv
- return pod
- }
- nonPrivilegedPSP := restrictivePSP()
- nonPrivilegedPSP.Name = "non-priv"
- nonPrivilegedPSP.Spec.Privileged = false
- privilegedPSP := restrictivePSP()
- privilegedPSP.Name = "priv"
- privilegedPSP.Spec.Privileged = true
- trueValue := true
- falseValue := false
- tests := map[string]struct {
- pod *kapi.Pod
- psps []*policy.PodSecurityPolicy
- shouldPassAdmit bool
- shouldPassValidate bool
- expectedPriv *bool
- expectedPSP string
- }{
- "pod with priv=nil allowed under non priv PSP": {
- pod: goodPod(),
- psps: []*policy.PodSecurityPolicy{nonPrivilegedPSP},
- shouldPassAdmit: true,
- shouldPassValidate: true,
- expectedPriv: nil,
- expectedPSP: nonPrivilegedPSP.Name,
- },
- "pod with priv=nil allowed under priv PSP": {
- pod: goodPod(),
- psps: []*policy.PodSecurityPolicy{privilegedPSP},
- shouldPassAdmit: true,
- shouldPassValidate: true,
- expectedPriv: nil,
- expectedPSP: privilegedPSP.Name,
- },
- "pod with priv=false allowed under non priv PSP": {
- pod: createPodWithPriv(false),
- psps: []*policy.PodSecurityPolicy{nonPrivilegedPSP},
- shouldPassAdmit: true,
- shouldPassValidate: true,
- expectedPriv: &falseValue,
- expectedPSP: nonPrivilegedPSP.Name,
- },
- "pod with priv=false allowed under priv PSP": {
- pod: createPodWithPriv(false),
- psps: []*policy.PodSecurityPolicy{privilegedPSP},
- shouldPassAdmit: true,
- shouldPassValidate: true,
- expectedPriv: &falseValue,
- expectedPSP: privilegedPSP.Name,
- },
- "pod with priv=true denied by non priv PSP": {
- pod: createPodWithPriv(true),
- psps: []*policy.PodSecurityPolicy{nonPrivilegedPSP},
- shouldPassAdmit: false,
- shouldPassValidate: false,
- },
- "pod with priv=true allowed by priv PSP": {
- pod: createPodWithPriv(true),
- psps: []*policy.PodSecurityPolicy{nonPrivilegedPSP, privilegedPSP},
- shouldPassAdmit: true,
- shouldPassValidate: true,
- expectedPriv: &trueValue,
- expectedPSP: privilegedPSP.Name,
- },
- }
- for k, v := range tests {
- testPSPAdmit(k, v.psps, v.pod, v.shouldPassAdmit, v.shouldPassValidate, v.expectedPSP, t)
- if v.shouldPassAdmit {
- priv := v.pod.Spec.Containers[0].SecurityContext.Privileged
- if (priv == nil) != (v.expectedPriv == nil) {
- t.Errorf("%s expected privileged to be %v, got %v", k, v.expectedPriv, priv)
- } else if priv != nil && *priv != *v.expectedPriv {
- t.Errorf("%s expected privileged to be %v, got %v", k, *v.expectedPriv, *priv)
- }
- }
- }
- }
- func defaultPod(t *testing.T, pod *kapi.Pod) *kapi.Pod {
- v1Pod := &v1.Pod{}
- if err := legacyscheme.Scheme.Convert(pod, v1Pod, nil); err != nil {
- t.Fatal(err)
- }
- legacyscheme.Scheme.Default(v1Pod)
- apiPod := &kapi.Pod{}
- if err := legacyscheme.Scheme.Convert(v1Pod, apiPod, nil); err != nil {
- t.Fatal(err)
- }
- return apiPod
- }
- func TestAdmitPreferNonmutating(t *testing.T) {
- mutating1 := restrictivePSP()
- mutating1.Name = "mutating1"
- mutating1.Spec.RunAsUser.Ranges = []policy.IDRange{{Min: int64(1), Max: int64(1)}}
- mutating2 := restrictivePSP()
- mutating2.Name = "mutating2"
- mutating2.Spec.RunAsUser.Ranges = []policy.IDRange{{Min: int64(2), Max: int64(2)}}
- privilegedPSP := permissivePSP()
- privilegedPSP.Name = "privileged"
- unprivilegedRunAsAnyPod := defaultPod(t, &kapi.Pod{
- ObjectMeta: metav1.ObjectMeta{},
- Spec: kapi.PodSpec{
- ServiceAccountName: "default",
- Containers: []kapi.Container{{Name: "mycontainer", Image: "myimage"}},
- },
- })
- changedPod := unprivilegedRunAsAnyPod.DeepCopy()
- changedPod.Spec.Containers[0].Image = "myimage2"
- podWithSC := unprivilegedRunAsAnyPod.DeepCopy()
- podWithSC.Annotations = map[string]string{psputil.ValidatedPSPAnnotation: privilegedPSP.Name}
- changedPodWithSC := changedPod.DeepCopy()
- changedPodWithSC.Annotations = map[string]string{psputil.ValidatedPSPAnnotation: privilegedPSP.Name}
- gcChangedPod := unprivilegedRunAsAnyPod.DeepCopy()
- gcChangedPod.OwnerReferences = []metav1.OwnerReference{{Kind: "Foo", Name: "bar"}}
- gcChangedPod.Finalizers = []string{"foo"}
- podWithAnnotation := unprivilegedRunAsAnyPod.DeepCopy()
- podWithAnnotation.ObjectMeta.Annotations = map[string]string{
- // "mutating2" is lexicographically behind "mutating1", so "mutating1" should be
- // chosen because it's the canonical PSP order.
- psputil.ValidatedPSPAnnotation: mutating2.Name,
- }
- tests := map[string]struct {
- operation kadmission.Operation
- pod *kapi.Pod
- podBeforeUpdate *kapi.Pod
- psps []*policy.PodSecurityPolicy
- shouldPassValidate bool
- expectMutation bool
- expectedContainerUser *int64
- expectedPSP string
- }{
- "pod should not be mutated by allow-all strategies": {
- operation: kadmission.Create,
- pod: unprivilegedRunAsAnyPod.DeepCopy(),
- psps: []*policy.PodSecurityPolicy{privilegedPSP},
- shouldPassValidate: true,
- expectMutation: false,
- expectedContainerUser: nil,
- expectedPSP: privilegedPSP.Name,
- },
- "pod should prefer non-mutating PSP on create": {
- operation: kadmission.Create,
- pod: unprivilegedRunAsAnyPod.DeepCopy(),
- psps: []*policy.PodSecurityPolicy{mutating2, mutating1, privilegedPSP},
- shouldPassValidate: true,
- expectMutation: false,
- expectedContainerUser: nil,
- expectedPSP: privilegedPSP.Name,
- },
- "pod should use deterministic mutating PSP on create": {
- operation: kadmission.Create,
- pod: unprivilegedRunAsAnyPod.DeepCopy(),
- psps: []*policy.PodSecurityPolicy{mutating2, mutating1},
- shouldPassValidate: true,
- expectMutation: true,
- expectedContainerUser: &mutating1.Spec.RunAsUser.Ranges[0].Min,
- expectedPSP: mutating1.Name,
- },
- "pod should use deterministic mutating PSP on create even if ValidatedPSPAnnotation is set": {
- operation: kadmission.Create,
- pod: podWithAnnotation,
- psps: []*policy.PodSecurityPolicy{mutating2, mutating1},
- shouldPassValidate: true,
- expectMutation: true,
- expectedContainerUser: &mutating1.Spec.RunAsUser.Ranges[0].Min,
- expectedPSP: mutating1.Name,
- },
- "pod should prefer non-mutating PSP on update": {
- operation: kadmission.Update,
- pod: changedPodWithSC.DeepCopy(),
- podBeforeUpdate: podWithSC.DeepCopy(),
- psps: []*policy.PodSecurityPolicy{mutating2, mutating1, privilegedPSP},
- shouldPassValidate: true,
- expectMutation: false,
- expectedContainerUser: nil,
- expectedPSP: privilegedPSP.Name,
- },
- "pod should not mutate on update, but fail validation": {
- operation: kadmission.Update,
- pod: changedPod.DeepCopy(),
- podBeforeUpdate: unprivilegedRunAsAnyPod.DeepCopy(),
- psps: []*policy.PodSecurityPolicy{mutating2, mutating1},
- shouldPassValidate: false,
- expectMutation: false,
- expectedContainerUser: nil,
- expectedPSP: "",
- },
- "pod should be allowed if completely unchanged on update": {
- operation: kadmission.Update,
- pod: unprivilegedRunAsAnyPod.DeepCopy(),
- podBeforeUpdate: unprivilegedRunAsAnyPod.DeepCopy(),
- psps: []*policy.PodSecurityPolicy{mutating2, mutating1},
- shouldPassValidate: true,
- expectMutation: false,
- expectedContainerUser: nil,
- expectedPSP: "",
- },
- "pod should be allowed if unchanged on update except finalizers,ownerrefs": {
- operation: kadmission.Update,
- pod: gcChangedPod.DeepCopy(),
- podBeforeUpdate: unprivilegedRunAsAnyPod.DeepCopy(),
- psps: []*policy.PodSecurityPolicy{mutating2, mutating1},
- shouldPassValidate: true,
- expectMutation: false,
- expectedContainerUser: nil,
- expectedPSP: "",
- },
- }
- for k, v := range tests {
- testPSPAdmitAdvanced(k, v.operation, v.psps, nil, &user.DefaultInfo{}, v.pod, v.podBeforeUpdate, true, v.shouldPassValidate, v.expectMutation, v.expectedPSP, t)
- actualPodUser := (*int64)(nil)
- if v.pod.Spec.SecurityContext != nil {
- actualPodUser = v.pod.Spec.SecurityContext.RunAsUser
- }
- if actualPodUser != nil {
- t.Errorf("%s expected pod user nil, got %v", k, *actualPodUser)
- }
- actualContainerUser := (*int64)(nil)
- if v.pod.Spec.Containers[0].SecurityContext != nil {
- actualContainerUser = v.pod.Spec.Containers[0].SecurityContext.RunAsUser
- }
- if (actualContainerUser == nil) != (v.expectedContainerUser == nil) {
- t.Errorf("%s expected container user %v, got %v", k, v.expectedContainerUser, actualContainerUser)
- } else if actualContainerUser != nil && *actualContainerUser != *v.expectedContainerUser {
- t.Errorf("%s expected container user %v, got %v", k, *v.expectedContainerUser, *actualContainerUser)
- }
- }
- }
- func TestFailClosedOnInvalidPod(t *testing.T) {
- plugin := NewTestAdmission(nil, nil)
- pod := &v1.Pod{}
- attrs := kadmission.NewAttributesRecord(pod, nil, kapi.Kind("Pod").WithVersion("version"), pod.Namespace, pod.Name, kapi.Resource("pods").WithVersion("version"), "", kadmission.Create, &metav1.CreateOptions{}, false, &user.DefaultInfo{})
- err := plugin.Admit(context.TODO(), attrs, nil)
- if err == nil {
- t.Fatalf("expected versioned pod object to fail mutating admission")
- }
- if !strings.Contains(err.Error(), "unexpected type") {
- t.Errorf("expected type error on Admit but got: %v", err)
- }
- err = plugin.Validate(context.TODO(), attrs, nil)
- if err == nil {
- t.Fatalf("expected versioned pod object to fail validating admission")
- }
- if !strings.Contains(err.Error(), "unexpected type") {
- t.Errorf("expected type error on Validate but got: %v", err)
- }
- }
- func TestAdmitCaps(t *testing.T) {
- createPodWithCaps := func(caps *kapi.Capabilities) *kapi.Pod {
- pod := goodPod()
- pod.Spec.Containers[0].SecurityContext.Capabilities = caps
- return pod
- }
- restricted := restrictivePSP()
- allowsFooInAllowed := restrictivePSP()
- allowsFooInAllowed.Name = "allowCapInAllowed"
- allowsFooInAllowed.Spec.AllowedCapabilities = []v1.Capability{"foo"}
- allowsFooInRequired := restrictivePSP()
- allowsFooInRequired.Name = "allowCapInRequired"
- allowsFooInRequired.Spec.DefaultAddCapabilities = []v1.Capability{"foo"}
- requiresFooToBeDropped := restrictivePSP()
- requiresFooToBeDropped.Name = "requireDrop"
- requiresFooToBeDropped.Spec.RequiredDropCapabilities = []v1.Capability{"foo"}
- allowAllInAllowed := restrictivePSP()
- allowAllInAllowed.Name = "allowAllCapsInAllowed"
- allowAllInAllowed.Spec.AllowedCapabilities = []v1.Capability{policy.AllowAllCapabilities}
- tc := map[string]struct {
- pod *kapi.Pod
- psps []*policy.PodSecurityPolicy
- shouldPassAdmit bool
- shouldPassValidate bool
- expectedCapabilities *kapi.Capabilities
- expectedPSP string
- }{
- // UC 1: if a PSP does not define allowed or required caps then a pod requesting a cap
- // should be rejected.
- "should reject cap add when not allowed or required": {
- pod: createPodWithCaps(&kapi.Capabilities{Add: []kapi.Capability{"foo"}}),
- psps: []*policy.PodSecurityPolicy{restricted},
- shouldPassAdmit: false,
- shouldPassValidate: false,
- },
- // UC 2: if a PSP allows a cap in the allowed field it should accept the pod request
- // to add the cap.
- "should accept cap add when in allowed": {
- pod: createPodWithCaps(&kapi.Capabilities{Add: []kapi.Capability{"foo"}}),
- psps: []*policy.PodSecurityPolicy{restricted, allowsFooInAllowed},
- shouldPassAdmit: true,
- shouldPassValidate: true,
- expectedPSP: allowsFooInAllowed.Name,
- },
- // UC 3: if a PSP requires a cap then it should accept the pod request
- // to add the cap.
- "should accept cap add when in required": {
- pod: createPodWithCaps(&kapi.Capabilities{Add: []kapi.Capability{"foo"}}),
- psps: []*policy.PodSecurityPolicy{restricted, allowsFooInRequired},
- shouldPassAdmit: true,
- shouldPassValidate: true,
- expectedPSP: allowsFooInRequired.Name,
- },
- // UC 4: if a PSP requires a cap to be dropped then it should fail both
- // in the verification of adds and verification of drops
- "should reject cap add when requested cap is required to be dropped": {
- pod: createPodWithCaps(&kapi.Capabilities{Add: []kapi.Capability{"foo"}}),
- psps: []*policy.PodSecurityPolicy{restricted, requiresFooToBeDropped},
- shouldPassAdmit: false,
- shouldPassValidate: false,
- },
- // UC 5: if a PSP requires a cap to be dropped it should accept
- // a manual request to drop the cap.
- "should accept cap drop when cap is required to be dropped": {
- pod: createPodWithCaps(&kapi.Capabilities{Drop: []kapi.Capability{"foo"}}),
- psps: []*policy.PodSecurityPolicy{requiresFooToBeDropped},
- shouldPassAdmit: true,
- shouldPassValidate: true,
- expectedPSP: requiresFooToBeDropped.Name,
- },
- // UC 6: required add is defaulted
- "required add is defaulted": {
- pod: goodPod(),
- psps: []*policy.PodSecurityPolicy{allowsFooInRequired},
- shouldPassAdmit: true,
- shouldPassValidate: true,
- expectedCapabilities: &kapi.Capabilities{
- Add: []kapi.Capability{"foo"},
- },
- expectedPSP: allowsFooInRequired.Name,
- },
- // UC 7: required drop is defaulted
- "required drop is defaulted": {
- pod: goodPod(),
- psps: []*policy.PodSecurityPolicy{requiresFooToBeDropped},
- shouldPassAdmit: true,
- shouldPassValidate: true,
- expectedCapabilities: &kapi.Capabilities{
- Drop: []kapi.Capability{"foo"},
- },
- expectedPSP: requiresFooToBeDropped.Name,
- },
- // UC 8: using '*' in allowed caps
- "should accept cap add when all caps are allowed": {
- pod: createPodWithCaps(&kapi.Capabilities{Add: []kapi.Capability{"foo"}}),
- psps: []*policy.PodSecurityPolicy{restricted, allowAllInAllowed},
- shouldPassAdmit: true,
- shouldPassValidate: true,
- expectedPSP: allowAllInAllowed.Name,
- },
- }
- for k, v := range tc {
- testPSPAdmit(k, v.psps, v.pod, v.shouldPassAdmit, v.shouldPassValidate, v.expectedPSP, t)
- if v.expectedCapabilities != nil {
- if !reflect.DeepEqual(v.expectedCapabilities, v.pod.Spec.Containers[0].SecurityContext.Capabilities) {
- t.Errorf("%s resulted in caps that were not expected - expected: %v, received: %v", k, v.expectedCapabilities, v.pod.Spec.Containers[0].SecurityContext.Capabilities)
- }
- }
- }
- for k, v := range tc {
- useInitContainers(v.pod)
- testPSPAdmit(k, v.psps, v.pod, v.shouldPassAdmit, v.shouldPassValidate, 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) {
- defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.CSIInlineVolume, true)()
- val := reflect.ValueOf(kapi.VolumeSource{})
- for i := 0; i < val.NumField(); i++ {
- // reflectively create the volume source
- fieldVal := val.Type().Field(i)
- volumeSource := kapi.VolumeSource{}
- volumeSourceVolume := reflect.New(fieldVal.Type.Elem())
- reflect.ValueOf(&volumeSource).Elem().FieldByName(fieldVal.Name).Set(volumeSourceVolume)
- volume := kapi.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 := goodPod()
- pod.Spec.Volumes = []kapi.Volume{volume}
- // create a PSP that allows no volumes
- psp := restrictivePSP()
- // expect a denial for this PSP
- testPSPAdmit(fmt.Sprintf("%s denial", string(fsType)), []*policy.PodSecurityPolicy{psp}, pod, false, false, "", t)
- // also expect a denial for this PSP if it's an init container
- useInitContainers(pod)
- testPSPAdmit(fmt.Sprintf("%s denial", string(fsType)), []*policy.PodSecurityPolicy{psp}, pod, false, false, "", t)
- // now add the fstype directly to the psp and it should validate
- psp.Spec.Volumes = []policy.FSType{fsType}
- testPSPAdmit(fmt.Sprintf("%s direct accept", string(fsType)), []*policy.PodSecurityPolicy{psp}, pod, true, true, psp.Name, t)
- // now change the psp to allow any volumes and the pod should still validate
- psp.Spec.Volumes = []policy.FSType{policy.All}
- testPSPAdmit(fmt.Sprintf("%s wildcard accept", string(fsType)), []*policy.PodSecurityPolicy{psp}, pod, true, true, psp.Name, t)
- }
- }
- func TestAdmitHostNetwork(t *testing.T) {
- createPodWithHostNetwork := func(hostNetwork bool) *kapi.Pod {
- pod := goodPod()
- pod.Spec.SecurityContext.HostNetwork = hostNetwork
- return pod
- }
- noHostNetwork := restrictivePSP()
- noHostNetwork.Name = "no-hostnetwork"
- noHostNetwork.Spec.HostNetwork = false
- hostNetwork := restrictivePSP()
- hostNetwork.Name = "hostnetwork"
- hostNetwork.Spec.HostNetwork = true
- tests := map[string]struct {
- pod *kapi.Pod
- psps []*policy.PodSecurityPolicy
- shouldPassAdmit bool
- shouldPassValidate bool
- expectedHostNetwork bool
- expectedPSP string
- }{
- "pod without hostnetwork request allowed under noHostNetwork PSP": {
- pod: goodPod(),
- psps: []*policy.PodSecurityPolicy{noHostNetwork},
- shouldPassAdmit: true,
- shouldPassValidate: true,
- expectedHostNetwork: false,
- expectedPSP: noHostNetwork.Name,
- },
- "pod without hostnetwork request allowed under hostNetwork PSP": {
- pod: goodPod(),
- psps: []*policy.PodSecurityPolicy{hostNetwork},
- shouldPassAdmit: true,
- shouldPassValidate: true,
- expectedHostNetwork: false,
- expectedPSP: hostNetwork.Name,
- },
- "pod with hostnetwork request denied by noHostNetwork PSP": {
- pod: createPodWithHostNetwork(true),
- psps: []*policy.PodSecurityPolicy{noHostNetwork},
- shouldPassAdmit: false,
- shouldPassValidate: false,
- },
- "pod with hostnetwork request allowed by hostNetwork PSP": {
- pod: createPodWithHostNetwork(true),
- psps: []*policy.PodSecurityPolicy{noHostNetwork, hostNetwork},
- shouldPassAdmit: true,
- shouldPassValidate: true,
- expectedHostNetwork: true,
- expectedPSP: hostNetwork.Name,
- },
- }
- for k, v := range tests {
- testPSPAdmit(k, v.psps, v.pod, v.shouldPassAdmit, v.shouldPassValidate, v.expectedPSP, t)
- if v.shouldPassAdmit {
- if v.pod.Spec.SecurityContext.HostNetwork != v.expectedHostNetwork {
- t.Errorf("%s expected hostNetwork to be %t", k, v.expectedHostNetwork)
- }
- }
- }
- // test again with init containers
- for k, v := range tests {
- useInitContainers(v.pod)
- testPSPAdmit(k, v.psps, v.pod, v.shouldPassAdmit, v.shouldPassValidate, v.expectedPSP, t)
- if v.shouldPassAdmit {
- if v.pod.Spec.SecurityContext.HostNetwork != v.expectedHostNetwork {
- t.Errorf("%s expected hostNetwork to be %t", k, v.expectedHostNetwork)
- }
- }
- }
- }
- func TestAdmitHostPorts(t *testing.T) {
- createPodWithHostPorts := func(port int32) *kapi.Pod {
- pod := goodPod()
- pod.Spec.Containers[0].Ports = []kapi.ContainerPort{
- {HostPort: port},
- }
- return pod
- }
- noHostPorts := restrictivePSP()
- noHostPorts.Name = "noHostPorts"
- hostPorts := restrictivePSP()
- hostPorts.Name = "hostPorts"
- hostPorts.Spec.HostPorts = []policy.HostPortRange{
- {Min: 1, Max: 10},
- }
- tests := map[string]struct {
- pod *kapi.Pod
- psps []*policy.PodSecurityPolicy
- shouldPassAdmit bool
- shouldPassValidate bool
- expectedPSP string
- }{
- "host port out of range": {
- pod: createPodWithHostPorts(11),
- psps: []*policy.PodSecurityPolicy{hostPorts},
- shouldPassAdmit: false,
- shouldPassValidate: false,
- },
- "host port in range": {
- pod: createPodWithHostPorts(5),
- psps: []*policy.PodSecurityPolicy{hostPorts},
- shouldPassAdmit: true,
- shouldPassValidate: true,
- expectedPSP: hostPorts.Name,
- },
- "no host ports with range": {
- pod: goodPod(),
- psps: []*policy.PodSecurityPolicy{hostPorts},
- shouldPassAdmit: true,
- shouldPassValidate: true,
- expectedPSP: hostPorts.Name,
- },
- "no host ports without range": {
- pod: goodPod(),
- psps: []*policy.PodSecurityPolicy{noHostPorts},
- shouldPassAdmit: true,
- shouldPassValidate: true,
- expectedPSP: noHostPorts.Name,
- },
- "host ports without range": {
- pod: createPodWithHostPorts(5),
- psps: []*policy.PodSecurityPolicy{noHostPorts},
- shouldPassAdmit: false,
- shouldPassValidate: false,
- },
- }
- for i := 0; i < 2; i++ {
- for k, v := range tests {
- v.pod.Spec.Containers, v.pod.Spec.InitContainers = v.pod.Spec.InitContainers, v.pod.Spec.Containers
- testPSPAdmit(k, v.psps, v.pod, v.shouldPassAdmit, v.shouldPassValidate, v.expectedPSP, t)
- }
- }
- }
- func TestAdmitHostPID(t *testing.T) {
- createPodWithHostPID := func(hostPID bool) *kapi.Pod {
- pod := goodPod()
- pod.Spec.SecurityContext.HostPID = hostPID
- return pod
- }
- noHostPID := restrictivePSP()
- noHostPID.Name = "no-hostpid"
- noHostPID.Spec.HostPID = false
- hostPID := restrictivePSP()
- hostPID.Name = "hostpid"
- hostPID.Spec.HostPID = true
- tests := map[string]struct {
- pod *kapi.Pod
- psps []*policy.PodSecurityPolicy
- shouldPassAdmit bool
- shouldPassValidate bool
- expectedHostPID bool
- expectedPSP string
- }{
- "pod without hostpid request allowed under noHostPID PSP": {
- pod: goodPod(),
- psps: []*policy.PodSecurityPolicy{noHostPID},
- shouldPassAdmit: true,
- shouldPassValidate: true,
- expectedHostPID: false,
- expectedPSP: noHostPID.Name,
- },
- "pod without hostpid request allowed under hostPID PSP": {
- pod: goodPod(),
- psps: []*policy.PodSecurityPolicy{hostPID},
- shouldPassAdmit: true,
- shouldPassValidate: true,
- expectedHostPID: false,
- expectedPSP: hostPID.Name,
- },
- "pod with hostpid request denied by noHostPID PSP": {
- pod: createPodWithHostPID(true),
- psps: []*policy.PodSecurityPolicy{noHostPID},
- shouldPassAdmit: false,
- },
- "pod with hostpid request allowed by hostPID PSP": {
- pod: createPodWithHostPID(true),
- psps: []*policy.PodSecurityPolicy{noHostPID, hostPID},
- shouldPassAdmit: true,
- shouldPassValidate: true,
- expectedHostPID: true,
- expectedPSP: hostPID.Name,
- },
- }
- for k, v := range tests {
- testPSPAdmit(k, v.psps, v.pod, v.shouldPassAdmit, v.shouldPassValidate, v.expectedPSP, t)
- if v.shouldPassAdmit {
- if v.pod.Spec.SecurityContext.HostPID != v.expectedHostPID {
- t.Errorf("%s expected hostPID to be %t", k, v.expectedHostPID)
- }
- }
- }
- }
- func TestAdmitHostIPC(t *testing.T) {
- createPodWithHostIPC := func(hostIPC bool) *kapi.Pod {
- pod := goodPod()
- pod.Spec.SecurityContext.HostIPC = hostIPC
- return pod
- }
- noHostIPC := restrictivePSP()
- noHostIPC.Name = "no-hostIPC"
- noHostIPC.Spec.HostIPC = false
- hostIPC := restrictivePSP()
- hostIPC.Name = "hostIPC"
- hostIPC.Spec.HostIPC = true
- tests := map[string]struct {
- pod *kapi.Pod
- psps []*policy.PodSecurityPolicy
- shouldPassAdmit bool
- shouldPassValidate bool
- expectedHostIPC bool
- expectedPSP string
- }{
- "pod without hostIPC request allowed under noHostIPC PSP": {
- pod: goodPod(),
- psps: []*policy.PodSecurityPolicy{noHostIPC},
- shouldPassAdmit: true,
- shouldPassValidate: true,
- expectedHostIPC: false,
- expectedPSP: noHostIPC.Name,
- },
- "pod without hostIPC request allowed under hostIPC PSP": {
- pod: goodPod(),
- psps: []*policy.PodSecurityPolicy{hostIPC},
- shouldPassAdmit: true,
- shouldPassValidate: true,
- expectedHostIPC: false,
- expectedPSP: hostIPC.Name,
- },
- "pod with hostIPC request denied by noHostIPC PSP": {
- pod: createPodWithHostIPC(true),
- psps: []*policy.PodSecurityPolicy{noHostIPC},
- shouldPassAdmit: false,
- shouldPassValidate: false,
- },
- "pod with hostIPC request allowed by hostIPC PSP": {
- pod: createPodWithHostIPC(true),
- psps: []*policy.PodSecurityPolicy{noHostIPC, hostIPC},
- shouldPassAdmit: true,
- shouldPassValidate: true,
- expectedHostIPC: true,
- expectedPSP: hostIPC.Name,
- },
- }
- for k, v := range tests {
- testPSPAdmit(k, v.psps, v.pod, v.shouldPassAdmit, v.shouldPassValidate, v.expectedPSP, t)
- if v.shouldPassAdmit {
- if v.pod.Spec.SecurityContext.HostIPC != v.expectedHostIPC {
- t.Errorf("%s expected hostIPC to be %t", k, v.expectedHostIPC)
- }
- }
- }
- }
- func createPodWithSecurityContexts(podSC *kapi.PodSecurityContext, containerSC *kapi.SecurityContext) *kapi.Pod {
- pod := goodPod()
- pod.Spec.SecurityContext = podSC
- pod.Spec.Containers[0].SecurityContext = containerSC
- return pod
- }
- func TestAdmitSELinux(t *testing.T) {
- runAsAny := permissivePSP()
- runAsAny.Name = "runAsAny"
- runAsAny.Spec.SELinux.Rule = policy.SELinuxStrategyRunAsAny
- runAsAny.Spec.SELinux.SELinuxOptions = nil
- mustRunAs := permissivePSP()
- mustRunAs.Name = "mustRunAs"
- mustRunAs.Spec.SELinux.Rule = policy.SELinuxStrategyMustRunAs
- mustRunAs.Spec.SELinux.SELinuxOptions = &v1.SELinuxOptions{}
- mustRunAs.Spec.SELinux.SELinuxOptions.Level = "level"
- mustRunAs.Spec.SELinux.SELinuxOptions.Role = "role"
- mustRunAs.Spec.SELinux.SELinuxOptions.Type = "type"
- mustRunAs.Spec.SELinux.SELinuxOptions.User = "user"
- getInternalSEOptions := func(policy *policy.PodSecurityPolicy) *kapi.SELinuxOptions {
- opt := kapi.SELinuxOptions{}
- k8s_api_v1.Convert_v1_SELinuxOptions_To_core_SELinuxOptions(policy.Spec.SELinux.SELinuxOptions, &opt, nil)
- return &opt
- }
- tests := map[string]struct {
- pod *kapi.Pod
- psps []*policy.PodSecurityPolicy
- shouldPassAdmit bool
- shouldPassValidate bool
- expectedPodSC *kapi.PodSecurityContext
- expectedContainerSC *kapi.SecurityContext
- expectedPSP string
- }{
- "runAsAny with no request": {
- pod: createPodWithSecurityContexts(nil, nil),
- psps: []*policy.PodSecurityPolicy{runAsAny},
- shouldPassAdmit: true,
- shouldPassValidate: true,
- expectedPodSC: nil,
- expectedContainerSC: nil,
- expectedPSP: runAsAny.Name,
- },
- "runAsAny with empty pod request": {
- pod: createPodWithSecurityContexts(&kapi.PodSecurityContext{}, nil),
- psps: []*policy.PodSecurityPolicy{runAsAny},
- shouldPassAdmit: true,
- shouldPassValidate: true,
- expectedPodSC: &kapi.PodSecurityContext{},
- expectedContainerSC: nil,
- expectedPSP: runAsAny.Name,
- },
- "runAsAny with empty container request": {
- pod: createPodWithSecurityContexts(nil, &kapi.SecurityContext{}),
- psps: []*policy.PodSecurityPolicy{runAsAny},
- shouldPassAdmit: true,
- shouldPassValidate: true,
- expectedPodSC: nil,
- expectedContainerSC: &kapi.SecurityContext{},
- expectedPSP: runAsAny.Name,
- },
- "runAsAny with pod request": {
- pod: createPodWithSecurityContexts(&kapi.PodSecurityContext{SELinuxOptions: &kapi.SELinuxOptions{User: "foo"}}, nil),
- psps: []*policy.PodSecurityPolicy{runAsAny},
- shouldPassAdmit: true,
- shouldPassValidate: true,
- expectedPodSC: &kapi.PodSecurityContext{SELinuxOptions: &kapi.SELinuxOptions{User: "foo"}},
- expectedContainerSC: nil,
- expectedPSP: runAsAny.Name,
- },
- "runAsAny with container request": {
- pod: createPodWithSecurityContexts(nil, &kapi.SecurityContext{SELinuxOptions: &kapi.SELinuxOptions{User: "foo"}}),
- psps: []*policy.PodSecurityPolicy{runAsAny},
- shouldPassAdmit: true,
- shouldPassValidate: true,
- expectedPodSC: nil,
- expectedContainerSC: &kapi.SecurityContext{SELinuxOptions: &kapi.SELinuxOptions{User: "foo"}},
- expectedPSP: runAsAny.Name,
- },
- "runAsAny with pod and container request": {
- pod: createPodWithSecurityContexts(&kapi.PodSecurityContext{SELinuxOptions: &kapi.SELinuxOptions{User: "bar"}}, &kapi.SecurityContext{SELinuxOptions: &kapi.SELinuxOptions{User: "foo"}}),
- psps: []*policy.PodSecurityPolicy{runAsAny},
- shouldPassAdmit: true,
- shouldPassValidate: true,
- expectedPodSC: &kapi.PodSecurityContext{SELinuxOptions: &kapi.SELinuxOptions{User: "bar"}},
- expectedContainerSC: &kapi.SecurityContext{SELinuxOptions: &kapi.SELinuxOptions{User: "foo"}},
- expectedPSP: runAsAny.Name,
- },
- "mustRunAs with bad pod request": {
- pod: createPodWithSecurityContexts(&kapi.PodSecurityContext{SELinuxOptions: &kapi.SELinuxOptions{User: "foo"}}, nil),
- psps: []*policy.PodSecurityPolicy{mustRunAs},
- shouldPassAdmit: false,
- shouldPassValidate: false,
- },
- "mustRunAs with bad container request": {
- pod: createPodWithSecurityContexts(nil, &kapi.SecurityContext{SELinuxOptions: &kapi.SELinuxOptions{User: "foo"}}),
- psps: []*policy.PodSecurityPolicy{mustRunAs},
- shouldPassAdmit: false,
- shouldPassValidate: false,
- },
- "mustRunAs with no request": {
- pod: createPodWithSecurityContexts(nil, nil),
- psps: []*policy.PodSecurityPolicy{mustRunAs},
- shouldPassAdmit: true,
- shouldPassValidate: true,
- expectedPodSC: &kapi.PodSecurityContext{SELinuxOptions: getInternalSEOptions(mustRunAs)},
- expectedContainerSC: nil,
- expectedPSP: mustRunAs.Name,
- },
- "mustRunAs with good pod request": {
- pod: createPodWithSecurityContexts(
- &kapi.PodSecurityContext{SELinuxOptions: &kapi.SELinuxOptions{Level: "level", Role: "role", Type: "type", User: "user"}},
- nil,
- ),
- psps: []*policy.PodSecurityPolicy{mustRunAs},
- shouldPassAdmit: true,
- shouldPassValidate: true,
- expectedPodSC: &kapi.PodSecurityContext{SELinuxOptions: getInternalSEOptions(mustRunAs)},
- expectedContainerSC: nil,
- expectedPSP: mustRunAs.Name,
- },
- "mustRunAs with good container request": {
- pod: createPodWithSecurityContexts(
- &kapi.PodSecurityContext{SELinuxOptions: &kapi.SELinuxOptions{Level: "level", Role: "role", Type: "type", User: "user"}},
- nil,
- ),
- psps: []*policy.PodSecurityPolicy{mustRunAs},
- shouldPassAdmit: true,
- shouldPassValidate: true,
- expectedPodSC: &kapi.PodSecurityContext{SELinuxOptions: getInternalSEOptions(mustRunAs)},
- expectedContainerSC: nil,
- expectedPSP: mustRunAs.Name,
- },
- }
- for k, v := range tests {
- testPSPAdmit(k, v.psps, v.pod, v.shouldPassAdmit, v.shouldPassValidate, v.expectedPSP, t)
- if v.shouldPassAdmit {
- if !reflect.DeepEqual(v.expectedPodSC, v.pod.Spec.SecurityContext) {
- t.Errorf("%s unexpected diff:\n%s", k, diff.ObjectGoPrintSideBySide(v.expectedPodSC, v.pod.Spec.SecurityContext))
- }
- if !reflect.DeepEqual(v.expectedContainerSC, v.pod.Spec.Containers[0].SecurityContext) {
- t.Errorf("%s unexpected diff:\n%s", k, diff.ObjectGoPrintSideBySide(v.expectedContainerSC, v.pod.Spec.Containers[0].SecurityContext))
- }
- }
- }
- }
- func TestAdmitAppArmor(t *testing.T) {
- createPodWithAppArmor := func(profile string) *kapi.Pod {
- pod := goodPod()
- apparmor.SetProfileNameFromPodAnnotations(pod.Annotations, defaultContainerName, profile)
- return pod
- }
- unconstrainedPSP := restrictivePSP()
- defaultedPSP := restrictivePSP()
- defaultedPSP.Annotations = map[string]string{
- apparmor.DefaultProfileAnnotationKey: apparmor.ProfileRuntimeDefault,
- }
- appArmorPSP := restrictivePSP()
- appArmorPSP.Annotations = map[string]string{
- apparmor.AllowedProfilesAnnotationKey: apparmor.ProfileRuntimeDefault,
- }
- appArmorDefaultPSP := restrictivePSP()
- appArmorDefaultPSP.Annotations = map[string]string{
- apparmor.DefaultProfileAnnotationKey: apparmor.ProfileRuntimeDefault,
- apparmor.AllowedProfilesAnnotationKey: apparmor.ProfileRuntimeDefault + "," + apparmor.ProfileNamePrefix + "foo",
- }
- tests := map[string]struct {
- pod *kapi.Pod
- psp *policy.PodSecurityPolicy
- shouldPassAdmit bool
- shouldPassValidate bool
- expectedProfile string
- }{
- "unconstrained with no profile": {
- pod: goodPod(),
- psp: unconstrainedPSP,
- shouldPassAdmit: true,
- shouldPassValidate: true,
- expectedProfile: "",
- },
- "unconstrained with profile": {
- pod: createPodWithAppArmor(apparmor.ProfileRuntimeDefault),
- psp: unconstrainedPSP,
- shouldPassAdmit: true,
- shouldPassValidate: true,
- expectedProfile: apparmor.ProfileRuntimeDefault,
- },
- "unconstrained with default profile": {
- pod: goodPod(),
- psp: defaultedPSP,
- shouldPassAdmit: true,
- shouldPassValidate: true,
- expectedProfile: apparmor.ProfileRuntimeDefault,
- },
- "AppArmor enforced with no profile": {
- pod: goodPod(),
- psp: appArmorPSP,
- shouldPassAdmit: false,
- shouldPassValidate: false,
- },
- "AppArmor enforced with default profile": {
- pod: goodPod(),
- psp: appArmorDefaultPSP,
- shouldPassAdmit: true,
- shouldPassValidate: true,
- expectedProfile: apparmor.ProfileRuntimeDefault,
- },
- "AppArmor enforced with good profile": {
- pod: createPodWithAppArmor(apparmor.ProfileNamePrefix + "foo"),
- psp: appArmorDefaultPSP,
- shouldPassAdmit: true,
- shouldPassValidate: true,
- expectedProfile: apparmor.ProfileNamePrefix + "foo",
- },
- "AppArmor enforced with local profile": {
- pod: createPodWithAppArmor(apparmor.ProfileNamePrefix + "bar"),
- psp: appArmorPSP,
- shouldPassAdmit: false,
- shouldPassValidate: false,
- },
- }
- for k, v := range tests {
- testPSPAdmit(k, []*policy.PodSecurityPolicy{v.psp}, v.pod, v.shouldPassAdmit, v.shouldPassValidate, v.psp.Name, t)
- if v.shouldPassAdmit {
- assert.Equal(t, v.expectedProfile, apparmor.GetProfileNameFromPodAnnotations(v.pod.Annotations, defaultContainerName), k)
- }
- }
- }
- func TestAdmitRunAsUser(t *testing.T) {
- podSC := func(user *int64) *kapi.PodSecurityContext {
- return &kapi.PodSecurityContext{RunAsUser: user}
- }
- containerSC := func(user *int64) *kapi.SecurityContext {
- return &kapi.SecurityContext{RunAsUser: user}
- }
- runAsAny := permissivePSP()
- runAsAny.Name = "runAsAny"
- runAsAny.Spec.RunAsUser.Rule = policy.RunAsUserStrategyRunAsAny
- mustRunAs := permissivePSP()
- mustRunAs.Name = "mustRunAs"
- mustRunAs.Spec.RunAsUser.Rule = policy.RunAsUserStrategyMustRunAs
- mustRunAs.Spec.RunAsUser.Ranges = []policy.IDRange{
- {Min: int64(999), Max: int64(1000)},
- }
- runAsNonRoot := permissivePSP()
- runAsNonRoot.Name = "runAsNonRoot"
- runAsNonRoot.Spec.RunAsUser.Rule = policy.RunAsUserStrategyMustRunAsNonRoot
- trueValue := true
- tests := map[string]struct {
- pod *kapi.Pod
- psps []*policy.PodSecurityPolicy
- shouldPassAdmit bool
- shouldPassValidate bool
- expectedPodSC *kapi.PodSecurityContext
- expectedContainerSC *kapi.SecurityContext
- expectedPSP string
- }{
- "runAsAny no pod request": {
- pod: createPodWithSecurityContexts(nil, nil),
- psps: []*policy.PodSecurityPolicy{runAsAny},
- shouldPassAdmit: true,
- shouldPassValidate: true,
- expectedPodSC: nil,
- expectedContainerSC: nil,
- expectedPSP: runAsAny.Name,
- },
- "runAsAny pod request": {
- pod: createPodWithSecurityContexts(podSC(utilpointer.Int64Ptr(1)), nil),
- psps: []*policy.PodSecurityPolicy{runAsAny},
- shouldPassAdmit: true,
- shouldPassValidate: true,
- expectedPodSC: podSC(utilpointer.Int64Ptr(1)),
- expectedContainerSC: nil,
- expectedPSP: runAsAny.Name,
- },
- "runAsAny container request": {
- pod: createPodWithSecurityContexts(nil, containerSC(utilpointer.Int64Ptr(1))),
- psps: []*policy.PodSecurityPolicy{runAsAny},
- shouldPassAdmit: true,
- shouldPassValidate: true,
- expectedPodSC: nil,
- expectedContainerSC: containerSC(utilpointer.Int64Ptr(1)),
- expectedPSP: runAsAny.Name,
- },
- "mustRunAs pod request out of range": {
- pod: createPodWithSecurityContexts(podSC(utilpointer.Int64Ptr(1)), nil),
- psps: []*policy.PodSecurityPolicy{mustRunAs},
- shouldPassAdmit: false,
- shouldPassValidate: false,
- },
- "mustRunAs container request out of range": {
- pod: createPodWithSecurityContexts(podSC(utilpointer.Int64Ptr(999)), containerSC(utilpointer.Int64Ptr(1))),
- psps: []*policy.PodSecurityPolicy{mustRunAs},
- shouldPassAdmit: false,
- shouldPassValidate: false,
- },
- "mustRunAs pod request in range": {
- pod: createPodWithSecurityContexts(podSC(utilpointer.Int64Ptr(999)), nil),
- psps: []*policy.PodSecurityPolicy{mustRunAs},
- shouldPassAdmit: true,
- shouldPassValidate: true,
- expectedPodSC: podSC(&mustRunAs.Spec.RunAsUser.Ranges[0].Min),
- expectedContainerSC: nil,
- expectedPSP: mustRunAs.Name,
- },
- "mustRunAs container request in range": {
- pod: createPodWithSecurityContexts(nil, containerSC(utilpointer.Int64Ptr(999))),
- psps: []*policy.PodSecurityPolicy{mustRunAs},
- shouldPassAdmit: true,
- shouldPassValidate: true,
- expectedPodSC: nil,
- expectedContainerSC: containerSC(&mustRunAs.Spec.RunAsUser.Ranges[0].Min),
- expectedPSP: mustRunAs.Name,
- },
- "mustRunAs pod and container request in range": {
- pod: createPodWithSecurityContexts(podSC(utilpointer.Int64Ptr(999)), containerSC(utilpointer.Int64Ptr(1000))),
- psps: []*policy.PodSecurityPolicy{mustRunAs},
- shouldPassAdmit: true,
- shouldPassValidate: true,
- expectedPodSC: podSC(utilpointer.Int64Ptr(999)),
- expectedContainerSC: containerSC(utilpointer.Int64Ptr(1000)),
- expectedPSP: mustRunAs.Name,
- },
- "mustRunAs no request": {
- pod: createPodWithSecurityContexts(nil, nil),
- psps: []*policy.PodSecurityPolicy{mustRunAs},
- shouldPassAdmit: true,
- shouldPassValidate: true,
- expectedPodSC: nil,
- expectedContainerSC: containerSC(&mustRunAs.Spec.RunAsUser.Ranges[0].Min),
- expectedPSP: mustRunAs.Name,
- },
- "runAsNonRoot no request": {
- pod: createPodWithSecurityContexts(nil, nil),
- psps: []*policy.PodSecurityPolicy{runAsNonRoot},
- shouldPassAdmit: true,
- shouldPassValidate: true,
- expectedPodSC: nil,
- expectedContainerSC: &kapi.SecurityContext{RunAsNonRoot: &trueValue},
- expectedPSP: runAsNonRoot.Name,
- },
- "runAsNonRoot pod request root": {
- pod: createPodWithSecurityContexts(podSC(utilpointer.Int64Ptr(0)), nil),
- psps: []*policy.PodSecurityPolicy{runAsNonRoot},
- shouldPassAdmit: false,
- shouldPassValidate: false,
- },
- "runAsNonRoot pod request non-root": {
- pod: createPodWithSecurityContexts(podSC(utilpointer.Int64Ptr(1)), nil),
- psps: []*policy.PodSecurityPolicy{runAsNonRoot},
- shouldPassAdmit: true,
- shouldPassValidate: true,
- expectedPodSC: podSC(utilpointer.Int64Ptr(1)),
- expectedPSP: runAsNonRoot.Name,
- },
- "runAsNonRoot container request root": {
- pod: createPodWithSecurityContexts(podSC(utilpointer.Int64Ptr(1)), containerSC(utilpointer.Int64Ptr(0))),
- psps: []*policy.PodSecurityPolicy{runAsNonRoot},
- shouldPassAdmit: false,
- shouldPassValidate: false,
- },
- "runAsNonRoot container request non-root": {
- pod: createPodWithSecurityContexts(podSC(utilpointer.Int64Ptr(1)), containerSC(utilpointer.Int64Ptr(2))),
- psps: []*policy.PodSecurityPolicy{runAsNonRoot},
- shouldPassAdmit: true,
- shouldPassValidate: true,
- expectedPodSC: podSC(utilpointer.Int64Ptr(1)),
- expectedContainerSC: containerSC(utilpointer.Int64Ptr(2)),
- expectedPSP: runAsNonRoot.Name,
- },
- }
- for k, v := range tests {
- testPSPAdmit(k, v.psps, v.pod, v.shouldPassAdmit, v.shouldPassValidate, v.expectedPSP, t)
- if v.shouldPassAdmit {
- if !reflect.DeepEqual(v.expectedPodSC, v.pod.Spec.SecurityContext) {
- t.Errorf("%s unexpected pod sc diff:\n%s", k, diff.ObjectGoPrintSideBySide(v.expectedPodSC, v.pod.Spec.SecurityContext))
- }
- if !reflect.DeepEqual(v.expectedContainerSC, v.pod.Spec.Containers[0].SecurityContext) {
- t.Errorf("%s unexpected container sc diff:\n%s", k, diff.ObjectGoPrintSideBySide(v.expectedContainerSC, v.pod.Spec.Containers[0].SecurityContext))
- }
- }
- }
- }
- func TestAdmitSupplementalGroups(t *testing.T) {
- podSC := func(group int64) *kapi.PodSecurityContext {
- return &kapi.PodSecurityContext{SupplementalGroups: []int64{group}}
- }
- runAsAny := permissivePSP()
- runAsAny.Name = "runAsAny"
- runAsAny.Spec.SupplementalGroups.Rule = policy.SupplementalGroupsStrategyRunAsAny
- mustRunAs := permissivePSP()
- mustRunAs.Name = "mustRunAs"
- mustRunAs.Spec.SupplementalGroups.Rule = policy.SupplementalGroupsStrategyMustRunAs
- mustRunAs.Spec.SupplementalGroups.Ranges = []policy.IDRange{{Min: int64(999), Max: int64(1000)}}
- tests := map[string]struct {
- pod *kapi.Pod
- psps []*policy.PodSecurityPolicy
- shouldPassAdmit bool
- shouldPassValidate bool
- expectedPodSC *kapi.PodSecurityContext
- expectedPSP string
- }{
- "runAsAny no pod request": {
- pod: createPodWithSecurityContexts(nil, nil),
- psps: []*policy.PodSecurityPolicy{runAsAny},
- shouldPassAdmit: true,
- shouldPassValidate: true,
- expectedPodSC: nil,
- expectedPSP: runAsAny.Name,
- },
- "runAsAny empty pod request": {
- pod: createPodWithSecurityContexts(&kapi.PodSecurityContext{}, nil),
- psps: []*policy.PodSecurityPolicy{runAsAny},
- shouldPassAdmit: true,
- shouldPassValidate: true,
- expectedPodSC: &kapi.PodSecurityContext{},
- expectedPSP: runAsAny.Name,
- },
- "runAsAny empty pod request empty supplemental groups": {
- pod: createPodWithSecurityContexts(&kapi.PodSecurityContext{SupplementalGroups: []int64{}}, nil),
- psps: []*policy.PodSecurityPolicy{runAsAny},
- shouldPassAdmit: true,
- shouldPassValidate: true,
- expectedPodSC: &kapi.PodSecurityContext{SupplementalGroups: []int64{}},
- expectedPSP: runAsAny.Name,
- },
- "runAsAny pod request": {
- pod: createPodWithSecurityContexts(podSC(1), nil),
- psps: []*policy.PodSecurityPolicy{runAsAny},
- shouldPassAdmit: true,
- shouldPassValidate: true,
- expectedPodSC: &kapi.PodSecurityContext{SupplementalGroups: []int64{1}},
- expectedPSP: runAsAny.Name,
- },
- "mustRunAs no pod request": {
- pod: createPodWithSecurityContexts(nil, nil),
- psps: []*policy.PodSecurityPolicy{mustRunAs},
- shouldPassAdmit: true,
- shouldPassValidate: true,
- expectedPodSC: podSC(mustRunAs.Spec.SupplementalGroups.Ranges[0].Min),
- expectedPSP: mustRunAs.Name,
- },
- "mustRunAs bad pod request": {
- pod: createPodWithSecurityContexts(podSC(1), nil),
- psps: []*policy.PodSecurityPolicy{mustRunAs},
- shouldPassAdmit: false,
- shouldPassValidate: false,
- },
- "mustRunAs good pod request": {
- pod: createPodWithSecurityContexts(podSC(999), nil),
- psps: []*policy.PodSecurityPolicy{mustRunAs},
- shouldPassAdmit: true,
- shouldPassValidate: true,
- expectedPodSC: podSC(999),
- expectedPSP: mustRunAs.Name,
- },
- }
- for k, v := range tests {
- testPSPAdmit(k, v.psps, v.pod, v.shouldPassAdmit, v.shouldPassValidate, v.expectedPSP, t)
- if v.shouldPassAdmit {
- if !reflect.DeepEqual(v.expectedPodSC, v.pod.Spec.SecurityContext) {
- t.Errorf("%s unexpected pod sc diff:\n%s", k, diff.ObjectGoPrintSideBySide(v.expectedPodSC, v.pod.Spec.SecurityContext))
- }
- }
- }
- }
- func TestAdmitFSGroup(t *testing.T) {
- createPodWithFSGroup := func(group int64) *kapi.Pod {
- pod := goodPod()
- // doesn't matter if we set it here or on the container, the
- // admission controller uses DetermineEffectiveSC to get the defaulting
- // behavior so it can validate what will be applied at runtime
- pod.Spec.SecurityContext.FSGroup = utilpointer.Int64Ptr(group)
- return pod
- }
- runAsAny := restrictivePSP()
- runAsAny.Name = "runAsAny"
- runAsAny.Spec.FSGroup.Rule = policy.FSGroupStrategyRunAsAny
- mustRunAs := restrictivePSP()
- mustRunAs.Name = "mustRunAs"
- tests := map[string]struct {
- pod *kapi.Pod
- psps []*policy.PodSecurityPolicy
- shouldPassAdmit bool
- shouldPassValidate bool
- expectedFSGroup *int64
- expectedPSP string
- }{
- "runAsAny no pod request": {
- pod: goodPod(),
- psps: []*policy.PodSecurityPolicy{runAsAny},
- shouldPassAdmit: true,
- shouldPassValidate: true,
- expectedFSGroup: nil,
- expectedPSP: runAsAny.Name,
- },
- "runAsAny pod request": {
- pod: createPodWithFSGroup(1),
- psps: []*policy.PodSecurityPolicy{runAsAny},
- shouldPassAdmit: true,
- shouldPassValidate: true,
- expectedFSGroup: utilpointer.Int64Ptr(1),
- expectedPSP: runAsAny.Name,
- },
- "mustRunAs no pod request": {
- pod: goodPod(),
- psps: []*policy.PodSecurityPolicy{mustRunAs},
- shouldPassAdmit: true,
- shouldPassValidate: true,
- expectedFSGroup: &mustRunAs.Spec.SupplementalGroups.Ranges[0].Min,
- expectedPSP: mustRunAs.Name,
- },
- "mustRunAs bad pod request": {
- pod: createPodWithFSGroup(1),
- psps: []*policy.PodSecurityPolicy{mustRunAs},
- shouldPassAdmit: false,
- shouldPassValidate: false,
- },
- "mustRunAs good pod request": {
- pod: createPodWithFSGroup(999),
- psps: []*policy.PodSecurityPolicy{mustRunAs},
- shouldPassAdmit: true,
- shouldPassValidate: true,
- expectedFSGroup: utilpointer.Int64Ptr(999),
- expectedPSP: mustRunAs.Name,
- },
- }
- for k, v := range tests {
- testPSPAdmit(k, v.psps, v.pod, v.shouldPassAdmit, v.shouldPassValidate, v.expectedPSP, t)
- if v.shouldPassAdmit {
- if v.pod.Spec.SecurityContext.FSGroup == nil && v.expectedFSGroup == nil {
- // ok, don't need to worry about identifying specific diffs
- continue
- }
- if v.pod.Spec.SecurityContext.FSGroup == nil && v.expectedFSGroup != nil {
- t.Errorf("%s expected FSGroup to be: %v but found nil", k, *v.expectedFSGroup)
- continue
- }
- if v.pod.Spec.SecurityContext.FSGroup != nil && v.expectedFSGroup == nil {
- t.Errorf("%s expected FSGroup to be nil but found: %v", k, *v.pod.Spec.SecurityContext.FSGroup)
- continue
- }
- if *v.expectedFSGroup != *v.pod.Spec.SecurityContext.FSGroup {
- t.Errorf("%s expected FSGroup to be: %v but found %v", k, *v.expectedFSGroup, *v.pod.Spec.SecurityContext.FSGroup)
- }
- }
- }
- }
- func TestAdmitReadOnlyRootFilesystem(t *testing.T) {
- createPodWithRORFS := func(rorfs bool) *kapi.Pod {
- pod := goodPod()
- pod.Spec.Containers[0].SecurityContext.ReadOnlyRootFilesystem = &rorfs
- return pod
- }
- noRORFS := restrictivePSP()
- noRORFS.Name = "no-rorfs"
- noRORFS.Spec.ReadOnlyRootFilesystem = false
- rorfs := restrictivePSP()
- rorfs.Name = "rorfs"
- rorfs.Spec.ReadOnlyRootFilesystem = true
- tests := map[string]struct {
- pod *kapi.Pod
- psps []*policy.PodSecurityPolicy
- shouldPassAdmit bool
- shouldPassValidate bool
- expectedRORFS bool
- expectedPSP string
- }{
- "no-rorfs allows pod request with rorfs": {
- pod: createPodWithRORFS(true),
- psps: []*policy.PodSecurityPolicy{noRORFS},
- shouldPassAdmit: true,
- shouldPassValidate: true,
- expectedRORFS: true,
- expectedPSP: noRORFS.Name,
- },
- "no-rorfs allows pod request without rorfs": {
- pod: createPodWithRORFS(false),
- psps: []*policy.PodSecurityPolicy{noRORFS},
- shouldPassAdmit: true,
- shouldPassValidate: true,
- expectedRORFS: false,
- expectedPSP: noRORFS.Name,
- },
- "rorfs rejects pod request without rorfs": {
- pod: createPodWithRORFS(false),
- psps: []*policy.PodSecurityPolicy{rorfs},
- shouldPassAdmit: false,
- shouldPassValidate: false,
- },
- "rorfs defaults nil pod request": {
- pod: goodPod(),
- psps: []*policy.PodSecurityPolicy{rorfs},
- shouldPassAdmit: true,
- shouldPassValidate: true,
- expectedRORFS: true,
- expectedPSP: rorfs.Name,
- },
- "rorfs accepts pod request with rorfs": {
- pod: createPodWithRORFS(true),
- psps: []*policy.PodSecurityPolicy{rorfs},
- shouldPassAdmit: true,
- shouldPassValidate: true,
- expectedRORFS: true,
- expectedPSP: rorfs.Name,
- },
- }
- for k, v := range tests {
- testPSPAdmit(k, v.psps, v.pod, v.shouldPassAdmit, v.shouldPassValidate, v.expectedPSP, t)
- if v.shouldPassAdmit {
- if v.pod.Spec.Containers[0].SecurityContext.ReadOnlyRootFilesystem == nil ||
- *v.pod.Spec.Containers[0].SecurityContext.ReadOnlyRootFilesystem != v.expectedRORFS {
- t.Errorf("%s expected ReadOnlyRootFilesystem to be %t but found %#v", k, v.expectedRORFS, v.pod.Spec.Containers[0].SecurityContext.ReadOnlyRootFilesystem)
- }
- }
- }
- }
- func TestAdmitSysctls(t *testing.T) {
- podWithSysctls := func(safeSysctls []string, unsafeSysctls []string) *kapi.Pod {
- pod := goodPod()
- dummySysctls := func(names []string) []kapi.Sysctl {
- sysctls := make([]kapi.Sysctl, len(names))
- for i, n := range names {
- sysctls[i].Name = n
- sysctls[i].Value = "dummy"
- }
- return sysctls
- }
- pod.Spec.SecurityContext = &kapi.PodSecurityContext{
- Sysctls: dummySysctls(append(safeSysctls, unsafeSysctls...)),
- }
- return pod
- }
- safeSysctls := restrictivePSP()
- safeSysctls.Name = "no sysctls"
- noSysctls := restrictivePSP()
- noSysctls.Name = "empty sysctls"
- noSysctls.Spec.ForbiddenSysctls = []string{"*"}
- mixedSysctls := restrictivePSP()
- mixedSysctls.Name = "wildcard sysctls"
- mixedSysctls.Spec.ForbiddenSysctls = []string{"net.*"}
- mixedSysctls.Spec.AllowedUnsafeSysctls = []string{"a.*", "b.*"}
- aUnsafeSysctl := restrictivePSP()
- aUnsafeSysctl.Name = "a sysctl"
- aUnsafeSysctl.Spec.AllowedUnsafeSysctls = []string{"a"}
- bUnsafeSysctl := restrictivePSP()
- bUnsafeSysctl.Name = "b sysctl"
- bUnsafeSysctl.Spec.AllowedUnsafeSysctls = []string{"b"}
- cUnsafeSysctl := restrictivePSP()
- cUnsafeSysctl.Name = "c sysctl"
- cUnsafeSysctl.Spec.AllowedUnsafeSysctls = []string{"c"}
- catchallSysctls := restrictivePSP()
- catchallSysctls.Name = "catchall sysctl"
- catchallSysctls.Spec.AllowedUnsafeSysctls = []string{"*"}
- tests := map[string]struct {
- pod *kapi.Pod
- psps []*policy.PodSecurityPolicy
- shouldPassAdmit bool
- shouldPassValidate bool
- expectedPSP string
- }{
- "pod without any sysctls request allowed under safeSysctls PSP": {
- pod: goodPod(),
- psps: []*policy.PodSecurityPolicy{safeSysctls},
- shouldPassAdmit: true,
- shouldPassValidate: true,
- expectedPSP: safeSysctls.Name,
- },
- "pod without any sysctls request allowed under noSysctls PSP": {
- pod: goodPod(),
- psps: []*policy.PodSecurityPolicy{noSysctls},
- shouldPassAdmit: true,
- shouldPassValidate: true,
- expectedPSP: noSysctls.Name,
- },
- "pod with safe sysctls request allowed under safeSysctls PSP": {
- pod: podWithSysctls([]string{"kernel.shm_rmid_forced", "net.ipv4.tcp_syncookies"}, []string{}),
- psps: []*policy.PodSecurityPolicy{safeSysctls},
- shouldPassAdmit: true,
- shouldPassValidate: true,
- expectedPSP: safeSysctls.Name,
- },
- "pod with unsafe sysctls request disallowed under noSysctls PSP": {
- pod: podWithSysctls([]string{}, []string{"a", "b"}),
- psps: []*policy.PodSecurityPolicy{noSysctls},
- shouldPassAdmit: false,
- shouldPassValidate: false,
- expectedPSP: noSysctls.Name,
- },
- "pod with unsafe sysctls a, b request disallowed under aUnsafeSysctl SCC": {
- pod: podWithSysctls([]string{}, []string{"a", "b"}),
- psps: []*policy.PodSecurityPolicy{aUnsafeSysctl},
- shouldPassAdmit: false,
- shouldPassValidate: false,
- },
- "pod with unsafe sysctls b request disallowed under aUnsafeSysctl SCC": {
- pod: podWithSysctls([]string{}, []string{"b"}),
- psps: []*policy.PodSecurityPolicy{aUnsafeSysctl},
- shouldPassAdmit: false,
- shouldPassValidate: false,
- },
- "pod with unsafe sysctls a request allowed under aUnsafeSysctl SCC": {
- pod: podWithSysctls([]string{}, []string{"a"}),
- psps: []*policy.PodSecurityPolicy{aUnsafeSysctl},
- shouldPassAdmit: true,
- shouldPassValidate: true,
- expectedPSP: aUnsafeSysctl.Name,
- },
- "pod with safe net sysctl request allowed under aUnsafeSysctl SCC": {
- pod: podWithSysctls([]string{"net.ipv4.ip_local_port_range"}, []string{}),
- psps: []*policy.PodSecurityPolicy{aUnsafeSysctl},
- shouldPassAdmit: true,
- shouldPassValidate: true,
- expectedPSP: aUnsafeSysctl.Name,
- },
- "pod with safe sysctls request disallowed under noSysctls PSP": {
- pod: podWithSysctls([]string{"net.ipv4.ip_local_port_range"}, []string{}),
- psps: []*policy.PodSecurityPolicy{noSysctls},
- shouldPassAdmit: false,
- shouldPassValidate: false,
- },
- "pod with matching sysctls request allowed under mixedSysctls PSP": {
- pod: podWithSysctls([]string{"kernel.shm_rmid_forced"}, []string{"a.b", "b.a"}),
- psps: []*policy.PodSecurityPolicy{mixedSysctls},
- shouldPassAdmit: true,
- shouldPassValidate: true,
- expectedPSP: mixedSysctls.Name,
- },
- "pod with not-matching unsafe sysctls request disallowed under mixedSysctls PSP": {
- pod: podWithSysctls([]string{}, []string{"e"}),
- psps: []*policy.PodSecurityPolicy{mixedSysctls},
- shouldPassAdmit: false,
- shouldPassValidate: false,
- },
- "pod with not-matching safe sysctls request disallowed under mixedSysctls PSP": {
- pod: podWithSysctls([]string{"net.ipv4.ip_local_port_range"}, []string{}),
- psps: []*policy.PodSecurityPolicy{mixedSysctls},
- shouldPassAdmit: false,
- shouldPassValidate: false,
- },
- "pod with sysctls request allowed under catchallSysctls PSP": {
- pod: podWithSysctls([]string{"net.ipv4.ip_local_port_range"}, []string{"f"}),
- psps: []*policy.PodSecurityPolicy{catchallSysctls},
- shouldPassAdmit: true,
- shouldPassValidate: true,
- expectedPSP: catchallSysctls.Name,
- },
- }
- for k, v := range tests {
- origSysctl := v.pod.Spec.SecurityContext.Sysctls
- testPSPAdmit(k, v.psps, v.pod, v.shouldPassAdmit, v.shouldPassValidate, v.expectedPSP, t)
- if v.shouldPassAdmit {
- if !reflect.DeepEqual(v.pod.Spec.SecurityContext.Sysctls, origSysctl) {
- t.Errorf("%s: wrong sysctls: expected=%v, got=%v", k, origSysctl, v.pod.Spec.SecurityContext.Sysctls)
- }
- }
- }
- }
- func testPSPAdmit(testCaseName string, psps []*policy.PodSecurityPolicy, pod *kapi.Pod, shouldPassAdmit, shouldPassValidate bool, expectedPSP string, t *testing.T) {
- testPSPAdmitAdvanced(testCaseName, kadmission.Create, psps, nil, &user.DefaultInfo{}, pod, nil, shouldPassAdmit, shouldPassValidate, true, expectedPSP, t)
- }
- // fakeAttributes decorate kadmission.Attributes. It's used to trace the added annotations.
- type fakeAttributes struct {
- kadmission.Attributes
- annotations map[string]string
- }
- func (f fakeAttributes) AddAnnotation(k, v string) error {
- f.annotations[k] = v
- return f.Attributes.AddAnnotation(k, v)
- }
- func testPSPAdmitAdvanced(testCaseName string, op kadmission.Operation, psps []*policy.PodSecurityPolicy, authz authorizer.Authorizer, userInfo user.Info, pod, oldPod *kapi.Pod, shouldPassAdmit, shouldPassValidate bool, canMutate bool, expectedPSP string, t *testing.T) {
- originalPod := pod.DeepCopy()
- plugin := NewTestAdmission(psps, authz)
- attrs := kadmission.NewAttributesRecord(pod, oldPod, kapi.Kind("Pod").WithVersion("version"), pod.Namespace, "", kapi.Resource("pods").WithVersion("version"), "", op, nil, false, userInfo)
- annotations := make(map[string]string)
- attrs = &fakeAttributes{attrs, annotations}
- err := admissiontesting.WithReinvocationTesting(t, plugin).Admit(context.TODO(), attrs, nil)
- if shouldPassAdmit && err != nil {
- t.Errorf("%s: expected no errors on Admit but received %v", testCaseName, err)
- }
- if shouldPassAdmit && err == nil {
- if pod.Annotations[psputil.ValidatedPSPAnnotation] != expectedPSP {
- t.Errorf("%s: expected to be admitted under %q PSP but found %q", testCaseName, expectedPSP, pod.Annotations[psputil.ValidatedPSPAnnotation])
- }
- if !canMutate {
- podWithoutPSPAnnotation := pod.DeepCopy()
- delete(podWithoutPSPAnnotation.Annotations, psputil.ValidatedPSPAnnotation)
- originalPodWithoutPSPAnnotation := originalPod.DeepCopy()
- delete(originalPodWithoutPSPAnnotation.Annotations, psputil.ValidatedPSPAnnotation)
- if !apiequality.Semantic.DeepEqual(originalPodWithoutPSPAnnotation.Spec, podWithoutPSPAnnotation.Spec) {
- t.Errorf("%s: expected no mutation on Admit, got %s", testCaseName, diff.ObjectGoPrintSideBySide(originalPodWithoutPSPAnnotation.Spec, podWithoutPSPAnnotation.Spec))
- }
- }
- }
- if !shouldPassAdmit && err == nil {
- t.Errorf("%s: expected errors on Admit but received none", testCaseName)
- }
- err = plugin.Validate(context.TODO(), attrs, nil)
- psp := ""
- if shouldPassAdmit && op == kadmission.Create {
- psp = expectedPSP
- }
- validateAuditAnnotation(t, testCaseName, annotations, "podsecuritypolicy.policy.k8s.io/admit-policy", psp)
- if shouldPassValidate && err != nil {
- t.Errorf("%s: expected no errors on Validate but received %v", testCaseName, err)
- } else if !shouldPassValidate && err == nil {
- t.Errorf("%s: expected errors on Validate but received none", testCaseName)
- }
- if shouldPassValidate {
- validateAuditAnnotation(t, testCaseName, annotations, "podsecuritypolicy.policy.k8s.io/validate-policy", expectedPSP)
- } else {
- validateAuditAnnotation(t, testCaseName, annotations, "podsecuritypolicy.policy.k8s.io/validate-policy", "")
- }
- }
- func validateAuditAnnotation(t *testing.T, testCaseName string, annotations map[string]string, key, value string) {
- if annotations[key] != value {
- t.Errorf("%s: expected to have annotations[%s] set to %q, got %q", testCaseName, key, value, annotations[key])
- }
- }
- func TestAssignSecurityContext(t *testing.T) {
- // psp that will deny privileged container requests and has a default value for a field (uid)
- psp := restrictivePSP()
- provider, err := kpsp.NewSimpleProvider(psp, "namespace", kpsp.NewSimpleStrategyFactory())
- if err != nil {
- t.Fatalf("failed to create provider: %v", err)
- }
- createContainer := func(priv bool) kapi.Container {
- return kapi.Container{
- SecurityContext: &kapi.SecurityContext{
- Privileged: &priv,
- },
- }
- }
- testCases := map[string]struct {
- pod *kapi.Pod
- shouldValidate bool
- expectedUID *int64
- }{
- "pod and container SC is not changed when invalid": {
- pod: &kapi.Pod{
- Spec: kapi.PodSpec{
- SecurityContext: &kapi.PodSecurityContext{},
- Containers: []kapi.Container{createContainer(true)},
- },
- },
- shouldValidate: false,
- },
- "must validate all containers": {
- pod: &kapi.Pod{
- Spec: kapi.PodSpec{
- // good container and bad container
- SecurityContext: &kapi.PodSecurityContext{},
- Containers: []kapi.Container{createContainer(false), createContainer(true)},
- },
- },
- shouldValidate: false,
- },
- "pod validates": {
- pod: &kapi.Pod{
- Spec: kapi.PodSpec{
- SecurityContext: &kapi.PodSecurityContext{},
- Containers: []kapi.Container{createContainer(false)},
- },
- },
- shouldValidate: true,
- },
- }
- for k, v := range testCases {
- errs := assignSecurityContext(provider, v.pod)
- if v.shouldValidate && len(errs) > 0 {
- t.Errorf("%s expected to validate but received errors %v", k, errs)
- continue
- }
- if !v.shouldValidate && len(errs) == 0 {
- t.Errorf("%s expected validation errors but received none", k)
- continue
- }
- }
- }
- func TestCreateProvidersFromConstraints(t *testing.T) {
- testCases := map[string]struct {
- // use a generating function so we can test for non-mutation
- psp func() *policy.PodSecurityPolicy
- expectedErr string
- }{
- "valid psp": {
- psp: func() *policy.PodSecurityPolicy {
- return &policy.PodSecurityPolicy{
- ObjectMeta: metav1.ObjectMeta{
- Name: "valid psp",
- },
- 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,
- },
- },
- }
- },
- },
- "bad psp strategy options": {
- psp: func() *policy.PodSecurityPolicy {
- return &policy.PodSecurityPolicy{
- ObjectMeta: metav1.ObjectMeta{
- Name: "bad psp user options",
- },
- Spec: policy.PodSecurityPolicySpec{
- SELinux: policy.SELinuxStrategyOptions{
- Rule: policy.SELinuxStrategyRunAsAny,
- },
- RunAsUser: policy.RunAsUserStrategyOptions{
- Rule: policy.RunAsUserStrategyMustRunAs,
- },
- RunAsGroup: &policy.RunAsGroupStrategyOptions{
- Rule: policy.RunAsGroupStrategyRunAsAny,
- },
- FSGroup: policy.FSGroupStrategyOptions{
- Rule: policy.FSGroupStrategyRunAsAny,
- },
- SupplementalGroups: policy.SupplementalGroupsStrategyOptions{
- Rule: policy.SupplementalGroupsStrategyRunAsAny,
- },
- },
- }
- },
- expectedErr: "MustRunAs requires at least one range",
- },
- }
- for k, v := range testCases {
- admit := &Plugin{
- Handler: kadmission.NewHandler(kadmission.Create, kadmission.Update),
- strategyFactory: kpsp.NewSimpleStrategyFactory(),
- }
- psp := v.psp()
- _, errs := admit.createProvidersFromPolicies([]*policy.PodSecurityPolicy{psp}, "namespace")
- if !reflect.DeepEqual(psp, v.psp()) {
- diff := diff.ObjectDiff(psp, v.psp())
- t.Errorf("%s createProvidersFromPolicies mutated policy. diff:\n%s", k, diff)
- }
- if len(v.expectedErr) > 0 && len(errs) != 1 {
- t.Errorf("%s expected a single error '%s' but received %v", k, v.expectedErr, errs)
- continue
- }
- if len(v.expectedErr) == 0 && len(errs) != 0 {
- t.Errorf("%s did not expect an error but received %v", k, errs)
- continue
- }
- // check that we got the error we expected
- if len(v.expectedErr) > 0 {
- if !strings.Contains(errs[0].Error(), v.expectedErr) {
- t.Errorf("%s expected error '%s' but received %v", k, v.expectedErr, errs[0])
- }
- }
- }
- }
- func TestPolicyAuthorization(t *testing.T) {
- policyWithName := func(name string) *policy.PodSecurityPolicy {
- p := permissivePSP()
- p.Name = name
- return p
- }
- tests := map[string]struct {
- user user.Info
- sa string
- ns string
- expectedPolicy string
- inPolicies []*policy.PodSecurityPolicy
- allowed map[string]map[string]map[string]bool
- allowedGroup string
- }{
- "policy allowed by user (extensions API Group)": {
- user: &user.DefaultInfo{Name: "user"},
- sa: "sa",
- ns: "test",
- allowed: map[string]map[string]map[string]bool{
- "user": {
- "test": {"policy": true},
- },
- },
- inPolicies: []*policy.PodSecurityPolicy{policyWithName("policy")},
- expectedPolicy: "policy",
- },
- "policy allowed by sa (extensions API Group)": {
- user: &user.DefaultInfo{Name: "user"},
- sa: "sa",
- ns: "test",
- allowed: map[string]map[string]map[string]bool{
- serviceaccount.MakeUsername("test", "sa"): {
- "test": {"policy": true},
- },
- },
- inPolicies: []*policy.PodSecurityPolicy{policyWithName("policy")},
- expectedPolicy: "policy",
- },
- "policy allowed by user (policy API Group)": {
- user: &user.DefaultInfo{Name: "user"},
- sa: "sa",
- ns: "test",
- allowed: map[string]map[string]map[string]bool{
- "user": {
- "test": {"policy": true},
- },
- },
- inPolicies: []*policy.PodSecurityPolicy{policyWithName("policy")},
- expectedPolicy: "policy",
- allowedGroup: policy.GroupName,
- },
- "policy allowed by sa (policy API Group)": {
- user: &user.DefaultInfo{Name: "user"},
- sa: "sa",
- ns: "test",
- allowed: map[string]map[string]map[string]bool{
- serviceaccount.MakeUsername("test", "sa"): {
- "test": {"policy": true},
- },
- },
- inPolicies: []*policy.PodSecurityPolicy{policyWithName("policy")},
- expectedPolicy: "policy",
- allowedGroup: policy.GroupName,
- },
- "no policies allowed": {
- user: &user.DefaultInfo{Name: "user"},
- sa: "sa",
- ns: "test",
- allowed: map[string]map[string]map[string]bool{},
- inPolicies: []*policy.PodSecurityPolicy{policyWithName("policy")},
- expectedPolicy: "",
- },
- "multiple policies allowed": {
- user: &user.DefaultInfo{Name: "user"},
- sa: "sa",
- ns: "test",
- allowed: map[string]map[string]map[string]bool{
- serviceaccount.MakeUsername("test", "sa"): {
- "test": {"policy1": true},
- "": {"policy4": true},
- "other": {"policy6": true},
- },
- "user": {
- "test": {"policy2": true},
- "": {"policy5": true},
- "other": {"policy7": true},
- },
- },
- inPolicies: []*policy.PodSecurityPolicy{
- // Prefix to force checking these policies first.
- policyWithName("a_policy1"), // not allowed in this namespace
- policyWithName("a_policy2"), // not allowed in this namespace
- policyWithName("policy2"), // allowed by sa
- policyWithName("policy3"), // allowed by user
- policyWithName("policy4"), // not allowed
- policyWithName("policy5"), // allowed by sa at cluster level
- policyWithName("policy6"), // allowed by user at cluster level
- },
- expectedPolicy: "policy2",
- },
- "policies are not allowed for nil user info": {
- user: nil,
- sa: "sa",
- ns: "test",
- allowed: map[string]map[string]map[string]bool{
- serviceaccount.MakeUsername("test", "sa"): {
- "test": {"policy1": true},
- },
- "user": {
- "test": {"policy2": true},
- },
- },
- inPolicies: []*policy.PodSecurityPolicy{
- policyWithName("policy1"),
- policyWithName("policy2"),
- policyWithName("policy3"),
- },
- // only the policies for the sa are allowed when user info is nil
- expectedPolicy: "policy1",
- },
- "policies are not allowed for nil sa info": {
- user: &user.DefaultInfo{Name: "user"},
- sa: "",
- ns: "test",
- allowed: map[string]map[string]map[string]bool{
- serviceaccount.MakeUsername("test", "sa"): {
- "test": {"policy1": true},
- },
- "user": {
- "test": {"policy2": true},
- },
- },
- inPolicies: []*policy.PodSecurityPolicy{
- policyWithName("policy1"),
- policyWithName("policy2"),
- policyWithName("policy3"),
- },
- // only the policies for the user are allowed when sa info is nil
- expectedPolicy: "policy2",
- },
- "policies are not allowed for nil sa and user info": {
- user: nil,
- sa: "",
- ns: "test",
- allowed: map[string]map[string]map[string]bool{
- serviceaccount.MakeUsername("test", "sa"): {
- "test": {"policy1": true},
- },
- "user": {
- "test": {"policy2": true},
- },
- },
- inPolicies: []*policy.PodSecurityPolicy{
- policyWithName("policy1"),
- policyWithName("policy2"),
- policyWithName("policy3"),
- },
- // no policies are allowed if sa and user are both nil
- expectedPolicy: "",
- },
- }
- for k, v := range tests {
- var (
- oldPod *kapi.Pod
- shouldPass = v.expectedPolicy != ""
- authz = &TestAuthorizer{usernameToNamespaceToAllowedPSPs: v.allowed, allowedAPIGroupName: v.allowedGroup}
- canMutate = true
- )
- pod := goodPod()
- pod.Namespace = v.ns
- pod.Spec.ServiceAccountName = v.sa
- testPSPAdmitAdvanced(k, kadmission.Create, v.inPolicies, authz, v.user,
- pod, oldPod, shouldPass, shouldPass, canMutate, v.expectedPolicy, t)
- }
- }
- func TestPolicyAuthorizationErrors(t *testing.T) {
- policyWithName := func(name string) *policy.PodSecurityPolicy {
- p := restrictivePSP()
- p.Name = name
- return p
- }
- const (
- sa = "sa"
- ns = "test"
- userName = "user"
- )
- tests := map[string]struct {
- inPolicies []*policy.PodSecurityPolicy
- allowed map[string]map[string]map[string]bool
- expectValidationErrs int
- }{
- "policies not allowed": {
- allowed: map[string]map[string]map[string]bool{},
- inPolicies: []*policy.PodSecurityPolicy{
- policyWithName("policy1"),
- policyWithName("policy2"),
- },
- expectValidationErrs: 0,
- },
- "policy allowed by user": {
- allowed: map[string]map[string]map[string]bool{
- "user": {
- "test": {"policy1": true},
- },
- },
- inPolicies: []*policy.PodSecurityPolicy{
- policyWithName("policy1"),
- policyWithName("policy2"),
- },
- expectValidationErrs: 1,
- },
- "policy allowed by service account": {
- allowed: map[string]map[string]map[string]bool{
- serviceaccount.MakeUsername("test", "sa"): {
- "test": {"policy2": true},
- },
- },
- inPolicies: []*policy.PodSecurityPolicy{
- policyWithName("policy1"),
- policyWithName("policy2"),
- },
- expectValidationErrs: 1,
- },
- "multiple policies allowed": {
- allowed: map[string]map[string]map[string]bool{
- "user": {
- "test": {"policy1": true},
- },
- serviceaccount.MakeUsername("test", "sa"): {
- "test": {"policy2": true},
- },
- },
- inPolicies: []*policy.PodSecurityPolicy{
- policyWithName("policy1"),
- policyWithName("policy2"),
- },
- expectValidationErrs: 2,
- },
- }
- for desc, tc := range tests {
- t.Run(desc, func(t *testing.T) {
- authz := &TestAuthorizer{usernameToNamespaceToAllowedPSPs: tc.allowed}
- pod := goodPod()
- pod.Namespace = ns
- pod.Spec.ServiceAccountName = sa
- pod.Spec.SecurityContext.HostPID = true
- plugin := NewTestAdmission(tc.inPolicies, authz)
- attrs := kadmission.NewAttributesRecord(pod, nil, kapi.Kind("Pod").WithVersion("version"), ns, "", kapi.Resource("pods").WithVersion("version"), "", kadmission.Create, &metav1.CreateOptions{}, false, &user.DefaultInfo{Name: userName})
- allowedPod, _, validationErrs, err := plugin.computeSecurityContext(context.Background(), attrs, pod, true, "")
- assert.Nil(t, allowedPod)
- assert.NoError(t, err)
- assert.Len(t, validationErrs, tc.expectValidationErrs)
- })
- }
- }
- func TestPreferValidatedPSP(t *testing.T) {
- restrictivePSPWithName := func(name string) *policy.PodSecurityPolicy {
- p := restrictivePSP()
- p.Name = name
- return p
- }
- permissivePSPWithName := func(name string) *policy.PodSecurityPolicy {
- p := permissivePSP()
- p.Name = name
- return p
- }
- tests := map[string]struct {
- inPolicies []*policy.PodSecurityPolicy
- expectValidationErrs int
- validatedPSPHint string
- expectedPSP string
- }{
- "no policy saved in annotations, PSPs are ordered lexicographically": {
- inPolicies: []*policy.PodSecurityPolicy{
- restrictivePSPWithName("001restrictive"),
- restrictivePSPWithName("002restrictive"),
- permissivePSPWithName("002permissive"),
- permissivePSPWithName("001permissive"),
- permissivePSPWithName("003permissive"),
- },
- expectValidationErrs: 0,
- validatedPSPHint: "",
- expectedPSP: "001permissive",
- },
- "policy saved in annotations is preferred": {
- inPolicies: []*policy.PodSecurityPolicy{
- restrictivePSPWithName("001restrictive"),
- restrictivePSPWithName("002restrictive"),
- permissivePSPWithName("001permissive"),
- permissivePSPWithName("002permissive"),
- permissivePSPWithName("003permissive"),
- },
- expectValidationErrs: 0,
- validatedPSPHint: "002permissive",
- expectedPSP: "002permissive",
- },
- "policy saved in annotations is invalid": {
- inPolicies: []*policy.PodSecurityPolicy{
- restrictivePSPWithName("001restrictive"),
- restrictivePSPWithName("002restrictive"),
- },
- expectValidationErrs: 2,
- validatedPSPHint: "foo",
- expectedPSP: "",
- },
- "policy saved in annotations is disallowed anymore": {
- inPolicies: []*policy.PodSecurityPolicy{
- restrictivePSPWithName("001restrictive"),
- restrictivePSPWithName("002restrictive"),
- },
- expectValidationErrs: 2,
- validatedPSPHint: "001restrictive",
- expectedPSP: "",
- },
- "policy saved in annotations is disallowed anymore, but find another one": {
- inPolicies: []*policy.PodSecurityPolicy{
- restrictivePSPWithName("001restrictive"),
- restrictivePSPWithName("002restrictive"),
- permissivePSPWithName("002permissive"),
- permissivePSPWithName("001permissive"),
- },
- expectValidationErrs: 0,
- validatedPSPHint: "001restrictive",
- expectedPSP: "001permissive",
- },
- }
- for desc, tc := range tests {
- t.Run(desc, func(t *testing.T) {
- authz := authorizerfactory.NewAlwaysAllowAuthorizer()
- allowPrivilegeEscalation := true
- pod := goodPod()
- pod.Namespace = "ns"
- pod.Spec.ServiceAccountName = "sa"
- pod.Spec.Containers[0].SecurityContext.AllowPrivilegeEscalation = &allowPrivilegeEscalation
- plugin := NewTestAdmission(tc.inPolicies, authz)
- attrs := kadmission.NewAttributesRecord(pod, nil, kapi.Kind("Pod").WithVersion("version"), "ns", "", kapi.Resource("pods").WithVersion("version"), "", kadmission.Update, &metav1.UpdateOptions{}, false, &user.DefaultInfo{Name: "test"})
- _, pspName, validationErrs, err := plugin.computeSecurityContext(context.Background(), attrs, pod, false, tc.validatedPSPHint)
- assert.NoError(t, err)
- assert.Len(t, validationErrs, tc.expectValidationErrs)
- assert.Equal(t, tc.expectedPSP, pspName)
- })
- }
- }
- func restrictivePSP() *policy.PodSecurityPolicy {
- allowPrivilegeEscalation := false
- return &policy.PodSecurityPolicy{
- ObjectMeta: metav1.ObjectMeta{
- Name: "restrictive",
- Annotations: map[string]string{},
- },
- Spec: policy.PodSecurityPolicySpec{
- AllowPrivilegeEscalation: &allowPrivilegeEscalation,
- RunAsUser: policy.RunAsUserStrategyOptions{
- Rule: policy.RunAsUserStrategyMustRunAs,
- Ranges: []policy.IDRange{
- {Min: int64(999), Max: int64(999)},
- },
- },
- RunAsGroup: &policy.RunAsGroupStrategyOptions{
- Rule: policy.RunAsGroupStrategyMustRunAs,
- Ranges: []policy.IDRange{
- {Min: int64(999), Max: int64(999)},
- },
- },
- SELinux: policy.SELinuxStrategyOptions{
- Rule: policy.SELinuxStrategyMustRunAs,
- SELinuxOptions: &v1.SELinuxOptions{
- Level: "s9:z0,z1",
- },
- },
- FSGroup: policy.FSGroupStrategyOptions{
- Rule: policy.FSGroupStrategyMustRunAs,
- Ranges: []policy.IDRange{
- {Min: int64(999), Max: int64(999)},
- },
- },
- SupplementalGroups: policy.SupplementalGroupsStrategyOptions{
- Rule: policy.SupplementalGroupsStrategyMustRunAs,
- Ranges: []policy.IDRange{
- {Min: int64(999), Max: int64(999)},
- },
- },
- },
- }
- }
- func permissivePSP() *policy.PodSecurityPolicy {
- allowPrivilegeEscalation := true
- return &policy.PodSecurityPolicy{
- ObjectMeta: metav1.ObjectMeta{
- Name: "privileged",
- Annotations: map[string]string{},
- },
- Spec: policy.PodSecurityPolicySpec{
- AllowPrivilegeEscalation: &allowPrivilegeEscalation,
- HostIPC: true,
- HostNetwork: true,
- HostPID: true,
- HostPorts: []policy.HostPortRange{{Min: 0, Max: 65536}},
- Volumes: []policy.FSType{policy.All},
- AllowedCapabilities: []v1.Capability{policy.AllowAllCapabilities},
- RunAsUser: policy.RunAsUserStrategyOptions{
- Rule: policy.RunAsUserStrategyRunAsAny,
- },
- RunAsGroup: &policy.RunAsGroupStrategyOptions{
- Rule: policy.RunAsGroupStrategyRunAsAny,
- },
- SELinux: policy.SELinuxStrategyOptions{
- Rule: policy.SELinuxStrategyRunAsAny,
- },
- FSGroup: policy.FSGroupStrategyOptions{
- Rule: policy.FSGroupStrategyRunAsAny,
- },
- SupplementalGroups: policy.SupplementalGroupsStrategyOptions{
- Rule: policy.SupplementalGroupsStrategyRunAsAny,
- },
- },
- }
- }
- // goodPod is empty and should not be used directly for testing since we're providing
- // two different PSPs. Since no values are specified it would be allowed to match any
- // psp when defaults are filled in.
- func goodPod() *kapi.Pod {
- return &kapi.Pod{
- ObjectMeta: metav1.ObjectMeta{
- Name: "pod",
- Namespace: "namespace",
- Annotations: map[string]string{},
- },
- Spec: kapi.PodSpec{
- ServiceAccountName: "default",
- SecurityContext: &kapi.PodSecurityContext{},
- Containers: []kapi.Container{
- {
- Name: defaultContainerName,
- SecurityContext: &kapi.SecurityContext{},
- },
- },
- },
- }
- }
|