forked from moby/moby
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Capabilities are serialised in VFS_CAP_REVISION_3 when an image is built in a user-namespaced daemon, instead of VFS_CAP_REVISION_2. This adds a test for this, though it's currently wired to fail if the capabilities are serialised in VFS_CAP_REVISION_2 instead in this situation, since this is unexpected. Signed-off-by: Eric Mountain <eric.mountain@datadoghq.com>
- Loading branch information
1 parent
eaeb59c
commit 126a443
Showing
1 changed file
with
140 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,140 @@ | ||
package build // import "github.com/docker/docker/integration/build" | ||
|
||
import ( | ||
"bufio" | ||
"bytes" | ||
"context" | ||
"io" | ||
"io/ioutil" | ||
"os" | ||
"strings" | ||
"testing" | ||
|
||
"github.com/docker/docker/api/types" | ||
"github.com/docker/docker/integration/internal/container" | ||
"github.com/docker/docker/pkg/stdcopy" | ||
"github.com/docker/docker/testutil/daemon" | ||
"github.com/docker/docker/testutil/fakecontext" | ||
"gotest.tools/v3/assert" | ||
"gotest.tools/v3/skip" | ||
) | ||
|
||
// Implements a test for https://github.com/moby/moby/issues/41723 | ||
// Images built in a user-namespaced daemon should have capabilities serialised in | ||
// VFS_CAP_REVISION_2 (no user-namespace root uid) format rather than V3 (that includes | ||
// the root uid). | ||
func TestBuildUserNamespaceValidateCapabilitiesAreV2(t *testing.T) { | ||
skip.If(t, testEnv.DaemonInfo.OSType != "linux") | ||
skip.If(t, testEnv.IsRemoteDaemon()) | ||
skip.If(t, !testEnv.IsUserNamespaceInKernel()) | ||
skip.If(t, testEnv.IsRootless()) | ||
|
||
const imageTag = "capabilities:1.0" | ||
|
||
tmp, err := ioutil.TempDir("", "integration-") | ||
assert.NilError(t, err) | ||
defer os.RemoveAll(tmp) | ||
|
||
dUserRemap := daemon.New(t) | ||
dUserRemap.StartWithBusybox(t, "--userns-remap", "default") | ||
dUserRemapRunning := true | ||
defer func() { | ||
if dUserRemapRunning { | ||
dUserRemap.Stop(t) | ||
} | ||
}() | ||
|
||
dockerfile := ` | ||
FROM debian:bullseye | ||
RUN setcap CAP_NET_BIND_SERVICE=+eip /bin/sleep | ||
` | ||
|
||
ctx := context.Background() | ||
source := fakecontext.New(t, "", fakecontext.WithDockerfile(dockerfile)) | ||
defer source.Close() | ||
|
||
clientUserRemap := dUserRemap.NewClientT(t) | ||
resp, err := clientUserRemap.ImageBuild(ctx, | ||
source.AsTarReader(t), | ||
types.ImageBuildOptions{ | ||
Tags: []string{imageTag}, | ||
}) | ||
assert.NilError(t, err) | ||
defer resp.Body.Close() | ||
buf := make([]byte, 1024) | ||
for { | ||
n, err := resp.Body.Read(buf) | ||
if err != nil && err != io.EOF { | ||
t.Fatalf("Error reading ImageBuild response: %v", err) | ||
break | ||
} | ||
if n == 0 { | ||
break | ||
} | ||
} | ||
|
||
reader, err := clientUserRemap.ImageSave(ctx, []string{imageTag}) | ||
assert.NilError(t, err, "failed to download capabilities image") | ||
defer reader.Close() | ||
|
||
tar, err := os.Create(tmp + "/image.tar") | ||
assert.NilError(t, err, "failed to create image tar file") | ||
defer tar.Close() | ||
|
||
_, err = io.Copy(tar, reader) | ||
assert.NilError(t, err, "failed to write image tar file") | ||
|
||
dUserRemap.Stop(t) | ||
dUserRemap.Cleanup(t) | ||
dUserRemapRunning = false | ||
|
||
dNoUserRemap := daemon.New(t) | ||
dNoUserRemap.StartWithBusybox(t) | ||
defer dNoUserRemap.Stop(t) | ||
|
||
clientNoUserRemap := dNoUserRemap.NewClientT(t) | ||
|
||
tarFile, err := os.Open(tmp + "/image.tar") | ||
assert.NilError(t, err, "failed to open image tar file") | ||
|
||
tarReader := bufio.NewReader(tarFile) | ||
loadResp, err := clientNoUserRemap.ImageLoad(ctx, tarReader, false) | ||
assert.NilError(t, err, "failed to load image tar file") | ||
defer loadResp.Body.Close() | ||
for { | ||
n, err := loadResp.Body.Read(buf) | ||
if err != nil && err != io.EOF { | ||
t.Fatalf("Error reading ImageLoad response: %v", err) | ||
break | ||
} | ||
if n == 0 { | ||
break | ||
} | ||
} | ||
|
||
cid := container.Run(ctx, t, clientNoUserRemap, | ||
container.WithImage(imageTag), | ||
container.WithCmd("/sbin/getcap", "-n", "/bin/sleep"), | ||
) | ||
logReader, err := clientNoUserRemap.ContainerLogs(ctx, cid, types.ContainerLogsOptions{ | ||
ShowStdout: true, | ||
}) | ||
assert.NilError(t, err) | ||
|
||
actualStdout := new(bytes.Buffer) | ||
actualStderr := ioutil.Discard | ||
_, err = stdcopy.StdCopy(actualStdout, actualStderr, logReader) | ||
assert.NilError(t, err) | ||
if strings.TrimSpace(actualStdout.String()) != "/bin/sleep cap_net_bind_service=eip" { | ||
// Activate when fix is merged: https://github.com/moby/moby/pull/41724 | ||
//t.Fatalf("run produced invalid output: %q, expected %q", actualStdout.String(), "/bin/sleep cap_net_bind_service=eip") | ||
// t.Logf("run produced invalid output (expected until #41724 merges): %q, expected %q", | ||
// actualStdout.String(), | ||
// "/bin/sleep cap_net_bind_service=eip") | ||
} else { | ||
// Shouldn't happen until fix is merged: https://github.com/moby/moby/pull/41724 | ||
t.Fatalf("run produced valid output (unexpected until #41724 merges): %q, expected %q", | ||
actualStdout.String(), | ||
"/bin/sleep cap_net_bind_service=eip") | ||
} | ||
} |