Skip to content

Commit

Permalink
adds tests for seccomp
Browse files Browse the repository at this point in the history
Signed-off-by: Mike Brown <[email protected]>

adds support for seccomp testing

Signed-off-by: Mike Brown <[email protected]>
  • Loading branch information
mikebrow committed Sep 16, 2017
1 parent 3028b83 commit 2c9f66d
Showing 1 changed file with 247 additions and 1 deletion.
248 changes: 247 additions & 1 deletion pkg/validate/security_context.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ package validate

import (
"fmt"
"io/ioutil"
"net"
"os"
"os/exec"
Expand All @@ -34,7 +35,19 @@ import (
)

const (
nginxContainerImage string = "nginx"
nginxContainerImage string = "nginx"
localhost string = "localhost/"
seccompBlockHostNameProfile = `
{
"defaultAction": "SCMP_ACT_ALLOW",
"syscalls": [
{
"name": "sethostname",
"action": "SCMP_ACT_ERRNO"
}
]
}
`
)

var _ = framework.KubeDescribe("Security Context", func() {
Expand Down Expand Up @@ -402,6 +415,146 @@ var _ = framework.KubeDescribe("Security Context", func() {
})
})

Context("runtime should support seccomp security context", func() {
var podID string
var podConfig *runtimeapi.PodSandboxConfig
sysCaps := []string{"ALL"}

AfterEach(func() {
By("stop PodSandbox")
rc.StopPodSandbox(podID)
By("delete PodSandbox")
rc.RemovePodSandbox(podID)
})

It("runtime should support an seccomp profile that blocks setting hostname with no sysCaps", func() {
By("create pod")
podID, podConfig = framework.CreatePodSandboxForContainer(rc)
By("create container with seccompBlockHostNameProfile and test")
containerID := createSeccompContainerWithProfile(rc, ic, podID, podConfig,
"container-with-block-hostname-seccomp-profile-test-",
seccompBlockHostNameProfile, nil, false, localhost, true)
startContainer(rc, containerID)
Eventually(func() runtimeapi.ContainerState {
return getContainerStatus(rc, containerID).State
}, time.Minute, time.Second*4).Should(Equal(runtimeapi.ContainerState_CONTAINER_RUNNING))
checkSetHostname(rc, containerID, false)
})

It("runtime should not support a custom seccomp profile without using localhost/ as a prefix", func() {
By("create pod")
podID, podConfig = framework.CreatePodSandboxForContainer(rc)
By("create container with seccompBlockHostNameProfile and test")
_ = createSeccompContainerWithProfile(rc, ic, podID, podConfig,
"container-with-block-hostname-seccomp-profile-test-",
seccompBlockHostNameProfile, nil, false, "", false)
})

It("runtime should ignore a seccomp profile that blocks setting hostname when privileged", func() {
By("create privileged pod")
podID, podConfig = createPrivilegedPodSandbox(rc, true)
By("create privileged container with seccompBlockHostNameProfile and test")
containerID := createSeccompContainerWithProfile(rc, ic, podID, podConfig,
"container-with-block-hostname-seccomp-profile-test-",
seccompBlockHostNameProfile, nil, true, localhost, true)
startContainer(rc, containerID)
Eventually(func() runtimeapi.ContainerState {
return getContainerStatus(rc, containerID).State
}, time.Minute, time.Second*4).Should(Equal(runtimeapi.ContainerState_CONTAINER_RUNNING))
checkSetHostname(rc, containerID, true)
})

It("runtime should block setting hostname with no seccomp profile specified", func() {
By("create pod")
podID, podConfig = framework.CreatePodSandboxForContainer(rc)
By("create container without seccomp profile and test")
containerID := framework.CreateDefaultContainer(rc, ic, podID, podConfig,
"container-with-no-seccomp-profile-test-")
startContainer(rc, containerID)
Eventually(func() runtimeapi.ContainerState {
return getContainerStatus(rc, containerID).State
}, time.Minute, time.Second*4).Should(Equal(runtimeapi.ContainerState_CONTAINER_RUNNING))
checkSetHostname(rc, containerID, false)
})

It("runtime should support setting hostname with docker/default seccomp profile and sysCaps", func() {
By("create pod")
podID, podConfig = framework.CreatePodSandboxForContainer(rc)
By("create container with docker/default seccomp profile and test")
containerID := createSeccompContainer(rc, ic, podID, podConfig,
"container-with-dockerdefault-seccomp-profile-test-", "docker/default", sysCaps, false, true)
startContainer(rc, containerID)
Eventually(func() runtimeapi.ContainerState {
return getContainerStatus(rc, containerID).State
}, time.Minute, time.Second*4).Should(Equal(runtimeapi.ContainerState_CONTAINER_RUNNING))
checkSetHostname(rc, containerID, true)
})

It("runtime should support setting hostname with runtime/default seccomp profile and sysCaps", func() {
By("create pod")
podID, podConfig = framework.CreatePodSandboxForContainer(rc)
By("create container with runtime/default seccomp profile and test")
containerID := createSeccompContainer(rc, ic, podID, podConfig,
"container-with-runtimedefault-seccomp-profile-test-", "runtime/default", sysCaps, false, true)
startContainer(rc, containerID)
Eventually(func() runtimeapi.ContainerState {
return getContainerStatus(rc, containerID).State
}, time.Minute, time.Second*4).Should(Equal(runtimeapi.ContainerState_CONTAINER_RUNNING))
checkSetHostname(rc, containerID, true)
})

It("runtime should block sethostname with docker/default seccomp profile and no sysCaps", func() {
By("create pod")
podID, podConfig = framework.CreatePodSandboxForContainer(rc)
By("create container with docker/default seccomp profile and test")
containerID := createSeccompContainer(rc, ic, podID, podConfig,
"container-with-dockerdefault-seccomp-profile-test-", "docker/default", nil, false, true)
startContainer(rc, containerID)
Eventually(func() runtimeapi.ContainerState {
return getContainerStatus(rc, containerID).State
}, time.Minute, time.Second*4).Should(Equal(runtimeapi.ContainerState_CONTAINER_RUNNING))
checkSetHostname(rc, containerID, false)
})

It("runtime should block sethostname with runtime/default seccomp profile and no sysCaps", func() {
By("create pod")
podID, podConfig = framework.CreatePodSandboxForContainer(rc)
By("create container with runtime/default seccomp profile and test")
containerID := createSeccompContainer(rc, ic, podID, podConfig,
"container-with-runtimedefault-seccomp-profile-test-", "runtime/default", nil, false, true)
startContainer(rc, containerID)
Eventually(func() runtimeapi.ContainerState {
return getContainerStatus(rc, containerID).State
}, time.Minute, time.Second*4).Should(Equal(runtimeapi.ContainerState_CONTAINER_RUNNING))
checkSetHostname(rc, containerID, false)
})

It("runtime should block sethostname with unconfined seccomp profile and no sysCaps", func() {
By("create pod")
podID, podConfig = framework.CreatePodSandboxForContainer(rc)
By("create container with runtime/default seccomp profile and test")
containerID := createSeccompContainer(rc, ic, podID, podConfig,
"container-with-runtimedefault-seccomp-profile-test-", "unconfined", nil, false, true)
startContainer(rc, containerID)
Eventually(func() runtimeapi.ContainerState {
return getContainerStatus(rc, containerID).State
}, time.Minute, time.Second*4).Should(Equal(runtimeapi.ContainerState_CONTAINER_RUNNING))
checkSetHostname(rc, containerID, false)
})

It("runtime should block sethostname with nil seccomp profile and no sysCaps", func() {
By("create pod")
podID, podConfig = framework.CreatePodSandboxForContainer(rc)
By("create container with runtime/default seccomp profile and test")
containerID := createSeccompContainer(rc, ic, podID, podConfig,
"container-with-runtimedefault-seccomp-profile-test-", "", nil, false, true)
startContainer(rc, containerID)
Eventually(func() runtimeapi.ContainerState {
return getContainerStatus(rc, containerID).State
}, time.Minute, time.Second*4).Should(Equal(runtimeapi.ContainerState_CONTAINER_RUNNING))
checkSetHostname(rc, containerID, false)
})
})
})

// createRunAsUserContainer creates the container with specified RunAsUser in ContainerConfig.
Expand Down Expand Up @@ -637,3 +790,96 @@ func createAndCheckHostNetwork(rc internalapi.RuntimeService, ic internalapi.Ima

return podID
}

// createSeccompContainerWithProfile creates container with specified seccompcontainer in ContainerConfig.
func createSeccompContainerWithProfile(rc internalapi.RuntimeService,
ic internalapi.ImageManagerService,
podID string,
podConfig *runtimeapi.PodSandboxConfig,
prefix, profileContent string,
caps []string,
privileged bool,
localhostprefix string,
expectContainerCreateToPass bool) string {
By("create seccomp profile for container")
profile := "cri-tools-seccomp-profile.json"
f, err := ioutil.TempFile("/tmp", profile)
framework.ExpectNoError(err, "failed to open temp file: %v", err)
defer f.Close()

_, err = f.WriteString(profileContent)
framework.ExpectNoError(err, "failed to write profiles to file: %v", err)

return createSeccompContainer(rc, ic, podID, podConfig, prefix, localhostprefix+f.Name(), caps, privileged, expectContainerCreateToPass)
}

// createSeccompContainer creates container with the specified seccomp profile.
func createSeccompContainer(rc internalapi.RuntimeService,
ic internalapi.ImageManagerService,
podID string,
podConfig *runtimeapi.PodSandboxConfig,
prefix string,
profile string,
caps []string,
privileged bool,
expectContainerCreateToPass bool) string {
By("create " + profile + " Seccomp container")
containerName := prefix + framework.NewUUID()
containerConfig := &runtimeapi.ContainerConfig{
Metadata: framework.BuildContainerMetadata(containerName, framework.DefaultAttempt),
Image: &runtimeapi.ImageSpec{Image: framework.DefaultContainerImage},
Command: []string{"sleep", "60"},
Linux: &runtimeapi.LinuxContainerConfig{
SecurityContext: &runtimeapi.LinuxContainerSecurityContext{
Privileged: privileged,
Capabilities: &runtimeapi.Capability{
AddCapabilities: caps,
},
SeccompProfilePath: profile,
},
},
}
if expectContainerCreateToPass {
return framework.CreateContainer(rc, ic, containerConfig, podID, podConfig)
} else {
return failCreatingContainer(rc, ic, containerConfig, podID, podConfig)
}
}

// failCreatingContainer creates a container with the prefix of containerName
// and expectation of failure in the create step.
func failCreatingContainer(rc internalapi.RuntimeService,
ic internalapi.ImageManagerService,
config *runtimeapi.ContainerConfig,
podID string,
podConfig *runtimeapi.PodSandboxConfig) string {
// Pull the image if it does not exist. (don't fail for inability to pull image)
imageName := config.Image.Image
if !strings.Contains(imageName, ":") {
imageName = imageName + ":latest"
}
status := framework.ImageStatus(ic, imageName)
if status == nil {
framework.PullPublicImage(ic, imageName)
}
By("Create container.")
containerID, err := rc.CreateContainer(podID, config, podConfig)
msg := fmt.Sprintf("create should fail with err %v", err)
Expect(err).To(HaveOccurred(), msg)
return containerID
}

// checkSetHostname checks if the hostname can be set in the container.
func checkSetHostname(rc internalapi.RuntimeService, containerID string, setable bool) {
By("set hostname in container to determine whether sethostname is blocked")

cmd := []string{"hostname", "ANewHostName"}
stdout, stderr, err := rc.ExecSync(containerID, cmd, time.Duration(defaultExecSyncTimeout)*time.Second)
msg := fmt.Sprintf("cmd %v, stdout %q, stderr %q", cmd, stdout, stderr)

if setable {
Expect(err).NotTo(HaveOccurred(), msg)
} else {
Expect(err).To(HaveOccurred(), msg)
}
}

0 comments on commit 2c9f66d

Please sign in to comment.