Files
moby/integration/build/build_userns_linux_test.go
Sebastiaan van Stijn 20d594fb79 deprecate pkg/stdcopy, move to api/stdcopy
The stdcopy package is used to produce and read multiplexed streams for
"attach" and "logs". It is used both by the API server (to produce), and
the client (to read / de-multiplex).

Move it to the api package, so that it can be included in the api module.

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2025-07-21 21:41:39 +02:00

136 lines
3.9 KiB
Go

package build
import (
"bufio"
"bytes"
"io"
"os"
"path/filepath"
"strings"
"testing"
"github.com/docker/docker/integration/internal/container"
"github.com/docker/docker/pkg/jsonmessage"
"github.com/docker/docker/testutil"
"github.com/docker/docker/testutil/daemon"
"github.com/docker/docker/testutil/fakecontext"
"github.com/docker/docker/testutil/fixtures/load"
"github.com/moby/moby/api/stdcopy"
"github.com/moby/moby/api/types/build"
containertypes "github.com/moby/moby/api/types/container"
"gotest.tools/v3/assert"
"gotest.tools/v3/poll"
"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())
ctx := testutil.StartSpan(baseContext, t)
const imageTag = "capabilities:1.0"
tmpDir := t.TempDir()
dUserRemap := daemon.New(t)
dUserRemap.Start(t, "--userns-remap", "default")
clientUserRemap := dUserRemap.NewClientT(t)
defer clientUserRemap.Close()
err := load.FrozenImagesLinux(ctx, clientUserRemap, "debian:bookworm-slim")
assert.NilError(t, err)
dUserRemapRunning := true
defer func() {
if dUserRemapRunning {
dUserRemap.Stop(t)
dUserRemap.Cleanup(t)
}
}()
dockerfile := `
FROM debian:bookworm-slim
RUN apt-get update && apt-get install -y libcap2-bin --no-install-recommends
RUN setcap CAP_NET_BIND_SERVICE=+eip /bin/sleep
`
source := fakecontext.New(t, "", fakecontext.WithDockerfile(dockerfile))
defer source.Close()
resp, err := clientUserRemap.ImageBuild(ctx,
source.AsTarReader(t),
build.ImageBuildOptions{
Tags: []string{imageTag},
})
assert.NilError(t, err)
defer resp.Body.Close()
buf := bytes.NewBuffer(nil)
err = jsonmessage.DisplayJSONMessagesStream(resp.Body, buf, 0, false, nil)
assert.NilError(t, err)
reader, err := clientUserRemap.ImageSave(ctx, []string{imageTag})
assert.NilError(t, err, "failed to download capabilities image")
defer reader.Close()
tar, err := os.Create(filepath.Join(tmpDir, "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.Start(t)
defer func() {
dNoUserRemap.Stop(t)
dNoUserRemap.Cleanup(t)
}()
clientNoUserRemap := dNoUserRemap.NewClientT(t)
defer clientNoUserRemap.Close()
tarFile, err := os.Open(tmpDir + "/image.tar")
assert.NilError(t, err, "failed to open image tar file")
defer tarFile.Close()
tarReader := bufio.NewReader(tarFile)
loadResp, err := clientNoUserRemap.ImageLoad(ctx, tarReader)
assert.NilError(t, err, "failed to load image tar file")
defer loadResp.Body.Close()
buf = bytes.NewBuffer(nil)
err = jsonmessage.DisplayJSONMessagesStream(loadResp.Body, buf, 0, false, nil)
assert.NilError(t, err)
cid := container.Run(ctx, t, clientNoUserRemap,
container.WithImage(imageTag),
container.WithCmd("/sbin/getcap", "-n", "/bin/sleep"),
)
poll.WaitOn(t, container.IsStopped(ctx, clientNoUserRemap, cid))
logReader, err := clientNoUserRemap.ContainerLogs(ctx, cid, containertypes.LogsOptions{
ShowStdout: true,
})
assert.NilError(t, err)
defer logReader.Close()
actualStdout := new(bytes.Buffer)
actualStderr := io.Discard
_, err = stdcopy.StdCopy(actualStdout, actualStderr, logReader)
assert.NilError(t, err)
if strings.TrimSpace(actualStdout.String()) != "/bin/sleep cap_net_bind_service=eip" {
t.Fatalf("run produced invalid output: %q, expected %q", actualStdout.String(), "/bin/sleep cap_net_bind_service=eip")
}
}