github.com/moby/docker@v26.1.3+incompatible/daemon/oci_linux_test.go (about)

     1  package daemon // import "github.com/docker/docker/daemon"
     2  
     3  import (
     4  	"context"
     5  	"os"
     6  	"path/filepath"
     7  	"testing"
     8  
     9  	containertypes "github.com/docker/docker/api/types/container"
    10  	"github.com/docker/docker/container"
    11  	"github.com/docker/docker/daemon/config"
    12  	"github.com/docker/docker/daemon/network"
    13  	"github.com/docker/docker/libnetwork"
    14  	nwconfig "github.com/docker/docker/libnetwork/config"
    15  	"github.com/google/go-cmp/cmp/cmpopts"
    16  	"github.com/opencontainers/runtime-spec/specs-go"
    17  	"golang.org/x/sys/unix"
    18  	"gotest.tools/v3/assert"
    19  	is "gotest.tools/v3/assert/cmp"
    20  	"gotest.tools/v3/skip"
    21  )
    22  
    23  func setupFakeDaemon(t *testing.T, c *container.Container) *Daemon {
    24  	t.Helper()
    25  	root := t.TempDir()
    26  
    27  	rootfs := filepath.Join(root, "rootfs")
    28  	err := os.MkdirAll(rootfs, 0o755)
    29  	assert.NilError(t, err)
    30  
    31  	netController, err := libnetwork.New(nwconfig.OptionDataDir(t.TempDir()))
    32  	assert.NilError(t, err)
    33  
    34  	d := &Daemon{
    35  		// some empty structs to avoid getting a panic
    36  		// caused by a null pointer dereference
    37  		linkIndex:     newLinkIndex(),
    38  		netController: netController,
    39  		imageService:  &fakeImageService{},
    40  	}
    41  
    42  	c.Root = root
    43  	c.BaseFS = rootfs
    44  
    45  	if c.Config == nil {
    46  		c.Config = new(containertypes.Config)
    47  	}
    48  	if c.HostConfig == nil {
    49  		c.HostConfig = new(containertypes.HostConfig)
    50  	}
    51  	if c.NetworkSettings == nil {
    52  		c.NetworkSettings = &network.Settings{Networks: make(map[string]*network.EndpointSettings)}
    53  	}
    54  
    55  	// HORRIBLE HACK: clean up shm mounts leaked by some tests. Otherwise the
    56  	// offending tests would fail due to the mounts blocking the temporary
    57  	// directory from being cleaned up.
    58  	t.Cleanup(func() {
    59  		if c.ShmPath != "" {
    60  			var err error
    61  			for err == nil { // Some tests over-mount over the same path multiple times.
    62  				err = unix.Unmount(c.ShmPath, unix.MNT_DETACH)
    63  			}
    64  		}
    65  	})
    66  
    67  	return d
    68  }
    69  
    70  type fakeImageService struct {
    71  	ImageService
    72  }
    73  
    74  func (i *fakeImageService) StorageDriver() string {
    75  	return "overlay"
    76  }
    77  
    78  // TestTmpfsDevShmNoDupMount checks that a user-specified /dev/shm tmpfs
    79  // mount (as in "docker run --tmpfs /dev/shm:rw,size=NNN") does not result
    80  // in "Duplicate mount point" error from the engine.
    81  // https://github.com/moby/moby/issues/35455
    82  func TestTmpfsDevShmNoDupMount(t *testing.T) {
    83  	skip.If(t, os.Getuid() != 0, "skipping test that requires root")
    84  	c := &container.Container{
    85  		ShmPath: "foobar", // non-empty, for c.IpcMounts() to work
    86  		HostConfig: &containertypes.HostConfig{
    87  			IpcMode: containertypes.IPCModeShareable, // default mode
    88  			// --tmpfs /dev/shm:rw,exec,size=NNN
    89  			Tmpfs: map[string]string{
    90  				"/dev/shm": "rw,exec,size=1g",
    91  			},
    92  		},
    93  	}
    94  	d := setupFakeDaemon(t, c)
    95  
    96  	_, err := d.createSpec(context.TODO(), &configStore{}, c, nil)
    97  	assert.Check(t, err)
    98  }
    99  
   100  // TestIpcPrivateVsReadonly checks that in case of IpcMode: private
   101  // and ReadonlyRootfs: true (as in "docker run --ipc private --read-only")
   102  // the resulting /dev/shm mount is NOT made read-only.
   103  // https://github.com/moby/moby/issues/36503
   104  func TestIpcPrivateVsReadonly(t *testing.T) {
   105  	skip.If(t, os.Getuid() != 0, "skipping test that requires root")
   106  	c := &container.Container{
   107  		HostConfig: &containertypes.HostConfig{
   108  			IpcMode:        containertypes.IPCModePrivate,
   109  			ReadonlyRootfs: true,
   110  		},
   111  	}
   112  	d := setupFakeDaemon(t, c)
   113  
   114  	s, err := d.createSpec(context.TODO(), &configStore{}, c, nil)
   115  	assert.Check(t, err)
   116  
   117  	// Find the /dev/shm mount in ms, check it does not have ro
   118  	for _, m := range s.Mounts {
   119  		if m.Destination != "/dev/shm" {
   120  			continue
   121  		}
   122  		assert.Check(t, is.Equal(false, inSlice(m.Options, "ro")))
   123  	}
   124  }
   125  
   126  // TestSysctlOverride ensures that any implicit sysctls (such as
   127  // Config.Domainname) are overridden by an explicit sysctl in the HostConfig.
   128  func TestSysctlOverride(t *testing.T) {
   129  	skip.If(t, os.Getuid() != 0, "skipping test that requires root")
   130  	c := &container.Container{
   131  		Config: &containertypes.Config{
   132  			Hostname:   "foobar",
   133  			Domainname: "baz.cyphar.com",
   134  		},
   135  		HostConfig: &containertypes.HostConfig{
   136  			NetworkMode: "bridge",
   137  			Sysctls:     map[string]string{},
   138  		},
   139  	}
   140  	d := setupFakeDaemon(t, c)
   141  
   142  	// Ensure that the implicit sysctl is set correctly.
   143  	s, err := d.createSpec(context.TODO(), &configStore{}, c, nil)
   144  	assert.NilError(t, err)
   145  	assert.Equal(t, s.Hostname, "foobar")
   146  	assert.Equal(t, s.Linux.Sysctl["kernel.domainname"], c.Config.Domainname)
   147  	if sysctlExists("net.ipv4.ip_unprivileged_port_start") {
   148  		assert.Equal(t, s.Linux.Sysctl["net.ipv4.ip_unprivileged_port_start"], "0")
   149  	}
   150  	if sysctlExists("net.ipv4.ping_group_range") {
   151  		assert.Equal(t, s.Linux.Sysctl["net.ipv4.ping_group_range"], "0 2147483647")
   152  	}
   153  
   154  	// Set an explicit sysctl.
   155  	c.HostConfig.Sysctls["kernel.domainname"] = "foobar.net"
   156  	assert.Assert(t, c.HostConfig.Sysctls["kernel.domainname"] != c.Config.Domainname)
   157  	c.HostConfig.Sysctls["net.ipv4.ip_unprivileged_port_start"] = "1024"
   158  
   159  	s, err = d.createSpec(context.TODO(), &configStore{}, c, nil)
   160  	assert.NilError(t, err)
   161  	assert.Equal(t, s.Hostname, "foobar")
   162  	assert.Equal(t, s.Linux.Sysctl["kernel.domainname"], c.HostConfig.Sysctls["kernel.domainname"])
   163  	assert.Equal(t, s.Linux.Sysctl["net.ipv4.ip_unprivileged_port_start"], c.HostConfig.Sysctls["net.ipv4.ip_unprivileged_port_start"])
   164  
   165  	// Ensure the ping_group_range is not set on a daemon with user-namespaces enabled
   166  	s, err = d.createSpec(context.TODO(), &configStore{Config: config.Config{RemappedRoot: "dummy:dummy"}}, c, nil)
   167  	assert.NilError(t, err)
   168  	_, ok := s.Linux.Sysctl["net.ipv4.ping_group_range"]
   169  	assert.Assert(t, !ok)
   170  
   171  	// Ensure the ping_group_range is set on a container in "host" userns mode
   172  	// on a daemon with user-namespaces enabled
   173  	c.HostConfig.UsernsMode = "host"
   174  	s, err = d.createSpec(context.TODO(), &configStore{Config: config.Config{RemappedRoot: "dummy:dummy"}}, c, nil)
   175  	assert.NilError(t, err)
   176  	assert.Equal(t, s.Linux.Sysctl["net.ipv4.ping_group_range"], "0 2147483647")
   177  }
   178  
   179  // TestSysctlOverrideHost ensures that any implicit network sysctls are not set
   180  // with host networking
   181  func TestSysctlOverrideHost(t *testing.T) {
   182  	skip.If(t, os.Getuid() != 0, "skipping test that requires root")
   183  	c := &container.Container{
   184  		Config: &containertypes.Config{},
   185  		HostConfig: &containertypes.HostConfig{
   186  			NetworkMode: "host",
   187  			Sysctls:     map[string]string{},
   188  		},
   189  	}
   190  	d := setupFakeDaemon(t, c)
   191  
   192  	// Ensure that the implicit sysctl is not set
   193  	s, err := d.createSpec(context.TODO(), &configStore{}, c, nil)
   194  	assert.NilError(t, err)
   195  	assert.Equal(t, s.Linux.Sysctl["net.ipv4.ip_unprivileged_port_start"], "")
   196  	assert.Equal(t, s.Linux.Sysctl["net.ipv4.ping_group_range"], "")
   197  
   198  	// Set an explicit sysctl.
   199  	c.HostConfig.Sysctls["net.ipv4.ip_unprivileged_port_start"] = "1024"
   200  
   201  	s, err = d.createSpec(context.TODO(), &configStore{}, c, nil)
   202  	assert.NilError(t, err)
   203  	assert.Equal(t, s.Linux.Sysctl["net.ipv4.ip_unprivileged_port_start"], c.HostConfig.Sysctls["net.ipv4.ip_unprivileged_port_start"])
   204  }
   205  
   206  func TestGetSourceMount(t *testing.T) {
   207  	// must be able to find source mount for /
   208  	mnt, _, err := getSourceMount("/")
   209  	assert.NilError(t, err)
   210  	assert.Equal(t, mnt, "/")
   211  
   212  	// must be able to find source mount for current directory
   213  	cwd, err := os.Getwd()
   214  	assert.NilError(t, err)
   215  	_, _, err = getSourceMount(cwd)
   216  	assert.NilError(t, err)
   217  }
   218  
   219  func TestDefaultResources(t *testing.T) {
   220  	skip.If(t, os.Getuid() != 0, "skipping test that requires root") // TODO: is this actually true? I'm guilty of following the cargo cult here.
   221  
   222  	c := &container.Container{
   223  		HostConfig: &containertypes.HostConfig{
   224  			IpcMode: containertypes.IPCModeNone,
   225  		},
   226  	}
   227  	d := setupFakeDaemon(t, c)
   228  
   229  	s, err := d.createSpec(context.Background(), &configStore{}, c, nil)
   230  	assert.NilError(t, err)
   231  	checkResourcesAreUnset(t, s.Linux.Resources)
   232  }
   233  
   234  func checkResourcesAreUnset(t *testing.T, r *specs.LinuxResources) {
   235  	t.Helper()
   236  
   237  	if r != nil {
   238  		if r.Memory != nil {
   239  			assert.Check(t, is.DeepEqual(r.Memory, &specs.LinuxMemory{}))
   240  		}
   241  		if r.CPU != nil {
   242  			assert.Check(t, is.DeepEqual(r.CPU, &specs.LinuxCPU{}))
   243  		}
   244  		assert.Check(t, is.Nil(r.Pids))
   245  		if r.BlockIO != nil {
   246  			assert.Check(t, is.DeepEqual(r.BlockIO, &specs.LinuxBlockIO{}, cmpopts.EquateEmpty()))
   247  		}
   248  		if r.Network != nil {
   249  			assert.Check(t, is.DeepEqual(r.Network, &specs.LinuxNetwork{}, cmpopts.EquateEmpty()))
   250  		}
   251  	}
   252  }