123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383 |
- // +build !windows
- package idtools
- import (
- "fmt"
- "io/ioutil"
- "os"
- "os/user"
- "path/filepath"
- "testing"
- "github.com/gotestyourself/gotestyourself/skip"
- "github.com/stretchr/testify/assert"
- "github.com/stretchr/testify/require"
- "golang.org/x/sys/unix"
- )
- const (
- tempUser = "tempuser"
- )
- type node struct {
- uid int
- gid int
- }
- func TestMkdirAllAndChown(t *testing.T) {
- RequiresRoot(t)
- dirName, err := ioutil.TempDir("", "mkdirall")
- if err != nil {
- t.Fatalf("Couldn't create temp dir: %v", err)
- }
- defer os.RemoveAll(dirName)
- testTree := map[string]node{
- "usr": {0, 0},
- "usr/bin": {0, 0},
- "lib": {33, 33},
- "lib/x86_64": {45, 45},
- "lib/x86_64/share": {1, 1},
- }
- if err := buildTree(dirName, testTree); err != nil {
- t.Fatal(err)
- }
- // test adding a directory to a pre-existing dir; only the new dir is owned by the uid/gid
- if err := MkdirAllAndChown(filepath.Join(dirName, "usr", "share"), 0755, IDPair{UID: 99, GID: 99}); err != nil {
- t.Fatal(err)
- }
- testTree["usr/share"] = node{99, 99}
- verifyTree, err := readTree(dirName, "")
- if err != nil {
- t.Fatal(err)
- }
- if err := compareTrees(testTree, verifyTree); err != nil {
- t.Fatal(err)
- }
- // test 2-deep new directories--both should be owned by the uid/gid pair
- if err := MkdirAllAndChown(filepath.Join(dirName, "lib", "some", "other"), 0755, IDPair{UID: 101, GID: 101}); err != nil {
- t.Fatal(err)
- }
- testTree["lib/some"] = node{101, 101}
- testTree["lib/some/other"] = node{101, 101}
- verifyTree, err = readTree(dirName, "")
- if err != nil {
- t.Fatal(err)
- }
- if err := compareTrees(testTree, verifyTree); err != nil {
- t.Fatal(err)
- }
- // test a directory that already exists; should be chowned, but nothing else
- if err := MkdirAllAndChown(filepath.Join(dirName, "usr"), 0755, IDPair{UID: 102, GID: 102}); err != nil {
- t.Fatal(err)
- }
- testTree["usr"] = node{102, 102}
- verifyTree, err = readTree(dirName, "")
- if err != nil {
- t.Fatal(err)
- }
- if err := compareTrees(testTree, verifyTree); err != nil {
- t.Fatal(err)
- }
- }
- func TestMkdirAllAndChownNew(t *testing.T) {
- RequiresRoot(t)
- dirName, err := ioutil.TempDir("", "mkdirnew")
- require.NoError(t, err)
- defer os.RemoveAll(dirName)
- testTree := map[string]node{
- "usr": {0, 0},
- "usr/bin": {0, 0},
- "lib": {33, 33},
- "lib/x86_64": {45, 45},
- "lib/x86_64/share": {1, 1},
- }
- require.NoError(t, buildTree(dirName, testTree))
- // test adding a directory to a pre-existing dir; only the new dir is owned by the uid/gid
- err = MkdirAllAndChownNew(filepath.Join(dirName, "usr", "share"), 0755, IDPair{UID: 99, GID: 99})
- require.NoError(t, err)
- testTree["usr/share"] = node{99, 99}
- verifyTree, err := readTree(dirName, "")
- require.NoError(t, err)
- require.NoError(t, compareTrees(testTree, verifyTree))
- // test 2-deep new directories--both should be owned by the uid/gid pair
- err = MkdirAllAndChownNew(filepath.Join(dirName, "lib", "some", "other"), 0755, IDPair{UID: 101, GID: 101})
- require.NoError(t, err)
- testTree["lib/some"] = node{101, 101}
- testTree["lib/some/other"] = node{101, 101}
- verifyTree, err = readTree(dirName, "")
- require.NoError(t, err)
- require.NoError(t, compareTrees(testTree, verifyTree))
- // test a directory that already exists; should NOT be chowned
- err = MkdirAllAndChownNew(filepath.Join(dirName, "usr"), 0755, IDPair{UID: 102, GID: 102})
- require.NoError(t, err)
- verifyTree, err = readTree(dirName, "")
- require.NoError(t, err)
- require.NoError(t, compareTrees(testTree, verifyTree))
- }
- func TestMkdirAndChown(t *testing.T) {
- RequiresRoot(t)
- dirName, err := ioutil.TempDir("", "mkdir")
- if err != nil {
- t.Fatalf("Couldn't create temp dir: %v", err)
- }
- defer os.RemoveAll(dirName)
- testTree := map[string]node{
- "usr": {0, 0},
- }
- if err := buildTree(dirName, testTree); err != nil {
- t.Fatal(err)
- }
- // test a directory that already exists; should just chown to the requested uid/gid
- if err := MkdirAndChown(filepath.Join(dirName, "usr"), 0755, IDPair{UID: 99, GID: 99}); err != nil {
- t.Fatal(err)
- }
- testTree["usr"] = node{99, 99}
- verifyTree, err := readTree(dirName, "")
- if err != nil {
- t.Fatal(err)
- }
- if err := compareTrees(testTree, verifyTree); err != nil {
- t.Fatal(err)
- }
- // create a subdir under a dir which doesn't exist--should fail
- if err := MkdirAndChown(filepath.Join(dirName, "usr", "bin", "subdir"), 0755, IDPair{UID: 102, GID: 102}); err == nil {
- t.Fatalf("Trying to create a directory with Mkdir where the parent doesn't exist should have failed")
- }
- // create a subdir under an existing dir; should only change the ownership of the new subdir
- if err := MkdirAndChown(filepath.Join(dirName, "usr", "bin"), 0755, IDPair{UID: 102, GID: 102}); err != nil {
- t.Fatal(err)
- }
- testTree["usr/bin"] = node{102, 102}
- verifyTree, err = readTree(dirName, "")
- if err != nil {
- t.Fatal(err)
- }
- if err := compareTrees(testTree, verifyTree); err != nil {
- t.Fatal(err)
- }
- }
- func buildTree(base string, tree map[string]node) error {
- for path, node := range tree {
- fullPath := filepath.Join(base, path)
- if err := os.MkdirAll(fullPath, 0755); err != nil {
- return fmt.Errorf("Couldn't create path: %s; error: %v", fullPath, err)
- }
- if err := os.Chown(fullPath, node.uid, node.gid); err != nil {
- return fmt.Errorf("Couldn't chown path: %s; error: %v", fullPath, err)
- }
- }
- return nil
- }
- func readTree(base, root string) (map[string]node, error) {
- tree := make(map[string]node)
- dirInfos, err := ioutil.ReadDir(base)
- if err != nil {
- return nil, fmt.Errorf("Couldn't read directory entries for %q: %v", base, err)
- }
- for _, info := range dirInfos {
- s := &unix.Stat_t{}
- if err := unix.Stat(filepath.Join(base, info.Name()), s); err != nil {
- return nil, fmt.Errorf("Can't stat file %q: %v", filepath.Join(base, info.Name()), err)
- }
- tree[filepath.Join(root, info.Name())] = node{int(s.Uid), int(s.Gid)}
- if info.IsDir() {
- // read the subdirectory
- subtree, err := readTree(filepath.Join(base, info.Name()), filepath.Join(root, info.Name()))
- if err != nil {
- return nil, err
- }
- for path, nodeinfo := range subtree {
- tree[path] = nodeinfo
- }
- }
- }
- return tree, nil
- }
- func compareTrees(left, right map[string]node) error {
- if len(left) != len(right) {
- return fmt.Errorf("Trees aren't the same size")
- }
- for path, nodeLeft := range left {
- if nodeRight, ok := right[path]; ok {
- if nodeRight.uid != nodeLeft.uid || nodeRight.gid != nodeLeft.gid {
- // mismatch
- return fmt.Errorf("mismatched ownership for %q: expected: %d:%d, got: %d:%d", path,
- nodeLeft.uid, nodeLeft.gid, nodeRight.uid, nodeRight.gid)
- }
- continue
- }
- return fmt.Errorf("right tree didn't contain path %q", path)
- }
- return nil
- }
- func delUser(t *testing.T, name string) {
- _, err := execCmd("userdel", name)
- assert.NoError(t, err)
- }
- func TestParseSubidFileWithNewlinesAndComments(t *testing.T) {
- tmpDir, err := ioutil.TempDir("", "parsesubid")
- if err != nil {
- t.Fatal(err)
- }
- fnamePath := filepath.Join(tmpDir, "testsubuid")
- fcontent := `tss:100000:65536
- # empty default subuid/subgid file
- dockremap:231072:65536`
- if err := ioutil.WriteFile(fnamePath, []byte(fcontent), 0644); err != nil {
- t.Fatal(err)
- }
- ranges, err := parseSubidFile(fnamePath, "dockremap")
- if err != nil {
- t.Fatal(err)
- }
- if len(ranges) != 1 {
- t.Fatalf("wanted 1 element in ranges, got %d instead", len(ranges))
- }
- if ranges[0].Start != 231072 {
- t.Fatalf("wanted 231072, got %d instead", ranges[0].Start)
- }
- if ranges[0].Length != 65536 {
- t.Fatalf("wanted 65536, got %d instead", ranges[0].Length)
- }
- }
- func TestGetRootUIDGID(t *testing.T) {
- uidMap := []IDMap{
- {
- ContainerID: 0,
- HostID: os.Getuid(),
- Size: 1,
- },
- }
- gidMap := []IDMap{
- {
- ContainerID: 0,
- HostID: os.Getgid(),
- Size: 1,
- },
- }
- uid, gid, err := GetRootUIDGID(uidMap, gidMap)
- assert.NoError(t, err)
- assert.Equal(t, os.Getegid(), uid)
- assert.Equal(t, os.Getegid(), gid)
- uidMapError := []IDMap{
- {
- ContainerID: 1,
- HostID: os.Getuid(),
- Size: 1,
- },
- }
- _, _, err = GetRootUIDGID(uidMapError, gidMap)
- assert.EqualError(t, err, "Container ID 0 cannot be mapped to a host ID")
- }
- func TestToContainer(t *testing.T) {
- uidMap := []IDMap{
- {
- ContainerID: 2,
- HostID: 2,
- Size: 1,
- },
- }
- containerID, err := toContainer(2, uidMap)
- assert.NoError(t, err)
- assert.Equal(t, uidMap[0].ContainerID, containerID)
- }
- func TestNewIDMappings(t *testing.T) {
- RequiresRoot(t)
- _, _, err := AddNamespaceRangesUser(tempUser)
- assert.NoError(t, err)
- defer delUser(t, tempUser)
- tempUser, err := user.Lookup(tempUser)
- assert.NoError(t, err)
- gids, err := tempUser.GroupIds()
- assert.NoError(t, err)
- group, err := user.LookupGroupId(string(gids[0]))
- assert.NoError(t, err)
- idMappings, err := NewIDMappings(tempUser.Username, group.Name)
- assert.NoError(t, err)
- rootUID, rootGID, err := GetRootUIDGID(idMappings.UIDs(), idMappings.GIDs())
- assert.NoError(t, err)
- dirName, err := ioutil.TempDir("", "mkdirall")
- assert.NoError(t, err, "Couldn't create temp directory")
- defer os.RemoveAll(dirName)
- err = MkdirAllAndChown(dirName, 0700, IDPair{UID: rootUID, GID: rootGID})
- assert.NoError(t, err, "Couldn't change ownership of file path. Got error")
- assert.True(t, CanAccess(dirName, idMappings.RootPair()), fmt.Sprintf("Unable to access %s directory with user UID:%d and GID:%d", dirName, rootUID, rootGID))
- }
- func TestLookupUserAndGroup(t *testing.T) {
- RequiresRoot(t)
- uid, gid, err := AddNamespaceRangesUser(tempUser)
- assert.NoError(t, err)
- defer delUser(t, tempUser)
- fetchedUser, err := LookupUser(tempUser)
- assert.NoError(t, err)
- fetchedUserByID, err := LookupUID(uid)
- assert.NoError(t, err)
- assert.Equal(t, fetchedUserByID, fetchedUser)
- fetchedGroup, err := LookupGroup(tempUser)
- assert.NoError(t, err)
- fetchedGroupByID, err := LookupGID(gid)
- assert.NoError(t, err)
- assert.Equal(t, fetchedGroupByID, fetchedGroup)
- }
- func TestLookupUserAndGroupThatDoesNotExist(t *testing.T) {
- fakeUser := "fakeuser"
- _, err := LookupUser(fakeUser)
- assert.EqualError(t, err, "getent unable to find entry \""+fakeUser+"\" in passwd database")
- _, err = LookupUID(-1)
- assert.Error(t, err)
- fakeGroup := "fakegroup"
- _, err = LookupGroup(fakeGroup)
- assert.EqualError(t, err, "getent unable to find entry \""+fakeGroup+"\" in group database")
- _, err = LookupGID(-1)
- assert.Error(t, err)
- }
- func RequiresRoot(t *testing.T) {
- skip.IfCondition(t, os.Getuid() != 0, "skipping test that requires root")
- }
|