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>
(cherry picked from commit 42fa7a1951
)
Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
This commit is contained in:
parent
09b65e0082
commit
89a731096d
3 changed files with 159 additions and 0 deletions
108
integration/capabilities/capabilities_linux_test.go
Normal file
108
integration/capabilities/capabilities_linux_test.go
Normal 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)
|
||||
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
}
|
33
integration/capabilities/main_linux_test.go
Normal file
33
integration/capabilities/main_linux_test.go
Normal 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) }
|
||||
}
|
|
@ -241,3 +241,21 @@ func WithRuntime(name string) func(*TestContainerConfig) {
|
|||
c.HostConfig.Runtime = name
|
||||
}
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue