mirror of
https://github.com/moby/moby.git
synced 2026-06-24 08:48:23 +00:00
Merge tag 'docker-v29.5.1' into docker-29.x
docker-v29.5.1
This commit is contained in:
7
.github/workflows/validate-milestone.yml
vendored
7
.github/workflows/validate-milestone.yml
vendored
@@ -24,14 +24,19 @@ jobs:
|
||||
repo: context.repo.repo,
|
||||
pull_number: context.payload.pull_request.number,
|
||||
});
|
||||
core.info(`Modified files: ${files.map(f => f.filename).join(', ')}`);
|
||||
|
||||
const touchesVersions = files.some(f => f.filename === 'releases/versions.yaml');
|
||||
core.info(`Touches version: ${touchesVersions}`);
|
||||
|
||||
// Use the PR's version when it bumps the file, base branch otherwise.
|
||||
// It's fine to trust the author in this case, it's not meant to be
|
||||
// a security gate, just a helpful check for maintainers.
|
||||
const ref = touchesVersions
|
||||
? context.payload.pull_request.head.sha
|
||||
: context.payload.pull_request.base.sha;
|
||||
: context.payload.pull_request.base.ref;
|
||||
|
||||
core.info(`Base ref: ${ref}`);
|
||||
|
||||
const resp = await github.rest.repos.getContent({
|
||||
owner: context.repo.owner,
|
||||
|
||||
@@ -9,6 +9,7 @@ import (
|
||||
"path/filepath"
|
||||
|
||||
"github.com/moby/go-archive"
|
||||
"github.com/moby/go-archive/compression"
|
||||
containertypes "github.com/moby/moby/api/types/container"
|
||||
"github.com/moby/moby/api/types/events"
|
||||
"github.com/moby/moby/v2/daemon/container"
|
||||
@@ -101,6 +102,15 @@ func (daemon *Daemon) containerExtractToDir(container *container.Container, path
|
||||
container.Lock()
|
||||
defer container.Unlock()
|
||||
|
||||
// Decompress the archive before switching into the container's
|
||||
// filesystem to avoid executing decompression binaries (xz, unpigz)
|
||||
// that may have been placed inside the container image.
|
||||
decompressed, err := compression.DecompressStream(content)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer decompressed.Close()
|
||||
|
||||
cfs, err := daemon.openContainerFS(container)
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -143,7 +153,7 @@ func (daemon *Daemon) containerExtractToDir(container *container.Container, path
|
||||
}
|
||||
}
|
||||
|
||||
return archive.Untar(content, absPath, options)
|
||||
return archive.UntarUncompressed(decompressed, absPath, options)
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
|
||||
@@ -7,7 +7,7 @@ import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"strings"
|
||||
"strconv"
|
||||
|
||||
"github.com/containerd/log"
|
||||
"github.com/moby/sys/mount"
|
||||
@@ -91,10 +91,21 @@ func (daemon *Daemon) openContainerFS(ctr *container.Container) (_ *containerFSV
|
||||
if err := mount.MakeRSlave("/"); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
root, err := os.OpenRoot(ctr.BaseFS)
|
||||
if err != nil {
|
||||
return fmt.Errorf("open container root: %w", err)
|
||||
}
|
||||
defer root.Close()
|
||||
|
||||
// TODO(vvoland): Refactor this after security release.
|
||||
for _, m := range mounts {
|
||||
dest, err := ctr.GetResourcePath(m.Destination)
|
||||
// Destination is an absolute path within container
|
||||
// filesystem. For the os.Root to work, we need to convert it
|
||||
// to a path relative to root fs /
|
||||
relDest, err := filepath.Rel("/", m.Destination)
|
||||
if err != nil {
|
||||
return err
|
||||
return fmt.Errorf("make destination relative: %w", err)
|
||||
}
|
||||
|
||||
var stat os.FileInfo
|
||||
@@ -102,7 +113,7 @@ func (daemon *Daemon) openContainerFS(ctr *container.Container) (_ *containerFSV
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err := createIfNotExists(dest, stat.IsDir()); err != nil {
|
||||
if err := createIfNotExists(root, relDest, stat.IsDir()); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -110,9 +121,7 @@ func (daemon *Daemon) openContainerFS(ctr *container.Container) (_ *containerFSV
|
||||
if m.NonRecursive {
|
||||
bindMode = "bind"
|
||||
}
|
||||
writeMode := "ro"
|
||||
if m.Writable {
|
||||
writeMode = "rw"
|
||||
if m.ReadOnlyNonRecursive {
|
||||
return errors.New("options conflict: Writable && ReadOnlyNonRecursive")
|
||||
}
|
||||
@@ -124,6 +133,37 @@ func (daemon *Daemon) openContainerFS(ctr *container.Container) (_ *containerFSV
|
||||
return errors.New("options conflict: ReadOnlyNonRecursive && ReadOnlyForceRecursive")
|
||||
}
|
||||
|
||||
// Open the mount target through os.Root so we have a
|
||||
// file descriptor pinning the resolved inode. Using
|
||||
// /proc/self/fd/<fd> as the mount target prevents any
|
||||
// subsequent symlink swap from redirecting the mount.
|
||||
targetFile, err := root.Open(relDest)
|
||||
if err != nil {
|
||||
return fmt.Errorf("open mount target %q: %w", m.Destination, err)
|
||||
}
|
||||
targetPath := "/proc/self/fd/" + strconv.FormatUint(uint64(targetFile.Fd()), 10)
|
||||
|
||||
// The kernel rejects remount and propagation-change syscalls
|
||||
// when the target is a /proc/self/fd path. Only the initial
|
||||
// bind mount works on such paths, so we perform that via the
|
||||
// fd path for TOCTOU safety and then resolve the real path for
|
||||
// the read-only remount and propagation change.
|
||||
if err := mount.Mount(m.Source, targetPath, "", bindMode); err != nil {
|
||||
targetFile.Close()
|
||||
return err
|
||||
}
|
||||
realPath, err := os.Readlink(targetPath)
|
||||
if err != nil {
|
||||
targetFile.Close()
|
||||
return fmt.Errorf("readlink %s: %w", targetPath, err)
|
||||
}
|
||||
if !m.Writable {
|
||||
if err := mount.Mount("", realPath, "", "ro,remount,bind"); err != nil {
|
||||
targetFile.Close()
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
// openContainerFS() is called for temporary mounts
|
||||
// outside the container. Soon these will be unmounted
|
||||
// with lazy unmount option and given we have mounted
|
||||
@@ -134,20 +174,21 @@ func (daemon *Daemon) openContainerFS(ctr *container.Container) (_ *containerFSV
|
||||
// all these mounts rprivate. Do not use propagation
|
||||
// property of volume as that should apply only when
|
||||
// mounting happens inside the container.
|
||||
opts := strings.Join([]string{bindMode, writeMode, "rprivate"}, ",")
|
||||
if err := mount.Mount(m.Source, dest, "", opts); err != nil {
|
||||
if err := mount.MakeRPrivate(realPath); err != nil {
|
||||
targetFile.Close()
|
||||
return err
|
||||
}
|
||||
|
||||
if !m.Writable && !m.ReadOnlyNonRecursive {
|
||||
if err := makeMountRRO(dest); err != nil {
|
||||
if err := makeMountRRO(realPath); err != nil {
|
||||
targetFile.Close()
|
||||
if m.ReadOnlyForceRecursive {
|
||||
return err
|
||||
} else {
|
||||
log.G(context.TODO()).WithError(err).Debugf("Failed to make %q recursively read-only", dest)
|
||||
}
|
||||
log.G(context.TODO()).WithError(err).Debugf("Failed to make %q recursively read-only", m.Destination)
|
||||
}
|
||||
}
|
||||
targetFile.Close()
|
||||
}
|
||||
|
||||
return mounttree.SwitchRoot(ctr.BaseFS)
|
||||
@@ -254,24 +295,24 @@ func (vw *containerFSView) Stat(ctx context.Context, path string) (*containertyp
|
||||
}
|
||||
|
||||
// createIfNotExists creates a file or a directory only if it does not already exist.
|
||||
func createIfNotExists(dest string, isDir bool) error {
|
||||
if _, err := os.Stat(dest); err != nil {
|
||||
// FIXME(thaJeztah): this ignores any other error (which may include "dest" is of the wrong type, or permission errors).
|
||||
if os.IsNotExist(err) {
|
||||
if isDir {
|
||||
return os.MkdirAll(dest, 0o755)
|
||||
}
|
||||
if err := os.MkdirAll(filepath.Dir(dest), 0o755); err != nil {
|
||||
return err
|
||||
}
|
||||
f, err := os.OpenFile(dest, os.O_CREATE, 0o755)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
_ = f.Close()
|
||||
// The path is scoped to root using [os.Root] to prevent symlink escape attacks.
|
||||
func createIfNotExists(root *os.Root, unsafePath string, isDir bool) error {
|
||||
if isDir {
|
||||
return root.MkdirAll(unsafePath, 0o755)
|
||||
}
|
||||
|
||||
parent := filepath.Dir(unsafePath)
|
||||
if parent != "." && parent != "/" {
|
||||
if err := root.MkdirAll(parent, 0o755); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
|
||||
f, err := root.OpenFile(unsafePath, os.O_CREATE|os.O_WRONLY, 0o755)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return f.Close()
|
||||
}
|
||||
|
||||
// makeMountRRO makes the mount recursively read-only.
|
||||
|
||||
@@ -10,30 +10,36 @@ import (
|
||||
|
||||
func TestCreateIfNotExists(t *testing.T) {
|
||||
t.Run("directory", func(t *testing.T) {
|
||||
toCreate := filepath.Join(t.TempDir(), "tocreate")
|
||||
dir := t.TempDir()
|
||||
root, err := os.OpenRoot(dir)
|
||||
assert.NilError(t, err)
|
||||
defer root.Close()
|
||||
|
||||
err := createIfNotExists(toCreate, true)
|
||||
err = createIfNotExists(root, "tocreate", true)
|
||||
assert.NilError(t, err)
|
||||
|
||||
fileinfo, err := os.Stat(toCreate)
|
||||
fileinfo, err := os.Stat(filepath.Join(dir, "tocreate"))
|
||||
assert.NilError(t, err, "Did not create destination")
|
||||
assert.Assert(t, fileinfo.IsDir(), "Should have been a dir, seems it's not")
|
||||
|
||||
err = createIfNotExists(toCreate, true)
|
||||
err = createIfNotExists(root, "tocreate", true)
|
||||
assert.NilError(t, err, "Should not fail if already exists")
|
||||
})
|
||||
t.Run("file", func(t *testing.T) {
|
||||
toCreate := filepath.Join(t.TempDir(), "file/to/create")
|
||||
dir := t.TempDir()
|
||||
root, err := os.OpenRoot(dir)
|
||||
assert.NilError(t, err)
|
||||
defer root.Close()
|
||||
|
||||
err := createIfNotExists(toCreate, false)
|
||||
err = createIfNotExists(root, "file/to/create", false)
|
||||
assert.NilError(t, err)
|
||||
|
||||
fileinfo, err := os.Stat(toCreate)
|
||||
fileinfo, err := os.Stat(filepath.Join(dir, "file/to/create"))
|
||||
assert.NilError(t, err, "Did not create destination")
|
||||
|
||||
assert.Assert(t, !fileinfo.IsDir(), "Should have been a file, but created a directory")
|
||||
|
||||
err = createIfNotExists(toCreate, true)
|
||||
err = createIfNotExists(root, "file/to/create", false)
|
||||
assert.NilError(t, err, "Should not fail if already exists")
|
||||
})
|
||||
}
|
||||
|
||||
@@ -98,14 +98,20 @@ func DeleteConntrackEntriesByPort(nlh nlwrap.Handle, proto types.Protocol, ports
|
||||
}).Warn("Failed to delete conntrack state for port")
|
||||
continue
|
||||
}
|
||||
if err := filter.AddIP(netlink.ConntrackOrigDstIP, port.HostIP); err != nil {
|
||||
log.G(context.TODO()).WithFields(log.Fields{
|
||||
"error": err,
|
||||
"hostIP": port.HostIP.String(),
|
||||
"proto": port.Proto.String(),
|
||||
"port": port.Port,
|
||||
}).Warn("Failed to delete conntrack state for port")
|
||||
continue
|
||||
// Only filter by destination IP when the host IP is specified. When
|
||||
// HostIP is 0.0.0.0 or ::, the binding applies to all interfaces, but
|
||||
// conntrack entries record the actual interface IP, so filtering by the
|
||||
// unspecified address would never match.
|
||||
if !port.HostIP.IsUnspecified() {
|
||||
if err := filter.AddIP(netlink.ConntrackOrigDstIP, port.HostIP); err != nil {
|
||||
log.G(context.TODO()).WithFields(log.Fields{
|
||||
"error": err,
|
||||
"hostIP": port.HostIP.String(),
|
||||
"proto": port.Proto.String(),
|
||||
"port": port.Port,
|
||||
}).Warn("Failed to delete conntrack state for port")
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
v4FlowPurged, err := nlh.ConntrackDeleteFilters(netlink.ConntrackTable, syscall.AF_INET, filter)
|
||||
|
||||
@@ -41,7 +41,7 @@ func getCgroupFromBuildOutput(buildOutput io.Reader) (string, error) {
|
||||
// Returns the container cgroup and daemon cgroup.
|
||||
func testBuildWithCgroupNs(ctx context.Context, t *testing.T, daemonNsMode string) (string, string) {
|
||||
d := daemon.New(t, daemon.WithDefaultCgroupNamespaceMode(daemonNsMode))
|
||||
d.StartWithBusybox(ctx, t)
|
||||
d.StartWithBusybox(ctx, t, "--iptables=false", "--ip6tables=false")
|
||||
defer d.Stop(t)
|
||||
|
||||
dockerfile := `
|
||||
@@ -72,6 +72,8 @@ func TestCgroupNamespacesBuild(t *testing.T) {
|
||||
skip.If(t, testEnv.IsRemoteDaemon())
|
||||
skip.If(t, !requirement.CgroupNamespacesEnabled())
|
||||
|
||||
t.Parallel()
|
||||
|
||||
ctx := testutil.StartSpan(baseContext, t)
|
||||
|
||||
// When the daemon defaults to private cgroup namespaces, containers launched
|
||||
@@ -85,6 +87,8 @@ func TestCgroupNamespacesBuildDaemonHostMode(t *testing.T) {
|
||||
skip.If(t, testEnv.IsRemoteDaemon())
|
||||
skip.If(t, !requirement.CgroupNamespacesEnabled())
|
||||
|
||||
t.Parallel()
|
||||
|
||||
ctx := testutil.StartSpan(baseContext, t)
|
||||
|
||||
// When the daemon defaults to host cgroup namespaces, containers
|
||||
|
||||
@@ -26,13 +26,15 @@ func TestCreateWithCDIDevices(t *testing.T) {
|
||||
skip.If(t, testEnv.DaemonInfo.OSType != "linux", "CDI devices are only supported on Linux")
|
||||
skip.If(t, testEnv.IsRemoteDaemon, "cannot run cdi tests with a remote daemon")
|
||||
|
||||
t.Parallel()
|
||||
|
||||
ctx := testutil.StartSpan(baseContext, t)
|
||||
|
||||
cwd, err := os.Getwd()
|
||||
assert.NilError(t, err)
|
||||
|
||||
d := daemon.New(t)
|
||||
d.StartWithBusybox(ctx, t, "--cdi-spec-dir="+filepath.Join(cwd, "testdata", "cdi"))
|
||||
d.StartWithBusybox(ctx, t, "--cdi-spec-dir="+filepath.Join(cwd, "testdata", "cdi"), "--iptables=false", "--ip6tables=false")
|
||||
defer d.Stop(t)
|
||||
|
||||
apiClient := d.NewClientT(t)
|
||||
@@ -74,6 +76,8 @@ func TestCDISpecDirsAreInSystemInfo(t *testing.T) {
|
||||
// TODO: This restriction can be relaxed with https://github.com/moby/moby/pull/46158
|
||||
skip.If(t, testEnv.IsRootless, "the t.TempDir test creates a folder with incorrect permissions for rootless")
|
||||
|
||||
t.Parallel()
|
||||
|
||||
testCases := []struct {
|
||||
description string
|
||||
config string
|
||||
@@ -146,6 +150,7 @@ func TestCDISpecDirsAreInSystemInfo(t *testing.T) {
|
||||
|
||||
args = append(args, "--config-file="+configPath)
|
||||
}
|
||||
args = append(args, "--iptables=false", "--ip6tables=false")
|
||||
d.Start(t, args...)
|
||||
defer d.Stop(t)
|
||||
|
||||
@@ -160,6 +165,8 @@ func TestCDIInfoDiscoveredDevices(t *testing.T) {
|
||||
skip.If(t, testEnv.IsRemoteDaemon, "cannot run daemon when remote daemon")
|
||||
skip.If(t, testEnv.DaemonInfo.OSType == "windows", "CDI not supported on Windows")
|
||||
|
||||
t.Parallel()
|
||||
|
||||
ctx := testutil.StartSpan(baseContext, t)
|
||||
|
||||
// Create a sample CDI spec file
|
||||
@@ -185,7 +192,7 @@ func TestCDIInfoDiscoveredDevices(t *testing.T) {
|
||||
assert.NilError(t, err, "Failed to write sample CDI spec file")
|
||||
|
||||
d := daemon.New(t)
|
||||
d.Start(t, "--feature", "cdi", "--cdi-spec-dir="+cdiDir)
|
||||
d.Start(t, "--feature", "cdi", "--cdi-spec-dir="+cdiDir, "--iptables=false", "--ip6tables=false")
|
||||
defer d.Stop(t)
|
||||
|
||||
c := d.NewClientT(t)
|
||||
|
||||
@@ -14,11 +14,13 @@ import (
|
||||
|
||||
cerrdefs "github.com/containerd/errdefs"
|
||||
"github.com/moby/go-archive"
|
||||
"github.com/moby/moby/api/types/build"
|
||||
buildtypes "github.com/moby/moby/api/types/build"
|
||||
"github.com/moby/moby/api/types/jsonstream"
|
||||
"github.com/moby/moby/client"
|
||||
"github.com/moby/moby/client/pkg/jsonmessage"
|
||||
"github.com/moby/moby/v2/integration/internal/build"
|
||||
"github.com/moby/moby/v2/integration/internal/container"
|
||||
"github.com/moby/moby/v2/internal/testutil/daemon"
|
||||
"github.com/moby/moby/v2/internal/testutil/fakecontext"
|
||||
"gotest.tools/v3/assert"
|
||||
is "gotest.tools/v3/assert/cmp"
|
||||
@@ -216,7 +218,7 @@ func makeTestImage(ctx context.Context, t *testing.T) (imageID string) {
|
||||
defer resp.Body.Close()
|
||||
|
||||
err = jsonmessage.DisplayStream(resp.Body, io.Discard, jsonmessage.WithAuxCallback(func(msg jsonstream.Message) {
|
||||
var r build.Result
|
||||
var r buildtypes.Result
|
||||
assert.NilError(t, json.Unmarshal(*msg.Aux, &r))
|
||||
imageID = r.ID
|
||||
}))
|
||||
@@ -291,7 +293,7 @@ func TestCopyFromContainer(t *testing.T) {
|
||||
|
||||
var imageID string
|
||||
err = jsonmessage.DisplayStream(resp.Body, io.Discard, jsonmessage.WithAuxCallback(func(msg jsonstream.Message) {
|
||||
var r build.Result
|
||||
var r buildtypes.Result
|
||||
assert.NilError(t, json.Unmarshal(*msg.Aux, &r))
|
||||
imageID = r.ID
|
||||
}))
|
||||
@@ -362,3 +364,76 @@ func TestCopyFromContainer(t *testing.T) {
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// TestCopyToContainerXZBinaryNotExecutedOnDaemon tests that when
|
||||
// uploading an xz-compressed archive to a container via the
|
||||
// PUT /containers/{id}/archive API, the daemon does NOT execute the xz
|
||||
// binary found inside the container's filesystem.
|
||||
// This is a regression test for
|
||||
// https://github.com/moby/moby/security/advisories/GHSA-x86f-5xw2-fm2r
|
||||
func TestCopyToContainerXZBinaryNotExecutedOnDaemon(t *testing.T) {
|
||||
skip.If(t, testEnv.DaemonInfo.OSType == "windows")
|
||||
skip.If(t, testEnv.IsRemoteDaemon, "cannot start daemon on remote test run")
|
||||
ctx := setupTest(t)
|
||||
|
||||
const tokenEnv = "MOBY_EXPLOIT_TEST_TOKEN"
|
||||
const tokenVal = "host-boundary-crossed"
|
||||
|
||||
d := daemon.New(t)
|
||||
defer d.Cleanup(t)
|
||||
d.SetEnvVar(tokenEnv, tokenVal)
|
||||
d.StartWithBusybox(ctx, t, "--iptables=false", "--ip6tables=false")
|
||||
defer d.Stop(t)
|
||||
|
||||
apiClient := d.NewClientT(t)
|
||||
|
||||
dir := t.TempDir()
|
||||
buildCtx := fakecontext.New(t, dir,
|
||||
// The fake xz writes the daemon's secret env var to a marker file.
|
||||
// A process inside the container would not have this env var.
|
||||
fakecontext.WithFile("fake-xz", "#!/bin/sh\necho $"+tokenEnv+" > /xz-was-executed\n"),
|
||||
fakecontext.WithDockerfile(`FROM busybox
|
||||
COPY fake-xz /usr/bin/xz
|
||||
RUN chmod +x /usr/bin/xz`),
|
||||
)
|
||||
defer buildCtx.Close()
|
||||
|
||||
imageID := build.Do(ctx, t, apiClient, buildCtx, client.ImageBuildOptions{})
|
||||
|
||||
cID := container.Run(ctx, t, apiClient, container.WithImage(imageID))
|
||||
defer container.Remove(ctx, t, apiClient, cID, client.ContainerRemoveOptions{Force: true})
|
||||
|
||||
// Craft a payload that starts with xz magic bytes so the daemon's
|
||||
// DecompressStream identifies it as xz-compressed. The payload is
|
||||
// deliberately invalid xz data; we only care whether the daemon
|
||||
// attempts to run the container's /usr/bin/xz to decompress it.
|
||||
xzMagic := []byte{0xFD, 0x37, 0x7A, 0x58, 0x5A, 0x00}
|
||||
payload := append(xzMagic, []byte("not-real-xz-data")...)
|
||||
|
||||
// CopyToContainer is expected to fail because the payload is not a
|
||||
// valid xz archive. We don't care about the error; we care about
|
||||
// whether the container's xz binary was invoked.
|
||||
_, _ = apiClient.CopyToContainer(ctx, cID, client.CopyToContainerOptions{
|
||||
DestinationPath: "/tmp",
|
||||
Content: bytes.NewReader(payload),
|
||||
})
|
||||
|
||||
t.Run("binary not executed", func(t *testing.T) {
|
||||
// If the container's /usr/bin/xz was executed, the marker file
|
||||
// will exist, and this assertion will fail.
|
||||
res, err := container.Exec(ctx, apiClient, cID, []string{"test", "-f", "/xz-was-executed"})
|
||||
assert.NilError(t, err)
|
||||
assert.Check(t, is.Equal(res.ExitCode, 1),
|
||||
"container's xz binary was executed by the daemon during archive extraction; marker file /xz-was-executed was created")
|
||||
})
|
||||
|
||||
t.Run("runs container", func(t *testing.T) {
|
||||
// If the binary ran, check that it ran in the daemon's process
|
||||
// context by looking for the daemon's secret env var in the
|
||||
// marker file. A container process would not have this env var.
|
||||
res, err := container.Exec(ctx, apiClient, cID, []string{"cat", "/xz-was-executed"})
|
||||
assert.NilError(t, err)
|
||||
assert.Check(t, !strings.Contains(res.Stdout(), tokenVal),
|
||||
"container's xz binary was executed in the daemon's process context: marker file contains the daemon's secret env var")
|
||||
})
|
||||
}
|
||||
|
||||
@@ -59,17 +59,19 @@ func TestExportContainerAfterDaemonRestart(t *testing.T) {
|
||||
skip.If(t, testEnv.DaemonInfo.OSType == "windows")
|
||||
skip.If(t, testEnv.IsRemoteDaemon)
|
||||
|
||||
t.Parallel()
|
||||
|
||||
ctx := testutil.StartSpan(baseContext, t)
|
||||
|
||||
d := daemon.New(t)
|
||||
c := d.NewClientT(t)
|
||||
|
||||
d.StartWithBusybox(ctx, t)
|
||||
d.StartWithBusybox(ctx, t, "--iptables=false", "--ip6tables=false")
|
||||
defer d.Stop(t)
|
||||
|
||||
ctrID := container.Create(ctx, t, c)
|
||||
|
||||
d.Restart(t)
|
||||
d.Restart(t, "--iptables=false", "--ip6tables=false")
|
||||
|
||||
res, err := c.ContainerExport(ctx, ctrID, client.ContainerExportOptions{})
|
||||
assert.NilError(t, err)
|
||||
|
||||
@@ -18,7 +18,7 @@ func testRunWithCgroupNs(ctx context.Context, t *testing.T, daemonNsMode string,
|
||||
d := daemon.New(t, daemon.WithDefaultCgroupNamespaceMode(daemonNsMode))
|
||||
apiClient := d.NewClientT(t)
|
||||
|
||||
d.StartWithBusybox(ctx, t)
|
||||
d.StartWithBusybox(ctx, t, "--iptables=false", "--ip6tables=false")
|
||||
defer d.Stop(t)
|
||||
|
||||
cID := container.Run(ctx, t, apiClient, containerOpts...)
|
||||
@@ -34,7 +34,7 @@ func testCreateFailureWithCgroupNs(ctx context.Context, t *testing.T, daemonNsMo
|
||||
d := daemon.New(t, daemon.WithDefaultCgroupNamespaceMode(daemonNsMode))
|
||||
apiClient := d.NewClientT(t)
|
||||
|
||||
d.StartWithBusybox(ctx, t)
|
||||
d.StartWithBusybox(ctx, t, "--iptables=false", "--ip6tables=false")
|
||||
defer d.Stop(t)
|
||||
_, err := container.CreateFromConfig(ctx, apiClient, container.NewTestConfig(containerOpts...))
|
||||
assert.ErrorContains(t, err, errStr)
|
||||
@@ -45,6 +45,8 @@ func TestCgroupNamespacesRun(t *testing.T) {
|
||||
skip.If(t, testEnv.IsRemoteDaemon())
|
||||
skip.If(t, !requirement.CgroupNamespacesEnabled())
|
||||
|
||||
t.Parallel()
|
||||
|
||||
ctx := testutil.StartSpan(baseContext, t)
|
||||
|
||||
// When the daemon defaults to private cgroup namespaces, containers launched
|
||||
@@ -59,6 +61,8 @@ func TestCgroupNamespacesRunPrivileged(t *testing.T) {
|
||||
skip.If(t, !requirement.CgroupNamespacesEnabled())
|
||||
skip.If(t, testEnv.DaemonInfo.CgroupVersion == "2", "on cgroup v2, privileged containers use private cgroupns")
|
||||
|
||||
t.Parallel()
|
||||
|
||||
ctx := testutil.StartSpan(baseContext, t)
|
||||
|
||||
// When the daemon defaults to private cgroup namespaces, privileged containers
|
||||
@@ -72,6 +76,8 @@ func TestCgroupNamespacesRunDaemonHostMode(t *testing.T) {
|
||||
skip.If(t, testEnv.IsRemoteDaemon())
|
||||
skip.If(t, !requirement.CgroupNamespacesEnabled())
|
||||
|
||||
t.Parallel()
|
||||
|
||||
ctx := testutil.StartSpan(baseContext, t)
|
||||
|
||||
// When the daemon defaults to host cgroup namespaces, containers
|
||||
@@ -85,6 +91,8 @@ func TestCgroupNamespacesRunHostMode(t *testing.T) {
|
||||
skip.If(t, testEnv.IsRemoteDaemon())
|
||||
skip.If(t, !requirement.CgroupNamespacesEnabled())
|
||||
|
||||
t.Parallel()
|
||||
|
||||
ctx := testutil.StartSpan(baseContext, t)
|
||||
|
||||
// When the daemon defaults to private cgroup namespaces, containers launched
|
||||
@@ -98,6 +106,8 @@ func TestCgroupNamespacesRunPrivateMode(t *testing.T) {
|
||||
skip.If(t, testEnv.IsRemoteDaemon())
|
||||
skip.If(t, !requirement.CgroupNamespacesEnabled())
|
||||
|
||||
t.Parallel()
|
||||
|
||||
ctx := testutil.StartSpan(baseContext, t)
|
||||
|
||||
// When the daemon defaults to private cgroup namespaces, containers launched
|
||||
@@ -111,6 +121,8 @@ func TestCgroupNamespacesRunPrivilegedAndPrivate(t *testing.T) {
|
||||
skip.If(t, testEnv.IsRemoteDaemon())
|
||||
skip.If(t, !requirement.CgroupNamespacesEnabled())
|
||||
|
||||
t.Parallel()
|
||||
|
||||
ctx := testutil.StartSpan(baseContext, t)
|
||||
|
||||
containerCgroup, daemonCgroup := testRunWithCgroupNs(ctx, t, "private", container.WithPrivileged(true), container.WithCgroupnsMode("private"))
|
||||
@@ -122,6 +134,8 @@ func TestCgroupNamespacesRunInvalidMode(t *testing.T) {
|
||||
skip.If(t, testEnv.IsRemoteDaemon())
|
||||
skip.If(t, !requirement.CgroupNamespacesEnabled())
|
||||
|
||||
t.Parallel()
|
||||
|
||||
ctx := testutil.StartSpan(baseContext, t)
|
||||
|
||||
// An invalid cgroup namespace mode should return an error on container creation
|
||||
@@ -136,12 +150,14 @@ func TestCgroupNamespacesRunOlderClient(t *testing.T) {
|
||||
skip.If(t, testEnv.IsRemoteDaemon())
|
||||
skip.If(t, !requirement.CgroupNamespacesEnabled())
|
||||
|
||||
t.Parallel()
|
||||
|
||||
ctx := testutil.StartSpan(baseContext, t)
|
||||
|
||||
d := daemon.New(t, daemon.WithEnvVars("DOCKER_MIN_API_VERSION=1.39"), daemon.WithDefaultCgroupNamespaceMode("private"))
|
||||
apiClient := d.NewClientT(t, client.WithAPIVersion("1.39"))
|
||||
|
||||
d.StartWithBusybox(ctx, t)
|
||||
d.StartWithBusybox(ctx, t, "--iptables=false", "--ip6tables=false")
|
||||
defer d.Stop(t)
|
||||
|
||||
cID := container.Run(ctx, t, apiClient)
|
||||
|
||||
@@ -23,6 +23,8 @@ func TestNRIContainerCreateEnvVarMod(t *testing.T) {
|
||||
skip.If(t, testEnv.DaemonInfo.OSType == "windows", "cannot start a separate daemon with NRI enabled on Windows")
|
||||
skip.If(t, testEnv.IsRootless)
|
||||
|
||||
t.Parallel()
|
||||
|
||||
ctx := testutil.StartSpan(baseContext, t)
|
||||
|
||||
sockPath := filepath.Join(t.TempDir(), "nri.sock")
|
||||
@@ -78,6 +80,8 @@ func TestNRIContainerCreateUnsupportedAdj(t *testing.T) {
|
||||
skip.If(t, testEnv.DaemonInfo.OSType == "windows", "cannot start a separate daemon with NRI enabled on Windows")
|
||||
skip.If(t, testEnv.IsRootless)
|
||||
|
||||
t.Parallel()
|
||||
|
||||
ctx := testutil.StartSpan(baseContext, t)
|
||||
|
||||
sockPath := filepath.Join(t.TempDir(), "nri.sock")
|
||||
@@ -138,6 +142,8 @@ func TestNRIContainerCreateAddMount(t *testing.T) {
|
||||
skip.If(t, testEnv.DaemonInfo.OSType == "windows", "cannot start a separate daemon with NRI enabled on Windows")
|
||||
skip.If(t, testEnv.IsRootless)
|
||||
|
||||
t.Parallel()
|
||||
|
||||
ctx := testutil.StartSpan(baseContext, t)
|
||||
|
||||
sockPath := filepath.Join(t.TempDir(), "nri.sock")
|
||||
@@ -258,6 +264,8 @@ func TestNRIReload(t *testing.T) {
|
||||
skip.If(t, testEnv.DaemonInfo.OSType == "windows", "cannot start a separate daemon with NRI enabled on Windows")
|
||||
skip.If(t, testEnv.IsRootless)
|
||||
|
||||
t.Parallel()
|
||||
|
||||
ctx := testutil.StartSpan(baseContext, t)
|
||||
|
||||
const pluginName = "00-nri-test-plugin"
|
||||
|
||||
@@ -56,9 +56,11 @@ func TestUsernsCommit(t *testing.T) {
|
||||
skip.If(t, !testEnv.IsUserNamespaceInKernel())
|
||||
skip.If(t, testEnv.IsRootless())
|
||||
|
||||
t.Parallel()
|
||||
|
||||
ctx := context.Background()
|
||||
dUserRemap := daemon.New(t, daemon.WithUserNsRemap("default"))
|
||||
dUserRemap.StartWithBusybox(ctx, t)
|
||||
dUserRemap.StartWithBusybox(ctx, t, "--iptables=false", "--ip6tables=false")
|
||||
clientUserRemap := dUserRemap.NewClientT(t)
|
||||
defer clientUserRemap.Close()
|
||||
|
||||
|
||||
@@ -24,6 +24,8 @@ func TestDiskUsageConcurrentPrune(t *testing.T) {
|
||||
skip.If(t, testEnv.IsRemoteDaemon, "cannot run daemon when remote daemon")
|
||||
skip.If(t, !testEnv.UsingSnapshotter(), "only happens with containerd image store")
|
||||
|
||||
t.Parallel()
|
||||
|
||||
ctx := testutil.StartSpan(baseContext, t)
|
||||
|
||||
d := daemon.New(t)
|
||||
|
||||
@@ -18,6 +18,8 @@ import (
|
||||
func TestDiskUsage(t *testing.T) {
|
||||
skip.If(t, testEnv.DaemonInfo.OSType == "windows") // d.Start fails on Windows with `protocol not available`
|
||||
|
||||
t.Parallel()
|
||||
|
||||
ctx := testutil.StartSpan(baseContext, t)
|
||||
|
||||
d := daemon.New(t)
|
||||
|
||||
@@ -45,12 +45,14 @@ func TestInfoAPIWarnings(t *testing.T) {
|
||||
skip.If(t, testEnv.IsRemoteDaemon, "cannot run daemon when remote daemon")
|
||||
skip.If(t, testEnv.DaemonInfo.OSType == "windows", "FIXME")
|
||||
|
||||
t.Parallel()
|
||||
|
||||
ctx := testutil.StartSpan(baseContext, t)
|
||||
|
||||
d := daemon.New(t)
|
||||
c := d.NewClientT(t)
|
||||
|
||||
d.Start(t, "-H=0.0.0.0:23756", "-H="+d.Sock())
|
||||
d.Start(t, "-H=0.0.0.0:23756", "-H="+d.Sock(), "--iptables=false", "--ip6tables=false")
|
||||
defer d.Stop(t)
|
||||
|
||||
result, err := c.Info(ctx, client.InfoOptions{})
|
||||
@@ -73,10 +75,12 @@ func TestInfoDebug(t *testing.T) {
|
||||
skip.If(t, testEnv.IsRemoteDaemon, "cannot run daemon when remote daemon")
|
||||
skip.If(t, testEnv.DaemonInfo.OSType == "windows", "FIXME: test starts daemon with -H unix://.....")
|
||||
|
||||
t.Parallel()
|
||||
|
||||
_ = testutil.StartSpan(baseContext, t)
|
||||
|
||||
d := daemon.New(t)
|
||||
d.Start(t, "--debug")
|
||||
d.Start(t, "--debug", "--iptables=false", "--ip6tables=false")
|
||||
defer d.Stop(t)
|
||||
|
||||
info := d.Info(t)
|
||||
@@ -95,13 +99,15 @@ func TestInfoInsecureRegistries(t *testing.T) {
|
||||
skip.If(t, testEnv.IsRemoteDaemon, "cannot run daemon when remote daemon")
|
||||
skip.If(t, testEnv.DaemonInfo.OSType == "windows", "FIXME: test starts daemon with -H unix://.....")
|
||||
|
||||
t.Parallel()
|
||||
|
||||
const (
|
||||
registryCIDR = "192.168.1.0/24"
|
||||
registryHost = "insecurehost.com:5000"
|
||||
)
|
||||
|
||||
d := daemon.New(t)
|
||||
d.Start(t, "--insecure-registry="+registryCIDR, "--insecure-registry="+registryHost)
|
||||
d.Start(t, "--insecure-registry="+registryCIDR, "--insecure-registry="+registryHost, "--iptables=false", "--ip6tables=false")
|
||||
defer d.Stop(t)
|
||||
|
||||
info := d.Info(t)
|
||||
@@ -122,13 +128,15 @@ func TestInfoRegistryMirrors(t *testing.T) {
|
||||
skip.If(t, testEnv.IsRemoteDaemon, "cannot run daemon when remote daemon")
|
||||
skip.If(t, testEnv.DaemonInfo.OSType == "windows", "FIXME: test starts daemon with -H unix://.....")
|
||||
|
||||
t.Parallel()
|
||||
|
||||
const (
|
||||
registryMirror1 = "https://192.168.1.2"
|
||||
registryMirror2 = "http://registry-mirror.example.com:5000"
|
||||
)
|
||||
|
||||
d := daemon.New(t)
|
||||
d.Start(t, "--registry-mirror="+registryMirror1, "--registry-mirror="+registryMirror2)
|
||||
d.Start(t, "--registry-mirror="+registryMirror1, "--registry-mirror="+registryMirror2, "--iptables=false", "--ip6tables=false")
|
||||
defer d.Stop(t)
|
||||
|
||||
info := d.Info(t)
|
||||
|
||||
@@ -101,14 +101,16 @@ func TestPingBuilderHeader(t *testing.T) {
|
||||
skip.If(t, testEnv.IsRemoteDaemon)
|
||||
skip.If(t, testEnv.DaemonInfo.OSType == "windows", "cannot spin up additional daemons on windows")
|
||||
|
||||
ctx := setupTest(t)
|
||||
t.Parallel()
|
||||
|
||||
ctx := testutil.StartSpan(baseContext, t)
|
||||
d := daemon.New(t)
|
||||
apiClient := d.NewClientT(t)
|
||||
defer apiClient.Close()
|
||||
|
||||
t.Run("default config", func(t *testing.T) {
|
||||
testutil.StartSpan(ctx, t)
|
||||
d.Start(t)
|
||||
d.Start(t, "--iptables=false", "--ip6tables=false")
|
||||
defer d.Stop(t)
|
||||
|
||||
expected := build.BuilderBuildKit
|
||||
@@ -126,7 +128,7 @@ func TestPingBuilderHeader(t *testing.T) {
|
||||
cfg := filepath.Join(d.RootDir(), "daemon.json")
|
||||
err := os.WriteFile(cfg, []byte(`{"features": { "buildkit": false }}`), 0o644)
|
||||
assert.NilError(t, err)
|
||||
d.Start(t, "--config-file", cfg)
|
||||
d.Start(t, "--config-file", cfg, "--iptables=false", "--ip6tables=false")
|
||||
defer d.Stop(t)
|
||||
|
||||
expected := build.BuilderV1
|
||||
|
||||
@@ -436,6 +436,8 @@ func TestRunMountImageSubpathDaemonRestart(t *testing.T) {
|
||||
skip.If(t, testEnv.IsRootless, "FIXME: https://github.com/moby/moby/issues/50999")
|
||||
skip.If(t, !testEnv.UsingSnapshotter(), "FIXME: https://github.com/moby/moby/issues/50999")
|
||||
|
||||
t.Parallel()
|
||||
|
||||
ctx := testutil.StartSpan(baseContext, t)
|
||||
|
||||
d := daemon.New(t)
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
---
|
||||
docker:
|
||||
current: "29.4.1"
|
||||
next: "29.5.0"
|
||||
current: "29.5.1"
|
||||
next: "29.5.2"
|
||||
|
||||
Reference in New Issue
Block a user