github.com/kim0/docker@v0.6.2-0.20161130212042-4addda3f07e7/integration-cli/docker_cli_userns_test.go (about) 1 // +build !windows 2 3 package main 4 5 import ( 6 "fmt" 7 "io/ioutil" 8 "os" 9 "os/exec" 10 "path" 11 "path/filepath" 12 "strconv" 13 "strings" 14 15 "github.com/docker/docker/pkg/integration/checker" 16 "github.com/docker/docker/pkg/stringid" 17 "github.com/docker/docker/pkg/system" 18 "github.com/go-check/check" 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 *check.C) { 25 testRequires(c, DaemonIsLinux, SameHostDaemon, UserNamespaceInKernel) 26 27 c.Assert(s.d.StartWithBusybox("--userns-remap", "default"), checker.IsNil) 28 29 tmpDir, err := ioutil.TempDir("", "userns") 30 c.Assert(err, checker.IsNil) 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 c.Assert(uidgid, checker.HasLen, 2, check.Commentf("Should have gotten uid/gid strings from root dirname: %s", filepath.Base(s.d.root))) 41 uid, err := strconv.Atoi(uidgid[0]) 42 c.Assert(err, checker.IsNil, check.Commentf("Can't parse uid")) 43 gid, err := strconv.Atoi(uidgid[1]) 44 c.Assert(err, checker.IsNil, check.Commentf("Can't parse gid")) 45 46 // writable by the remapped root UID/GID pair 47 c.Assert(os.Chown(tmpDir, uid, gid), checker.IsNil) 48 49 out, err := s.d.Cmd("run", "-d", "--name", "userns", "-v", tmpDir+":/goofy", "-v", tmpDirNotExists+":/donald", "busybox", "sh", "-c", "touch /goofy/testfile; top") 50 c.Assert(err, checker.IsNil, check.Commentf("Output: %s", out)) 51 user := s.findUser(c, "userns") 52 c.Assert(uidgid[0], checker.Equals, user) 53 54 // check that the created directory is owned by remapped uid:gid 55 statNotExists, err := system.Stat(tmpDirNotExists) 56 c.Assert(err, checker.IsNil) 57 c.Assert(statNotExists.UID(), checker.Equals, uint32(uid), check.Commentf("Created directory not owned by remapped root UID")) 58 c.Assert(statNotExists.GID(), checker.Equals, uint32(gid), check.Commentf("Created directory not owned by remapped root GID")) 59 60 pid, err := s.d.Cmd("inspect", "--format={{.State.Pid}}", "userns") 61 c.Assert(err, checker.IsNil, check.Commentf("Could not inspect running container: out: %q", pid)) 62 // check the uid and gid maps for the PID to ensure root is remapped 63 // (cmd = cat /proc/<pid>/uid_map | grep -E '0\s+9999\s+1') 64 out, rc1, err := runCommandPipelineWithOutput( 65 exec.Command("cat", "/proc/"+strings.TrimSpace(pid)+"/uid_map"), 66 exec.Command("grep", "-E", fmt.Sprintf("0[[:space:]]+%d[[:space:]]+", uid))) 67 c.Assert(rc1, checker.Equals, 0, check.Commentf("Didn't match uid_map: output: %s", out)) 68 69 out, rc2, err := runCommandPipelineWithOutput( 70 exec.Command("cat", "/proc/"+strings.TrimSpace(pid)+"/gid_map"), 71 exec.Command("grep", "-E", fmt.Sprintf("0[[:space:]]+%d[[:space:]]+", gid))) 72 c.Assert(rc2, checker.Equals, 0, check.Commentf("Didn't match gid_map: output: %s", out)) 73 74 // check that the touched file is owned by remapped uid:gid 75 stat, err := system.Stat(filepath.Join(tmpDir, "testfile")) 76 c.Assert(err, checker.IsNil) 77 c.Assert(stat.UID(), checker.Equals, uint32(uid), check.Commentf("Touched file not owned by remapped root UID")) 78 c.Assert(stat.GID(), checker.Equals, uint32(gid), check.Commentf("Touched file not owned by remapped root GID")) 79 80 // use host usernamespace 81 out, err = s.d.Cmd("run", "-d", "--name", "userns_skip", "--userns", "host", "busybox", "sh", "-c", "touch /goofy/testfile; top") 82 c.Assert(err, checker.IsNil, check.Commentf("Output: %s", out)) 83 user = s.findUser(c, "userns_skip") 84 // userns are skipped, user is root 85 c.Assert(user, checker.Equals, "root") 86 } 87 88 // findUser finds the uid or name of the user of the first process that runs in a container 89 func (s *DockerDaemonSuite) findUser(c *check.C, container string) string { 90 out, err := s.d.Cmd("top", container) 91 c.Assert(err, checker.IsNil, check.Commentf("Output: %s", out)) 92 rows := strings.Split(out, "\n") 93 if len(rows) < 2 { 94 // No process rows founds 95 c.FailNow() 96 } 97 return strings.Fields(rows[1])[0] 98 }