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>
This commit is contained in:
Luboslav Pivarc 2023-07-17 11:48:02 +02:00 committed by Cory Snider
parent 3aef732e61
commit 42fa7a1951
3 changed files with 159 additions and 0 deletions

View file

@ -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)
}
})
}
}

View file

@ -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) }
}

View file

@ -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)
}
}