mirror of
https://github.com/moby/moby.git
synced 2022-11-09 12:21:53 -05:00
7ad0da7051
NewIdentityMapping took group name as an argument, and used the group name also to parse the /etc/sub{uid,gui}. But as per linux man pages, the sub{uid,gid} file maps username or uid, not a group name. Therefore, all occurrences where mapping is used need to consider only username and uid. Code trying to map using gid and group name in the daemon is also removed. Signed-off-by: Akhil Mohan <akhil.mohan@mayadata.io>
236 lines
7.1 KiB
Go
236 lines
7.1 KiB
Go
package idtools // import "github.com/docker/docker/pkg/idtools"
|
|
|
|
import (
|
|
"bufio"
|
|
"fmt"
|
|
"os"
|
|
"strconv"
|
|
"strings"
|
|
)
|
|
|
|
// IDMap contains a single entry for user namespace range remapping. An array
|
|
// of IDMap entries represents the structure that will be provided to the Linux
|
|
// kernel for creating a user namespace.
|
|
type IDMap struct {
|
|
ContainerID int `json:"container_id"`
|
|
HostID int `json:"host_id"`
|
|
Size int `json:"size"`
|
|
}
|
|
|
|
type subIDRange struct {
|
|
Start int
|
|
Length int
|
|
}
|
|
|
|
type ranges []subIDRange
|
|
|
|
func (e ranges) Len() int { return len(e) }
|
|
func (e ranges) Swap(i, j int) { e[i], e[j] = e[j], e[i] }
|
|
func (e ranges) Less(i, j int) bool { return e[i].Start < e[j].Start }
|
|
|
|
const (
|
|
subuidFileName = "/etc/subuid"
|
|
subgidFileName = "/etc/subgid"
|
|
)
|
|
|
|
// MkdirAllAndChown creates a directory (include any along the path) and then modifies
|
|
// ownership to the requested uid/gid. If the directory already exists, this
|
|
// function will still change ownership to the requested uid/gid pair.
|
|
func MkdirAllAndChown(path string, mode os.FileMode, owner Identity) error {
|
|
return mkdirAs(path, mode, owner, true, true)
|
|
}
|
|
|
|
// MkdirAndChown creates a directory and then modifies ownership to the requested uid/gid.
|
|
// If the directory already exists, this function still changes ownership.
|
|
// Note that unlike os.Mkdir(), this function does not return IsExist error
|
|
// in case path already exists.
|
|
func MkdirAndChown(path string, mode os.FileMode, owner Identity) error {
|
|
return mkdirAs(path, mode, owner, false, true)
|
|
}
|
|
|
|
// MkdirAllAndChownNew creates a directory (include any along the path) and then modifies
|
|
// ownership ONLY of newly created directories to the requested uid/gid. If the
|
|
// directories along the path exist, no change of ownership will be performed
|
|
func MkdirAllAndChownNew(path string, mode os.FileMode, owner Identity) error {
|
|
return mkdirAs(path, mode, owner, true, false)
|
|
}
|
|
|
|
// GetRootUIDGID retrieves the remapped root uid/gid pair from the set of maps.
|
|
// If the maps are empty, then the root uid/gid will default to "real" 0/0
|
|
func GetRootUIDGID(uidMap, gidMap []IDMap) (int, int, error) {
|
|
uid, err := toHost(0, uidMap)
|
|
if err != nil {
|
|
return -1, -1, err
|
|
}
|
|
gid, err := toHost(0, gidMap)
|
|
if err != nil {
|
|
return -1, -1, err
|
|
}
|
|
return uid, gid, nil
|
|
}
|
|
|
|
// toContainer takes an id mapping, and uses it to translate a
|
|
// host ID to the remapped ID. If no map is provided, then the translation
|
|
// assumes a 1-to-1 mapping and returns the passed in id
|
|
func toContainer(hostID int, idMap []IDMap) (int, error) {
|
|
if idMap == nil {
|
|
return hostID, nil
|
|
}
|
|
for _, m := range idMap {
|
|
if (hostID >= m.HostID) && (hostID <= (m.HostID + m.Size - 1)) {
|
|
contID := m.ContainerID + (hostID - m.HostID)
|
|
return contID, nil
|
|
}
|
|
}
|
|
return -1, fmt.Errorf("Host ID %d cannot be mapped to a container ID", hostID)
|
|
}
|
|
|
|
// toHost takes an id mapping and a remapped ID, and translates the
|
|
// ID to the mapped host ID. If no map is provided, then the translation
|
|
// assumes a 1-to-1 mapping and returns the passed in id #
|
|
func toHost(contID int, idMap []IDMap) (int, error) {
|
|
if idMap == nil {
|
|
return contID, nil
|
|
}
|
|
for _, m := range idMap {
|
|
if (contID >= m.ContainerID) && (contID <= (m.ContainerID + m.Size - 1)) {
|
|
hostID := m.HostID + (contID - m.ContainerID)
|
|
return hostID, nil
|
|
}
|
|
}
|
|
return -1, fmt.Errorf("Container ID %d cannot be mapped to a host ID", contID)
|
|
}
|
|
|
|
// Identity is either a UID and GID pair or a SID (but not both)
|
|
type Identity struct {
|
|
UID int
|
|
GID int
|
|
SID string
|
|
}
|
|
|
|
// IdentityMapping contains a mappings of UIDs and GIDs
|
|
type IdentityMapping struct {
|
|
uids []IDMap
|
|
gids []IDMap
|
|
}
|
|
|
|
// NewIDMappingsFromMaps creates a new mapping from two slices
|
|
// Deprecated: this is a temporary shim while transitioning to IDMapping
|
|
func NewIDMappingsFromMaps(uids []IDMap, gids []IDMap) *IdentityMapping {
|
|
return &IdentityMapping{uids: uids, gids: gids}
|
|
}
|
|
|
|
// RootPair returns a uid and gid pair for the root user. The error is ignored
|
|
// because a root user always exists, and the defaults are correct when the uid
|
|
// and gid maps are empty.
|
|
func (i *IdentityMapping) RootPair() Identity {
|
|
uid, gid, _ := GetRootUIDGID(i.uids, i.gids)
|
|
return Identity{UID: uid, GID: gid}
|
|
}
|
|
|
|
// ToHost returns the host UID and GID for the container uid, gid.
|
|
// Remapping is only performed if the ids aren't already the remapped root ids
|
|
func (i *IdentityMapping) ToHost(pair Identity) (Identity, error) {
|
|
var err error
|
|
target := i.RootPair()
|
|
|
|
if pair.UID != target.UID {
|
|
target.UID, err = toHost(pair.UID, i.uids)
|
|
if err != nil {
|
|
return target, err
|
|
}
|
|
}
|
|
|
|
if pair.GID != target.GID {
|
|
target.GID, err = toHost(pair.GID, i.gids)
|
|
}
|
|
return target, err
|
|
}
|
|
|
|
// ToContainer returns the container UID and GID for the host uid and gid
|
|
func (i *IdentityMapping) ToContainer(pair Identity) (int, int, error) {
|
|
uid, err := toContainer(pair.UID, i.uids)
|
|
if err != nil {
|
|
return -1, -1, err
|
|
}
|
|
gid, err := toContainer(pair.GID, i.gids)
|
|
return uid, gid, err
|
|
}
|
|
|
|
// Empty returns true if there are no id mappings
|
|
func (i *IdentityMapping) Empty() bool {
|
|
return len(i.uids) == 0 && len(i.gids) == 0
|
|
}
|
|
|
|
// UIDs return the UID mapping
|
|
// TODO: remove this once everything has been refactored to use pairs
|
|
func (i *IdentityMapping) UIDs() []IDMap {
|
|
return i.uids
|
|
}
|
|
|
|
// GIDs return the UID mapping
|
|
// TODO: remove this once everything has been refactored to use pairs
|
|
func (i *IdentityMapping) GIDs() []IDMap {
|
|
return i.gids
|
|
}
|
|
|
|
func createIDMap(subidRanges ranges) []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) (ranges, error) {
|
|
return parseSubidFile(subuidFileName, username)
|
|
}
|
|
|
|
func parseSubgid(username string) (ranges, error) {
|
|
return parseSubidFile(subgidFileName, username)
|
|
}
|
|
|
|
// parseSubidFile will read the appropriate file (/etc/subuid or /etc/subgid)
|
|
// and return all found ranges for a specified username. If the special value
|
|
// "ALL" is supplied for username, then all ranges in the file will be returned
|
|
func parseSubidFile(path, username string) (ranges, error) {
|
|
var rangeList ranges
|
|
|
|
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()
|
|
}
|