moby/daemon/oci_linux_test.go
Cory Snider 098a44c07f Finish refactor of UID/GID usage to a new struct
Finish the refactor which was partially completed with commit
34536c498d, passing around IdentityMapping structs instead of pairs of
[]IDMap slices.

Existing code which uses []IDMap relies on zero-valued fields to be
valid, empty mappings. So in order to successfully finish the
refactoring without introducing bugs, their replacement therefore also
needs to have a useful zero value which represents an empty mapping.
Change IdentityMapping to be a pass-by-value type so that there are no
nil pointers to worry about.

The functionality provided by the deprecated NewIDMappingsFromMaps
function is required by unit tests to to construct arbitrary
IdentityMapping values. And the daemon will always need to access the
mappings to pass them to the Linux kernel. Accommodate these use cases
by exporting the struct fields instead. BuildKit currently depends on
the UIDs and GIDs methods so we cannot get rid of them yet.

Signed-off-by: Cory Snider <csnider@mirantis.com>
2022-03-14 16:28:57 -04:00

202 lines
6.3 KiB
Go

package daemon // import "github.com/docker/docker/daemon"
import (
"os"
"path/filepath"
"testing"
containertypes "github.com/docker/docker/api/types/container"
"github.com/docker/docker/container"
"github.com/docker/docker/daemon/config"
"github.com/docker/docker/daemon/network"
"github.com/docker/docker/libnetwork"
"github.com/docker/docker/pkg/containerfs"
"gotest.tools/v3/assert"
is "gotest.tools/v3/assert/cmp"
"gotest.tools/v3/skip"
)
func setupFakeDaemon(t *testing.T, c *container.Container) *Daemon {
root, err := os.MkdirTemp("", "oci_linux_test-root")
assert.NilError(t, err)
rootfs := filepath.Join(root, "rootfs")
err = os.MkdirAll(rootfs, 0755)
assert.NilError(t, err)
netController, err := libnetwork.New()
assert.NilError(t, err)
d := &Daemon{
// some empty structs to avoid getting a panic
// caused by a null pointer dereference
configStore: &config.Config{},
linkIndex: newLinkIndex(),
netController: netController,
}
c.Root = root
c.BaseFS = containerfs.NewLocalContainerFS(rootfs)
if c.Config == nil {
c.Config = new(containertypes.Config)
}
if c.HostConfig == nil {
c.HostConfig = new(containertypes.HostConfig)
}
if c.NetworkSettings == nil {
c.NetworkSettings = &network.Settings{Networks: make(map[string]*network.EndpointSettings)}
}
return d
}
func cleanupFakeContainer(c *container.Container) {
_ = os.RemoveAll(c.Root)
}
// TestTmpfsDevShmNoDupMount checks that a user-specified /dev/shm tmpfs
// mount (as in "docker run --tmpfs /dev/shm:rw,size=NNN") does not result
// in "Duplicate mount point" error from the engine.
// https://github.com/moby/moby/issues/35455
func TestTmpfsDevShmNoDupMount(t *testing.T) {
skip.If(t, os.Getuid() != 0, "skipping test that requires root")
c := &container.Container{
ShmPath: "foobar", // non-empty, for c.IpcMounts() to work
HostConfig: &containertypes.HostConfig{
IpcMode: containertypes.IPCModeShareable, // default mode
// --tmpfs /dev/shm:rw,exec,size=NNN
Tmpfs: map[string]string{
"/dev/shm": "rw,exec,size=1g",
},
},
}
d := setupFakeDaemon(t, c)
defer cleanupFakeContainer(c)
_, err := d.createSpec(c)
assert.Check(t, err)
}
// TestIpcPrivateVsReadonly checks that in case of IpcMode: private
// and ReadonlyRootfs: true (as in "docker run --ipc private --read-only")
// the resulting /dev/shm mount is NOT made read-only.
// https://github.com/moby/moby/issues/36503
func TestIpcPrivateVsReadonly(t *testing.T) {
skip.If(t, os.Getuid() != 0, "skipping test that requires root")
c := &container.Container{
HostConfig: &containertypes.HostConfig{
IpcMode: containertypes.IPCModePrivate,
ReadonlyRootfs: true,
},
}
d := setupFakeDaemon(t, c)
defer cleanupFakeContainer(c)
s, err := d.createSpec(c)
assert.Check(t, err)
// Find the /dev/shm mount in ms, check it does not have ro
for _, m := range s.Mounts {
if m.Destination != "/dev/shm" {
continue
}
assert.Check(t, is.Equal(false, inSlice(m.Options, "ro")))
}
}
// TestSysctlOverride ensures that any implicit sysctls (such as
// Config.Domainname) are overridden by an explicit sysctl in the HostConfig.
func TestSysctlOverride(t *testing.T) {
skip.If(t, os.Getuid() != 0, "skipping test that requires root")
c := &container.Container{
Config: &containertypes.Config{
Hostname: "foobar",
Domainname: "baz.cyphar.com",
},
HostConfig: &containertypes.HostConfig{
NetworkMode: "bridge",
Sysctls: map[string]string{},
},
}
d := setupFakeDaemon(t, c)
defer cleanupFakeContainer(c)
// Ensure that the implicit sysctl is set correctly.
s, err := d.createSpec(c)
assert.NilError(t, err)
assert.Equal(t, s.Hostname, "foobar")
assert.Equal(t, s.Linux.Sysctl["kernel.domainname"], c.Config.Domainname)
if sysctlExists("net.ipv4.ip_unprivileged_port_start") {
assert.Equal(t, s.Linux.Sysctl["net.ipv4.ip_unprivileged_port_start"], "0")
}
if sysctlExists("net.ipv4.ping_group_range") {
assert.Equal(t, s.Linux.Sysctl["net.ipv4.ping_group_range"], "0 2147483647")
}
// Set an explicit sysctl.
c.HostConfig.Sysctls["kernel.domainname"] = "foobar.net"
assert.Assert(t, c.HostConfig.Sysctls["kernel.domainname"] != c.Config.Domainname)
c.HostConfig.Sysctls["net.ipv4.ip_unprivileged_port_start"] = "1024"
s, err = d.createSpec(c)
assert.NilError(t, err)
assert.Equal(t, s.Hostname, "foobar")
assert.Equal(t, s.Linux.Sysctl["kernel.domainname"], c.HostConfig.Sysctls["kernel.domainname"])
assert.Equal(t, s.Linux.Sysctl["net.ipv4.ip_unprivileged_port_start"], c.HostConfig.Sysctls["net.ipv4.ip_unprivileged_port_start"])
// Ensure the ping_group_range is not set on a daemon with user-namespaces enabled
d.configStore.RemappedRoot = "dummy:dummy"
s, err = d.createSpec(c)
assert.NilError(t, err)
_, ok := s.Linux.Sysctl["net.ipv4.ping_group_range"]
assert.Assert(t, !ok)
// Ensure the ping_group_range is set on a container in "host" userns mode
// on a daemon with user-namespaces enabled
c.HostConfig.UsernsMode = "host"
s, err = d.createSpec(c)
assert.NilError(t, err)
assert.Equal(t, s.Linux.Sysctl["net.ipv4.ping_group_range"], "0 2147483647")
}
// TestSysctlOverrideHost ensures that any implicit network sysctls are not set
// with host networking
func TestSysctlOverrideHost(t *testing.T) {
skip.If(t, os.Getuid() != 0, "skipping test that requires root")
c := &container.Container{
Config: &containertypes.Config{},
HostConfig: &containertypes.HostConfig{
NetworkMode: "host",
Sysctls: map[string]string{},
},
}
d := setupFakeDaemon(t, c)
defer cleanupFakeContainer(c)
// Ensure that the implicit sysctl is not set
s, err := d.createSpec(c)
assert.NilError(t, err)
assert.Equal(t, s.Linux.Sysctl["net.ipv4.ip_unprivileged_port_start"], "")
assert.Equal(t, s.Linux.Sysctl["net.ipv4.ping_group_range"], "")
// Set an explicit sysctl.
c.HostConfig.Sysctls["net.ipv4.ip_unprivileged_port_start"] = "1024"
s, err = d.createSpec(c)
assert.NilError(t, err)
assert.Equal(t, s.Linux.Sysctl["net.ipv4.ip_unprivileged_port_start"], c.HostConfig.Sysctls["net.ipv4.ip_unprivileged_port_start"])
}
func TestGetSourceMount(t *testing.T) {
// must be able to find source mount for /
mnt, _, err := getSourceMount("/")
assert.NilError(t, err)
assert.Equal(t, mnt, "/")
// must be able to find source mount for current directory
cwd, err := os.Getwd()
assert.NilError(t, err)
_, _, err = getSourceMount(cwd)
assert.NilError(t, err)
}