Skip to content

Commit

Permalink
tests: Add CRI tests for integrity protection of LCOW layers (#1193)
Browse files Browse the repository at this point in the history
Add tests that validate that integrity protection is checked when
LCOW layers have dm-verity hashes appended.

Signed-off-by: Maksim An <maksiman@microsoft.com>
  • Loading branch information
anmaxvl authored Oct 22, 2021
1 parent 821c9a9 commit f174aa8
Show file tree
Hide file tree
Showing 4 changed files with 136 additions and 40 deletions.
43 changes: 3 additions & 40 deletions test/cri-containerd/container_layers_packing_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,11 @@
package cri_containerd

import (
"bufio"
"bytes"
"context"
"fmt"
"strings"
"testing"

"github.com/Microsoft/hcsshim/internal/shimdiag"
"github.com/Microsoft/hcsshim/osversion"
"github.com/Microsoft/hcsshim/pkg/annotations"
testutilities "github.com/Microsoft/hcsshim/test/functional/utilities"
Expand All @@ -22,46 +19,12 @@ const (
alpine70ExtraLayers = "cplatpublic.azurecr.io/alpine70extra:latest"
)

func filterStrings(input []string, include string) []string {
var result []string
for _, str := range input {
if strings.Contains(str, include) {
result = append(result, str)
}
}
return result
}

func shimDiagExec(ctx context.Context, t *testing.T, podID string, cmd []string) string {
shimName := fmt.Sprintf("k8s.io-%s", podID)
shim, err := shimdiag.GetShim(shimName)
if err != nil {
t.Fatalf("failed to find shim %v: %v", shimName, err)
}
shimClient := shimdiag.NewShimDiagClient(shim)

bufOut := &bytes.Buffer{}
bw := bufio.NewWriter(bufOut)
bufErr := &bytes.Buffer{}
bwErr := bufio.NewWriter(bufErr)

exitCode, err := execInHost(ctx, shimClient, cmd, nil, bw, bwErr)
if err != nil {
t.Fatalf("failed to exec request in the host with: %v and %v", err, bufErr.String())
}
if exitCode != 0 {
t.Fatalf("exec request in host failed with exit code %v: %v", exitCode, bufErr.String())
}

return strings.TrimSpace(bufOut.String())
}

func validateTargets(ctx context.Context, t *testing.T, deviceNumber int, podID string, expected int) {
dmDiag := shimDiagExec(ctx, t, podID, []string{"ls", "-l", "/dev/mapper"})
dmDiag := shimDiagExecOutput(ctx, t, podID, []string{"ls", "-l", "/dev/mapper"})
dmPattern := fmt.Sprintf("dm-linear-pmem%d", deviceNumber)
dmLines := filterStrings(strings.Split(dmDiag, "\n"), dmPattern)

lrDiag := shimDiagExec(ctx, t, podID, []string{"ls", "-l", "/run/layers"})
lrDiag := shimDiagExecOutput(ctx, t, podID, []string{"ls", "-l", "/run/layers"})
lrPattern := fmt.Sprintf("p%d", deviceNumber)
lrLines := filterStrings(strings.Split(lrDiag, "\n"), lrPattern)
if len(lrLines) != len(dmLines) {
Expand Down Expand Up @@ -184,7 +147,7 @@ func Test_Annotation_Disable_Multi_Mapping(t *testing.T) {
startContainer(t, client, ctx, containerID)
defer stopContainer(t, client, ctx, containerID)

dmDiag := shimDiagExec(ctx, t, podID, []string{"ls", "-l", "/dev/mapper"})
dmDiag := shimDiagExecOutput(ctx, t, podID, []string{"ls", "-l", "/dev/mapper"})
filtered := filterStrings(strings.Split(dmDiag, "\n"), "dm-linear")
if len(filtered) > 0 {
t.Fatalf("no linear devices should've been created.\n%s", dmDiag)
Expand Down
39 changes: 39 additions & 0 deletions test/cri-containerd/exec.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,12 @@
package cri_containerd

import (
"bufio"
"bytes"
"context"
"fmt"
"io"
"strings"
"testing"

"github.com/Microsoft/hcsshim/internal/cmd"
Expand Down Expand Up @@ -69,3 +73,38 @@ func execInHost(ctx context.Context, client shimdiag.ShimDiagService, args []str
}
return resp.ExitCode, nil
}

// shimDiagExecOutput is a small wrapper on top of execInHost, that returns the exec output
func shimDiagExecOutput(ctx context.Context, t *testing.T, podID string, cmd []string) string {
shimName := fmt.Sprintf("k8s.io-%s", podID)
shim, err := shimdiag.GetShim(shimName)
if err != nil {
t.Fatalf("failed to find shim %v: %v", shimName, err)
}
shimClient := shimdiag.NewShimDiagClient(shim)

bufOut := &bytes.Buffer{}
bw := bufio.NewWriter(bufOut)
bufErr := &bytes.Buffer{}
bwErr := bufio.NewWriter(bufErr)

exitCode, err := execInHost(ctx, shimClient, cmd, nil, bw, bwErr)
if err != nil {
t.Fatalf("failed to exec request in the host with: %v and %v", err, bufErr.String())
}
if exitCode != 0 {
t.Fatalf("exec request in host failed with exit code %v: %v", exitCode, bufErr.String())
}

return strings.TrimSpace(bufOut.String())
}

func filterStrings(input []string, include string) []string {
var result []string
for _, str := range input {
if strings.Contains(str, include) {
result = append(result, str)
}
}
return result
}
93 changes: 93 additions & 0 deletions test/cri-containerd/layer_integrity_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
// +build functional

package cri_containerd

import (
"context"
"fmt"
"strconv"
"strings"
"testing"

"github.com/Microsoft/hcsshim/pkg/annotations"
)

func Test_LCOW_Layer_Integrity(t *testing.T) {
requireFeatures(t, featureLCOWIntegrity, featureLCOW)

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

pullRequiredLCOWImages(t, []string{imageLcowK8sPause})

// Delete container image in case it already exists.
removeImages(t, []string{imageLcowAlpine})

// Pull image with dm-verity enabled.
pullRequiredLCOWImages(
t,
[]string{imageLcowAlpine},
WithSandboxLabels(map[string]string{
"containerd.io/diff/io.microsoft.lcow.append-dm-verity": "true",
}),
)

type config struct {
layerType string
vPMemCount int
rootFSType string
}

for _, scenario := range []config{
{
layerType: "scsi",
vPMemCount: 0,
rootFSType: "initrd",
},
{
layerType: "pmem",
vPMemCount: 16,
rootFSType: "initrd",
},
{
layerType: "pmem",
vPMemCount: 16,
rootFSType: "vhd",
},
} {
t.Run(fmt.Sprintf("Integrity-For-%s-%s", scenario.layerType, scenario.rootFSType), func(t *testing.T) {
podReq := getRunPodSandboxRequest(
t,
lcowRuntimeHandler,
WithSandboxAnnotations(map[string]string{
annotations.VPMemCount: strconv.Itoa(scenario.vPMemCount),
annotations.PreferredRootFSType: scenario.rootFSType,
}),
)
podID := runPodSandbox(t, client, ctx, podReq)
defer removePodSandbox(t, client, ctx, podID)

// Launch container
cmd := []string{"ash", "-c", "while true; do sleep 1; done"}
contReq := getCreateContainerRequest(
podID,
fmt.Sprintf("alpine-%s-%s", scenario.layerType, scenario.rootFSType),
imageLcowAlpine,
cmd,
podReq.Config,
)
contID := createContainer(t, client, ctx, contReq)
defer removeContainer(t, client, ctx, contID)
startContainer(t, client, ctx, contID)
defer stopContainer(t, client, ctx, contID)

// Validate that verity target(s) present
output := shimDiagExecOutput(ctx, t, podID, []string{"ls", "-l", "/dev/mapper"})
filtered := filterStrings(strings.Split(output, "\n"), fmt.Sprintf("dm-verity-%s", scenario.layerType))
if len(filtered) == 0 {
t.Fatalf("expected verity targets for %s devices, none found.\n%s\n", scenario.layerType, output)
}
})
}
}
1 change: 1 addition & 0 deletions test/cri-containerd/main_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,7 @@ const (
featureGPU = "GPU"
featureCRIUpdateContainer = "UpdateContainer"
featureTerminateOnRestart = "TerminateOnRestart"
featureLCOWIntegrity = "LCOWIntegrity"
)

var allFeatures = []string{
Expand Down

0 comments on commit f174aa8

Please sign in to comment.