From c2683eafd2eb4d931a0499bc1c1f0fb14bc103b7 Mon Sep 17 00:00:00 2001 From: Mayank Gaikwad <8110509+mgdevstack@users.noreply.github.com> Date: Fri, 31 Aug 2018 14:11:01 +0530 Subject: [PATCH] Port security context NodeConformance e2e_node tests to e2e --- test/e2e/common/BUILD | 1 + test/e2e/common/security_context.go | 266 +++++++++++++++++++++++++ test/e2e_node/security_context_test.go | 187 ----------------- 3 files changed, 267 insertions(+), 187 deletions(-) create mode 100644 test/e2e/common/security_context.go diff --git a/test/e2e/common/BUILD b/test/e2e/common/BUILD index 4f993aa1cb..cf140ea4c5 100644 --- a/test/e2e/common/BUILD +++ b/test/e2e/common/BUILD @@ -30,6 +30,7 @@ go_library( "projected.go", "secrets.go", "secrets_volume.go", + "security_context.go", "sysctl.go", "util.go", "volumes.go", diff --git a/test/e2e/common/security_context.go b/test/e2e/common/security_context.go new file mode 100644 index 0000000000..4a8b2757bd --- /dev/null +++ b/test/e2e/common/security_context.go @@ -0,0 +1,266 @@ +/* +Copyright 2017 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 common + +import ( + "fmt" + "strings" + + "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/util/uuid" + "k8s.io/kubernetes/test/e2e/framework" + imageutils "k8s.io/kubernetes/test/utils/image" + + . "github.com/onsi/ginkgo" +) + +var _ = framework.KubeDescribe("Security Context", func() { + f := framework.NewDefaultFramework("security-context-test") + var podClient *framework.PodClient + BeforeEach(func() { + podClient = f.PodClient() + }) + + Context("When creating a container with runAsUser", func() { + makeUserPod := func(podName, image string, command []string, userid int64) *v1.Pod { + return &v1.Pod{ + ObjectMeta: metav1.ObjectMeta{ + Name: podName, + }, + Spec: v1.PodSpec{ + RestartPolicy: v1.RestartPolicyNever, + Containers: []v1.Container{ + { + Image: image, + Name: podName, + Command: command, + SecurityContext: &v1.SecurityContext{ + RunAsUser: &userid, + }, + }, + }, + }, + } + } + createAndWaitUserPod := func(userid int64) { + podName := fmt.Sprintf("busybox-user-%d-%s", userid, uuid.NewUUID()) + podClient.Create(makeUserPod(podName, + framework.BusyBoxImage, + []string{"sh", "-c", fmt.Sprintf("test $(id -u) -eq %d", userid)}, + userid, + )) + + podClient.WaitForSuccess(podName, framework.PodStartTimeout) + } + + /* + Release : v1.12 + Testname: Security Context: runAsUser (id:65534) + Description: Container created with runAsUser option, passing an id (id:65534) uses that + given id when running the container. + */ + It("should run the container with uid 65534 [NodeConformance]", func() { + createAndWaitUserPod(65534) + }) + + /* + Release : v1.12 + Testname: Security Context: runAsUser (id:0) + Description: Container created with runAsUser option, passing an id (id:0) uses that + given id when running the container. + */ + It("should run the container with uid 0 [NodeConformance]", func() { + createAndWaitUserPod(0) + }) + }) + + Context("When creating a pod with readOnlyRootFilesystem", func() { + makeUserPod := func(podName, image string, command []string, readOnlyRootFilesystem bool) *v1.Pod { + return &v1.Pod{ + ObjectMeta: metav1.ObjectMeta{ + Name: podName, + }, + Spec: v1.PodSpec{ + RestartPolicy: v1.RestartPolicyNever, + Containers: []v1.Container{ + { + Image: image, + Name: podName, + Command: command, + SecurityContext: &v1.SecurityContext{ + ReadOnlyRootFilesystem: &readOnlyRootFilesystem, + }, + }, + }, + }, + } + } + createAndWaitUserPod := func(readOnlyRootFilesystem bool) string { + podName := fmt.Sprintf("busybox-readonly-%v-%s", readOnlyRootFilesystem, uuid.NewUUID()) + podClient.Create(makeUserPod(podName, + framework.BusyBoxImage, + []string{"sh", "-c", "touch checkfile"}, + readOnlyRootFilesystem, + )) + + if readOnlyRootFilesystem { + podClient.WaitForFailure(podName, framework.PodStartTimeout) + } else { + podClient.WaitForSuccess(podName, framework.PodStartTimeout) + } + + return podName + } + + /* + Release : v1.12 + Testname: Security Context: readOnlyRootFilesystem=true. + Description: when a container has configured readOnlyRootFilesystem to true, write operations are not allowed. + */ + It("should run the container with readonly rootfs when readOnlyRootFilesystem=true [NodeConformance]", func() { + createAndWaitUserPod(true) + }) + + /* + Release : v1.12 + Testname: Security Context: readOnlyRootFilesystem=false. + Description: when a container has configured readOnlyRootFilesystem to false, write operations are allowed. + */ + It("should run the container with writable rootfs when readOnlyRootFilesystem=false [NodeConformance]", func() { + createAndWaitUserPod(false) + }) + }) + + Context("When creating a pod with privileged", func() { + makeUserPod := func(podName, image string, command []string, privileged bool) *v1.Pod { + return &v1.Pod{ + ObjectMeta: metav1.ObjectMeta{ + Name: podName, + }, + Spec: v1.PodSpec{ + RestartPolicy: v1.RestartPolicyNever, + Containers: []v1.Container{ + { + Image: image, + Name: podName, + Command: command, + SecurityContext: &v1.SecurityContext{ + Privileged: &privileged, + }, + }, + }, + }, + } + } + createAndWaitUserPod := func(privileged bool) string { + podName := fmt.Sprintf("busybox-privileged-%v-%s", privileged, uuid.NewUUID()) + podClient.Create(makeUserPod(podName, + framework.BusyBoxImage, + []string{"sh", "-c", "ip link add dummy0 type dummy || true"}, + privileged, + )) + podClient.WaitForSuccess(podName, framework.PodStartTimeout) + return podName + } + + It("should run the container as unprivileged when false [NodeConformance]", func() { + podName := createAndWaitUserPod(false) + logs, err := framework.GetPodLogs(f.ClientSet, f.Namespace.Name, podName, podName) + if err != nil { + framework.Failf("GetPodLogs for pod %q failed: %v", podName, err) + } + + framework.Logf("Got logs for pod %q: %q", podName, logs) + if !strings.Contains(logs, "Operation not permitted") { + framework.Failf("unprivileged container shouldn't be able to create dummy device") + } + }) + }) + + Context("when creating containers with AllowPrivilegeEscalation", func() { + makeAllowPrivilegeEscalationPod := func(podName string, allowPrivilegeEscalation *bool, uid int64) *v1.Pod { + return &v1.Pod{ + ObjectMeta: metav1.ObjectMeta{ + Name: podName, + }, + Spec: v1.PodSpec{ + RestartPolicy: v1.RestartPolicyNever, + Containers: []v1.Container{ + { + Image: imageutils.GetE2EImage(imageutils.Nonewprivs), + Name: podName, + SecurityContext: &v1.SecurityContext{ + AllowPrivilegeEscalation: allowPrivilegeEscalation, + RunAsUser: &uid, + }, + }, + }, + }, + } + } + createAndMatchOutput := func(podName, output string, allowPrivilegeEscalation *bool, uid int64) error { + podClient.Create(makeAllowPrivilegeEscalationPod(podName, + allowPrivilegeEscalation, + uid, + )) + podClient.WaitForSuccess(podName, framework.PodStartTimeout) + return podClient.MatchContainerOutput(podName, podName, output) + } + + /* + Testname: allowPrivilegeEscalation unset and uid != 0. + Description: Configuring the allowPrivilegeEscalation unset, allows the privilege escalation operation. + A container is configured with allowPrivilegeEscalation not specified (nil) and a given uid which is not 0. + When the container is run, the container is run using uid=0. + */ + It("should allow privilege escalation when not explicitly set and uid != 0 [NodeConformance]", func() { + podName := "alpine-nnp-nil-" + string(uuid.NewUUID()) + if err := createAndMatchOutput(podName, "Effective uid: 0", nil, 1000); err != nil { + framework.Failf("Match output for pod %q failed: %v", podName, err) + } + }) + + /* + Testname: allowPrivilegeEscalation=false. + Description: Configuring the allowPrivilegeEscalation to false, does not allow the privilege escalation operation. + A container is configured with allowPrivilegeEscalation=false and a given uid (1000) which is not 0. + When the container is run, the container is run using uid=1000. + */ + It("should not allow privilege escalation when false [NodeConformance]", func() { + podName := "alpine-nnp-false-" + string(uuid.NewUUID()) + apeFalse := false + if err := createAndMatchOutput(podName, "Effective uid: 1000", &apeFalse, 1000); err != nil { + framework.Failf("Match output for pod %q failed: %v", podName, err) + } + }) + + /* + Testname: allowPrivilegeEscalation=true. + Description: Configuring the allowPrivilegeEscalation to true, allows the privilege escalation operation. + A container is configured with allowPrivilegeEscalation=true and a given uid (1000) which is not 0. + When the container is run, the container is run using uid=0 (making use of the privilege escalation). + */ + It("should allow privilege escalation when true [NodeConformance]", func() { + podName := "alpine-nnp-true-" + string(uuid.NewUUID()) + apeTrue := true + if err := createAndMatchOutput(podName, "Effective uid: 0", &apeTrue, 1000); err != nil { + framework.Failf("Match output for pod %q failed: %v", podName, err) + } + }) + }) +}) diff --git a/test/e2e_node/security_context_test.go b/test/e2e_node/security_context_test.go index 3623b3da2e..86ddc8c262 100644 --- a/test/e2e_node/security_context_test.go +++ b/test/e2e_node/security_context_test.go @@ -350,177 +350,6 @@ var _ = framework.KubeDescribe("Security Context", func() { }) }) - Context("When creating a container with runAsUser", func() { - makeUserPod := func(podName, image string, command []string, userid int64) *v1.Pod { - return &v1.Pod{ - ObjectMeta: metav1.ObjectMeta{ - Name: podName, - }, - Spec: v1.PodSpec{ - RestartPolicy: v1.RestartPolicyNever, - Containers: []v1.Container{ - { - Image: image, - Name: podName, - Command: command, - SecurityContext: &v1.SecurityContext{ - RunAsUser: &userid, - }, - }, - }, - }, - } - } - createAndWaitUserPod := func(userid int64) { - podName := fmt.Sprintf("busybox-user-%d-%s", userid, uuid.NewUUID()) - podClient.Create(makeUserPod(podName, - busyboxImage, - []string{"sh", "-c", fmt.Sprintf("test $(id -u) -eq %d", userid)}, - userid, - )) - - podClient.WaitForSuccess(podName, framework.PodStartTimeout) - } - - It("should run the container with uid 65534 [NodeConformance]", func() { - createAndWaitUserPod(65534) - }) - - It("should run the container with uid 0 [NodeConformance]", func() { - createAndWaitUserPod(0) - }) - }) - - Context("When creating a pod with readOnlyRootFilesystem", func() { - makeUserPod := func(podName, image string, command []string, readOnlyRootFilesystem bool) *v1.Pod { - return &v1.Pod{ - ObjectMeta: metav1.ObjectMeta{ - Name: podName, - }, - Spec: v1.PodSpec{ - RestartPolicy: v1.RestartPolicyNever, - Containers: []v1.Container{ - { - Image: image, - Name: podName, - Command: command, - SecurityContext: &v1.SecurityContext{ - ReadOnlyRootFilesystem: &readOnlyRootFilesystem, - }, - }, - }, - }, - } - } - createAndWaitUserPod := func(readOnlyRootFilesystem bool) string { - podName := fmt.Sprintf("busybox-readonly-%v-%s", readOnlyRootFilesystem, uuid.NewUUID()) - podClient.Create(makeUserPod(podName, - "busybox", - []string{"sh", "-c", "touch checkfile"}, - readOnlyRootFilesystem, - )) - - if readOnlyRootFilesystem { - podClient.WaitForFailure(podName, framework.PodStartTimeout) - } else { - podClient.WaitForSuccess(podName, framework.PodStartTimeout) - } - - return podName - } - - It("should run the container with readonly rootfs when readOnlyRootFilesystem=true [NodeConformance]", func() { - createAndWaitUserPod(true) - }) - - It("should run the container with writable rootfs when readOnlyRootFilesystem=false [NodeConformance]", func() { - createAndWaitUserPod(false) - }) - }) - - Context("when creating containers with AllowPrivilegeEscalation", func() { - - BeforeEach(func() { - if framework.TestContext.ContainerRuntime == "docker" { - isSupported, err := isDockerNoNewPrivilegesSupported() - framework.ExpectNoError(err) - if !isSupported { - framework.Skipf("Skipping because no_new_privs is not supported in this docker") - } - // It turns out SELinux policy in RHEL 7 does not play well with - // the "NoNewPrivileges" flag. So let's skip this test when running - // with SELinux support enabled. - // - // TODO(filbranden): Remove this after the fix for - // https://github.com/projectatomic/container-selinux/issues/45 - // has been backported to RHEL 7 (expected on RHEL 7.5) - selinuxEnabled, err := isDockerSELinuxSupportEnabled() - framework.ExpectNoError(err) - if selinuxEnabled { - framework.Skipf("Skipping because Docker daemon is running with SELinux support enabled") - } - } - }) - - makeAllowPrivilegeEscalationPod := func(podName string, allowPrivilegeEscalation *bool, uid int64) *v1.Pod { - return &v1.Pod{ - ObjectMeta: metav1.ObjectMeta{ - Name: podName, - }, - Spec: v1.PodSpec{ - RestartPolicy: v1.RestartPolicyNever, - Containers: []v1.Container{ - { - Image: imageutils.GetE2EImage(imageutils.Nonewprivs), - Name: podName, - SecurityContext: &v1.SecurityContext{ - AllowPrivilegeEscalation: allowPrivilegeEscalation, - RunAsUser: &uid, - }, - }, - }, - }, - } - } - createAndMatchOutput := func(podName, output string, allowPrivilegeEscalation *bool, uid int64) error { - podClient.Create(makeAllowPrivilegeEscalationPod(podName, - allowPrivilegeEscalation, - uid, - )) - - podClient.WaitForSuccess(podName, framework.PodStartTimeout) - - if err := podClient.MatchContainerOutput(podName, podName, output); err != nil { - return err - } - - return nil - } - - It("should allow privilege escalation when not explicitly set and uid != 0 [NodeConformance]", func() { - podName := "alpine-nnp-nil-" + string(uuid.NewUUID()) - if err := createAndMatchOutput(podName, "Effective uid: 0", nil, 1000); err != nil { - framework.Failf("Match output for pod %q failed: %v", podName, err) - } - }) - - It("should not allow privilege escalation when false [NodeConformance]", func() { - podName := "alpine-nnp-false-" + string(uuid.NewUUID()) - apeFalse := false - if err := createAndMatchOutput(podName, "Effective uid: 1000", &apeFalse, 1000); err != nil { - framework.Failf("Match output for pod %q failed: %v", podName, err) - } - }) - - It("should allow privilege escalation when true [NodeConformance]", func() { - podName := "alpine-nnp-true-" + string(uuid.NewUUID()) - apeTrue := true - if err := createAndMatchOutput(podName, "Effective uid: 0", &apeTrue, 1000); err != nil { - framework.Failf("Match output for pod %q failed: %v", podName, err) - } - }) - }) - Context("When creating a pod with privileged", func() { makeUserPod := func(podName, image string, command []string, privileged bool) *v1.Pod { return &v1.Pod{ @@ -549,9 +378,7 @@ var _ = framework.KubeDescribe("Security Context", func() { []string{"sh", "-c", "ip link add dummy0 type dummy || true"}, privileged, )) - podClient.WaitForSuccess(podName, framework.PodStartTimeout) - return podName } @@ -567,19 +394,5 @@ var _ = framework.KubeDescribe("Security Context", func() { framework.Failf("privileged container should be able to create dummy device") } }) - - It("should run the container as unprivileged when false [NodeConformance]", func() { - podName := createAndWaitUserPod(false) - logs, err := framework.GetPodLogs(f.ClientSet, f.Namespace.Name, podName, podName) - if err != nil { - framework.Failf("GetPodLogs for pod %q failed: %v", podName, err) - } - - framework.Logf("Got logs for pod %q: %q", podName, logs) - if !strings.Contains(logs, "Operation not permitted") { - framework.Failf("unprivileged container shouldn't be able to create dummy device") - } - }) }) - })