Skip to content

Commit

Permalink
Add positive and negative tests for security policy mount constraints
Browse files Browse the repository at this point in the history
Signed-off-by: Maksim An <maksiman@microsoft.com>
  • Loading branch information
anmaxvl committed Mar 21, 2022
1 parent b7dee59 commit b4b0736
Showing 1 changed file with 220 additions and 3 deletions.
223 changes: 220 additions & 3 deletions test/cri-containerd/policy_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,18 +5,31 @@ package cri_containerd

import (
"context"
"strings"
"testing"

runtime "k8s.io/cri-api/pkg/apis/runtime/v1alpha2"

"github.com/Microsoft/hcsshim/internal/tools/securitypolicy/helpers"
"github.com/Microsoft/hcsshim/pkg/annotations"
"github.com/Microsoft/hcsshim/pkg/securitypolicy"
"k8s.io/cri-api/pkg/apis/runtime/v1alpha2"
)

var (
validPolicyAlpineCommand = []string{"ash", "-c", "echo 'Hello'"}
)

type configOpt func(*securitypolicy.ContainerConfig) error

type configSideEffect func(*runtime.CreateContainerRequest) error

func withMountConstraints(mc []securitypolicy.MountConfig) configOpt {
return func(config *securitypolicy.ContainerConfig) error {
config.Mounts = append(config.Mounts, mc...)
return nil
}
}

func securityPolicyFromContainers(containers []securitypolicy.ContainerConfig) (string, error) {
pc, err := helpers.PolicyContainersFromConfigs(containers)
if err != nil {
Expand All @@ -39,7 +52,7 @@ func sandboxSecurityPolicy(t *testing.T) string {
return policyString
}

func alpineSecurityPolicy(t *testing.T) string {
func alpineSecurityPolicy(t *testing.T, opts ...configOpt) string {
defaultContainers := helpers.DefaultContainerConfigs()
alpineContainer := securitypolicy.NewContainerConfig(
"alpine:latest",
Expand All @@ -50,6 +63,12 @@ func alpineSecurityPolicy(t *testing.T) string {
[]securitypolicy.MountConfig{},
)

for _, o := range opts {
if err := o(&alpineContainer); err != nil {
t.Fatalf("failed to apply configOpt: %s", err)
}
}

containers := append(defaultContainers, alpineContainer)
policyString, err := securityPolicyFromContainers(containers)
if err != nil {
Expand All @@ -58,7 +77,7 @@ func alpineSecurityPolicy(t *testing.T) string {
return policyString
}

func sandboxRequestWithPolicy(t *testing.T, policy string) *v1alpha2.RunPodSandboxRequest {
func sandboxRequestWithPolicy(t *testing.T, policy string) *runtime.RunPodSandboxRequest {
return getRunPodSandboxRequest(
t,
lcowRuntimeHandler,
Expand Down Expand Up @@ -117,3 +136,201 @@ func Test_RunSimpleAlpineContainer_WithPolicy_Allowed(t *testing.T) {
startContainer(t, client, ctx, containerID)
stopContainer(t, client, ctx, containerID)
}

func Test_RunContainer_WithMountConstraints_Allowed(t *testing.T) {
requireFeatures(t, featureLCOW)
pullRequiredLCOWImages(t, []string{imageLcowK8sPause, imageLcowAlpine})

client := newTestRuntimeClient(t)
ctx, cancel := context.WithCancel(context.Background())
defer cancel()

type config struct {
name string
sideEffect configSideEffect
opts []configOpt
}

for _, testConfig := range []config{
{
name: "DefaultMounts",
sideEffect: func(_ *runtime.CreateContainerRequest) error {
return nil
},
opts: []configOpt{},
},
{
name: "SandboxMountRW",
sideEffect: func(req *runtime.CreateContainerRequest) error {
req.Config.Mounts = append(req.Config.Mounts, &runtime.Mount{
HostPath: "sandbox://sandbox/path",
ContainerPath: "/container/path",
})
return nil
},
opts: []configOpt{withMountConstraints([]securitypolicy.MountConfig{
{
HostPath: "sandbox://sandbox/path",
ContainerPath: "/container/path",
},
})},
},
{
name: "SandboxMountRO",
sideEffect: func(req *runtime.CreateContainerRequest) error {
req.Config.Mounts = append(req.Config.Mounts, &runtime.Mount{
HostPath: "sandbox://sandbox/path",
ContainerPath: "/container/path",
Readonly: true,
})
return nil
},
opts: []configOpt{withMountConstraints([]securitypolicy.MountConfig{
{
HostPath: "sandbox://sandbox/path",
ContainerPath: "/container/path",
Readonly: true,
},
})},
},
} {
t.Run(testConfig.name, func(t *testing.T) {
alpinePolicy := alpineSecurityPolicy(t, testConfig.opts...)
sandboxRequest := sandboxRequestWithPolicy(t, alpinePolicy)

podID := runPodSandbox(t, client, ctx, sandboxRequest)
defer removePodSandbox(t, client, ctx, podID)
defer stopPodSandbox(t, client, ctx, podID)

containerRequest := getCreateContainerRequest(
podID,
"alpine-with-policy",
"alpine:latest",
validPolicyAlpineCommand,
sandboxRequest.Config,
)

if err := testConfig.sideEffect(containerRequest); err != nil {
t.Fatalf("failed to apply containerRequest side effect: %s", err)
}

containerID := createContainer(t, client, ctx, containerRequest)
defer removeContainer(t, client, ctx, containerID)
defer stopContainer(t, client, ctx, containerID)
})
}
}

func Test_RunContainer_WithMountConstraints_NotAllowed(t *testing.T) {
requireFeatures(t, featureLCOW)
pullRequiredLCOWImages(t, []string{imageLcowK8sPause, imageLcowAlpine})

client := newTestRuntimeClient(t)
ctx, cancel := context.WithCancel(context.Background())
defer cancel()

type config struct {
name string
sideEffect configSideEffect
opts []configOpt
expectedError string
}

testSandboxMountOpts := []configOpt{
withMountConstraints([]securitypolicy.MountConfig{
{
HostPath: "sandbox://sandbox/path",
ContainerPath: "/container/path",
},
}),
}
for _, testConfig := range []config{
{
name: "InvalidSandboxMountSource",
sideEffect: func(req *runtime.CreateContainerRequest) error {
req.Config.Mounts = append(req.Config.Mounts, &runtime.Mount{
HostPath: "sandbox://sandbox/invalid/path",
ContainerPath: "/container/path",
})
return nil
},
opts: testSandboxMountOpts,
expectedError: "is not allowed by mount constraints",
},
{
name: "InvalidSandboxMountDestination",
sideEffect: func(req *runtime.CreateContainerRequest) error {
req.Config.Mounts = append(req.Config.Mounts, &runtime.Mount{
HostPath: "sandbox://sandbox/path",
ContainerPath: "/container/path/invalid",
})
return nil
},
opts: testSandboxMountOpts,
expectedError: "is not allowed by mount constraints",
},
{
name: "InvalidSandboxMountFlagRO",
sideEffect: func(req *runtime.CreateContainerRequest) error {
req.Config.Mounts = append(req.Config.Mounts, &runtime.Mount{
HostPath: "sandbox://sandbox/path",
ContainerPath: "/container/path",
Readonly: true,
})
return nil
},
opts: testSandboxMountOpts,
expectedError: "is not allowed by mount constraints",
},
{
name: "InvalidSandboxMountFlagRW",
sideEffect: func(req *runtime.CreateContainerRequest) error {
req.Config.Mounts = append(req.Config.Mounts, &runtime.Mount{
HostPath: "sandbox://sandbox/path",
ContainerPath: "/container/path",
})
return nil
},
opts: []configOpt{withMountConstraints([]securitypolicy.MountConfig{
{
HostPath: "sandbox://sandbox/path",
ContainerPath: "/container/path",
Readonly: true,
},
})},
expectedError: "is not allowed by mount constraints",
},
} {
t.Run(testConfig.name, func(t *testing.T) {
alpinePolicy := alpineSecurityPolicy(t, testConfig.opts...)
sandboxRequest := sandboxRequestWithPolicy(t, alpinePolicy)

podID := runPodSandbox(t, client, ctx, sandboxRequest)
defer removePodSandbox(t, client, ctx, podID)
defer stopPodSandbox(t, client, ctx, podID)

containerRequest := getCreateContainerRequest(
podID,
"alipne-with-policy",
"alpine:latest",
validPolicyAlpineCommand,
sandboxRequest.Config,
)

if err := testConfig.sideEffect(containerRequest); err != nil {
t.Fatalf("failed to apply containerRequest side effect: %s", err)
}

containerID := createContainer(t, client, ctx, containerRequest)
_, err := client.StartContainer(ctx, &runtime.StartContainerRequest{
ContainerId: containerID,
})
if err == nil {
t.Fatal("expected container start failure")
}
if !strings.Contains(err.Error(), testConfig.expectedError) {
t.Fatalf("expected %q in error message, got: %q", testConfig.expectedError, err)
}
})
}
}

0 comments on commit b4b0736

Please sign in to comment.