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 }