Forráskód Böngészése

Integration test for capabilities

Verify non-root containers are able to use file
capabilities.

Signed-off-by: Luboslav Pivarc <lpivarc@redhat.com>
Co-authored-by: Cory Snider <csnider@mirantis.com>
Signed-off-by: Cory Snider <csnider@mirantis.com>
Luboslav Pivarc 2 éve
szülő
commit
42fa7a1951

+ 108 - 0
integration/capabilities/capabilities_linux_test.go

@@ -0,0 +1,108 @@
+package capabilities
+
+import (
+	"bytes"
+	"context"
+	"io"
+	"strings"
+	"testing"
+	"time"
+
+	"github.com/docker/docker/api/types"
+	"github.com/docker/docker/integration/internal/container"
+	"github.com/docker/docker/pkg/stdcopy"
+	"github.com/docker/docker/testutil/fakecontext"
+
+	"gotest.tools/v3/assert"
+	"gotest.tools/v3/poll"
+)
+
+func TestNoNewPrivileges(t *testing.T) {
+	defer setupTest(t)()
+
+	withFileCapability := `
+		FROM debian:bullseye-slim
+		RUN apt-get update && apt-get install -y libcap2-bin --no-install-recommends
+		RUN setcap CAP_DAC_OVERRIDE=+eip /bin/cat
+		RUN echo "hello" > /txt && chown 0:0 /txt && chmod 700 /txt
+		RUN useradd -u 1500 test
+	`
+	imageTag := "captest"
+
+	source := fakecontext.New(t, "", fakecontext.WithDockerfile(withFileCapability))
+	defer source.Close()
+
+	client := testEnv.APIClient()
+
+	// Build image
+	ctx := context.TODO()
+	resp, err := client.ImageBuild(ctx,
+		source.AsTarReader(t),
+		types.ImageBuildOptions{
+			Tags: []string{imageTag},
+		})
+	assert.NilError(t, err)
+	_, err = io.Copy(io.Discard, resp.Body)
+	assert.NilError(t, err)
+	resp.Body.Close()
+
+	testCases := []struct {
+		doc            string
+		opts           []func(*container.TestContainerConfig)
+		stdOut, stdErr string
+	}{
+		{
+			doc: "CapabilityRequested=true",
+			opts: []func(*container.TestContainerConfig){
+				container.WithUser("test"),
+				container.WithCapability("CAP_DAC_OVERRIDE"),
+			},
+			stdOut: "hello",
+		},
+		{
+			doc: "CapabilityRequested=false",
+			opts: []func(*container.TestContainerConfig){
+				container.WithUser("test"),
+				container.WithDropCapability("CAP_DAC_OVERRIDE"),
+			},
+			stdErr: "exec /bin/cat: operation not permitted",
+		},
+	}
+
+	for _, tc := range testCases {
+		tc := tc
+		t.Run(tc.doc, func(t *testing.T) {
+			// Run the container with the image
+			opts := append(tc.opts,
+				container.WithImage(imageTag),
+				container.WithCmd("/bin/cat", "/txt"),
+				container.WithSecurityOpt("no-new-privileges=true"),
+			)
+			cid := container.Run(ctx, t, client, opts...)
+			poll.WaitOn(t, container.IsInState(ctx, client, cid, "exited"), poll.WithDelay(100*time.Millisecond))
+
+			// Assert on outputs
+			logReader, err := client.ContainerLogs(ctx, cid, types.ContainerLogsOptions{
+				ShowStdout: true,
+				ShowStderr: true,
+			})
+			assert.NilError(t, err)
+			defer logReader.Close()
+
+			var actualStdout, actualStderr bytes.Buffer
+			_, err = stdcopy.StdCopy(&actualStdout, &actualStderr, logReader)
+			assert.NilError(t, err)
+
+			stdOut := strings.TrimSpace(actualStdout.String())
+			stdErr := strings.TrimSpace(actualStderr.String())
+			if stdOut != tc.stdOut {
+				t.Fatalf("test produced invalid output: %q, expected %q. Stderr:%q", stdOut, tc.stdOut, stdErr)
+			}
+			if stdErr != tc.stdErr {
+				t.Fatalf("test produced invalid error: %q, expected %q. Stdout:%q", stdErr, tc.stdErr, stdOut)
+
+			}
+		})
+	}
+
+}

+ 33 - 0
integration/capabilities/main_linux_test.go

@@ -0,0 +1,33 @@
+package capabilities
+
+import (
+	"fmt"
+	"os"
+	"testing"
+
+	"github.com/docker/docker/testutil/environment"
+)
+
+var testEnv *environment.Execution
+
+func TestMain(m *testing.M) {
+	var err error
+	testEnv, err = environment.New()
+	if err != nil {
+		fmt.Println(err)
+		os.Exit(1)
+	}
+	err = environment.EnsureFrozenImagesLinux(testEnv)
+	if err != nil {
+		fmt.Println(err)
+		os.Exit(1)
+	}
+
+	testEnv.Print()
+	os.Exit(m.Run())
+}
+
+func setupTest(t *testing.T) func() {
+	environment.ProtectAll(t, testEnv)
+	return func() { testEnv.Clean(t) }
+}

+ 18 - 0
integration/internal/container/ops.go

@@ -255,3 +255,21 @@ func WithCDIDevices(cdiDeviceNames ...string) func(*TestContainerConfig) {
 		c.HostConfig.DeviceRequests = append(c.HostConfig.DeviceRequests, request)
 	}
 }
+
+func WithCapability(capabilities ...string) func(*TestContainerConfig) {
+	return func(c *TestContainerConfig) {
+		c.HostConfig.CapAdd = append(c.HostConfig.CapAdd, capabilities...)
+	}
+}
+
+func WithDropCapability(capabilities ...string) func(*TestContainerConfig) {
+	return func(c *TestContainerConfig) {
+		c.HostConfig.CapDrop = append(c.HostConfig.CapDrop, capabilities...)
+	}
+}
+
+func WithSecurityOpt(opt string) func(*TestContainerConfig) {
+	return func(c *TestContainerConfig) {
+		c.HostConfig.SecurityOpt = append(c.HostConfig.SecurityOpt, opt)
+	}
+}