idtools_unix_test.go 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383
  1. // +build !windows
  2. package idtools
  3. import (
  4. "fmt"
  5. "io/ioutil"
  6. "os"
  7. "os/user"
  8. "path/filepath"
  9. "testing"
  10. "github.com/gotestyourself/gotestyourself/skip"
  11. "github.com/stretchr/testify/assert"
  12. "github.com/stretchr/testify/require"
  13. "golang.org/x/sys/unix"
  14. )
  15. const (
  16. tempUser = "tempuser"
  17. )
  18. type node struct {
  19. uid int
  20. gid int
  21. }
  22. func TestMkdirAllAndChown(t *testing.T) {
  23. RequiresRoot(t)
  24. dirName, err := ioutil.TempDir("", "mkdirall")
  25. if err != nil {
  26. t.Fatalf("Couldn't create temp dir: %v", err)
  27. }
  28. defer os.RemoveAll(dirName)
  29. testTree := map[string]node{
  30. "usr": {0, 0},
  31. "usr/bin": {0, 0},
  32. "lib": {33, 33},
  33. "lib/x86_64": {45, 45},
  34. "lib/x86_64/share": {1, 1},
  35. }
  36. if err := buildTree(dirName, testTree); err != nil {
  37. t.Fatal(err)
  38. }
  39. // test adding a directory to a pre-existing dir; only the new dir is owned by the uid/gid
  40. if err := MkdirAllAndChown(filepath.Join(dirName, "usr", "share"), 0755, IDPair{UID: 99, GID: 99}); err != nil {
  41. t.Fatal(err)
  42. }
  43. testTree["usr/share"] = node{99, 99}
  44. verifyTree, err := readTree(dirName, "")
  45. if err != nil {
  46. t.Fatal(err)
  47. }
  48. if err := compareTrees(testTree, verifyTree); err != nil {
  49. t.Fatal(err)
  50. }
  51. // test 2-deep new directories--both should be owned by the uid/gid pair
  52. if err := MkdirAllAndChown(filepath.Join(dirName, "lib", "some", "other"), 0755, IDPair{UID: 101, GID: 101}); err != nil {
  53. t.Fatal(err)
  54. }
  55. testTree["lib/some"] = node{101, 101}
  56. testTree["lib/some/other"] = node{101, 101}
  57. verifyTree, err = readTree(dirName, "")
  58. if err != nil {
  59. t.Fatal(err)
  60. }
  61. if err := compareTrees(testTree, verifyTree); err != nil {
  62. t.Fatal(err)
  63. }
  64. // test a directory that already exists; should be chowned, but nothing else
  65. if err := MkdirAllAndChown(filepath.Join(dirName, "usr"), 0755, IDPair{UID: 102, GID: 102}); err != nil {
  66. t.Fatal(err)
  67. }
  68. testTree["usr"] = node{102, 102}
  69. verifyTree, err = readTree(dirName, "")
  70. if err != nil {
  71. t.Fatal(err)
  72. }
  73. if err := compareTrees(testTree, verifyTree); err != nil {
  74. t.Fatal(err)
  75. }
  76. }
  77. func TestMkdirAllAndChownNew(t *testing.T) {
  78. RequiresRoot(t)
  79. dirName, err := ioutil.TempDir("", "mkdirnew")
  80. require.NoError(t, err)
  81. defer os.RemoveAll(dirName)
  82. testTree := map[string]node{
  83. "usr": {0, 0},
  84. "usr/bin": {0, 0},
  85. "lib": {33, 33},
  86. "lib/x86_64": {45, 45},
  87. "lib/x86_64/share": {1, 1},
  88. }
  89. require.NoError(t, buildTree(dirName, testTree))
  90. // test adding a directory to a pre-existing dir; only the new dir is owned by the uid/gid
  91. err = MkdirAllAndChownNew(filepath.Join(dirName, "usr", "share"), 0755, IDPair{UID: 99, GID: 99})
  92. require.NoError(t, err)
  93. testTree["usr/share"] = node{99, 99}
  94. verifyTree, err := readTree(dirName, "")
  95. require.NoError(t, err)
  96. require.NoError(t, compareTrees(testTree, verifyTree))
  97. // test 2-deep new directories--both should be owned by the uid/gid pair
  98. err = MkdirAllAndChownNew(filepath.Join(dirName, "lib", "some", "other"), 0755, IDPair{UID: 101, GID: 101})
  99. require.NoError(t, err)
  100. testTree["lib/some"] = node{101, 101}
  101. testTree["lib/some/other"] = node{101, 101}
  102. verifyTree, err = readTree(dirName, "")
  103. require.NoError(t, err)
  104. require.NoError(t, compareTrees(testTree, verifyTree))
  105. // test a directory that already exists; should NOT be chowned
  106. err = MkdirAllAndChownNew(filepath.Join(dirName, "usr"), 0755, IDPair{UID: 102, GID: 102})
  107. require.NoError(t, err)
  108. verifyTree, err = readTree(dirName, "")
  109. require.NoError(t, err)
  110. require.NoError(t, compareTrees(testTree, verifyTree))
  111. }
  112. func TestMkdirAndChown(t *testing.T) {
  113. RequiresRoot(t)
  114. dirName, err := ioutil.TempDir("", "mkdir")
  115. if err != nil {
  116. t.Fatalf("Couldn't create temp dir: %v", err)
  117. }
  118. defer os.RemoveAll(dirName)
  119. testTree := map[string]node{
  120. "usr": {0, 0},
  121. }
  122. if err := buildTree(dirName, testTree); err != nil {
  123. t.Fatal(err)
  124. }
  125. // test a directory that already exists; should just chown to the requested uid/gid
  126. if err := MkdirAndChown(filepath.Join(dirName, "usr"), 0755, IDPair{UID: 99, GID: 99}); err != nil {
  127. t.Fatal(err)
  128. }
  129. testTree["usr"] = node{99, 99}
  130. verifyTree, err := readTree(dirName, "")
  131. if err != nil {
  132. t.Fatal(err)
  133. }
  134. if err := compareTrees(testTree, verifyTree); err != nil {
  135. t.Fatal(err)
  136. }
  137. // create a subdir under a dir which doesn't exist--should fail
  138. if err := MkdirAndChown(filepath.Join(dirName, "usr", "bin", "subdir"), 0755, IDPair{UID: 102, GID: 102}); err == nil {
  139. t.Fatalf("Trying to create a directory with Mkdir where the parent doesn't exist should have failed")
  140. }
  141. // create a subdir under an existing dir; should only change the ownership of the new subdir
  142. if err := MkdirAndChown(filepath.Join(dirName, "usr", "bin"), 0755, IDPair{UID: 102, GID: 102}); err != nil {
  143. t.Fatal(err)
  144. }
  145. testTree["usr/bin"] = node{102, 102}
  146. verifyTree, err = readTree(dirName, "")
  147. if err != nil {
  148. t.Fatal(err)
  149. }
  150. if err := compareTrees(testTree, verifyTree); err != nil {
  151. t.Fatal(err)
  152. }
  153. }
  154. func buildTree(base string, tree map[string]node) error {
  155. for path, node := range tree {
  156. fullPath := filepath.Join(base, path)
  157. if err := os.MkdirAll(fullPath, 0755); err != nil {
  158. return fmt.Errorf("Couldn't create path: %s; error: %v", fullPath, err)
  159. }
  160. if err := os.Chown(fullPath, node.uid, node.gid); err != nil {
  161. return fmt.Errorf("Couldn't chown path: %s; error: %v", fullPath, err)
  162. }
  163. }
  164. return nil
  165. }
  166. func readTree(base, root string) (map[string]node, error) {
  167. tree := make(map[string]node)
  168. dirInfos, err := ioutil.ReadDir(base)
  169. if err != nil {
  170. return nil, fmt.Errorf("Couldn't read directory entries for %q: %v", base, err)
  171. }
  172. for _, info := range dirInfos {
  173. s := &unix.Stat_t{}
  174. if err := unix.Stat(filepath.Join(base, info.Name()), s); err != nil {
  175. return nil, fmt.Errorf("Can't stat file %q: %v", filepath.Join(base, info.Name()), err)
  176. }
  177. tree[filepath.Join(root, info.Name())] = node{int(s.Uid), int(s.Gid)}
  178. if info.IsDir() {
  179. // read the subdirectory
  180. subtree, err := readTree(filepath.Join(base, info.Name()), filepath.Join(root, info.Name()))
  181. if err != nil {
  182. return nil, err
  183. }
  184. for path, nodeinfo := range subtree {
  185. tree[path] = nodeinfo
  186. }
  187. }
  188. }
  189. return tree, nil
  190. }
  191. func compareTrees(left, right map[string]node) error {
  192. if len(left) != len(right) {
  193. return fmt.Errorf("Trees aren't the same size")
  194. }
  195. for path, nodeLeft := range left {
  196. if nodeRight, ok := right[path]; ok {
  197. if nodeRight.uid != nodeLeft.uid || nodeRight.gid != nodeLeft.gid {
  198. // mismatch
  199. return fmt.Errorf("mismatched ownership for %q: expected: %d:%d, got: %d:%d", path,
  200. nodeLeft.uid, nodeLeft.gid, nodeRight.uid, nodeRight.gid)
  201. }
  202. continue
  203. }
  204. return fmt.Errorf("right tree didn't contain path %q", path)
  205. }
  206. return nil
  207. }
  208. func delUser(t *testing.T, name string) {
  209. _, err := execCmd("userdel", name)
  210. assert.NoError(t, err)
  211. }
  212. func TestParseSubidFileWithNewlinesAndComments(t *testing.T) {
  213. tmpDir, err := ioutil.TempDir("", "parsesubid")
  214. if err != nil {
  215. t.Fatal(err)
  216. }
  217. fnamePath := filepath.Join(tmpDir, "testsubuid")
  218. fcontent := `tss:100000:65536
  219. # empty default subuid/subgid file
  220. dockremap:231072:65536`
  221. if err := ioutil.WriteFile(fnamePath, []byte(fcontent), 0644); err != nil {
  222. t.Fatal(err)
  223. }
  224. ranges, err := parseSubidFile(fnamePath, "dockremap")
  225. if err != nil {
  226. t.Fatal(err)
  227. }
  228. if len(ranges) != 1 {
  229. t.Fatalf("wanted 1 element in ranges, got %d instead", len(ranges))
  230. }
  231. if ranges[0].Start != 231072 {
  232. t.Fatalf("wanted 231072, got %d instead", ranges[0].Start)
  233. }
  234. if ranges[0].Length != 65536 {
  235. t.Fatalf("wanted 65536, got %d instead", ranges[0].Length)
  236. }
  237. }
  238. func TestGetRootUIDGID(t *testing.T) {
  239. uidMap := []IDMap{
  240. {
  241. ContainerID: 0,
  242. HostID: os.Getuid(),
  243. Size: 1,
  244. },
  245. }
  246. gidMap := []IDMap{
  247. {
  248. ContainerID: 0,
  249. HostID: os.Getgid(),
  250. Size: 1,
  251. },
  252. }
  253. uid, gid, err := GetRootUIDGID(uidMap, gidMap)
  254. assert.NoError(t, err)
  255. assert.Equal(t, os.Getegid(), uid)
  256. assert.Equal(t, os.Getegid(), gid)
  257. uidMapError := []IDMap{
  258. {
  259. ContainerID: 1,
  260. HostID: os.Getuid(),
  261. Size: 1,
  262. },
  263. }
  264. _, _, err = GetRootUIDGID(uidMapError, gidMap)
  265. assert.EqualError(t, err, "Container ID 0 cannot be mapped to a host ID")
  266. }
  267. func TestToContainer(t *testing.T) {
  268. uidMap := []IDMap{
  269. {
  270. ContainerID: 2,
  271. HostID: 2,
  272. Size: 1,
  273. },
  274. }
  275. containerID, err := toContainer(2, uidMap)
  276. assert.NoError(t, err)
  277. assert.Equal(t, uidMap[0].ContainerID, containerID)
  278. }
  279. func TestNewIDMappings(t *testing.T) {
  280. RequiresRoot(t)
  281. _, _, err := AddNamespaceRangesUser(tempUser)
  282. assert.NoError(t, err)
  283. defer delUser(t, tempUser)
  284. tempUser, err := user.Lookup(tempUser)
  285. assert.NoError(t, err)
  286. gids, err := tempUser.GroupIds()
  287. assert.NoError(t, err)
  288. group, err := user.LookupGroupId(string(gids[0]))
  289. assert.NoError(t, err)
  290. idMappings, err := NewIDMappings(tempUser.Username, group.Name)
  291. assert.NoError(t, err)
  292. rootUID, rootGID, err := GetRootUIDGID(idMappings.UIDs(), idMappings.GIDs())
  293. assert.NoError(t, err)
  294. dirName, err := ioutil.TempDir("", "mkdirall")
  295. assert.NoError(t, err, "Couldn't create temp directory")
  296. defer os.RemoveAll(dirName)
  297. err = MkdirAllAndChown(dirName, 0700, IDPair{UID: rootUID, GID: rootGID})
  298. assert.NoError(t, err, "Couldn't change ownership of file path. Got error")
  299. assert.True(t, CanAccess(dirName, idMappings.RootPair()), fmt.Sprintf("Unable to access %s directory with user UID:%d and GID:%d", dirName, rootUID, rootGID))
  300. }
  301. func TestLookupUserAndGroup(t *testing.T) {
  302. RequiresRoot(t)
  303. uid, gid, err := AddNamespaceRangesUser(tempUser)
  304. assert.NoError(t, err)
  305. defer delUser(t, tempUser)
  306. fetchedUser, err := LookupUser(tempUser)
  307. assert.NoError(t, err)
  308. fetchedUserByID, err := LookupUID(uid)
  309. assert.NoError(t, err)
  310. assert.Equal(t, fetchedUserByID, fetchedUser)
  311. fetchedGroup, err := LookupGroup(tempUser)
  312. assert.NoError(t, err)
  313. fetchedGroupByID, err := LookupGID(gid)
  314. assert.NoError(t, err)
  315. assert.Equal(t, fetchedGroupByID, fetchedGroup)
  316. }
  317. func TestLookupUserAndGroupThatDoesNotExist(t *testing.T) {
  318. fakeUser := "fakeuser"
  319. _, err := LookupUser(fakeUser)
  320. assert.EqualError(t, err, "getent unable to find entry \""+fakeUser+"\" in passwd database")
  321. _, err = LookupUID(-1)
  322. assert.Error(t, err)
  323. fakeGroup := "fakegroup"
  324. _, err = LookupGroup(fakeGroup)
  325. assert.EqualError(t, err, "getent unable to find entry \""+fakeGroup+"\" in group database")
  326. _, err = LookupGID(-1)
  327. assert.Error(t, err)
  328. }
  329. func RequiresRoot(t *testing.T) {
  330. skip.IfCondition(t, os.Getuid() != 0, "skipping test that requires root")
  331. }