mirror of
https://github.com/moby/moby.git
synced 2026-08-02 22:26:52 +00:00
pkg/idtools: rewrite to use moby/sys/user
Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
This commit is contained in:
@@ -1,11 +1,10 @@
|
||||
package idtools // import "github.com/docker/docker/pkg/idtools"
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"fmt"
|
||||
"os"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/moby/sys/user"
|
||||
)
|
||||
|
||||
// IDMap contains a single entry for user namespace range remapping. An array
|
||||
@@ -17,17 +16,6 @@ type IDMap struct {
|
||||
Size int `json:"size"`
|
||||
}
|
||||
|
||||
type subIDRange struct {
|
||||
Start int
|
||||
Length int
|
||||
}
|
||||
|
||||
type subIDRanges []subIDRange
|
||||
|
||||
func (e subIDRanges) Len() int { return len(e) }
|
||||
func (e subIDRanges) Swap(i, j int) { e[i], e[j] = e[j], e[i] }
|
||||
func (e subIDRanges) Less(i, j int) bool { return e[i].Start < e[j].Start }
|
||||
|
||||
const (
|
||||
subuidFileName = "/etc/subuid"
|
||||
subgidFileName = "/etc/subgid"
|
||||
@@ -162,65 +150,16 @@ func (i IdentityMapping) Empty() bool {
|
||||
return len(i.UIDMaps) == 0 && len(i.GIDMaps) == 0
|
||||
}
|
||||
|
||||
func createIDMap(subidRanges subIDRanges) []IDMap {
|
||||
idMap := []IDMap{}
|
||||
|
||||
containerID := 0
|
||||
for _, idrange := range subidRanges {
|
||||
idMap = append(idMap, IDMap{
|
||||
ContainerID: containerID,
|
||||
HostID: idrange.Start,
|
||||
Size: idrange.Length,
|
||||
})
|
||||
containerID = containerID + idrange.Length
|
||||
}
|
||||
return idMap
|
||||
func parseSubuid(username string) ([]user.SubID, error) {
|
||||
return user.ParseSubIDFileFilter(subuidFileName, func(sid user.SubID) bool {
|
||||
return sid.Name == username
|
||||
})
|
||||
}
|
||||
|
||||
func parseSubuid(username string) (subIDRanges, error) {
|
||||
return parseSubidFile(subuidFileName, username)
|
||||
}
|
||||
|
||||
func parseSubgid(username string) (subIDRanges, error) {
|
||||
return parseSubidFile(subgidFileName, username)
|
||||
}
|
||||
|
||||
// parseSubidFile will read the appropriate file (/etc/subuid or /etc/subgid)
|
||||
// and return all found subIDRanges for a specified username. If the special value
|
||||
// "ALL" is supplied for username, then all subIDRanges in the file will be returned
|
||||
func parseSubidFile(path, username string) (subIDRanges, error) {
|
||||
var rangeList subIDRanges
|
||||
|
||||
subidFile, err := os.Open(path)
|
||||
if err != nil {
|
||||
return rangeList, err
|
||||
}
|
||||
defer subidFile.Close()
|
||||
|
||||
s := bufio.NewScanner(subidFile)
|
||||
for s.Scan() {
|
||||
text := strings.TrimSpace(s.Text())
|
||||
if text == "" || strings.HasPrefix(text, "#") {
|
||||
continue
|
||||
}
|
||||
parts := strings.Split(text, ":")
|
||||
if len(parts) != 3 {
|
||||
return rangeList, fmt.Errorf("Cannot parse subuid/gid information: Format not correct for %s file", path)
|
||||
}
|
||||
if parts[0] == username || username == "ALL" {
|
||||
startid, err := strconv.Atoi(parts[1])
|
||||
if err != nil {
|
||||
return rangeList, fmt.Errorf("String to int conversion failed during subuid/gid parsing of %s: %v", path, err)
|
||||
}
|
||||
length, err := strconv.Atoi(parts[2])
|
||||
if err != nil {
|
||||
return rangeList, fmt.Errorf("String to int conversion failed during subuid/gid parsing of %s: %v", path, err)
|
||||
}
|
||||
rangeList = append(rangeList, subIDRange{startid, length})
|
||||
}
|
||||
}
|
||||
|
||||
return rangeList, s.Err()
|
||||
func parseSubgid(username string) ([]user.SubID, error) {
|
||||
return user.ParseSubIDFileFilter(subgidFileName, func(sid user.SubID) bool {
|
||||
return sid.Name == username
|
||||
})
|
||||
}
|
||||
|
||||
// CurrentIdentity returns the identity of the current process
|
||||
|
||||
@@ -1,28 +0,0 @@
|
||||
package idtools // import "github.com/docker/docker/pkg/idtools"
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"gotest.tools/v3/assert"
|
||||
)
|
||||
|
||||
func TestCreateIDMapOrder(t *testing.T) {
|
||||
subidRanges := subIDRanges{
|
||||
{100000, 1000},
|
||||
{1000, 1},
|
||||
}
|
||||
|
||||
idMap := createIDMap(subidRanges)
|
||||
assert.DeepEqual(t, idMap, []IDMap{
|
||||
{
|
||||
ContainerID: 0,
|
||||
HostID: 100000,
|
||||
Size: 1000,
|
||||
},
|
||||
{
|
||||
ContainerID: 1000,
|
||||
HostID: 1000,
|
||||
Size: 1,
|
||||
},
|
||||
})
|
||||
}
|
||||
@@ -228,11 +228,11 @@ func LoadIdentityMapping(name string) (IdentityMapping, error) {
|
||||
return IdentityMapping{}, fmt.Errorf("could not get user for username %s: %v", name, err)
|
||||
}
|
||||
|
||||
subuidRanges, err := lookupSubUIDRanges(usr)
|
||||
subuidRanges, err := lookupSubRangesFile("/etc/subuid", usr)
|
||||
if err != nil {
|
||||
return IdentityMapping{}, err
|
||||
}
|
||||
subgidRanges, err := lookupSubGIDRanges(usr)
|
||||
subgidRanges, err := lookupSubRangesFile("/etc/subgid", usr)
|
||||
if err != nil {
|
||||
return IdentityMapping{}, err
|
||||
}
|
||||
@@ -243,36 +243,28 @@ func LoadIdentityMapping(name string) (IdentityMapping, error) {
|
||||
}, nil
|
||||
}
|
||||
|
||||
func lookupSubUIDRanges(usr user.User) ([]IDMap, error) {
|
||||
rangeList, err := parseSubuid(strconv.Itoa(usr.Uid))
|
||||
func lookupSubRangesFile(path string, usr user.User) ([]IDMap, error) {
|
||||
uidstr := strconv.Itoa(usr.Uid)
|
||||
rangeList, err := user.ParseSubIDFileFilter(path, func(sid user.SubID) bool {
|
||||
return sid.Name == usr.Name || sid.Name == uidstr
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if len(rangeList) == 0 {
|
||||
rangeList, err = parseSubuid(usr.Name)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
if len(rangeList) == 0 {
|
||||
return nil, fmt.Errorf("no subuid ranges found for user %q", usr.Name)
|
||||
}
|
||||
return createIDMap(rangeList), nil
|
||||
}
|
||||
|
||||
func lookupSubGIDRanges(usr user.User) ([]IDMap, error) {
|
||||
rangeList, err := parseSubgid(strconv.Itoa(usr.Uid))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
idMap := []IDMap{}
|
||||
|
||||
containerID := 0
|
||||
for _, idrange := range rangeList {
|
||||
idMap = append(idMap, IDMap{
|
||||
ContainerID: containerID,
|
||||
HostID: int(idrange.SubID),
|
||||
Size: int(idrange.Count),
|
||||
})
|
||||
containerID = containerID + int(idrange.Count)
|
||||
}
|
||||
if len(rangeList) == 0 {
|
||||
rangeList, err = parseSubgid(usr.Name)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
if len(rangeList) == 0 {
|
||||
return nil, fmt.Errorf("no subgid ranges found for user %q", usr.Name)
|
||||
}
|
||||
return createIDMap(rangeList), nil
|
||||
return idMap, nil
|
||||
}
|
||||
|
||||
@@ -6,7 +6,7 @@ import (
|
||||
"fmt"
|
||||
"os"
|
||||
"os/exec"
|
||||
"os/user"
|
||||
stduser "os/user"
|
||||
"path/filepath"
|
||||
"syscall"
|
||||
"testing"
|
||||
@@ -15,6 +15,8 @@ import (
|
||||
"gotest.tools/v3/assert"
|
||||
is "gotest.tools/v3/assert/cmp"
|
||||
"gotest.tools/v3/skip"
|
||||
|
||||
"github.com/moby/sys/user"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -343,18 +345,20 @@ dockremap:231072:65536`
|
||||
if err := os.WriteFile(fnamePath, []byte(fcontent), 0o644); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
ranges, err := parseSubidFile(fnamePath, "dockremap")
|
||||
ranges, err := user.ParseSubIDFileFilter(fnamePath, func(sid user.SubID) bool {
|
||||
return sid.Name == "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].SubID != 231072 {
|
||||
t.Fatalf("wanted 231072, got %d instead", ranges[0].SubID)
|
||||
}
|
||||
if ranges[0].Length != 65536 {
|
||||
t.Fatalf("wanted 65536, got %d instead", ranges[0].Length)
|
||||
if ranges[0].Count != 65536 {
|
||||
t.Fatalf("wanted 65536, got %d instead", ranges[0].Count)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -410,7 +414,7 @@ func TestNewIDMappings(t *testing.T) {
|
||||
assert.Check(t, err)
|
||||
defer delUser(t, tempUser)
|
||||
|
||||
tempUser, err := user.Lookup(tempUser)
|
||||
tempUser, err := stduser.Lookup(tempUser)
|
||||
assert.Check(t, err)
|
||||
|
||||
idMapping, err := LoadIdentityMapping(tempUser.Username)
|
||||
|
||||
@@ -9,6 +9,7 @@ import (
|
||||
"sync"
|
||||
|
||||
"github.com/docker/docker/internal/lazyregexp"
|
||||
"github.com/moby/sys/user"
|
||||
)
|
||||
|
||||
// add a user and/or group to Linux /etc/passwd, /etc/group using standard
|
||||
@@ -129,38 +130,44 @@ func createSubordinateRanges(name string) error {
|
||||
}
|
||||
|
||||
func findNextUIDRange() (int, error) {
|
||||
ranges, err := parseSubuid("ALL")
|
||||
ranges, err := user.CurrentUserSubUIDs()
|
||||
if err != nil {
|
||||
return -1, fmt.Errorf("couldn't parse all ranges in /etc/subuid file: %v", err)
|
||||
}
|
||||
sort.Sort(ranges)
|
||||
sortRanges(ranges)
|
||||
return findNextRangeStart(ranges)
|
||||
}
|
||||
|
||||
func findNextGIDRange() (int, error) {
|
||||
ranges, err := parseSubgid("ALL")
|
||||
ranges, err := user.CurrentUserSubGIDs()
|
||||
if err != nil {
|
||||
return -1, fmt.Errorf("couldn't parse all ranges in /etc/subgid file: %v", err)
|
||||
}
|
||||
sort.Sort(ranges)
|
||||
sortRanges(ranges)
|
||||
return findNextRangeStart(ranges)
|
||||
}
|
||||
|
||||
func findNextRangeStart(rangeList subIDRanges) (int, error) {
|
||||
startID := defaultRangeStart
|
||||
for _, arange := range rangeList {
|
||||
if wouldOverlap(arange, startID) {
|
||||
startID = arange.Start + arange.Length
|
||||
}
|
||||
}
|
||||
return startID, nil
|
||||
func sortRanges(ranges []user.SubID) {
|
||||
sort.Slice(ranges, func(i, j int) bool {
|
||||
return ranges[i].SubID < ranges[j].SubID
|
||||
})
|
||||
}
|
||||
|
||||
func wouldOverlap(arange subIDRange, ID int) bool {
|
||||
low := ID
|
||||
high := ID + defaultRangeLen
|
||||
if (low >= arange.Start && low <= arange.Start+arange.Length) ||
|
||||
(high <= arange.Start+arange.Length && high >= arange.Start) {
|
||||
func findNextRangeStart(rangeList []user.SubID) (int, error) {
|
||||
var startID int64 = defaultRangeStart
|
||||
for _, arange := range rangeList {
|
||||
if wouldOverlap(arange, startID) {
|
||||
startID = arange.SubID + arange.Count
|
||||
}
|
||||
}
|
||||
return int(startID), nil
|
||||
}
|
||||
|
||||
func wouldOverlap(arange user.SubID, ID int64) bool {
|
||||
var low int64 = ID
|
||||
var high int64 = ID + defaultRangeLen
|
||||
if (low >= arange.SubID && low <= arange.SubID+arange.Count) ||
|
||||
(high <= arange.SubID+arange.Count && high >= arange.SubID) {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
|
||||
Reference in New Issue
Block a user