diff --git a/pkg/security/admission/admission_test.go b/pkg/security/admission/admission_test.go index da0ce0c5f5b2..07e0de4d0106 100644 --- a/pkg/security/admission/admission_test.go +++ b/pkg/security/admission/admission_test.go @@ -179,7 +179,7 @@ func testSCCAdmit(testCaseName string, sccs []*securityapi.SecurityContextConstr } } -func TestAdmit(t *testing.T) { +func TestAdmitSuccess(t *testing.T) { // create the annotated namespace and add it to the fake client namespace := admissiontesting.CreateNamespaceForTest() serviceAccount := admissiontesting.CreateSAForTest() @@ -251,20 +251,6 @@ func TestAdmit(t *testing.T) { // create the admission plugin p := NewTestAdmission(cache, tc) - // setup test data - uidNotInRange := goodPod() - var uid int64 = 1001 - uidNotInRange.Spec.Containers[0].SecurityContext.RunAsUser = &uid - - invalidMCSLabels := goodPod() - invalidMCSLabels.Spec.Containers[0].SecurityContext.SELinuxOptions = &kapi.SELinuxOptions{ - Level: "s1:q0,q1", - } - - disallowedPriv := goodPod() - var priv bool = true - disallowedPriv.Spec.Containers[0].SecurityContext.Privileged = &priv - // specifies a UID in the range of the preallocated UID annotation specifyUIDInRange := goodPod() var goodUID int64 = 3 @@ -293,57 +279,16 @@ func TestAdmit(t *testing.T) { Level: "s0:c1,c0", } - requestsHostNetwork := goodPod() - requestsHostNetwork.Spec.SecurityContext.HostNetwork = true - - requestsHostPID := goodPod() - requestsHostPID.Spec.SecurityContext.HostPID = true - - requestsHostIPC := goodPod() - requestsHostIPC.Spec.SecurityContext.HostIPC = true - - requestsHostPorts := goodPod() - requestsHostPorts.Spec.Containers[0].Ports = []kapi.ContainerPort{{HostPort: 1}} - - requestsSupplementalGroup := goodPod() - requestsSupplementalGroup.Spec.SecurityContext.SupplementalGroups = []int64{1} - - requestsFSGroup := goodPod() - fsGroup := int64(1) - requestsFSGroup.Spec.SecurityContext.FSGroup = &fsGroup - - requestsPodLevelMCS := goodPod() - requestsPodLevelMCS.Spec.SecurityContext.SELinuxOptions = &kapi.SELinuxOptions{ - User: "user", - Type: "type", - Role: "role", - Level: "level", - } - testCases := map[string]struct { pod *kapi.Pod - shouldAdmit bool expectedUID int64 expectedLevel string expectedFSGroup int64 expectedSupGroups []int64 expectedPriv bool }{ - "uidNotInRange": { - pod: uidNotInRange, - shouldAdmit: false, - }, - "invalidMCSLabels": { - pod: invalidMCSLabels, - shouldAdmit: false, - }, - "disallowedPriv": { - pod: disallowedPriv, - shouldAdmit: false, - }, "specifyUIDInRange": { pod: specifyUIDInRange, - shouldAdmit: true, expectedUID: *specifyUIDInRange.Spec.Containers[0].SecurityContext.RunAsUser, expectedLevel: "s0:c1,c0", expectedFSGroup: defaultGroup, @@ -351,7 +296,6 @@ func TestAdmit(t *testing.T) { }, "specifyLabels": { pod: specifyLabels, - shouldAdmit: true, expectedUID: 1, expectedLevel: specifyLabels.Spec.Containers[0].SecurityContext.SELinuxOptions.Level, expectedFSGroup: defaultGroup, @@ -359,7 +303,6 @@ func TestAdmit(t *testing.T) { }, "specifyFSGroup": { pod: specifyFSGroupInRange, - shouldAdmit: true, expectedUID: 1, expectedLevel: "s0:c1,c0", expectedFSGroup: *specifyFSGroupInRange.Spec.SecurityContext.FSGroup, @@ -367,7 +310,6 @@ func TestAdmit(t *testing.T) { }, "specifySupGroup": { pod: specifySupGroup, - shouldAdmit: true, expectedUID: 1, expectedLevel: "s0:c1,c0", expectedFSGroup: defaultGroup, @@ -375,92 +317,216 @@ func TestAdmit(t *testing.T) { }, "specifyPodLevelSELinuxLevel": { pod: specifyPodLevelSELinux, - shouldAdmit: true, expectedUID: 1, expectedLevel: "s0:c1,c0", expectedFSGroup: defaultGroup, expectedSupGroups: []int64{defaultGroup}, }, + } + + for i := 0; i < 2; i++ { + for k, v := range testCases { + v.pod.Spec.Containers, v.pod.Spec.InitContainers = v.pod.Spec.InitContainers, v.pod.Spec.Containers + containers := v.pod.Spec.Containers + if i == 0 { + containers = v.pod.Spec.InitContainers + } + attrs := kadmission.NewAttributesRecord(v.pod, nil, kapi.Kind("Pod").WithVersion("version"), v.pod.Namespace, v.pod.Name, kapi.Resource("pods").WithVersion("version"), "", kadmission.Create, &user.DefaultInfo{}) + err := p.Admit(attrs) + + if err != nil { + t.Fatalf("%s expected no errors but received %v", k, err) + } + + validatedSCC, ok := v.pod.Annotations[allocator.ValidatedSCCAnnotation] + if !ok { + t.Errorf("%s expected to find the validated annotation on the pod for the scc but found none", k) + } + if validatedSCC != saSCC.Name { + t.Errorf("%s should have validated against %s but found %s", k, saSCC.Name, validatedSCC) + } + + // ensure anything we expected to be defaulted on the container level is set + if *containers[0].SecurityContext.RunAsUser != v.expectedUID { + t.Errorf("%s expected UID %d but found %d", k, v.expectedUID, *containers[0].SecurityContext.RunAsUser) + } + if containers[0].SecurityContext.SELinuxOptions.Level != v.expectedLevel { + t.Errorf("%s expected Level %s but found %s", k, v.expectedLevel, containers[0].SecurityContext.SELinuxOptions.Level) + } + + // ensure anything we expected to be defaulted on the pod level is set + if v.pod.Spec.SecurityContext.SELinuxOptions.Level != v.expectedLevel { + t.Errorf("%s expected pod level SELinux Level %s but found %s", k, v.expectedLevel, v.pod.Spec.SecurityContext.SELinuxOptions.Level) + } + if *v.pod.Spec.SecurityContext.FSGroup != v.expectedFSGroup { + t.Errorf("%s expected fsgroup %d but found %d", k, v.expectedFSGroup, *v.pod.Spec.SecurityContext.FSGroup) + } + if len(v.pod.Spec.SecurityContext.SupplementalGroups) != len(v.expectedSupGroups) { + t.Errorf("%s found unexpected supplemental groups. Expected: %v, actual %v", k, v.expectedSupGroups, v.pod.Spec.SecurityContext.SupplementalGroups) + } + for _, g := range v.expectedSupGroups { + if !hasSupGroup(g, v.pod.Spec.SecurityContext.SupplementalGroups) { + t.Errorf("%s expected sup group %d", k, g) + } + } + } + } +} + +func TestAdmitFailure(t *testing.T) { + // create the annotated namespace and add it to the fake client + namespace := admissiontesting.CreateNamespaceForTest() + serviceAccount := admissiontesting.CreateSAForTest() + + tc := clientsetfake.NewSimpleClientset(namespace, serviceAccount) + + // create scc that requires allocation retrieval + saSCC := &securityapi.SecurityContextConstraints{ + ObjectMeta: metav1.ObjectMeta{ + Name: "scc-sa", + }, + RunAsUser: securityapi.RunAsUserStrategyOptions{ + Type: securityapi.RunAsUserStrategyMustRunAsRange, + }, + SELinuxContext: securityapi.SELinuxContextStrategyOptions{ + Type: securityapi.SELinuxStrategyMustRunAs, + }, + FSGroup: securityapi.FSGroupStrategyOptions{ + Type: securityapi.FSGroupStrategyMustRunAs, + }, + SupplementalGroups: securityapi.SupplementalGroupsStrategyOptions{ + Type: securityapi.SupplementalGroupsStrategyMustRunAs, + }, + Groups: []string{"system:serviceaccounts"}, + } + // create scc that has specific requirements that shouldn't match but is permissioned to + // service accounts to test that even though this has matching priorities (0) and a + // lower point value score (which will cause it to be sorted in front of scc-sa) it should not + // validate the requests so we should try scc-sa. + var exactUID int64 = 999 + saExactSCC := &securityapi.SecurityContextConstraints{ + ObjectMeta: metav1.ObjectMeta{ + Name: "scc-sa-exact", + }, + RunAsUser: securityapi.RunAsUserStrategyOptions{ + Type: securityapi.RunAsUserStrategyMustRunAs, + UID: &exactUID, + }, + SELinuxContext: securityapi.SELinuxContextStrategyOptions{ + Type: securityapi.SELinuxStrategyMustRunAs, + SELinuxOptions: &kapi.SELinuxOptions{ + Level: "s9:z0,z1", + }, + }, + FSGroup: securityapi.FSGroupStrategyOptions{ + Type: securityapi.FSGroupStrategyMustRunAs, + Ranges: []securityapi.IDRange{ + {Min: 999, Max: 999}, + }, + }, + SupplementalGroups: securityapi.SupplementalGroupsStrategyOptions{ + Type: securityapi.SupplementalGroupsStrategyMustRunAs, + Ranges: []securityapi.IDRange{ + {Min: 999, Max: 999}, + }, + }, + Groups: []string{"system:serviceaccounts"}, + } + + indexer := cache.NewIndexer(cache.MetaNamespaceKeyFunc, cache.Indexers{cache.NamespaceIndex: cache.MetaNamespaceIndexFunc}) + cache := securitylisters.NewSecurityContextConstraintsLister(indexer) + + indexer.Add(saExactSCC) + indexer.Add(saSCC) + + // create the admission plugin + p := NewTestAdmission(cache, tc) + + // setup test data + uidNotInRange := goodPod() + var uid int64 = 1001 + uidNotInRange.Spec.Containers[0].SecurityContext.RunAsUser = &uid + + invalidMCSLabels := goodPod() + invalidMCSLabels.Spec.Containers[0].SecurityContext.SELinuxOptions = &kapi.SELinuxOptions{ + Level: "s1:q0,q1", + } + + disallowedPriv := goodPod() + var priv bool = true + disallowedPriv.Spec.Containers[0].SecurityContext.Privileged = &priv + + requestsHostNetwork := goodPod() + requestsHostNetwork.Spec.SecurityContext.HostNetwork = true + + requestsHostPorts := goodPod() + requestsHostPorts.Spec.Containers[0].Ports = []kapi.ContainerPort{{HostPort: 1}} + + requestsHostPID := goodPod() + requestsHostPID.Spec.SecurityContext.HostPID = true + + requestsHostIPC := goodPod() + requestsHostIPC.Spec.SecurityContext.HostIPC = true + + requestsSupplementalGroup := goodPod() + requestsSupplementalGroup.Spec.SecurityContext.SupplementalGroups = []int64{1} + + requestsFSGroup := goodPod() + fsGroup := int64(1) + requestsFSGroup.Spec.SecurityContext.FSGroup = &fsGroup + + requestsPodLevelMCS := goodPod() + requestsPodLevelMCS.Spec.SecurityContext.SELinuxOptions = &kapi.SELinuxOptions{ + User: "user", + Type: "type", + Role: "role", + Level: "level", + } + + testCases := map[string]struct { + pod *kapi.Pod + }{ + "uidNotInRange": { + pod: uidNotInRange, + }, + "invalidMCSLabels": { + pod: invalidMCSLabels, + }, + "disallowedPriv": { + pod: disallowedPriv, + }, "requestsHostNetwork": { - pod: requestsHostNetwork, - shouldAdmit: false, + pod: requestsHostNetwork, }, "requestsHostPorts": { - pod: requestsHostPorts, - shouldAdmit: false, + pod: requestsHostPorts, }, "requestsHostPID": { - pod: requestsHostPID, - shouldAdmit: false, + pod: requestsHostPID, }, "requestsHostIPC": { - pod: requestsHostIPC, - shouldAdmit: false, + pod: requestsHostIPC, }, "requestsSupplementalGroup": { - pod: requestsSupplementalGroup, - shouldAdmit: false, + pod: requestsSupplementalGroup, }, "requestsFSGroup": { - pod: requestsFSGroup, - shouldAdmit: false, + pod: requestsFSGroup, }, "requestsPodLevelMCS": { - pod: requestsPodLevelMCS, - shouldAdmit: false, + pod: requestsPodLevelMCS, }, } for i := 0; i < 2; i++ { for k, v := range testCases { v.pod.Spec.Containers, v.pod.Spec.InitContainers = v.pod.Spec.InitContainers, v.pod.Spec.Containers - containers := v.pod.Spec.Containers - if i == 0 { - containers = v.pod.Spec.InitContainers - } attrs := kadmission.NewAttributesRecord(v.pod, nil, kapi.Kind("Pod").WithVersion("version"), v.pod.Namespace, v.pod.Name, kapi.Resource("pods").WithVersion("version"), "", kadmission.Create, &user.DefaultInfo{}) err := p.Admit(attrs) - if v.shouldAdmit && err != nil { - t.Fatalf("%s expected no errors but received %v", k, err) - } - if !v.shouldAdmit && err == nil { + if err == nil { t.Errorf("%s expected errors but received none", k) } - - if v.shouldAdmit { - validatedSCC, ok := v.pod.Annotations[allocator.ValidatedSCCAnnotation] - if !ok { - t.Errorf("%s expected to find the validated annotation on the pod for the scc but found none", k) - } - if validatedSCC != saSCC.Name { - t.Errorf("%s should have validated against %s but found %s", k, saSCC.Name, validatedSCC) - } - - // ensure anything we expected to be defaulted on the container level is set - if *containers[0].SecurityContext.RunAsUser != v.expectedUID { - t.Errorf("%s expected UID %d but found %d", k, v.expectedUID, *containers[0].SecurityContext.RunAsUser) - } - if containers[0].SecurityContext.SELinuxOptions.Level != v.expectedLevel { - t.Errorf("%s expected Level %s but found %s", k, v.expectedLevel, containers[0].SecurityContext.SELinuxOptions.Level) - } - - // ensure anything we expected to be defaulted on the pod level is set - if v.pod.Spec.SecurityContext.SELinuxOptions.Level != v.expectedLevel { - t.Errorf("%s expected pod level SELinux Level %s but found %s", k, v.expectedLevel, v.pod.Spec.SecurityContext.SELinuxOptions.Level) - } - if *v.pod.Spec.SecurityContext.FSGroup != v.expectedFSGroup { - t.Errorf("%s expected fsgroup %d but found %d", k, v.expectedFSGroup, *v.pod.Spec.SecurityContext.FSGroup) - } - if len(v.pod.Spec.SecurityContext.SupplementalGroups) != len(v.expectedSupGroups) { - t.Errorf("%s found unexpected supplemental groups. Expected: %v, actual %v", k, v.expectedSupGroups, v.pod.Spec.SecurityContext.SupplementalGroups) - } - for _, g := range v.expectedSupGroups { - if !hasSupGroup(g, v.pod.Spec.SecurityContext.SupplementalGroups) { - t.Errorf("%s expected sup group %d", k, g) - } - } - } } } @@ -496,10 +562,8 @@ func TestAdmit(t *testing.T) { for k, v := range testCases { v.pod.Spec.Containers, v.pod.Spec.InitContainers = v.pod.Spec.InitContainers, v.pod.Spec.Containers - if !v.shouldAdmit { - // pods that were rejected by strict SCC, should pass with relaxed SCC - testSCCAdmission(v.pod, p, adminSCC.Name, k, t) - } + // pods that were rejected by strict SCC, should pass with relaxed SCC + testSCCAdmission(v.pod, p, adminSCC.Name, k, t) } } }