Skip to content

Commit 7e518ec

Browse files
authored
Merge pull request #49087 from dmcgowan/split-idtools-internal
Split idtools to an internal package and package to be moved
2 parents 9ecbe7b + 6134528 commit 7e518ec

7 files changed

+22
-463
lines changed

user/idtools.go

+1-20
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,8 @@
1-
package idtools // import "github.com/docker/docker/pkg/idtools"
1+
package idtools
22

33
import (
44
"fmt"
55
"os"
6-
7-
"github.com/moby/sys/user"
86
)
97

108
// IDMap contains a single entry for user namespace range remapping. An array
@@ -16,11 +14,6 @@ type IDMap struct {
1614
Size int `json:"size"`
1715
}
1816

19-
const (
20-
subuidFileName = "/etc/subuid"
21-
subgidFileName = "/etc/subgid"
22-
)
23-
2417
// MkdirAllAndChown creates a directory (include any along the path) and then modifies
2518
// ownership to the requested uid/gid. If the directory already exists, this
2619
// function will still change ownership and permissions.
@@ -150,18 +143,6 @@ func (i IdentityMapping) Empty() bool {
150143
return len(i.UIDMaps) == 0 && len(i.GIDMaps) == 0
151144
}
152145

153-
func parseSubuid(username string) ([]user.SubID, error) {
154-
return user.ParseSubIDFileFilter(subuidFileName, func(sid user.SubID) bool {
155-
return sid.Name == username
156-
})
157-
}
158-
159-
func parseSubgid(username string) ([]user.SubID, error) {
160-
return user.ParseSubIDFileFilter(subgidFileName, func(sid user.SubID) bool {
161-
return sid.Name == username
162-
})
163-
}
164-
165146
// CurrentIdentity returns the identity of the current process
166147
func CurrentIdentity() Identity {
167148
return Identity{UID: os.Getuid(), GID: os.Getegid()}

user/idtools_unix.go

+14-118
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,10 @@
11
//go:build !windows
22

3-
package idtools // import "github.com/docker/docker/pkg/idtools"
3+
package idtools
44

55
import (
6-
"bytes"
76
"fmt"
8-
"io"
97
"os"
10-
"os/exec"
118
"path/filepath"
129
"strconv"
1310
"syscall"
@@ -72,127 +69,25 @@ func mkdirAs(path string, mode os.FileMode, owner Identity, mkAll, chownExisting
7269
return nil
7370
}
7471

75-
// LookupUser uses traditional local system files lookup (from libcontainer/user) on a username,
76-
// followed by a call to `getent` for supporting host configured non-files passwd and group dbs
72+
// LookupUser uses traditional local system files lookup (from libcontainer/user) on a username
73+
//
74+
// Deprecated: use [user.LookupUser] instead
7775
func LookupUser(name string) (user.User, error) {
78-
// first try a local system files lookup using existing capabilities
79-
usr, err := user.LookupUser(name)
80-
if err == nil {
81-
return usr, nil
82-
}
83-
// local files lookup failed; attempt to call `getent` to query configured passwd dbs
84-
usr, err = getentUser(name)
85-
if err != nil {
86-
return user.User{}, err
87-
}
88-
return usr, nil
76+
return user.LookupUser(name)
8977
}
9078

91-
// LookupUID uses traditional local system files lookup (from libcontainer/user) on a uid,
92-
// followed by a call to `getent` for supporting host configured non-files passwd and group dbs
79+
// LookupUID uses traditional local system files lookup (from libcontainer/user) on a uid
80+
//
81+
// Deprecated: use [user.LookupUid] instead
9382
func LookupUID(uid int) (user.User, error) {
94-
// first try a local system files lookup using existing capabilities
95-
usr, err := user.LookupUid(uid)
96-
if err == nil {
97-
return usr, nil
98-
}
99-
// local files lookup failed; attempt to call `getent` to query configured passwd dbs
100-
return getentUser(strconv.Itoa(uid))
101-
}
102-
103-
func getentUser(name string) (user.User, error) {
104-
reader, err := callGetent("passwd", name)
105-
if err != nil {
106-
return user.User{}, err
107-
}
108-
users, err := user.ParsePasswd(reader)
109-
if err != nil {
110-
return user.User{}, err
111-
}
112-
if len(users) == 0 {
113-
return user.User{}, fmt.Errorf("getent failed to find passwd entry for %q", name)
114-
}
115-
return users[0], nil
83+
return user.LookupUid(uid)
11684
}
11785

11886
// LookupGroup uses traditional local system files lookup (from libcontainer/user) on a group name,
119-
// followed by a call to `getent` for supporting host configured non-files passwd and group dbs
87+
//
88+
// Deprecated: use [user.LookupGroup] instead
12089
func LookupGroup(name string) (user.Group, error) {
121-
// first try a local system files lookup using existing capabilities
122-
group, err := user.LookupGroup(name)
123-
if err == nil {
124-
return group, nil
125-
}
126-
// local files lookup failed; attempt to call `getent` to query configured group dbs
127-
return getentGroup(name)
128-
}
129-
130-
// LookupGID uses traditional local system files lookup (from libcontainer/user) on a group ID,
131-
// followed by a call to `getent` for supporting host configured non-files passwd and group dbs
132-
func LookupGID(gid int) (user.Group, error) {
133-
// first try a local system files lookup using existing capabilities
134-
group, err := user.LookupGid(gid)
135-
if err == nil {
136-
return group, nil
137-
}
138-
// local files lookup failed; attempt to call `getent` to query configured group dbs
139-
return getentGroup(strconv.Itoa(gid))
140-
}
141-
142-
func getentGroup(name string) (user.Group, error) {
143-
reader, err := callGetent("group", name)
144-
if err != nil {
145-
return user.Group{}, err
146-
}
147-
groups, err := user.ParseGroup(reader)
148-
if err != nil {
149-
return user.Group{}, err
150-
}
151-
if len(groups) == 0 {
152-
return user.Group{}, fmt.Errorf("getent failed to find groups entry for %q", name)
153-
}
154-
return groups[0], nil
155-
}
156-
157-
func callGetent(database, key string) (io.Reader, error) {
158-
getentCmd, err := resolveBinary("getent")
159-
// if no `getent` command within the execution environment, can't do anything else
160-
if err != nil {
161-
return nil, fmt.Errorf("unable to find getent command: %w", err)
162-
}
163-
command := exec.Command(getentCmd, database, key)
164-
// we run getent within container filesystem, but without /dev so /dev/null is not available for exec to mock stdin
165-
command.Stdin = io.NopCloser(bytes.NewReader(nil))
166-
out, err := command.CombinedOutput()
167-
if err != nil {
168-
exitCode, errC := getExitCode(err)
169-
if errC != nil {
170-
return nil, err
171-
}
172-
switch exitCode {
173-
case 1:
174-
return nil, fmt.Errorf("getent reported invalid parameters/database unknown")
175-
case 2:
176-
return nil, fmt.Errorf("getent unable to find entry %q in %s database", key, database)
177-
case 3:
178-
return nil, fmt.Errorf("getent database doesn't support enumeration")
179-
default:
180-
return nil, err
181-
}
182-
}
183-
return bytes.NewReader(out), nil
184-
}
185-
186-
// getExitCode returns the ExitStatus of the specified error if its type is
187-
// exec.ExitError, returns 0 and an error otherwise.
188-
func getExitCode(err error) (int, error) {
189-
exitCode := 0
190-
if exiterr, ok := err.(*exec.ExitError); ok {
191-
if procExit, ok := exiterr.Sys().(syscall.WaitStatus); ok {
192-
return procExit.ExitStatus(), nil
193-
}
194-
}
195-
return exitCode, fmt.Errorf("failed to get exit code")
90+
return user.LookupGroup(name)
19691
}
19792

19893
// setPermissions performs a chown/chmod only if the uid/gid don't match what's requested
@@ -223,7 +118,8 @@ func setPermissions(p string, mode os.FileMode, owner Identity, stat os.FileInfo
223118
// using the data from /etc/sub{uid,gid} ranges, creates the
224119
// proper uid and gid remapping ranges for that user/group pair
225120
func LoadIdentityMapping(name string) (IdentityMapping, error) {
226-
usr, err := LookupUser(name)
121+
// TODO: Consider adding support for calling out to "getent"
122+
usr, err := user.LookupUser(name)
227123
if err != nil {
228124
return IdentityMapping{}, fmt.Errorf("could not get user for username %s: %v", name, err)
229125
}

user/idtools_unix_test.go

+1-111
Original file line numberDiff line numberDiff line change
@@ -1,26 +1,17 @@
11
//go:build !windows
22

3-
package idtools // import "github.com/docker/docker/pkg/idtools"
3+
package idtools
44

55
import (
66
"fmt"
77
"os"
8-
"os/exec"
9-
stduser "os/user"
108
"path/filepath"
11-
"syscall"
129
"testing"
1310

1411
"golang.org/x/sys/unix"
1512
"gotest.tools/v3/assert"
1613
is "gotest.tools/v3/assert/cmp"
1714
"gotest.tools/v3/skip"
18-
19-
"github.com/moby/sys/user"
20-
)
21-
22-
const (
23-
tempUser = "tempuser"
2415
)
2516

2617
type node struct {
@@ -327,41 +318,6 @@ func compareTrees(left, right map[string]node) error {
327318
return nil
328319
}
329320

330-
func delUser(t *testing.T, name string) {
331-
out, err := exec.Command("userdel", name).CombinedOutput()
332-
assert.Check(t, err, out)
333-
}
334-
335-
func TestParseSubidFileWithNewlinesAndComments(t *testing.T) {
336-
tmpDir, err := os.MkdirTemp("", "parsesubid")
337-
if err != nil {
338-
t.Fatal(err)
339-
}
340-
fnamePath := filepath.Join(tmpDir, "testsubuid")
341-
fcontent := `tss:100000:65536
342-
# empty default subuid/subgid file
343-
344-
dockremap:231072:65536`
345-
if err := os.WriteFile(fnamePath, []byte(fcontent), 0o644); err != nil {
346-
t.Fatal(err)
347-
}
348-
ranges, err := user.ParseSubIDFileFilter(fnamePath, func(sid user.SubID) bool {
349-
return sid.Name == "dockremap"
350-
})
351-
if err != nil {
352-
t.Fatal(err)
353-
}
354-
if len(ranges) != 1 {
355-
t.Fatalf("wanted 1 element in ranges, got %d instead", len(ranges))
356-
}
357-
if ranges[0].SubID != 231072 {
358-
t.Fatalf("wanted 231072, got %d instead", ranges[0].SubID)
359-
}
360-
if ranges[0].Count != 65536 {
361-
t.Fatalf("wanted 65536, got %d instead", ranges[0].Count)
362-
}
363-
}
364-
365321
func TestGetRootUIDGID(t *testing.T) {
366322
uidMap := []IDMap{
367323
{
@@ -408,72 +364,6 @@ func TestToContainer(t *testing.T) {
408364
assert.Check(t, is.Equal(uidMap[0].ContainerID, containerID))
409365
}
410366

411-
func TestNewIDMappings(t *testing.T) {
412-
RequiresRoot(t)
413-
_, _, err := AddNamespaceRangesUser(tempUser)
414-
assert.Check(t, err)
415-
defer delUser(t, tempUser)
416-
417-
tempUser, err := stduser.Lookup(tempUser)
418-
assert.Check(t, err)
419-
420-
idMapping, err := LoadIdentityMapping(tempUser.Username)
421-
assert.Check(t, err)
422-
423-
rootUID, rootGID, err := GetRootUIDGID(idMapping.UIDMaps, idMapping.GIDMaps)
424-
assert.Check(t, err)
425-
426-
dirName, err := os.MkdirTemp("", "mkdirall")
427-
assert.Check(t, err, "Couldn't create temp directory")
428-
defer os.RemoveAll(dirName)
429-
430-
err = MkdirAllAndChown(dirName, 0o700, Identity{UID: rootUID, GID: rootGID})
431-
assert.Check(t, err, "Couldn't change ownership of file path. Got error")
432-
cmd := exec.Command("ls", "-la", dirName)
433-
cmd.SysProcAttr = &syscall.SysProcAttr{
434-
Credential: &syscall.Credential{Uid: uint32(rootUID), Gid: uint32(rootGID)},
435-
}
436-
out, err := cmd.CombinedOutput()
437-
assert.Check(t, err, "Unable to access %s directory with user UID:%d and GID:%d:\n%s", dirName, rootUID, rootGID, string(out))
438-
}
439-
440-
func TestLookupUserAndGroup(t *testing.T) {
441-
RequiresRoot(t)
442-
uid, gid, err := AddNamespaceRangesUser(tempUser)
443-
assert.Check(t, err)
444-
defer delUser(t, tempUser)
445-
446-
fetchedUser, err := LookupUser(tempUser)
447-
assert.Check(t, err)
448-
449-
fetchedUserByID, err := LookupUID(uid)
450-
assert.Check(t, err)
451-
assert.Check(t, is.DeepEqual(fetchedUserByID, fetchedUser))
452-
453-
fetchedGroup, err := LookupGroup(tempUser)
454-
assert.Check(t, err)
455-
456-
fetchedGroupByID, err := LookupGID(gid)
457-
assert.Check(t, err)
458-
assert.Check(t, is.DeepEqual(fetchedGroupByID, fetchedGroup))
459-
}
460-
461-
func TestLookupUserAndGroupThatDoesNotExist(t *testing.T) {
462-
fakeUser := "fakeuser"
463-
_, err := LookupUser(fakeUser)
464-
assert.Check(t, is.Error(err, `getent unable to find entry "fakeuser" in passwd database`))
465-
466-
_, err = LookupUID(-1)
467-
assert.Check(t, is.ErrorContains(err, ""))
468-
469-
fakeGroup := "fakegroup"
470-
_, err = LookupGroup(fakeGroup)
471-
assert.Check(t, is.Error(err, `getent unable to find entry "fakegroup" in group database`))
472-
473-
_, err = LookupGID(-1)
474-
assert.Check(t, is.ErrorContains(err, ""))
475-
}
476-
477367
// TestMkdirIsNotDir checks that mkdirAs() function (used by MkdirAll...)
478368
// returns a correct error in case a directory which it is about to create
479369
// already exists but is a file (rather than a directory).

user/idtools_windows.go

+6-2
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,20 @@
1-
package idtools // import "github.com/docker/docker/pkg/idtools"
1+
package idtools
22

33
import (
44
"os"
55
)
66

77
const (
8+
// Deprecated: copy value locally
89
SeTakeOwnershipPrivilege = "SeTakeOwnershipPrivilege"
910
)
1011

1112
const (
13+
// Deprecated: copy value locally
1214
ContainerAdministratorSidString = "S-1-5-93-2-1"
13-
ContainerUserSidString = "S-1-5-93-2-2"
15+
16+
// Deprecated: copy value locally
17+
ContainerUserSidString = "S-1-5-93-2-2"
1418
)
1519

1620
// This is currently a wrapper around [os.MkdirAll] since currently

0 commit comments

Comments
 (0)