github.com/docker/engine@v22.0.0-20211208180946-d456264580cf+incompatible/integration-cli/docker_cli_userns_test.go (about) 1 //go:build !windows 2 // +build !windows 3 4 package main 5 6 import ( 7 "fmt" 8 "os" 9 "os/exec" 10 "path" 11 "path/filepath" 12 "strconv" 13 "strings" 14 "testing" 15 16 "github.com/docker/docker/pkg/stringid" 17 "github.com/docker/docker/pkg/system" 18 "gotest.tools/v3/assert" 19 ) 20 21 // user namespaces test: run daemon with remapped root setting 22 // 1. validate uid/gid maps are set properly 23 // 2. verify that files created are owned by remapped root 24 func (s *DockerDaemonSuite) TestDaemonUserNamespaceRootSetting(c *testing.T) { 25 testRequires(c, UserNamespaceInKernel) 26 27 s.d.StartWithBusybox(c, "--userns-remap", "default") 28 29 tmpDir, err := os.MkdirTemp("", "userns") 30 assert.NilError(c, err) 31 32 defer os.RemoveAll(tmpDir) 33 34 // Set a non-existent path 35 tmpDirNotExists := path.Join(os.TempDir(), "userns"+stringid.GenerateRandomID()) 36 defer os.RemoveAll(tmpDirNotExists) 37 38 // we need to find the uid and gid of the remapped root from the daemon's root dir info 39 uidgid := strings.Split(filepath.Base(s.d.Root), ".") 40 assert.Equal(c, len(uidgid), 2, fmt.Sprintf("Should have gotten uid/gid strings from root dirname: %s", filepath.Base(s.d.Root))) 41 uid, err := strconv.Atoi(uidgid[0]) 42 assert.NilError(c, err, "Can't parse uid") 43 gid, err := strconv.Atoi(uidgid[1]) 44 assert.NilError(c, err, "Can't parse gid") 45 46 // writable by the remapped root UID/GID pair 47 assert.NilError(c, os.Chown(tmpDir, uid, gid)) 48 49 out, err := s.d.Cmd("run", "-d", "--name", "userns", "-v", tmpDir+":/goofy", "-v", tmpDirNotExists+":/donald", "busybox", "sh", "-c", "touch /goofy/testfile; exec top") 50 assert.NilError(c, err, "Output: %s", out) 51 52 user := s.findUser(c, "userns") 53 assert.Equal(c, uidgid[0], user) 54 55 // check that the created directory is owned by remapped uid:gid 56 statNotExists, err := system.Stat(tmpDirNotExists) 57 assert.NilError(c, err) 58 assert.Equal(c, statNotExists.UID(), uint32(uid), "Created directory not owned by remapped root UID") 59 assert.Equal(c, statNotExists.GID(), uint32(gid), "Created directory not owned by remapped root GID") 60 61 pid, err := s.d.Cmd("inspect", "--format={{.State.Pid}}", "userns") 62 assert.Assert(c, err == nil, "Could not inspect running container: out: %q", pid) 63 // check the uid and gid maps for the PID to ensure root is remapped 64 // (cmd = cat /proc/<pid>/uid_map | grep -E '0\s+9999\s+1') 65 _, err = RunCommandPipelineWithOutput( 66 exec.Command("cat", "/proc/"+strings.TrimSpace(pid)+"/uid_map"), 67 exec.Command("grep", "-E", fmt.Sprintf("0[[:space:]]+%d[[:space:]]+", uid))) 68 assert.NilError(c, err) 69 70 _, err = RunCommandPipelineWithOutput( 71 exec.Command("cat", "/proc/"+strings.TrimSpace(pid)+"/gid_map"), 72 exec.Command("grep", "-E", fmt.Sprintf("0[[:space:]]+%d[[:space:]]+", gid))) 73 assert.NilError(c, err) 74 75 // check that the touched file is owned by remapped uid:gid 76 stat, err := system.Stat(filepath.Join(tmpDir, "testfile")) 77 assert.NilError(c, err) 78 assert.Equal(c, stat.UID(), uint32(uid), "Touched file not owned by remapped root UID") 79 assert.Equal(c, stat.GID(), uint32(gid), "Touched file not owned by remapped root GID") 80 81 // use host usernamespace 82 out, err = s.d.Cmd("run", "-d", "--name", "userns_skip", "--userns", "host", "busybox", "sh", "-c", "touch /goofy/testfile; exec top") 83 assert.Assert(c, err == nil, "Output: %s", out) 84 user = s.findUser(c, "userns_skip") 85 // userns are skipped, user is root 86 assert.Equal(c, user, "root") 87 } 88 89 // findUser finds the uid or name of the user of the first process that runs in a container 90 func (s *DockerDaemonSuite) findUser(c *testing.T, container string) string { 91 out, err := s.d.Cmd("top", container) 92 assert.Assert(c, err == nil, "Output: %s", out) 93 rows := strings.Split(out, "\n") 94 if len(rows) < 2 { 95 // No process rows founds 96 c.FailNow() 97 } 98 return strings.Fields(rows[1])[0] 99 }